Eine aufbereitete Darstellung der Quelle

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

Benutzer

Quelle  GeckoProfiler.cpp

  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/. */


#include "vm/GeckoProfiler-inl.h"

#include "mozilla/DebugOnly.h"
#include "mozilla/Sprintf.h"

#include "gc/GC.h"
#include "gc/PublicIterators.h"
#include "jit/BaselineJIT.h"
#include "jit/JitcodeMap.h"
#include "jit/JitRuntime.h"
#include "jit/JSJitFrameIter.h"
#include "jit/PerfSpewer.h"
#include "js/experimental/SourceHook.h"
#include "vm/FrameIter.h"  // js::OnlyJSJitFrameIter
#include "vm/JitActivation.h"
#include "vm/JSScript.h"
#include "vm/MutexIDs.h"

#include "gc/Marking-inl.h"
#include "jit/JSJitFrameIter-inl.h"

using namespace js;
using mozilla::Utf8Unit;

GeckoProfilerThread::GeckoProfilerThread()
    : profilingStack_(nullptr), profilingStackIfEnabled_(nullptr) {}

GeckoProfilerRuntime::GeckoProfilerRuntime(JSRuntime* rt)
    : rt(rt),
      scriptSources_(mutexid::GeckoProfilerScriptSources),
      slowAssertions(false),
      enabled_(false),
      eventMarker_(nullptr),
      intervalMarker_(nullptr),
      flowMarker_(nullptr),
      terminatingFlowMarker_(nullptr) {
  MOZ_ASSERT(rt != nullptr);
}

void GeckoProfilerThread::setProfilingStack(ProfilingStack* profilingStack,
                                            bool enabled) {
  profilingStack_ = profilingStack;
  profilingStackIfEnabled_ = enabled ? profilingStack : nullptr;
}

void GeckoProfilerRuntime::setEventMarker(void (*fn)(mozilla::MarkerCategory,
                                                     const char*,
                                                     const char*)) {
  eventMarker_ = fn;
}

void GeckoProfilerRuntime::setIntervalMarker(void (*fn)(
    mozilla::MarkerCategory, const char*, mozilla::TimeStamp, const char*)) {
  intervalMarker_ = fn;
}

void GeckoProfilerRuntime::setFlowMarker(void (*fn)(mozilla::MarkerCategory,
                                                    const char*, uint64_t)) {
  flowMarker_ = fn;
}

void GeckoProfilerRuntime::setTerminatingFlowMarker(
    void (*fn)(mozilla::MarkerCategory, const char*, uint64_t)) {
  terminatingFlowMarker_ = fn;
}

// Get a pointer to the top-most profiling frame, given the exit frame pointer.
static jit::JitFrameLayout* GetTopProfilingJitFrame(jit::JitActivation* act) {
  // If there is no exit frame set, just return.
  if (!act->hasExitFP()) {
    return nullptr;
  }

  // Skip wasm frames that might be in the way.
  OnlyJSJitFrameIter iter(act);
  if (iter.done()) {
    return nullptr;
  }

  // Skip if the activation has no JS frames. This can happen if there's only a
  // TrampolineNative frame because these are skipped by the profiling frame
  // iterator.
  jit::JSJitProfilingFrameIterator jitIter(
      (jit::CommonFrameLayout*)iter.frame().fp());
  if (jitIter.done()) {
    return nullptr;
  }

  return jitIter.framePtr();
}

void GeckoProfilerRuntime::enable(bool enabled) {
  JSContext* cx = rt->mainContextFromAnyThread();
  MOZ_ASSERT(cx->geckoProfiler().infraInstalled());

  if (enabled_ == enabled) {
    return;
  }

  /*
   * Ensure all future generated code will be instrumented, or that all
   * currently instrumented code is discarded
   */

  ReleaseAllJITCode(rt->gcContext());

  // This function is called when the Gecko profiler makes a new Sampler
  // (and thus, a new circular buffer). Set all current entries in the
  // JitcodeGlobalTable as expired and reset the buffer range start.
  if (rt->hasJitRuntime() && rt->jitRuntime()->hasJitcodeGlobalTable()) {
    rt->jitRuntime()->getJitcodeGlobalTable()->setAllEntriesAsExpired();
  }
  rt->setProfilerSampleBufferRangeStart(0);

  // Ensure that lastProfilingFrame is null for the main thread.
  if (cx->jitActivation) {
    cx->jitActivation->setLastProfilingFrame(nullptr);
    cx->jitActivation->setLastProfilingCallSite(nullptr);
  }

  // Enable/disable JIT code info collection for the Gecko Profiler.
  jit::ResetPerfSpewer(enabled);

  enabled_ = enabled;

  scriptSources_.writeLock()->clear();

  /* Toggle Gecko Profiler-related jumps on baseline jitcode.
   * The call to |ReleaseAllJITCode| above will release most baseline jitcode,
   * but not jitcode for scripts with active frames on the stack.  These scripts
   * need to have their profiler state toggled so they behave properly.
   */

  jit::ToggleBaselineProfiling(cx, enabled);

  // Update lastProfilingFrame to point to the top-most JS jit-frame currently
  // on stack.
  if (cx->jitActivation) {
    // Walk through all activations, and set their lastProfilingFrame
    // appropriately.
    if (enabled) {
      jit::JitActivation* jitActivation = cx->jitActivation;
      while (jitActivation) {
        auto* lastProfilingFrame = GetTopProfilingJitFrame(jitActivation);
        jitActivation->setLastProfilingFrame(lastProfilingFrame);
        jitActivation->setLastProfilingCallSite(nullptr);
        jitActivation = jitActivation->prevJitActivation();
      }
    } else {
      jit::JitActivation* jitActivation = cx->jitActivation;
      while (jitActivation) {
        jitActivation->setLastProfilingFrame(nullptr);
        jitActivation->setLastProfilingCallSite(nullptr);
        jitActivation = jitActivation->prevJitActivation();
      }
    }
  }

  // WebAssembly code does not need to be released, but profiling string
  // labels have to be generated so that they are available during async
  // profiling stack iteration.
  for (RealmsIter r(rt); !r.done(); r.next()) {
    r->wasm.ensureProfilingLabels(enabled);
  }

#ifdef JS_STRUCTURED_SPEW
  // Enable the structured spewer if the environment variable is set.
  if (enabled) {
    cx->spewer().enableSpewing();
  } else {
    cx->spewer().disableSpewing();
  }
#endif
}

/* Lookup the string for the function/script, creating one if necessary */
const char* GeckoProfilerRuntime::profileString(JSContext* cx,
                                                BaseScript* script) {
  JS::Zone* zone = script->zone();
  if (!zone->profilerStrings) {
    auto map = cx->make_unique<JS::WeakCache<ProfileStringMap>>(zone);
    if (!map) {
      return nullptr;
    }
    zone->profilerStrings = std::move(map);
  }

  ProfileStringMap& map = zone->profilerStrings->get();
  ProfileStringMap::AddPtr ptr = map.lookupForAdd(script);

  if (!ptr) {
    UniqueChars str = allocProfileString(cx, script);
    if (!str) {
      return nullptr;
    }
    MOZ_ASSERT(script->hasBytecode());
    if (!map.add(ptr, script, std::move(str))) {
      ReportOutOfMemory(cx);
      return nullptr;
    }
  }

  return ptr->value().get();
}

void GeckoProfilerRuntime::markEvent(const char* event, const char* details,
                                     JS::ProfilingCategoryPair jsPair) {
  MOZ_ASSERT(enabled());
  if (eventMarker_) {
    JS::AutoSuppressGCAnalysis nogc;
    mozilla::MarkerCategory category(
        static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair));
    eventMarker_(category, event, details);
  }
}

void GeckoProfilerRuntime::markInterval(const char* event,
                                        mozilla::TimeStamp start,
                                        const char* details,
                                        JS::ProfilingCategoryPair jsPair) {
  MOZ_ASSERT(enabled());
  if (intervalMarker_) {
    JS::AutoSuppressGCAnalysis nogc;
    mozilla::MarkerCategory category(
        static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair));
    intervalMarker_(category, event, start, details);
  }
}

void GeckoProfilerRuntime::markFlow(const char* markerName, uint64_t flowId,
                                    JS::ProfilingCategoryPair jsPair) {
  MOZ_ASSERT(enabled());
  if (flowMarker_) {
    JS::AutoSuppressGCAnalysis nogc;
    mozilla::MarkerCategory category(
        static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair));
    flowMarker_(category, markerName, flowId);
  }
}

void GeckoProfilerRuntime::markTerminatingFlow(
    const char* markerName, uint64_t flowId, JS::ProfilingCategoryPair jsPair) {
  MOZ_ASSERT(enabled());
  if (terminatingFlowMarker_) {
    JS::AutoSuppressGCAnalysis nogc;
    mozilla::MarkerCategory category(
        static_cast<mozilla::baseprofiler::ProfilingCategoryPair>(jsPair));
    terminatingFlowMarker_(category, markerName, flowId);
  }
}

bool GeckoProfilerThread::enter(JSContext* cx, JSScript* script) {
  const char* dynamicString =
      cx->runtime()->geckoProfiler().profileString(cx, script);
  if (dynamicString == nullptr) {
    return false;
  }

  if (!cx->runtime()->geckoProfiler().insertScriptSource(
          script->scriptSource())) {
    ReportOutOfMemory(cx);
    return false;
  }

#ifdef DEBUG
  // In debug builds, assert the JS profiling stack frames already on the
  // stack have a non-null pc. Only look at the top frames to avoid quadratic
  // behavior.
  uint32_t sp = profilingStack_->stackPointer;
  if (sp > 0 && sp - 1 < profilingStack_->stackCapacity()) {
    size_t start = (sp > 4) ? sp - 4 : 0;
    for (size_t i = start; i < sp - 1; i++) {
      MOZ_ASSERT_IF(profilingStack_->frames[i].isJsFrame(),
                    profilingStack_->frames[i].pc());
    }
  }
#endif

  profilingStack_->pushJsFrame(
      "", dynamicString, script, script->code(),
      script->realm()->creationOptions().profilerRealmID(),
      script->scriptSource()->id());
  return true;
}

void GeckoProfilerThread::exit(JSContext* cx, JSScript* script) {
  profilingStack_->pop();

#ifdef DEBUG
  /* Sanity check to make sure push/pop balanced */
  uint32_t sp = profilingStack_->stackPointer;
  if (sp < profilingStack_->stackCapacity()) {
    JSRuntime* rt = script->runtimeFromMainThread();
    const char* dynamicString = rt->geckoProfiler().profileString(cx, script);
    /* Can't fail lookup because we should already be in the set */
    MOZ_ASSERT(dynamicString);

    // Bug 822041
    if (!profilingStack_->frames[sp].isJsFrame()) {
      fprintf(stderr, "--- ABOUT TO FAIL ASSERTION ---\n");
      fprintf(stderr, " frames=%p size=%u/%u\n", (void*)profilingStack_->frames,
              uint32_t(profilingStack_->stackPointer),
              profilingStack_->stackCapacity());
      for (int32_t i = sp; i >= 0; i--) {
        ProfilingStackFrame& frame = profilingStack_->frames[i];
        if (frame.isJsFrame()) {
          fprintf(stderr, "  [%d] JS %s\n", i, frame.dynamicString());
        } else {
          fprintf(stderr, "  [%d] Label %s\n", i, frame.dynamicString());
        }
      }
    }

    ProfilingStackFrame& frame = profilingStack_->frames[sp];
    MOZ_ASSERT(frame.isJsFrame());
    MOZ_ASSERT(frame.script() == script);
    MOZ_ASSERT(strcmp((const char*)frame.dynamicString(), dynamicString) == 0);
  }
#endif
}

/*
 * Serializes the script/function pair into a "descriptive string" which is
 * allowed to fail. This function cannot trigger a GC because it could finalize
 * some scripts, resize the hash table of profile strings, and invalidate the
 * AddPtr held while invoking allocProfileString.
 */

