Logo

index : blog

---

  • summary
  • about
  • tree
  • log
  • branches
<< path: root/public/blog.git/html/bench/record.py blob: a50035e935558978cda1a2acd901cccc3681a5c8 [raw] [clear marker]

        
0#!/bin/python
1
2import time
3import subprocess
4
5from os import listdir
6from sys import exit, argv
7from time import sleep
8from pathlib import Path
9
10
11script_dir = Path(__file__).resolve().parent
12
13output_csv_fn = lambda a: f"metrics_{a}.csv"
14output_plot_fn = lambda a: f"plot_{a}.png"
15
16INTERVAL = 1.5
17CSV_HEADER = "ts,cpu_percent,ram_mb,open_fds"
18GNUPLOT_FILE = script_dir / "plot_metrics.gp"
19DIR_CSV = Path("metrics")
20DIR_PLOT = Path("output")
21
22
23def run(pid, fp):
24 create_new_file_or_exit(fp);
25
26 proc1, total1 = current_cpu(pid)
27 p = Path(f"/proc/{pid}")
28
29 num_cpus = open_file_or_exit("/proc/cpuinfo")
30 num_cpus = num_cpus.count("processor\t:")
31
32 print(CSV_HEADER)
33
34 while p.exists():
35 proc2, total2 = current_cpu(pid)
36 total = total2 - total1
37
38 if total == 0:
39 proc1 = proc2
40 total1 = total2
41 continue
42
43 cpu_percent = 100 * (proc2 - proc1) / total * num_cpus
44 cpu_percent = round(cpu_percent, 1)
45
46 proc1 = proc2
47 total1 = total2
48
49 ram = open_file_or_exit(f"/proc/{pid}/status")
50 ram = ram.split("\n")
51
52 for line in ram:
53 if line.startswith("VmRSS"):
54 ram = line.split()
55 mb = round(int(ram[1]) / 1024, 1)
56 ram = f"{mb}"
57
58 fds = Path(f"/proc/{pid}/fd")
59 fds = len(listdir(fds))
60
61 ts = int(time.monotonic())
62 csv_line = f"{ts},{cpu_percent},{ram},{fds}"
63
64 print(csv_line)
65 line_append(fp, csv_line)
66 sleep(INTERVAL)
67
68 print("Terminated.")
69
70
71def current_cpu(pid):
72 fields = open_file_or_exit(f"/proc/{pid}/stat")
73 fields = fields.split()
74
75 utime = int(fields[13])
76 stime = int(fields[14])
77
78 cpu_fields = open_file_or_exit("/proc/stat")
79 cpu_fields = cpu_fields.split("\n")[0].split()
80
81 total = sum(int(x) for x in cpu_fields[1:])
82 return utime + stime, total
83
84
85def open_file_or_exit(fp):
86 try:
87 with open(fp, 'r', encoding="utf8") as f:
88 return f.read()
89 except Exception as e:
90 print(e)
91 exit(1)
92
93
94def create_new_file_or_exit(fn):
95 try:
96 with open(fn, "w+", encoding="utf8") as f:
97 f.write(CSV_HEADER + "\n")
98 except Exception as e:
99 print(e)
100 exit(1)
101
102
103def line_append(fn, line):
104 try:
105 with open(fn, "a+", encoding="utf8") as f:
106 f.write(line + "\n")
107 except Exception as e:
108 print(e)
109 exit(1)
110
111
112def run_command(cmd):
113 try:
114 subprocess.run(cmd, capture_output=True, text=True, check=True)
115 except subprocess.CalledProcessError as e:
116 print(f"Command failed {e.returncode}: {e.stderr}")
117 exit(1)
118
119
120if __name__ == "__main__":
121 args = argv
122
123 if len(args) == 1 or len(args) > 4:
124 print("Usage: record.py PID [-p, -plot]")
125 exit(1)
126
127 DIR_CSV.mkdir(exist_ok=True)
128 DIR_PLOT.mkdir(exist_ok=True)
129
130 if "-po" in args:
131 if len(args) != 3:
132 print("Need filepath to csv file")
133 exit(1)
134
135 fp = Path(args[2])
136 out = fp.name
137 out = Path(out).with_suffix(".png")
138 gnuplot_cmd = [
139 "gnuplot",
140 "-e",
141 f"file='{fp}'",
142 "-e",
143 f"set output '{out}'",
144 GNUPLOT_FIL
145 ]
146 run_command(gnuplot_cmd)
147 exit(0)
148
149 pid = args[1]
150 ts = int(time.monotonic())
151 fp = DIR_CSV / Path(output_csv_fn(f"{pid}_{ts}"))
152 out = DIR_PLOT / Path(output_plot_fn(f"{pid}_{ts}"))
153
154 try:
155 run(pid, fp)
156 except KeyboardInterrupt:
157 print("Terminated by user.")
158
159 if "-p" in args:
160 gnuplot_cmd = [
161 "gnuplot",
162 "-e",
163 f"file='{fp}'",
164 "-e",
165 f"set output '{out}'",
166 GNUPLOT_FILE
167 ]
168 run_command(gnuplot_cmd)
169
170
Copyright 2026  E766CB298A6D1E64 | Git-Thing heavily inspired by cgit