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
docs / inlined_stack_traces.md [blame]
# Dealing with stack traces that involve inlined code
[Rendered](https://chromium.googlesource.com/chromium/src/+/main/docs/inlined_stack_traces.md)
Sometimes we get crashes with stack traces that are hard to use.
They may contain the file and line-number for the inlined method
but only the method name for the caller.
There may also be layers of the stack
that are missing completely.
E.g.
```
0xd3c90a78(libmonochrome.so -vector.h:1047 ) blink::EventDispatcher::Dispatch()
0xde86db5a
0xd3c8fcdd(libmonochrome.so -event_dispatcher.cc:59 )blink::EventDispatcher::DispatchEvent(blink::Node&, blink::Event*)
0xd3e27bb9(libmonochrome.so -container_node.cc:1279 )blink::ContainerNode::DidInsertNodeVector(blink::HeapVector<blink::Member<blink::Node>, 11u> const&, blink::Node*, blink::HeapVector<blink::Member<blink::Node>, 11u> const&)
0xd3e27485(libmonochrome.so -container_node.cc:851 )blink::ContainerNode::AppendChild(blink::Node*, blink::ExceptionState&)
0xd3eedf4f(libmonochrome.so -v8_node.cc:534 )blink::V8Node::insertBeforeMethodCallbackForMainWorld(v8::FunctionCallbackInfo<v8::Value> const&)
0xd3ddfee3(libmonochrome.so -api-arguments-inl.h:95 )v8::internal::Builtin_Impl_HandleApiCall(v8::internal::BuiltinArguments, v8::internal::Isolate*)
```
This stack trace ends with `blink::EventDispatcher::Dispatch()`
but references `vector.h:1047`.
There is not enough information in this trace to know what code inside `Dispatch()` triggered the crash.
## Getting the code
First of all, we need the correct source code.
This crash comes from Chrome version `69.0.3497.91`.
See of *Syncing and building a release tag* in [this doc](https://www.chromium.org/developers/how-tos/get-the-code/working-with-release-branches#TOC-Syncing-and-building-a-release-tag)
for how to check out code at a specific tag.
Now we can see `vector.h:1047` is actually the `CHECK_LT` in
```
T& at(size_t i) {
CHECK_LT(i, size());
return Base::Buffer()[i];
}
```
To get further, we need to look at the compiled code in the binary that produced the stack trace.
## Interpreting the addresses
The addresses that appear in the stack trace are memory addresses.
We need to translate them into offsets into the binary file.
A crash report should come with a memory map
that tells you the address at which every library and binary has been loaded.
So, subtracting this from the address in the trace
give the correct address for looking at the code.
In this example, the `libmonochrome.so` was loaded at `0xd24cd000`
so the code we are interested in is at `0x17c3a78`.
## Dumping the compiled code
[This doc](https://chromium.googlesource.com/chromium/src/+/main/docs/disassemble_code.md) describes how to dump the assembler code for a method from a binary.
In this example, it's a crash from an Android Chrome binary.
Only Googlers have access to the unstripped binary files needed for this example
but the steps are generic and work with any version of Chromium
(or indeed other binaries).
In this case, we can dump the entire `Dispatch()` method
and find `0x17c3a78`.
This looks like
```
17c3a6c: be00 bkpt 0x0000
17c3a6e: de05 udf #5
17c3a70: be00 bkpt 0x0000
17c3a72: de05 udf #5
17c3a74: be00 bkpt 0x0000
17c3a76: de05 udf #5
17c3a78: be00 bkpt 0x0000
17c3a7a: de05 udf #5
```
You don't need to be able to read ARM assembler to make some sense of this.
We're looking for a `CHECK_LT`
and we've found [`bkpt`](http://www.keil.com/support/man/docs/armasm/armasm_dom1361289865326.htm)
which makes sense.
It doesn't look like you can just execute your way to `0x17c3a78`,
so presumably we jump there.
Searching for that address elsewhere we find only one reference to it
as the target of a branch instruction.
It's the last line below:
```
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/core/dom/events/event_dispatcher.cc:190
17c38e0: f010 0f30 tst.w r0, #48 ; 0x30
17c38e4: f47f af47 bne.w 17c3776 <blink::EventDispatcher::Dispatch()+0xbe>
_ZNK5blink10MemberBaseINS_9EventPathELNS_28TracenessMemberConfigurationE0EEdeEv():
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/platform/heap/member.h:91
17c38e8: 6a48 ldr r0, [r1, #36] ; 0x24
17c38ea: f04f 0801 mov.w r8, #1
17c38ee: f04f 0a00 mov.w sl, #0
_ZNK3WTF6VectorIN5blink16NodeEventContextELj0ENS1_13HeapAllocatorEE4sizeEv():
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/platform/wtf/vector.h:1035
17c38f2: 6885 ldr r5, [r0, #8]
17c38f4: e022 b.n 17c393c <blink::EventDispatcher::Dispatch()+0x284>
_ZNK5blink10MemberBaseINS_9EventPathELNS_28TracenessMemberConfigurationE0EEdeEv():
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/platform/heap/member.h:91
17c38f6: 6a48 ldr r0, [r1, #36] ; 0x24
_ZNK3WTF6VectorIN5blink16NodeEventContextELj0ENS1_13HeapAllocatorEE4sizeEv():
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/platform/wtf/vector.h:1035
17c38f8: 6882 ldr r2, [r0, #8]
_ZN3WTF6VectorIN5blink16NodeEventContextELj0ENS1_13HeapAllocatorEE2atEj():
/b/build/slave/official-arm/build/src/out/Release/../../third_party/blink/renderer/platform/wtf/vector.h:1047
17c38fa: 4590 cmp r8, r2
17c38fc: f080 80bc bcs.w 17c3a78 <blink::EventDispatcher::Dispatch()+0x3c0>
```
Before it you can see that we have code inlined from `vector.h:1047`
and looking further back up the code,
this appears to be a lot of inlined code from `vector.h` and `member.h`
all the way back up until you find `event_dispatcher.cc:190` which is
```
if (DispatchEventAtTarget() == kContinueDispatching)
DispatchEventAtBubbling();
```
This calls
```
inline EventDispatchContinuation EventDispatcher::DispatchEventAtTarget() {
event_->SetEventPhase(Event::kAtTarget);
event_->GetEventPath()[0].HandleLocalEvents(*event_);
return event_->PropagationStopped() ? kDoneDispatching : kContinueDispatching;
}
```
which is marked as `inline`.
This explains why it's not even mentioned in the stack trace
(it's file and line info does not appear at all)
and why there is so much code between the `event_dispatcher.cc:190`
and the crash.
So the real stack trace is
```
vector.h:1047 WTF::Vector::at()
event_dispatcher.cc:241 blink::EventDispatcher::DispatchEventAtTarget
event_dispatcher.cc:190 blink::EventDispatcher::Dispatch()
```
It's possible that optimization can lead to more complex code
e.g. having multiple routes to the same piece of code.
In this case, things are pretty clear
and there are no jumps from further up the method
that land into the code we are looking at.