diff -r aa7ac93d23b2 Include/ceval.h --- a/Include/ceval.h Wed Feb 01 23:12:20 2017 +0200 +++ b/Include/ceval.h Wed Feb 01 22:42:05 2017 +0100 @@ -24,7 +24,8 @@ PyAPI_FUNC(PyObject *) PyEval_CallMethod #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 aa7ac93d23b2 Include/pystate.h --- a/Include/pystate.h Wed Feb 01 23:12:20 2017 +0200 +++ b/Include/pystate.h Wed Feb 01 22:42:05 2017 +0100 @@ -66,6 +66,7 @@ typedef int (*Py_tracefunc)(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 -r aa7ac93d23b2 Lib/test/test_sys_settrace.py --- a/Lib/test/test_sys_settrace.py Wed Feb 01 23:12:20 2017 +0200 +++ b/Lib/test/test_sys_settrace.py Wed Feb 01 22:42:05 2017 +0100 @@ -397,6 +397,14 @@ class TraceTestCase(unittest.TestCase): [(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 aa7ac93d23b2 Python/ceval.c --- a/Python/ceval.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Python/ceval.c Wed Feb 01 22:42:05 2017 +0100 @@ -4539,6 +4539,7 @@ PyObject * 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 @@ PyObject * 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 @@ maybe_call_line_trace(Py_tracefunc func, 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 @@ PyEval_SetProfile(Py_tracefunc func, PyO } 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 @@ PyEval_SetTrace(Py_tracefunc func, PyObj /* 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 = trace_instructions; +} + +void +PyEval_SetTrace(Py_tracefunc func, PyObject *arg) +{ + _PyEval_SetTraceEx(func, arg, 0); } void diff -r aa7ac93d23b2 Python/pystate.c --- a/Python/pystate.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Python/pystate.c Wed Feb 01 22:42:05 2017 +0100 @@ -194,6 +194,7 @@ new_threadstate(PyInterpreterState *inte 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 aa7ac93d23b2 Python/sysmodule.c --- a/Python/sysmodule.c Wed Feb 01 23:12:20 2017 +0200 +++ b/Python/sysmodule.c Wed Feb 01 22:42:05 2017 +0100 @@ -355,18 +355,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) @@ -436,7 +438,7 @@ trace_trampoline(PyObject *self, PyFrame 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; } @@ -450,15 +452,28 @@ trace_trampoline(PyObject *self, PyFrame } static PyObject * -sys_settrace(PyObject *self, PyObject *args) +sys_settrace(PyObject *self, PyObject *args, PyObject *kwargs) { + static char *kwlist[] = {"", "trace_instructions", NULL}; + PyObject *trace_callback; + int trace_instructions = 0; + if (trace_init() == -1) return NULL; - if (args == Py_None) - PyEval_SetTrace(NULL, NULL); - else - PyEval_SetTrace(trace_trampoline, args); - Py_RETURN_NONE; + + if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|$p:settrace", kwlist, + &trace_callback, &trace_instructions)) { + return NULL; + } + if (trace_callback == Py_None) { + _PyEval_SetTraceEx(NULL, NULL, 0); + } + else { + _PyEval_SetTraceEx(trace_trampoline, trace_callback, + trace_instructions); + } + Py_INCREF(Py_None); + return Py_None; } PyDoc_STRVAR(settrace_doc, @@ -1438,7 +1453,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", (PyCFunction)sys_settrace, METH_VARARGS|METH_KEYWORDS, settrace_doc}, {"gettrace", sys_gettrace, METH_NOARGS, gettrace_doc}, {"call_tracing", sys_call_tracing, METH_VARARGS, call_tracing_doc}, {"_debugmallocstats", sys_debugmallocstats, METH_NOARGS,