Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/mozglue/misc/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 9 kB image not shown  

Quelle  WindowsDiagnostics.h   Sprache: 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/. */


#ifndef mozilla_WindowsDiagnostics_h
#define mozilla_WindowsDiagnostics_h

#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Types.h"

// Bug 1898761: NativeNt.h depends on headers that live outside mozglue/misc/
// and are not available in SpiderMonkey builds. Until we fix this, we cannot
// depend on NativeNt.h in mozglue/misc/ cpp files.
#if !defined(IMPL_MFBT)
#  include "mozilla/NativeNt.h"
#endif  // !IMPL_MFBT

#include <windows.h>
#include <winternl.h>

#include <functional>

namespace mozilla {

enum class WindowsDiagnosticsError : uint32_t {
  None,
  InternalFailure,
  DebuggerPresent,
  ModuleNotFound,
  BadModule,
};

// Struct storing the visible, loggable error-state of a Windows thread.
// Approximately `std::pair(::GetLastError(), ::RtlGetLastNtStatus())`.
//
// Uses sentinel values rather than a proper `Maybe` type to simplify
// minidump-analysis.
struct WinErrorState {
  // Last error, as provided by ::GetLastError().
  DWORD error = ~0;
  // Last NTSTATUS, as provided by the TIB.
  NTSTATUS ntStatus = ~0;

 private:
  // per WINE et al.; stable since NT 3.51
  constexpr static size_t kLastNtStatusOffset =
      sizeof(size_t) == 8 ? 0x1250 : 0xbf4;

  static void SetLastNtStatus(NTSTATUS status) {
    auto* teb = ::NtCurrentTeb();
    *reinterpret_cast<NTSTATUS*>(reinterpret_cast<char*>(teb) +
                                 kLastNtStatusOffset) = status;
  }

  static NTSTATUS GetLastNtStatus() {
    auto const* teb = ::NtCurrentTeb();
    return *reinterpret_cast<NTSTATUS const*>(
        reinterpret_cast<char const*>(teb) + kLastNtStatusOffset);
  }

 public:
  // Restore (or just set) the error state of the current thread.
  static void Apply(WinErrorState const& state) {
    SetLastNtStatus(state.ntStatus);
    ::SetLastError(state.error);
  }

  // Clear the error-state of the current thread.
  static void Clear() { Apply({.error = 0, .ntStatus = 0}); }

  // Get the error-state of the current thread.
  static WinErrorState Get() {
    return WinErrorState{
        .error = ::GetLastError(),
        .ntStatus = GetLastNtStatus(),
    };
  }

  bool operator==(WinErrorState const& that) const {
    return this->error == that.error && this->ntStatus == that.ntStatus;
  }

  bool operator!=(WinErrorState const& that) const { return !operator==(that); }
};

#if defined(_M_AMD64)

using OnSingleStepCallback = std::function<bool(void*, CONTEXT*)>;

class MOZ_RAII AutoOnSingleStepCallback {
 public:
  MFBT_API AutoOnSingleStepCallback(OnSingleStepCallback aOnSingleStepCallback,
                                    void* aState);
  MFBT_API ~AutoOnSingleStepCallback();

  AutoOnSingleStepCallback(const AutoOnSingleStepCallback&) = delete;
  AutoOnSingleStepCallback(AutoOnSingleStepCallback&&) = delete;
  AutoOnSingleStepCallback& operator=(const AutoOnSingleStepCallback&) = delete;
  AutoOnSingleStepCallback& operator=(AutoOnSingleStepCallback&&) = delete;
};

MFBT_API MOZ_NEVER_INLINE __attribute__((naked)) void EnableTrapFlag();
MFBT_API MOZ_NEVER_INLINE __attribute__((naked)) void DisableTrapFlag();
MFBT_API LONG SingleStepExceptionHandler(_EXCEPTION_POINTERS* aExceptionInfo);

// Run aCallbackToRun instruction by instruction, and between each instruction
// call aOnSingleStepCallback. Single-stepping ends when aOnSingleStepCallback
// returns false (in which case aCallbackToRun will continue to run
// unmonitored), or when we reach the end of aCallbackToRun.
template <typename CallbackToRun>
[[clang::optnone]] MOZ_NEVER_INLINE WindowsDiagnosticsError
CollectSingleStepData(CallbackToRun aCallbackToRun,
                      OnSingleStepCallback aOnSingleStepCallback,
                      void* aOnSingleStepCallbackState) {
  if (::IsDebuggerPresent()) {
    return WindowsDiagnosticsError::DebuggerPresent;
  }

  AutoOnSingleStepCallback setCallback(std::move(aOnSingleStepCallback),
                                       aOnSingleStepCallbackState);

  auto veh = ::AddVectoredExceptionHandler(TRUE, SingleStepExceptionHandler);
  if (!veh) {
    return WindowsDiagnosticsError::InternalFailure;
  }

  EnableTrapFlag();
  aCallbackToRun();
  DisableTrapFlag();
  ::RemoveVectoredExceptionHandler(veh);

  return WindowsDiagnosticsError::None;
}

// This block uses nt::PEHeaders and thus depends on NativeNt.h.
#  if !defined(IMPL_MFBT)

template <int NMaxSteps, int NMaxErrorStates>
struct ModuleSingleStepData {
  uint32_t mStepsLog[NMaxSteps]{};
  WinErrorState mErrorStatesLog[NMaxErrorStates]{};
  uint16_t mStepsAtErrorState[NMaxErrorStates]{};
};

template <int NMaxSteps, int NMaxErrorStates>
struct ModuleSingleStepState {
  uintptr_t mModuleStart;
  uintptr_t mModuleEnd;
  uint32_t mSteps;
  uint32_t mErrorStates;
  WinErrorState mLastRecordedErrorState;
  ModuleSingleStepData<NMaxSteps, NMaxErrorStates> mData;

