From 369ed6b2f825af9a98097b66fc9ef7d14c61f3f1 Mon Sep 17 00:00:00 2001 From: Mark Dewing Date: Thu, 5 Nov 2015 13:24:57 -0600 Subject: [PATCH] Display native code frames. Filter out native code frames inside the Python library. Eventually other libraries may be filtered out as well (ctypes.so, etc.) The default for 'vmprofshow' is now to show native frames (except under PyPy, in order to preserve existing behavior). The command line flag '--python_only' will limit the display to only the Python frames. Add a simple extension module for testing mixed Python and native stacks. --- tests/extension.prof | Bin 0 -> 822326 bytes tests/run_slow.py | 17 +++++++++++++++++ tests/slow_ext.cpp | 33 +++++++++++++++++++++++++++++++++ vmprof/addrspace.py | 30 +++++++++++++++++++++++++++--- vmprof/profiler.py | 3 +++ vmprof/show.py | 18 ++++++++++++++---- vmprof/stats.py | 4 ++-- 7 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 tests/extension.prof create mode 100644 tests/run_slow.py create mode 100644 tests/slow_ext.cpp diff --git a/tests/extension.prof b/tests/extension.prof new file mode 100644 index 0000000000000000000000000000000000000000..4398cb138a84a0258a1a1a47d7a296b61f76cb7e GIT binary patch literal 822326 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 literal 0 HcmV?d00001 diff --git a/tests/run_slow.py b/tests/run_slow.py new file mode 100644 index 00000000..0c5602d9 --- /dev/null +++ b/tests/run_slow.py @@ -0,0 +1,17 @@ + +# Test profiling of mixed Python / native code stacks + +from ctypes import * + +h = cdll.LoadLibrary("./slow_ext.so") + +def more_slow(): + h.slow2(10000) + +def slow_wrap(): + h.slow2(20000) + more_slow() + +if __name__ == '__main__': + slow_wrap() + diff --git a/tests/slow_ext.cpp b/tests/slow_ext.cpp new file mode 100644 index 00000000..809862c1 --- /dev/null +++ b/tests/slow_ext.cpp @@ -0,0 +1,33 @@ + +// Extension module with slow code +// Test profiling of native code +// +// Compile with: +// g++ -fPIC -g -O0 --shared -o slow_ext.so slow_ext.cpp + +#include + +extern "C" { +int slow1(int n); +int slow2(int n); +} + +int slow1(int n) +{ + int sum = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < 100000; j++) { + sum += i*j; + } + } + + return sum; +} + +int slow2(int n) +{ + return slow1(n); +} + + + diff --git a/vmprof/addrspace.py b/vmprof/addrspace.py index 9a315657..612cb884 100644 --- a/vmprof/addrspace.py +++ b/vmprof/addrspace.py @@ -38,6 +38,9 @@ class JittedVirtual(Hashable): class VirtualFrame(Hashable): pass +class NativeFrame(Hashable): + pass + class BaseMetaFrame(Hashable): def add_to_meta(self, meta): meta[self.name] = meta.get(self.name, 0) + 1 @@ -57,6 +60,16 @@ class WarmupFrame(BaseMetaFrame): class BlackholeWarmupFrame(WarmupFrame): name = 'blackhole' +def is_python_runtime(lib): + lib_path = [] + if lib and lib.name: + lib_path = lib.name.split('/') + + if lib_path and 'libpython' in lib_path[-1]: + return True + + return False + class AddressSpace(object): def __init__(self, libs): all = [(lib.start, lib) for lib in libs] @@ -114,7 +127,7 @@ def filter(self, profiles): filtered_profiles.append((current, prof[1])) return filtered_profiles - def _next_profile(self, lst, jit_frames, addr_set, interp_name, + def _filter_stack(self, lst, jit_frames, addr_set, interp_name, only_virtual): current = [] jitting = False @@ -136,17 +149,28 @@ def _next_profile(self, lst, jit_frames, addr_set, interp_name, current.append(JitAddr(jit_addr)) jitting = False continue + if addr in self.meta_data: current.append(self.meta_data[addr](addr)) - elif is_virtual or not only_virtual: + continue + + cls = None + if not only_virtual: + if not is_python_runtime(lib): + cls = NativeFrame + + if is_virtual: if jitting: cls = JittedVirtual else: cls = VirtualFrame + + if cls: if previous_virtual != addr: current.append(cls(addr)) previous_virtual = current[-1] addr_set.add(addr) + return current def filter_addr(self, profiles, only_virtual=True, @@ -159,7 +183,7 @@ def filter_addr(self, profiles, only_virtual=True, if len(prof[0]) < 5: skipped += 1 continue # broken profile - current = self._next_profile(prof[0], jit_frames, addr_set, + current = self._filter_stack(prof[0], jit_frames, addr_set, interp_name, only_virtual) if current: current.reverse() diff --git a/vmprof/profiler.py b/vmprof/profiler.py index b66d1688..c9f4cfcf 100644 --- a/vmprof/profiler.py +++ b/vmprof/profiler.py @@ -30,6 +30,9 @@ def read_profile(prof_filename, lib_cache={}, extra_libs=None, period, profiles, virtual_symbols, libs, interp_name = read_prof(prof) + if interp_name == 'pypy': + virtual_only = True + if not virtual_only or include_extra_info: exe_name = libs[0].name for lib in libs: diff --git a/vmprof/show.py b/vmprof/show.py index 5482d2a6..18fc5402 100644 --- a/vmprof/show.py +++ b/vmprof/show.py @@ -31,15 +31,17 @@ def __init__(self, prune_percent=None, prune_level=None, indent=None): self._prune_level = prune_level or 1000 self._indent = indent or 2 - def show(self, profile): + def show(self, profile, virtual_only): """ Read and display a vmprof profile file. :param profile: The filename of the vmprof profile file to display. :type profile: str + :param virtual_only: Display only Python frames + :type profile: bool """ try: - stats = vmprof.read_profile(profile, virtual_only=True, include_extra_info=True) + stats = vmprof.read_profile(profile, virtual_only=virtual_only, include_extra_info=True) except Exception as e: print("Fatal: could not read vmprof profile file '{}': {}".format(profile, e)) return @@ -91,6 +93,13 @@ def print_node(parent, node, level): p3 = click.style(funname, fg='white', bold=False) p5 = click.style("{:>2}".format(level), fg='red', bold=False) + elif parts == 0: + # Native code frame + funname = node.name + p2 = click.style(funname, fg='green', bold=True) + p2b = click.style(('.' * level * self._indent), fg='green', bold=False) + p3 = "" + else: raise Exception("fail!") @@ -108,9 +117,10 @@ def print_node(parent, node, level): @click.option('--prune_percent', type=float, default=0, help='The indention per level within the call graph.') @click.option('--prune_level', type=int, default=None, help='Prune output of a profile stats node when CPU.') @click.option('--indent', type=int, default=2, help='The indention per level within the call graph.') -def main(profile, prune_percent, prune_level, indent): +@click.option('--python_only', is_flag=True, help='Show only Python frames.') +def main(profile, prune_percent, prune_level, indent, python_only): pp = PrettyPrinter(prune_percent=prune_percent, prune_level=prune_level, indent=indent) - pp.show(profile) + pp.show(profile, virtual_only=python_only) if __name__ == '__main__': diff --git a/vmprof/stats.py b/vmprof/stats.py index 7d57c11d..69cae3fe 100644 --- a/vmprof/stats.py +++ b/vmprof/stats.py @@ -1,6 +1,6 @@ import six from vmprof.addrspace import JittedVirtual, JitAddr, VirtualFrame,\ - BaseMetaFrame + BaseMetaFrame, NativeFrame class EmptyProfileFile(Exception): pass @@ -106,7 +106,7 @@ def get_tree(self): for i in range(1, len(profile[0])): addr = profile[0][i] name = self._get_name(addr) - if not isinstance(addr, (VirtualFrame, JittedVirtual)): + if not isinstance(addr, (VirtualFrame, JittedVirtual, NativeFrame)): continue last_virtual = addr last_virtual_pos = i