This patch makes the single-step data collection code that we implemented for bug 1571516 reusable, while preserving its behavior. No functional changes, except for a slight reordering of the error-result enum. We define a generic CollectSingleStepData function that embeds the magic for starting to trigger single step exceptions and for acting upon them. We define a more specialized CollectModuleSingleStepData function which can be reused if the purpose of single step data collection is to monitor what paths are taken within a specific module. It stores the collected data in stack, so that it can be accessed from crash reports. This code is considered unstable and thus only available in Nightly and early Beta and only used on paths that are known to crash already. Differential Revision: https://phabricator.services.mozilla.com/D211195
82 lines
2.9 KiB
C++
82 lines
2.9 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
|
/* 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 "mozilla/WindowsDiagnostics.h"
|
|
|
|
#include "mozilla/Assertions.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/Types.h"
|
|
|
|
#include <windows.h>
|
|
#include <winternl.h>
|
|
|
|
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) && defined(_M_X64)
|
|
|
|
namespace mozilla {
|
|
|
|
static OnSingleStepCallback sOnSingleStepCallback{};
|
|
static void* sOnSingleStepCallbackState = nullptr;
|
|
static bool sIsSingleStepping = false;
|
|
|
|
MFBT_API AutoOnSingleStepCallback::AutoOnSingleStepCallback(
|
|
OnSingleStepCallback aOnSingleStepCallback, void* aState) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!sIsSingleStepping && !sOnSingleStepCallback &&
|
|
!sOnSingleStepCallbackState,
|
|
"Single-stepping is already active");
|
|
|
|
sOnSingleStepCallback = std::move(aOnSingleStepCallback);
|
|
sOnSingleStepCallbackState = aState;
|
|
sIsSingleStepping = true;
|
|
}
|
|
|
|
MFBT_API AutoOnSingleStepCallback::~AutoOnSingleStepCallback() {
|
|
sOnSingleStepCallback = OnSingleStepCallback();
|
|
sOnSingleStepCallbackState = nullptr;
|
|
sIsSingleStepping = false;
|
|
}
|
|
|
|
// Going though this assembly code turns on the trap flag, which will trigger
|
|
// a first single-step exception. It is then up to the exception handler to
|
|
// keep the trap flag enabled so that a new single step exception gets
|
|
// triggered with the following instruction.
|
|
MFBT_API MOZ_NEVER_INLINE __attribute__((naked)) void EnableTrapFlag() {
|
|
asm volatile(
|
|
"pushfq;"
|
|
"orw $0x100,(%rsp);"
|
|
"popfq;"
|
|
"retq;");
|
|
}
|
|
|
|
// This function does not do anything special, but when we reach its address
|
|
// while single-stepping the exception handler will know that it is now time to
|
|
// leave the trap flag turned off.
|
|
MFBT_API MOZ_NEVER_INLINE __attribute__((naked)) void DisableTrapFlag() {
|
|
asm volatile("retq;");
|
|
}
|
|
|
|
MFBT_API LONG SingleStepExceptionHandler(_EXCEPTION_POINTERS* aExceptionInfo) {
|
|
if (sIsSingleStepping && sOnSingleStepCallback &&
|
|
aExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_SINGLE_STEP) {
|
|
auto instructionPointer = aExceptionInfo->ContextRecord->Rip;
|
|
bool keepOnSingleStepping = false;
|
|
if (instructionPointer != reinterpret_cast<uintptr_t>(&DisableTrapFlag)) {
|
|
keepOnSingleStepping = sOnSingleStepCallback(
|
|
sOnSingleStepCallbackState, aExceptionInfo->ContextRecord);
|
|
}
|
|
if (keepOnSingleStepping) {
|
|
aExceptionInfo->ContextRecord->EFlags |= 0x100;
|
|
} else {
|
|
sIsSingleStepping = false;
|
|
}
|
|
return EXCEPTION_CONTINUE_EXECUTION;
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
} // namespace mozilla
|
|
|
|
#endif // MOZ_DIAGNOSTIC_ASSERT_ENABLED && _M_X64
|