michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jscrashreport.h" michael@0: michael@0: #include michael@0: michael@0: #include "jsapi.h" michael@0: #include "jscrashformat.h" michael@0: #include "jsutil.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::crash; michael@0: michael@0: #if defined(XP_WIN) michael@0: michael@0: static const int stack_snapshot_max_size = 32768; michael@0: michael@0: #include michael@0: michael@0: static bool michael@0: GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) michael@0: { michael@0: /* Try to figure out how big the stack is. */ michael@0: char dummy; michael@0: MEMORY_BASIC_INFORMATION info; michael@0: if (VirtualQuery(reinterpret_cast(&dummy), &info, sizeof(info)) == 0) michael@0: return false; michael@0: if (info.State != MEM_COMMIT) michael@0: return false; michael@0: michael@0: /* 256 is a fudge factor to account for the rest of GetStack's frame. */ michael@0: uint64_t p = uint64_t(&dummy) - 256; michael@0: uint64_t len = stack_snapshot_max_size; michael@0: michael@0: if (p + len > uint64_t(info.BaseAddress) + info.RegionSize) michael@0: len = uint64_t(info.BaseAddress) + info.RegionSize - p; michael@0: michael@0: if (len > size) michael@0: len = size; michael@0: michael@0: *stack = p; michael@0: *stack_len = len; michael@0: michael@0: /* Get the register state. */ michael@0: #if defined(_MSC_VER) && defined(_M_IX86) michael@0: /* ASM version for win2k that doesn't support RtlCaptureContext */ michael@0: uint32_t vip, vsp, vbp; michael@0: __asm { michael@0: MyLabel: michael@0: mov [vbp], ebp; michael@0: mov [vsp], esp; michael@0: mov eax, [MyLabel]; michael@0: mov [vip], eax; michael@0: } michael@0: regs->ip = vip; michael@0: regs->sp = vsp; michael@0: regs->bp = vbp; michael@0: #else michael@0: CONTEXT context; michael@0: RtlCaptureContext(&context); michael@0: #if defined(_M_IX86) michael@0: regs->ip = context.Eip; michael@0: regs->sp = context.Esp; michael@0: regs->bp = context.Ebp; michael@0: #elif defined(_M_X64) michael@0: regs->ip = context.Rip; michael@0: regs->sp = context.Rsp; michael@0: regs->bp = context.Rbp; michael@0: #else michael@0: #error unknown cpu architecture michael@0: #endif michael@0: #endif michael@0: michael@0: js_memcpy(buffer, (void *)p, len); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #elif 0 michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: static bool michael@0: GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) michael@0: { michael@0: /* 256 is a fudge factor to account for the rest of GetStack's frame. */ michael@0: char dummy; michael@0: uint64_t p = uint64_t(&dummy) - 256; michael@0: uint64_t pgsz = getpagesize(); michael@0: uint64_t len = stack_snapshot_max_size; michael@0: p &= ~(pgsz - 1); michael@0: michael@0: /* Try to figure out how big the stack is. */ michael@0: while (len > 0) { michael@0: if (mlock((const void *)p, len) == 0) { michael@0: munlock((const void *)p, len); michael@0: break; michael@0: } michael@0: len -= pgsz; michael@0: } michael@0: michael@0: if (len > size) michael@0: len = size; michael@0: michael@0: *stack = p; michael@0: *stack_len = len; michael@0: michael@0: /* Get the register state. */ michael@0: ucontext_t context; michael@0: if (getcontext(&context) != 0) michael@0: return false; michael@0: michael@0: #if defined(__x86_64__) michael@0: regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_RSP]; michael@0: regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_RBP]; michael@0: regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_RIP]; michael@0: #elif defined(__i386__) michael@0: regs->sp = (uint64_t)context.uc_mcontext.gregs[REG_ESP]; michael@0: regs->bp = (uint64_t)context.uc_mcontext.gregs[REG_EBP]; michael@0: regs->ip = (uint64_t)context.uc_mcontext.gregs[REG_EIP]; michael@0: #else michael@0: #error unknown cpu architecture michael@0: #endif michael@0: michael@0: js_memcpy(buffer, (void *)p, len); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: #else michael@0: michael@0: static bool michael@0: GetStack(uint64_t *stack, uint64_t *stack_len, CrashRegisters *regs, char *buffer, size_t size) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: namespace js { michael@0: namespace crash { michael@0: michael@0: class Stack : private CrashStack michael@0: { michael@0: public: michael@0: Stack(uint64_t id); michael@0: michael@0: bool snapshot(); michael@0: }; michael@0: michael@0: Stack::Stack(uint64_t id) michael@0: : CrashStack(id) michael@0: { michael@0: } michael@0: michael@0: bool michael@0: Stack::snapshot() michael@0: { michael@0: snaptime = time(nullptr); michael@0: return GetStack(&stack_base, &stack_len, ®s, stack, sizeof(stack)); michael@0: } michael@0: michael@0: class Ring : private CrashRing michael@0: { michael@0: public: michael@0: Ring(uint64_t id); michael@0: michael@0: void push(uint64_t tag, void *data, size_t size); michael@0: michael@0: private: michael@0: size_t bufferSize() { return crash_buffer_size; } michael@0: void copyBytes(void *data, size_t size); michael@0: }; michael@0: michael@0: Ring::Ring(uint64_t id) michael@0: : CrashRing(id) michael@0: { michael@0: } michael@0: michael@0: void michael@0: Ring::push(uint64_t tag, void *data, size_t size) michael@0: { michael@0: uint64_t t = time(nullptr); michael@0: michael@0: copyBytes(&tag, sizeof(uint64_t)); michael@0: copyBytes(&t, sizeof(uint64_t)); michael@0: copyBytes(data, size); michael@0: uint64_t mysize = size; michael@0: copyBytes(&mysize, sizeof(uint64_t)); michael@0: } michael@0: michael@0: void michael@0: Ring::copyBytes(void *data, size_t size) michael@0: { michael@0: if (size >= bufferSize()) michael@0: size = bufferSize(); michael@0: michael@0: if (offset + size > bufferSize()) { michael@0: size_t first = bufferSize() - offset; michael@0: size_t second = size - first; michael@0: js_memcpy(&buffer[offset], data, first); michael@0: js_memcpy(buffer, (char *)data + first, second); michael@0: offset = second; michael@0: } else { michael@0: js_memcpy(&buffer[offset], data, size); michael@0: offset += size; michael@0: } michael@0: } michael@0: michael@0: } /* namespace crash */ michael@0: } /* namespace js */ michael@0: michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: static bool gInitialized; michael@0: michael@0: static Stack gGCStack(JS_CRASH_STACK_GC); michael@0: static Stack gErrorStack(JS_CRASH_STACK_ERROR); michael@0: static Ring gRingBuffer(JS_CRASH_RING); michael@0: #endif michael@0: michael@0: void michael@0: js::crash::SnapshotGCStack() michael@0: { michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: if (gInitialized) michael@0: gGCStack.snapshot(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: js::crash::SnapshotErrorStack() michael@0: { michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: if (gInitialized) michael@0: gErrorStack.snapshot(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: js::crash::SaveCrashData(uint64_t tag, void *ptr, size_t size) michael@0: { michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: if (gInitialized) michael@0: gRingBuffer.push(tag, ptr, size); michael@0: #endif michael@0: } michael@0: michael@0: JS_PUBLIC_API(void) michael@0: JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback) michael@0: { michael@0: #ifdef JS_CRASH_DIAGNOSTICS michael@0: if (!gInitialized) { michael@0: gInitialized = true; michael@0: (*callback)(&gGCStack, sizeof(gGCStack)); michael@0: (*callback)(&gErrorStack, sizeof(gErrorStack)); michael@0: (*callback)(&gRingBuffer, sizeof(gRingBuffer)); michael@0: } michael@0: #endif michael@0: } michael@0: