Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/Firefox/js/src/wasm/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 17 kB image not shown  

Quelle  WasmDebug.cpp   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:
 *
 * Copyright 2016 Mozilla Foundation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


#include "wasm/WasmDebug.h"

#include "debugger/Debugger.h"
#include "ds/Sort.h"
#include "jit/MacroAssembler.h"
#include "js/ColumnNumber.h"  // JS::WasmFunctionIndex
#include "wasm/WasmJS.h"
#include "wasm/WasmStubs.h"
#include "wasm/WasmValidate.h"

#include "gc/GCContext-inl.h"
#include "wasm/WasmInstance-inl.h"

using namespace js;
using namespace js::jit;
using namespace js::wasm;

DebugState::DebugState(const Code& code, const Module& module)
    : code_(&code),
      module_(&module),
      enterFrameTrapsEnabled_(false),
      enterAndLeaveFrameTrapsCounter_(0) {
  MOZ_RELEASE_ASSERT(code.codeMeta().debugEnabled);
}

void DebugState::trace(JSTracer* trc) {
  for (auto iter = breakpointSites_.iter(); !iter.done(); iter.next()) {
    WasmBreakpointSite* site = iter.get().value();
    site->trace(trc);
  }
}

void DebugState::finalize(JS::GCContext* gcx) {
  for (auto iter = breakpointSites_.iter(); !iter.done(); iter.next()) {
    WasmBreakpointSite* site = iter.get().value();
    site->delete_(gcx);
  }
}

static bool SlowCallSiteSearchByOffset(const CodeBlock& code, uint32_t offset,
                                       CallSite* callSite) {
  for (uint32_t callSiteIndex = 0; callSiteIndex < code.callSites.length();
       callSiteIndex++) {
    if (code.callSites.kind(callSiteIndex) == CallSiteKind::Breakpoint &&
        code.callSites[callSiteIndex].lineOrBytecode() == offset) {
      *callSite = code.callSites[callSiteIndex];
      return true;
    }
  }
  return false;
}

bool DebugState::getLineOffsets(size_t lineno, Vector<uint32_t>* offsets) {
  CallSite callSite;
  return !SlowCallSiteSearchByOffset(debugCode(), lineno, &callSite) ||
         offsets->append(lineno);
}

bool DebugState::getAllColumnOffsets(Vector<ExprLoc>* offsets) {
  for (uint32_t callSiteIndex = 0;
       callSiteIndex < debugCode().callSites.length(); callSiteIndex++) {
    if (debugCode().callSites.kind(callSiteIndex) != CallSiteKind::Breakpoint) {
      continue;
    }
    uint32_t offset = debugCode().callSites[callSiteIndex].lineOrBytecode();
    if (!offsets->emplaceBack(
            offset,
            JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin,
            offset)) {
      return false;
    }
  }
  return true;
}

bool DebugState::getOffsetLocation(uint32_t offset, uint32_t* lineno,
                                   JS::LimitedColumnNumberOneOrigin* column) {
  CallSite callSite;
  if (!SlowCallSiteSearchByOffset(debugCode(), offset, &callSite)) {
    return false;
  }
  *lineno = offset;
  *column = JS::LimitedColumnNumberOneOrigin(
      JS::WasmFunctionIndex::DefaultBinarySourceColumnNumberOneOrigin);
  return true;
}

bool DebugState::stepModeEnabled(uint32_t funcIndex) const {
  return stepperCounters_.lookup(funcIndex).found();
}

bool DebugState::incrementStepperCount(JSContext* cx, Instance* instance,
                                       uint32_t funcIndex) {
  StepperCounters::AddPtr p = stepperCounters_.lookupForAdd(funcIndex);
  if (p) {
    MOZ_ASSERT(p->value() > 0);
    p->value()++;
    return true;
  }

  if (!stepperCounters_.add(p, funcIndex, 1)) {
    ReportOutOfMemory(cx);
    return false;
  }

  enableDebuggingForFunction(instance, funcIndex);
  enableDebugTrapping(instance);

  return true;
}

