From 26566195fe41c82e590d27e2e9c0ebc1ccf7485b Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Jul 2014 10:19:51 -0400 Subject: Start implementing coroutine tracing in C tracer, will be hard :( --HG-- branch : c-coroutine --- coverage/tracer.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/coverage/tracer.c b/coverage/tracer.c index ca8d61c1..bfebf1d4 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -66,6 +66,7 @@ typedef struct { /* Python objects manipulated directly by the Collector class. */ PyObject * should_trace; PyObject * warn; + PyObject * coroutine_id_func; PyObject * data; PyObject * should_trace_cache; PyObject * arcs; @@ -138,6 +139,7 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) self->should_trace = NULL; self->warn = NULL; + self->coroutine_id_func = NULL; self->data = NULL; self->should_trace_cache = NULL; self->arcs = NULL; @@ -171,6 +173,7 @@ CTracer_dealloc(CTracer *self) Py_XDECREF(self->should_trace); Py_XDECREF(self->warn); + Py_XDECREF(self->coroutine_id_func); Py_XDECREF(self->data); Py_XDECREF(self->should_trace_cache); @@ -250,6 +253,18 @@ CTracer_record_pair(CTracer *self, int l1, int l2) return ret; } +/* Get the proper data_stack to use. In Python, defaultdict makes this easier. */ +static int +CTracer_get_data_stack(CTracer *self) +{ + if (self->coroutine_id_func) { + } + else { + } + + return RET_OK; +} + /* * The Trace Function */ @@ -610,6 +625,9 @@ CTracer_members[] = { { "warn", T_OBJECT, offsetof(CTracer, warn), 0, PyDoc_STR("Function for issuing warnings.") }, + { "coroutine_id_func", T_OBJECT, offsetof(CTracer, coroutine_id_func), 0, + PyDoc_STR("Function for determining coroutine context") }, + { "data", T_OBJECT, offsetof(CTracer, data), 0, PyDoc_STR("The raw dictionary of trace data.") }, -- cgit v1.2.1 From 7bebd77165852ba165406df1587f8b9a69ab1363 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Jul 2014 20:30:09 -0400 Subject: An option to not run the PyTracer tests --HG-- branch : c-coroutine --- igor.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/igor.py b/igor.py index e0071bd8..07e13f68 100644 --- a/igor.py +++ b/igor.py @@ -45,6 +45,9 @@ def run_tests(tracer, *nose_args): import nose.core if tracer == "py": label = "with Python tracer" + if os.environ.get("COVERAGE_NO_PYTRACER"): + print("Skipping tests, don't want PyTracer") + return else: label = "with C tracer" if os.environ.get("COVERAGE_NO_EXTENSION"): -- cgit v1.2.1 From a8ace9db7e4ba7fbcf2a068b8f1e52fee5a3624a Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sun, 20 Jul 2014 20:31:12 -0400 Subject: Refactor some C tracer code in prep for data stacks --HG-- branch : c-coroutine --- coverage/tracer.c | 116 +++++++++++++++++++++++++++--------------------------- 1 file changed, 59 insertions(+), 57 deletions(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index bfebf1d4..3cf8c81d 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -54,10 +54,23 @@ frame. */ typedef struct { - PyObject * file_data; /* PyMem_Malloc'ed, a borrowed ref. */ + /* The current file_data dictionary. Borrowed. */ + PyObject * file_data; + + /* The line number of the last line recorded, for tracing arcs. + -1 means there was no previous line, as when entering a code object. + */ int last_line; } DataStackEntry; +/* A data stack is a dynamically allocated vector of DataStackEntry's. */ +typedef struct { + int depth; /* The index of the last-used entry in stack. */ + int alloc; /* number of entries allocated at stack. */ + /* The file data at each level, or NULL if not recording. */ + DataStackEntry * stack; +} DataStack; + /* The CTracer type. */ typedef struct { @@ -87,19 +100,11 @@ typedef struct { the keys are line numbers. In both cases, the value is irrelevant (None). */ - /* The index of the last-used entry in data_stack. */ - int depth; - /* The file data at each level, or NULL if not recording. */ - DataStackEntry * data_stack; - int data_stack_alloc; /* number of entries allocated at data_stack. */ - /* The current file_data dictionary. Borrowed. */ - PyObject * cur_file_data; + DataStack data_stack; - /* The line number of the last line recorded, for tracing arcs. - -1 means there was no previous line, as when entering a code object. - */ - int last_line; + /* The current file's data stack entry, copied from the stack. */ + DataStackEntry cur_entry; /* The parent frame for the last exception event, to fix missing returns. */ PyFrameObject * last_exc_back; @@ -147,17 +152,17 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) self->started = 0; self->tracing_arcs = 0; - self->depth = -1; - self->data_stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry)); - if (self->data_stack == NULL) { + self->data_stack.depth = -1; + self->data_stack.stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry)); + if (self->data_stack.stack == NULL) { STATS( self->stats.errors++; ) PyErr_NoMemory(); return RET_ERROR; } - self->data_stack_alloc = STACK_DELTA; + self->data_stack.alloc = STACK_DELTA; - self->cur_file_data = NULL; - self->last_line = -1; + self->cur_entry.file_data = NULL; + self->cur_entry.last_line = -1; self->last_exc_back = NULL; @@ -177,7 +182,7 @@ CTracer_dealloc(CTracer *self) Py_XDECREF(self->data); Py_XDECREF(self->should_trace_cache); - PyMem_Free(self->data_stack); + PyMem_Free(self->data_stack.stack); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -232,7 +237,7 @@ showlog(int depth, int lineno, PyObject * filename, const char * msg) static const char * what_sym[] = {"CALL", "EXC ", "LINE", "RET "}; #endif -/* Record a pair of integers in self->cur_file_data. */ +/* Record a pair of integers in self->cur_entry.file_data. */ static int CTracer_record_pair(CTracer *self, int l1, int l2) { @@ -240,7 +245,7 @@ CTracer_record_pair(CTracer *self, int l1, int l2) PyObject * t = Py_BuildValue("(ii)", l1, l2); if (t != NULL) { - if (PyDict_SetItem(self->cur_file_data, t, Py_None) < 0) { + if (PyDict_SetItem(self->cur_entry.file_data, t, Py_None) < 0) { STATS( self->stats.errors++; ) ret = RET_ERROR; } @@ -309,16 +314,15 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse we'll need to keep more of the missed frame's state. */ STATS( self->stats.missed_returns++; ) - if (self->depth >= 0) { - if (self->tracing_arcs && self->cur_file_data) { - if (CTracer_record_pair(self, self->last_line, -self->last_exc_firstlineno) < 0) { + if (self->data_stack.depth >= 0) { + if (self->tracing_arcs && self->cur_entry.file_data) { + if (CTracer_record_pair(self, self->cur_entry.last_line, -self->last_exc_firstlineno) < 0) { return RET_ERROR; } } - SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn"); - self->cur_file_data = self->data_stack[self->depth].file_data; - self->last_line = self->data_stack[self->depth].last_line; - self->depth--; + SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn"); + self->cur_entry = self->data_stack.stack[self->data_stack.depth]; + self->data_stack.depth--; } } self->last_exc_back = NULL; @@ -329,25 +333,24 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse case PyTrace_CALL: /* 0 */ STATS( self->stats.calls++; ) /* Grow the stack. */ - self->depth++; - if (self->depth >= self->data_stack_alloc) { + self->data_stack.depth++; + if (self->data_stack.depth >= self->data_stack.alloc) { STATS( self->stats.stack_reallocs++; ) /* We've outgrown our data_stack array: make it bigger. */ - int bigger = self->data_stack_alloc + STACK_DELTA; - DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack, bigger * sizeof(DataStackEntry)); + int bigger = self->data_stack.alloc + STACK_DELTA; + DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack.stack, bigger * sizeof(DataStackEntry)); if (bigger_data_stack == NULL) { STATS( self->stats.errors++; ) PyErr_NoMemory(); - self->depth--; + self->data_stack.depth--; return RET_ERROR; } - self->data_stack = bigger_data_stack; - self->data_stack_alloc = bigger; + self->data_stack.stack = bigger_data_stack; + self->data_stack.alloc = bigger; } /* Push the current state on the stack. */ - self->data_stack[self->depth].file_data = self->cur_file_data; - self->data_stack[self->depth].last_line = self->last_line; + self->data_stack.stack[self->data_stack.depth] = self->cur_entry; /* Check if we should trace this line. */ filename = frame->f_code->co_filename; @@ -399,50 +402,49 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse return RET_ERROR; } } - self->cur_file_data = file_data; + self->cur_entry.file_data = file_data; /* Make the frame right in case settrace(gettrace()) happens. */ Py_INCREF(self); frame->f_trace = (PyObject*)self; - SHOWLOG(self->depth, frame->f_lineno, filename, "traced"); + SHOWLOG(self->data_stack.depth, frame->f_lineno, filename, "traced"); } else { - self->cur_file_data = NULL; - SHOWLOG(self->depth, frame->f_lineno, filename, "skipped"); + self->cur_entry.file_data = NULL; + SHOWLOG(self->data_stack.depth, frame->f_lineno, filename, "skipped"); } Py_DECREF(tracename); Py_DECREF(disposition); - self->last_line = -1; + self->cur_entry.last_line = -1; break; case PyTrace_RETURN: /* 3 */ STATS( self->stats.returns++; ) /* A near-copy of this code is above in the missing-return handler. */ - if (self->depth >= 0) { - if (self->tracing_arcs && self->cur_file_data) { + if (self->data_stack.depth >= 0) { + if (self->tracing_arcs && self->cur_entry.file_data) { int first = frame->f_code->co_firstlineno; - if (CTracer_record_pair(self, self->last_line, -first) < 0) { + if (CTracer_record_pair(self, self->cur_entry.last_line, -first) < 0) { return RET_ERROR; } } - SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "return"); - self->cur_file_data = self->data_stack[self->depth].file_data; - self->last_line = self->data_stack[self->depth].last_line; - self->depth--; + SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "return"); + self->cur_entry = self->data_stack.stack[self->data_stack.depth]; + self->data_stack.depth--; } break; case PyTrace_LINE: /* 2 */ STATS( self->stats.lines++; ) - if (self->depth >= 0) { - SHOWLOG(self->depth, frame->f_lineno, frame->f_code->co_filename, "line"); - if (self->cur_file_data) { + if (self->data_stack.depth >= 0) { + SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "line"); + if (self->cur_entry.file_data) { /* We're tracing in this frame: record something. */ if (self->tracing_arcs) { /* Tracing arcs: key is (last_line,this_line). */ - if (CTracer_record_pair(self, self->last_line, frame->f_lineno) < 0) { + if (CTracer_record_pair(self, self->cur_entry.last_line, frame->f_lineno) < 0) { return RET_ERROR; } } @@ -453,7 +455,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse STATS( self->stats.errors++; ) return RET_ERROR; } - ret = PyDict_SetItem(self->cur_file_data, this_line, Py_None); + ret = PyDict_SetItem(self->cur_entry.file_data, this_line, Py_None); Py_DECREF(this_line); if (ret < 0) { STATS( self->stats.errors++; ) @@ -461,7 +463,7 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse } } } - self->last_line = frame->f_lineno; + self->cur_entry.last_line = frame->f_lineno; } break; @@ -577,7 +579,7 @@ CTracer_start(CTracer *self, PyObject *args_unused) PyEval_SetTrace((Py_tracefunc)CTracer_trace, (PyObject*)self); self->started = 1; self->tracing_arcs = self->arcs && PyObject_IsTrue(self->arcs); - self->last_line = -1; + self->cur_entry.last_line = -1; /* start() returns a trace function usable with sys.settrace() */ Py_INCREF(self); @@ -609,7 +611,7 @@ CTracer_get_stats(CTracer *self) "new_files", self->stats.new_files, "missed_returns", self->stats.missed_returns, "stack_reallocs", self->stats.stack_reallocs, - "stack_alloc", self->data_stack_alloc, + "stack_alloc", self->data_stack.alloc, "errors", self->stats.errors ); #else -- cgit v1.2.1 From 873b19b457aaf4d2cfac90a1179e4b3581a44bc8 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Fri, 25 Jul 2014 08:00:02 -0600 Subject: Refactor DataStack code into its own functions --HG-- branch : c-coroutine --- coverage/tracer.c | 118 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 79 insertions(+), 39 deletions(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index 3cf8c81d..28b0a757 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -102,6 +102,7 @@ typedef struct { */ DataStack data_stack; + DataStack * pdata_stack; /* The current file's data stack entry, copied from the stack. */ DataStackEntry cur_entry; @@ -125,8 +126,51 @@ typedef struct { #endif /* COLLECT_STATS */ } CTracer; + #define STACK_DELTA 100 +static int +DataStack_init(CTracer *self, DataStack *pdata_stack) +{ + pdata_stack->depth = -1; + pdata_stack->stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry)); + if (pdata_stack->stack == NULL) { + STATS( self->stats.errors++; ) + PyErr_NoMemory(); + return RET_ERROR; + } + pdata_stack->alloc = STACK_DELTA; + return RET_OK; +} + +static void +DataStack_dealloc(CTracer *self, DataStack *pdata_stack) +{ + PyMem_Free(pdata_stack->stack); +} + +static int +DataStack_grow(CTracer *self, DataStack *pdata_stack) +{ + pdata_stack->depth++; + if (pdata_stack->depth >= pdata_stack->alloc) { + STATS( self->stats.stack_reallocs++; ) + /* We've outgrown our data_stack array: make it bigger. */ + int bigger = pdata_stack->alloc + STACK_DELTA; + DataStackEntry * bigger_data_stack = PyMem_Realloc(pdata_stack->stack, bigger * sizeof(DataStackEntry)); + if (bigger_data_stack == NULL) { + STATS( self->stats.errors++; ) + PyErr_NoMemory(); + pdata_stack->depth--; + return RET_ERROR; + } + pdata_stack->stack = bigger_data_stack; + pdata_stack->alloc = bigger; + } + return RET_OK; +} + + static int CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) { @@ -152,14 +196,10 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) self->started = 0; self->tracing_arcs = 0; - self->data_stack.depth = -1; - self->data_stack.stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry)); - if (self->data_stack.stack == NULL) { - STATS( self->stats.errors++; ) - PyErr_NoMemory(); + if (DataStack_init(self, &self->data_stack)) { return RET_ERROR; } - self->data_stack.alloc = STACK_DELTA; + self->pdata_stack = &self->data_stack; self->cur_entry.file_data = NULL; self->cur_entry.last_line = -1; @@ -182,7 +222,7 @@ CTracer_dealloc(CTracer *self) Py_XDECREF(self->data); Py_XDECREF(self->should_trace_cache); - PyMem_Free(self->data_stack.stack); + DataStack_dealloc(self, &self->data_stack); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -258,16 +298,19 @@ CTracer_record_pair(CTracer *self, int l1, int l2) return ret; } -/* Get the proper data_stack to use. In Python, defaultdict makes this easier. */ +/* Set self->pdata_stack to the proper data_stack to use. In Python, defaultdict makes this easier. */ static int -CTracer_get_data_stack(CTracer *self) +CTracer_set_pdata_stack(CTracer *self) { + self->pdata_stack = &self->data_stack; + return RET_OK; +/* if (self->coroutine_id_func) { } else { + return &self->data_stack; } - - return RET_OK; +*/ } /* @@ -314,15 +357,18 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse we'll need to keep more of the missed frame's state. */ STATS( self->stats.missed_returns++; ) - if (self->data_stack.depth >= 0) { + if (CTracer_set_pdata_stack(self)) { + return RET_ERROR; + } + if (self->pdata_stack->depth >= 0) { if (self->tracing_arcs && self->cur_entry.file_data) { if (CTracer_record_pair(self, self->cur_entry.last_line, -self->last_exc_firstlineno) < 0) { return RET_ERROR; } } - SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn"); - self->cur_entry = self->data_stack.stack[self->data_stack.depth]; - self->data_stack.depth--; + SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "missedreturn"); + self->cur_entry = self->pdata_stack->stack[self->pdata_stack->depth]; + self->pdata_stack->depth--; } } self->last_exc_back = NULL; @@ -333,24 +379,15 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse case PyTrace_CALL: /* 0 */ STATS( self->stats.calls++; ) /* Grow the stack. */ - self->data_stack.depth++; - if (self->data_stack.depth >= self->data_stack.alloc) { - STATS( self->stats.stack_reallocs++; ) - /* We've outgrown our data_stack array: make it bigger. */ - int bigger = self->data_stack.alloc + STACK_DELTA; - DataStackEntry * bigger_data_stack = PyMem_Realloc(self->data_stack.stack, bigger * sizeof(DataStackEntry)); - if (bigger_data_stack == NULL) { - STATS( self->stats.errors++; ) - PyErr_NoMemory(); - self->data_stack.depth--; - return RET_ERROR; - } - self->data_stack.stack = bigger_data_stack; - self->data_stack.alloc = bigger; + if (CTracer_set_pdata_stack(self)) { + return RET_ERROR; + } + if (DataStack_grow(self, self->pdata_stack)) { + return RET_ERROR; } /* Push the current state on the stack. */ - self->data_stack.stack[self->data_stack.depth] = self->cur_entry; + self->pdata_stack->stack[self->pdata_stack->depth] = self->cur_entry; /* Check if we should trace this line. */ filename = frame->f_code->co_filename; @@ -406,11 +443,11 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse /* Make the frame right in case settrace(gettrace()) happens. */ Py_INCREF(self); frame->f_trace = (PyObject*)self; - SHOWLOG(self->data_stack.depth, frame->f_lineno, filename, "traced"); + SHOWLOG(self->pdata_stack->depth, frame->f_lineno, filename, "traced"); } else { self->cur_entry.file_data = NULL; - SHOWLOG(self->data_stack.depth, frame->f_lineno, filename, "skipped"); + SHOWLOG(self->pdata_stack->depth, frame->f_lineno, filename, "skipped"); } Py_DECREF(tracename); @@ -422,7 +459,10 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse case PyTrace_RETURN: /* 3 */ STATS( self->stats.returns++; ) /* A near-copy of this code is above in the missing-return handler. */ - if (self->data_stack.depth >= 0) { + if (CTracer_set_pdata_stack(self)) { + return RET_ERROR; + } + if (self->pdata_stack->depth >= 0) { if (self->tracing_arcs && self->cur_entry.file_data) { int first = frame->f_code->co_firstlineno; if (CTracer_record_pair(self, self->cur_entry.last_line, -first) < 0) { @@ -430,16 +470,16 @@ CTracer_trace(CTracer *self, PyFrameObject *frame, int what, PyObject *arg_unuse } } - SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "return"); - self->cur_entry = self->data_stack.stack[self->data_stack.depth]; - self->data_stack.depth--; + SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "return"); + self->cur_entry = self->pdata_stack->stack[self->pdata_stack->depth]; + self->pdata_stack->depth--; } break; case PyTrace_LINE: /* 2 */ STATS( self->stats.lines++; ) - if (self->data_stack.depth >= 0) { - SHOWLOG(self->data_stack.depth, frame->f_lineno, frame->f_code->co_filename, "line"); + if (self->pdata_stack->depth >= 0) { + SHOWLOG(self->pdata_stack->depth, frame->f_lineno, frame->f_code->co_filename, "line"); if (self->cur_entry.file_data) { /* We're tracing in this frame: record something. */ if (self->tracing_arcs) { @@ -611,7 +651,7 @@ CTracer_get_stats(CTracer *self) "new_files", self->stats.new_files, "missed_returns", self->stats.missed_returns, "stack_reallocs", self->stats.stack_reallocs, - "stack_alloc", self->data_stack.alloc, + "stack_alloc", self->pdata_stack->alloc, "errors", self->stats.errors ); #else -- cgit v1.2.1 From 7f041a2e34847a3e871d4968decfe2a4231a7498 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 26 Jul 2014 15:28:23 -0600 Subject: Finish implementation of coroutine_id_func in C tracer, but it doesn't make gevent better. --HG-- branch : c-coroutine --- coverage/tracer.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 77 insertions(+), 15 deletions(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index 28b0a757..f2d0e628 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -30,6 +30,7 @@ #define MyText_AS_BYTES(o) PyUnicode_AsASCIIString(o) #define MyText_AS_STRING(o) PyBytes_AS_STRING(o) #define MyInt_FromLong(l) PyLong_FromLong(l) +#define MyInt_AsLong(o) PyLong_AsLong(o) #define MyType_HEAD_INIT PyVarObject_HEAD_INIT(NULL, 0) @@ -40,6 +41,7 @@ #define MyText_AS_BYTES(o) (Py_INCREF(o), o) #define MyText_AS_STRING(o) PyString_AS_STRING(o) #define MyInt_FromLong(l) PyInt_FromLong(l) +#define MyInt_AsLong(o) PyInt_AsLong(o) #define MyType_HEAD_INIT PyObject_HEAD_INIT(NULL) 0, @@ -101,7 +103,12 @@ typedef struct { (None). */ - DataStack data_stack; + DataStack data_stack; /* Used if we aren't doing coroutines. */ + PyObject * data_stack_index; /* Used if we are doing coroutines. */ + DataStack * data_stacks; + int data_stacks_alloc; + int data_stacks_used; + DataStack * pdata_stack; /* The current file's data stack entry, copied from the stack. */ @@ -133,13 +140,8 @@ static int DataStack_init(CTracer *self, DataStack *pdata_stack) { pdata_stack->depth = -1; - pdata_stack->stack = PyMem_Malloc(STACK_DELTA*sizeof(DataStackEntry)); - if (pdata_stack->stack == NULL) { - STATS( self->stats.errors++; ) - PyErr_NoMemory(); - return RET_ERROR; - } - pdata_stack->alloc = STACK_DELTA; + pdata_stack->stack = NULL; + pdata_stack->alloc = 0; return RET_OK; } @@ -199,6 +201,16 @@ CTracer_init(CTracer *self, PyObject *args_unused, PyObject *kwds_unused) if (DataStack_init(self, &self->data_stack)) { return RET_ERROR; } + self->data_stack_index = PyDict_New(); + if (self->data_stack_index == NULL) { + STATS( self->stats.errors++; ) + return RET_ERROR; + } + + self->data_stacks = NULL; + self->data_stacks_alloc = 0; + self->data_stacks_used = 0; + self->pdata_stack = &self->data_stack; self->cur_entry.file_data = NULL; @@ -223,6 +235,14 @@ CTracer_dealloc(CTracer *self) Py_XDECREF(self->should_trace_cache); DataStack_dealloc(self, &self->data_stack); + if (self->data_stacks) { + for (int i = 0; i < self->data_stacks_used; i++) { + DataStack_dealloc(self, self->data_stacks + i); + } + PyMem_Free(self->data_stacks); + } + + Py_XDECREF(self->data_stack_index); Py_TYPE(self)->tp_free((PyObject*)self); } @@ -298,19 +318,61 @@ CTracer_record_pair(CTracer *self, int l1, int l2) return ret; } -/* Set self->pdata_stack to the proper data_stack to use. In Python, defaultdict makes this easier. */ +/* Set self->pdata_stack to the proper data_stack to use. */ static int CTracer_set_pdata_stack(CTracer *self) { - self->pdata_stack = &self->data_stack; - return RET_OK; -/* - if (self->coroutine_id_func) { + if (self->coroutine_id_func != Py_None) { + PyObject * co_obj = NULL; + PyObject * stack_index = NULL; + long the_index = 0; + + co_obj = PyObject_CallObject(self->coroutine_id_func, NULL); + if (co_obj == NULL) { + return RET_ERROR; + } + stack_index = PyDict_GetItem(self->data_stack_index, co_obj); + if (stack_index == NULL) { + /* A new coroutine object. Make a new data stack. */ + the_index = self->data_stacks_used; + stack_index = MyInt_FromLong(the_index); + if (PyDict_SetItem(self->data_stack_index, co_obj, stack_index) < 0) { + /* TODO */ + Py_XDECREF(co_obj); + Py_XDECREF(stack_index); + return RET_ERROR; + } + self->data_stacks_used++; + if (self->data_stacks_used >= self->data_stacks_alloc) { + int bigger = self->data_stacks_alloc + 10; + DataStack * bigger_stacks = PyMem_Realloc(self->data_stacks, bigger * sizeof(DataStack)); + if (bigger_stacks == NULL) { + STATS( self->stats.errors++; ) + PyErr_NoMemory(); + Py_XDECREF(co_obj); + Py_XDECREF(stack_index); + return RET_ERROR; + } + self->data_stacks = bigger_stacks; + self->data_stacks_alloc = bigger; + } + DataStack_init(self, &self->data_stacks[the_index]); + } + else { + Py_INCREF(stack_index); + the_index = MyInt_AsLong(stack_index); + } + + self->pdata_stack = &self->data_stacks[the_index]; + + Py_XDECREF(co_obj); + Py_XDECREF(stack_index); } else { - return &self->data_stack; + self->pdata_stack = &self->data_stack; } -*/ + + return RET_OK; } /* -- cgit v1.2.1 From b33bba1f032530c7f5d4e1aee43b15b84239b816 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Sat, 26 Jul 2014 15:36:22 -0600 Subject: Remove a TODO --HG-- branch : c-coroutine --- coverage/tracer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coverage/tracer.c b/coverage/tracer.c index f2d0e628..3131d7df 100644 --- a/coverage/tracer.c +++ b/coverage/tracer.c @@ -337,7 +337,7 @@ CTracer_set_pdata_stack(CTracer *self) the_index = self->data_stacks_used; stack_index = MyInt_FromLong(the_index); if (PyDict_SetItem(self->data_stack_index, co_obj, stack_index) < 0) { - /* TODO */ + STATS( self->stats.errors++; ) Py_XDECREF(co_obj); Py_XDECREF(stack_index); return RET_ERROR; -- cgit v1.2.1 From d802adad2dd55ea2d34515de800e81cce13adff2 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 31 Jul 2014 06:39:50 -0400 Subject: Update tests for really testing gevent with a C extension. --HG-- branch : c-coroutine --- tests/test_coroutine.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_coroutine.py b/tests/test_coroutine.py index fe6c8326..b204827e 100644 --- a/tests/test_coroutine.py +++ b/tests/test_coroutine.py @@ -101,7 +101,7 @@ class CoroutineTest(CoverageTest): self.make_file("try_it.py", code) - out = self.run_command("coverage run --timid %s try_it.py" % args) + out = self.run_command("coverage run %s try_it.py" % args) expected_out = "%d\n" % (sum(range(self.LIMIT))) self.assertEqual(out, expected_out) @@ -129,7 +129,7 @@ class CoroutineTest(CoverageTest): self.try_some_code(self.EVENTLET, "--coroutine=eventlet") def test_gevent(self): - raise SkipTest("Still not sure why gevent isn't working...") + #raise SkipTest("Still not sure why gevent isn't working...") if gevent is None: raise SkipTest("No gevent available") -- cgit v1.2.1 From b4c78975cbc67d14bcd9cba5bff2abaa3850b1c9 Mon Sep 17 00:00:00 2001 From: Ned Batchelder Date: Thu, 31 Jul 2014 06:40:06 -0400 Subject: Temporary debugging tweaks to tests --HG-- branch : c-coroutine --- tests/test_coroutine.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_coroutine.py b/tests/test_coroutine.py index b204827e..38de7a3d 100644 --- a/tests/test_coroutine.py +++ b/tests/test_coroutine.py @@ -32,7 +32,7 @@ def line_count(s): class CoroutineTest(CoverageTest): """Tests of the coroutine support in coverage.py.""" - LIMIT = 1000 + LIMIT = 3 # Should be 1000, but this gives me a reasonable amount of output. # The code common to all the concurrency models. COMMON = """ @@ -136,6 +136,19 @@ class CoroutineTest(CoverageTest): self.try_some_code(self.GEVENT, "--coroutine=gevent") + def test_gevent_badly(self): + # This test shouldn't pass. It should fail because we are running + # gevent code without the --coroutine=gevent flag. It's here so I can + # see how gevent code looks when it isn't measured properly. The C + # extension implementation of coroutining is currently acting precisely + # as if no coroutine support is available (demonstrated by this test), + # and I don't know why. This test is part of me debugging that + # problem. + if gevent is None: + raise SkipTest("No gevent available") + + self.try_some_code(self.GEVENT, "") + def print_simple_annotation(code, linenos): """Print the lines in `code` with X for each line number in `linenos`.""" -- cgit v1.2.1