Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/viztracer/modules/eventnode.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ void
clear_node(struct EventNode* node) {
switch (node->ntype) {
case FEE_NODE:
// Clear common optional fields
Py_CLEAR(node->data.fee.args);
Py_CLEAR(node->data.fee.retval);
if (node->data.fee.type == PyTrace_CALL || node->data.fee.type == PyTrace_RETURN) {
Py_CLEAR(node->data.fee.code);
Py_CLEAR(node->data.fee.args);
Py_CLEAR(node->data.fee.retval);
} else {
node->data.fee.ml_name = NULL;
if (node->data.fee.m_module) {
Expand Down
59 changes: 59 additions & 0 deletions src/viztracer/modules/snaptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,30 @@ tracer_pycall_callback(TracerObject* self, PyCodeObject* code)
info->stack_top = info->stack_top->next;
info->stack_top->ts = get_ts();
info->stack_top->func = Py_NewRef(code);
if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_CALLSITE)) {
PyFrameObject* curr_frame = PyEval_GetFrame();
PyFrameObject* caller_frame = PyFrame_GetBack(curr_frame);
if (caller_frame) {
PyCodeObject* caller_code = PyFrame_GetCode(caller_frame);
if (!info->stack_top->args) {
info->stack_top->args = PyDict_New();
}
if (caller_code && caller_code->co_filename) {
int lineno = PyFrame_GetLineNumber(caller_frame);
PyObject* colon = PyUnicode_FromString(":");
PyObject* lineno_str = PyUnicode_FromFormat("%d", lineno);
PyObject* info_str = PyUnicode_Concat(caller_code->co_filename, colon);
Py_DECREF(colon);
PyObject* info_full = PyUnicode_Concat(info_str, lineno_str);
Py_DECREF(info_str);
Py_DECREF(lineno_str);
PyDict_SetItemString(info->stack_top->args, "caller_info", info_full);
Py_DECREF(info_full);
}
Py_XDECREF(caller_code);
Py_XDECREF(caller_frame);
}
}
if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_FUNCTION_ARGS)) {
log_func_args(info->stack_top, PyEval_GetFrame(), self->log_func_repr);
}
Expand Down Expand Up @@ -486,6 +510,30 @@ tracer_ccall_callback(TracerObject* self, PyCodeObject* code, PyObject* arg)
info->stack_top = info->stack_top->next;
info->stack_top->ts = get_ts();
info->stack_top->func = Py_NewRef(arg);
if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_CALLSITE)) {
PyFrameObject* curr_frame = PyEval_GetFrame();
PyFrameObject* caller_frame = PyFrame_GetBack(curr_frame);
if (caller_frame) {
PyCodeObject* caller_code = PyFrame_GetCode(caller_frame);
if (!info->stack_top->args) {
info->stack_top->args = PyDict_New();
}
if (caller_code && caller_code->co_filename) {
int lineno = PyFrame_GetLineNumber(caller_frame);
PyObject* colon = PyUnicode_FromString(":");
PyObject* lineno_str = PyUnicode_FromFormat("%d", lineno);
PyObject* info_str = PyUnicode_Concat(caller_code->co_filename, colon);
Py_DECREF(colon);
PyObject* info_full = PyUnicode_Concat(info_str, lineno_str);
Py_DECREF(info_str);
Py_DECREF(lineno_str);
PyDict_SetItemString(info->stack_top->args, "caller_info", info_full);
Py_DECREF(info_full);
}
Py_XDECREF(caller_code);
Py_XDECREF(caller_frame);
}
}
if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_FUNCTION_ARGS)) {
log_func_args(info->stack_top, PyEval_GetFrame(), self->log_func_repr);
}
Expand Down Expand Up @@ -629,6 +677,8 @@ tracer_creturn_callback(TracerObject* self, PyCodeObject* code, PyObject* arg)
node->tid = info->tid;
node->data.fee.type = PyTrace_C_RETURN;
node->data.fee.ml_name = cfunc->m_ml->ml_name;
// steal the reference when return
node->data.fee.args = Py_XNewRef(stack_top->args);
if (cfunc->m_module) {
// The function belongs to a module
node->data.fee.m_module = Py_NewRef(cfunc->m_module);
Expand Down Expand Up @@ -1315,6 +1365,15 @@ tracer_load(TracerObject* self, PyObject* Py_UNUSED(unused))
}
PyDict_SetItemString(arg_dict, "return_value", node->data.fee.retval);
}
// Promote callsite info as top-level field for convenience
if (arg_dict) {
PyObject* caller_info = PyDict_GetItemString(arg_dict, "caller_info");
if (caller_info) {
PyDict_SetItemString(dict, "caller_info", caller_info);
// Ensure args also contains caller_info for downstream tools
PyDict_SetItemString(arg_dict, "caller_info", caller_info);
}
}
if (arg_dict) {
PyDict_SetItemString(dict, "args", arg_dict);
Py_DECREF(arg_dict);
Expand Down
1 change: 1 addition & 0 deletions src/viztracer/modules/snaptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
#define SNAPTRACE_IGNORE_FROZEN (1 << 7)
#define SNAPTRACE_LOG_ASYNC (1 << 8)
#define SNAPTRACE_TRACE_SELF (1 << 9)
#define SNAPTRACE_LOG_CALLSITE (1 << 10)

#define SET_FLAG(reg, flag) ((reg) |= (flag))
#define UNSET_FLAG(reg, flag) ((reg) &= (~(flag)))
Expand Down
35 changes: 35 additions & 0 deletions src/viztracer/modules/snaptrace_member.c
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,9 @@ Tracer_log_func_repr_getter(TracerObject* self, void* closure)
return Py_NewRef(self->log_func_repr);
}

static int Tracer_log_callsite_setter(TracerObject* self, PyObject* value, void* closure);
static PyObject* Tracer_log_callsite_getter(TracerObject* self, void* closure);

PyGetSetDef Tracer_getsetters[] = {
{"max_stack_depth", (getter)Tracer_max_stack_depth_getter, (setter)Tracer_max_stack_depth_setter, "max_stack_depth", NULL},
{"include_files", (getter)Tracer_include_files_getter, (setter)Tracer_include_files_setter, "include_files", NULL},
Expand All @@ -476,8 +479,40 @@ PyGetSetDef Tracer_getsetters[] = {
{"min_duration", (getter)Tracer_min_duration_getter, (setter)Tracer_min_duration_setter, "min_duration", NULL},
{"log_func_retval", (getter)Tracer_log_func_retval_getter, (setter)Tracer_log_func_retval_setter, "log_func_retval", NULL},
{"log_func_args", (getter)Tracer_log_func_args_getter, (setter)Tracer_log_func_args_setter, "log_func_args", NULL},
{"log_callsite", (getter)Tracer_log_callsite_getter, (setter)Tracer_log_callsite_setter, "log_callsite", NULL},
{"log_async", (getter)Tracer_log_async_getter, (setter)Tracer_log_async_setter, "log_async", NULL},
{"trace_self", (getter)Tracer_trace_self_getter, (setter)Tracer_trace_self_setter, "trace_self", NULL},
{"log_func_repr", (getter)Tracer_log_func_repr_getter, (setter)Tracer_log_func_repr_setter, "log_func_repr", NULL},
{NULL}
};

static int
Tracer_log_callsite_setter(TracerObject* self, PyObject* value, void* closure)
{
if (value == NULL) {
PyErr_SetString(PyExc_AttributeError, "Cannot delete the attribute");
return -1;
}

if (!PyBool_Check(value)) {
PyErr_SetString(PyExc_TypeError, "log_callsite must be a boolean");
return -1;
}

if (value == Py_True) {
SET_FLAG(self->check_flags, SNAPTRACE_LOG_CALLSITE);
} else {
UNSET_FLAG(self->check_flags, SNAPTRACE_LOG_CALLSITE);
}
return 0;
}

static PyObject*
Tracer_log_callsite_getter(TracerObject* self, void* closure)
{
if (CHECK_FLAG(self->check_flags, SNAPTRACE_LOG_CALLSITE)) {
Py_RETURN_TRUE;
} else {
Py_RETURN_FALSE;
}
}
3 changes: 3 additions & 0 deletions src/viztracer/viztracer.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def __init__(self,
ignore_frozen: bool = False,
log_func_retval: bool = False,
log_func_args: bool = False,
log_callsite: bool = False,
log_func_repr: Callable[..., str] | None = None,
log_func_with_objprint: bool = False,
log_print: bool = False,
Expand Down Expand Up @@ -60,6 +61,7 @@ def __init__(self,
self.ignore_frozen = ignore_frozen
self.log_func_args = log_func_args
self.log_func_retval = log_func_retval
self.log_callsite = log_callsite
self.log_async = log_async
self.log_gc = log_gc
self.log_print = log_print
Expand Down Expand Up @@ -149,6 +151,7 @@ def init_kwargs(self) -> dict:
"ignore_frozen": self.ignore_frozen,
"log_func_retval": self.log_func_retval,
"log_func_args": self.log_func_args,
"log_callsite": self.log_callsite,
"log_print": self.log_print,
"log_gc": self.log_gc,
"log_sparse": self.log_sparse,
Expand Down