Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix the recording of arcs for non-Python files #1051

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions coverage/ctracer/datastack.c
Expand Up @@ -45,6 +45,9 @@ DataStack_grow(Stats *pstats, DataStack *pdata_stack)

pdata_stack->stack = bigger_data_stack;
pdata_stack->alloc = bigger;
} else {
/* Zero the entry, because it may have been previously used and can still contain data. */
memset(pdata_stack->stack + pdata_stack->depth, 0, sizeof(DataStackEntry));
}
return RET_OK;
}
3 changes: 2 additions & 1 deletion coverage/ctracer/datastack.h
Expand Up @@ -22,7 +22,8 @@ typedef struct DataStackEntry {
PyObject * file_tracer;

/* The line number of the last line recorded, for tracing arcs.
-1 means there was no previous line, as when entering a code object.
0 means there was no previous line.
A negative number -N means we've just entered the code object which starts at line N.
*/
int last_line;

Expand Down
143 changes: 72 additions & 71 deletions coverage/ctracer/tracer.c
Expand Up @@ -296,6 +296,53 @@ CTracer_set_pdata_stack(CTracer *self)
* Parts of the trace function.
*/

static void
CTracer_line_number_range(CTracer *self, PyFrameObject *frame, int lineno, int *lineno_from, int *lineno_to)
{
if (self->pcur_entry->file_tracer == Py_None) {
*lineno_from = *lineno_to = lineno;
return;
}

PyObject * from_to = NULL;
PyObject * pyint = NULL;

int f_lineno = PyFrame_GetLineNumber(frame);
if (lineno != f_lineno) {
/* Tracers look at f_lineno, so that's where we store the line number we want them to see. */
frame->f_lineno = lineno;
}

STATS( self->stats.pycalls++; )
from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
frame->f_lineno = f_lineno;
if (from_to == NULL) {
goto error;
}
if (!PyTuple_Check(from_to) || PyTuple_Size(from_to) != 2) {
PyErr_SetString(
PyExc_TypeError,
"line_number_range must return 2-tuple"
);
goto error;
}
pyint = PyTuple_GetItem(from_to, 0);
if (pyint == NULL || pyint_as_int(pyint, lineno_from) < 0) {
goto error;
}
pyint = PyTuple_GetItem(from_to, 1);
if (pyint == NULL || pyint_as_int(pyint, lineno_to) < 0) {
goto error;
}
goto cleanup;

error:
CTracer_disable_plugin(self, self->pcur_entry->disposition);

cleanup:
Py_XDECREF(from_to);
}

static int
CTracer_handle_call(CTracer *self, PyFrameObject *frame)
{
Expand Down Expand Up @@ -542,11 +589,16 @@ CTracer_handle_call(CTracer *self, PyFrameObject *frame)
real_call = (MyFrame_GetLasti(frame) < 0);
#endif

if (real_call) {
self->pcur_entry->last_line = -MyFrame_GetCode(frame)->co_firstlineno;
}
else {
self->pcur_entry->last_line = PyFrame_GetLineNumber(frame);
if (self->tracing_arcs && self->pcur_entry->file_data) {
int lineno_from = 0, lineno_to = 0;
if (real_call) {
CTracer_line_number_range(self, frame, MyFrame_GetCode(frame)->co_firstlineno, &lineno_from, &lineno_to);
} else {
CTracer_line_number_range(self, frame, PyFrame_GetLineNumber(frame), &lineno_from, &lineno_to);
}
if (lineno_from > 0) {
self->pcur_entry->last_line = real_call ? -lineno_from : lineno_from;
}
}

ok:
Expand All @@ -571,6 +623,9 @@ CTracer_disable_plugin(CTracer *self, PyObject * disposition)
PyObject * ret;
PyErr_Print();

self->pcur_entry->file_data = NULL;
self->pcur_entry->file_tracer = Py_None;

STATS( self->stats.pycalls++; )
ret = PyObject_CallFunctionObjArgs(self->disable_plugin, disposition, NULL);
if (ret == NULL) {
Expand All @@ -588,40 +643,6 @@ CTracer_disable_plugin(CTracer *self, PyObject * disposition)
PyErr_Print();
}


static int
CTracer_unpack_pair(CTracer *self, PyObject *pair, int *p_one, int *p_two)
{
int ret = RET_ERROR;
int the_int;
PyObject * pyint = NULL;
int index;

if (!PyTuple_Check(pair) || PyTuple_Size(pair) != 2) {
PyErr_SetString(
PyExc_TypeError,
"line_number_range must return 2-tuple"
);
goto error;
}

for (index = 0; index < 2; index++) {
pyint = PyTuple_GetItem(pair, index);
if (pyint == NULL) {
goto error;
}
if (pyint_as_int(pyint, &the_int) < 0) {
goto error;
}
*(index == 0 ? p_one : p_two) = the_int;
}

ret = RET_OK;

error:
return ret;
}

static int
CTracer_handle_line(CTracer *self, PyFrameObject *frame)
{
Expand All @@ -632,36 +653,17 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
if (self->pdata_stack->depth >= 0) {
SHOWLOG(PyFrame_GetLineNumber(frame), MyFrame_GetCode(frame)->co_filename, "line");
if (self->pcur_entry->file_data) {
int lineno_from = -1;
int lineno_to = -1;

/* We're tracing in this frame: record something. */
if (self->pcur_entry->file_tracer != Py_None) {
PyObject * from_to = NULL;
STATS( self->stats.pycalls++; )
from_to = PyObject_CallMethodObjArgs(self->pcur_entry->file_tracer, str_line_number_range, frame, NULL);
if (from_to == NULL) {
CTracer_disable_plugin(self, self->pcur_entry->disposition);
goto ok;
}
ret2 = CTracer_unpack_pair(self, from_to, &lineno_from, &lineno_to);
Py_DECREF(from_to);
if (ret2 < 0) {
CTracer_disable_plugin(self, self->pcur_entry->disposition);
goto ok;
}
}
else {
lineno_from = lineno_to = PyFrame_GetLineNumber(frame);
}

if (lineno_from != -1) {
/* We're tracing in this frame: try to record something. */
int lineno_from = 0, lineno_to = 0;
CTracer_line_number_range(self, frame, PyFrame_GetLineNumber(frame), &lineno_from, &lineno_to);
if (lineno_from > 0) {
for (; lineno_from <= lineno_to; lineno_from++) {
if (self->tracing_arcs) {
/* Tracing arcs: key is (last_line,this_line). */
if (CTracer_record_pair(self, self->pcur_entry->last_line, lineno_from) < 0) {
goto error;
}
self->pcur_entry->last_line = lineno_from;
}
else {
/* Tracing lines: key is simply this_line. */
Expand All @@ -676,14 +678,11 @@ CTracer_handle_line(CTracer *self, PyFrameObject *frame)
goto error;
}
}

self->pcur_entry->last_line = lineno_from;
}
}
}
}

ok:
ret = RET_OK;

error:
Expand All @@ -699,7 +698,6 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame)
PyObject * pCode = NULL;

STATS( self->stats.returns++; )
/* A near-copy of this code is above in the missing-return handler. */
if (CTracer_set_pdata_stack(self) < 0) {
goto error;
}
Expand Down Expand Up @@ -737,9 +735,12 @@ CTracer_handle_return(CTracer *self, PyFrameObject *frame)
real_return = !(is_yield || is_yield_from);
#endif
if (real_return) {
int first = MyFrame_GetCode(frame)->co_firstlineno;
if (CTracer_record_pair(self, self->pcur_entry->last_line, -first) < 0) {
goto error;
int lineno_from = 0, lineno_to = 0;
CTracer_line_number_range(self, frame, MyFrame_GetCode(frame)->co_firstlineno, &lineno_from, &lineno_to);
if (lineno_from > 0) {
if (CTracer_record_pair(self, self->pcur_entry->last_line, -lineno_from) < 0) {
goto error;
}
}
}
}
Expand Down Expand Up @@ -920,7 +921,7 @@ CTracer_call(CTracer *self, PyObject *args, PyObject *kwds)
#endif

/* Save off the frame's lineno, and use the forced one, if provided. */
orig_lineno = frame->f_lineno;
orig_lineno = PyFrame_GetLineNumber(frame);
if (lineno > 0) {
frame->f_lineno = lineno;
}
Expand Down
2 changes: 1 addition & 1 deletion coverage/sqldata.py
Expand Up @@ -981,7 +981,7 @@ def arcs(self, filename: str) -> Optional[List[TArc]]:

Negative numbers have special meaning. If the starting line number is
-N, it represents an entry to the code object that starts at line N.
If the ending ling number is -N, it's an exit from the code object that
If the ending line number is -N, it's an exit from the code object that
starts at line N.

"""
Expand Down