forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
/
stacktraces.jl
130 lines (111 loc) · 4.07 KB
/
stacktraces.jl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# This file is a part of Julia. License is MIT: https://julialang.org/license
# Tests for /base/stacktraces.jl
let
@noinline child() = stacktrace()
@noinline parent() = child()
@noinline grandparent() = parent()
line_numbers = @__LINE__ - [3, 2, 1]
stack = grandparent()
# Basic tests.
@assert length(stack) >= 3 "Compiler has unexpectedly inlined functions"
@test [:child, :parent, :grandparent] == [f.func for f in stack[1:3]]
for (line, frame) in zip(line_numbers, stack[1:3])
@test [Symbol(@__FILE__), line] == [frame.file, frame.line]
end
@test [false, false, false] == [f.from_c for f in stack[1:3]]
# Test remove_frames!
stack = StackTraces.remove_frames!(grandparent(), :parent)
@test stack[1] == StackFrame(:grandparent, @__FILE__, line_numbers[3])
stack = StackTraces.remove_frames!(grandparent(), [:child, :something_nonexistent])
@test stack[1:2] == [
StackFrame(:parent, @__FILE__, line_numbers[2]),
StackFrame(:grandparent, @__FILE__, line_numbers[3])
]
b = PipeBuffer()
frame = stack[1]
serialize(b, frame)
frame2 = deserialize(b)
@test frame !== frame2
@test frame == frame2
@test !isnull(frame.linfo)
@test isnull(frame2.linfo)
end
let
# Test from_c
default, with_c, without_c = stacktrace(), stacktrace(true), stacktrace(false)
@test default == without_c
@test length(with_c) > length(without_c)
@test !isempty(filter(frame -> frame.from_c, with_c))
@test isempty(filter(frame -> frame.from_c, without_c))
end
@test StackTraces.lookup(C_NULL) == [StackTraces.UNKNOWN]
let ct = current_task()
# After a task switch, there should be nothing in catch_backtrace
yieldto(@task yieldto(ct))
@test catch_backtrace() == StackFrame[]
@noinline bad_function() = throw(UndefVarError(:nonexistent))
@noinline function try_stacktrace()
try
bad_function()
catch
return stacktrace()
end
end
@noinline function try_catch()
try
bad_function()
catch
return catch_stacktrace()
end
end
line_numbers = @__LINE__ .- [15, 10, 5]
# Test try...catch with stacktrace
@test try_stacktrace()[1] == StackFrame(:try_stacktrace, @__FILE__, line_numbers[2])
# Test try...catch with catch_stacktrace
@test try_catch()[1:2] == [
StackFrame(:bad_function, @__FILE__, line_numbers[1]),
StackFrame(:try_catch, @__FILE__, line_numbers[3])
]
end
module inlined_test
using Base.Test
@inline g(x) = (y = throw("a"); y) # the inliner does not insert the proper markers when inlining a single expression
@inline h(x) = (y = g(x); y) # this test could be extended to check for that if we switch to linear representation
f(x) = (y = h(x); y)
trace = (try; f(3); catch; catch_stacktrace(); end)[1:3]
can_inline = Bool(Base.JLOptions().can_inline)
for (frame, func, inlined) in zip(trace, [g,h,f], (can_inline, can_inline, false))
@test frame.func === typeof(func).name.mt.name
#@test get(frame.linfo).def === which(func, (Any,)).func
#@test get(frame.linfo).specTypes === Tuple{typeof(func), Int}
# line
@test frame.file === Symbol(@__FILE__)
@test !frame.from_c
@test frame.inlined === inlined
end
end
let li = expand(quote let x = 1 end end).args[1]::LambdaInfo,
sf = StackFrame(:a, :b, 3, li, false, false, 0),
repr = string(sf)
@test repr == " in Toplevel LambdaInfo thunk at b:3"
end
let li = typeof(getfield).name.mt.cache.func::LambdaInfo,
sf = StackFrame(:a, :b, 3, li, false, false, 0),
repr = string(sf)
@test repr == " in getfield(...) at b:3"
end
let ctestptr = cglobal((:ctest, "libccalltest")),
ctest = StackTraces.lookup(ctestptr + 1)
@test length(ctest) == 1
@test ctest[1].func === :ctest
@test isnull(ctest[1].linfo)
@test ctest[1].from_c
@test ctest[1].pointer === UInt64(ctestptr)
end
# #19655
let
# not in a `catch`, so should return an empty StackTrace
st = stacktrace(empty!(backtrace()))
@test isempty(st)
@test isa(st, StackTrace)
end