profile.py
156 lines
| 4.0 KiB
| text/x-python
|
PythonLexer
r1 | ||||
r5088 | # Copyright (C) 2010-2023 RhodeCode GmbH | |||
r1 | # | |||
# This program is free software: you can redistribute it and/or modify | ||||
# it under the terms of the GNU Affero General Public License, version 3 | ||||
# (only), as published by the Free Software Foundation. | ||||
# | ||||
# This program is distributed in the hope that it will be useful, | ||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
# GNU General Public License for more details. | ||||
# | ||||
# You should have received a copy of the GNU Affero General Public License | ||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
# | ||||
# This program is dual-licensed. If you wish to learn more about the | ||||
# RhodeCode Enterprise Edition, including its added features, Support services, | ||||
# and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
""" | ||||
This is a standalone script which will start VCS and RC. | ||||
Performance numbers will be written on each interval to: | ||||
vcs_profileX.csv | ||||
rc_profileX.csv | ||||
To stop the script by press Ctrl-C | ||||
""" | ||||
import datetime | ||||
import os | ||||
import psutil | ||||
r4926 | import subprocess | |||
r1 | import sys | |||
import time | ||||
import traceback | ||||
r5087 | import urllib.request | |||
import urllib.parse | ||||
import urllib.error | ||||
r1 | ||||
PROFILING_INTERVAL = 5 | ||||
RC_WEBSITE = "http://localhost:5001/" | ||||
def get_file(prefix): | ||||
out_file = None | ||||
r3057 | for i in range(100): | |||
r1 | file_path = "%s_profile%.3d.csv" % (prefix, i) | |||
if os.path.exists(file_path): | ||||
continue | ||||
out_file = open(file_path, "w") | ||||
out_file.write("Time; CPU %; Memory (MB); Total FDs; Dulwich FDs; Threads\n") | ||||
break | ||||
return out_file | ||||
def dump_system(): | ||||
r3057 | print("System Overview...") | |||
print("\nCPU Count: %d (%d real)" % | ||||
(psutil.cpu_count(), psutil.cpu_count(logical=False))) | ||||
print("\nDisk:") | ||||
print(psutil.disk_usage(os.sep)) | ||||
print("\nMemory:") | ||||
print(psutil.virtual_memory()) | ||||
print("\nMemory (swap):") | ||||
print(psutil.swap_memory()) | ||||
r1 | ||||
def count_dulwich_fds(proc): | ||||
r4926 | p = subprocess.Popen(["lsof", "-p", proc.pid], stdout=subprocess.PIPE) | |||
r1 | out, err = p.communicate() | |||
count = 0 | ||||
for line in out.splitlines(): | ||||
content = line.split() | ||||
# http://git-scm.com/book/en/Git-Internals-Packfiles | ||||
if content[-1].endswith(".idx"): | ||||
count += 1 | ||||
return count | ||||
def dump_process(pid, out_file): | ||||
now = datetime.datetime.now() | ||||
cpu = pid.cpu_percent() | ||||
mem = pid.memory_info() | ||||
fds = pid.num_fds() | ||||
dulwich_fds = count_dulwich_fds(pid) | ||||
threads = pid.num_threads() | ||||
content = [now.strftime('%m/%d/%y %H:%M:%S'), | ||||
cpu, | ||||
"%.2f" % (mem[0]/1024.0/1024.0), | ||||
fds, dulwich_fds, threads] | ||||
out_file.write("; ".join([str(item) for item in content])) | ||||
out_file.write("\n") | ||||
# Open output files | ||||
vcs_out = get_file("vcs") | ||||
if vcs_out is None: | ||||
r3057 | print("Unable to enumerate output file for VCS") | |||
r1 | sys.exit(1) | |||
rc_out = get_file("rc") | ||||
if rc_out is None: | ||||
r3057 | print("Unable to enumerate output file for RC") | |||
r1 | sys.exit(1) | |||
# Show system information | ||||
dump_system() | ||||
r3057 | print("\nStarting VCS...") | |||
r1 | vcs = psutil.Popen(["vcsserver"]) | |||
time.sleep(1) | ||||
if not vcs.is_running(): | ||||
r3057 | print("VCS - Failed to start") | |||
r1 | sys.exit(1) | |||
r3057 | print("VCS - Ok") | |||
r1 | ||||
r3057 | print("\nStarting RhodeCode...") | |||
r1 | rc = psutil.Popen("RC_VCSSERVER_TEST_DISABLE=1 paster serve test.ini", | |||
r4926 | shell=True, stdin=subprocess.PIPE) | |||
r1 | time.sleep(1) | |||
if not rc.is_running(): | ||||
r3057 | print("RC - Failed to start") | |||
r1 | vcs.terminate() | |||
sys.exit(1) | ||||
# Send command to create the databases | ||||
rc.stdin.write("y\n") | ||||
# Verify that the website is up | ||||
time.sleep(4) | ||||
try: | ||||
r4914 | urllib.request.urlopen(RC_WEBSITE) | |||
r1 | except IOError: | |||
r3057 | print("RC - Website not started") | |||
r1 | vcs.terminate() | |||
sys.exit(1) | ||||
r3057 | print("RC - Ok") | |||
r1 | ||||
r3057 | print("\nProfiling...\n%s\n" % ("-"*80)) | |||
r1 | while True: | |||
try: | ||||
dump_process(vcs, vcs_out) | ||||
dump_process(rc, rc_out) | ||||
time.sleep(PROFILING_INTERVAL) | ||||
except Exception: | ||||
r3057 | print(traceback.format_exc()) | |||
r1 | break | |||
# Finalize the profiling | ||||
vcs_out.close() | ||||
rc_out.close() | ||||
vcs.terminate() | ||||
rc.terminate() | ||||