/* static */
UniqueChars GeckoProfilerRuntime::allocProfileString(JSContext* cx,
                                                     BaseScript* script) {
  // Note: this profiler string is regexp-matched by
  // profiler code. Most recently at
  // https://github.com/firefox-devtools/profiler/blob/245b1a400c5c368ccc13641d0335398bafa0e870/src/profile-logic/process-profile.js#L520-L525

  // If the script has a function, try calculating its name.
  JSAtom* name = nullptr;
  size_t nameLength = 0;
  JSFunction* func = script->function();
  if (func && func->fullDisplayAtom()) {
    name = func->fullDisplayAtom();
    nameLength = JS::GetDeflatedUTF8StringLength(name);
  }

  // Calculate filename length. We cap this to a reasonable limit to avoid
  // performance impact of strlen/alloc/memcpy.
  constexpr size_t MaxFilenameLength = 200;
  const char* filenameStr = script->filename() ? script->filename() : "(null)";
  size_t filenameLength = js_strnlen(filenameStr, MaxFilenameLength);

  // Calculate line + column length.
  bool hasLineAndColumn = false;
  size_t lineAndColumnLength = 0;
  char lineAndColumnStr[30];
  if (name || script->isFunction() || script->isForEval()) {
    lineAndColumnLength =
        SprintfLiteral(lineAndColumnStr, "%u:%u", script->lineno(),
                       script->column().oneOriginValue());
    hasLineAndColumn = true;
  }

  // Full profile string for scripts with functions is:
  //      FuncName (FileName:Lineno:Column)
  // Full profile string for scripts without functions is:
  //      FileName:Lineno:Column
  // Full profile string for scripts without functions and without lines is:
  //      FileName

  // Calculate full string length.
  size_t fullLength = 0;
  if (name) {
    MOZ_ASSERT(hasLineAndColumn);
    fullLength = nameLength + 2 + filenameLength + 1 + lineAndColumnLength + 1;
  } else if (hasLineAndColumn) {
    fullLength = filenameLength + 1 + lineAndColumnLength;
  } else {
    fullLength = filenameLength;
  }

  // Allocate string.
  UniqueChars str(cx->pod_malloc<char>(fullLength + 1));
  if (!str) {
    return nullptr;
  }

  size_t cur = 0;

  // Fill string with function name if needed.
  if (name) {
    mozilla::DebugOnly<size_t> written = JS::DeflateStringToUTF8Buffer(
        name, mozilla::Span(str.get() + cur, nameLength));
    MOZ_ASSERT(written == nameLength);
    cur += nameLength;
    str[cur++] = ' ';
    str[cur++] = '(';
  }

  // Fill string with filename chars.
  memcpy(str.get() + cur, filenameStr, filenameLength);
  cur += filenameLength;

  // Fill line + column chars.
  if (hasLineAndColumn) {
    str[cur++] = ':';
    memcpy(str.get() + cur, lineAndColumnStr, lineAndColumnLength);
    cur += lineAndColumnLength;
  }

  // Terminal ')' if necessary.
  if (name) {
    str[cur++] = ')';
  }

  MOZ_ASSERT(cur == fullLength);
  str[cur] = 0;

  return str;
}

void GeckoProfilerThread::trace(JSTracer* trc) {
  if (profilingStack_) {
    size_t size = profilingStack_->stackSize();
    for (size_t i = 0; i < size; i++) {
      profilingStack_->frames[i].trace(trc);
    }
  }
}

size_t GeckoProfilerRuntime::stringsCount() {
  size_t count = 0;
  for (AllZonesIter zone(rt); !zone.done(); zone.next()) {
    if (zone->profilerStrings) {
      count += zone->profilerStrings->get().count();
    }
  }
  return count;
}

void GeckoProfilerRuntime::stringsReset() {
  for (AllZonesIter zone(rt); !zone.done(); zone.next()) {
    if (zone->profilerStrings) {
      zone->profilerStrings->get().clear();
    }
  }
}

