Files
Astral/prof/prof.py
T
2025-12-07 12:05:36 -03:00

116 lines
3.0 KiB
Python
Executable File

#!/usr/bin/env python3
# This script takes data generated by the profiler in Astral and creates a flamegraph out of it.
# Format of the profiler file:
# 1 byte indicating the number of calls in the backtrace
# 8-byte Pointers starting by deepest call in the stack
# Repeat until EOF
import sys
import os
import subprocess
if len(sys.argv) != 3:
print(sys.argv[0] + ": usage: " + sys.argv[0] + " INPUT_FILE OUTPUT_FILE")
exit(1)
# Open needed files
in_file = open(sys.argv[1], "rb")
out_file = open(sys.argv[2], "w")
# Download the flamegraph script if nescessary
os.chdir(os.path.dirname(__file__))
if not os.access("flamegraph.pl", os.R_OK):
os.system("wget https://raw.githubusercontent.com/brendangregg/FlameGraph/refs/heads/master/flamegraph.pl")
os.system("chmod +x flamegraph.pl")
print("Starting subprocesses")
# Start the flamegraph renderer
proc_fg = subprocess.Popen(
["./flamegraph.pl"],
stdin=subprocess.PIPE,
stdout=out_file,
text=True
)
traces = {}
mappings = {}
print("Getting function information")
# get info out of addr2line
while True:
backtrace_size = in_file.read(1)
if not backtrace_size:
# EOF
break
backtrace_size = int.from_bytes(backtrace_size, byteorder='little', signed=False)
backtrace = []
# Get backtrace, calling addr2line for non-cached mappings
i = backtrace_size
while i > 0:
i -= 1
addr = in_file.read(8)
if not addr:
print(("Corrupted file (unexpected EOF, reading %d entries)" % (backtrace_size)), file=sys.stderr)
exit(1)
addr = hex(int.from_bytes(addr, byteorder='little', signed=False))
# Add it to the backtrace
backtrace.append(addr)
if addr in mappings:
continue
# Get the mapping
msg = addr + "\n"
# Start and send the data to addr2line
data = subprocess.Popen(
["addr2line", "-i", "-s", "-f", "-e", "../build-x86_64/iso/astral"],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
text=True
).communicate(input=msg)[0].split('\n')
mappings[addr] = data
# format the response in a way flamegraph expects
formatted_str = ""
for addr in backtrace:
map_data = mappings[addr]
aux_function = ""
for data_line in map_data:
if aux_function == "":
aux_function = data_line
continue
file, line = data_line.split(":")
formatted_str = file + "`" + aux_function + ";" + formatted_str
aux_function = ""
formatted_str = formatted_str[:-1] # remove ;
if not formatted_str in traces:
traces[formatted_str] = 0
traces[formatted_str] += 1
print("Generating flamegraph")
# send the trace over to flamegraph
for trace in traces:
final_string = trace + " " + str(traces[trace])
proc_fg.stdin.write(final_string + "\n")
proc_fg.stdin.flush()
print("Waiting...");
# wait until it is done with its thing
proc_fg.stdin.close()
proc_fg.wait()