Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  ExecutionTracer.h   Sprache: C

 

/* 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 debugger_ExecutionTracer_h
#define debugger_ExecutionTracer_h

#include "mozilla/Assertions.h"         // MOZ_DIAGNOSTIC_ASSERT, MOZ_ASSERT
#include "mozilla/BaseProfilerUtils.h"  // profiler_current_thread_id
#include "mozilla/EndianUtils.h"        // NativeEndian
#include "mozilla/Maybe.h"  // mozilla::Maybe, mozilla::Some, mozilla::Nothing
#include "mozilla/Span.h"
#include "mozilla/TimeStamp.h"

#include <bit>       // std::has_single_bit
#include <limits>    // std::numeric_limits
#include <stddef.h>  // size_t
#include <stdint.h>  // uint8_t, uint16_t

#include "js/CharacterEncoding.h"  // JS::UTF8Chars
#include "js/Debug.h"              // JS::ExecutionTrace
#include "js/RootingAPI.h"         // JS::Rooted
#include "js/Utility.h"            // js_malloc, js_free
#include "js/Value.h"              // JS::Value
#include "vm/ErrorObject.h"        // ErrorObject
#include "vm/JSContext.h"          // JSContext
#include "vm/Stack.h"              // js::AbstractFramePtr

namespace js {

enum class OutOfLineEntryType : uint8_t {
  ScriptURL,
  Atom,
  Shape,
};

enum class InlineEntryType : uint8_t {
  StackFunctionEnter,
  StackFunctionLeave,
  LabelEnter,
  LabelLeave,
  Error,
};

enum class PropertyKeyKind : uint8_t {
  Undefined,
  String,
  Int,
  Symbol,
};

using TracingScratchBuffer = mozilla::Vector<char, 512>;

// TODO: it should be noted that part of this design is informed by the fact
// that it evolved from a prototype which wrote this data from a content
// process and read it from the parent process, allowing the parent process to
// display the trace in real time as the program executes. Bug 1910182 tracks
// the next steps for making that prototype a reality.
template <size_t BUFFER_SIZE>
class TracingBuffer {
  static_assert(std::has_single_bit(BUFFER_SIZE));

  // BUFFER_SIZE is the size of the underlying ring buffer, and BUFFER_MASK
  // masks off indices into it in order to wrap around
  static const size_t BUFFER_MASK = BUFFER_SIZE - 1;

  // The entry header is just a u16 that holds the size of the entry in bytes.
  // This is used for asserting the integrity of the data as well as for
  // skipping the read head forward if it's going to be overwritten by the
  // write head
  static const size_t ENTRY_HEADER_SIZE = sizeof(uint16_t);

  // The underlying ring buffer
  uint8_t* buffer_ = nullptr;

  // NOTE: The following u64s are unwrapped indices into the ring buffer, so
  // they must always be masked off with BUFFER_MASK before using them to
  // access buffer_:

  // Represents how much has been written into the ring buffer and is ready
  // for reading
  uint64_t writeHead_ = 0;

  // Represents how much has been read from the ring buffer
  uint64_t readHead_ = 0;

  // When not equal to writeHead_, this represents unfinished write progress
  // into the buffer. After each entry successfully finished writing,
  // writeHead_ is set to this value
  uint64_t uncommittedWriteHead_ = 0;

  // Similar to uncommittedWriteHead_, but for the purposes of reading
  uint64_t uncommittedReadHead_ = 0;

  bool ensureScratchBufferSize(TracingScratchBuffer& scratchBuffer,
                               size_t requiredSize) {
    if (scratchBuffer.length() >= requiredSize) {
      return true;
    }
    return scratchBuffer.growByUninitialized(requiredSize -
                                             scratchBuffer.length());
  }

 public:
  static const size_t SIZE = BUFFER_SIZE;

  ~TracingBuffer() {
    if (buffer_) {
      js_free(buffer_);
    }
  }

  bool init() {
    buffer_ = static_cast<uint8_t*>(js_malloc(BUFFER_SIZE));
    return buffer_ != nullptr;
  }

  bool readable() { return writeHead_ > readHead_; }

  uint64_t uncommittedWriteHead() { return uncommittedWriteHead_; }

  uint64_t readHead() { return readHead_; }

  void beginWritingEntry() {
    // uncommittedWriteHead_ can be > writeHead_ if a previous write failed.
    // In that case, this effectively discards whatever was written during that
    // time
    MOZ_ASSERT(uncommittedWriteHead_ >= writeHead_);
    uncommittedWriteHead_ = writeHead_;
    uncommittedWriteHead_ += ENTRY_HEADER_SIZE;
  }

  void finishWritingEntry() {
    MOZ_RELEASE_ASSERT(uncommittedWriteHead_ - writeHead_ <=
                       std::numeric_limits<uint16_t>::max());
    uint16_t entryHeader = uint16_t(uncommittedWriteHead_ - writeHead_);
    writeAtOffset(entryHeader, writeHead_);
    writeHead_ = uncommittedWriteHead_;
  }

  void beginReadingEntry() {
    MOZ_ASSERT(uncommittedReadHead_ == readHead_);
    // We will read the entry header (still pointed to by readHead_) from
    // inside finishReadingEntry
    uncommittedReadHead_ += ENTRY_HEADER_SIZE;
  }

  void finishReadingEntry() {
    uint16_t entryHeader;
    readAtOffset(&entryHeader, readHead_);
    size_t read = uncommittedReadHead_ - readHead_;

    MOZ_RELEASE_ASSERT(entryHeader == uint16_t(read));
    readHead_ += entryHeader;
    uncommittedReadHead_ = readHead_;
  }

  void skipEntry() {
    uint16_t entryHeader;
    readAtOffset(&entryHeader, readHead_);
    readHead_ += entryHeader;
    uncommittedReadHead_ = readHead_;
  }

  void writeBytesAtOffset(const uint8_t* bytes, size_t length,
                          uint64_t offset) {
    MOZ_ASSERT(offset + length <= readHead_ + BUFFER_SIZE);

    size_t maskedWriteHead = offset & BUFFER_MASK;
    if (maskedWriteHead + length > BUFFER_SIZE) {
      size_t firstChunk = BUFFER_SIZE - maskedWriteHead;
      memcpy(buffer_ + maskedWriteHead, bytes, firstChunk);
      memcpy(buffer_, bytes + firstChunk, length - firstChunk);
    } else {
      memcpy(buffer_ + maskedWriteHead, bytes, length);
    }
  }

  void writeBytes(const uint8_t* bytes, size_t length) {
    // Skip the read head forward if we're about to overwrite unread entries
    while (MOZ_UNLIKELY(uncommittedWriteHead_ + length >
                        readHead_ + BUFFER_SIZE)) {
      skipEntry();
    }

    writeBytesAtOffset(bytes, length, uncommittedWriteHead_);
    uncommittedWriteHead_ += length;
  }

  template <typename T>
  void write(T val) {
    // No magic hidden work allowed here - we are just reducing duplicate code
    // serializing integers and floats.
    static_assert(std::is_arithmetic_v<T>);
    if constexpr (sizeof(T) > 1) {
      val = mozilla::NativeEndian::swapToLittleEndian(val);
    }

    writeBytes(reinterpret_cast<const uint8_t*>(&val), sizeof(T));
  }

  template <typename T>
  void writeAtOffset(T val, uint64_t offset) {
    static_assert(std::is_arithmetic_v<T>);
    if constexpr (sizeof(T) > 1) {
      val = mozilla::NativeEndian::swapToLittleEndian(val);
    }

    writeBytesAtOffset(reinterpret_cast<const uint8_t*>(&val), sizeof(T),
                       offset);
  }

  void writeEmptyString() {
    write(uint8_t(JS::TracerStringEncoding::Latin1));
    write(uint32_t(0));  // length
  }

  void writeEmptySmallString() { write(uint16_t(0)); }

  enum class InlineStringEncoding { No, Yes };

  // Helper for writing the length and encoding, which are sometimes squished
  // into one value
  template <typename LengthType = uint32_t,
            InlineStringEncoding InlineEncoding = InlineStringEncoding::No>
  void writeAdjustedLengthAndEncoding(
      size_t* length, JS::TracerStringEncoding encoding,
      size_t lengthLimit = std::numeric_limits<LengthType>::max()) {
    if (*length > lengthLimit) {
      *length = lengthLimit;
    }

    if constexpr (InlineEncoding == InlineStringEncoding::No) {
      write(uint8_t(encoding));
      write(LengthType(*length));
    } else {
      constexpr LengthType encodingBits = 2;
      LengthType typedLength =
          LengthType(*length) |
          (uint16_t(encoding) << (sizeof(LengthType) * 8 - encodingBits));
      write(typedLength);
    }
  }

  template <typename LengthType = uint32_t,
            InlineStringEncoding InlineEncoding = InlineStringEncoding::No>
  bool writeString(
      JSContext* cx, JS::Handle<JSString*> str,
      size_t lengthLimit = std::numeric_limits<LengthType>::max()) {
    JS::TracerStringEncoding encoding;
    if (str->hasLatin1Chars()) {
      encoding = JS::TracerStringEncoding::Latin1;
    } else {
      encoding = JS::TracerStringEncoding::TwoByte;
    }

    // TODO: if ropes are common we can certainly serialize them without
    // linearizing - this is just easy
    JSLinearString* linear = str->ensureLinear(cx);
    if (!linear) {
      return false;
    }

    size_t length = linear->length();
    writeAdjustedLengthAndEncoding<LengthType, InlineEncoding>(
        &length, encoding, lengthLimit);

    size_t size = length;
    JS::AutoAssertNoGC nogc;
    const uint8_t* charBuffer = nullptr;
    if (encoding == JS::TracerStringEncoding::TwoByte) {
      size *= sizeof(char16_t);
      charBuffer = reinterpret_cast<const uint8_t*>(linear->twoByteChars(nogc));
    } else {
      charBuffer = reinterpret_cast<const uint8_t*>(linear->latin1Chars(nogc));
    }
    writeBytes(charBuffer, size);
    return true;
  }

  template <typename CharType, JS::TracerStringEncoding Encoding,
            typename LengthType = uint32_t,
            InlineStringEncoding InlineEncoding = InlineStringEncoding::No>
  void writeCString(
      const CharType* chars,
      size_t lengthLimit = std::numeric_limits<LengthType>::max()) {
    size_t length = std::char_traits<CharType>::length(chars);
    static_assert(sizeof(CharType) == 1 ||
                  Encoding == JS::TracerStringEncoding::TwoByte);
    static_assert(sizeof(CharType) <= 2);

    writeAdjustedLengthAndEncoding<LengthType, InlineEncoding>(
        &length, Encoding, lengthLimit);

    const size_t size = length * sizeof(CharType);
    writeBytes(reinterpret_cast<const uint8_t*>(chars), size);
  }

  bool writeSmallString(JSContext* cx, JS::Handle<JSString*> str) {
    return writeString<uint16_t, InlineStringEncoding::Yes>(
        cx, str, JS::ValueSummary::SMALL_STRING_LENGTH_LIMIT);
  }

  template <typename CharType, JS::TracerStringEncoding Encoding>
  void writeSmallCString(const CharType* chars) {
    writeCString<CharType, Encoding, char16_t, InlineStringEncoding::Yes>(
        chars, JS::ValueSummary::SMALL_STRING_LENGTH_LIMIT);
  }

  void readBytesAtOffset(uint8_t* bytes, size_t length, uint64_t offset) {
    size_t maskedReadHead = offset & BUFFER_MASK;
    if (maskedReadHead + length > BUFFER_SIZE) {
      size_t firstChunk = BUFFER_SIZE - maskedReadHead;
      memcpy(bytes, buffer_ + maskedReadHead, firstChunk);
      memcpy(bytes + firstChunk, buffer_, length - firstChunk);
    } else {
      memcpy(bytes, buffer_ + maskedReadHead, length);
    }
  }

  void readBytes(uint8_t* bytes, size_t length) {
    readBytesAtOffset(bytes, length, uncommittedReadHead_);
    uncommittedReadHead_ += length;
  }

  template <typename T>
  void read(T* val) {
    static_assert(std::is_arithmetic_v<T>);

    readBytes(reinterpret_cast<uint8_t*>(val), sizeof(T));
    if constexpr (sizeof(T) > 1) {
      *val = mozilla::NativeEndian::swapFromLittleEndian(*val);
    }
  }

  template <typename T>
  void readAtOffset(T* val, uint64_t offset) {
    static_assert(std::is_arithmetic_v<T>);

    readBytesAtOffset(reinterpret_cast<uint8_t*>(val), sizeof(T), offset);
    if constexpr (sizeof(T) > 1) {
      *val = mozilla::NativeEndian::swapFromLittleEndian(*val);
    }
  }

  // Reads a string from our buffer into the stringBuffer. Converts everything
  // to null-terminated UTF-8
  template <typename LengthType = uint32_t,
            InlineStringEncoding InlineEncoding = InlineStringEncoding::No>
  bool readString(TracingScratchBuffer& scratchBuffer,
                  mozilla::Vector<char>& stringBuffer, size_t* index) {
    uint8_t encodingByte;
    LengthType length;
    if constexpr (InlineEncoding == InlineStringEncoding::Yes) {
      LengthType lengthAndEncoding;
      read(&lengthAndEncoding);
      constexpr LengthType encodingBits = 2;
      constexpr LengthType encodingShift =
          sizeof(LengthType) * 8 - encodingBits;
      constexpr LengthType encodingMask = 0b11 << encodingShift;
      length = lengthAndEncoding & ~encodingMask;
      encodingByte = (lengthAndEncoding & encodingMask) >> encodingShift;
    } else {
      read(&encodingByte);
      read(&length);
    }

    JS::TracerStringEncoding encoding = JS::TracerStringEncoding(encodingByte);

    *index = stringBuffer.length();

    if (length == 0) {
      if (!stringBuffer.append('\0')) {
        return false;
      }
      return true;
    }

    if (encoding == JS::TracerStringEncoding::UTF8) {
      size_t reserveLength = length + 1;
      if (!stringBuffer.growByUninitialized(reserveLength)) {
        return false;
      }
      char* writePtr = stringBuffer.end() - reserveLength;
      readBytes(reinterpret_cast<uint8_t*>(writePtr), length);
      writePtr[length] = '\0';
    } else if (encoding == JS::TracerStringEncoding::Latin1) {
      if (!ensureScratchBufferSize(scratchBuffer, length)) {
        return false;
      }
      readBytes(reinterpret_cast<uint8_t*>(scratchBuffer.begin()), length);

      // A single latin-1 code point maps to either 1 or 2 UTF-8 code units.
      // The + 1 is for the null terminator.
      size_t reserveLength = length * 2 + 1;
      if (!stringBuffer.reserve(stringBuffer.length() + reserveLength)) {
        return false;
      }
      char* writePtr = stringBuffer.end();

      size_t convertedLength = mozilla::ConvertLatin1toUtf8(
          mozilla::Span<const char>(scratchBuffer.begin(), length),
          mozilla::Span<char>(writePtr, reserveLength));
      writePtr[convertedLength] = 0;

      // We reserved above, which just grows the capacity but not the length.
      // This just commits the exact length increase.
      if (!stringBuffer.growByUninitialized(convertedLength + 1)) {
        return false;
      }
    } else {
      MOZ_ASSERT(encoding == JS::TracerStringEncoding::TwoByte);
      if (!ensureScratchBufferSize(scratchBuffer, length * sizeof(char16_t))) {
        return false;
      }
      readBytes(reinterpret_cast<uint8_t*>(scratchBuffer.begin()),
                length * sizeof(char16_t));

      // Non-surrogate-paired single UTF-16 code unit maps to 1 to 3 UTF-8
      // code units. Surrogate paired UTF-16 code units map to 4 to 6 UTF-8
      // code units.
      size_t reserveLength = length * 3 + 1;
      if (!stringBuffer.reserve(stringBuffer.length() + reserveLength)) {
        return false;
      }
      char* writePtr = stringBuffer.end();

      size_t convertedLength = mozilla::ConvertUtf16toUtf8(
          mozilla::Span<const char16_t>(
              reinterpret_cast<char16_t*>(scratchBuffer.begin()), length),
          mozilla::Span<char>(writePtr, reserveLength));
      writePtr[convertedLength] = 0;

      // We reserved above, which just grows the capacity but not the length.
      // This just commits the exact length increase.
      if (!stringBuffer.growByUninitialized(convertedLength + 1)) {
        return false;
      }
    }

    return true;
  }

  bool readSmallString(TracingScratchBuffer& scratchBuffer,
                       mozilla::Vector<char>& stringBuffer, size_t* index) {
    return readString<uint16_t, InlineStringEncoding::Yes>(scratchBuffer,
                                                           stringBuffer, index);
  }
};

// These sizes are to some degree picked out of a hat, and eventually it might
// be nice to make them configurable. For reference, I measured it costing
// 145MB to open gdocs and create an empty document, so 256MB is just some
// extra wiggle room for complex use cases.
using InlineDataBuffer = TracingBuffer<1 << 28>;

// We include a separate buffer for value summaries, so that we can store them
// contiguously and so we don't lose information from the inline data if a
// script has a lot of large values for instance.
using ValueDataBuffer = InlineDataBuffer;

// The size for the out of line data is much smaller, so I just picked a size
// that was much smaller but big enough that I didn't see us running out of it
// when playing around on various complex apps. Again, it would be great in the
// future for this to be configurable.
using OutOfLineDataBuffer = TracingBuffer<1 << 22>;

class ValueSummaries {
  ValueDataBuffer* valueData_ = nullptr;
  OutOfLineDataBuffer* outOfLineData_ = nullptr;

  friend struct ::JS_TracerSummaryWriter;

 public:
  // Sometimes we write ValueSummarys as nested properties of other
  // ValueSummarys. This enum is used to indicate that in code when necessary.
  // (This value is not written into the serialized format, and should instead
  // be tracked by the reader)
  enum class IsNested { No, Yes };

  void init(ValueDataBuffer* valueData, OutOfLineDataBuffer* outOfLineData) {
    valueData_ = valueData;
    outOfLineData_ = outOfLineData;
  }

  bool writeValue(JSContext* cx, JS::Handle<JS::Value> val, IsNested nested);

  // valueBufferIndex will hold the index at which we wrote the arguments into
  // the valueData_ buffer
  bool writeArguments(JSContext* cx, AbstractFramePtr frame,
                      uint64_t* valueBufferIndex);

  // Unrolls the underlying ring buffer into a contiguous, compacted buffer
  // and puts it into the context's valueBuffer field.
  bool populateOutputBuffer(JS::ExecutionTrace::TracedJSContext& context);

  // If ringBufferIndex is still valid, translates it into an index into the
  // output buffer. Otherwise, this returns
  // JS::ExecutionTrace::EXPIRED_VALUES_MAGIC.
  int32_t getOutputBufferIndex(uint64_t ringBufferIndex);

  void writeHeader(JS::ValueType type, uint8_t flags);
  bool writeShapeSummary(JSContext* cx, JS::Handle<NativeShape*> shape);

  // Only writes the class name
  bool writeMinimalShapeSummary(JSContext* cx, JS::Handle<Shape*> shape);

  void writeObjectHeader(JS::ObjectSummary::Kind kind, uint8_t flags);

  bool writeObject(JSContext* cx, JS::Handle<JSObject*> obj, IsNested nested);

  bool writeFunctionSummary(JSContext* cx, JS::Handle<JSFunction*> fn,
                            IsNested nested);
  bool writeArrayObjectSummary(JSContext* cx, JS::Handle<ArrayObject*> array,
                               IsNested nested);
  bool writeSetObjectSummary(JSContext* cx, JS::Handle<SetObject*> set,
                             IsNested nested);
  bool writeMapObjectSummary(JSContext* cx, JS::Handle<MapObject*> map,
                             IsNested nested);
  bool writeErrorObjectSummary(JSContext* cx, JS::Handle<JSObject*> obj,
                               JS::Handle<ErrorObject*> error, IsNested nested);
  bool writeGenericOrWrappedPrimitiveObjectSummary(
      JSContext* cx, JS::Handle<NativeObject*> nobj, IsNested nested);
  bool writeExternalObjectSummary(JSContext* cx, JS::Handle<NativeObject*> nobj,
                                  IsNested nested);

  bool writeStringLikeValue(JSContext* cx, JS::ValueType valueType,
                            JS::Handle<JSString*> str);
};

// An ExecutionTracer is responsible for recording JS execution while it is
// enabled to a set of ring buffers, and providing that information as a JS
// object when requested. See Debugger.md (collectNativeTrace) for more details.
class ExecutionTracer {
 private:
  // The fields below should only be accessed while we hold the lock.
  static Mutex globalInstanceLock MOZ_UNANNOTATED;
  static mozilla::Vector<ExecutionTracer*> globalInstances;

  // The buffers below should only be accessed while we hold the lock.
  Mutex bufferLock_ MOZ_UNANNOTATED;

  // This holds the actual entries, one for each push or pop of a frame or label
  InlineDataBuffer inlineData_;

  // This holds data that may be duplicated across entries, like script URLs or
  // function names. This should generally be much smaller in terms of raw
  // bytes. Note however that we can still wrap around this buffer and lose
  // entries - the system is best effort, and the consumer must accomodate the
  // fact that entries from inlineData_ may reference expired data from
  // outOfLineData_
  OutOfLineDataBuffer outOfLineData_;

  // This holds summaries of various values recorded during tracing. Currently
  // this only contains values for function arguments. TODO: Add support for
  // function return values.
  ValueDataBuffer valueData_;

  // This is just an ID that allows the profiler to easily correlate the trace
  // for a given context with the correct thread in the output profile.
  // We're operating on the assumption that there is one JSContext per thread,
  // which should be true enough for our uses in Firefox, but doesn't have to
  // be true everywhere.
  mozilla::baseprofiler::BaseProfilerThreadId threadId_;

  // This is a helper class for writing value data to the valueData_ and
  // outOfLineData_ buffers. It holds pointers to those two buffers and houses
  // all of the logic for writing the value summaries themselves.
  ValueSummaries valueSummaries_;

  // When we encounter an error during tracing, we write one final Error entry
  // and suspend tracing indefinitely. This allows the consumer to get some
  // information about what led up to the error, while preventing any
  // additional future overhead. An alternative to this approach would be to
  // clean up all of our buffers on error, but since the user must have elected
  // to turn on tracing, we assume that they would rather have a greater chance
  // of more information about what led up to the error rather than a greater
  // chance of avoiding a crash due to OOM.
  void handleError(JSContext* cx);

  void writeScriptUrl(ScriptSource* scriptSource);

  // Writes an atom into the outOfLineData_, associating it with the specified
  // id. In practice, `id` comes from an atom id inside a cache in the
  // JSContext which is incremented each time a new atom is registered and
  // cleared when tracing is done.
  bool writeAtom(JSContext* cx, JS::Handle<JSAtom*> atom, uint32_t id);
  bool writeFunctionFrame(JSContext* cx, AbstractFramePtr frame);

  // The below functions read data from the inlineData_ and outOfLineData_ ring
  // buffers into structs to be consumed by clients of the
  // JS_TracerSnapshotTrace API.
  bool readFunctionFrame(JS::ExecutionTrace::EventKind kind,
                         JS::ExecutionTrace::TracedEvent& event);
  bool readLabel(JS::ExecutionTrace::EventKind kind,
                 JS::ExecutionTrace::TracedEvent& event,
                 TracingScratchBuffer& scratchBuffer,
                 mozilla::Vector<char>& stringBuffer);
  bool readInlineEntry(mozilla::Vector<JS::ExecutionTrace::TracedEvent>& events,
                       TracingScratchBuffer& scratchBuffer,
                       mozilla::Vector<char>& stringBuffer);
  bool readOutOfLineEntry(
      mozilla::HashMap<uint32_t, size_t>& scriptUrls,
      mozilla::HashMap<uint32_t, size_t>& atoms,
      mozilla::Vector<JS::ExecutionTrace::ShapeSummary>& shapes,
      TracingScratchBuffer& scratchBuffer, mozilla::Vector<char>& stringBuffer);
  bool readInlineEntries(
      mozilla::Vector<JS::ExecutionTrace::TracedEvent>& events,
      TracingScratchBuffer& scratchBuffer, mozilla::Vector<char>& stringBuffer);
  bool readOutOfLineEntries(
      mozilla::HashMap<uint32_t, size_t>& scriptUrls,
      mozilla::HashMap<uint32_t, size_t>& atoms,
      mozilla::Vector<JS::ExecutionTrace::ShapeSummary>& shapes,
      TracingScratchBuffer& scratchBuffer, mozilla::Vector<char>& stringBuffer);

 public:
  ExecutionTracer() : bufferLock_(mutexid::ExecutionTracerInstanceLock) {}

  ~ExecutionTracer() {
    LockGuard<Mutex> guard(globalInstanceLock);

    globalInstances.eraseIfEqual(this);
  }

  mozilla::baseprofiler::BaseProfilerThreadId threadId() const {
    return threadId_;
  }

  bool init() {
    LockGuard<Mutex> guard(globalInstanceLock);
    LockGuard<Mutex> guard2(bufferLock_);

    threadId_ = mozilla::baseprofiler::profiler_current_thread_id();

    if (!inlineData_.init()) {
      return false;
    }
    if (!outOfLineData_.init()) {
      return false;
    }
    if (!valueData_.init()) {
      return false;
    }

    if (!globalInstances.append(this)) {
      return false;
    }

    valueSummaries_.init(&valueData_, &outOfLineData_);

    return true;
  }

  void onEnterFrame(JSContext* cx, AbstractFramePtr frame);
  void onLeaveFrame(JSContext* cx, AbstractFramePtr frame);

  template <typename CharType, JS::TracerStringEncoding Encoding>
  void onEnterLabel(const CharType* eventType);
  template <typename CharType, JS::TracerStringEncoding Encoding>
  void onLeaveLabel(const CharType* eventType);

  // Reads the execution trace from the underlying ring buffers and outputs it
  // into a native struct. For more information about this struct, see
  // js/public/Debug.h
  bool getNativeTrace(JS::ExecutionTrace::TracedJSContext& context,
                      TracingScratchBuffer& scratchBuffer,
                      mozilla::Vector<char>& stringBuffer);

  // Calls getNativeTrace for every JSContext in the process, populating the
  // provided ExecutionTrace with the result.
  static bool getNativeTraceForAllContexts(JS::ExecutionTrace& trace);
};

}  // namespace js

#endif /* debugger_ExecutionTracer_h */

Messung V0.5 in Prozent
C=81 H=100 G=90

¤ Dauer der Verarbeitung: 0.16 Sekunden  (vorverarbeitet am  2026-09-29) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1126438
#Domains=1897691