// Get all script sources as a list of ProfilerJSSourceData.
js::ProfilerJSSources GeckoProfilerRuntime::getProfilerScriptSources(
    bool gatherSourceText) {
  js::ProfilerJSSources result;

  auto guard = scriptSources_.readLock();
  for (auto iter = guard->iter(); !iter.done(); iter.next()) {
    const RefPtr<ScriptSource>& scriptSource = iter.get();
    MOZ_ASSERT(scriptSource);

    bool hasSourceText;
    bool retrievableSource;
    ScriptSource::getSourceProperties(scriptSource, &hasSourceText,
                                      &retrievableSource);

    uint32_t sourceId = scriptSource->id();

    // Get filename for all source types. Create single copy to be moved.
    const char* filename = scriptSource->filename();
    size_t filenameLen = 0;
    JS::UniqueChars filenameCopy;
    if (filename) {
      filenameLen = strlen(filename);
      filenameCopy.reset(static_cast<char*>(js_malloc(filenameLen + 1)));
      if (filenameCopy) {
        strcpy(filenameCopy.get(), filename);
      }
    }

    // Get the start line and column for inline scripts.
    uint32_t startLine = scriptSource->startLine();
    uint32_t startColumn = scriptSource->startColumn().oneOriginValue();

    // Get sourceMapURL for all source types. Create single copy to be moved.
    const char16_t* sourceMapURL = nullptr;
    size_t sourceMapURLLen = 0;
    JS::UniqueTwoByteChars sourceMapURLCopy;
    if (scriptSource->hasSourceMapURL()) {
      sourceMapURL = scriptSource->sourceMapURL();
      sourceMapURLLen = js_strlen(sourceMapURL);
      sourceMapURLCopy.reset(static_cast<char16_t*>(
          js_malloc((sourceMapURLLen + 1) * sizeof(char16_t))));
      if (sourceMapURLCopy) {
        js_memcpy(sourceMapURLCopy.get(), sourceMapURL,
                  sourceMapURLLen * sizeof(char16_t));
        sourceMapURLCopy[sourceMapURLLen] = 0;
      } else {
        sourceMapURLLen = 0;
      }
    }

    // If not gathering source text, just store metadata
    if (!gatherSourceText) {
      (void)result.append(ProfilerJSSourceData(
          sourceId, std::move(filenameCopy), filenameLen, startLine,
          startColumn, std::move(sourceMapURLCopy), sourceMapURLLen));
      continue;
    }

    if (retrievableSource) {
      (void)result.append(ProfilerJSSourceData::CreateRetrievableFile(
          sourceId, std::move(filenameCopy), filenameLen, startLine,
          startColumn, std::move(sourceMapURLCopy), sourceMapURLLen));
      continue;
    }

    if (!hasSourceText) {
      (void)result.append(ProfilerJSSourceData(
          sourceId, std::move(filenameCopy), filenameLen, startLine,
          startColumn, std::move(sourceMapURLCopy), sourceMapURLLen));
      continue;
    }

    size_t sourceLength = scriptSource->length();
    if (sourceLength == 0) {
      (void)result.append(ProfilerJSSourceData(
          sourceId, JS::UniqueTwoByteChars(), 0, std::move(filenameCopy),
          filenameLen, startLine, startColumn, std::move(sourceMapURLCopy),
          sourceMapURLLen));
      continue;
    }

    SubstringCharsResult sourceResult(JS::UniqueChars(nullptr));
    size_t charsLength = 0;

    if (scriptSource->shouldUnwrapEventHandlerBody()) {
      sourceResult = scriptSource->functionBodyStringChars(&charsLength);

      if (charsLength == 0) {
        (void)result.append(ProfilerJSSourceData(
            sourceId, JS::UniqueTwoByteChars(), 0, std::move(filenameCopy),
            filenameLen, startLine, startColumn, std::move(sourceMapURLCopy),
            sourceMapURLLen));
        continue;
      }
    } else {
      sourceResult = scriptSource->substringChars(0, sourceLength);
      charsLength = sourceLength;
    }

    // Convert SubstringCharsResult to ProfilerJSSourceData.
    // Note: The returned buffers are NOT null-terminated. The length is
    // tracked separately in charsLength and passed to ProfilerJSSourceData.
    if (sourceResult.is<JS::UniqueChars>()) {
      auto& utf8Chars = sourceResult.as<JS::UniqueChars>();
      if (!utf8Chars) {
        continue;
      }
      (void)result.append(ProfilerJSSourceData(
          sourceId, std::move(utf8Chars), charsLength, std::move(filenameCopy),
          filenameLen, startLine, startColumn, std::move(sourceMapURLCopy),
          sourceMapURLLen));
    } else {
      auto& utf16Chars = sourceResult.as<JS::UniqueTwoByteChars>();
      if (!utf16Chars) {
        continue;
      }
      (void)result.append(ProfilerJSSourceData(
          sourceId, std::move(utf16Chars), charsLength, std::move(filenameCopy),
          filenameLen, startLine, startColumn, std::move(sourceMapURLCopy),
          sourceMapURLLen));
    }
  }

  return result;
}

void ProfilingStackFrame::trace(JSTracer* trc) {
  if (isJsFrame()) {
    JSScript* s = rawScript();
    TraceRoot(trc, &s, "ProfilingStackFrame script");
    spOrScript = s;
  }
}

GeckoProfilerBaselineOSRMarker::GeckoProfilerBaselineOSRMarker(
    JSContext* cx, bool hasProfilerFrame)
    : profiler(&cx->geckoProfiler()) {
  if (!hasProfilerFrame || !cx->runtime()->geckoProfiler().enabled()) {
    profiler = nullptr;
    return;
  }

  uint32_t sp = profiler->profilingStack_->stackPointer;
  if (sp >= profiler->profilingStack_->stackCapacity()) {
    profiler = nullptr;
    return;
  }

  spBefore_ = sp;
  if (sp == 0) {
    return;
  }

  ProfilingStackFrame& frame = profiler->profilingStack_->frames[sp - 1];
  MOZ_ASSERT(!frame.isOSRFrame());
  frame.setIsOSRFrame(true);
}

