Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
Pyston
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
Boxiang Sun
Pyston
Commits
9067a589
Commit
9067a589
authored
Jun 18, 2015
by
Kevin Modzelewski
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #564 from toshok/incremental-traceback
Incremental traceback
parents
cf145fb8
d034a63e
Changes
19
Show whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
508 additions
and
282 deletions
+508
-282
src/codegen/ast_interpreter.cpp
src/codegen/ast_interpreter.cpp
+39
-15
src/codegen/irgen/irgenerator.cpp
src/codegen/irgen/irgenerator.cpp
+4
-4
src/codegen/unwinding.cpp
src/codegen/unwinding.cpp
+265
-93
src/codegen/unwinding.h
src/codegen/unwinding.h
+15
-0
src/core/types.h
src/core/types.h
+7
-2
src/runtime/capi.cpp
src/runtime/capi.cpp
+1
-1
src/runtime/classobj.cpp
src/runtime/classobj.cpp
+1
-1
src/runtime/cxx_unwind.cpp
src/runtime/cxx_unwind.cpp
+60
-97
src/runtime/generator.cpp
src/runtime/generator.cpp
+3
-3
src/runtime/import.cpp
src/runtime/import.cpp
+5
-5
src/runtime/list.cpp
src/runtime/list.cpp
+1
-1
src/runtime/objmodel.h
src/runtime/objmodel.h
+0
-1
src/runtime/stacktrace.cpp
src/runtime/stacktrace.cpp
+23
-39
src/runtime/super.cpp
src/runtime/super.cpp
+1
-1
src/runtime/traceback.cpp
src/runtime/traceback.cpp
+20
-10
src/runtime/traceback.h
src/runtime/traceback.h
+8
-5
src/runtime/types.cpp
src/runtime/types.cpp
+1
-0
test/tests/incremental_tb.py
test/tests/incremental_tb.py
+54
-0
test/tests/traceback_limits.py
test/tests/traceback_limits.py
+0
-4
No files found.
src/codegen/ast_interpreter.cpp
View file @
9067a589
...
...
@@ -69,13 +69,10 @@ private:
ASTInterpreter
*
interpreter
;
public:
RegisterHelper
(
ASTInterpreter
*
interpreter
,
void
*
frame_addr
);
RegisterHelper
();
~
RegisterHelper
();
static
void
deregister
(
void
*
frame_addr
)
{
assert
(
s_interpreterMap
.
count
(
frame_addr
));
s_interpreterMap
.
erase
(
frame_addr
);
}
void
doRegister
(
void
*
frame_addr
,
ASTInterpreter
*
interpreter
);
static
void
deregister
(
void
*
frame_addr
);
};
union
Value
{
...
...
@@ -102,11 +99,13 @@ public:
void
initArguments
(
int
nargs
,
BoxedClosure
*
closure
,
BoxedGenerator
*
generator
,
Box
*
arg1
,
Box
*
arg2
,
Box
*
arg3
,
Box
**
args
);
static
Value
execute
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
=
NULL
,
AST_stmt
*
start_at
=
NULL
);
// This must not be inlined, because we rely on being able to detect when we're inside of it (by checking whether
// %rip is inside its instruction range) during a stack-trace in order to produce tracebacks inside interpreted
// code.
__attribute__
((
__no_inline__
))
static
Value
execute
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
=
NULL
,
AST_stmt
*
start_at
=
NULL
);
executeInner
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
,
AST_stmt
*
start_at
,
RegisterHelper
*
reg
);
private:
Box
*
createFunction
(
AST
*
node
,
AST_arguments
*
args
,
const
std
::
vector
<
AST_stmt
*>&
body
);
...
...
@@ -318,22 +317,35 @@ void ASTInterpreter::initArguments(int nargs, BoxedClosure* _closure, BoxedGener
}
}
RegisterHelper
::
RegisterHelper
(
ASTInterpreter
*
interpreter
,
void
*
frame_addr
)
:
frame_addr
(
frame_addr
),
interpreter
(
interpreter
)
{
interpreter
->
frame_addr
=
frame_addr
;
s_interpreterMap
[
frame_addr
]
=
interpreter
;
RegisterHelper
::
RegisterHelper
()
:
frame_addr
(
NULL
),
interpreter
(
NULL
)
{
}
RegisterHelper
::~
RegisterHelper
()
{
assert
(
interpreter
);
assert
(
interpreter
->
frame_addr
==
frame_addr
);
interpreter
->
frame_addr
=
nullptr
;
deregister
(
frame_addr
);
}
Value
ASTInterpreter
::
execute
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
,
AST_stmt
*
start_at
)
{
STAT_TIMER
(
t0
,
"us_timer_astinterpreter_execute"
);
void
RegisterHelper
::
doRegister
(
void
*
frame_addr
,
ASTInterpreter
*
interpreter
)
{
assert
(
!
this
->
interpreter
);
assert
(
!
this
->
frame_addr
);
this
->
frame_addr
=
frame_addr
;
this
->
interpreter
=
interpreter
;
interpreter
->
frame_addr
=
frame_addr
;
s_interpreterMap
[
frame_addr
]
=
interpreter
;
}
void
RegisterHelper
::
deregister
(
void
*
frame_addr
)
{
assert
(
frame_addr
);
assert
(
s_interpreterMap
.
count
(
frame_addr
));
s_interpreterMap
.
erase
(
frame_addr
);
}
Value
ASTInterpreter
::
executeInner
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
,
AST_stmt
*
start_at
,
RegisterHelper
*
reg
)
{
void
*
frame_addr
=
__builtin_frame_address
(
0
);
RegisterHelper
frame_registerer
(
&
interpreter
,
frame_add
r
);
reg
->
doRegister
(
frame_addr
,
&
interprete
r
);
Value
v
;
...
...
@@ -373,6 +385,14 @@ Value ASTInterpreter::execute(ASTInterpreter& interpreter, CFGBlock* start_block
return
v
;
}
Value
ASTInterpreter
::
execute
(
ASTInterpreter
&
interpreter
,
CFGBlock
*
start_block
,
AST_stmt
*
start_at
)
{
STAT_TIMER
(
t0
,
"us_timer_astinterpreter_execute"
);
RegisterHelper
frame_registerer
;
return
executeInner
(
interpreter
,
start_block
,
start_at
,
&
frame_registerer
);
}
Value
ASTInterpreter
::
doBinOp
(
Box
*
left
,
Box
*
right
,
int
op
,
BinExpType
exp_type
)
{
switch
(
exp_type
)
{
case
BinExpType
:
:
AugBinOp
:
...
...
@@ -610,6 +630,10 @@ Value ASTInterpreter::visit_invoke(AST_Invoke* node) {
v
=
visit_stmt
(
node
->
stmt
);
next_block
=
node
->
normal_dest
;
}
catch
(
ExcInfo
e
)
{
auto
source
=
getCF
()
->
clfunc
->
source
.
get
();
exceptionCaughtInInterpreter
(
LineInfo
(
node
->
lineno
,
node
->
col_offset
,
source
->
fn
,
source
->
getName
()),
&
e
);
next_block
=
node
->
exc_dest
;
last_exception
=
e
;
}
...
...
@@ -1269,7 +1293,7 @@ Value ASTInterpreter::visit_attribute(AST_Attribute* node) {
}
}
const
void
*
interpreter_instr_addr
=
(
void
*
)
&
ASTInterpreter
::
execute
;
const
void
*
interpreter_instr_addr
=
(
void
*
)
&
ASTInterpreter
::
execute
Inner
;
Box
*
astInterpretFunction
(
CompiledFunction
*
cf
,
int
nargs
,
Box
*
closure
,
Box
*
generator
,
Box
*
globals
,
Box
*
arg1
,
Box
*
arg2
,
Box
*
arg3
,
Box
**
args
)
{
...
...
src/codegen/irgen/irgenerator.cpp
View file @
9067a589
...
...
@@ -110,8 +110,8 @@ static llvm::Value* getClosureElementGep(IREmitter& emitter, llvm::Value* closur
static
llvm
::
Value
*
getBoxedLocalsGep
(
llvm
::
IRBuilder
<
true
>&
builder
,
llvm
::
Value
*
v
)
{
static_assert
(
offsetof
(
FrameInfo
,
exc
)
==
0
,
""
);
static_assert
(
sizeof
(
ExcInfo
)
==
24
,
""
);
static_assert
(
offsetof
(
FrameInfo
,
boxedLocals
)
==
24
,
""
);
static_assert
(
sizeof
(
ExcInfo
)
==
32
,
""
);
static_assert
(
offsetof
(
FrameInfo
,
boxedLocals
)
==
32
,
""
);
return
builder
.
CreateConstInBoundsGEP2_32
(
v
,
0
,
1
);
}
...
...
@@ -122,9 +122,9 @@ static llvm::Value* getExcinfoGep(llvm::IRBuilder<true>& builder, llvm::Value* v
static
llvm
::
Value
*
getFrameObjGep
(
llvm
::
IRBuilder
<
true
>&
builder
,
llvm
::
Value
*
v
)
{
static_assert
(
offsetof
(
FrameInfo
,
exc
)
==
0
,
""
);
static_assert
(
sizeof
(
ExcInfo
)
==
24
,
""
);
static_assert
(
sizeof
(
ExcInfo
)
==
32
,
""
);
static_assert
(
sizeof
(
Box
*
)
==
8
,
""
);
static_assert
(
offsetof
(
FrameInfo
,
frame_obj
)
==
32
,
""
);
static_assert
(
offsetof
(
FrameInfo
,
frame_obj
)
==
40
,
""
);
return
builder
.
CreateConstInBoundsGEP2_32
(
v
,
0
,
2
);
// TODO: this could be made more resilient by doing something like
// gep->accumulateConstantOffset(g.tm->getDataLayout(), ap_offset)
...
...
src/codegen/unwinding.cpp
View file @
9067a589
...
...
@@ -58,6 +58,8 @@ struct uw_table_entry {
namespace
pyston
{
static
BoxedClass
*
unwind_session_cls
;
// Parse an .eh_frame section, and construct a "binary search table" such as you would find in a .eh_frame_hdr section.
// Currently only supports .eh_frame sections with exactly one fde.
// See http://www.airs.com/blog/archives/460 for some useful info.
...
...
@@ -277,6 +279,10 @@ struct PythonFrameId {
uint64_t
ip
;
uint64_t
bp
;
PythonFrameId
()
{}
PythonFrameId
(
FrameType
type
,
uint64_t
ip
,
uint64_t
bp
)
:
type
(
type
),
ip
(
ip
),
bp
(
bp
)
{}
bool
operator
==
(
const
PythonFrameId
&
rhs
)
const
{
return
(
this
->
type
==
rhs
.
type
)
&&
(
this
->
ip
==
rhs
.
ip
);
}
};
...
...
@@ -289,13 +295,11 @@ public:
intptr_t
regs
[
16
];
uint16_t
regs_valid
;
PythonFrameIteratorImpl
(
const
PythonFrameIteratorImpl
&
)
=
delete
;
void
operator
=
(
const
PythonFrameIteratorImpl
&
)
=
delete
;
PythonFrameIteratorImpl
(
const
PythonFrameIteratorImpl
&&
)
=
delete
;
void
operator
=
(
const
PythonFrameIteratorImpl
&&
)
=
delete
;
PythonFrameIteratorImpl
()
:
regs_valid
(
0
)
{}
PythonFrameIteratorImpl
(
PythonFrameId
::
FrameType
type
,
uint64_t
ip
,
uint64_t
bp
,
CompiledFunction
*
cf
)
:
id
(
PythonFrameId
(
type
,
ip
,
bp
)),
cf
(
cf
),
regs_valid
(
0
)
{}
CompiledFunction
*
getCF
()
const
{
assert
(
cf
);
return
cf
;
...
...
@@ -411,45 +415,47 @@ static unw_word_t getFunctionEnd(unw_word_t ip) {
return
pip
.
end_ip
;
}
// While I'm not a huge fan of the callback-passing style, libunwind cursors are only valid for
// the stack frame that they were created in, so we need to use this approach (as opposed to
// C++11 range loops, for example).
// Return true from the handler to stop iteration at that frame.
void
unwindPythonStack
(
std
::
function
<
bool
(
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
)
>
func
)
{
static
bool
inASTInterpreterExecuteInner
(
unw_word_t
ip
)
{
static
unw_word_t
interpreter_instr_end
=
getFunctionEnd
((
unw_word_t
)
interpreter_instr_addr
);
static
unw_word_t
generator_entry_end
=
getFunctionEnd
((
unw_word_t
)
generatorEntry
);
unw_context_t
ctx
;
unw_cursor_t
cursor
;
unw_getcontext
(
&
ctx
);
unw_init_local
(
&
cursor
,
&
ctx
);
return
((
unw_word_t
)
interpreter_instr_addr
<=
ip
&&
ip
<
interpreter_instr_end
);
}
// If the previous (lower, ie newer) frame was a frame that got OSR'd into,
// we skip the previous frame, its OSR parent.
bool
was_osr
=
false
;
static
bool
inGeneratorEntry
(
unw_word_t
ip
)
{
static
unw_word_t
generator_entry_end
=
getFunctionEnd
((
unw_word_t
)
generatorEntry
);
return
((
unw_word_t
)
generatorEntry
<=
ip
&&
ip
<
generator_entry_end
);
}
while
(
true
)
{
int
r
=
unw_step
(
&
cursor
);
assert
(
r
>=
0
);
if
(
r
==
0
)
break
;
static
inline
unw_word_t
get_cursor_reg
(
unw_cursor_t
*
cursor
,
int
reg
)
{
unw_word_t
v
;
unw_get_reg
(
cursor
,
reg
,
&
v
);
return
v
;
}
static
inline
unw_word_t
get_cursor_ip
(
unw_cursor_t
*
cursor
)
{
return
get_cursor_reg
(
cursor
,
UNW_REG_IP
)
-
1
;
}
static
inline
unw_word_t
get_cursor_bp
(
unw_cursor_t
*
cursor
)
{
return
get_cursor_reg
(
cursor
,
UNW_TDEP_BP
);
}
// if the given ip/bp correspond to a jitted frame or
// ASTInterpreter::execute_inner frame, return true and return the
// frame information through the PythonFrameIteratorImpl* info arg.
bool
frameIsPythonFrame
(
unw_word_t
ip
,
unw_word_t
bp
,
unw_cursor_t
*
cursor
,
PythonFrameIteratorImpl
*
info
)
{
CompiledFunction
*
cf
=
getCFForAddress
(
ip
);
bool
jitted
=
cf
!=
NULL
;
if
(
!
cf
)
{
if
(
inASTInterpreterExecuteInner
(
ip
))
{
cf
=
getCFForInterpretedFrame
((
void
*
)
bp
);
assert
(
cf
);
}
}
unw_word_t
ip
;
unw_get_reg
(
&
cursor
,
UNW_REG_IP
,
&
ip
);
unw_word_t
bp
;
unw_get_reg
(
&
cursor
,
UNW_TDEP_BP
,
&
bp
);
if
(
!
cf
)
return
false
;
CompiledFunction
*
cf
=
getCFForAddress
(
ip
);
if
(
cf
)
{
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
info
(
new
PythonFrameIteratorImpl
());
info
->
id
.
type
=
PythonFrameId
::
COMPILED
;
info
->
id
.
ip
=
ip
;
info
->
id
.
bp
=
bp
;
info
->
cf
=
cf
;
if
(
!
was_osr
)
{
*
info
=
PythonFrameIteratorImpl
(
jitted
?
PythonFrameId
::
COMPILED
:
PythonFrameId
::
INTERPRETED
,
ip
,
bp
,
cf
);
if
(
jitted
)
{
// Try getting all the callee-save registers, and save the ones we were able to get.
// Some of them may be inaccessible, I think because they weren't defined by that
// stack frame, which can show up as a -UNW_EBADREG return code.
...
...
@@ -457,41 +463,213 @@ void unwindPythonStack(std::function<bool(std::unique_ptr<PythonFrameIteratorImp
if
(
!
assembler
::
Register
::
fromDwarf
(
i
).
isCalleeSave
())
continue
;
unw_word_t
r
;
int
code
=
unw_get_reg
(
&
cursor
,
i
,
&
r
);
int
code
=
unw_get_reg
(
cursor
,
i
,
&
r
);
ASSERT
(
code
==
0
||
code
==
-
UNW_EBADREG
,
"%d %d"
,
code
,
i
);
if
(
code
==
0
)
{
info
->
regs
[
i
]
=
r
;
info
->
regs_valid
|=
(
1
<<
i
);
}
}
}
bool
stop
=
func
(
std
::
move
(
info
));
if
(
stop
)
break
;
return
true
;
}
class
PythonUnwindSession
:
public
Box
{
ExcInfo
exc_info
;
bool
skip
;
bool
is_active
;
Timer
t
;
public:
DEFAULT_CLASS_SIMPLE
(
unwind_session_cls
);
PythonUnwindSession
()
:
exc_info
(
NULL
,
NULL
,
NULL
),
skip
(
false
),
is_active
(
false
),
t
(
/*min_usec=*/
10000
)
{}
ExcInfo
*
getExcInfoStorage
()
{
RELEASE_ASSERT
(
is_active
,
""
);
return
&
exc_info
;
}
was_osr
=
(
bool
)
cf
->
entry_descriptor
;
continue
;
bool
shouldSkipFrame
()
const
{
return
skip
;
}
void
setShouldSkipNextFrame
(
bool
skip
)
{
this
->
skip
=
skip
;
}
bool
isActive
()
const
{
return
is_active
;
}
void
begin
()
{
RELEASE_ASSERT
(
!
is_active
,
""
);
exc_info
=
ExcInfo
(
NULL
,
NULL
,
NULL
);
skip
=
false
;
is_active
=
true
;
t
.
restart
();
static
StatCounter
stat
(
"unwind_sessions"
);
stat
.
log
();
}
void
end
()
{
RELEASE_ASSERT
(
is_active
,
""
);
is_active
=
false
;
static
StatCounter
stat
(
"us_unwind_session"
);
stat
.
log
(
t
.
end
());
}
void
addTraceback
(
const
LineInfo
&
line_info
)
{
RELEASE_ASSERT
(
is_active
,
""
);
if
(
exc_info
.
reraise
)
{
exc_info
.
reraise
=
false
;
return
;
}
BoxedTraceback
::
here
(
line_info
,
&
exc_info
.
traceback
);
}
void
logException
()
{
#if STAT_EXCEPTIONS
static
StatCounter
num_exceptions
(
"num_exceptions"
);
num_exceptions
.
log
();
std
::
string
stat_name
;
if
(
PyType_Check
(
exc_info
.
type
))
stat_name
=
"num_exceptions_"
+
std
::
string
(
static_cast
<
BoxedClass
*>
(
exc_info
.
type
)
->
tp_name
);
else
stat_name
=
"num_exceptions_"
+
std
::
string
(
exc_info
.
value
->
cls
->
tp_name
);
Stats
::
log
(
Stats
::
getStatCounter
(
stat_name
));
#if STAT_EXCEPTIONS_LOCATION
logByCurrentPythonLine
(
stat_name
);
#endif
#endif
}
static
void
gcHandler
(
GCVisitor
*
v
,
Box
*
_o
)
{
assert
(
_o
->
cls
==
unwind_session_cls
);
PythonUnwindSession
*
o
=
static_cast
<
PythonUnwindSession
*>
(
_o
);
// this is our hack for eventually collecting
// exceptions/tracebacks after the exception has been caught.
// If a collection happens and a given thread's
// PythonUnwindSession isn't active, its exception info can be
// collected.
if
(
!
o
->
is_active
)
return
;
v
->
visitIf
(
o
->
exc_info
.
type
);
v
->
visitIf
(
o
->
exc_info
.
value
);
v
->
visitIf
(
o
->
exc_info
.
traceback
);
}
};
static
__thread
PythonUnwindSession
*
cur_unwind
;
PythonUnwindSession
*
beginPythonUnwindSession
()
{
if
(
!
cur_unwind
)
{
cur_unwind
=
new
PythonUnwindSession
();
pyston
::
gc
::
registerPermanentRoot
(
cur_unwind
);
}
cur_unwind
->
begin
();
return
cur_unwind
;
}
PythonUnwindSession
*
getActivePythonUnwindSession
()
{
RELEASE_ASSERT
(
cur_unwind
&&
cur_unwind
->
isActive
(),
""
);
return
cur_unwind
;
}
if
((
unw_word_t
)
interpreter_instr_addr
<=
ip
&&
ip
<
interpreter_instr_end
)
{
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
info
(
new
PythonFrameIteratorImpl
());
info
->
id
.
type
=
PythonFrameId
::
INTERPRETED
;
info
->
id
.
ip
=
ip
;
info
->
id
.
bp
=
bp
;
cf
=
info
->
cf
=
getCFForInterpretedFrame
((
void
*
)
bp
);
void
endPythonUnwindSession
(
PythonUnwindSession
*
unwind
)
{
RELEASE_ASSERT
(
unwind
&&
unwind
==
cur_unwind
,
""
);
unwind
->
end
();
}
void
*
getPythonUnwindSessionExceptionStorage
(
PythonUnwindSession
*
unwind
)
{
RELEASE_ASSERT
(
unwind
&&
unwind
==
cur_unwind
,
""
);
PythonUnwindSession
*
state
=
static_cast
<
PythonUnwindSession
*>
(
unwind
);
return
state
->
getExcInfoStorage
();
}
void
throwingException
(
PythonUnwindSession
*
unwind
)
{
RELEASE_ASSERT
(
unwind
&&
unwind
==
cur_unwind
,
""
);
unwind
->
logException
();
}
static
const
LineInfo
lineInfoForFrame
(
PythonFrameIteratorImpl
*
frame_it
)
{
AST_stmt
*
current_stmt
=
frame_it
->
getCurrentStatement
();
auto
*
cf
=
frame_it
->
getCF
();
assert
(
cf
);
if
(
!
was_osr
)
{
bool
stop
=
func
(
std
::
move
(
info
));
if
(
stop
)
break
;
auto
source
=
cf
->
clfunc
->
source
.
get
();
return
LineInfo
(
current_stmt
->
lineno
,
current_stmt
->
col_offset
,
source
->
fn
,
source
->
getName
());
}
void
exceptionCaughtInInterpreter
(
LineInfo
line_info
,
ExcInfo
*
exc_info
)
{
// basically the same as PythonUnwindSession::addTraceback, but needs to
// be callable after an PythonUnwindSession has ended. The interpreter
// will call this from catch blocks if it needs to ensure that a
// line is added. Right now this only happens in
// ASTInterpreter::visit_invoke.
// It's basically the same except for one thing: we don't have to
// worry about the 'skip' (osr) state that PythonUnwindSession handles
// here, because the only way we could have gotten into the ast
// interpreter is if the exception wasn't caught, and if there was
// the osr frame for the one the interpreter is running, it would
// have already caught it.
if
(
exc_info
->
reraise
)
{
exc_info
->
reraise
=
false
;
return
;
}
BoxedTraceback
::
here
(
line_info
,
&
exc_info
->
traceback
);
}
was_osr
=
(
bool
)
cf
->
entry_descriptor
;
continue
;
void
unwindingThroughFrame
(
PythonUnwindSession
*
unwind_session
,
unw_cursor_t
*
cursor
)
{
unw_word_t
ip
=
get_cursor_ip
(
cursor
);
unw_word_t
bp
=
get_cursor_bp
(
cursor
);
PythonFrameIteratorImpl
frame_iter
;
if
(
frameIsPythonFrame
(
ip
,
bp
,
cursor
,
&
frame_iter
))
{
if
(
!
unwind_session
->
shouldSkipFrame
())
unwind_session
->
addTraceback
(
lineInfoForFrame
(
&
frame_iter
));
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
unwind_session
->
setShouldSkipNextFrame
((
bool
)
frame_iter
.
cf
->
entry_descriptor
);
}
}
// While I'm not a huge fan of the callback-passing style, libunwind cursors are only valid for
// the stack frame that they were created in, so we need to use this approach (as opposed to
// C++11 range loops, for example).
// Return true from the handler to stop iteration at that frame.
template
<
typename
Func
>
void
unwindPythonStack
(
Func
func
)
{
PythonUnwindSession
*
unwind_session
=
new
PythonUnwindSession
();
unwind_session
->
begin
();
if
((
unw_word_t
)
generatorEntry
<=
ip
&&
ip
<
generator_entry_end
)
{
unw_context_t
ctx
;
unw_cursor_t
cursor
;
unw_getcontext
(
&
ctx
);
unw_init_local
(
&
cursor
,
&
ctx
);
while
(
true
)
{
int
r
=
unw_step
(
&
cursor
);
assert
(
r
>=
0
);
if
(
r
==
0
)
break
;
unw_word_t
ip
=
get_cursor_ip
(
&
cursor
);
unw_word_t
bp
=
get_cursor_bp
(
&
cursor
);
bool
stop_unwinding
=
false
;
PythonFrameIteratorImpl
frame_iter
;
if
(
frameIsPythonFrame
(
ip
,
bp
,
&
cursor
,
&
frame_iter
))
{
if
(
!
unwind_session
->
shouldSkipFrame
())
stop_unwinding
=
func
(
&
frame_iter
);
// frame_iter->cf->entry_descriptor will be non-null for OSR frames.
unwind_session
->
setShouldSkipNextFrame
((
bool
)
frame_iter
.
cf
->
entry_descriptor
);
}
if
(
stop_unwinding
)
break
;
if
(
inGeneratorEntry
(
ip
))
{
// for generators continue unwinding in the context in which the generator got called
Context
*
remote_ctx
=
getReturnContextForGeneratorFrame
((
void
*
)
bp
);
// setup unw_context_t struct from the infos we have, seems like this is enough to make unwinding work.
...
...
@@ -509,28 +687,20 @@ void unwindPythonStack(std::function<bool(std::unique_ptr<PythonFrameIteratorImp
// keep unwinding
}
unwind_session
->
end
();
}
static
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
getTopPythonFrame
()
{
STAT_TIMER
(
t0
,
"us_timer_getTopPythonFrame"
);
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
rtn
(
nullptr
);
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
iter
)
{
rtn
=
std
::
move
(
iter
);
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
iter
)
{
rtn
=
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
(
new
PythonFrameIteratorImpl
(
*
iter
)
);
return
true
;
});
return
rtn
;
}
static
const
LineInfo
*
lineInfoForFrame
(
PythonFrameIteratorImpl
&
frame_it
)
{
AST_stmt
*
current_stmt
=
frame_it
.
getCurrentStatement
();
auto
*
cf
=
frame_it
.
getCF
();
assert
(
cf
);
auto
source
=
cf
->
clfunc
->
source
.
get
();
return
new
LineInfo
(
current_stmt
->
lineno
,
current_stmt
->
col_offset
,
source
->
fn
,
source
->
getName
());
}
// To produce a traceback, we:
//
// 1. Use libunwind to produce a cursor into our stack.
...
...
@@ -584,20 +754,16 @@ BoxedTraceback* getTraceback() {
Timer
_t
(
"getTraceback"
,
1000
);
std
::
vector
<
const
LineInfo
*>
entries
;
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
const
LineInfo
*
line_info
=
lineInfoForFrame
(
*
frame_iter
.
get
());
if
(
line_info
)
entries
.
push_back
(
line_info
);
Box
*
tb
=
new
BoxedTraceback
();
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
BoxedTraceback
::
here
(
lineInfoForFrame
(
frame_iter
),
&
tb
);
return
false
;
});
std
::
reverse
(
entries
.
begin
(),
entries
.
end
());
long
us
=
_t
.
end
();
us_gettraceback
.
log
(
us
);
return
new
BoxedTraceback
(
std
::
move
(
entries
)
);
return
static_cast
<
BoxedTraceback
*>
(
tb
);
}
ExcInfo
*
getFrameExcInfo
()
{
...
...
@@ -605,7 +771,7 @@ ExcInfo* getFrameExcInfo() {
ExcInfo
*
copy_from_exc
=
NULL
;
ExcInfo
*
cur_exc
=
NULL
;
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
FrameInfo
*
frame_info
=
frame_iter
->
getFrameInfo
();
copy_from_exc
=
&
frame_info
->
exc
;
...
...
@@ -665,9 +831,9 @@ BoxedModule* getCurrentModule() {
PythonFrameIterator
getPythonFrame
(
int
depth
)
{
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
rtn
(
nullptr
);
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
if
(
depth
==
0
)
{
rtn
=
std
::
move
(
frame_iter
);
rtn
=
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
(
new
PythonFrameIteratorImpl
(
*
frame_iter
)
);
return
true
;
}
depth
--
;
...
...
@@ -691,13 +857,13 @@ PythonFrameIterator::PythonFrameIterator(std::unique_ptr<PythonFrameIteratorImpl
std
::
swap
(
this
->
impl
,
impl
);
}
// TODO factor getStackLoc
la
sIncludingUserHidden and fastLocalsToBoxedLocals
// TODO factor getStackLoc
al
sIncludingUserHidden and fastLocalsToBoxedLocals
// because they are pretty ugly but have a pretty repetitive pattern.
FrameStackState
getFrameStackState
()
{
FrameStackState
rtn
(
NULL
,
NULL
);
bool
found
=
false
;
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
BoxedDict
*
d
;
BoxedClosure
*
closure
;
CompiledFunction
*
cf
;
...
...
@@ -957,9 +1123,9 @@ FrameInfo* PythonFrameIterator::getFrameInfo() {
PythonFrameIterator
PythonFrameIterator
::
getCurrentVersion
()
{
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
rtn
(
nullptr
);
auto
&
impl
=
this
->
impl
;
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
if
(
frame_iter
->
pointsToTheSameAs
(
*
impl
.
get
()))
{
rtn
=
std
::
move
(
frame_iter
);
rtn
=
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
(
new
PythonFrameIteratorImpl
(
*
frame_iter
)
);
return
true
;
}
return
false
;
...
...
@@ -975,9 +1141,9 @@ PythonFrameIterator PythonFrameIterator::back() {
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
rtn
(
nullptr
);
auto
&
impl
=
this
->
impl
;
bool
found
=
false
;
unwindPythonStack
([
&
](
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
frame_iter
)
{
unwindPythonStack
([
&
](
PythonFrameIteratorImpl
*
frame_iter
)
{
if
(
found
)
{
rtn
=
std
::
move
(
frame_iter
);
rtn
=
std
::
unique_ptr
<
PythonFrameIteratorImpl
>
(
new
PythonFrameIteratorImpl
(
*
frame_iter
)
);
return
true
;
}
...
...
@@ -1015,4 +1181,10 @@ void logByCurrentPythonLine(const std::string& stat_name) {
llvm
::
JITEventListener
*
makeTracebacksListener
()
{
return
new
TracebacksEventListener
();
}
void
setupUnwinding
()
{
unwind_session_cls
=
BoxedHeapClass
::
create
(
type_cls
,
object_cls
,
PythonUnwindSession
::
gcHandler
,
0
,
0
,
sizeof
(
PythonUnwindSession
),
false
,
"unwind_session"
);
unwind_session_cls
->
freeze
();
}
}
src/codegen/unwinding.h
View file @
9067a589
...
...
@@ -19,6 +19,10 @@
#include "codegen/codegen.h"
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#undef UNW_LOCAL_ONLY
namespace
pyston
{
class
Box
;
...
...
@@ -29,6 +33,7 @@ struct FrameInfo;
void
registerDynamicEhFrame
(
uint64_t
code_addr
,
size_t
code_size
,
uint64_t
eh_frame_addr
,
size_t
eh_frame_size
);
void
setupUnwinding
();
BoxedModule
*
getCurrentModule
();
Box
*
getGlobals
();
// returns either the module or a globals dict
Box
*
getGlobalsDict
();
// always returns a dict-like object
...
...
@@ -36,6 +41,16 @@ CompiledFunction* getCFForAddress(uint64_t addr);
BoxedTraceback
*
getTraceback
();
class
PythonUnwindSession
;
PythonUnwindSession
*
beginPythonUnwindSession
();
PythonUnwindSession
*
getActivePythonUnwindSession
();
void
throwingException
(
PythonUnwindSession
*
unwind_session
);
void
endPythonUnwindSession
(
PythonUnwindSession
*
unwind_session
);
void
*
getPythonUnwindSessionExceptionStorage
(
PythonUnwindSession
*
unwind_session
);
void
unwindingThroughFrame
(
PythonUnwindSession
*
unwind_session
,
unw_cursor_t
*
cursor
);
void
exceptionCaughtInInterpreter
(
LineInfo
line_info
,
ExcInfo
*
exc_info
);
struct
ExecutionPoint
{
CompiledFunction
*
cf
;
AST_stmt
*
current_stmt
;
...
...
src/core/types.h
View file @
9067a589
...
...
@@ -88,6 +88,10 @@ public:
GCVisitor
(
TraceStack
*
stack
)
:
stack
(
stack
)
{}
// These all work on *user* pointers, ie pointers to the user_data section of GCAllocations
void
visitIf
(
void
*
p
)
{
if
(
p
)
visit
(
p
);
}
void
visit
(
void
*
p
);
void
visitRange
(
void
*
const
*
start
,
void
*
const
*
end
);
void
visitPotential
(
void
*
p
);
...
...
@@ -666,7 +670,7 @@ void raiseSyntaxErrorHelper(llvm::StringRef file, llvm::StringRef func, AST* nod
struct
LineInfo
{
public:
const
int
line
,
column
;
int
line
,
column
;
std
::
string
file
,
func
;
LineInfo
(
int
line
,
int
column
,
llvm
::
StringRef
file
,
llvm
::
StringRef
func
)
...
...
@@ -675,11 +679,12 @@ public:
struct
ExcInfo
{
Box
*
type
,
*
value
,
*
traceback
;
bool
reraise
;
#ifndef NDEBUG
ExcInfo
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
);
#else
ExcInfo
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
)
:
type
(
type
),
value
(
value
),
traceback
(
traceback
)
{}
ExcInfo
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
)
:
type
(
type
),
value
(
value
),
traceback
(
traceback
)
,
reraise
(
false
)
{}
#endif
bool
matches
(
BoxedClass
*
cls
)
const
;
void
printExcAndTraceback
()
const
;
...
...
src/runtime/capi.cpp
View file @
9067a589
...
...
@@ -926,7 +926,7 @@ void checkAndThrowCAPIException() {
RELEASE_ASSERT
(
value
->
cls
==
type
,
"unsupported"
);
if
(
tb
!=
None
)
raiseRaw
(
ExcInfo
(
value
->
cls
,
value
,
tb
)
);
throw
ExcInfo
(
value
->
cls
,
value
,
tb
);
raiseExc
(
value
);
}
}
...
...
src/runtime/classobj.cpp
View file @
9067a589
...
...
@@ -162,7 +162,7 @@ static Box* classobjGetattribute(Box* _cls, Box* _attr) {
}
}
Box
*
r
=
classLookup
(
cls
,
std
::
string
(
attr
->
s
()
));
Box
*
r
=
classLookup
(
cls
,
attr
->
s
(
));
if
(
!
r
)
raiseExcHelper
(
AttributeError
,
"class %s has no attribute '%s'"
,
cls
->
name
->
data
(),
attr
->
data
());
...
...
src/runtime/cxx_unwind.cpp
View file @
9067a589
...
...
@@ -27,6 +27,7 @@
#include "core/types.h" // for ExcInfo
#include "core/util.h" // Timer
#include "runtime/generator.h" // generatorEntry
#include "runtime/traceback.h" // BoxedTraceback::addLine
#define UNW_LOCAL_ONLY
#include <libunwind.h>
...
...
@@ -35,9 +36,6 @@
#define NORETURN __attribute__((__noreturn__))
// canary used in ExcData in debug mode to catch exception-value corruption.
#define CANARY_VALUE 0xdeadbeef
// An action of 0 in the LSDA action table indicates cleanup.
#define CLEANUP_ACTION 0
...
...
@@ -73,42 +71,13 @@ template <typename T> static inline void check(T x) {
namespace
pyston
{
struct
ExcData
;
extern
thread_local
ExcData
exception_ferry
;
struct
ExcData
{
ExcInfo
exc
;
#ifndef NDEBUG
unsigned
canary
=
CANARY_VALUE
;
#endif
ExcData
()
:
exc
(
nullptr
,
nullptr
,
nullptr
)
{}
ExcData
(
ExcInfo
e
)
:
exc
(
e
)
{}
ExcData
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
)
:
exc
(
type
,
value
,
traceback
)
{}
void
check
()
const
{
assert
(
this
);
assert
(
canary
==
CANARY_VALUE
);
assert
(
exc
.
type
&&
exc
.
value
&&
exc
.
traceback
);
ASSERT
(
gc
::
isValidGCObject
(
exc
.
type
),
"%p"
,
exc
.
type
);
ASSERT
(
gc
::
isValidGCObject
(
exc
.
value
),
"%p"
,
exc
.
value
);
ASSERT
(
gc
::
isValidGCObject
(
exc
.
traceback
),
"%p"
,
exc
.
traceback
);
assert
(
this
==
&
exception_ferry
);
}
};
thread_local
ExcData
exception_ferry
;
static_assert
(
offsetof
(
ExcData
,
exc
)
==
0
,
"wrong offset"
);
// Timer that auto-logs.
struct
LogTimer
{
StatCounter
&
counter
;
Timer
timer
;
LogTimer
(
const
char
*
desc
,
StatCounter
&
ctr
,
long
min_usec
=
-
1
)
:
counter
(
ctr
),
timer
(
desc
,
min_usec
)
{}
~
LogTimer
()
{
counter
.
log
(
timer
.
end
());
}
};
void
checkExcInfo
(
const
ExcInfo
*
exc
)
{
assert
(
exc
);
assert
(
exc
->
type
&&
exc
->
value
&&
exc
->
traceback
);
ASSERT
(
gc
::
isValidGCObject
(
exc
->
type
),
"%p"
,
exc
->
type
);
ASSERT
(
gc
::
isValidGCObject
(
exc
->
value
),
"%p"
,
exc
->
value
);
ASSERT
(
gc
::
isValidGCObject
(
exc
->
traceback
),
"%p"
,
exc
->
traceback
);
}
static
StatCounter
us_unwind_loop
(
"us_unwind_loop"
);
static
StatCounter
us_unwind_resume_catch
(
"us_unwind_resume_catch"
);
...
...
@@ -422,8 +391,8 @@ static inline bool find_call_site_entry(const lsda_info_t* info, const uint8_t*
}
static
inline
NORETURN
void
resume
(
unw_cursor_t
*
cursor
,
const
uint8_t
*
landing_pad
,
int64_t
switch_value
,
const
Exc
Data
*
exc_data
)
{
exc_data
->
check
(
);
const
Exc
Info
*
exc_data
)
{
checkExcInfo
(
exc_data
);
assert
(
landing_pad
);
if
(
VERBOSITY
(
"cxx_unwind"
)
>=
4
)
printf
(
" * RESUMED: ip %p switch_value %ld
\n
"
,
(
const
void
*
)
landing_pad
,
(
long
)
switch_value
);
...
...
@@ -508,16 +477,8 @@ static inline int64_t determine_action(const lsda_info_t* info, const call_site_
RELEASE_ASSERT
(
0
,
"action chain exhausted and no cleanup indicated"
);
}
static
inline
int
step
(
unw_cursor_t
*
cp
)
{
LogTimer
t
(
"unw_step"
,
us_unwind_step
,
5
);
return
unw_step
(
cp
);
}
// The stack-unwinding loop.
// TODO: integrate incremental traceback generation into this function
static
inline
void
unwind_loop
(
const
ExcData
*
exc_data
)
{
Timer
t
(
"unwind_loop"
,
50
);
static
inline
void
unwind_loop
(
ExcInfo
*
exc_data
)
{
// NB. https://monoinfinito.wordpress.com/series/exception-handling-in-c/ is a very useful resource
// as are http://www.airs.com/blog/archives/460 and http://www.airs.com/blog/archives/464
unw_cursor_t
cursor
;
...
...
@@ -530,13 +491,14 @@ static inline void unwind_loop(const ExcData* exc_data) {
unw_getcontext
(
&
uc
);
unw_init_local
(
&
cursor
,
&
uc
);
while
(
step
(
&
cursor
)
>
0
)
{
auto
unwind_session
=
getActivePythonUnwindSession
();
while
(
unw_step
(
&
cursor
)
>
0
)
{
unw_proc_info_t
pip
;
{
// NB. unw_get_proc_info is slow; a significant chunk of all time spent unwinding is spent here.
LogTimer
t_procinfo
(
"get_proc_info"
,
us_unwind_get_proc_info
,
10
);
check
(
unw_get_proc_info
(
&
cursor
,
&
pip
));
}
assert
((
pip
.
lsda
==
0
)
==
(
pip
.
handler
==
0
));
assert
(
pip
.
flags
==
0
);
...
...
@@ -544,6 +506,11 @@ static inline void unwind_loop(const ExcData* exc_data) {
print_frame
(
&
cursor
,
&
pip
);
}
// let the PythonUnwindSession know that we're in a new frame,
// giving it a chance to possibly add a traceback entry for
// it.
unwindingThroughFrame
(
unwind_session
,
&
cursor
);
// Skip frames without handlers
if
(
pip
.
handler
==
0
)
{
continue
;
...
...
@@ -560,8 +527,6 @@ static inline void unwind_loop(const ExcData* exc_data) {
call_site_entry_t
entry
;
{
LogTimer
t_call_site
(
"find_call_site_entry"
,
us_unwind_find_call_site_entry
,
10
);
// 2. Find our current IP in the call site table.
unw_word_t
ip
;
unw_get_reg
(
&
cursor
,
UNW_REG_IP
,
&
ip
);
...
...
@@ -592,18 +557,35 @@ static inline void unwind_loop(const ExcData* exc_data) {
}
int64_t
switch_value
=
determine_action
(
&
info
,
&
entry
);
us_unwind_loop
.
log
(
t
.
end
());
if
(
switch_value
!=
CLEANUP_ACTION
)
{
// we're transfering control to a non-cleanup landing pad.
// i.e. a catch block. thus ends our unwind session.
endPythonUnwindSession
(
unwind_session
);
}
static_assert
(
THREADING_USE_GIL
,
"have to make the unwind session usage in this file thread safe!"
);
// there is a python unwinding implementation detail leaked
// here - that the unwind session can be ended but its
// exception storage is still around.
//
// this manifests itself as this short window here where we've
// (possibly) ended the unwind session above but we still need
// to pass exc_data (which is the exceptionStorage for this
// unwind session) to resume().
//
// the only way this could bite us is if we somehow clobber
// the PythonUnwindSession's storage, or cause a GC to occur, before
// transfering control to the landing pad in resume().
//
resume
(
&
cursor
,
entry
.
landing_pad
,
switch_value
,
exc_data
);
}
us_unwind_loop
.
log
(
t
.
end
());
// Hit end of stack! return & let unwindException determine what to do.
}
// The unwinder entry-point.
static
void
unwind
(
const
ExcData
*
exc
)
{
exc
->
check
(
);
static
void
unwind
(
ExcInfo
*
exc
)
{
checkExcInfo
(
exc
);
unwind_loop
(
exc
);
// unwind_loop returned, couldn't find any handler. ruh-roh.
panic
();
...
...
@@ -637,7 +619,7 @@ extern "C" void _Unwind_Resume(struct _Unwind_Exception* _exc) {
if
(
VERBOSITY
(
"cxx_unwind"
)
>=
4
)
printf
(
"***** _Unwind_Resume() *****
\n
"
);
// we give `_exc' type `struct _Unwind_Exception*' because unwind.h demands it; it's not actually accurate
const
pyston
::
ExcData
*
data
=
(
const
pyston
::
ExcData
*
)
_exc
;
pyston
::
ExcInfo
*
data
=
(
pyston
::
ExcInfo
*
)
_exc
;
pyston
::
unwind
(
data
);
}
...
...
@@ -648,45 +630,24 @@ extern "C" void* __cxa_allocate_exception(size_t size) noexcept {
// we should only ever be throwing ExcInfos
RELEASE_ASSERT
(
size
==
sizeof
(
pyston
::
ExcInfo
),
"allocating exception whose size doesn't match ExcInfo"
);
// Instead of allocating memory for this exception, we return a pointer to a pre-allocated thread-local variable.
//
// This variable, pyston::exception_ferry, is used only while we are unwinding, and should not be used outside of
// the unwinder. Since it's a thread-local variable, we *cannot* throw any exceptions while it is live, otherwise we
// would clobber it and forget our old exception.
//
// Q: Why can't we just use cur_thread_state.curexc_{type,value,traceback}?
//
// A: Because that conflates the space used to store exceptions during C++ unwinding with the space used to store
// them during C-API return-code based unwinding! This actually comes up in practice - the original version *did*
// use curexc_{type,value,traceback}, and it had a bug.
//
// In particular, we need to unset the C API exception at an appropriate point so as not to make C-API functions
// *think* an exception is being thrown when one isn't. The natural place is __cxa_begin_catch, BUT we need some way
// to communicate the exception info to the inside of the catch block - and all we get is the return value of
// __cxa_begin_catch, which is a single pointer, when we need three!
//
// You might think we could get away with only unsetting the C-API information in __cxa_end_catch, but you'd be
// wrong! Firstly, this would prohibit calling C-API functions inside a catch-block. Secondly, __cxa_end_catch is
// always called when leaving a catch block, even if we're leaving it by re-raising the exception. So if we store
// our exception info in curexc_*, and then unset these in __cxa_end_catch, then we'll wipe our exception info
// during unwinding!
return
(
void
*
)
&
pyston
::
exception_ferry
;
// we begin the unwind session here rather than in __cxa_throw
// because we need to return the session's exception storage
// from this method.
return
pyston
::
getPythonUnwindSessionExceptionStorage
(
pyston
::
beginPythonUnwindSession
());
}
// Takes the value that resume() sent us in RAX, and returns a pointer to the exception object actually thrown. In our
// case, these are the same, and should always be &pyston::exception_ferry.
extern
"C"
void
*
__cxa_begin_catch
(
void
*
exc_obj_in
)
noexcept
{
pyston
::
gc
::
endGCUnexpectedRegion
();
assert
(
exc_obj_in
);
pyston
::
us_unwind_resume_catch
.
log
(
pyston
::
per_thread_resume_catch_timer
.
end
());
if
(
VERBOSITY
(
"cxx_unwind"
)
>=
4
)
printf
(
"***** __cxa_begin_catch() *****
\n
"
);
pyston
::
Exc
Data
*
e
=
(
pyston
::
ExcData
*
)
exc_obj_in
;
e
->
check
(
);
return
(
void
*
)
&
e
->
exc
;
pyston
::
Exc
Info
*
e
=
(
pyston
::
ExcInfo
*
)
exc_obj_in
;
checkExcInfo
(
e
);
return
e
;
}
extern
"C"
void
__cxa_end_catch
()
{
...
...
@@ -700,7 +661,6 @@ extern "C" void __cxa_end_catch() {
extern
"C"
std
::
type_info
EXCINFO_TYPE_INFO
;
extern
"C"
void
__cxa_throw
(
void
*
exc_obj
,
std
::
type_info
*
tinfo
,
void
(
*
dtor
)(
void
*
))
{
pyston
::
gc
::
startGCUnexpectedRegion
();
assert
(
!
pyston
::
in_cleanup_code
);
assert
(
exc_obj
);
RELEASE_ASSERT
(
tinfo
==
&
EXCINFO_TYPE_INFO
,
"can't throw a non-ExcInfo value! type info: %p"
,
tinfo
);
...
...
@@ -708,16 +668,19 @@ extern "C" void __cxa_throw(void* exc_obj, std::type_info* tinfo, void (*dtor)(v
if
(
VERBOSITY
(
"cxx_unwind"
)
>=
4
)
printf
(
"***** __cxa_throw() *****
\n
"
);
const
pyston
::
ExcData
*
exc_data
=
(
const
pyston
::
ExcData
*
)
exc_obj
;
exc_data
->
check
();
pyston
::
ExcInfo
*
exc_data
=
(
pyston
::
ExcInfo
*
)
exc_obj
;
checkExcInfo
(
exc_data
);
// let unwinding.cpp know we've started unwinding
pyston
::
throwingException
(
pyston
::
getActivePythonUnwindSession
());
pyston
::
unwind
(
exc_data
);
}
extern
"C"
void
*
__cxa_get_exception_ptr
(
void
*
exc_obj_in
)
noexcept
{
assert
(
exc_obj_in
);
pyston
::
Exc
Data
*
e
=
(
pyston
::
ExcData
*
)
exc_obj_in
;
e
->
check
(
);
return
(
void
*
)
&
e
->
exc
;
pyston
::
Exc
Info
*
e
=
(
pyston
::
ExcInfo
*
)
exc_obj_in
;
checkExcInfo
(
e
);
return
e
;
}
// We deliberately don't implement rethrowing because we can't implement it correctly with our current strategy for
...
...
src/runtime/generator.cpp
View file @
9067a589
...
...
@@ -155,7 +155,7 @@ static void generatorSendInternal(BoxedGenerator* self, Box* v) {
freeGeneratorStack
(
self
);
// don't raise StopIteration exceptions because those are handled specially.
if
(
!
self
->
exception
.
matches
(
StopIteration
))
raiseRaw
(
self
->
exception
)
;
throw
self
->
exception
;
return
;
}
...
...
@@ -193,7 +193,7 @@ Box* generatorSend(Box* s, Box* v) {
self
->
exception
=
ExcInfo
(
nullptr
,
nullptr
,
nullptr
);
if
(
old_exc
.
type
==
NULL
)
raiseExcHelper
(
StopIteration
,
(
const
char
*
)
nullptr
);
raiseRaw
(
old_exc
)
;
throw
old_exc
;
}
return
self
->
returnValue
;
...
...
@@ -272,7 +272,7 @@ extern "C" Box* yield(BoxedGenerator* obj, Box* value) {
if
(
self
->
exception
.
type
)
{
ExcInfo
e
=
self
->
exception
;
self
->
exception
=
ExcInfo
(
NULL
,
NULL
,
NULL
);
raiseRaw
(
e
)
;
throw
e
;
}
return
self
->
returnValue
;
}
...
...
src/runtime/import.cpp
View file @
9067a589
...
...
@@ -49,7 +49,7 @@ Box* createAndRunModule(const std::string& name, const std::string& fn) {
compileAndRunModule
(
ast
,
module
);
}
catch
(
ExcInfo
e
)
{
removeModule
(
name
);
raiseRaw
(
e
)
;
throw
e
;
}
Box
*
r
=
getSysModulesDict
()
->
getOrNull
(
boxString
(
name
));
...
...
@@ -74,7 +74,7 @@ static Box* createAndRunModule(const std::string& name, const std::string& fn, c
compileAndRunModule
(
ast
,
module
);
}
catch
(
ExcInfo
e
)
{
removeModule
(
name
);
raiseRaw
(
e
)
;
throw
e
;
}
Box
*
r
=
getSysModulesDict
()
->
getOrNull
(
boxString
(
name
));
...
...
@@ -223,7 +223,7 @@ SearchResult findModule(const std::string& name, const std::string& full_name, B
BoxedString
*
p
=
static_cast
<
BoxedString
*>
(
_p
);
joined_path
.
clear
();
llvm
::
sys
::
path
::
append
(
joined_path
,
std
::
string
(
p
->
s
()
),
name
);
llvm
::
sys
::
path
::
append
(
joined_path
,
p
->
s
(
),
name
);
std
::
string
dn
(
joined_path
.
str
());
llvm
::
sys
::
path
::
append
(
joined_path
,
"__init__.py"
);
...
...
@@ -406,7 +406,7 @@ static Box* importSub(const std::string& name, const std::string& full_name, Box
RELEASE_ASSERT
(
0
,
"%d"
,
sr
.
type
);
}
catch
(
ExcInfo
e
)
{
removeModule
(
name
);
raiseRaw
(
e
)
;
throw
e
;
}
if
(
parent_module
&&
parent_module
!=
None
)
...
...
@@ -560,7 +560,7 @@ static void ensureFromlist(Box* module, Box* fromlist, std::string& buf, bool re
pathlist
=
getattrInternal
(
module
,
path_str
,
NULL
);
}
catch
(
ExcInfo
e
)
{
if
(
!
e
.
matches
(
AttributeError
))
raiseRaw
(
e
)
;
throw
e
;
}
if
(
pathlist
==
NULL
)
{
...
...
src/runtime/list.cpp
View file @
9067a589
...
...
@@ -727,7 +727,7 @@ void listSort(BoxedList* self, Box* cmp, Box* key, Box* reverse) {
std
::
stable_sort
<
Box
**
,
PyLt
>
(
self
->
elts
->
elts
,
self
->
elts
->
elts
+
self
->
size
,
PyLt
());
}
catch
(
ExcInfo
e
)
{
remove_keys
();
raiseRaw
(
e
)
;
throw
e
;
}
remove_keys
();
...
...
src/runtime/objmodel.h
View file @
9067a589
...
...
@@ -36,7 +36,6 @@ ExcInfo excInfoForRaise(Box*, Box*, Box*);
extern
"C"
void
raise0
()
__attribute__
((
__noreturn__
));
extern
"C"
void
raise3
(
Box
*
,
Box
*
,
Box
*
)
__attribute__
((
__noreturn__
));
void
raiseExc
(
Box
*
exc_obj
)
__attribute__
((
__noreturn__
));
void
raiseRaw
(
const
ExcInfo
&
e
)
__attribute__
((
__noreturn__
));
void
_printStacktrace
();
extern
"C"
Box
*
deopt
(
AST_expr
*
expr
,
Box
*
value
);
...
...
src/runtime/stacktrace.cpp
View file @
9067a589
...
...
@@ -46,37 +46,8 @@ void showBacktrace() {
}
}
void
raiseRaw
(
const
ExcInfo
&
e
)
__attribute__
((
__noreturn__
));
void
raiseRaw
(
const
ExcInfo
&
e
)
{
STAT_TIMER
(
t0
,
"us_timer_raiseraw"
);
// Should set these to None rather than null before getting here:
assert
(
e
.
type
);
assert
(
e
.
value
);
assert
(
e
.
traceback
);
assert
(
gc
::
isValidGCObject
(
e
.
type
));
assert
(
gc
::
isValidGCObject
(
e
.
value
));
assert
(
gc
::
isValidGCObject
(
e
.
traceback
));
#if STAT_EXCEPTIONS
static
StatCounter
num_exceptions
(
"num_exceptions"
);
num_exceptions
.
log
();
std
::
string
stat_name
;
if
(
PyType_Check
(
e
.
type
))
stat_name
=
"num_exceptions_"
+
std
::
string
(
static_cast
<
BoxedClass
*>
(
e
.
type
)
->
tp_name
);
else
stat_name
=
"num_exceptions_"
+
std
::
string
(
e
.
value
->
cls
->
tp_name
);
Stats
::
log
(
Stats
::
getStatCounter
(
stat_name
));
#if STAT_EXCEPTIONS_LOCATION
logByCurrentPythonLine
(
stat_name
);
#endif
#endif
throw
e
;
}
void
raiseExc
(
Box
*
exc_obj
)
{
raiseRaw
(
ExcInfo
(
exc_obj
->
cls
,
exc_obj
,
getTraceback
()
));
throw
ExcInfo
(
exc_obj
->
cls
,
exc_obj
,
new
BoxedTraceback
(
));
}
// Have a special helper function for syntax errors, since we want to include the location
...
...
@@ -84,10 +55,8 @@ void raiseExc(Box* exc_obj) {
void
raiseSyntaxError
(
const
char
*
msg
,
int
lineno
,
int
col_offset
,
llvm
::
StringRef
file
,
llvm
::
StringRef
func
)
{
Box
*
exc
=
runtimeCall
(
SyntaxError
,
ArgPassSpec
(
1
),
boxString
(
msg
),
NULL
,
NULL
,
NULL
,
NULL
);
auto
tb
=
getTraceback
();
std
::
vector
<
const
LineInfo
*>
entries
=
tb
->
lines
;
entries
.
push_back
(
new
LineInfo
(
lineno
,
col_offset
,
file
,
func
));
raiseRaw
(
ExcInfo
(
exc
->
cls
,
exc
,
new
BoxedTraceback
(
std
::
move
(
entries
))));
auto
tb
=
new
BoxedTraceback
(
LineInfo
(
lineno
,
col_offset
,
file
,
func
),
None
);
throw
ExcInfo
(
exc
->
cls
,
exc
,
tb
);
}
void
raiseSyntaxErrorHelper
(
llvm
::
StringRef
file
,
llvm
::
StringRef
func
,
AST
*
node_at
,
const
char
*
msg
,
...)
{
...
...
@@ -205,11 +174,13 @@ extern "C" void raise0() {
if
(
exc_info
->
type
==
None
)
raiseExcHelper
(
TypeError
,
"exceptions must be old-style classes or derived from BaseException, not NoneType"
);
raiseRaw
(
*
exc_info
);
exc_info
->
reraise
=
true
;
throw
*
exc_info
;
}
#ifndef NDEBUG
ExcInfo
::
ExcInfo
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
)
:
type
(
type
),
value
(
value
),
traceback
(
traceback
)
{
ExcInfo
::
ExcInfo
(
Box
*
type
,
Box
*
value
,
Box
*
traceback
)
:
type
(
type
),
value
(
value
),
traceback
(
traceback
),
reraise
(
false
)
{
}
#endif
...
...
@@ -229,8 +200,12 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
assert
(
type
&&
value
&&
tb
);
// use None for default behavior, not nullptr
// TODO switch this to PyErr_Normalize
if
(
tb
==
None
)
tb
=
getTraceback
();
if
(
tb
==
None
)
{
tb
=
NULL
;
}
else
if
(
tb
!=
NULL
&&
!
PyTraceBack_Check
(
tb
))
{
raiseExcHelper
(
TypeError
,
"raise: arg 3 must be a traceback or None"
);
}
/* Next, repeatedly, replace a tuple exception with its first item */
while
(
PyTuple_Check
(
type
)
&&
PyTuple_Size
(
type
)
>
0
)
{
...
...
@@ -242,6 +217,7 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
if
(
PyExceptionClass_Check
(
type
))
{
PyErr_NormalizeException
(
&
type
,
&
value
,
&
tb
);
if
(
!
PyExceptionInstance_Check
(
value
))
{
raiseExcHelper
(
TypeError
,
"calling %s() should have returned an instance of "
"BaseException, not '%s'"
,
...
...
@@ -268,11 +244,19 @@ ExcInfo excInfoForRaise(Box* type, Box* value, Box* tb) {
assert
(
PyExceptionClass_Check
(
type
));
if
(
tb
==
NULL
)
{
tb
=
new
BoxedTraceback
();
}
return
ExcInfo
(
type
,
value
,
tb
);
}
extern
"C"
void
raise3
(
Box
*
arg0
,
Box
*
arg1
,
Box
*
arg2
)
{
raiseRaw
(
excInfoForRaise
(
arg0
,
arg1
,
arg2
));
bool
reraise
=
arg2
!=
NULL
&&
arg2
!=
None
;
auto
exc_info
=
excInfoForRaise
(
arg0
,
arg1
,
arg2
);
exc_info
.
reraise
=
reraise
;
throw
exc_info
;
}
void
raiseExcHelper
(
BoxedClass
*
cls
,
Box
*
arg
)
{
...
...
src/runtime/super.cpp
View file @
9067a589
...
...
@@ -128,7 +128,7 @@ Box* superGetattribute(Box* _s, Box* _attr) {
}
}
Box
*
r
=
typeLookup
(
s
->
cls
,
std
::
string
(
attr
->
s
()
),
NULL
);
Box
*
r
=
typeLookup
(
s
->
cls
,
attr
->
s
(
),
NULL
);
// TODO implement this
RELEASE_ASSERT
(
r
,
"should call the equivalent of objectGetattr here"
);
return
processDescriptor
(
r
,
s
,
s
->
cls
);
...
...
src/runtime/traceback.cpp
View file @
9067a589
...
...
@@ -24,6 +24,7 @@
#include "core/stats.h"
#include "core/types.h"
#include "gc/collector.h"
#include "runtime/list.h"
#include "runtime/objmodel.h"
#include "runtime/types.h"
#include "runtime/util.h"
...
...
@@ -40,6 +41,8 @@ void BoxedTraceback::gcHandler(GCVisitor* v, Box* b) {
if
(
self
->
py_lines
)
v
->
visit
(
self
->
py_lines
);
if
(
self
->
tb_next
)
v
->
visit
(
self
->
tb_next
);
boxGCHandler
(
v
,
b
);
}
...
...
@@ -53,16 +56,17 @@ void printTraceback(Box* b) {
fprintf
(
stderr
,
"Traceback (most recent call last):
\n
"
);
for
(
auto
line
:
tb
->
lines
)
{
fprintf
(
stderr
,
" File
\"
%s
\"
, line %d, in %s:
\n
"
,
line
->
file
.
c_str
(),
line
->
line
,
line
->
func
.
c_str
());
for
(;
tb
&&
tb
!=
None
;
tb
=
static_cast
<
BoxedTraceback
*>
(
tb
->
tb_next
))
{
auto
&
line
=
tb
->
line
;
fprintf
(
stderr
,
" File
\"
%s
\"
, line %d, in %s:
\n
"
,
line
.
file
.
c_str
(),
line
.
line
,
line
.
func
.
c_str
());
if
(
line
->
line
<
0
)
if
(
line
.
line
<
0
)
continue
;
FILE
*
f
=
fopen
(
line
->
file
.
c_str
(),
"r"
);
FILE
*
f
=
fopen
(
line
.
file
.
c_str
(),
"r"
);
if
(
f
)
{
assert
(
line
->
line
<
10000000
&&
"Refusing to try to seek that many lines forward"
);
for
(
int
i
=
1
;
i
<
line
->
line
;
i
++
)
{
assert
(
line
.
line
<
10000000
&&
"Refusing to try to seek that many lines forward"
);
for
(
int
i
=
1
;
i
<
line
.
line
;
i
++
)
{
char
*
buf
=
NULL
;
size_t
size
;
size_t
r
=
getline
(
&
buf
,
&
size
,
f
);
...
...
@@ -97,17 +101,23 @@ Box* BoxedTraceback::getLines(Box* b) {
if
(
!
tb
->
py_lines
)
{
BoxedList
*
lines
=
new
BoxedList
();
lines
->
ensure
(
tb
->
lines
.
size
());
for
(
auto
line
:
tb
->
lines
)
{
auto
l
=
BoxedTuple
::
create
({
boxString
(
line
->
file
),
boxString
(
line
->
func
),
boxInt
(
line
->
line
)
});
for
(
BoxedTraceback
*
wtb
=
tb
;
wtb
&&
wtb
!=
None
;
wtb
=
static_cast
<
BoxedTraceback
*>
(
wtb
->
tb_next
))
{
if
(
wtb
->
has_line
)
{
auto
&
line
=
wtb
->
line
;
auto
l
=
BoxedTuple
::
create
({
boxString
(
line
.
file
),
boxString
(
line
.
func
),
boxInt
(
line
.
line
)
});
listAppendInternal
(
lines
,
l
);
}
}
tb
->
py_lines
=
lines
;
}
return
tb
->
py_lines
;
}
void
BoxedTraceback
::
here
(
LineInfo
lineInfo
,
Box
**
tb
)
{
*
tb
=
new
BoxedTraceback
(
lineInfo
,
*
tb
);
}
void
setupTraceback
()
{
traceback_cls
=
BoxedHeapClass
::
create
(
type_cls
,
object_cls
,
BoxedTraceback
::
gcHandler
,
0
,
0
,
sizeof
(
BoxedTraceback
),
false
,
"traceback"
);
...
...
src/runtime/traceback.h
View file @
9067a589
...
...
@@ -27,19 +27,22 @@ class GCVisitor;
extern
"C"
BoxedClass
*
traceback_cls
;
class
BoxedTraceback
:
public
Box
{
public:
std
::
vector
<
const
LineInfo
*>
lines
;
Box
*
tb_next
;
bool
has_line
;
LineInfo
line
;
Box
*
py_lines
;
BoxedTraceback
(
std
::
vector
<
const
LineInfo
*>
lines
)
:
lines
(
std
::
move
(
lines
)
),
py_lines
(
NULL
)
{}
BoxedTraceback
()
:
py_lines
(
NULL
)
{}
BoxedTraceback
(
LineInfo
line
,
Box
*
tb_next
)
:
tb_next
(
tb_next
),
has_line
(
true
),
line
(
line
),
py_lines
(
NULL
)
{}
BoxedTraceback
()
:
tb_next
(
None
),
has_line
(
false
),
line
(
-
1
,
-
1
,
""
,
""
),
py_lines
(
NULL
)
{}
DEFAULT_CLASS
(
traceback_cls
);
void
addLine
(
const
LineInfo
*
line
);
static
Box
*
getLines
(
Box
*
b
);
static
void
gcHandler
(
gc
::
GCVisitor
*
v
,
Box
*
b
);
// somewhat equivalent to PyTraceBack_Here
static
void
here
(
LineInfo
lineInfo
,
Box
**
tb
);
};
void
printTraceback
(
Box
*
b
);
...
...
src/runtime/types.cpp
View file @
9067a589
...
...
@@ -2703,6 +2703,7 @@ void setupRuntime() {
closure_cls
->
freeze
();
setupUnwinding
();
setupInterpreter
();
setupCAPI
();
...
...
test/tests/incremental_tb.py
0 → 100644
View file @
9067a589
import
traceback
import
sys
def
f
():
a
,
b
,
c
=
sys
.
exc_info
()
raise
a
,
b
,
c
et0
,
ev0
,
tb0
=
None
,
None
,
None
try
:
1
/
0
except
:
pass
for
i
in
xrange
(
10
):
try
:
f
()
except
:
et0
,
ev0
,
tb0
=
sys
.
exc_info
()
print
"******** 0"
,
''
.
join
(
traceback
.
format_exception
(
et0
,
ev0
,
tb0
))
et1
,
ev1
,
tb1
=
None
,
None
,
None
et2
,
ev2
,
tb2
=
None
,
None
,
None
def
f1
():
raise
def
f2
():
f1
()
def
f21
():
raise
Exception
()
def
f3
():
try
:
f21
()
except
:
global
et1
,
tv1
,
tb1
et1
,
tv1
,
tb1
=
sys
.
exc_info
()
f2
()
try
:
f3
()
except
:
et2
,
tv2
,
tb2
=
sys
.
exc_info
()
print
"******** 1"
,
''
.
join
(
traceback
.
format_exception
(
et1
,
ev1
,
tb1
))
print
"******** 2"
,
''
.
join
(
traceback
.
format_exception
(
et2
,
ev2
,
tb2
))
print
et1
is
et2
print
ev1
is
ev2
print
tb1
is
tb2
test/tests/traceback_limits.py
View file @
9067a589
# expected: fail
# - we don't stop tracebacks at the catching except handler. this is hard do the way it gets added to
# (ie a bare "raise" statement will add more traceback entries to the traceback it raises)
import
sys
import
traceback
...
...
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