diff --git a/Include/ceval.h b/Include/ceval.h index 89c6062..23295d5 100644 --- a/Include/ceval.h +++ b/Include/ceval.h @@ -23,6 +23,7 @@ PyAPI_FUNC(PyObject *) PyEval_CallMethod(PyObject *obj, #ifndef Py_LIMITED_API PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *); +PyAPI_FUNC(void) PyEval_SetTraceInstructions(int); PyAPI_FUNC(void) _PyEval_SetCoroutineWrapper(PyObject *); PyAPI_FUNC(PyObject *) _PyEval_GetCoroutineWrapper(void); PyAPI_FUNC(void) _PyEval_SetAsyncGenFirstiter(PyObject *); diff --git a/Include/pystate.h b/Include/pystate.h index afc3c0c..64f3f93 100644 --- a/Include/pystate.h +++ b/Include/pystate.h @@ -66,6 +66,7 @@ typedef int (*Py_tracefunc)(PyObject *, struct _frame *, int, PyObject *); #define PyTrace_C_CALL 4 #define PyTrace_C_EXCEPTION 5 #define PyTrace_C_RETURN 6 +#define PyTrace_INSTRUCTION 7 #endif #ifdef Py_LIMITED_API @@ -89,6 +90,7 @@ typedef struct _ts { the trace/profile. */ int tracing; int use_tracing; + int inst_tracing; Py_tracefunc c_profilefunc; Py_tracefunc c_tracefunc; diff --git a/Python/ceval.c b/Python/ceval.c index d5172b9..67250ee 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -4484,6 +4484,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args) PyThreadState *tstate = PyThreadState_GET(); int save_tracing = tstate->tracing; int save_use_tracing = tstate->use_tracing; + int save_inst_tracing = tstate->inst_tracing; PyObject *result; tstate->tracing = 0; @@ -4492,6 +4493,7 @@ _PyEval_CallTracing(PyObject *func, PyObject *args) result = PyObject_Call(func, args, NULL); tstate->tracing = save_tracing; tstate->use_tracing = save_use_tracing; + tstate->inst_tracing = save_inst_tracing; return result; } @@ -4521,6 +4523,9 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj, frame->f_lineno = line; result = call_trace(func, obj, tstate, frame, PyTrace_LINE, Py_None); } + else if (tstate->inst_tracing) { + result = call_trace(func, obj, tstate, frame, PyTrace_INSTRUCTION, Py_None); + } *instr_prev = frame->f_lasti; return result; } @@ -4559,6 +4564,18 @@ PyEval_SetTrace(Py_tracefunc func, PyObject *arg) /* Flag that tracing or profiling is turned on */ tstate->use_tracing = ((func != NULL) || (tstate->c_profilefunc != NULL)); + /* instruction tracing must be explicitly enabled after this call. */ + tstate->inst_tracing = 0; +} + +void +PyEval_SetTraceInstructions(int inst_tracing) +{ + /* for now this is provided as a separate function from PyEval_SetTrace, + to avoid breaking anything. Since PyEval_SetTrace is not part of the + limited API it may be appropriate to change PyEval_SetTrace though. */ + PyThreadState *tstate = PyThreadState_GET(); + tstate->inst_tracing = inst_tracing; } void diff --git a/Python/pystate.c b/Python/pystate.c index 65c244e..539225b 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -194,6 +194,7 @@ new_threadstate(PyInterpreterState *interp, int init) tstate->recursion_critical = 0; tstate->tracing = 0; tstate->use_tracing = 0; + tstate->inst_tracing = 0; tstate->gilstate_counter = 0; tstate->async_exc = NULL; #ifdef WITH_THREAD diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 52034ff..a5bd907 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -358,18 +358,20 @@ same value."); * Cached interned string objects used for calling the profile and * trace functions. Initialized by trace_init(). */ -static PyObject *whatstrings[7] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL}; +static PyObject *whatstrings[8] = { + NULL, NULL, NULL, NULL, NULL, NULL, NULL,NULL}; static int trace_init(void) { - static const char * const whatnames[7] = { + static const char * const whatnames[8] = { "call", "exception", "line", "return", - "c_call", "c_exception", "c_return" + "c_call", "c_exception", "c_return", + "instruction" }; PyObject *name; int i; - for (i = 0; i < 7; ++i) { + for (i = 0; i < 8; ++i) { if (whatstrings[i] == NULL) { name = PyUnicode_InternFromString(whatnames[i]); if (name == NULL) @@ -457,10 +459,20 @@ sys_settrace(PyObject *self, PyObject *args) { if (trace_init() == -1) return NULL; - if (args == Py_None) + PyObject *trace_callback = NULL; + int trace_instructions = 0; + if (!PyArg_ParseTuple(args, "O|p:settrace", + &trace_callback, &trace_instructions)) { + return NULL; + } + if (trace_callback == Py_None) { PyEval_SetTrace(NULL, NULL); - else - PyEval_SetTrace(trace_trampoline, args); + PyEval_SetTraceInstructions(0); + } + else { + PyEval_SetTrace(trace_trampoline, trace_callback); + PyEval_SetTraceInstructions(trace_instructions); + } Py_INCREF(Py_None); return Py_None; } @@ -1421,7 +1433,7 @@ static PyMethodDef sys_methods[] = { {"getprofile", sys_getprofile, METH_NOARGS, getprofile_doc}, {"setrecursionlimit", sys_setrecursionlimit, METH_VARARGS, setrecursionlimit_doc}, - {"settrace", sys_settrace, METH_O, settrace_doc}, + {"settrace", sys_settrace, METH_VARARGS, settrace_doc}, {"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc}, {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, {"_debugmallocstats", sys_debugmallocstats, METH_NOARGS,