GeckoProfilerBaselineOSRMarker::~GeckoProfilerBaselineOSRMarker() {
  if (profiler == nullptr) {
    return;
  }

  uint32_t sp = profiler->stackPointer();
  MOZ_ASSERT(spBefore_ == sp);
  if (sp == 0) {
    return;
  }

  ProfilingStackFrame& frame = profiler->stack()[sp - 1];
  MOZ_ASSERT(frame.isOSRFrame());
  frame.setIsOSRFrame(false);
}

JS_PUBLIC_API JSScript* ProfilingStackFrame::script() const {
  MOZ_ASSERT(isJsFrame());
  auto* script = reinterpret_cast<JSScript*>(spOrScript.operator void*());
  if (!script) {
    return nullptr;
  }

  // If profiling is suppressed then we can't trust the script pointers to be
  // valid as they could be in the process of being moved by a compacting GC
  // (although it's still OK to get the runtime from them).
  JSContext* cx = script->runtimeFromAnyThread()->mainContextFromAnyThread();
  if (!cx->isProfilerSamplingEnabled()) {
    return nullptr;
  }

  MOZ_ASSERT(!IsForwarded(script));
  return script;
}

JS_PUBLIC_API JSFunction* ProfilingStackFrame::function() const {
  JSScript* script = this->script();
  return script ? script->function() : nullptr;
}

JS_PUBLIC_API jsbytecode* ProfilingStackFrame::pc() const {
  MOZ_ASSERT(isJsFrame());
  if (pcOffsetIfJS_ == NullPCOffset) {
    return nullptr;
  }

  JSScript* script = this->script();
  return script ? script->offsetToPC(pcOffsetIfJS_) : nullptr;
}

/* static */
int32_t ProfilingStackFrame::pcToOffset(JSScript* aScript, jsbytecode* aPc) {
  return aPc ? aScript->pcToOffset(aPc) : NullPCOffset;
}

void ProfilingStackFrame::setPC(jsbytecode* pc) {
  MOZ_ASSERT(isJsFrame());
  JSScript* script = this->script();
  MOZ_ASSERT(
      script);  // This should not be called while profiling is suppressed.
  pcOffsetIfJS_ = pcToOffset(script, pc);
}

JS_PUBLIC_API uint32_t ProfilingStackFrame::sourceId() const {
  return sourceId_;
}

JS_PUBLIC_API void js::SetContextProfilingStack(
    JSContext* cx, ProfilingStack* profilingStack) {
  cx->geckoProfiler().setProfilingStack(
      profilingStack, cx->runtime()->geckoProfiler().enabled());
}

JS_PUBLIC_API void js::EnableContextProfilingStack(JSContext* cx,
                                                   bool enabled) {
  cx->geckoProfiler().enable(enabled);
  cx->runtime()->geckoProfiler().enable(enabled);
}

JS_PUBLIC_API void js::RegisterContextProfilerMarkers(
    JSContext* cx,
    void (*eventMarker)(mozilla::MarkerCategory, const char*, const char*),
    void (*intervalMarker)(mozilla::MarkerCategory, const char*,
                           mozilla::TimeStamp, const char*),
    void (*flowMarker)(mozilla::MarkerCategory, const char*, uint64_t),
    void (*terminatingFlowMarker)(mozilla::MarkerCategory, const char*,
                                  uint64_t)) {
  MOZ_ASSERT(cx->runtime()->geckoProfiler().enabled());
  cx->runtime()->geckoProfiler().setEventMarker(eventMarker);
  cx->runtime()->geckoProfiler().setIntervalMarker(intervalMarker);
  cx->runtime()->geckoProfiler().setFlowMarker(flowMarker);
  cx->runtime()->geckoProfiler().setTerminatingFlowMarker(
      terminatingFlowMarker);
}

JS_PUBLIC_API js::ProfilerJSSources js::GetProfilerScriptSources(
    JSRuntime* rt, bool gatherSourceText) {
  return rt->geckoProfiler().getProfilerScriptSources(gatherSourceText);
}

