-
Notifications
You must be signed in to change notification settings - Fork 35
/
python.cc
130 lines (114 loc) · 4.32 KB
/
python.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include "libpstack/python.h"
#include <dlfcn.h>
#include <string.h>
#include <string>
#include <regex.h>
#include "libpstack/global.h"
#include "libpstack/proc.h"
#include "libpstack/stringify.h"
namespace pstack {
static std::tuple<Elf::Object::sptr, Elf::Addr, Elf::Addr> getInterpHead(Procman::Process &);
PyInterpInfo
getPyInterpInfo(Procman::Process &proc) {
Elf::Object::sptr libpython;
Elf::Addr libpythonAddr;
Elf::Addr interpreterHead;
std::tie(libpython, libpythonAddr, interpreterHead) = getInterpHead(proc);
if (libpython == nullptr)
return PyInterpInfo {nullptr, 0, 0, "", 0};
std::string filename = libpython->io->filename();
auto index = filename.find("python");
if (filename.length() < index + 9) //index + len("pythonX.Y")
throw Exception() << "Can't parse python version from lib/exec name: " << filename;
char majorChar = filename[index + 6];
char minorChar = filename[index + 8];
if (!isdigit(majorChar) || !isdigit(minorChar))
throw Exception() << "lib/exec name doesn't match \"*pythonX.Y.*\" format";
int major = majorChar - '0';
int minor = minorChar - '0';
if (verbose)
*debug << "python version is: " << major << "." << minor << std::endl;
return PyInterpInfo {
libpython, libpythonAddr, interpreterHead,
"v" + std::to_string(major) + "." + std::to_string(minor),
V2HEX(major, minor)};
}
std::tuple<Elf::Object::sptr, Elf::Addr, Elf::Addr>
getInterpHead(Procman::Process &proc) {
// As a local python2 hack, we have a global variable pointing at interp_head
// We can use that to avoid needing any debug info for the interpreter.
// (Python3 does not require this hack, because _PyRuntime is exported
// in the dynamic symbols.)
try {
auto [ libpython, libpythonAddr, interpreterHead ] =
proc.resolveSymbolDetail("Py_interp_headp", false,
[&](std::string_view name) {
return name.find("python") != std::string::npos;
});
if (verbose)
*debug << "found interp_headp in ELF syms" << std::endl;
Elf::Addr interpHead;
proc.io->readObj(libpythonAddr + interpreterHead.st_value, &interpHead);
return std::make_tuple(libpython, libpythonAddr, interpHead);
}
catch (...) {
if (verbose)
*debug << "Py_interp_headp symbol not found. Trying fallback" << std::endl;
}
#ifdef WITH_PYTHON2
try {
return getInterpHead<2>(proc);
} catch (...) {
if (verbose)
*debug << "Python 2 interpreter not found" << std::endl;
}
#endif
#ifdef WITH_PYTHON3
try {
return getInterpHead<3>(proc);
} catch (...) {
if (verbose)
*debug << "Python 3 interpreter not found" << std::endl;
}
#endif
if (verbose)
*debug << "Couldn't find a python interpreter" << std::endl;
return std::make_tuple(nullptr, 0, 0);
}
// libpthread includes offsets for fields in various structures. We can use
// _thred_db_pthread_tid to work out the offset within a pthread structure of
// the "tid" in a pthread. This gives us a way to find an LWP for a given
// pthread_t. (In Linux's 1:1 modern threadding model, each pthread_t is associated
// with a single LWP, or Linux task.)
bool
pthreadTidOffset(Procman::Process &proc, size_t *offsetp)
{
static size_t offset;
static enum { notDone, notFound, found } status;
if (status == notDone) {
try {
auto addr = proc.resolveSymbol("_thread_db_pthread_tid", true,
[](std::string_view name) {
return
name.find("libc." ) != std::string::npos ||
name.find("libpthread" ) != std::string::npos ||
name.find("libthread" ) != std::string::npos; });
uint32_t desc[3];
proc.io->readObj(addr, &desc[0], 3);
offset = desc[2];
status = found;
if (verbose)
*debug << "found thread offset " << offset << "\n";
} catch (const std::exception &ex) {
if (verbose)
*debug << "failed to find offset of tid in pthread: " << ex.what();
status = notFound;
}
}
if (status == found) {
*offsetp = offset;
return true;
}
return false;
}
}