Skip to content

Commit cd813d8

Browse files
committed
Add python script
1 parent 738e407 commit cd813d8

File tree

3 files changed

+132
-4
lines changed

3 files changed

+132
-4
lines changed
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
import re
2+
import argparse
3+
import os
4+
import sys
5+
import subprocess
6+
from prometheus_api_client import PrometheusConnect
7+
from utils import FLAMEGRAPHS_DIR, get_git_root
8+
9+
def get_stack_lines(prom, group_by_kvs, stack_keys, metric_name, sum_metrics=None):
10+
"""
11+
Filters metrics from prometheus for entries that look like:
12+
[ { labels: [["key1", "span1;span2"], ["key2", "span3"]], "metric": metric_name, "value": 2 } ]
13+
14+
It will find entries that have all of stack_keys as present in the labels and then concatenate the corresponding values into a single flat stack entry and then add the value at the end.
15+
It will write a file with one line each for flamegraph.pl or inferno-flamegraph to consume.
16+
If sum_metrics is not None, instead of searching for metric_name, it will sum the values of the metrics in sum_metrics.
17+
"""
18+
lines = []
19+
stack_sums = {}
20+
non_zero = False
21+
22+
if sum_metrics is not None:
23+
regex = "|".join(re.escape(m) for m in sum_metrics)
24+
promql = f'{{__name__=~"^{regex}$"}}'
25+
metrics = prom.custom_query(promql)
26+
else:
27+
promql = f'{{__name__=~"^{metric_name}$"}}'
28+
metrics = prom.custom_query(promql)
29+
30+
# Process metrics
31+
for metric in metrics:
32+
labels = metric['metric']
33+
filter = False
34+
for key, value in group_by_kvs:
35+
if key not in labels or labels[key] != value:
36+
filter = True
37+
break
38+
if filter:
39+
continue
40+
41+
stack_values = []
42+
for key in stack_keys:
43+
if key not in labels:
44+
filter = True
45+
break
46+
stack_values.append(labels[key])
47+
if filter:
48+
continue
49+
50+
stack = ';'.join(stack_values)
51+
value = int(metric['value'][1])
52+
stack_sums[stack] = stack_sums.get(stack, 0) + value
53+
54+
if value != 0:
55+
non_zero = True
56+
57+
lines = [f"{stack} {value}" for stack, value in stack_sums.items() if value != 0]
58+
59+
# Currently cycle tracker does not use gauge
60+
return lines if non_zero else []
61+
62+
63+
def create_flamegraph(fname, prom, group_by_kvs, stack_keys, metric_name, sum_metrics=None, reverse=False):
64+
lines = get_stack_lines(prom, group_by_kvs, stack_keys, metric_name, sum_metrics)
65+
if not lines:
66+
return
67+
68+
suffixes = [key for key in stack_keys if key != "cycle_tracker_span"]
69+
70+
git_root = get_git_root()
71+
flamegraph_dir = os.path.join(git_root, FLAMEGRAPHS_DIR)
72+
os.makedirs(flamegraph_dir, exist_ok=True)
73+
74+
path_prefix = f"{flamegraph_dir}{fname}.{'.'.join(suffixes)}.{metric_name}{'.reverse' if reverse else ''}"
75+
stacks_path = f"{path_prefix}.stacks"
76+
flamegraph_path = f"{path_prefix}.svg"
77+
78+
with open(stacks_path, 'w') as f:
79+
for line in lines:
80+
f.write(f"{line}\n")
81+
82+
with open(flamegraph_path, 'w') as f:
83+
command = ["inferno-flamegraph", "--title", f"{fname} {' '.join(suffixes)} {metric_name}", stacks_path]
84+
if reverse:
85+
command.append("--reverse")
86+
87+
subprocess.run(command, stdout=f, check=False)
88+
print(f"Created flamegraph at {flamegraph_path}")
89+
90+
91+
def create_flamegraphs(prom, group_by, stack_keys, metric_name, sum_metrics=None, reverse=False):
92+
# Assume group_by is a list of length 1
93+
group_by_values_list = prom.get_label_values(label_name=group_by[0])
94+
for group_by_values in group_by_values_list:
95+
group_by_kvs = list(zip(group_by, [group_by_values]))
96+
fname = 'metrics' + '-' + '-'.join([group_by_values])
97+
create_flamegraph(fname, prom, group_by_kvs, stack_keys, metric_name, sum_metrics, reverse=reverse)
98+
99+
100+
def create_custom_flamegraphs(prom, group_by=["group"]):
101+
for reverse in [False, True]:
102+
create_flamegraphs(prom, group_by, ["cycle_tracker_span", "dsl_ir", "opcode"], "frequency",
103+
reverse=reverse)
104+
create_flamegraphs(prom, group_by, ["cycle_tracker_span", "dsl_ir", "opcode", "air_name"], "cells_used",
105+
reverse=reverse)
106+
create_flamegraphs(prom, group_by, ["cell_tracker_span"], "cells_used",
107+
sum_metrics=["simple_advice_cells", "fixed_cells", "lookup_advice_cells"],
108+
reverse=reverse)
109+
110+
111+
def main():
112+
import shutil
113+
114+
if not shutil.which("inferno-flamegraph"):
115+
print("You must have inferno-flamegraph installed to use this script.")
116+
sys.exit(1)
117+
118+
argparser = argparse.ArgumentParser()
119+
argparser.add_argument('prometheus_url', type=str, help="Path to the prometheus server")
120+
args = argparser.parse_args()
121+
122+
prom = PrometheusConnect(url=args.prometheus_url, disable_ssl=True)
123+
124+
create_custom_flamegraphs(prom)
125+
126+
127+
if __name__ == '__main__':
128+
main()

crates/vm/src/metrics/cycle_tracker/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ mod emit {
5656
pub fn increment_opcode(&self, (dsl_ir, opcode): &(Option<String>, String)) {
5757
let labels = [
5858
("opcode", opcode.clone()),
59-
("dsl_ir", dsl_ir.clone().unwrap_or_default()),
59+
("dsl_ir", dsl_ir.clone().unwrap_or("-".to_string())),
6060
("cycle_tracker_span", self.get_full_name()),
6161
];
6262
counter!("frequency", &labels).increment(1u64);
@@ -73,7 +73,7 @@ mod emit {
7373
let labels = [
7474
("air_name", air_name.clone()),
7575
("opcode", opcode.clone()),
76-
("dsl_ir", dsl_ir.clone().unwrap_or_default()),
76+
("dsl_ir", dsl_ir.clone().unwrap_or("-".to_string())),
7777
("cycle_tracker_span", self.get_full_name()),
7878
];
7979
counter!("cells_used", &labels).increment(trace_cells_used as u64);

crates/vm/src/metrics/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,15 +137,15 @@ impl VmMetrics {
137137

138138
for ((dsl_ir, opcode), value) in self.counts.iter() {
139139
let labels = [
140-
("dsl_ir", dsl_ir.clone().unwrap_or_else(String::new)),
140+
("dsl_ir", dsl_ir.clone().unwrap_or("-".to_string())),
141141
("opcode", opcode.clone()),
142142
];
143143
counter!("frequency", &labels).absolute(*value as u64);
144144
}
145145

146146
for ((dsl_ir, opcode, air_name), value) in self.trace_cells.iter() {
147147
let labels = [
148-
("dsl_ir", dsl_ir.clone().unwrap_or_else(String::new)),
148+
("dsl_ir", dsl_ir.clone().unwrap_or("-".to_string())),
149149
("opcode", opcode.clone()),
150150
("air_name", air_name.clone()),
151151
];

0 commit comments

Comments
 (0)