|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #include "jscrashreport.h" |
|
8 |
|
9 #include <time.h> |
|
10 |
|
11 #include "jsapi.h" |
|
12 #include "jscrashformat.h" |
|
13 #include "jsutil.h" |
|
14 |
|
15 using namespace js; |
|
16 using namespace js::crash; |
|
17 |
|
18 #if defined(XP_WIN) |
|
19 |
|
20 static const int stack_snapshot_max_size = 32768; |
|
21 |
|
22 #include <windows.h> |
|
23 |
|
24 static bool |
|
25 GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) |
|
26 { |
|
27 /* Try to figure out how big the stack is. */ |
|
28 char dummy; |
|
29 MEMORY_BASIC_INFORMATION info; |
|
30 if (VirtualQuery(reinterpret_cast<LPCVOID>(&dummy), &info, sizeof(info)) == 0) |
|
31 return false; |
|
32 if (info.State != MEM_COMMIT) |
|
33 return false; |
|
34 |
|
35 /* 256 is a fudge factor to account for the rest of GetStack's frame. */ |
|
36 uint64_t p = uint64_t(&dummy) - 256; |
|
37 uint64_t len = stack_snapshot_max_size; |
|
38 |
|
39 if (p + len > uint64_t(info.BaseAddress) + info.RegionSize) |
|
40 len = uint64_t(info.BaseAddress) + info.RegionSize - p; |
|
41 |
|
42 if (len > size) |
|
43 len = size; |
|
44 |
|
45 *stack = p; |
|
46 *stack_len = len; |
|
47 |
|
48 /* Get the register state. */ |
|
49 #if defined(_MSC_VER) && defined(_M_IX86) |
|
50 /* ASM version for win2k that doesn't support RtlCaptureContext */ |
|
51 uint32_t vip, vsp, vbp; |
|
52 __asm { |
|
53 MyLabel: |
|
54 mov [vbp], ebp; |
|
55 mov [vsp], esp; |
|
56 mov eax, [MyLabel]; |
|
57 mov [vip], eax; |
|
58 } |
|
59 regs->ip = vip; |
|
60 regs->sp = vsp; |
|
61 regs->bp = vbp; |
|
62 #else |
|
63 CONTEXT context; |
|
64 RtlCaptureContext(&context); |
|
65 #if defined(_M_IX86) |
|
66 regs->ip = context.Eip; |
|
67 regs->sp = context.Esp; |
|
68 regs->bp = context.Ebp; |
|
69 #elif defined(_M_X64) |
|
70 regs->ip = context.Rip; |
|
71 regs->sp = context.Rsp; |
|
72 regs->bp = context.Rbp; |
|
73 #else |
|
74 #error unknown cpu architecture |
|
75 #endif |
|
76 #endif |
|
77 |
|
78 js_memcpy(buffer, (void *)p, len); |
|
79 |
|
80 return true; |
|
81 } |
|
82 |
|
83 #elif 0 |
|
84 |
|
85 #include <sys/mman.h> |
|
86 #include <ucontext.h> |
|
87 #include <unistd.h> |
|
88 |
|
89 static bool |
|
90 GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) |
|
91 { |
|
92 /* 256 is a fudge factor to account for the rest of GetStack's frame. */ |
|
93 char dummy; |
|
94 uint64_t p = uint64_t(&dummy) - 256; |
|
95 uint64_t pgsz = getpagesize(); |
|
96 uint64_t len = stack_snapshot_max_size; |
|
97 p &= ~(pgsz - 1); |
|
98 |
|
99 /* Try to figure out how big the stack is. */ |
|
100 while (len > 0) { |
|
101 if (mlock((const void *)p, len) == 0) { |
|
102 munlock((const void *)p, len); |
|
103 break; |
|
104 } |
|
105 len -= pgsz; |
|
106 } |
|
107 |
|
108 if (len > size) |
|
109 len = size; |
|
110 |
|
111 *stack = p; |
|
112 *stack_len = len; |
|
113 |
|
114 /* Get the register state. */ |
|
115 ucontext_t context; |
|
116 if (getcontext(&context) != 0) |
|
117 return false; |
|
118 |
|
119 #if defined(__x86_64__) |
|
120 regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_RSP]; |
|
121 regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_RBP]; |
|
122 regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_RIP]; |
|
123 #elif defined(__i386__) |
|
124 regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_ESP]; |
|
125 regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_EBP]; |
|
126 regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_EIP]; |
|
127 #else |
|
128 #error unknown cpu architecture |
|
129 #endif |
|
130 |
|
131 js_memcpy(buffer, (void *)p, len); |
|
132 |
|
133 return true; |
|
134 } |
|
135 |
|
136 #else |
|
137 |
|
138 static bool |
|
139 GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) |
|
140 { |
|
141 return false; |
|
142 } |
|
143 |
|
144 #endif |
|
145 |
|
146 namespace js { |
|
147 namespace crash { |
|
148 |
|
149 class Stack : private CrashStack |
|
150 { |
|
151 public: |
|
152 Stack(uint64_t id); |
|
153 |
|
154 bool snapshot(); |
|
155 }; |
|
156 |
|
157 Stack::Stack(uint64_t id) |
|
158 : CrashStack(id) |
|
159 { |
|
160 } |
|
161 |
|
162 bool |
|
163 Stack::snapshot() |
|
164 { |
|
165 snaptime = time(nullptr); |
|
166 return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack)); |
|
167 } |
|
168 |
|
169 class Ring : private CrashRing |
|
170 { |
|
171 public: |
|
172 Ring(uint64_t id); |
|
173 |
|
174 void push(uint64_t tag, void *data, size_t size); |
|
175 |
|
176 private: |
|
177 size_t bufferSize() { return crash_buffer_size; } |
|
178 void copyBytes(void *data, size_t size); |
|
179 }; |
|
180 |
|
181 Ring::Ring(uint64_t id) |
|
182 : CrashRing(id) |
|
183 { |
|
184 } |
|
185 |
|
186 void |
|
187 Ring::push(uint64_t tag, void *data, size_t size) |
|
188 { |
|
189 uint64_t t = time(nullptr); |
|
190 |
|
191 copyBytes(&tag, sizeof(uint64_t)); |
|
192 copyBytes(&t, sizeof(uint64_t)); |
|
193 copyBytes(data, size); |
|
194 uint64_t mysize = size; |
|
195 copyBytes(&mysize, sizeof(uint64_t)); |
|
196 } |
|
197 |
|
198 void |
|
199 Ring::copyBytes(void *data, size_t size) |
|
200 { |
|
201 if (size >= bufferSize()) |
|
202 size = bufferSize(); |
|
203 |
|
204 if (offset + size > bufferSize()) { |
|
205 size_t first = bufferSize() - offset; |
|
206 size_t second = size - first; |
|
207 js_memcpy(&buffer[offset], data, first); |
|
208 js_memcpy(buffer, (char *)data + first, second); |
|
209 offset = second; |
|
210 } else { |
|
211 js_memcpy(&buffer[offset], data, size); |
|
212 offset += size; |
|
213 } |
|
214 } |
|
215 |
|
216 } /* namespace crash */ |
|
217 } /* namespace js */ |
|
218 |
|
219 #ifdef JS_CRASH_DIAGNOSTICS |
|
220 static bool gInitialized; |
|
221 |
|
222 static Stack gGCStack(JS_CRASH_STACK_GC); |
|
223 static Stack gErrorStack(JS_CRASH_STACK_ERROR); |
|
224 static Ring gRingBuffer(JS_CRASH_RING); |
|
225 #endif |
|
226 |
|
227 void |
|
228 js::crash::SnapshotGCStack() |
|
229 { |
|
230 #ifdef JS_CRASH_DIAGNOSTICS |
|
231 if (gInitialized) |
|
232 gGCStack.snapshot(); |
|
233 #endif |
|
234 } |
|
235 |
|
236 void |
|
237 js::crash::SnapshotErrorStack() |
|
238 { |
|
239 #ifdef JS_CRASH_DIAGNOSTICS |
|
240 if (gInitialized) |
|
241 gErrorStack.snapshot(); |
|
242 #endif |
|
243 } |
|
244 |
|
245 void |
|
246 js::crash::SaveCrashData(uint64_t tag, void *ptr, size_t size) |
|
247 { |
|
248 #ifdef JS_CRASH_DIAGNOSTICS |
|
249 if (gInitialized) |
|
250 gRingBuffer.push(tag, ptr, size); |
|
251 #endif |
|
252 } |
|
253 |
|
254 JS_PUBLIC_API(void) |
|
255 JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) |
|
256 { |
|
257 #ifdef JS_CRASH_DIAGNOSTICS |
|
258 if (!gInitialized) { |
|
259 gInitialized = true; |
|
260 (*callback)(&gGCStack, sizeof(gGCStack)); |
|
261 (*callback)(&gErrorStack, sizeof(gErrorStack)); |
|
262 (*callback)(&gRingBuffer, sizeof(gRingBuffer)); |
|
263 } |
|
264 #endif |
|
265 } |
|
266 |