forked from bloomberg/pystack
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpyframe.cpp
More file actions
189 lines (162 loc) Β· 5.54 KB
/
pyframe.cpp
File metadata and controls
189 lines (162 loc) Β· 5.54 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <memory>
#include <sstream>
#include "logging.h"
#include "mem.h"
#include "process.h"
#include "pycode.h"
#include "pycompat.h"
#include "pyframe.h"
#include "pytypes.h"
#include "version.h"
static constexpr int FRAME_LIMIT = 4096;
namespace pystack {
FrameObject::FrameObject(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t addr,
ssize_t frame_no)
: d_manager(manager)
{
LOG(DEBUG) << "Copying frame number " << frame_no;
LOG(DEBUG) << std::hex << std::showbase << "Copying frame struct from address " << addr;
Structure<py_frame_v> frame(manager, addr);
d_addr = addr;
d_frame_no = frame_no;
d_is_shim = getIsShim(manager, frame);
ssize_t next_frame_no = frame_no + 1;
if (d_is_shim) {
LOG(DEBUG) << "Skipping over a shim frame inserted by the interpreter";
next_frame_no = frame_no;
}
d_code = getCode(manager, frame);
auto prev_addr = frame.getField(&py_frame_v::o_back);
LOG(DEBUG) << std::hex << std::showbase << "Previous frame address: " << prev_addr;
if (prev_addr) {
d_prev = std::make_shared<FrameObject>(manager, prev_addr, next_frame_no);
}
d_is_entry = isEntry(manager, frame);
}
bool
FrameObject::getIsShim(
const std::shared_ptr<const AbstractProcessManager>& manager,
Structure<py_frame_v>& frame)
{
if (manager->versionIsAtLeast(3, 12)) {
constexpr int FRAME_OWNED_BY_CSTACK = 3;
return frame.getField(&py_frame_v::o_owner) == FRAME_OWNED_BY_CSTACK;
}
return false; // Versions before 3.12 don't have shim frames.
}
std::unique_ptr<CodeObject>
FrameObject::getCode(
const std::shared_ptr<const AbstractProcessManager>& manager,
Structure<py_frame_v>& frame)
{
remote_addr_t py_code_addr = frame.getField(&py_frame_v::o_code);
LOG(DEBUG) << std::hex << std::showbase << "Attempting to construct code object from address "
<< py_code_addr;
uintptr_t last_instruction;
if (manager->versionIsAtLeast(3, 11)) {
last_instruction = frame.getField(&py_frame_v::o_prev_instr);
} else {
last_instruction = frame.getField(&py_frame_v::o_lasti);
}
try {
return std::make_unique<CodeObject>(manager, py_code_addr, last_instruction);
} catch (const RemoteMemCopyError& ex) {
// This may not have been a code object at all, or it may have been
// trashed by memory corruption. Either way, indicate that we failed
// to understand what code this frame is running.
return std::make_unique<CodeObject>("???", "???", LocationInfo{0, 0, 0, 0});
}
}
bool
FrameObject::isEntry(
const std::shared_ptr<const AbstractProcessManager>& manager,
Structure<py_frame_v>& frame)
{
if (manager->versionIsAtLeast(3, 12)) {
// This is an entry frame if the previous frame was a shim, or if
// this is the most recent frame and is itself a shim (meaning that
// the entry frame this shim was created for hasn't been pushed yet,
// so the latest _PyEval_EvalFrameDefault call has no Python frames).
return (d_prev && d_prev->d_is_shim) || (d_frame_no == 0 && d_is_shim);
} else if (manager->versionIsAtLeast(3, 11)) {
// This is an entry frame if it has an entry flag set.
return frame.getField(&py_frame_v::o_is_entry);
}
return true;
}
void
FrameObject::resolveLocalVariables()
{
LOG(DEBUG) << "Resolving local variables from frame number " << d_frame_no;
if (d_code == nullptr) {
LOG(INFO) << "Frame is a shim frame, skipping local variable resolution";
return;
}
const size_t n_arguments = d_code->NArguments();
const size_t n_locals = d_code->Varnames().size();
Structure<py_frame_v> frame(d_manager, d_addr);
const remote_addr_t locals_addr = frame.getFieldRemoteAddress(&py_frame_v::o_localsplus);
if (n_locals < n_arguments) {
throw std::runtime_error("Found more arguments than local variables");
}
std::vector<remote_addr_t> tuple_buffer(n_locals);
LOG(DEBUG) << "Copying buffer containing local variables";
d_manager->copyMemoryFromProcess(locals_addr, n_locals * sizeof(remote_addr_t), tuple_buffer.data());
auto addLocal = [&](size_t index, auto& map) {
remote_addr_t addr = tuple_buffer[index];
if (addr == (remote_addr_t) nullptr) {
return;
}
std::string key = d_code->Varnames()[index];
LOG(DEBUG) << "Copying local variable at address " << std::hex << std::showbase << addr;
std::string value = Object(d_manager, addr).toString();
LOG(DEBUG) << "Local variable resolved to: " << key << ": " << value;
map.insert(std::make_pair(key, value));
};
LOG(DEBUG) << "Copying content of local variables";
for (size_t i = 0; i < tuple_buffer.size(); ++i) {
if (i < n_arguments) {
addLocal(i, d_arguments);
} else {
addLocal(i, d_locals);
}
}
}
ssize_t
FrameObject::FrameNo() const
{
return d_frame_no;
}
std::shared_ptr<FrameObject>
FrameObject::PreviousFrame()
{
return d_prev;
}
std::shared_ptr<CodeObject>
FrameObject::Code()
{
return d_code;
}
const std::unordered_map<std::string, std::string>&
FrameObject::Arguments() const
{
return d_arguments;
}
const std::unordered_map<std::string, std::string>&
FrameObject::Locals() const
{
return d_locals;
}
bool
FrameObject::IsEntryFrame() const
{
return this->d_is_entry;
}
bool
FrameObject::IsShim() const
{
return this->d_is_shim;
}
} // namespace pystack