diff --git a/selfdrive/debug/profiling/py-spy/profile.sh b/selfdrive/debug/profiling/py-spy/profile.sh new file mode 100755 index 000000000..2fdbe8d16 --- /dev/null +++ b/selfdrive/debug/profiling/py-spy/profile.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash +set -e + +cd "$(dirname "$0")" + +# find process with name passed in (excluding this process) +for PID in $(pgrep -f $1); do + if [ "$PID" != "$$" ]; then + ps -p $PID -o args + TRACE_PID=$PID + break + fi +done + +if [ -z "$TRACE_PID" ]; then + echo "could not find PID for $1" + exit 1 +fi + +sudo env PATH=$PATH py-spy record -d 5 -o /tmp/perf$TRACE_PID.svg -p $TRACE_PID && +google-chrome /tmp/perf$TRACE_PID.svg diff --git a/selfdrive/debug/profiling/pyflame/build.txt b/selfdrive/debug/profiling/pyflame/build.txt deleted file mode 100644 index 72687b6e5..000000000 --- a/selfdrive/debug/profiling/pyflame/build.txt +++ /dev/null @@ -1,9 +0,0 @@ -https://github.com/brendangregg/FlameGraph @ v1.0 - -phone: -git clone git@github.com:uber/pyflame.git -cd pyflame -git apply < ../fix-aarch64.patch -./autogen.sh -./configure -make diff --git a/selfdrive/debug/profiling/pyflame/fix-aarch64.patch b/selfdrive/debug/profiling/pyflame/fix-aarch64.patch deleted file mode 100644 index 1326f5e87..000000000 --- a/selfdrive/debug/profiling/pyflame/fix-aarch64.patch +++ /dev/null @@ -1,339 +0,0 @@ -diff --git a/.travis.yml b/.travis.yml -index 9cc00ec..8a2d6c7 100644 ---- a/.travis.yml -+++ b/.travis.yml -@@ -12,6 +12,7 @@ env: - - PYVERSION=python3.4 - - PYVERSION=python3.5 - - PYVERSION=python3.6 -+ - PYVERSION=python3.7 - - addons: - apt: -diff --git a/configure.ac b/configure.ac -index 0858fc3..8827b3a 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -23,7 +23,7 @@ AS_IF([test x"$host_cpu" = xx86_64], - ], - [AC_MSG_NOTICE([Threading support will be disabled (only works on x86-64)]) - AC_DEFINE([ENABLE_THREADS], [0], [Threads are enabled.]) -- AC_DEFINE([USE_ELF64], [0], [Expect 64-bit ELF symbols.]) -+ AC_DEFINE([USE_ELF64], [1], [Expect 64-bit ELF symbols.]) - ]) - - AC_DEFINE_UNQUOTED([HOST_CPU], ["$host_cpu"], [CPU of target architecture.]) -@@ -111,7 +111,12 @@ PKG_CHECK_MODULES([PY36], [python-3.6], [enable_py36="yes"], [AC_MSG_WARN([Build - AM_CONDITIONAL([ENABLE_PY36], [test x"$enable_py36" = xyes]) - AM_COND_IF([ENABLE_PY36], [AC_DEFINE([ENABLE_PY36], [1], [Python 3.6 will be enabled])]) - --AS_IF([test x"$enable_py26" = xyes -o x"$enable_py34" = xyes -o x"$enable_py36" = xyes], -+enable_py37=no -+PKG_CHECK_MODULES([PY37], [python-3.7], [enable_py37="yes"], [AC_MSG_WARN([Building without Python 3.7 support])]) -+AM_CONDITIONAL([ENABLE_PY37], [test x"$enable_py37" = xyes]) -+AM_COND_IF([ENABLE_PY37], [AC_DEFINE([ENABLE_PY37], [1], [Python 3.7 will be enabled])]) -+ -+AS_IF([test x"$enable_py26" = xyes -o x"$enable_py34" = xyes -o x"$enable_py36" = xyes -o x"$enable_py37" = xyes], - [AC_MSG_NOTICE([Found at least one copy of Python.h])], - [AC_MSG_ERROR([Failed to find a supported Python.h])] - ) -@@ -127,7 +132,8 @@ echo - echo " with threads = $enable_threads" - echo " with Python 2.6/7 = $enable_py26" - echo " with Python 3.4/5 = $enable_py34" --echo " with Python 3.6+ = $enable_py36" -+echo " with Python 3.6 = $enable_py36" -+echo " with Python 3.7+ = $enable_py37" - echo - echo " CXX = $CXX" - echo " CXXFLAGS = $CXXFLAGS" -diff --git a/docs/faq.rst b/docs/faq.rst -index 6fe8d1e..da6cec8 100644 ---- a/docs/faq.rst -+++ b/docs/faq.rst -@@ -7,7 +7,7 @@ What Python Versions Are Supported? - Python 2 is tested with Python 2.6 and 2.7. Earlier versions of Python 2 are - likely to work as well, but have not been tested. - --Python 3 is tested with Python 3.4, 3.5, and 3.6. Python 3.6 introduces a new -+Python 3 is tested with Python 3.4, 3.5, 3.6 and 3.7 Python 3.6 introduces a new - ABI for the ``PyCodeObject`` type, so Pyflame only supports the Python 3 - versions that header files were available for when Pyflame was compiled. - -diff --git a/docs/man.md b/docs/man.md -index 1aa8d25..538a764 100644 ---- a/docs/man.md -+++ b/docs/man.md -@@ -87,7 +87,7 @@ The following options are less commonly used. - cases when profiling embedded Python builds (e.g. uWSGI), and only if - pyflame doesn't automatically detect the correct ABI. *VERSION* should be a - two digit integer consisting of the Python major and minor version, e.g. 27 -- for Python 2.7 or 36 for Python 3.6. -+ for Python 2.7, 36 for Python 3.6 or 37 for Python 3.7. - - **--flamechart** - : Print the timestamp for each stack. This is useful for generating "flame -diff --git a/docs/pyflame.man b/docs/pyflame.man -index 971a1de..7fdeb85 100644 ---- a/docs/pyflame.man -+++ b/docs/pyflame.man -@@ -119,7 +119,7 @@ uWSGI), and only if pyflame doesn\[aq]t automatically detect the correct - ABI. - \f[I]VERSION\f[] should be a two digit integer consisting of the Python - major and minor version, e.g. --27 for Python 2.7 or 36 for Python 3.6. -+27 for Python 2.7, 36 for Python 3.6 or 37 for Python 3.7. - .RS - .RE - .TP -diff --git a/src/Makefile.am b/src/Makefile.am -index 11d8ab6..4a62bdb 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -2,11 +2,11 @@ - # - # The way this code is structured is the code that know about Python interpreter - # internals is in frob.cc. This file isn't compiled directly, instead there are --# files frob{26,34,36}.cc that define preprocessor macros to compile frob.cc for -+# files frob{26,34,36,37}.cc that define preprocessor macros to compile frob.cc for - # different Python ABIs. - # - # The libtool magic here makes it so that frob26.cc is compiled with python2 --# flags, and frob3{4,6}.cc are compiled with python3.4/3.6 flags. -+# flags, and frob3{4,6,7}.cc are compiled with python3.4/3.6/3.7 flags. - - bin_PROGRAMS = pyflame - pyflame_SOURCES = aslr.cc frame.cc thread.cc namespace.cc posix.cc prober.cc ptrace.cc pyflame.cc pyfrob.cc symbol.cc -@@ -34,3 +34,11 @@ libfrob36_la_CXXFLAGS = $(PY36_CFLAGS) - noinst_LTLIBRARIES += libfrob36.la - pyflame_LDADD += libfrob36.la - endif -+ -+if ENABLE_PY37 -+libfrob37_la_SOURCES = frob37.cc offset37.c -+libfrob37_la_CFLAGS = $(PY37_CFLAGS) -+libfrob37_la_CXXFLAGS = $(PY37_CFLAGS) -+noinst_LTLIBRARIES += libfrob37.la -+pyflame_LDADD += libfrob37.la -+endif -diff --git a/src/frob.cc b/src/frob.cc -index 6fcff39..c1c5b30 100644 ---- a/src/frob.cc -+++ b/src/frob.cc -@@ -17,6 +17,7 @@ - // information. - - #include -+ - #include - - #if PYFLAME_PY_VERSION >= 34 -@@ -89,6 +90,22 @@ unsigned long ByteData(unsigned long addr) { - return addr + offsetof(PyBytesObject, ob_sval); - } - -+#elif PYFLAME_PY_VERSION == 37 -+namespace py37 { -+std::string StringDataPython3(pid_t pid, unsigned long addr); -+ -+unsigned long StringSize(unsigned long addr) { -+ return addr + offsetof(PyVarObject, ob_size); -+} -+ -+std::string StringData(pid_t pid, unsigned long addr) { -+ return StringDataPython3(pid, addr); -+} -+ -+unsigned long ByteData(unsigned long addr) { -+ return addr + offsetof(PyBytesObject, ob_sval); -+} -+ - #else - static_assert(false, "uh oh, bad PYFLAME_PY_VERSION"); - #endif -diff --git a/src/prober.cc b/src/prober.cc -index a691c83..c653357 100644 ---- a/src/prober.cc -+++ b/src/prober.cc -@@ -64,7 +64,7 @@ static const char usage_str[] = - " -x, --exclude-idle Exclude idle time from statistics\n" - "\n" - "Advanced Options:\n" -- " --abi Force a particular Python ABI (26, 34, 36)\n" -+ " --abi Force a particular Python ABI (26, 34, 36, 37)\n" - " --flamechart Include timestamps for generating Chrome " - "\"flamecharts\"\n"); - -@@ -79,6 +79,9 @@ static const int build_abis[] = { - #ifdef ENABLE_PY36 - 36, - #endif -+#ifdef ENABLE_PY37 -+ 37, -+#endif - }; - - static_assert(sizeof(build_abis) > 0, "No Python ABIs detected!"); -@@ -221,6 +224,9 @@ int Prober::ParseOpts(int argc, char **argv) { - case 36: - abi_ = PyABI::Py36; - break; -+ case 37: -+ abi_ = PyABI::Py37; -+ break; - default: - std::cerr << "Unknown or unsupported ABI version: " << abi_version - << "\n"; -diff --git a/src/ptrace.cc b/src/ptrace.cc -index 610fabb..85483ad 100644 ---- a/src/ptrace.cc -+++ b/src/ptrace.cc -@@ -122,6 +122,7 @@ void PtraceInterrupt(pid_t pid) { - DoWait(pid); - } - -+#if 0 - user_regs_struct PtraceGetRegs(pid_t pid) { - user_regs_struct regs; - if (ptrace(PTRACE_GETREGS, pid, 0, ®s)) { -@@ -139,6 +140,7 @@ void PtraceSetRegs(pid_t pid, user_regs_struct regs) { - throw PtraceException(ss.str()); - } - } -+#endif - - void PtracePoke(pid_t pid, unsigned long addr, long data) { - if (ptrace(PTRACE_POKEDATA, pid, addr, (void *)data)) { -diff --git a/src/pyfrob.cc b/src/pyfrob.cc -index 7c0588d..b280d96 100644 ---- a/src/pyfrob.cc -+++ b/src/pyfrob.cc -@@ -14,6 +14,7 @@ - - #include "./pyfrob.h" - -+#include - #include - #include - -@@ -25,6 +26,8 @@ - #include "./ptrace.h" - #include "./symbol.h" - -+extern size_t PYFLAME_PYTHON37_OFFSET; -+ - #define FROB_FUNCS \ - std::vector GetThreads(pid_t pid, PyAddresses addr, \ - bool enable_threads); -@@ -126,11 +129,20 @@ FROB_FUNCS - } - #endif - -+#ifdef ENABLE_PY37 -+namespace py37 { -+FROB_FUNCS -+extern size_t tstate_offset; -+} -+#endif -+ - // Fill the addrs_ member - int PyFrob::set_addrs_(PyABI *abi) { - Namespace ns(pid_); - try { - addrs_ = Addrs(pid_, &ns, abi); -+ if (*abi >= PyABI::Py37) -+ addrs_.tstate_addr += PYFLAME_PYTHON37_OFFSET; - } catch (const SymbolException &exc) { - return 1; - } -@@ -172,6 +184,11 @@ int PyFrob::DetectABI(PyABI abi) { - case PyABI::Py36: - get_threads_ = py36::GetThreads; - break; -+#endif -+#ifdef ENABLE_PY37 -+ case PyABI::Py37: -+ get_threads_ = py37::GetThreads; -+ break; - #endif - default: - std::ostringstream os; -@@ -198,4 +215,5 @@ std::string PyFrob::Status() const { - std::vector PyFrob::GetThreads(void) const { - return get_threads_(pid_, addrs_, enable_threads_); - } -+ - } // namespace pyflame -diff --git a/src/symbol.cc b/src/symbol.cc -index 39c3e81..3a6123c 100644 ---- a/src/symbol.cc -+++ b/src/symbol.cc -@@ -137,31 +137,43 @@ PyABI ELF::WalkTable(int sym, int str, PyAddresses *addrs) { - reinterpret_cast(p() + s->sh_offset + i * s->sh_entsize); - const char *name = - reinterpret_cast(p() + d->sh_offset + sym->st_name); -- if (!addrs->tstate_addr && strcmp(name, "_PyThreadState_Current") == 0) { -- addrs->tstate_addr = static_cast(sym->st_value); -- } else if (!addrs->interp_head_addr && strcmp(name, "interp_head") == 0) { -- addrs->interp_head_addr = static_cast(sym->st_value); -- } else if (!addrs->interp_head_addr && -- strcmp(name, "PyInterpreterState_Head") == 0) { -- addrs->interp_head_fn_addr = static_cast(sym->st_value); -- } else if (!have_abi) { -+ if (!addrs->tstate_addr) { -+ if (strcmp(name, "_PyThreadState_Current") == 0) { -+ addrs->tstate_addr = static_cast(sym->st_value); -+ } else if (strcmp(name, "_PyRuntime") == 0) { -+ addrs->tstate_addr = static_cast(sym->st_value); -+ } -+ } -+ -+ if (!addrs->interp_head_addr) { -+ if (strcmp(name, "interp_head") == 0) { -+ addrs->interp_head_addr = static_cast(sym->st_value); -+ } else if (strcmp(name, "PyInterpreterState_Head") == 0) { -+ addrs->interp_head_fn_addr = static_cast(sym->st_value); -+ } -+ } -+ -+ if (!have_abi) { - if (strcmp(name, "PyString_Type") == 0) { - // If we find PyString_Type, this is some kind of Python 2. - have_abi = true; - abi = PyABI::Py26; -- } else if (strcmp(name, "PyBytes_Type") == 0) { -+ } else if (strcmp(name, "PyBytes_Type") == 0 && abi < PyABI::Py34) { - // If we find PyBytes_Type, it's Python 3. Continue looping though, in -- // case we see a Python 3.6 symbol. -+ // case we see a Python 3.6+ symbol. - abi = PyABI::Py34; - } else if (strcmp(name, "_PyEval_RequestCodeExtraIndex") == 0 || - strcmp(name, "_PyCode_GetExtra") == 0 || - strcmp(name, "_PyCode_SetExtra") == 0) { - // Symbols added for Python 3.6, see: - // https://www.python.org/dev/peps/pep-0523/ -- have_abi = true; - abi = PyABI::Py36; -+ } else if (strcmp(name, "_PyRuntime") == 0) { -+ have_abi = true; -+ abi = PyABI::Py37; - } - } -+ - } - return abi; - } -diff --git a/src/symbol.h b/src/symbol.h -index bb92b9a..818424d 100644 ---- a/src/symbol.h -+++ b/src/symbol.h -@@ -53,7 +53,8 @@ enum class PyABI { - Unknown = 0, // Unknown Python ABI - Py26 = 26, // ABI for Python 2.6/2.7 - Py34 = 34, // ABI for Python 3.4/3.5 -- Py36 = 36 // ABI for Python 3.6 -+ Py36 = 36, // ABI for Python 3.6 -+ Py37 = 37 // ABI for Python 3.7 - }; - - // Symbols diff --git a/selfdrive/debug/profiling/pyflame/flame.sh b/selfdrive/debug/profiling/pyflame/flame.sh deleted file mode 100755 index f41051eda..000000000 --- a/selfdrive/debug/profiling/pyflame/flame.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -sudo $HOME/one/external/pyflame/pyflame -s 5 -o /tmp/perf$1.txt -p $1 && -$HOME/one/external/pyflame/flamegraph.pl /tmp/perf$1.txt > /tmp/perf$1.svg && -google-chrome /tmp/perf$1.svg diff --git a/selfdrive/debug/profiling/pyflame/flamegraph.pl b/selfdrive/debug/profiling/pyflame/flamegraph.pl deleted file mode 100755 index 4723421e5..000000000 --- a/selfdrive/debug/profiling/pyflame/flamegraph.pl +++ /dev/null @@ -1,1125 +0,0 @@ -#!/usr/bin/perl -w -# -# flamegraph.pl flame stack grapher. -# -# This takes stack samples and renders a call graph, allowing hot functions -# and codepaths to be quickly identified. Stack samples can be generated using -# tools such as DTrace, perf, SystemTap, and Instruments. -# -# USAGE: ./flamegraph.pl [options] input.txt > graph.svg -# -# grep funcA input.txt | ./flamegraph.pl [options] > graph.svg -# -# Then open the resulting .svg in a web browser, for interactivity: mouse-over -# frames for info, click to zoom, and ctrl-F to search. -# -# Options are listed in the usage message (--help). -# -# The input is stack frames and sample counts formatted as single lines. Each -# frame in the stack is semicolon separated, with a space and count at the end -# of the line. These can be generated for Linux perf script output using -# stackcollapse-perf.pl, for DTrace using stackcollapse.pl, and for other tools -# using the other stackcollapse programs. Example input: -# -# swapper;start_kernel;rest_init;cpu_idle;default_idle;native_safe_halt 1 -# -# An optional extra column of counts can be provided to generate a differential -# flame graph of the counts, colored red for more, and blue for less. This -# can be useful when using flame graphs for non-regression testing. -# See the header comment in the difffolded.pl program for instructions. -# -# The input functions can optionally have annotations at the end of each -# function name, following a precedent by some tools (Linux perf's _[k]): -# _[k] for kernel -# _[i] for inlined -# _[j] for jit -# _[w] for waker -# Some of the stackcollapse programs support adding these annotations, eg, -# stackcollapse-perf.pl --kernel --jit. They are used merely for colors by -# some palettes, eg, flamegraph.pl --color=java. -# -# The output flame graph shows relative presence of functions in stack samples. -# The ordering on the x-axis has no meaning; since the data is samples, time -# order of events is not known. The order used sorts function names -# alphabetically. -# -# While intended to process stack samples, this can also process stack traces. -# For example, tracing stacks for memory allocation, or resource usage. You -# can use --title to set the title to reflect the content, and --countname -# to change "samples" to "bytes" etc. -# -# There are a few different palettes, selectable using --color. By default, -# the colors are selected at random (except for differentials). Functions -# called "-" will be printed gray, which can be used for stack separators (eg, -# between user and kernel stacks). -# -# HISTORY -# -# This was inspired by Neelakanth Nadgir's excellent function_call_graph.rb -# program, which visualized function entry and return trace events. As Neel -# wrote: "The output displayed is inspired by Roch's CallStackAnalyzer which -# was in turn inspired by the work on vftrace by Jan Boerhout". See: -# https://blogs.oracle.com/realneel/entry/visualizing_callstacks_via_dtrace_and -# -# Copyright 2016 Netflix, Inc. -# Copyright 2011 Joyent, Inc. All rights reserved. -# Copyright 2011 Brendan Gregg. All rights reserved. -# -# CDDL HEADER START -# -# The contents of this file are subject to the terms of the -# Common Development and Distribution License (the "License"). -# You may not use this file except in compliance with the License. -# -# You can obtain a copy of the license at docs/cddl1.txt or -# http://opensource.org/licenses/CDDL-1.0. -# See the License for the specific language governing permissions -# and limitations under the License. -# -# When distributing Covered Code, include this CDDL HEADER in each -# file and include the License file at docs/cddl1.txt. -# If applicable, add the following below this CDDL HEADER, with the -# fields enclosed by brackets "[]" replaced with your own identifying -# information: Portions Copyright [yyyy] [name of copyright owner] -# -# CDDL HEADER END -# -# 11-Oct-2014 Adrien Mahieux Added zoom. -# 21-Nov-2013 Shawn Sterling Added consistent palette file option -# 17-Mar-2013 Tim Bunce Added options and more tunables. -# 15-Dec-2011 Dave Pacheco Support for frames with whitespace. -# 10-Sep-2011 Brendan Gregg Created this. - -use strict; - -use Getopt::Long; - -use open qw(:std :utf8); - -# tunables -my $encoding; -my $fonttype = "Verdana"; -my $imagewidth = 1200; # max width, pixels -my $frameheight = 16; # max height is dynamic -my $fontsize = 12; # base text size -my $fontwidth = 0.59; # avg width relative to fontsize -my $minwidth = 0.1; # min function width, pixels -my $nametype = "Function:"; # what are the names in the data? -my $countname = "samples"; # what are the counts in the data? -my $colors = "hot"; # color theme -my $bgcolor1 = "#eeeeee"; # background color gradient start -my $bgcolor2 = "#eeeeb0"; # background color gradient stop -my $nameattrfile; # file holding function attributes -my $timemax; # (override the) sum of the counts -my $factor = 1; # factor to scale counts by -my $hash = 0; # color by function name -my $palette = 0; # if we use consistent palettes (default off) -my %palette_map; # palette map hash -my $pal_file = "palette.map"; # palette map file name -my $stackreverse = 0; # reverse stack order, switching merge end -my $inverted = 0; # icicle graph -my $negate = 0; # switch differential hues -my $titletext = ""; # centered heading -my $titledefault = "Flame Graph"; # overwritten by --title -my $titleinverted = "Icicle Graph"; # " " -my $searchcolor = "rgb(230,0,230)"; # color for search highlighting -my $notestext = ""; # embedded notes in SVG -my $subtitletext = ""; # second level title (optional) -my $help = 0; - -sub usage { - die < outfile.svg\n - --title TEXT # change title text - --subtitle TEXT # second level title (optional) - --width NUM # width of image (default 1200) - --height NUM # height of each frame (default 16) - --minwidth NUM # omit smaller functions (default 0.1 pixels) - --fonttype FONT # font type (default "Verdana") - --fontsize NUM # font size (default 12) - --countname TEXT # count type label (default "samples") - --nametype TEXT # name type label (default "Function:") - --colors PALETTE # set color palette. choices are: hot (default), mem, - # io, wakeup, chain, java, js, perl, red, green, blue, - # aqua, yellow, purple, orange - --hash # colors are keyed by function name hash - --cp # use consistent palette (palette.map) - --reverse # generate stack-reversed flame graph - --inverted # icicle graph - --negate # switch differential hues (blue<->red) - --notes TEXT # add notes comment in SVG (for debugging) - --help # this message - - eg, - $0 --title="Flame Graph: malloc()" trace.txt > graph.svg -USAGE_END -} - -GetOptions( - 'fonttype=s' => \$fonttype, - 'width=i' => \$imagewidth, - 'height=i' => \$frameheight, - 'encoding=s' => \$encoding, - 'fontsize=f' => \$fontsize, - 'fontwidth=f' => \$fontwidth, - 'minwidth=f' => \$minwidth, - 'title=s' => \$titletext, - 'subtitle=s' => \$subtitletext, - 'nametype=s' => \$nametype, - 'countname=s' => \$countname, - 'nameattr=s' => \$nameattrfile, - 'total=s' => \$timemax, - 'factor=f' => \$factor, - 'colors=s' => \$colors, - 'hash' => \$hash, - 'cp' => \$palette, - 'reverse' => \$stackreverse, - 'inverted' => \$inverted, - 'negate' => \$negate, - 'notes=s' => \$notestext, - 'help' => \$help, -) or usage(); -$help && usage(); - -# internals -my $ypad1 = $fontsize * 3; # pad top, include title -my $ypad2 = $fontsize * 2 + 10; # pad bottom, include labels -my $ypad3 = $fontsize * 2; # pad top, include subtitle (optional) -my $xpad = 10; # pad lefm and right -my $framepad = 1; # vertical padding for frames -my $depthmax = 0; -my %Events; -my %nameattr; - -if ($titletext eq "") { - unless ($inverted) { - $titletext = $titledefault; - } else { - $titletext = $titleinverted; - } -} - -if ($nameattrfile) { - # The name-attribute file format is a function name followed by a tab then - # a sequence of tab separated name=value pairs. - open my $attrfh, $nameattrfile or die "Can't read $nameattrfile: $!\n"; - while (<$attrfh>) { - chomp; - my ($funcname, $attrstr) = split /\t/, $_, 2; - die "Invalid format in $nameattrfile" unless defined $attrstr; - $nameattr{$funcname} = { map { split /=/, $_, 2 } split /\t/, $attrstr }; - } -} - -if ($notestext =~ /[<>]/) { - die "Notes string can't contain < or >" -} - -# background colors: -# - yellow gradient: default (hot, java, js, perl) -# - blue gradient: mem, chain -# - gray gradient: io, wakeup, flat colors (red, green, blue, ...) -if ($colors eq "mem" or $colors eq "chain") { - $bgcolor1 = "#eeeeee"; $bgcolor2 = "#e0e0ff"; -} -if ($colors =~ /^(io|wakeup|red|green|blue|aqua|yellow|purple|orange)$/) { - $bgcolor1 = "#f8f8f8"; $bgcolor2 = "#e8e8e8"; -} - -# SVG functions -{ package SVG; - sub new { - my $class = shift; - my $self = {}; - bless ($self, $class); - return $self; - } - - sub header { - my ($self, $w, $h) = @_; - my $enc_attr = ''; - if (defined $encoding) { - $enc_attr = qq{ encoding="$encoding"}; - } - $self->{svg} .= < - - - - -SVG - } - - sub include { - my ($self, $content) = @_; - $self->{svg} .= $content; - } - - sub colorAllocate { - my ($self, $r, $g, $b) = @_; - return "rgb($r,$g,$b)"; - } - - sub group_start { - my ($self, $attr) = @_; - - my @g_attr = map { - exists $attr->{$_} ? sprintf(qq/$_="%s"/, $attr->{$_}) : () - } qw(class style onmouseover onmouseout onclick); - push @g_attr, $attr->{g_extra} if $attr->{g_extra}; - $self->{svg} .= sprintf qq/\n/, join(' ', @g_attr); - - $self->{svg} .= sprintf qq/%s<\/title>/, $attr->{title} - if $attr->{title}; # should be first element within g container - - if ($attr->{href}) { - my @a_attr; - push @a_attr, sprintf qq/xlink:href="%s"/, $attr->{href} if $attr->{href}; - # default target=_top else links will open within SVG - push @a_attr, sprintf qq/target="%s"/, $attr->{target} || "_top"; - push @a_attr, $attr->{a_extra} if $attr->{a_extra}; - $self->{svg} .= sprintf qq//, join(' ', @a_attr); - } - } - - sub group_end { - my ($self, $attr) = @_; - $self->{svg} .= qq/<\/a>\n/ if $attr->{href}; - $self->{svg} .= qq/<\/g>\n/; - } - - sub filledRectangle { - my ($self, $x1, $y1, $x2, $y2, $fill, $extra) = @_; - $x1 = sprintf "%0.1f", $x1; - $x2 = sprintf "%0.1f", $x2; - my $w = sprintf "%0.1f", $x2 - $x1; - my $h = sprintf "%0.1f", $y2 - $y1; - $extra = defined $extra ? $extra : ""; - $self->{svg} .= qq/\n/; - } - - sub stringTTF { - my ($self, $color, $font, $size, $angle, $x, $y, $str, $loc, $extra) = @_; - $x = sprintf "%0.2f", $x; - $loc = defined $loc ? $loc : "left"; - $extra = defined $extra ? $extra : ""; - $self->{svg} .= qq/$str<\/text>\n/; - } - - sub svg { - my $self = shift; - return "$self->{svg}\n"; - } - 1; -} - -sub namehash { - # Generate a vector hash for the name string, weighting early over - # later characters. We want to pick the same colors for function - # names across different flame graphs. - my $name = shift; - my $vector = 0; - my $weight = 1; - my $max = 1; - my $mod = 10; - # if module name present, trunc to 1st char - $name =~ s/.(.*?)`//; - foreach my $c (split //, $name) { - my $i = (ord $c) % $mod; - $vector += ($i / ($mod++ - 1)) * $weight; - $max += 1 * $weight; - $weight *= 0.70; - last if $mod > 12; - } - return (1 - $vector / $max) -} - -sub color { - my ($type, $hash, $name) = @_; - my ($v1, $v2, $v3); - - if ($hash) { - $v1 = namehash($name); - $v2 = $v3 = namehash(scalar reverse $name); - } else { - $v1 = rand(1); - $v2 = rand(1); - $v3 = rand(1); - } - - # theme palettes - if (defined $type and $type eq "hot") { - my $r = 205 + int(50 * $v3); - my $g = 0 + int(230 * $v1); - my $b = 0 + int(55 * $v2); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "mem") { - my $r = 0; - my $g = 190 + int(50 * $v2); - my $b = 0 + int(210 * $v1); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "io") { - my $r = 80 + int(60 * $v1); - my $g = $r; - my $b = 190 + int(55 * $v2); - return "rgb($r,$g,$b)"; - } - - # multi palettes - if (defined $type and $type eq "java") { - # Handle both annotations (_[j], _[i], ...; which are - # accurate), as well as input that lacks any annotations, as - # best as possible. Without annotations, we get a little hacky - # and match on java|org|com, etc. - if ($name =~ m:_\[j\]$:) { # jit annotation - $type = "green"; - } elsif ($name =~ m:_\[i\]$:) { # inline annotation - $type = "aqua"; - } elsif ($name =~ m:^L?(java|org|com|io|sun)/:) { # Java - $type = "green"; - } elsif ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:_\[k\]$:) { # kernel annotation - $type = "orange"; - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "perl") { - if ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:Perl: or $name =~ m:\.pl:) { # Perl - $type = "green"; - } elsif ($name =~ m:_\[k\]$:) { # kernel - $type = "orange"; - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "js") { - # Handle both annotations (_[j], _[i], ...; which are - # accurate), as well as input that lacks any annotations, as - # best as possible. Without annotations, we get a little hacky, - # and match on a "/" with a ".js", etc. - if ($name =~ m:_\[j\]$:) { # jit annotation - if ($name =~ m:/:) { - $type = "green"; # source - } else { - $type = "aqua"; # builtin - } - } elsif ($name =~ /::/) { # C++ - $type = "yellow"; - } elsif ($name =~ m:/.*\.js:) { # JavaScript (match "/" in path) - $type = "green"; - } elsif ($name =~ m/:/) { # JavaScript (match ":" in builtin) - $type = "aqua"; - } elsif ($name =~ m/^ $/) { # Missing symbol - $type = "green"; - } elsif ($name =~ m:_\[k\]:) { # kernel - $type = "orange"; - } else { # system - $type = "red"; - } - # fall-through to color palettes - } - if (defined $type and $type eq "wakeup") { - $type = "aqua"; - # fall-through to color palettes - } - if (defined $type and $type eq "chain") { - if ($name =~ m:_\[w\]:) { # waker - $type = "aqua" - } else { # off-CPU - $type = "blue"; - } - # fall-through to color palettes - } - - # color palettes - if (defined $type and $type eq "red") { - my $r = 200 + int(55 * $v1); - my $x = 50 + int(80 * $v1); - return "rgb($r,$x,$x)"; - } - if (defined $type and $type eq "green") { - my $g = 200 + int(55 * $v1); - my $x = 50 + int(60 * $v1); - return "rgb($x,$g,$x)"; - } - if (defined $type and $type eq "blue") { - my $b = 205 + int(50 * $v1); - my $x = 80 + int(60 * $v1); - return "rgb($x,$x,$b)"; - } - if (defined $type and $type eq "yellow") { - my $x = 175 + int(55 * $v1); - my $b = 50 + int(20 * $v1); - return "rgb($x,$x,$b)"; - } - if (defined $type and $type eq "purple") { - my $x = 190 + int(65 * $v1); - my $g = 80 + int(60 * $v1); - return "rgb($x,$g,$x)"; - } - if (defined $type and $type eq "aqua") { - my $r = 50 + int(60 * $v1); - my $g = 165 + int(55 * $v1); - my $b = 165 + int(55 * $v1); - return "rgb($r,$g,$b)"; - } - if (defined $type and $type eq "orange") { - my $r = 190 + int(65 * $v1); - my $g = 90 + int(65 * $v1); - return "rgb($r,$g,0)"; - } - - return "rgb(0,0,0)"; -} - -sub color_scale { - my ($value, $max) = @_; - my ($r, $g, $b) = (255, 255, 255); - $value = -$value if $negate; - if ($value > 0) { - $g = $b = int(210 * ($max - $value) / $max); - } elsif ($value < 0) { - $r = $g = int(210 * ($max + $value) / $max); - } - return "rgb($r,$g,$b)"; -} - -sub color_map { - my ($colors, $func) = @_; - if (exists $palette_map{$func}) { - return $palette_map{$func}; - } else { - $palette_map{$func} = color($colors, $hash, $func); - return $palette_map{$func}; - } -} - -sub write_palette { - open(FILE, ">$pal_file"); - foreach my $key (sort keys %palette_map) { - print FILE $key."->".$palette_map{$key}."\n"; - } - close(FILE); -} - -sub read_palette { - if (-e $pal_file) { - open(FILE, $pal_file) or die "can't open file $pal_file: $!"; - while ( my $line = ) { - chomp($line); - (my $key, my $value) = split("->",$line); - $palette_map{$key}=$value; - } - close(FILE) - } -} - -my %Node; # Hash of merged frame data -my %Tmp; - -# flow() merges two stacks, storing the merged frames and value data in %Node. -sub flow { - my ($last, $this, $v, $d) = @_; - - my $len_a = @$last - 1; - my $len_b = @$this - 1; - - my $i = 0; - my $len_same; - for (; $i <= $len_a; $i++) { - last if $i > $len_b; - last if $last->[$i] ne $this->[$i]; - } - $len_same = $i; - - for ($i = $len_a; $i >= $len_same; $i--) { - my $k = "$last->[$i];$i"; - # a unique ID is constructed from "func;depth;etime"; - # func-depth isn't unique, it may be repeated later. - $Node{"$k;$v"}->{stime} = delete $Tmp{$k}->{stime}; - if (defined $Tmp{$k}->{delta}) { - $Node{"$k;$v"}->{delta} = delete $Tmp{$k}->{delta}; - } - delete $Tmp{$k}; - } - - for ($i = $len_same; $i <= $len_b; $i++) { - my $k = "$this->[$i];$i"; - $Tmp{$k}->{stime} = $v; - if (defined $d) { - $Tmp{$k}->{delta} += $i == $len_b ? $d : 0; - } - } - - return $this; -} - -# parse input -my @Data; -my $last = []; -my $time = 0; -my $delta = undef; -my $ignored = 0; -my $line; -my $maxdelta = 1; - -# reverse if needed -foreach (<>) { - chomp; - $line = $_; - if ($stackreverse) { - # there may be an extra samples column for differentials - # XXX todo: redo these REs as one. It's repeated below. - my($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - my $samples2 = undef; - if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { - $samples2 = $samples; - ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - unshift @Data, join(";", reverse split(";", $stack)) . " $samples $samples2"; - } else { - unshift @Data, join(";", reverse split(";", $stack)) . " $samples"; - } - } else { - unshift @Data, $line; - } -} - -# process and merge frames -foreach (sort @Data) { - chomp; - # process: folded_stack count - # eg: func_a;func_b;func_c 31 - my ($stack, $samples) = (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - unless (defined $samples and defined $stack) { - ++$ignored; - next; - } - - # there may be an extra samples column for differentials: - my $samples2 = undef; - if ($stack =~ /^(.*)\s+?(\d+(?:\.\d*)?)$/) { - $samples2 = $samples; - ($stack, $samples) = $stack =~ (/^(.*)\s+?(\d+(?:\.\d*)?)$/); - } - $delta = undef; - if (defined $samples2) { - $delta = $samples2 - $samples; - $maxdelta = abs($delta) if abs($delta) > $maxdelta; - } - - # for chain graphs, annotate waker frames with "_[w]", for later - # coloring. This is a hack, but has a precedent ("_[k]" from perf). - if ($colors eq "chain") { - my @parts = split ";--;", $stack; - my @newparts = (); - $stack = shift @parts; - $stack .= ";--;"; - foreach my $part (@parts) { - $part =~ s/;/_[w];/g; - $part .= "_[w]"; - push @newparts, $part; - } - $stack .= join ";--;", @parts; - } - - # merge frames and populate %Node: - $last = flow($last, [ '', split ";", $stack ], $time, $delta); - - if (defined $samples2) { - $time += $samples2; - } else { - $time += $samples; - } -} -flow($last, [], $time, $delta); - -warn "Ignored $ignored lines with invalid format\n" if $ignored; -unless ($time) { - warn "ERROR: No stack counts found\n"; - my $im = SVG->new(); - # emit an error message SVG, for tools automating flamegraph use - my $imageheight = $fontsize * 5; - $im->header($imagewidth, $imageheight); - $im->stringTTF($im->colorAllocate(0, 0, 0), $fonttype, $fontsize + 2, - 0.0, int($imagewidth / 2), $fontsize * 2, - "ERROR: No valid input provided to flamegraph.pl.", "middle"); - print $im->svg; - exit 2; -} -if ($timemax and $timemax < $time) { - warn "Specified --total $timemax is less than actual total $time, so ignored\n" - if $timemax/$time > 0.02; # only warn is significant (e.g., not rounding etc) - undef $timemax; -} -$timemax ||= $time; - -my $widthpertime = ($imagewidth - 2 * $xpad) / $timemax; -my $minwidth_time = $minwidth / $widthpertime; - -# prune blocks that are too narrow and determine max depth -while (my ($id, $node) = each %Node) { - my ($func, $depth, $etime) = split ";", $id; - my $stime = $node->{stime}; - die "missing start for $id" if not defined $stime; - - if (($etime-$stime) < $minwidth_time) { - delete $Node{$id}; - next; - } - $depthmax = $depth if $depth > $depthmax; -} - -# draw canvas, and embed interactive JavaScript program -my $imageheight = (($depthmax + 1) * $frameheight) + $ypad1 + $ypad2; -$imageheight += $ypad3 if $subtitletext ne ""; -my $im = SVG->new(); -$im->header($imagewidth, $imageheight); -my $inc = < - - - - - - - -INC -$im->include($inc); -$im->filledRectangle(0, 0, $imagewidth, $imageheight, 'url(#background)'); -my ($white, $black, $vvdgrey, $vdgrey, $dgrey) = ( - $im->colorAllocate(255, 255, 255), - $im->colorAllocate(0, 0, 0), - $im->colorAllocate(40, 40, 40), - $im->colorAllocate(160, 160, 160), - $im->colorAllocate(200, 200, 200), - ); -$im->stringTTF($black, $fonttype, $fontsize + 5, 0.0, int($imagewidth / 2), $fontsize * 2, $titletext, "middle"); -if ($subtitletext ne "") { - $im->stringTTF($vdgrey, $fonttype, $fontsize, 0.0, int($imagewidth / 2), $fontsize * 4, $subtitletext, "middle"); -} -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $imageheight - ($ypad2 / 2), " ", "", 'id="details"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $xpad, $fontsize * 2, - "Reset Zoom", "", 'id="unzoom" onclick="unzoom()" style="opacity:0.0;cursor:pointer"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, - $fontsize * 2, "Search", "", 'id="search" onmouseover="searchover()" onmouseout="searchout()" onclick="search_prompt()" style="opacity:0.1;cursor:pointer"'); -$im->stringTTF($black, $fonttype, $fontsize, 0.0, $imagewidth - $xpad - 100, $imageheight - ($ypad2 / 2), " ", "", 'id="matched"'); - -if ($palette) { - read_palette(); -} - -# draw frames -while (my ($id, $node) = each %Node) { - my ($func, $depth, $etime) = split ";", $id; - my $stime = $node->{stime}; - my $delta = $node->{delta}; - - $etime = $timemax if $func eq "" and $depth == 0; - - my $x1 = $xpad + $stime * $widthpertime; - my $x2 = $xpad + $etime * $widthpertime; - my ($y1, $y2); - unless ($inverted) { - $y1 = $imageheight - $ypad2 - ($depth + 1) * $frameheight + $framepad; - $y2 = $imageheight - $ypad2 - $depth * $frameheight; - } else { - $y1 = $ypad1 + $depth * $frameheight; - $y2 = $ypad1 + ($depth + 1) * $frameheight - $framepad; - } - - my $samples = sprintf "%.0f", ($etime - $stime) * $factor; - (my $samples_txt = $samples) # add commas per perlfaq5 - =~ s/(^[-+]?\d+?(?=(?>(?:\d{3})+)(?!\d))|\G\d{3}(?=\d))/$1,/g; - - my $info; - if ($func eq "" and $depth == 0) { - $info = "all ($samples_txt $countname, 100%)"; - } else { - my $pct = sprintf "%.2f", ((100 * $samples) / ($timemax * $factor)); - my $escaped_func = $func; - # clean up SVG breaking characters: - $escaped_func =~ s/&/&/g; - $escaped_func =~ s//>/g; - $escaped_func =~ s/"/"/g; - $escaped_func =~ s/_\[[kwij]\]$//; # strip any annotation - unless (defined $delta) { - $info = "$escaped_func ($samples_txt $countname, $pct%)"; - } else { - my $d = $negate ? -$delta : $delta; - my $deltapct = sprintf "%.2f", ((100 * $d) / ($timemax * $factor)); - $deltapct = $d > 0 ? "+$deltapct" : $deltapct; - $info = "$escaped_func ($samples_txt $countname, $pct%; $deltapct%)"; - } - } - - my $nameattr = { %{ $nameattr{$func}||{} } }; # shallow clone - $nameattr->{class} ||= "func_g"; - $nameattr->{onmouseover} ||= "s(this)"; - $nameattr->{onmouseout} ||= "c()"; - $nameattr->{onclick} ||= "zoom(this)"; - $nameattr->{title} ||= $info; - $im->group_start($nameattr); - - my $color; - if ($func eq "--") { - $color = $vdgrey; - } elsif ($func eq "-") { - $color = $dgrey; - } elsif (defined $delta) { - $color = color_scale($delta, $maxdelta); - } elsif ($palette) { - $color = color_map($colors, $func); - } else { - $color = color($colors, $hash, $func); - } - $im->filledRectangle($x1, $y1, $x2, $y2, $color, 'rx="2" ry="2"'); - - my $chars = int( ($x2 - $x1) / ($fontsize * $fontwidth)); - my $text = ""; - if ($chars >= 3) { # room for one char plus two dots - $func =~ s/_\[[kwij]\]$//; # strip any annotation - $text = substr $func, 0, $chars; - substr($text, -2, 2) = ".." if $chars < length $func; - $text =~ s/&/&/g; - $text =~ s//>/g; - } - $im->stringTTF($black, $fonttype, $fontsize, 0.0, $x1 + 3, 3 + ($y1 + $y2) / 2, $text, ""); - - $im->group_end($nameattr); -} - -print $im->svg; - -if ($palette) { - write_palette(); -} - -# vim: ts=8 sts=8 sw=8 noexpandtab diff --git a/selfdrive/debug/profiling/pyflame/pyflame b/selfdrive/debug/profiling/pyflame/pyflame deleted file mode 100755 index 31ef536f1..000000000 Binary files a/selfdrive/debug/profiling/pyflame/pyflame and /dev/null differ