python-record-api icon indicating copy to clipboard operation
python-record-api copied to clipboard

Cleanup of Python Stack Access

Open saulshanabrook opened this issue 5 years ago • 2 comments

There were some comments here by @Caagr98 on how we can clean up our ability to access the Python stack, that we could try to incorporate. I haven't looked into them yet.

saulshanabrook avatar Sep 07 '20 12:09 saulshanabrook

Here's the simplified code I ended up with.

import sys
import ctypes as c

__all__ = ("get_stack",)

class Frame(c.Structure):
	_fields_ = (
		*(
			("_ob_next", c.POINTER(c.py_object)),
			("_ob_prev", c.POINTER(c.py_object)),
		) * sys.flags.debug,
		("ob_refcnt", c.c_ssize_t),
		("ob_type", c.py_object),
		("ob_size", c.c_ssize_t),
		("f_back", c.py_object), # c.POINTER(Frame)
		("f_code", c.py_object),
		("f_builtins", c.py_object),
		("f_globals", c.py_object),
		("f_locals", c.py_object),
		# The two fields below are pointers to PyObject arrays, but ctypes is
		# kinda weird so it's easier to just use them as void pointers
		("f_valuestack", c.c_void_p),
		("f_stacktop", c.c_void_p),
	)

def get_stack(frame):
	PTR_SIZE = c.sizeof(c.POINTER(c.py_object))
	_frame = Frame.from_address(id(frame))

	return tuple(
		c.py_object.from_address(addr).value
		for addr in range(_frame.f_valuestack, _frame.f_stacktop, PTR_SIZE)
	)

I'm not 100% certain I don't need to do anything about refcounts, but py_object probably deals with that automatically.

Kyuuhachi avatar Sep 07 '20 13:09 Kyuuhachi

@Caagr98 Awesome, thank you for this!

If anyone feels like updating our stack functionality to use this logic, you can edit the record_api/get_stack.py file.

In our usage we just need to be able to look at the ith item from the top of the stack, we never need to iterate through it all. So all we really need is the __getitem__ function to work on that OpStack class with negative indexes. Since performance is a concern here, we don't have any bounds checks.

saulshanabrook avatar Sep 07 '20 13:09 saulshanabrook