void DebugState::decrementStepperCount(JS::GCContext* gcx, Instance* instance,
                                       uint32_t funcIndex) {
  const CodeRange& codeRange =
      debugCode().codeRanges[funcToCodeRangeIndex(funcIndex)];
  MOZ_ASSERT(codeRange.isFunction());

  MOZ_ASSERT(!stepperCounters_.empty());
  StepperCounters::Ptr p = stepperCounters_.lookup(funcIndex);
  MOZ_ASSERT(p);
  if (--p->value()) {
    return;
  }

  stepperCounters_.remove(p);

  bool anyStepping = !stepperCounters_.empty();
  bool anyBreakpoints = !breakpointSites_.empty();
  bool anyEnterAndLeave = enterAndLeaveFrameTrapsCounter_ > 0;

  bool keepDebugging = false;
  for (uint32_t callSiteIndex = 0;
       callSiteIndex < debugCode().callSites.length(); callSiteIndex++) {
    if (debugCode().callSites.kind(callSiteIndex) != CallSiteKind::Breakpoint) {
      continue;
    }
    uint32_t offset =
        debugCode().callSites[callSiteIndex].returnAddressOffset();
    if (codeRange.begin() <= offset && offset <= codeRange.end()) {
      keepDebugging = keepDebugging || breakpointSites_.has(offset);
    }
  }

  if (!keepDebugging && !anyEnterAndLeave) {
    disableDebuggingForFunction(instance, funcIndex);
    if (!anyStepping && !anyBreakpoints) {
      disableDebugTrapping(instance);
    }
  }
}

bool DebugState::hasBreakpointTrapAtOffset(uint32_t offset) {
  CallSite callSite;
  return SlowCallSiteSearchByOffset(debugCode(), offset, &callSite);
}

void DebugState::toggleBreakpointTrap(JSRuntime* rt, Instance* instance,
                                      uint32_t offset, bool enabled) {
  CallSite callSite;
  if (!SlowCallSiteSearchByOffset(debugCode(), offset, &callSite)) {
    return;
  }
  size_t debugTrapOffset = callSite.returnAddressOffset();

  const CodeSegment& codeSegment = debugSegment();
  const CodeRange* codeRange =
      code_->lookupFuncRange(codeSegment.base() + debugTrapOffset);
  MOZ_ASSERT(codeRange);

  uint32_t funcIndex = codeRange->funcIndex();
  if (stepperCounters_.lookup(funcIndex)) {
    return;  // no need to toggle when step mode is enabled
  }

  bool anyEnterAndLeave = enterAndLeaveFrameTrapsCounter_ > 0;
  bool anyStepping = !stepperCounters_.empty();
  bool anyBreakpoints = !breakpointSites_.empty();

  if (enabled) {
    enableDebuggingForFunction(instance, funcIndex);
    enableDebugTrapping(instance);
  } else if (!anyEnterAndLeave) {
    disableDebuggingForFunction(instance, funcIndex);
    if (!anyStepping && !anyBreakpoints) {
      disableDebugTrapping(instance);
    }
  }
}

WasmBreakpointSite* DebugState::getBreakpointSite(uint32_t offset) const {
  WasmBreakpointSiteMap::Ptr p = breakpointSites_.lookup(offset);
  if (!p) {
    return nullptr;
  }

  return p->value();
}

