Skip to content

Commit

Permalink
Fix decoding Python 3.13 stacks without symbols
Browse files Browse the repository at this point in the history
The eager `isValid` check that we implemented originally fails when
symbols aren't available, as we're unable to look up `PyCode_Type` to
compare the type of `f_executable` against it.

Instead, let's treat every `f_executable` as though it's a `PyCode_Type`
without checking in advance. If it's not able to be interpreted as
a `PyCode_Type`, we'll expect a `RemoteMemCopyError` from chasing an
invalid pointer at some point, and can handle that the way that we have
been handling `!isValid`.

Signed-off-by: Matt Wozniski <[email protected]>
  • Loading branch information
godlygeek committed Sep 11, 2024
1 parent c77eb4f commit 3db2f8e
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 33 deletions.
39 changes: 9 additions & 30 deletions src/pystack/_pystack/pycode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -162,41 +162,11 @@ getLocationInfo(
return location_info;
}

static bool
isValid(const std::shared_ptr<const AbstractProcessManager>& manager, remote_addr_t addr)
{
if (manager->versionIsAtLeast(3, 13)) {
// In Python 3.13, the frame f_executable field can be a code object or a bunch
// of other possible types (including None). We consider valid only the cases
// where it is a code object.
remote_addr_t pycodeobject_addr = manager->getAddressFromCache("PyCode_Type");
if (pycodeobject_addr == 0) {
Object code_obj(manager, addr);
if (code_obj.objectType() == Object::ObjectType::CODE) {
manager->registerAddressInCache("PyCode_Type", code_obj.typeAddr());
return true;
}
return false;
} else {
Structure<py_object_v> obj(manager, addr);
return obj.getField(&py_object_v::o_ob_type) == pycodeobject_addr;
}
}
return true;
}

CodeObject::CodeObject(
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t addr,
uintptr_t lasti)
{
if (!isValid(manager, addr)) {
d_filename = "???";
d_scope = "???";
d_location_info = LocationInfo{0, 0, 0, 0};
d_narguments = 0;
return;
}
LOG(DEBUG) << std::hex << std::showbase << "Copying code struct from address " << addr;
Structure<py_code_v> code(manager, addr);

Expand Down Expand Up @@ -235,6 +205,15 @@ CodeObject::CodeObject(
});
}

CodeObject::CodeObject(std::string filename, std::string scope, LocationInfo location_info)
: d_filename(filename)
, d_scope(scope)
, d_location_info(location_info)
, d_narguments()
, d_varnames()
{
}

std::string
CodeObject::Filename() const
{
Expand Down
3 changes: 1 addition & 2 deletions src/pystack/_pystack/pycode.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class CodeObject
const std::shared_ptr<const AbstractProcessManager>& manager,
remote_addr_t addr,
uintptr_t lastli);
CodeObject(std::string filename, std::string scope, LocationInfo location_info);

// Getters
std::string Filename() const;
Expand All @@ -41,8 +42,6 @@ class CodeObject
std::string d_scope;
LocationInfo d_location_info;
int d_narguments;

private:
std::vector<std::string> d_varnames;
};
} // namespace pystack
9 changes: 8 additions & 1 deletion src/pystack/_pystack/pyframe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,14 @@ FrameObject::getCode(
} else {
last_instruction = frame.getField(&py_frame_v::o_lasti);
}
return std::make_unique<CodeObject>(manager, py_code_addr, last_instruction);
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
Expand Down

0 comments on commit 3db2f8e

Please sign in to comment.