  ModuleSingleStepState(uintptr_t aModuleStart, uintptr_t aModuleEnd)
      : mModuleStart{aModuleStart},
        mModuleEnd{aModuleEnd},
        mSteps{},
        mErrorStates{},
        mLastRecordedErrorState{},
        mData{} {}
};

namespace InstructionFilter {

// These functions return true if the instruction behind aInstructionPointer
// should be recorded during single-stepping.

inline bool All(const uint8_t* aInstructionPointer) { return true; }

// Note: This filter does *not* currently identify all call/ret instructions.
//       For example, prefixed instructions are not recognized.
inline bool CallRet(const uint8_t* aInstructionPointer) {
  auto firstByte = aInstructionPointer[0];
  // E8: CALL rel. Call near, relative.
  if (firstByte == 0xe8) {
    return true;
  }
  // FF /2: CALL r. Call near, absolute indirect.
  else if (firstByte == 0xff) {
    auto secondByte = aInstructionPointer[1];
    if ((secondByte & 0x38) == 0x10) {
      return true;
    }
  }
  // C3: RET. Near return.
  else if (firstByte == 0xc3) {
    return true;
  }
  // C2: RET imm. Near return and pop imm bytes.
  else if (firstByte == 0xc2) {
    return true;
  }
  return false;
}

}  // namespace InstructionFilter

// This function runs aCallbackToRun instruction by instruction, recording
// information about the paths taken within a specific module given by
// aModulePath. It then calls aPostCollectionCallback with the collected data.
//
// We store the collected data in stack, so that it is available in crash
// reports in case we decide to crash from aPostCollectionCallback. Remember to
// carefully estimate the stack usage when choosing NMaxSteps and
// NMaxErrorStates. Consider using an InstructionFilter if you need to reduce
// the number of steps that get recorded.
//
// This function is typically useful on known-to-crash paths, where we can
// replace the crash by a new single-stepped attempt at doing the operation
// that just failed. If the operation fails while single-stepped, we'll be able
// to produce a crash report that contains single step data, which may prove
// useful to understand why the operation failed.
template <
    int NMaxSteps, int NMaxErrorStates, typename CallbackToRun,
    typename PostCollectionCallback,
    typename InstructionFilterCallback = decltype(&InstructionFilter::All)>
WindowsDiagnosticsError CollectModuleSingleStepData(
    const wchar_t* aModulePath, CallbackToRun aCallbackToRun,
    PostCollectionCallback aPostCollectionCallback,
    InstructionFilterCallback aInstructionFilter = InstructionFilter::All) {
  HANDLE mod = ::GetModuleHandleW(aModulePath);
  if (!mod) {
    return WindowsDiagnosticsError::ModuleNotFound;
  }

  nt::PEHeaders headers{mod};
  auto maybeBounds = headers.GetBounds();
  if (maybeBounds.isNothing()) {
    return WindowsDiagnosticsError::BadModule;
  }

  auto& bounds = maybeBounds.ref();
  using State = ModuleSingleStepState<NMaxSteps, NMaxErrorStates>;
  State state{reinterpret_cast<uintptr_t>(bounds.begin().get()),
              reinterpret_cast<uintptr_t>(bounds.end().get())};

  WindowsDiagnosticsError rv = CollectSingleStepData(
      std::move(aCallbackToRun),
      [&aInstructionFilter](void* aState, CONTEXT* aContextRecord) -> bool {
        auto& state = *reinterpret_cast<State*>(aState);
        auto instructionPointer = aContextRecord->Rip;
        // Record data for the current step, if in module
        if (state.mModuleStart <= instructionPointer &&
            instructionPointer < state.mModuleEnd &&
            aInstructionFilter(
                reinterpret_cast<const uint8_t*>(instructionPointer))) {
          // We record the instruction pointer
          if (state.mSteps < NMaxSteps) {
            state.mData.mStepsLog[state.mSteps] =
                static_cast<uint32_t>(instructionPointer - state.mModuleStart);
          }

          // We record changes in the error state
          auto currentErrorState{WinErrorState::Get()};
          if (currentErrorState != state.mLastRecordedErrorState) {
            state.mLastRecordedErrorState = currentErrorState;

            if (state.mErrorStates < NMaxErrorStates) {
              state.mData.mErrorStatesLog[state.mErrorStates] =
                  currentErrorState;
              state.mData.mStepsAtErrorState[state.mErrorStates] = state.mSteps;
            }

            ++state.mErrorStates;
          }

          ++state.mSteps;
        }

        // Continue single-stepping
        return true;
      },
      reinterpret_cast<void*>(&state));

  if (rv != WindowsDiagnosticsError::None) {
    return rv;
  }

  aPostCollectionCallback(state.mData);

  return WindowsDiagnosticsError::None;
}

#  endif  // !IMPL_MFBT

#endif  // _M_AMD64

}  // namespace mozilla

#endif  // mozilla_WindowsDiagnostics_h

100%


¤ Dauer der Verarbeitung: 0.18 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.