This is my patch

[ 587993 ] SET_LINENO killer

Remove SET_LINENO.  Tracing is now supported by inspecting co_lnotab.

Many sundry changes to document and adapt to this change.
This commit is contained in:
Michael W. Hudson 2002-08-15 14:59:02 +00:00
parent add88060c1
commit dd32a91cc0
19 changed files with 341 additions and 187 deletions

View File

@ -23,17 +23,15 @@ the following command can be used to get the disassembly of
\begin{verbatim} \begin{verbatim}
>>> dis.dis(myfunc) >>> dis.dis(myfunc)
0 SET_LINENO 1 2 0 LOAD_GLOBAL 0 (len)
3 LOAD_FAST 0 (alist)
3 SET_LINENO 2 6 CALL_FUNCTION 1
6 LOAD_GLOBAL 0 (len) 9 RETURN_VALUE
9 LOAD_FAST 0 (alist) 10 RETURN_NONE
12 CALL_FUNCTION 1
15 RETURN_VALUE
16 LOAD_CONST 0 (None)
19 RETURN_VALUE
\end{verbatim} \end{verbatim}
(The ``2'' is a line number).
The \module{dis} module defines the following functions and constants: The \module{dis} module defines the following functions and constants:
\begin{funcdesc}{dis}{\optional{bytesource}} \begin{funcdesc}{dis}{\optional{bytesource}}
@ -56,6 +54,7 @@ Disassembles a code object, indicating the last instruction if \var{lasti}
was provided. The output is divided in the following columns: was provided. The output is divided in the following columns:
\begin{enumerate} \begin{enumerate}
\item the line number, for the first instruction of each line
\item the current instruction, indicated as \samp{-->}, \item the current instruction, indicated as \samp{-->},
\item a labelled instruction, indicated with \samp{>\code{>}}, \item a labelled instruction, indicated with \samp{>\code{>}},
\item the address of the instruction, \item the address of the instruction,
@ -402,6 +401,14 @@ is evaluated, the locals are passed to the class definition.
Returns with TOS to the caller of the function. Returns with TOS to the caller of the function.
\end{opcodedesc} \end{opcodedesc}
\begin{opcodedesc}{RETURN_NONE}{}
Returns \constant{None} to the caller of the function. This opcode is
generated as the last opcode of every function and only then, for
reasons to do with tracing support. See the comments in the function
\cfunction{maybe_call_line_trace} in \file{Python/ceval.c} for the
gory details. \versionadded{2.3}.
\end{opcodedesc}
\begin{opcodedesc}{YIELD_VALUE}{} \begin{opcodedesc}{YIELD_VALUE}{}
Pops \code{TOS} and yields it from a generator. Pops \code{TOS} and yields it from a generator.
\end{opcodedesc} \end{opcodedesc}
@ -621,7 +628,7 @@ free variable storage.
\end{opcodedesc} \end{opcodedesc}
\begin{opcodedesc}{SET_LINENO}{lineno} \begin{opcodedesc}{SET_LINENO}{lineno}
Sets the current line number to \var{lineno}. This opcode is obsolete.
\end{opcodedesc} \end{opcodedesc}
\begin{opcodedesc}{RAISE_VARARGS}{argc} \begin{opcodedesc}{RAISE_VARARGS}{argc}

View File

@ -118,10 +118,10 @@ A shorthand for \code{format_list(extract_stack(\var{f}, \var{limit}))}.
\begin{funcdesc}{tb_lineno}{tb} \begin{funcdesc}{tb_lineno}{tb}
This function returns the current line number set in the traceback This function returns the current line number set in the traceback
object. This is normally the same as the \code{\var{tb}.tb_lineno} object. This function was necessary because in versions of Python
field of the object, but when optimization is used (the -O flag) this prior to 2.3 when the \programopt{O} flag was passed to Python the
field is not updated correctly; this function calculates the correct \code{\var{tb}.tb_lineno} was not updated correctly. This function
value. has no use in versions past 2.3.
\end{funcdesc} \end{funcdesc}

View File

@ -2340,12 +2340,11 @@ Some tips for experts:
\item \item
When the Python interpreter is invoked with the \programopt{-O} flag, When the Python interpreter is invoked with the \programopt{-O} flag,
optimized code is generated and stored in \file{.pyo} files. optimized code is generated and stored in \file{.pyo} files. The
The optimizer currently doesn't help much; it only removes optimizer currently doesn't help much; it only removes
\keyword{assert} statements and \code{SET_LINENO} instructions. \keyword{assert} statements. When \programopt{-O} is used, \emph{all}
When \programopt{-O} is used, \emph{all} bytecode is optimized; bytecode is optimized; \code{.pyc} files are ignored and \code{.py}
\code{.pyc} files are ignored and \code{.py} files are compiled to files are compiled to optimized bytecode.
optimized bytecode.
\item \item
Passing two \programopt{-O} flags to the Python interpreter Passing two \programopt{-O} flags to the Python interpreter

View File