WasmBreakpointSite* DebugState::getOrCreateBreakpointSite(JSContext* cx,
                                                          Instance* instance,
                                                          uint32_t offset) {
  WasmBreakpointSite* site;

  WasmBreakpointSiteMap::AddPtr p = breakpointSites_.lookupForAdd(offset);
  if (!p) {
    site = cx->new_<WasmBreakpointSite>(instance->object(), offset);
    if (!site) {
      return nullptr;
    }

    if (!breakpointSites_.add(p, offset, site)) {
      js_delete(site);
      ReportOutOfMemory(cx);
      return nullptr;
    }

    AddCellMemory(instance->object(), sizeof(WasmBreakpointSite),
                  MemoryUse::BreakpointSite);

    toggleBreakpointTrap(cx->runtime(), instance, offset, true);
  } else {
    site = p->value();
  }
  return site;
}

bool DebugState::hasBreakpointSite(uint32_t offset) {
  return breakpointSites_.has(offset);
}

void DebugState::destroyBreakpointSite(JS::GCContext* gcx, Instance* instance,
                                       uint32_t offset) {
  WasmBreakpointSiteMap::Ptr p = breakpointSites_.lookup(offset);
  MOZ_ASSERT(p);
  gcx->delete_(instance->objectUnbarriered(), p->value(),
               MemoryUse::BreakpointSite);
  breakpointSites_.remove(p);
  toggleBreakpointTrap(gcx->runtime(), instance, offset, false);
}

void DebugState::clearBreakpointsIn(JS::GCContext* gcx,
                                    WasmInstanceObject* instance,
                                    js::Debugger* dbg, JSObject* handler) {
  MOZ_ASSERT(instance);

  // Breakpoints hold wrappers in the instance's compartment for the handler.
  // Make sure we don't try to search for the unwrapped handler.
  MOZ_ASSERT_IF(handler, instance->compartment() == handler->compartment());

  if (breakpointSites_.empty()) {
    return;
  }
  for (WasmBreakpointSiteMap::Enum e(breakpointSites_); !e.empty();
       e.popFront()) {
    WasmBreakpointSite* site = e.front().value();
    MOZ_ASSERT(site->instanceObject == instance);

    Breakpoint* nextbp;
    for (Breakpoint* bp = site->firstBreakpoint(); bp; bp = nextbp) {
      nextbp = bp->nextInSite();
      MOZ_ASSERT(bp->site == site);
      if ((!dbg || bp->debugger == dbg) &&
          (!handler || bp->getHandler() == handler)) {
        bp->delete_(gcx);
      }
    }
    if (site->isEmpty()) {
      gcx->delete_(instance, site, MemoryUse::BreakpointSite);
      e.removeFront();
    }
  }
}

void DebugState::enableDebuggingForFunction(Instance* instance,
                                            uint32_t funcIndex) {
  instance->setDebugFilter(funcIndex, true);
}

void DebugState::disableDebuggingForFunction(Instance* instance,
                                             uint32_t funcIndex) {
  instance->setDebugFilter(funcIndex, false);
}

void DebugState::enableDebugTrapping(Instance* instance) {
  instance->setDebugStub(code_->sharedStubs().segment->base() +
                         code_->debugStubOffset());
}

void DebugState::disableDebugTrapping(Instance* instance) {
  instance->setDebugStub(nullptr);
}

