142 lines
5.0 KiB
Python
142 lines
5.0 KiB
Python
|
import sys
|
||
|
import json
|
||
|
|
||
|
# Map start block to current allocation info.
|
||
|
current_heap = {}
|
||
|
allocation_history = []
|
||
|
root = {}
|
||
|
|
||
|
def change_root(trace, size):
|
||
|
level = root
|
||
|
for frame in reversed(trace):
|
||
|
file_location = frame[1]
|
||
|
if file_location not in level:
|
||
|
level[file_location] = {"blocks": 0,
|
||
|
"file": file_location,
|
||
|
"function": frame[2],
|
||
|
"subcalls": {}}
|
||
|
level[file_location]["blocks"] += size
|
||
|
level = level[file_location]["subcalls"]
|
||
|
|
||
|
total_actions = 0
|
||
|
non_single_block_streak = 0
|
||
|
max_nsbs = 0
|
||
|
last_action = None
|
||
|
last_total_actions = 0
|
||
|
count = 0
|
||
|
actions = {}
|
||
|
last_ticks_ms = 0
|
||
|
ticks_ms = 0
|
||
|
block_sizes = {}
|
||
|
allocation_sources = {}
|
||
|
with open(sys.argv[1], "r") as f:
|
||
|
for line in f:
|
||
|
if not line.strip():
|
||
|
break
|
||
|
for line in f:
|
||
|
action = None
|
||
|
if line.startswith("Breakpoint 2"):
|
||
|
break
|
||
|
next(f) # throw away breakpoint code line
|
||
|
# print(next(f)) # first frame
|
||
|
block = 0
|
||
|
size = 0
|
||
|
trace = []
|
||
|
for line in f:
|
||
|
# print(line.strip())
|
||
|
if line[0] == "#":
|
||
|
frame = line.strip().split()
|
||
|
if frame[1].startswith("0x"):
|
||
|
trace.append((frame[1], frame[-1], frame[3]))
|
||
|
else:
|
||
|
trace.append(("0x0", frame[-1], frame[1]))
|
||
|
elif line[0] == "$":
|
||
|
#print(line.strip().split()[-1])
|
||
|
block = int(line.strip().split()[-1][2:], 16)
|
||
|
next_line = next(f)
|
||
|
size = int(next_line.strip().split()[-1][2:], 16)
|
||
|
# next_line = next(f)
|
||
|
# ticks_ms = int(next_line.strip().split()[-1][2:], 16)
|
||
|
if not line.strip():
|
||
|
break
|
||
|
|
||
|
action = "unknown"
|
||
|
if block not in current_heap:
|
||
|
current_heap[block] = {"start_block": block, "size": size, "start_trace": trace, "start_time": total_actions}
|
||
|
action = "alloc"
|
||
|
if size == 1:
|
||
|
max_nsbs = max(max_nsbs, non_single_block_streak)
|
||
|
non_single_block_streak = 0
|
||
|
else:
|
||
|
non_single_block_streak += 1
|
||
|
#change_root(trace, size)
|
||
|
if size not in block_sizes:
|
||
|
block_sizes[size] = 0
|
||
|
source = trace[-1][-1]
|
||
|
if source not in allocation_sources:
|
||
|
print(trace)
|
||
|
allocation_sources[source] = 0
|
||
|
allocation_sources[source] += 1
|
||
|
block_sizes[size] += 1
|
||
|
else:
|
||
|
alloc = current_heap[block]
|
||
|
alloc["end_trace"] = trace
|
||
|
alloc["end_time"] = total_actions
|
||
|
change_root(alloc["start_trace"], -1 * alloc["size"])
|
||
|
if size > 0:
|
||
|
action = "realloc"
|
||
|
current_heap[block] = {"start_block": block, "size": size, "start_trace": trace, "start_time": total_actions}
|
||
|
#change_root(trace, size)
|
||
|
else:
|
||
|
action = "free"
|
||
|
if trace[0][2] == "gc_sweep":
|
||
|
action = "sweep"
|
||
|
non_single_block_streak = 0
|
||
|
if (trace[3][2] == "py_gc_collect" or (trace[3][2] == "gc_deinit" and count > 1)) and last_action != "sweep":
|
||
|
print(ticks_ms - last_ticks_ms, total_actions - last_total_actions, "gc.collect", max_nsbs)
|
||
|
print(actions)
|
||
|
print(block_sizes)
|
||
|
print(allocation_sources)
|
||
|
actions = {}
|
||
|
block_sizes = {}
|
||
|
allocation_sources = {}
|
||
|
if count % 2 == 0:
|
||
|
print()
|
||
|
count += 1
|
||
|
last_total_actions = total_actions
|
||
|
last_ticks_ms = ticks_ms
|
||
|
max_nsbs = 0
|
||
|
del current_heap[block]
|
||
|
alloc["end_cause"] = action
|
||
|
allocation_history.append(alloc)
|
||
|
if action not in actions:
|
||
|
actions[action] = 0
|
||
|
actions[action] += 1
|
||
|
last_action = action
|
||
|
#print(total_actions, non_single_block_streak, action, block, size)
|
||
|
total_actions += 1
|
||
|
print(actions)
|
||
|
print(max_nsbs)
|
||
|
print()
|
||
|
|
||
|
for alloc in current_heap.values():
|
||
|
alloc["end_trace"] = ""
|
||
|
alloc["end_time"] = total_actions
|
||
|
allocation_history.append(alloc)
|
||
|
|
||
|
def print_frame(frame, indent=0):
|
||
|
for key in sorted(frame):
|
||
|
if not frame[key]["blocks"] or key.startswith("../py/malloc.c") or key.startswith("../py/gc.c"):
|
||
|
continue
|
||
|
print(" " * (indent - 1), key, frame[key]["function"], frame[key]["blocks"], "blocks")
|
||
|
print_frame(frame[key]["subcalls"], indent + 2)
|
||
|
|
||
|
# print_frame(root)
|
||
|
# total_blocks = 0
|
||
|
# for key in sorted(root):
|
||
|
# total_blocks += root[key]["blocks"]
|
||
|
# print(total_blocks, "total blocks")
|
||
|
|
||
|
# with open("allocation_history.json", "w") as f:
|
||
|
# json.dump(allocation_history, f)
|