Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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/. */
7 #include "jscrashreport.h"
9 #include <time.h>
11 #include "jsapi.h"
12 #include "jscrashformat.h"
13 #include "jsutil.h"
15 using namespace js;
16 using namespace js::crash;
18 #if defined(XP_WIN)
20 static const int stack_snapshot_max_size = 32768;
22 #include <windows.h>
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;
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;
39 if (p + len > uint64_t(info.BaseAddress) + info.RegionSize)
40 len = uint64_t(info.BaseAddress) + info.RegionSize - p;
42 if (len > size)
43 len = size;
45 *stack = p;
46 *stack_len = len;
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
78 js_memcpy(buffer, (void *)p, len);
80 return true;
81 }
83 #elif 0
85 #include <sys/mman.h>
86 #include <ucontext.h>
87 #include <unistd.h>
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);
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 }
108 if (len > size)
109 len = size;
111 *stack = p;
112 *stack_len = len;
114 /* Get the register state. */
115 ucontext_t context;
116 if (getcontext(&context) != 0)
117 return false;
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
131 js_memcpy(buffer, (void *)p, len);
133 return true;
134 }
136 #else
138 static bool
139 GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size)
140 {
141 return false;
142 }
144 #endif
146 namespace js {
147 namespace crash {
149 class Stack : private CrashStack
150 {
151 public:
152 Stack(uint64_t id);
154 bool snapshot();
155 };
157 Stack::Stack(uint64_t id)
158 : CrashStack(id)
159 {
160 }
162 bool
163 Stack::snapshot()
164 {
165 snaptime = time(nullptr);
166 return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack));
167 }
169 class Ring : private CrashRing
170 {
171 public:
172 Ring(uint64_t id);
174 void push(uint64_t tag, void *data, size_t size);
176 private:
177 size_t bufferSize() { return crash_buffer_size; }
178 void copyBytes(void *data, size_t size);
179 };
181 Ring::Ring(uint64_t id)
182 : CrashRing(id)
183 {
184 }
186 void
187 Ring::push(uint64_t tag, void *data, size_t size)
188 {
189 uint64_t t = time(nullptr);
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 }
198 void
199 Ring::copyBytes(void *data, size_t size)
200 {
201 if (size >= bufferSize())
202 size = bufferSize();
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 }
216 } /* namespace crash */
217 } /* namespace js */
219 #ifdef JS_CRASH_DIAGNOSTICS
220 static bool gInitialized;
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
227 void
228 js::crash::SnapshotGCStack()
229 {
230 #ifdef JS_CRASH_DIAGNOSTICS
231 if (gInitialized)
232 gGCStack.snapshot();
233 #endif
234 }
236 void
237 js::crash::SnapshotErrorStack()
238 {
239 #ifdef JS_CRASH_DIAGNOSTICS
240 if (gInitialized)
241 gErrorStack.snapshot();
242 #endif
243 }
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 }
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 }