void DebugState::adjustEnterAndLeaveFrameTrapsState(JSContext* cx,
                                                    Instance* instance,
                                                    bool enabled) {
  MOZ_ASSERT_IF(!enabled, enterAndLeaveFrameTrapsCounter_ > 0);

  bool wasEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
  enterAndLeaveFrameTrapsCounter_ += enabled ? 1 : -1;
  bool stillEnabled = enterAndLeaveFrameTrapsCounter_ > 0;
  if (wasEnabled == stillEnabled) {
    return;
  }

  MOZ_RELEASE_ASSERT(&instance->codeMeta() == &codeMeta());
  MOZ_RELEASE_ASSERT(instance->codeMetaForAsmJS() == codeMetaForAsmJS());
  uint32_t numFuncs = codeMeta().numFuncs();
  if (enabled) {
    MOZ_ASSERT(enterAndLeaveFrameTrapsCounter_ > 0);
    for (uint32_t funcIdx = 0; funcIdx < numFuncs; funcIdx++) {
      enableDebuggingForFunction(instance, funcIdx);
    }
    enableDebugTrapping(instance);
  } else {
    MOZ_ASSERT(enterAndLeaveFrameTrapsCounter_ == 0);
    bool anyEnabled = false;
    for (uint32_t funcIdx = 0; funcIdx < numFuncs; funcIdx++) {
      // For each function, disable the bit if nothing else is going on.  This
      // means determining if there's stepping or breakpoints.
      bool mustLeaveEnabled = stepperCounters_.lookup(funcIdx).found();
      for (auto iter = breakpointSites_.iter();
           !iter.done() && !mustLeaveEnabled; iter.next()) {
        WasmBreakpointSite* site = iter.get().value();
        CallSite callSite;
        if (SlowCallSiteSearchByOffset(debugCode(), site->offset, &callSite)) {
          size_t debugTrapOffset = callSite.returnAddressOffset();
          const CodeSegment& codeSegment = debugSegment();
          const CodeRange* codeRange =
              code_->lookupFuncRange(codeSegment.base() + debugTrapOffset);
          MOZ_ASSERT(codeRange);
          mustLeaveEnabled = codeRange->funcIndex() == funcIdx;
        }
      }
      if (mustLeaveEnabled) {
        anyEnabled = true;
      } else {
        disableDebuggingForFunction(instance, funcIdx);
      }
    }
    if (!anyEnabled) {
      disableDebugTrapping(instance);
    }
  }
}

void DebugState::ensureEnterFrameTrapsState(JSContext* cx, Instance* instance,
                                            bool enabled) {
  if (enterFrameTrapsEnabled_ == enabled) {
    return;
  }

  adjustEnterAndLeaveFrameTrapsState(cx, instance, enabled);

  enterFrameTrapsEnabled_ = enabled;
}

bool DebugState::debugGetLocalTypes(uint32_t funcIndex, ValTypeVector* locals,
                                    size_t* argsLength,
                                    StackResults* stackResults) {
  const TypeContext& types = *codeMeta().types;
  const FuncType& funcType = codeMeta().getFuncType(funcIndex);
  const ValTypeVector& args = funcType.args();
  const ValTypeVector& results = funcType.results();
  ResultType resultType(ResultType::Vector(results));
  *argsLength = args.length();
  *stackResults = ABIResultIter::HasStackResults(resultType)
                      ? StackResults::HasStackResults
                      : StackResults::NoStackResults;
  if (!locals->appendAll(args)) {
    return false;
  }

  // Decode local var types from wasm binary function body.
  uint32_t bytecodeOffset = codeMeta().funcBytecodeOffset(funcIndex);
  Decoder d(bytecode().begin() + bytecodeOffset, bytecode().end(),
            bytecodeOffset,
            /* error = */ nullptr);
  return DecodeValidatedLocalEntries(types, d, locals);
}

