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