aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/chattyparser.py100
1 files changed, 54 insertions, 46 deletions
diff --git a/src/chattyparser.py b/src/chattyparser.py
index e63ec70..d445528 100755
--- a/src/chattyparser.py
+++ b/src/chattyparser.py
@@ -1,28 +1,31 @@
#!/usr/bin/env python3
+"""Parser and Plotter for the traces produced by chattymalloc"""
+
import re
+import sys
+
import matplotlib.pyplot as plt
import numpy as np
-import sys
-ptr = "(?:0x)?(?P<ptr>(?:\w+)|(?:\(nil\)))"
-size = "(?P<size>\d+)"
-alignment = "(?P<alignment>\d+)"
+PTR = "(?:0x)?(?P<ptr>(?:\\w+)|(?:\\(nil\\)))"
+SIZE = "(?P<size>\\d+)"
+ALIGNMENT = "(?P<alignment>\\d+)"
-malloc_re = re.compile(f"^m {size} {ptr}$")
-free_re = re.compile(f"^f {ptr}$")
-calloc_re = re.compile(f"^c (?P<nmemb>\d+) {size} {ptr}$")
-realloc_re = re.compile(f"^r {ptr} {size} {ptr.replace('ptr', 'nptr')}$")
-memalign_re = re.compile(f"^ma {alignment} {size} {ptr}$")
-posix_memalign_re = re.compile(f"^p_ma {ptr} {alignment} {size} (?P<ret>\d+)$")
-valloc_re = re.compile(f"^v {size} {ptr}$")
-pvalloc_re = re.compile(f"^pv {size} {ptr}$")
-aligned_alloc_re = re.compile(f"^a_m {alignment} {size} {ptr}$")
+MALLOC_RE = re.compile(f"^m {SIZE} {PTR}$")
+FREE_RE = re.compile(f"^f {PTR}$")
+CALLOC_RE = re.compile(f"^c (?P<nmemb>\\d+) {SIZE} {PTR}$")
+REALLOC_RE = re.compile(f"^r {PTR} {SIZE} {PTR.replace('ptr', 'nptr')}$")
+MEMALIGN_RE = re.compile(f"^ma {ALIGNMENT} {SIZE} {PTR}$")
+POSIX_MEMALIGN_RE = re.compile(f"^p_ma {PTR} {ALIGNMENT} {SIZE} (?P<ret>\\d+)$")
+VALLOC_RE = re.compile(f"^v {SIZE} {PTR}$")
+PVALLOC_RE = re.compile(f"^pv {SIZE} {PTR}$")
+ALIGNED_ALLOC_RE = re.compile(f"^a_m {ALIGNMENT} {SIZE} {PTR}$")
-trace_regex = {"malloc": malloc_re, "free": free_re, "calloc": calloc_re,
- "realloc": realloc_re, "memalign": memalign_re,
- "posix_memalign": posix_memalign_re, "valloc": valloc_re,
- "pvalloc": pvalloc_re, "aligned_alloc": aligned_alloc_re}
+TRACE_REGEX = {"malloc": MALLOC_RE, "free": FREE_RE, "calloc": CALLOC_RE,
+ "realloc": REALLOC_RE, "memalign": MEMALIGN_RE,
+ "posix_memalign": POSIX_MEMALIGN_RE, "valloc": VALLOC_RE,
+ "pvalloc": PVALLOC_RE, "aligned_alloc": ALIGNED_ALLOC_RE}
def record_allocation(hist, total_size, allocations, ptr, size, coll_size,
@@ -46,13 +49,13 @@ def record_allocation(hist, total_size, allocations, ptr, size, coll_size,
# realloc returns new pointer
if optr and optr in allocations:
size -= allocations[optr]
- del(allocations[optr])
+ del allocations[optr]
allocations[ptr] = size
if not nohist:
hist[size] = hist.get(size, 0) + 1
- if type(total_size[-1]) != int or type(size) != int:
+ if not isinstance(total_size[-1], int) or not isinstance(size, int):
print("invalid type", type(total_size[-1]), type(size))
return
@@ -71,13 +74,17 @@ def record_allocation(hist, total_size, allocations, ptr, size, coll_size,
elif req_size:
total_size.append(total_size[-1])
- del(allocations[ptr])
+ del allocations[ptr]
# free of invalid pointer
elif coll_size:
total_size.append(total_size[-1])
def parse(path="chattymalloc.txt", coll_size=True, req_size=None, nohist=False):
+ """parse a chattymalloc trace
+
+ :returns: a histogram dict, a dict of occurred function calls, list of total requested memory
+ """
# count function calls
calls = {"malloc": 0, "free": 0, "calloc": 0, "realloc": 0, "memalign": 0,
"posix_memalign": 0, "valloc": 0, "pvalloc": 0, "aligned_alloc": 0}
@@ -87,19 +94,19 @@ def parse(path="chattymalloc.txt", coll_size=True, req_size=None, nohist=False):
requested_size = [0]
# Dictionary mapping allocation sizes to the count of their appearance
hist = {}
- ln = 0
+ line_count = 0
def record(ptr, size, optr=None, free=False):
"""Wrapper around record_allocation using local variables from parse"""
record_allocation(hist, requested_size, allocations, ptr, size,
coll_size, req_size, nohist, optr, free)
- with open(path, "r") as f:
- for i, l in enumerate(f.readlines()):
- ln += 1
+ with open(path, "r") as trace_file:
+ for line in trace_file.readlines():
+ line_count += 1
valid_line = False
- for func, func_regex in trace_regex.items():
- res = func_regex.match(l)
+ for func, func_regex in TRACE_REGEX.items():
+ res = func_regex.match(line)
if res is not None:
calls[func] += 1
res = res.groupdict()
@@ -117,7 +124,7 @@ def parse(path="chattymalloc.txt", coll_size=True, req_size=None, nohist=False):
break
if not valid_line:
- print("\ninvalid line at", ln, ":", l)
+ print("\ninvalid line at", line_count, ":", line)
return hist, calls, np.array(requested_size)
@@ -125,7 +132,7 @@ def parse(path="chattymalloc.txt", coll_size=True, req_size=None, nohist=False):
def plot(path):
hist, calls, total_sizes = parse(path=path, req_size=None)
- plot_hist_ascii(path+".hist", hist, calls)
+ plot_hist_ascii(f"{path}.hist", hist, calls)
top5 = [t[1] for t in sorted([(n, s) for s, n in hist.items()])[-5:]]
del hist
@@ -140,9 +147,9 @@ def plot_profile(total_sizes, trace_path, plot_path, sizes):
plt.plot(x_vals, total_sizes / 1000, marker='',
linestyle='-', label="Total requested")
- for s in sizes:
- _, _, total_size = parse(path=trace_path, nohist=True, req_size=s)
- plt.plot(x_vals, total_size / 1000, label=s)
+ for size in sizes:
+ _, _, total_size = parse(path=trace_path, nohist=True, req_size=size)
+ plt.plot(x_vals, total_size / 1000, label=size)
plt.legend(loc="lower center")
plt.xlabel("Allocations")
@@ -159,33 +166,34 @@ def plot_hist_ascii(path, hist, calls):
total = sum(calls.values()) - calls["free"]
- with open(path, "w") as f:
- print("Total function calls:", total, file=f)
- for func in trace_regex:
- print(func, calls[func], file=f)
+ with open(path, "w") as hist_file:
+ print("Total function calls:", total, file=hist_file)
+ for func in TRACE_REGEX:
+ print(func, calls[func], file=hist_file)
- print(file=f)
+ print(file=hist_file)
- print("allocations <= 64", sum([n for s, n in hist.items() if s <= 64]), file=f)
- print("allocations <= 1024", sum([n for s, n in hist.items() if s <= 1024]), file=f)
- print("allocations <= 4096", sum([n for s, n in hist.items() if s <= 4096]), file=f)
- print(file=f)
+ print("allocations <= 64", sum([n for s, n in hist.items() if s <= 64]), file=hist_file)
+ print("allocations <= 1024", sum([n for s, n in hist.items() if s <= 1024]), file=hist_file)
+ print("allocations <= 4096", sum([n for s, n in hist.items() if s <= 4096]), file=hist_file)
+ print(file=hist_file)
- print("Histogram of sizes:", file=f)
+ print("Histogram of sizes:", file=hist_file)
sbins = sorted(bins)
binmaxlength = str(len(str(sbins[-1])) + 1)
amountmaxlength = str(len(str(sorted(bins.values())[-1])))
- for b in sbins:
- perc = bins[b]/total*100
+ for current_bin in sbins:
+ perc = bins[current_bin]/total*100
binsize = "{:<" + binmaxlength + "} - {:>" + binmaxlength + "}"
- print(binsize.format((b)*16, (b+1)*16-1), end=" ", file=f)
+ print(binsize.format((current_bin)*16, (current_bin+1)*16-1), end=" ", file=hist_file)
amount = "{:<" + amountmaxlength + "} {:.2f}% {}"
- print(amount.format(bins[b], perc, '*'*int(perc/2)), file=f)
+ print(amount.format(bins[current_bin], perc, '*'*int(perc/2)), file=hist_file)
if __name__ == "__main__":
if len(sys.argv) != 2:
- print("chattyparser: parse chattymalloc output and create size histogram and memory profile", file=sys.stderr)
+ print("chattyparser: parse chattymalloc output and",
+ "create size histogram and memory profile", file=sys.stderr)
print(f"Usage: {sys.argv[0]} chattymalloc-file", file=sys.stderr)
exit(1)