This repository was archived by the owner on Apr 12, 2019. It is now read-only.
forked from JuliaLang/julia
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstacktraces.jl
159 lines (135 loc) · 4.92 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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# This file is a part of Julia. License is MIT: https://julialang.org/license
# Tests for /base/stacktraces.jl
using Serialization, Base.StackTraces
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 frame.linfo !== nothing
@test frame2.linfo === nothing
end
# Test from_c
let (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 stacktrace(catch_backtrace())
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_backtrace
@test try_catch()[1:2] == [
StackFrame(:bad_function, @__FILE__, line_numbers[1]),
StackFrame(:try_catch, @__FILE__, line_numbers[3])
]
end
module inlined_test
using 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; stacktrace(catch_backtrace()); 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 src = Meta.lower(Main, quote let x = 1 end end).args[1]::Core.CodeInfo,
li = ccall(:jl_new_method_instance_uninit, Ref{Core.MethodInstance}, ()),
sf
li.inferred = src
li.specTypes = Tuple{}
li.def = @__MODULE__
sf = StackFrame(:a, :b, 3, li, false, false, 0)
repr = string(sf)
@test repr == "Toplevel MethodInstance thunk at b:3"
end
let li = typeof(fieldtype).name.mt.cache.func::Core.MethodInstance,
sf = StackFrame(:a, :b, 3, li, false, false, 0),
repr = string(sf)
@test repr == "fieldtype(...) at b:3"
end
let ctestptr = cglobal((:ctest, "libccalltest")),
ctest = StackTraces.lookup(ctestptr + 1)
@test length(ctest) == 1
@test ctest[1].func === :ctest
@test ctest[1].linfo === nothing
@test ctest[1].from_c
@test ctest[1].pointer === UInt64(ctestptr)
end
# issue #19655
let st = stacktrace(empty!(backtrace()))
# not in a `catch`, so should return an empty StackTrace
@test isempty(st)
@test isa(st, StackTrace)
end
module StackTracesTestMod
unfiltered_stacktrace() = stacktrace()
filtered_stacktrace() = StackTraces.remove_frames!(stacktrace(), StackTracesTestMod)
end
# Test that `removes_frames!` can correctly remove frames from within the module
trace = StackTracesTestMod.unfiltered_stacktrace()
@test occursin("unfiltered_stacktrace", string(trace))
trace = StackTracesTestMod.filtered_stacktrace()
@test !occursin("filtered_stacktrace", string(trace))
let bt, topline = @__LINE__
try
let x = 1
y = 2x
z = 2z-1
end
catch
bt = stacktrace(catch_backtrace())
end
@test bt[1].line == topline+4
end