Minimal debug/rep functionality

Richie Hindle richie at entrian.com
Fri Sep 20 10:31:40 EDT 2002


Tim,

> Having been writing a fairly comprehensive code and path coverage tool
> (incomplete - currently working on determining what *is* an executable line
> ... think I need to work with the AST)

I've done this recently as well, and I'm now on my third iteration of
the work-out-which-lines-are-executable code (!)

The first version used the AST, but there are several holes in that -
'else' lines, for instance, are identified by the parser as executable
lines, but the trace function doesn't always get called for them.  I
then wrote a system that found all the SET_LINENO instructions, and
that gave perfect results (as long as you weren't using -O) but then I
found out that SET_LINENO is going away in Python 2.3...

My current system (which I think works 8-) uses co_lnotab, which is a
delightful data structure that describes the relationship between
bytecodes and line numbers.  Here's the relevant piece of code, which
builds in 'lineNumbers' a list of the execuable line number in the
code object 'code':

# Derive the line numbers from co_lnotab; that's a list of pairs of
# increments, one for the bytecode address and one for the line
# number (see compile.c in the Python sources).
lineNumbers = []
lnotab = code.co_lnotab
previousAddress = -1
previousLineNo = -1
lineNo = code.co_firstlineno
byteCodeAddress = 0
for i in range( 0, len( lnotab ), 2 ):
    byteCodeAddress = byteCodeAddress + ord( lnotab[ i ] )
    lineNo = lineNo + ord( lnotab[ i+1 ] )
    
    # When the compiler wants to increment the line number by more
    # than 255 in one go, it increments it by 255 as many times as it
    # needs and then the remainder.  Fair enough, but there's a bug
    # whereby the byte code address gets incremented along
    # with the *first* 255, not the last.  Hence you get a line that
    # claims to have code on it when in fact it doesn't.  We detect
    # that here.
    if byteCodeAddress != previousAddress and not \
       ( lineNo - previousLineNo == 255 and 
         len( lnotab ) > i+2 and
         ord( lnotab[ i+2 ] ) == 0 ):
        lineNumbers.append( lineNo )
        previousAddress = byteCodeAddress
        previousLineNo = lineNo

Hope that helps.  The coverage tool that this belongs to is finished
but unreleased (mostly because of lack of thorough testing, but I'm
using it myself with no problems) - if you'd like a copy, drop me an
email.

-- 
Richie Hindle
richie at entrian.com



More information about the Python-list mailing list