@ -658,7 +658,6 @@ In 2.3, you get this:
\end{itemize} \end{itemize}
%====================================================================== %======================================================================
\section{New and Improved Modules} \section{New and Improved Modules}
@ -987,9 +986,9 @@ allocate objects, and \cfunction{PyObject_GC_Del} to deallocate them.
when running Python's \file{configure} script. (Contributed by Ondrej when running Python's \file{configure} script. (Contributed by Ondrej
Palkovsky.) Palkovsky.)
\item The \csimplemacro{DL_EXPORT} and \csimplemacro{DL_IMPORT} macros are now \item The \csimplemacro{DL_EXPORT} and \csimplemacro{DL_IMPORT} macros
deprecated. Initialization functions for Python extension modules are now deprecated. Initialization functions for Python extension
should now be declared using the new macro modules should now be declared using the new macro
\csimplemacro{PyMODINIT_FUNC}, while the Python core will generally \csimplemacro{PyMODINIT_FUNC}, while the Python core will generally
use the \csimplemacro{PyAPI_FUNC} and \csimplemacro{PyAPI_DATA} use the \csimplemacro{PyAPI_FUNC} and \csimplemacro{PyAPI_DATA}
macros. macros.
@ -1076,6 +1075,29 @@ Finally, there are various miscellaneous fixes:
\item The tools used to build the documentation now work under Cygwin \item The tools used to build the documentation now work under Cygwin
as well as \UNIX. as well as \UNIX.
\item The \code{SET_LINENO} opcode has been removed. Back in the
mists of time, this opcode was needed to produce line numbers in
tracebacks and support trace functions (for, e.g., \module{pdb}).
Since Python 1.5, the line numbers in tracebacks have been computed
using a different mechanism that works with ``python -O''. For Python
2.3 Michael Hudson implemented a similar scheme to determine when to
call the trace function, removing the need for \code{SET_LINENO}
entirely.
Python code will be hard pushed to notice a difference from this
change, apart from a slight speed up when python is run without
\programopt{-O}.
C extensions that access the \member{f_lineno} field of frame objects
should instead call \code{PyCode_Addr2Line(f->f_code, f->f_lasti)}.
This will have the added effect of making the code work as desired
under ``python -O'' in earlier versions of Python.
To make tracing work as expected, it was found necessary to add a new
opcode, \cdata{RETURN_NONE}, to the VM. If you want to know why, read
the comments in the function \cfunction{maybe_call_line_trace} in
\file{Python/ceval.c}.
\end{itemize} \end{itemize}

View File

@ -71,6 +71,9 @@ extern "C" {
#define INPLACE_OR 79 #define INPLACE_OR 79
#define BREAK_LOOP 80 #define BREAK_LOOP 80
#define RETURN_NONE 81 /* *only* for function epilogues
-- see comments in
ceval.c:maybe_call_line_trace for why */
#define LOAD_LOCALS 82 #define LOAD_LOCALS 82
#define RETURN_VALUE 83 #define RETURN_VALUE 83
#define IMPORT_STAR 84 #define IMPORT_STAR 84
@ -119,8 +122,6 @@ extern "C" {
#define STORE_FAST 125 /* Local variable number */ #define STORE_FAST 125 /* Local variable number */
#define DELETE_FAST 126 /* Local variable number */ #define DELETE_FAST 126 /* Local variable number */
#define SET_LINENO 127 /* Current line number */
#define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */ #define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */
/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */ /* CALL_FUNCTION_XXX opcodes defined below depend on this definition */
#define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */ #define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */

View File

@ -55,6 +55,20 @@ def distb(tb=None):
def disassemble(co, lasti=-1): def disassemble(co, lasti=-1):
"""Disassemble a code object.""" """Disassemble a code object."""
code = co.co_code code = co.co_code
byte_increments = [ord(c) for c in co.co_lnotab[0::2]]
line_increments = [ord(c) for c in co.co_lnotab[1::2]]
table_length = len(byte_increments) # == len(line_increments)
lineno = co.co_firstlineno
table_index = 0
while (table_index < table_length
and byte_increments[table_index] == 0):
lineno += line_increments[table_index]
table_index += 1
addr = 0
line_incr = 0
labels = findlabels(code) labels = findlabels(code)
n = len(code) n = len(code)
i = 0 i = 0
@ -63,7 +77,23 @@ def disassemble(co, lasti=-1):
while i < n: while i < n:
c = code[i] c = code[i]
op = ord(c) op = ord(c)
if op == SET_LINENO and i > 0: print # Extra blank line
if i >= addr:
lineno += line_incr
while table_index < table_length:
addr += byte_increments[table_index]
line_incr = line_increments[table_index]
table_index += 1
if line_incr:
break
else:
addr = sys.maxint
if i > 0:
print
print "%3d"%lineno,
else:
print ' ',
if i == lasti: print '-->', if i == lasti: print '-->',
else: print ' ', else: print ' ',
if i in labels: print '>>', if i in labels: print '>>',
@ -224,6 +254,7 @@ def_op('INPLACE_XOR', 78)
def_op('INPLACE_OR', 79) def_op('INPLACE_OR', 79)
def_op('BREAK_LOOP', 80) def_op('BREAK_LOOP', 80)
def_op('RETURN_NONE', 81)
def_op('LOAD_LOCALS', 82) def_op('LOAD_LOCALS', 82)
def_op('RETURN_VALUE', 83) def_op('RETURN_VALUE', 83)
def_op('IMPORT_STAR', 84) def_op('IMPORT_STAR', 84)
@ -277,9 +308,6 @@ haslocal.append(125)
def_op('DELETE_FAST', 126) # Local variable number def_op('DELETE_FAST', 126) # Local variable number
haslocal.append(126) haslocal.append(126)
def_op('SET_LINENO', 127) # Current line number
SET_LINENO = 127
def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3) def_op('RAISE_VARARGS', 130) # Number of raise arguments (1, 2, or 3)
def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8) def_op('CALL_FUNCTION', 131) # #args + (#kwargs << 8)
def_op('MAKE_FUNCTION', 132) # Number of args with default values def_op('MAKE_FUNCTION', 132) # Number of args with default values

View File

@ -711,7 +711,7 @@ def getframeinfo(frame, context=1):
raise TypeError, 'arg is not a frame or traceback object' raise TypeError, 'arg is not a frame or traceback object'
filename = getsourcefile(frame) or getfile(frame) filename = getsourcefile(frame) or getfile(frame)
lineno = getlineno(frame) lineno = frame.f_lineno
if context > 0: if context > 0:
start = lineno - 1 - context//2 start = lineno - 1 - context//2
try: try:
@ -730,18 +730,8 @@ def getframeinfo(frame, context=1):
def getlineno(frame): def getlineno(frame):
"""Get the line number from a frame object, allowing for optimization.""" """Get the line number from a frame object, allowing for optimization."""
# Written by Marc-André Lemburg; revised by Jim Hugunin and Fredrik Lundh. # FrameType.f_lineno is now a descriptor that grovels co_lnotab
lineno = frame.f_lineno return frame.f_lineno
code = frame.f_code
if hasattr(code, 'co_lnotab'):
table = code.co_lnotab
lineno = code.co_firstlineno
addr = 0
for i in range(0, len(table), 2):
addr = addr + ord(table[i])
if addr > frame.f_lasti: break
lineno = lineno + ord(table[i+1])
return lineno
def getouterframes(frame, context=1): def getouterframes(frame, context=1):
"""Get a list of records for a frame and all higher (calling) frames. """Get a list of records for a frame and all higher (calling) frames.

