Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
10a9e781
Commit
10a9e781
authored
Apr 18, 2006
by
Jeremy Hylton
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor: Move code that uses co_lnotab from ceval to codeobject
parent
9c31ff03
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
162 additions
and
117 deletions
+162
-117
Include/code.h
Include/code.h
+15
-0
Objects/codeobject.c
Objects/codeobject.c
+133
-0
Python/ceval.c
Python/ceval.c
+14
-117
No files found.
Include/code.h
View file @
10a9e781
...
...
@@ -72,6 +72,21 @@ PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int);
((*(co)->co_code->ob_type->tp_as_buffer->bf_getreadbuffer) \
((co)->co_code, 0, (void **)(pp)))
typedef
struct
_addr_pair
{
int
ap_lower
;
int
ap_upper
;
}
PyAddrPair
;
/* Check whether lasti (an instruction offset) falls outside bounds
and whether it is a line number that should be traced. Returns
a line number if it should be traced or -1 if the line should not.
If lasti is not within bounds, updates bounds.
*/
PyAPI_FUNC
(
int
)
PyCode_CheckLineNumber
(
PyCodeObject
*
co
,
int
lasti
,
PyAddrPair
*
bounds
);
#ifdef __cplusplus
}
#endif
...
...
Objects/codeobject.c
View file @
10a9e781
...
...
@@ -451,3 +451,136 @@ PyCode_Addr2Line(PyCodeObject *co, int addrq)
}
return
line
;
}
/*
Check whether the current instruction is at the start of a line.
*/
/* 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 *always* call the
line trace function when the above test fails.
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 LOAD_CONST 0 (None)
24 RETURN_VALUE
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).
What we do is only call the line trace function if the co_lnotab
indicates we have jumped to the *start* of a line, i.e. if the
current instruction offset matches the offset given for the
start of a line by the co_lnotab.
This also takes care of the situation where '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.
Why do we set f_lineno when tracing? Well, consider the code
above when 'a' is true. If stepping through this with 'n' in
pdb, you would stop at line 1 with a "call" type event, then
line events on lines 2 and 3, then a "return" type event -- but
you would be shown line 5 during this event. This is a change
from the behaviour in 2.2 and before, and I've found it
confusing in practice. By setting and using f_lineno when
tracing, one can report a line number different from that
suggested by f_lasti on this one occasion where it's desirable.
*/
int
PyCode_CheckLineNumber
(
PyCodeObject
*
co
,
int
lasti
,
PyAddrPair
*
bounds
)
{
int
size
,
addr
,
line
;
unsigned
char
*
p
;
p
=
(
unsigned
char
*
)
PyString_AS_STRING
(
co
->
co_lnotab
);
size
=
PyString_GET_SIZE
(
co
->
co_lnotab
)
/
2
;
addr
=
0
;
line
=
co
->
co_firstlineno
;
assert
(
line
>
0
);
/* 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. */
/* 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
>
lasti
)
break
;
addr
+=
*
p
++
;
if
(
*
p
)
bounds
->
ap_lower
=
addr
;
line
+=
*
p
++
;
--
size
;
}
/* If lasti and addr don't match exactly, we don't want to
change the lineno slot on the frame or execute a trace
function. Return -1 instead.
*/
if
(
addr
!=
lasti
)
line
=
-
1
;
if
(
size
>
0
)
{
while
(
--
size
>=
0
)
{
addr
+=
*
p
++
;
if
(
*
p
++
)
break
;
}
bounds
->
ap_upper
=
addr
;
}
else
{
bounds
->
ap_upper
=
INT_MAX
;
}
return
line
;
}
Python/ceval.c
View file @
10a9e781
...
...
@@ -3219,132 +3219,29 @@ maybe_call_line_trace(Py_tracefunc func, PyObject *obj,
PyFrameObject
*
frame
,
int
*
instr_lb
,
int
*
instr_ub
,
int
*
instr_prev
)
{
/* 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 *always* call the
line trace function when the above test fails.
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 LOAD_CONST 0 (None)
24 RETURN_VALUE
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).
What we do is only call the line trace function if the co_lnotab
indicates we have jumped to the *start* of a line, i.e. if the
current instruction offset matches the offset given for the
start of a line by the co_lnotab.
This also takes care of the situation where '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.
Why do we set f_lineno when tracing? Well, consider the code
above when 'a' is true. If stepping through this with 'n' in
pdb, you would stop at line 1 with a "call" type event, then
line events on lines 2 and 3, then a "return" type event -- but
you would be shown line 5 during this event. This is a change
from the behaviour in 2.2 and before, and I've found it
confusing in practice. By setting and using f_lineno when
tracing, one can report a line number different from that
suggested by f_lasti on this one occasion where it's desirable.
*/
int
result
=
0
;
/* If the last instruction executed isn't in the current
instruction window, reset the window. If the last
instruction happens to fall at the start of a line or if it
represents a jump backwards, call the trace function.
*/
if
((
frame
->
f_lasti
<
*
instr_lb
||
frame
->
f_lasti
>=
*
instr_ub
))
{
PyCodeObject
*
co
=
frame
->
f_code
;
int
size
,
addr
,
line
;
unsigned
char
*
p
;
size
=
PyString_GET_SIZE
(
co
->
co_lnotab
)
/
2
;
p
=
(
unsigned
char
*
)
PyString_AS_STRING
(
co
->
co_lnotab
);
addr
=
0
;
line
=
co
->
co_firstlineno
;
/* 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. */
/* 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
++
;
if
(
*
p
)
*
instr_lb
=
addr
;
line
+=
*
p
++
;
--
size
;
}
int
line
;
PyAddrPair
bounds
;
if
(
addr
==
frame
->
f_lasti
)
{
line
=
PyCode_CheckLineNumber
(
frame
->
f_code
,
frame
->
f_lasti
,
&
bounds
);
if
(
line
>=
0
)
{
frame
->
f_lineno
=
line
;
result
=
call_trace
(
func
,
obj
,
frame
,
PyTrace_LINE
,
Py_None
);
}
if
(
size
>
0
)
{
while
(
--
size
>=
0
)
{
addr
+=
*
p
++
;
if
(
*
p
++
)
break
;
}
*
instr_ub
=
addr
;
}
else
{
*
instr_ub
=
INT_MAX
;
}
}
*
instr_lb
=
bounds
.
ap_lower
;
*
instr_ub
=
bounds
.
ap_upper
;
}
else
if
(
frame
->
f_lasti
<=
*
instr_prev
)
{
/* jumping back in the same line forces a trace event */
result
=
call_trace
(
func
,
obj
,
frame
,
PyTrace_LINE
,
Py_None
);
result
=
call_trace
(
func
,
obj
,
frame
,
PyTrace_LINE
,
Py_None
);
}
*
instr_prev
=
frame
->
f_lasti
;
return
result
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment