diff -r e06af4027546 Include/ceval.h --- a/Include/ceval.h Mon Feb 06 07:15:57 2017 -0800 +++ b/Include/ceval.h Mon Feb 06 11:58:04 2017 -0500 @@ -24,7 +24,8 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(void) PyEval_SetProfile(Py_tracefunc, PyObject *); -PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc, PyObject *); +PyAPI_FUNC(void) PyEval_SetTrace(Py_tracefunc func, PyObject *arg); +PyAPI_FUNC(void) _PyEval_SetTraceEx(Py_tracefunc func, PyObject *arg, int trace_instructions); PyAPI_FUNC(void) _PyEval_SetCoroutineWrapper(PyObject *); PyAPI_FUNC(PyObject *) _PyEval_GetCoroutineWrapper(void); PyAPI_FUNC(void) _PyEval_SetAsyncGenFirstiter(PyObject *); diff -r e06af4027546 Include/pystate.h --- a/Include/pystate.h Mon Feb 06 07:15:57 2017 -0800 +++ b/Include/pystate.h Mon Feb 06 11:58:04 2017 -0500 @@ -66,6 +66,7 @@ #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 @@ the trace/profile. */ int tracing; int use_tracing; + int inst_tracing; Py_tracefunc c_profilefunc; Py_tracefunc c_tracefunc; diff -r e06af4027546 Lib/test/test_sys_settrace.py --- a/Lib/test/test_sys_settrace.py Mon Feb 06 07:15:57 2017 -0800 +++ b/Lib/test/test_sys_settrace.py Mon Feb 06 11:58:04 2017 -0500 @@ -397,6 +397,14 @@ [(0, 'call'), (1, 'line')]) + def test_settrace_args(self): + def func(): + pass + # first parameter is mandatory + self.assertRaises(TypeError, sys.settrace) + # trace_instructions is a keyword-only parameter + self.assertRaises(TypeError, sys.settrace, func, True) + class RaisingTraceFuncTestCase(unittest.TestCase): def setUp(self): diff -r e06af4027546 Python/ceval.c --- a/Python/ceval.c Mon Feb 06 07:15:57 2017 -0800 +++ b/Python/ceval.c Mon Feb 06 11:58:04 2017 -0500 @@ -4539,6 +4539,7 @@ 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; @@ -4547,6 +4548,7 @@ result = PyObject_Call(func, args, NULL); tstate->tracing = save_tracing; tstate->use_tracing = save_use_tracing; + tstate->inst_tracing = save_inst_tracing; return result; } @@ -4576,6 +4578,9 @@ 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; } @@ -4598,7 +4603,7 @@ } void -PyEval_SetTrace(Py_tracefunc func, PyObject *arg) +_PyEval_SetTraceEx(Py_tracefunc func, PyObject *arg, int trace_instructions) { PyThreadState *tstate = PyThreadState_GET(); PyObject *temp = tstate->c_traceobj; @@ -4614,6 +4619,14 @@ /* Flag that tracing or profiling is turned on */ tstate->use_tracing = ((func != NULL) || (tstate->c_profilefunc != NULL)); + /* set the tracing mode: either line-oriented or per instruction. */ + tstate->inst_tracing = trace_instructions; +} + +void +PyEval_SetTrace(Py_tracefunc func, PyObject *arg) +{ + _PyEval_SetTraceEx(func, arg, 0); } void diff -r e06af4027546 Python/pystate.c --- a/Python/pystate.c Mon Feb 06 07:15:57 2017 -0800 +++ b/Python/pystate.c Mon Feb 06 11:58:04 2017 -0500 @@ -194,6 +194,7 @@ 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 -r e06af4027546 Python/sysmodule.c --- a/Python/sysmodule.c Mon Feb 06 07:15:57 2017 -0800 +++ b/Python/sysmodule.c Mon Feb 06 11:58:04 2017 -0500 @@ -355,18 +355,20 @@ * 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) @@ -436,7 +438,7 @@ return 0; result = call_trampoline(callback, frame, what, arg); if (result == NULL) { - PyEval_SetTrace(NULL, NULL); + _PyEval_SetTraceEx(NULL, NULL, 0); Py_CLEAR(frame->f_trace); return -1; } @@ -469,6 +471,29 @@ ); static PyObject * +sys_settraceinst(PyObject *self, PyObject *args) +{ + PyObject *trace_func = NULL; + int inst_tracing = 0; + if (trace_init() == -1) + return NULL; + if (!PyArg_ParseTuple(args, "Oi", &trace_func, &inst_tracing)) + return NULL; + if (trace_func == Py_None) + _PyEval_SetTraceEx(NULL, NULL, 0); + else + _PyEval_SetTraceEx(trace_trampoline, trace_func, inst_tracing); + Py_RETURN_NONE; +} + +PyDoc_STRVAR(settraceinst_doc, +"settraceinst(function)\n\ +\n\ +Set the global debug tracing function, and enable instruction tracing. \n\ +TODO: refer to the detailed documentation (also fix settrace doc above)." +); + +static PyObject * sys_gettrace(PyObject *self, PyObject *args) { PyThreadState *tstate = PyThreadState_GET(); @@ -483,11 +508,32 @@ PyDoc_STRVAR(gettrace_doc, "gettrace()\n\ \n\ -Return the global debug tracing function set with sys.settrace.\n\ +Return the global debug tracing function set with sys.settrace. \n\ See the debugger chapter in the library manual." ); static PyObject * +sys_gettraceinst(PyObject *self, PyObject *args) +{ + PyThreadState *tstate = PyThreadState_GET(); + PyObject *trace_func = tstate->c_traceobj; + + if (trace_func == NULL) + trace_func = Py_None; + Py_INCREF(trace_func); + + return Py_BuildValue("(Oi)", trace_func, tstate->inst_tracing); +} + +PyDoc_STRVAR(gettraceinst_doc, +"gettraceinst()\n\ +\n\ +Return a tuple containing global debug tracing function \n\ +and an integer indicating whether instruction tracing is enabled via \n\ +sys.settraceinst." +); + +static PyObject * sys_setprofile(PyObject *self, PyObject *args) { if (trace_init() == -1) @@ -1439,7 +1485,9 @@ {"setrecursionlimit", sys_setrecursionlimit, METH_VARARGS, setrecursionlimit_doc}, {"settrace", sys_settrace, METH_O, settrace_doc}, + {"settraceinst", sys_settraceinst, METH_VARARGS, settraceinst_doc}, {"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc}, + {"gettraceinst", sys_gettraceinst, METH_NOARGS, gettraceinst_doc}, {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, {"_debugmallocstats", sys_debugmallocstats, METH_NOARGS, debugmallocstats_doc},