View File

@ -105,7 +105,13 @@ class Pdb(bdb.Bdb, cmd.Cmd):
if len(line) > 0 and line[0] != '#': if len(line) > 0 and line[0] != '#':
self.onecmd(line) self.onecmd(line)
# Override Bdb methods (except user_call, for now) # Override Bdb methods
def user_call(self, frame, argument_list):
"""This method is called when there is the remote possibility
that we ever need to stop in this function."""
print '--Call--'
self.interaction(frame, None)
def user_line(self, frame): def user_line(self, frame):
"""This function is called when we stop or break at this line.""" """This function is called when we stop or break at this line."""

View File

@ -91,10 +91,8 @@ class HotShotTestCase(unittest.TestCase):
f_lineno = f.func_code.co_firstlineno f_lineno = f.func_code.co_firstlineno
g_lineno = g.func_code.co_firstlineno g_lineno = g.func_code.co_firstlineno
events = [(ENTER, ("test_hotshot", g_lineno, "g")), events = [(ENTER, ("test_hotshot", g_lineno, "g")),
(LINE, ("test_hotshot", g_lineno, "g")),
(LINE, ("test_hotshot", g_lineno+1, "g")), (LINE, ("test_hotshot", g_lineno+1, "g")),
(ENTER, ("test_hotshot", f_lineno, "f")), (ENTER, ("test_hotshot", f_lineno, "f")),
(LINE, ("test_hotshot", f_lineno, "f")),
(LINE, ("test_hotshot", f_lineno+1, "f")), (LINE, ("test_hotshot", f_lineno+1, "f")),
(LINE, ("test_hotshot", f_lineno+2, "f")), (LINE, ("test_hotshot", f_lineno+2, "f")),
(EXIT, ("test_hotshot", f_lineno, "f")), (EXIT, ("test_hotshot", f_lineno, "f")),

View File

@ -59,7 +59,7 @@ def print_tb(tb, limit=None, file=None):
n = 0 n = 0
while tb is not None and (limit is None or n < limit): while tb is not None and (limit is None or n < limit):
f = tb.tb_frame f = tb.tb_frame
lineno = tb_lineno(tb) lineno = tb.tb_lineno
co = f.f_code co = f.f_code
filename = co.co_filename filename = co.co_filename
name = co.co_name name = co.co_name
@ -92,7 +92,7 @@ def extract_tb(tb, limit = None):
n = 0 n = 0
while tb is not None and (limit is None or n < limit): while tb is not None and (limit is None or n < limit):
f = tb.tb_frame f = tb.tb_frame
lineno = tb_lineno(tb) lineno = tb.tb_lineno
co = f.f_code co = f.f_code
filename = co.co_filename filename = co.co_filename
name = co.co_name name = co.co_name
@ -263,7 +263,7 @@ def extract_stack(f=None, limit = None):
list = [] list = []
n = 0 n = 0
while f is not None and (limit is None or n < limit): while f is not None and (limit is None or n < limit):
lineno = f.f_lineno # XXX Too bad if -O is used lineno = f.f_lineno
co = f.f_code co = f.f_code
filename = co.co_filename filename = co.co_filename
name = co.co_name name = co.co_name
@ -279,23 +279,6 @@ def extract_stack(f=None, limit = None):
def tb_lineno(tb): def tb_lineno(tb):
"""Calculate correct line number of traceback given in tb. """Calculate correct line number of traceback given in tb.
Even works with -O on. Obsolete in 2.3.
""" """
# Coded by Marc-Andre Lemburg from the example of PyCode_Addr2Line() return tb.tb_lineno
# in compile.c.
# Revised version by Jim Hugunin to work with JPython too.
c = tb.tb_frame.f_code
if not hasattr(c, 'co_lnotab'):
return tb.tb_lineno
tab = c.co_lnotab
line = c.co_firstlineno
stopat = tb.tb_lasti
addr = 0
for i in range(0, len(tab), 2):
addr = addr + ord(tab[i])
if addr > stopat:
break
line = line + ord(tab[i+1])
return line

View File

@ -57,6 +57,11 @@ Type/class unification and new-style classes
Core and builtins Core and builtins
- SET_LINENO is gone. co_lnotab is now consulted to determine when to
call the trace function. C code that accessed f_lineno should call
PyCode_Addr2Line instead (f_lineno is still there, but not kept up
to date).
- There's a new warning category, FutureWarning. This is used to warn - There's a new warning category, FutureWarning. This is used to warn
about a number of situations where the value or sign of an integer about a number of situations where the value or sign of an integer
result will change in Python 2.4 as a result of PEP 237 (integer result will change in Python 2.4 as a result of PEP 237 (integer

View File

@ -152,7 +152,7 @@ logreader_tp_iter(LogReaderObject *self)
* Low bits: Opcode: Meaning: * Low bits: Opcode: Meaning:
* 0x00 ENTER enter a frame * 0x00 ENTER enter a frame
* 0x01 EXIT exit a frame * 0x01 EXIT exit a frame
* 0x02 LINENO SET_LINENO instruction was executed * 0x02 LINENO execution moved onto a different line
* 0x03 OTHER more bits are needed to deecode * 0x03 OTHER more bits are needed to deecode
* *
* If the type is OTHER, the record is not packed so tightly, and the * If the type is OTHER, the record is not packed so tightly, and the
@ -888,9 +888,12 @@ tracer_callback(ProfilerObject *self, PyFrameObject *frame, int what,
case PyTrace_LINE: case PyTrace_LINE:
if (self->linetimings) if (self->linetimings)
return pack_lineno_tdelta(self, frame->f_lineno, get_tdelta(self)); return pack_lineno_tdelta(self, PyCode_Addr2Line(frame->f_code,
frame->f_lasti),
get_tdelta(self));
else else
return pack_lineno(self, frame->f_lineno); return pack_lineno(self, PyCode_Addr2Line(frame->f_code,
frame->f_lasti));
default: default:
/* ignore PyTrace_EXCEPTION */ /* ignore PyTrace_EXCEPTION */
@ -1227,8 +1230,8 @@ PyDoc_STRVAR(profiler_object__doc__,
"\n" "\n"
"closed: True if the profiler has already been closed.\n" "closed: True if the profiler has already been closed.\n"
"frametimings: True if ENTER/EXIT events collect timing information.\n" "frametimings: True if ENTER/EXIT events collect timing information.\n"
"lineevents: True if SET_LINENO events are reported to the profiler.\n" "lineevents: True if line events are reported to the profiler.\n"
"linetimings: True if SET_LINENO events collect timing information."); "linetimings: True if line events collect timing information.");
static PyTypeObject ProfilerType = { static PyTypeObject ProfilerType = {
PyObject_HEAD_INIT(NULL) PyObject_HEAD_INIT(NULL)

View File

@ -16,7 +16,6 @@ static PyMemberDef frame_memberlist[] = {
{"f_builtins", T_OBJECT, OFF(f_builtins),RO}, {"f_builtins", T_OBJECT, OFF(f_builtins),RO},
{"f_globals", T_OBJECT, OFF(f_globals), RO}, {"f_globals", T_OBJECT, OFF(f_globals), RO},
{"f_lasti", T_INT, OFF(f_lasti), RO}, {"f_lasti", T_INT, OFF(f_lasti), RO},
{"f_lineno", T_INT, OFF(f_lineno), RO},
{"f_restricted",T_INT, OFF(f_restricted),RO}, {"f_restricted",T_INT, OFF(f_restricted),RO},
{"f_trace", T_OBJECT, OFF(f_trace)}, {"f_trace", T_OBJECT, OFF(f_trace)},
{"f_exc_type", T_OBJECT, OFF(f_exc_type)}, {"f_exc_type", T_OBJECT, OFF(f_exc_type)},
@ -33,8 +32,19 @@ frame_getlocals(PyFrameObject *f, void *closure)
return f->f_locals; return f->f_locals;
} }
static PyObject *
frame_getlineno(PyFrameObject *f, void *closure)
{
int lineno;
lineno = PyCode_Addr2Line(f->f_code, f->f_lasti);
return PyInt_FromLong(lineno);
}
static PyGetSetDef frame_getsetlist[] = { static PyGetSetDef frame_getsetlist[] = {
{"f_locals", (getter)frame_getlocals, NULL, NULL}, {"f_locals", (getter)frame_getlocals, NULL, NULL},
{"f_lineno", (getter)frame_getlineno, NULL, NULL},
{0} {0}
}; };
@ -306,7 +316,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, PyObject *globals,
f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL; f->f_exc_type = f->f_exc_value = f->f_exc_traceback = NULL;
f->f_tstate = tstate; f->f_tstate = tstate;
f->f_lasti = 0; f->f_lasti = -1;
f->f_lineno = code->co_firstlineno; f->f_lineno = code->co_firstlineno;
f->f_restricted = (builtins != tstate->interp->builtins); f->f_restricted = (builtins != tstate->interp->builtins);
f->f_iblock = 0; f->f_iblock = 0;

View File

@ -51,6 +51,9 @@ static int call_trace(Py_tracefunc, PyObject *, PyFrameObject *,
static void call_trace_protected(Py_tracefunc, PyObject *, static void call_trace_protected(Py_tracefunc, PyObject *,
PyFrameObject *, int); PyFrameObject *, int);
static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *); static void call_exc_trace(Py_tracefunc, PyObject *, PyFrameObject *);
static void maybe_call_line_trace(int, Py_tracefunc, PyObject *,
PyFrameObject *, int *, int *);
static PyObject *apply_slice(PyObject *, PyObject *, PyObject *); static PyObject *apply_slice(PyObject *, PyObject *, PyObject *);
static int assign_slice(PyObject *, PyObject *, static int assign_slice(PyObject *, PyObject *,
PyObject *, PyObject *); PyObject *, PyObject *);
@ -499,6 +502,16 @@ eval_frame(PyFrameObject *f)
PyObject *retval = NULL; /* Return value */ PyObject *retval = NULL; /* Return value */
PyThreadState *tstate = PyThreadState_GET(); PyThreadState *tstate = PyThreadState_GET();
PyCodeObject *co; PyCodeObject *co;
/* when tracing we set things up so that
not (instr_lb <= current_bytecode_offset < instr_ub)
is true when the line being executed has changed. The
initial values are such as to make this false the first
time it is tested. */
int instr_ub = -1, instr_lb = 0;
unsigned char *first_instr; unsigned char *first_instr;
PyObject *names; PyObject *names;
PyObject *consts; PyObject *consts;
@ -586,7 +599,12 @@ eval_frame(PyFrameObject *f)
fastlocals = f->f_localsplus; fastlocals = f->f_localsplus;
freevars = f->f_localsplus + f->f_nlocals; freevars = f->f_localsplus + f->f_nlocals;
_PyCode_GETCODEPTR(co, &first_instr); _PyCode_GETCODEPTR(co, &first_instr);
next_instr = first_instr + f->f_lasti; if (f->f_lasti < 0) {
next_instr = first_instr;
}
else {
next_instr = first_instr + f->f_lasti;
}
stack_pointer = f->f_stacktop; stack_pointer = f->f_stacktop;
assert(stack_pointer != NULL); assert(stack_pointer != NULL);
f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */ f->f_stacktop = NULL; /* remains NULL unless yield suspends frame */
@ -637,8 +655,9 @@ eval_frame(PyFrameObject *f)
w = NULL; w = NULL;
for (;;) { for (;;) {
assert(stack_pointer >= f->f_valuestack); /* else underflow */ assert(stack_pointer >= f->f_valuestack); /* else underflow */
assert(STACK_LEVEL() <= f->f_stacksize); /* else overflow */ assert(STACK_LEVEL() <= f->f_stacksize); /* else overflow */
/* Do periodic things. Doing this every time through /* Do periodic things. Doing this every time through
the loop would add too much overhead, so we do it the loop would add too much overhead, so we do it
only every Nth instruction. We also do it if only every Nth instruction. We also do it if
@ -658,8 +677,8 @@ eval_frame(PyFrameObject *f)
#if !defined(HAVE_SIGNAL_H) || defined(macintosh) #if !defined(HAVE_SIGNAL_H) || defined(macintosh)
/* If we have true signals, the signal handler /* If we have true signals, the signal handler
will call Py_AddPendingCall() so we don't will call Py_AddPendingCall() so we don't
have to call sigcheck(). On the Mac and have to call PyErr_CheckSignals(). On the
DOS, alas, we have to call it. */ Mac and DOS, alas, we have to call it. */
if (PyErr_CheckSignals()) { if (PyErr_CheckSignals()) {
why = WHY_EXCEPTION; why = WHY_EXCEPTION;
goto on_error; goto on_error;
@ -686,9 +705,7 @@ eval_frame(PyFrameObject *f)
fast_next_opcode: fast_next_opcode:
/* Extract opcode and argument */ /* Extract opcode and argument */
#if defined(Py_DEBUG) || defined(LLTRACE)
f->f_lasti = INSTR_OFFSET(); f->f_lasti = INSTR_OFFSET();
#endif
opcode = NEXTOP(); opcode = NEXTOP();
if (HAS_ARG(opcode)) if (HAS_ARG(opcode))
@ -708,15 +725,26 @@ eval_frame(PyFrameObject *f)
if (lltrace) { if (lltrace) {
if (HAS_ARG(opcode)) { if (HAS_ARG(opcode)) {
printf("%d: %d, %d\n", printf("%d: %d, %d\n",
(int) (INSTR_OFFSET() - 3), f->f_lasti, opcode, oparg);
opcode, oparg);
} }
else { else {
printf("%d: %d\n", printf("%d: %d\n",
(int) (INSTR_OFFSET() - 1), opcode); f->f_lasti, opcode);
} }
} }
#endif #endif
/* line-by-line tracing support */
if (tstate->c_tracefunc != NULL && !tstate->tracing) {
/* see maybe_call_line_trace
for expository comments */
maybe_call_line_trace(opcode,
tstate->c_tracefunc,
tstate->c_traceobj,
f, &instr_lb, &instr_ub);
}
/* Main switch on opcode */ /* Main switch on opcode */
switch (opcode) { switch (opcode) {
@ -728,26 +756,6 @@ eval_frame(PyFrameObject *f)
/* case STOP_CODE: this is an error! */ /* case STOP_CODE: this is an error! */
case SET_LINENO:
#ifdef LLTRACE
if (lltrace)
printf("--- %s:%d \n", filename, oparg);
#endif
f->f_lineno = oparg;
if (tstate->c_tracefunc == NULL || tstate->tracing)
goto fast_next_opcode;
/* Trace each line of code reached */
f->f_lasti = INSTR_OFFSET();
/* Inline call_trace() for performance: */
tstate->tracing++;
tstate->use_tracing = 0;
err = (tstate->c_tracefunc)(tstate->c_traceobj, f,
PyTrace_LINE, Py_None);
tstate->use_tracing = (tstate->c_tracefunc
|| tstate->c_profilefunc);
tstate->tracing--;
break;
case LOAD_FAST: case LOAD_FAST:
x = GETLOCAL(oparg); x = GETLOCAL(oparg);
if (x != NULL) { if (x != NULL) {
@ -1504,9 +1512,17 @@ eval_frame(PyFrameObject *f)
why = WHY_RETURN; why = WHY_RETURN;
break; break;
case RETURN_NONE:
retval = Py_None;
Py_INCREF(retval);
why = WHY_RETURN;
break;
case YIELD_VALUE: case YIELD_VALUE:
retval = POP(); retval = POP();
f->f_stacktop = stack_pointer; f->f_stacktop = stack_pointer;
/* abuse the lasti field: here it points to
the *next* instruction */
f->f_lasti = INSTR_OFFSET(); f->f_lasti = INSTR_OFFSET();
why = WHY_YIELD; why = WHY_YIELD;
break; break;
@ -1954,7 +1970,6 @@ eval_frame(PyFrameObject *f)
int n = na + 2 * nk; int n = na + 2 * nk;
PyObject **pfunc = stack_pointer - n - 1; PyObject **pfunc = stack_pointer - n - 1;
PyObject *func = *pfunc; PyObject *func = *pfunc;
f->f_lasti = INSTR_OFFSET() - 3; /* For tracing */
/* Always dispatch PyCFunction first, because /* Always dispatch PyCFunction first, because
these are presumed to be the most frequent these are presumed to be the most frequent
@ -2022,7 +2037,6 @@ eval_frame(PyFrameObject *f)
n++; n++;
pfunc = stack_pointer - n - 1; pfunc = stack_pointer - n - 1;
func = *pfunc; func = *pfunc;
f->f_lasti = INSTR_OFFSET() - 3; /* For tracing */
if (PyMethod_Check(func) if (PyMethod_Check(func)
&& PyMethod_GET_SELF(func) != NULL) { && PyMethod_GET_SELF(func) != NULL) {
@ -2134,7 +2148,8 @@ eval_frame(PyFrameObject *f)
default: default:
fprintf(stderr, fprintf(stderr,
"XXX lineno: %d, opcode: %d\n", "XXX lineno: %d, opcode: %d\n",
f->f_lineno, opcode); PyCode_Addr2Line(f->f_code, f->f_lasti),
opcode);
PyErr_SetString(PyExc_SystemError, "unknown opcode"); PyErr_SetString(PyExc_SystemError, "unknown opcode");
why = WHY_EXCEPTION; why = WHY_EXCEPTION;
break; break;
@ -2189,9 +2204,6 @@ eval_frame(PyFrameObject *f)
/* Log traceback info if this is a real exception */ /* Log traceback info if this is a real exception */
if (why == WHY_EXCEPTION) { if (why == WHY_EXCEPTION) {
f->f_lasti = INSTR_OFFSET() - 1;
if (HAS_ARG(opcode))
f->f_lasti -= 2;
PyTraceBack_Here(f); PyTraceBack_Here(f);
if (tstate->c_tracefunc != NULL) if (tstate->c_tracefunc != NULL)
@ -2875,6 +2887,125 @@ call_trace(Py_tracefunc func, PyObject *obj, PyFrameObject *frame,
return result; return result;
} }
static void
maybe_call_line_trace(int opcode, Py_tracefunc func, PyObject *obj,
PyFrameObject *frame, int *instr_lb, int *instr_ub)
{
/* The theory of SET_LINENO-less tracing.
In a nutshell, we use the co_lnotab field of the code object
to tell when execution has moved onto a different line.
As mentioned above, the basic idea is so set things up so
that
*instr_lb <= frame->f_lasti < *instr_ub
is true so long as execution does not change lines.
This is all fairly simple. Digging the information out of
co_lnotab takes some work, but is conceptually clear.
Somewhat harder to explain is why we don't call the line
trace function when executing a POP_TOP or RETURN_NONE
opcodes. An example probably serves best.
Consider this code:
1: def f(a):
2: if a:
3: print 1
4: else:
5: print 2
which compiles to this:
2 0 LOAD_FAST 0 (a)
3 JUMP_IF_FALSE 9 (to 15)
6 POP_TOP
3 7 LOAD_CONST 1 (1)
10 PRINT_ITEM
11 PRINT_NEWLINE
12 JUMP_FORWARD 6 (to 21)
>> 15 POP_TOP
5 16 LOAD_CONST 2 (2)
19 PRINT_ITEM
20 PRINT_NEWLINE
>> 21 RETURN_NONE
If a is false, execution will jump to instruction at offset
15 and the co_lnotab will claim that execution has moved to
line 3. This is at best misleading. In this case we could
associate the POP_TOP with line 4, but that doesn't make
sense in all cases (I think).
On the other hand, if a is true, execution will jump from
instruction offset 12 to offset 21. Then the co_lnotab would
imply that execution has moved to line 5, which is again
misleading.
This is why it is important that RETURN_NONE is *only* used
for the "falling off the end of the function" form of
returning None -- using it for code like
1: def f():
2: return
would, once again, lead to misleading tracing behaviour.
It is also worth mentioning that getting tracing behaviour
right is the *entire* motivation for adding the RETURN_NONE
opcode.
*/
if (opcode != POP_TOP && opcode != RETURN_NONE &&
(frame->f_lasti < *instr_lb || frame->f_lasti > *instr_ub)) {
PyCodeObject* co = frame->f_code;
int size, addr;
unsigned char* p;
call_trace(func, obj, frame, PyTrace_LINE, Py_None);
size = PyString_Size(co->co_lnotab) / 2;
p = (unsigned char*)PyString_AsString(co->co_lnotab);
/* possible optimization: if f->f_lasti == instr_ub
(likely to be a common case) then we already know
instr_lb -- if we stored the matching value of p
somwhere we could skip the first while loop. */
addr = 0;
/* see comments in compile.c for the description of
co_lnotab. A point to remember: increments to p
should come in pairs -- although we don't care about
the line increments here, treating them as byte
increments gets confusing, to say the least. */
while (size >= 0) {
if (addr + *p > frame->f_lasti)
break;
addr += *p++;
p++;
--size;
}
*instr_lb = addr;
if (size > 0) {
while (--size >= 0) {
addr += *p++;
if (*p++)
break;
}
*instr_ub = addr;
}
else {
*instr_ub = INT_MAX;
}
}
}
void void
PyEval_SetProfile(Py_tracefunc func, PyObject *arg) PyEval_SetProfile(Py_tracefunc func, PyObject *arg)
{ {

View File

@ -407,9 +407,10 @@ PyCode_New(int argcount, int nlocals, int stacksize, int flags,
/* All about c_lnotab. /* All about c_lnotab.
c_lnotab is an array of unsigned bytes disguised as a Python string. In -O c_lnotab is an array of unsigned bytes disguised as a Python string. Since
mode, SET_LINENO opcodes aren't generated, and bytecode offsets are mapped version 2.3, SET_LINENO opcodes are never generated and bytecode offsets are
to source code line #s (when needed for tracebacks) via c_lnotab instead. mapped to source code line #s via c_lnotab instead.
The array is conceptually a list of The array is conceptually a list of
(bytecode offset increment, line number increment) (bytecode offset increment, line number increment)
pairs. The details are important and delicate, best illustrated by example: pairs. The details are important and delicate, best illustrated by example:
@ -830,11 +831,6 @@ static void
com_addoparg(struct compiling *c, int op, int arg) com_addoparg(struct compiling *c, int op, int arg)
{ {
int extended_arg = arg >> 16; int extended_arg = arg >> 16;
if (op == SET_LINENO) {
com_set_lineno(c, arg);
if (Py_OptimizeFlag)
return;
}
if (extended_arg){ if (extended_arg){
com_addbyte(c, EXTENDED_ARG); com_addbyte(c, EXTENDED_ARG);
com_addint(c, extended_arg); com_addint(c, extended_arg);
@ -1738,7 +1734,7 @@ com_call_function(struct compiling *c, node *n)
break; break;
if (ch->n_lineno != lineno) { if (ch->n_lineno != lineno) {
lineno = ch->n_lineno; lineno = ch->n_lineno;
com_addoparg(c, SET_LINENO, lineno); com_set_lineno(c, lineno);
} }
com_argument(c, ch, &keywords); com_argument(c, ch, &keywords);
if (keywords == NULL) if (keywords == NULL)
@ -3168,7 +3164,7 @@ com_if_stmt(struct compiling *c, node *n)
continue; continue;
} }
if (i > 0) if (i > 0)
com_addoparg(c, SET_LINENO, ch->n_lineno); com_set_lineno(c, ch->n_lineno);
com_node(c, ch); com_node(c, ch);
com_addfwref(c, JUMP_IF_FALSE, &a); com_addfwref(c, JUMP_IF_FALSE, &a);
com_addbyte(c, POP_TOP); com_addbyte(c, POP_TOP);
@ -3195,7 +3191,7 @@ com_while_stmt(struct compiling *c, node *n)
com_addfwref(c, SETUP_LOOP, &break_anchor); com_addfwref(c, SETUP_LOOP, &break_anchor);
block_push(c, SETUP_LOOP); block_push(c, SETUP_LOOP);
c->c_begin = c->c_nexti; c->c_begin = c->c_nexti;
com_addoparg(c, SET_LINENO, n->n_lineno); com_set_lineno(c, n->n_lineno);
com_node(c, CHILD(n, 1)); com_node(c, CHILD(n, 1));
com_addfwref(c, JUMP_IF_FALSE, &anchor); com_addfwref(c, JUMP_IF_FALSE, &anchor);
com_addbyte(c, POP_TOP); com_addbyte(c, POP_TOP);
@ -3228,7 +3224,7 @@ com_for_stmt(struct compiling *c, node *n)
com_node(c, CHILD(n, 3)); com_node(c, CHILD(n, 3));
com_addbyte(c, GET_ITER); com_addbyte(c, GET_ITER);
c->c_begin = c->c_nexti; c->c_begin = c->c_nexti;
com_addoparg(c, SET_LINENO, n->n_lineno); com_set_lineno(c, n->n_lineno);
com_addfwref(c, FOR_ITER, &anchor); com_addfwref(c, FOR_ITER, &anchor);
com_push(c, 1); com_push(c, 1);
com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL); com_assign(c, CHILD(n, 1), OP_ASSIGN, NULL);
@ -3339,7 +3335,7 @@ com_try_except(struct compiling *c, node *n)
} }
except_anchor = 0; except_anchor = 0;
com_push(c, 3); /* tb, val, exc pushed by exception */ com_push(c, 3); /* tb, val, exc pushed by exception */
com_addoparg(c, SET_LINENO, ch->n_lineno); com_set_lineno(c, ch->n_lineno);
if (NCH(ch) > 1) { if (NCH(ch) > 1) {
com_addbyte(c, DUP_TOP); com_addbyte(c, DUP_TOP);
com_push(c, 1); com_push(c, 1);
@ -3401,7 +3397,7 @@ com_try_finally(struct compiling *c, node *n)
com_push(c, 3); com_push(c, 3);
com_backpatch(c, finally_anchor); com_backpatch(c, finally_anchor);
ch = CHILD(n, NCH(n)-1); ch = CHILD(n, NCH(n)-1);
com_addoparg(c, SET_LINENO, ch->n_lineno); com_set_lineno(c, ch->n_lineno);
com_node(c, ch); com_node(c, ch);
com_addbyte(c, END_FINALLY); com_addbyte(c, END_FINALLY);
block_pop(c, END_FINALLY); block_pop(c, END_FINALLY);
@ -3727,7 +3723,7 @@ com_node(struct compiling *c, node *n)
case simple_stmt: case simple_stmt:
/* small_stmt (';' small_stmt)* [';'] NEWLINE */ /* small_stmt (';' small_stmt)* [';'] NEWLINE */
com_addoparg(c, SET_LINENO, n->n_lineno); com_set_lineno(c, n->n_lineno);
{ {
int i; int i;
for (i = 0; i < NCH(n)-1; i += 2) for (i = 0; i < NCH(n)-1; i += 2)
@ -3736,7 +3732,7 @@ com_node(struct compiling *c, node *n)
break; break;
case compound_stmt: case compound_stmt:
com_addoparg(c, SET_LINENO, n->n_lineno); com_set_lineno(c, n->n_lineno);
n = CHILD(n, 0); n = CHILD(n, 0);
goto loop; goto loop;
@ -3990,10 +3986,7 @@ compile_funcdef(struct compiling *c, node *n)
c->c_infunction = 1; c->c_infunction = 1;
com_node(c, CHILD(n, 4)); com_node(c, CHILD(n, 4));
c->c_infunction = 0; c->c_infunction = 0;
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_addbyte(c, RETURN_NONE);
com_push(c, 1);
com_addbyte(c, RETURN_VALUE);
com_pop(c, 1);
} }
static void static void
@ -4050,7 +4043,7 @@ compile_classdef(struct compiling *c, node *n)
static void static void
compile_node(struct compiling *c, node *n) compile_node(struct compiling *c, node *n)
{ {
com_addoparg(c, SET_LINENO, n->n_lineno); com_set_lineno(c, n->n_lineno);
switch (TYPE(n)) { switch (TYPE(n)) {
@ -4060,19 +4053,13 @@ compile_node(struct compiling *c, node *n)
n = CHILD(n, 0); n = CHILD(n, 0);
if (TYPE(n) != NEWLINE) if (TYPE(n) != NEWLINE)
com_node(c, n); com_node(c, n);
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_addbyte(c, RETURN_NONE);
com_push(c, 1);
com_addbyte(c, RETURN_VALUE);
com_pop(c, 1);
c->c_interactive--; c->c_interactive--;
break; break;
case file_input: /* A whole file, or built-in function exec() */ case file_input: /* A whole file, or built-in function exec() */
com_file_input(c, n); com_file_input(c, n);
com_addoparg(c, LOAD_CONST, com_addconst(c, Py_None)); com_addbyte(c, RETURN_NONE);
com_push(c, 1);
com_addbyte(c, RETURN_VALUE);
com_pop(c, 1);
break; break;
case eval_input: /* Built-in function input() */ case eval_input: /* Built-in function input() */

View File

@ -13,12 +13,12 @@
static unsigned char M___hello__[] = { static unsigned char M___hello__[] = {
99,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0, 99,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,
0,115,15,0,0,0,127,0,0,127,1,0,100,0,0,71, 0,115,9,0,0,0,100,0,0,71,72,100,1,0,83,40,
72,100,1,0,83,40,2,0,0,0,115,14,0,0,0,72, 2,0,0,0,115,14,0,0,0,72,101,108,108,111,32,119,
101,108,108,111,32,119,111,114,108,100,46,46,46,78,40,0, 111,114,108,100,46,46,46,78,40,0,0,0,0,40,0,0,
0,0,0,40,0,0,0,0,40,0,0,0,0,40,0,0, 0,0,40,0,0,0,0,40,0,0,0,0,115,8,0,0,
0,0,115,8,0,0,0,104,101,108,108,111,46,112,121,115, 0,104,101,108,108,111,46,112,121,115,1,0,0,0,63,1,
1,0,0,0,63,1,0,0,0,115,0,0,0,0, 0,0,0,115,0,0,0,0,
}; };
#define SIZE (int)sizeof(M___hello__) #define SIZE (int)sizeof(M___hello__)

View File

@ -49,6 +49,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
algorithm relying on the above scheme. Perhaps we should simply algorithm relying on the above scheme. Perhaps we should simply
start counting in increments of 10 from now on ?! start counting in increments of 10 from now on ?!
MWH, 2002-08-03: Removed SET_LINENO. Couldn't be bothered figuring
out the MAGIC schemes, so just incremented it by 10.
Known values: Known values:
Python 1.5: 20121 Python 1.5: 20121
Python 1.5.1: 20121 Python 1.5.1: 20121
@ -60,8 +63,9 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
Python 2.1.2: 60202 Python 2.1.2: 60202
Python 2.2: 60717 Python 2.2: 60717
Python 2.3a0: 62011 Python 2.3a0: 62011
Python 2.3a0: 62021
*/ */
#define MAGIC (62011 | ((long)'\r'<<16) | ((long)'\n'<<24)) #define MAGIC (62021 | ((long)'\r'<<16) | ((long)'\n'<<24))
/* Magic word as global; note that _PyImport_Init() can change the /* Magic word as global; note that _PyImport_Init() can change the
value of this global to accommodate for alterations of how the value of this global to accommodate for alterations of how the

View File

@ -103,8 +103,7 @@ PyTypeObject PyTraceBack_Type = {
}; };
static tracebackobject * static tracebackobject *
newtracebackobject(tracebackobject *next, PyFrameObject *frame, int lasti, newtracebackobject(tracebackobject *next, PyFrameObject *frame)
int lineno)
{ {
tracebackobject *tb; tracebackobject *tb;
if ((next != NULL && !PyTraceBack_Check(next)) || if ((next != NULL && !PyTraceBack_Check(next)) ||
@ -118,8 +117,9 @@ newtracebackobject(tracebackobject *next, PyFrameObject *frame, int lasti,
tb->tb_next = next; tb->tb_next = next;
Py_XINCREF(frame); Py_XINCREF(frame);
tb->tb_frame = frame; tb->tb_frame = frame;
tb->tb_lasti = lasti; tb->tb_lasti = frame->f_lasti;
tb->tb_lineno = lineno; tb->tb_lineno = PyCode_Addr2Line(frame->f_code,
frame->f_lasti);
PyObject_GC_Track(tb); PyObject_GC_Track(tb);
} }
return tb; return tb;
@ -130,8 +130,7 @@ PyTraceBack_Here(PyFrameObject *frame)
{ {
PyThreadState *tstate = frame->f_tstate; PyThreadState *tstate = frame->f_tstate;
tracebackobject *oldtb = (tracebackobject *) tstate->curexc_traceback; tracebackobject *oldtb = (tracebackobject *) tstate->curexc_traceback;
tracebackobject *tb = newtracebackobject(oldtb, tracebackobject *tb = newtracebackobject(oldtb, frame);
frame, frame->f_lasti, frame->f_lineno);
if (tb == NULL) if (tb == NULL)
return -1; return -1;
tstate->curexc_traceback = (PyObject *)tb; tstate->curexc_traceback = (PyObject *)tb;

View File

@ -370,41 +370,29 @@ class CoverageResults:
except IOError, err: except IOError, err:
sys.stderr.write("cannot save counts files because %s" % err) sys.stderr.write("cannot save counts files because %s" % err)
# Given a code string, return the SET_LINENO information def _find_LINENO_from_code(code):
def _find_LINENO_from_string(co_code): """return the numbers of the lines containing the source code that
"""return all of the SET_LINENO information from a code string""" was compiled into code"""
import dis
linenos = {} linenos = {}
# This code was filched from the `dis' module then modified line_increments = [ord(c) for c in code.co_lnotab[1::2]]
n = len(co_code) table_length = len(line_increments)
i = 0
prev_op = None lineno = code.co_first_lineno
prev_lineno = 0
while i < n: for li in line_increments:
c = co_code[i] linenos[lineno] = 1
op = ord(c) lineno += li
if op == dis.SET_LINENO: linenos[lineno] = 1
if prev_op == op:
# two SET_LINENO in a row, so the previous didn't
# indicate anything. This occurs with triple
# quoted strings (?). Remove the old one.
del linenos[prev_lineno]
prev_lineno = ord(co_code[i+1]) + ord(co_code[i+2])*256
linenos[prev_lineno] = 1
if op >= dis.HAVE_ARGUMENT:
i = i + 3
else:
i = i + 1
prev_op = op
return linenos return linenos
def _find_LINENO(code): def _find_LINENO(code):
"""return all of the SET_LINENO information from a code object""" """return all of the lineno information from a code object"""
import types import types
# get all of the lineno information from the code of this scope level # get all of the lineno information from the code of this scope level
linenos = _find_LINENO_from_string(code.co_code) linenos = _find_LINENO_from_code(code)
# and check the constants for references to other code objects # and check the constants for references to other code objects
for c in code.co_consts: for c in code.co_consts:
@ -416,9 +404,6 @@ def _find_LINENO(code):
def find_executable_linenos(filename): def find_executable_linenos(filename):
"""return a dict of the line numbers from executable statements in a file """return a dict of the line numbers from executable statements in a file
Works by finding all of the code-like objects in the module then searching
the byte code for 'SET_LINENO' terms (so this won't work one -O files).
""" """
import parser import parser
@ -428,10 +413,6 @@ def find_executable_linenos(filename):
ast = parser.suite(prog) ast = parser.suite(prog)
code = parser.compileast(ast, filename) code = parser.compileast(ast, filename)
# The only way I know to find line numbers is to look for the
# SET_LINENO instructions. Isn't there some way to get it from
# the AST?
return _find_LINENO(code) return _find_LINENO(code)
### XXX because os.path.commonprefix seems broken by my way of thinking... ### XXX because os.path.commonprefix seems broken by my way of thinking...