JS_PUBLIC_API ProfilerJSSourceData
js::RetrieveProfilerSourceContent(JSContext* cx, const char* filename) {
  MOZ_ASSERT(filename && strlen(filename));
  if (!cx) {
    return ProfilerJSSourceData();  // Return unavailable
  }

  // Check if source hook is available
  if (!cx->runtime()->sourceHook.ref()) {
    return ProfilerJSSourceData();  // Return unavailable
  }

  size_t sourceLength = 0;
  char* utf8Source = nullptr;

  bool loadSuccess = cx->runtime()->sourceHook->load(
      cx, filename, nullptr, &utf8Source, &sourceLength);

  if (!loadSuccess) {
    // Clear the pending exception that have been set by the source hook.
    JS_ClearPendingException(cx);
    return ProfilerJSSourceData();  // Return unavailable
  }

  if (utf8Source) {
    return ProfilerJSSourceData(JS::UniqueChars(utf8Source), sourceLength);
  }

  // Hook returned success but no source data. Return unavailable.
  return ProfilerJSSourceData();
}

AutoSuppressProfilerSampling::AutoSuppressProfilerSampling(JSContext* cx)
    : cx_(cx), previouslyEnabled_(cx->isProfilerSamplingEnabled()) {
  if (previouslyEnabled_) {
    cx_->disableProfilerSampling();
  }
}

AutoSuppressProfilerSampling::~AutoSuppressProfilerSampling() {
  if (previouslyEnabled_) {
    cx_->enableProfilerSampling();
  }
}

namespace JS {

// clang-format off

// ProfilingSubcategory_X:
// One enum for each category X, listing that category's subcategories. This
// allows the sProfilingCategoryInfo macro construction below to look up a
// per-category index for a subcategory.
#define SUBCATEGORY_ENUMS_BEGIN_CATEGORY(name, labelAsString, color) \
  enum class ProfilingSubcategory_##name : uint32_t {
#define SUBCATEGORY_ENUMS_SUBCATEGORY(category, name, labelAsString) \
    name,
#define SUBCATEGORY_ENUMS_END_CATEGORY \
  };
MOZ_PROFILING_CATEGORY_LIST(SUBCATEGORY_ENUMS_BEGIN_CATEGORY,
                            SUBCATEGORY_ENUMS_SUBCATEGORY,
                            SUBCATEGORY_ENUMS_END_CATEGORY)
#undef SUBCATEGORY_ENUMS_BEGIN_CATEGORY
#undef SUBCATEGORY_ENUMS_SUBCATEGORY
#undef SUBCATEGORY_ENUMS_END_CATEGORY

// sProfilingCategoryPairInfo:
// A list of ProfilingCategoryPairInfos with the same order as
// ProfilingCategoryPair, which can be used to map a ProfilingCategoryPair to
// its information.
#define CATEGORY_INFO_BEGIN_CATEGORY(name, labelAsString, color)
#define CATEGORY_INFO_SUBCATEGORY(category, name, labelAsString) \
  {ProfilingCategory::category,                                  \
   uint32_t(ProfilingSubcategory_##category::name), labelAsString},
#define CATEGORY_INFO_END_CATEGORY
const ProfilingCategoryPairInfo sProfilingCategoryPairInfo[] = {
  MOZ_PROFILING_CATEGORY_LIST(CATEGORY_INFO_BEGIN_CATEGORY,
                              CATEGORY_INFO_SUBCATEGORY,
                              CATEGORY_INFO_END_CATEGORY)
};
#undef CATEGORY_INFO_BEGIN_CATEGORY
#undef CATEGORY_INFO_SUBCATEGORY
#undef CATEGORY_INFO_END_CATEGORY

// clang-format on

JS_PUBLIC_API const ProfilingCategoryPairInfo& GetProfilingCategoryPairInfo(
    ProfilingCategoryPair aCategoryPair) {
  static_assert(
      std::size(sProfilingCategoryPairInfo) ==
          uint32_t(ProfilingCategoryPair::COUNT),
      "sProfilingCategoryPairInfo and ProfilingCategory need to have the "
      "same order and the same length");

  uint32_t categoryPairIndex = uint32_t(aCategoryPair);
  MOZ_RELEASE_ASSERT(categoryPairIndex <=
                     uint32_t(ProfilingCategoryPair::LAST));
  return sProfilingCategoryPairInfo[categoryPairIndex];
}

}  // namespace JS

Messung V0.5 in Prozent
C=89 H=95 G=91

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© 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=141584
#Domains=752002