bool DebugState::getGlobal(Instance& instance, uint32_t globalIndex,
                           MutableHandleValue vp) {
  const GlobalDesc& global = codeMeta().globals[globalIndex];

  if (global.isConstant()) {
    LitVal value = global.constantValue();
    switch (value.type().kind()) {
      case ValType::I32:
        vp.set(Int32Value(value.i32()));
        break;
      case ValType::I64:
        // Just display as a Number; it's ok if we lose some precision
        vp.set(NumberValue((double)value.i64()));
        break;
      case ValType::F32:
        vp.set(NumberValue(JS::CanonicalizeNaN(value.f32())));
        break;
      case ValType::F64:
        vp.set(NumberValue(JS::CanonicalizeNaN(value.f64())));
        break;
      case ValType::Ref:
        // It's possible to do better.  We could try some kind of hashing
        // scheme, to make the pointer recognizable without revealing it.
        vp.set(MagicValue(JS_OPTIMIZED_OUT));
        break;
      case ValType::V128:
        // Debugger must be updated to handle this, and should be updated to
        // handle i64 in any case.
        vp.set(MagicValue(JS_OPTIMIZED_OUT));
        break;
      default:
        MOZ_CRASH("Global constant type");
    }
    return true;
  }

  void* dataPtr = instance.data() + global.offset();
  if (global.isIndirect()) {
    dataPtr = *static_cast<void**>(dataPtr);
  }
  switch (global.type().kind()) {
    case ValType::I32: {
      vp.set(Int32Value(*static_cast<int32_t*>(dataPtr)));
      break;
    }
    case ValType::I64: {
      // Just display as a Number; it's ok if we lose some precision
      vp.set(NumberValue((double)*static_cast<int64_t*>(dataPtr)));
      break;
    }
    case ValType::F32: {
      vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<float*>(dataPtr))));
      break;
    }
    case ValType::F64: {
      vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<double*>(dataPtr))));
      break;
    }
    case ValType::Ref: {
      // Just hide it.  See above.
      vp.set(MagicValue(JS_OPTIMIZED_OUT));
      break;
    }
    case ValType::V128: {
      // Just hide it.  See above.
      vp.set(MagicValue(JS_OPTIMIZED_OUT));
      break;
    }
    default: {
      MOZ_CRASH("Global variable type");
      break;
    }
  }
  return true;
}

bool DebugState::getSourceMappingURL(JSContext* cx,
                                     MutableHandleString result) const {
  result.set(nullptr);

  for (const CustomSection& customSection :
       module_->moduleMeta().customSections) {
    const Bytes& sectionName = customSection.name;
    if (strlen(SourceMappingURLSectionName) != sectionName.length() ||
        memcmp(SourceMappingURLSectionName, sectionName.begin(),
               sectionName.length()) != 0) {
      continue;
    }

    // Parse found "SourceMappingURL" custom section.
    Decoder d(customSection.payload->begin(), customSection.payload->end(), 0,
              /* error = */ nullptr);
    uint32_t nchars;
    if (!d.readVarU32(&nchars)) {
      return true;  // ignoring invalid section data
    }
    const uint8_t* chars;
    if (!d.readBytes(nchars, &chars) || d.currentPosition() != d.end()) {
      return true;  // ignoring invalid section data
    }

    JS::UTF8Chars utf8Chars(reinterpret_cast<const char*>(chars), nchars);
    JSString* str = JS_NewStringCopyUTF8N(cx, utf8Chars);
    if (!str) {
      return false;
    }
    result.set(str);
    return true;
  }

  // Check presence of "SourceMap:" HTTP response header.
  char* sourceMapURL = codeMeta().sourceMapURL().get();
  if (sourceMapURL && strlen(sourceMapURL)) {
    JS::UTF8Chars utf8Chars(sourceMapURL, strlen(sourceMapURL));
    JSString* str = JS_NewStringCopyUTF8N(cx, utf8Chars);
    if (!str) {
      return false;
    }
    result.set(str);
  }
  return true;
}

void DebugState::addSizeOfMisc(
    mozilla::MallocSizeOf mallocSizeOf, CodeMetadata::SeenSet* seenCodeMeta,
    CodeMetadataForAsmJS::SeenSet* seenCodeMetaForAsmJS,
    Code::SeenSet* seenCode, size_t* code, size_t* data) const {
  code_->addSizeOfMiscIfNotSeen(mallocSizeOf, seenCodeMeta,
                                seenCodeMetaForAsmJS, seenCode, code, data);
  module_->addSizeOfMisc(mallocSizeOf, seenCodeMeta, seenCodeMetaForAsmJS,
                         seenCode, code, data);
}

Messung V0.5
C=91 H=97 G=93

¤ Dauer der Verarbeitung: 0.15 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 und die Messung sind noch experimentell.