Commit a7cc6c21 authored by Sasha Goldshtein's avatar Sasha Goldshtein

Refactored Time class and added usage example

parent 5cd2e0bc
......@@ -7,20 +7,48 @@ import subprocess
import ctypes
import os
class Time(object):
# Adapted from http://stackoverflow.com/a/1205762
CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h>
class timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]
librt = ctypes.CDLL('librt.so.1', use_errno=True)
clock_gettime = librt.clock_gettime
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
@staticmethod
def monotonic_time():
"""monotonic_time()
Returns the reading of the monotonic clock, in nanoseconds.
"""
t = Time.timespec()
if Time.clock_gettime(Time.CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0:
errno_ = ctypes.get_errno()
raise OSError(errno_, os.strerror(errno_))
return t.tv_sec*1e9 + t.tv_nsec
examples = """
EXAMPLES:
memleak.py -p $(pidof allocs)
./memleak.py -p $(pidof allocs)
Trace allocations and display a summary of "leaked" (outstanding)
allocations every 5 seconds
memleak.py -p $(pidof allocs) -t
./memleak.py -p $(pidof allocs) -t
Trace allocations and display each individual call to malloc/free
memleak.py -p $(pidof allocs) -a -i 10
./memleak.py -p $(pidof allocs) -a -i 10
Trace allocations and display allocated addresses, sizes, and stacks
every 10 seconds for outstanding allocations
memleak.py
./memleak.py
Trace allocations in kernel mode and display a summary of outstanding
allocations every 5 seconds
./memleak.py -o 60000
Trace allocations in kernel mode and display a summary of outstanding
allocations that are at least one minute (60 seconds) old
"""
description = """
......@@ -29,12 +57,20 @@ Supports both user-mode allocations made with malloc/free and kernel-mode
allocations made with kmalloc/kfree.
"""
parser = argparse.ArgumentParser(description=description, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=examples)
parser.add_argument("-p", "--pid", help="the PID to trace; if not specified, trace kernel allocs")
parser.add_argument("-t", "--trace", action="store_true", help="print trace messages for each alloc/free call")
parser.add_argument("-i", "--interval", default=5, help="interval in seconds to print outstanding allocations")
parser.add_argument("-a", "--show-allocs", default=False, action="store_true", help="show allocation addresses and sizes as well as call stacks")
parser.add_argument("-o", "--older", default=500, help="prune allocations younger than this age in milliseconds")
parser = argparse.ArgumentParser(description=description,
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=examples)
parser.add_argument("-p", "--pid",
help="the PID to trace; if not specified, trace kernel allocs")
parser.add_argument("-t", "--trace", action="store_true",
help="print trace messages for each alloc/free call")
parser.add_argument("-i", "--interval", default=5,
help="interval in seconds to print outstanding allocations")
parser.add_argument("-a", "--show-allocs", default=False, action="store_true",
help="show allocation addresses and sizes as well as call stacks")
parser.add_argument("-o", "--older", default=500,
help="prune allocations younger than this age in milliseconds")
# TODO Run a command and trace that command (-c)
args = parser.parse_args()
......@@ -106,7 +142,7 @@ def decode_addr(code_ranges, addr):
for binary, (start, end) in code_ranges.items():
if addr >= start and addr <= end:
offset = addr - start if binary.endswith(".so") else addr
return "%s %s" % (binary, decode_sym(binary, offset))
return "%s [%s]" % (decode_sym(binary, offset), binary)
return "%x" % addr
def decode_stack(info):
......@@ -116,45 +152,25 @@ def decode_stack(info):
for i in range(0, info.num_frames):
addr = info.callstack[i]
if kernel_trace:
stack += " %s (%x) ;" % (bpf_program.ksym(addr), addr)
stack += " %s [kernel] (%x) ;" % (bpf_program.ksym(addr), addr)
else:
stack += " %s (%x) ;" % (decode_addr(code_ranges, addr), addr)
return stack
# Adapted from http://stackoverflow.com/a/1205762
CLOCK_MONOTONIC_RAW = 4 # see <linux/time.h>
class timespec(ctypes.Structure):
_fields_ = [
('tv_sec', ctypes.c_long),
('tv_nsec', ctypes.c_long)
]
librt = ctypes.CDLL('librt.so.1', use_errno=True)
clock_gettime = librt.clock_gettime
clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(timespec)]
def monotonic_time():
t = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW , ctypes.pointer(t)) != 0:
errno_ = ctypes.get_errno()
raise OSError(errno_, os.strerror(errno_))
return t.tv_sec*1e9 + t.tv_nsec
def print_outstanding():
stacks = {}
print("*** Outstanding allocations:")
allocs = bpf_program.get_table("allocs")
for address, info in sorted(allocs.items(), key=lambda a: -a[1].size):
if monotonic_time() - min_age_ns < info.timestamp_ns:
if Time.monotonic_time() - min_age_ns < info.timestamp_ns:
continue
stack = decode_stack(info)
if stack in stacks: stacks[stack] += info.size
else: stacks[stack] = info.size
if stack in stacks: stacks[stack] = (stacks[stack][0] + 1, stacks[stack][1] + info.size)
else: stacks[stack] = (1, info.size)
if args.show_allocs:
print("\taddr = %x size = %s" % (address.value, info.size))
for stack, size in sorted(stacks.items(), key=lambda s: -s[1]):
print("\t%d bytes allocated from stack\n\t\t%s" % (size, stack.replace(";", "\n\t\t")))
for stack, (count, size) in sorted(stacks.items(), key=lambda s: -s[1][1]):
print("\t%d bytes in %d allocations from stack\n\t\t%s" % (size, count, stack.replace(";", "\n\t\t")))
while True:
if trace_all:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment