profiling: replace pyflame with py-spy (#22864)
* py-spy * remove pyflame * find process by namepull/22832/head
parent
327464fb84
commit
caad4919ac
6 changed files with 21 additions and 1478 deletions
@ -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 |
@ -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 |
|
@ -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 <Python.h>
|
|
||||||
+
|
|
||||||
#include <frameobject.h>
|
|
||||||
|
|
||||||
#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 <cstddef>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
@@ -25,6 +26,8 @@
|
|
||||||
#include "./ptrace.h"
|
|
||||||
#include "./symbol.h"
|
|
||||||
|
|
||||||
+extern size_t PYFLAME_PYTHON37_OFFSET;
|
|
||||||
+
|
|
||||||
#define FROB_FUNCS \
|
|
||||||
std::vector<Thread> 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<Thread> 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<const sym_t *>(p() + s->sh_offset + i * s->sh_entsize);
|
|
||||||
const char *name =
|
|
||||||
reinterpret_cast<const char *>(p() + d->sh_offset + sym->st_name);
|
|
||||||
- if (!addrs->tstate_addr && strcmp(name, "_PyThreadState_Current") == 0) {
|
|
||||||
- addrs->tstate_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
- } else if (!addrs->interp_head_addr && strcmp(name, "interp_head") == 0) {
|
|
||||||
- addrs->interp_head_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
- } else if (!addrs->interp_head_addr &&
|
|
||||||
- strcmp(name, "PyInterpreterState_Head") == 0) {
|
|
||||||
- addrs->interp_head_fn_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
- } else if (!have_abi) {
|
|
||||||
+ if (!addrs->tstate_addr) {
|
|
||||||
+ if (strcmp(name, "_PyThreadState_Current") == 0) {
|
|
||||||
+ addrs->tstate_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
+ } else if (strcmp(name, "_PyRuntime") == 0) {
|
|
||||||
+ addrs->tstate_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!addrs->interp_head_addr) {
|
|
||||||
+ if (strcmp(name, "interp_head") == 0) {
|
|
||||||
+ addrs->interp_head_addr = static_cast<unsigned long>(sym->st_value);
|
|
||||||
+ } else if (strcmp(name, "PyInterpreterState_Head") == 0) {
|
|
||||||
+ addrs->interp_head_fn_addr = static_cast<unsigned long>(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
|
|
@ -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 |
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Loading…
Reference in new issue