commit 94f70bc8859d5878629e0bbd8ac81cf3a19ad5bd
parent 18074099dc9a0a4c4247bf4183961db7e4ed2693
Author: Nazım Can Altınova <canaltinova@gmail.com>
Date: Tue, 21 Oct 2025 23:59:08 +0000
Bug 1959977 - Collect sourceId of JIT frames r=iain,mstange,profiler-reviewers
Thanks to the patches from Iain before, we now have ScriptSource inside
the JitCodeGlobalEntries. So we directly retrieve the sourceIds from
there during serialization (because we know that they are kept alive
until then).
And serialization into the profile format is done the same way as index
into sources table, by adding a `[sourceIndex]` at the end of the
location.
Differential Revision: https://phabricator.services.mozilla.com/D259255
Diffstat:
8 files changed, 107 insertions(+), 46 deletions(-)
diff --git a/js/public/ProfilingFrameIterator.h b/js/public/ProfilingFrameIterator.h
@@ -238,10 +238,12 @@ class MOZ_STACK_CLASS ProfiledFrameHandle {
void* addr_;
void* canonicalAddr_;
const char* label_;
+ uint32_t sourceId_;
uint32_t depth_;
ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
- void* addr, const char* label, uint32_t depth);
+ void* addr, const char* label, uint32_t sourceId,
+ uint32_t depth);
public:
const char* label() const { return label_; }
@@ -251,6 +253,8 @@ class MOZ_STACK_CLASS ProfiledFrameHandle {
JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const;
JS_PUBLIC_API uint64_t realmID() const;
+
+ JS_PUBLIC_API uint32_t sourceId() const;
};
class ProfiledFrameRange {
@@ -292,6 +296,7 @@ class ProfiledFrameRange {
js::jit::JitcodeGlobalEntry* entry_;
// Assume maximum inlining depth is <64
const char* labels_[64];
+ uint32_t sourceIds_[64];
uint32_t depth_;
};
diff --git a/js/src/jit/JitcodeMap.cpp b/js/src/jit/JitcodeMap.cpp
@@ -47,7 +47,8 @@ void* IonEntry::canonicalNativeAddrFor(void* ptr) const {
return (void*)(((uint8_t*)nativeStartAddr()) + region.nativeOffset());
}
-uint32_t IonEntry::callStackAtAddr(void* ptr, const char** results,
+uint32_t IonEntry::callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
MOZ_ASSERT(maxResults >= 1);
@@ -63,7 +64,9 @@ uint32_t IonEntry::callStackAtAddr(void* ptr, const char** results,
locationIter.readNext(&scriptIdx, &pcOffset);
MOZ_ASSERT(getStr(scriptIdx));
- results[count++] = getStr(scriptIdx);
+ labelResults[count] = getStr(scriptIdx);
+ sourceIdResults[count] = getScriptSource(scriptIdx).scriptSource->id();
+ count++;
if (count >= maxResults) {
break;
}
@@ -95,10 +98,12 @@ static IonEntry& IonEntryForIonIC(JSRuntime* rt, const IonICEntry* icEntry) {
void* IonICEntry::canonicalNativeAddrFor(void* ptr) const { return ptr; }
uint32_t IonICEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
- const char** results,
+ const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
const IonEntry& entry = IonEntryForIonIC(rt, this);
- return entry.callStackAtAddr(rejoinAddr(), results, maxResults);
+ return entry.callStackAtAddr(rejoinAddr(), labelResults, sourceIdResults,
+ maxResults);
}
uint64_t IonICEntry::realmID(JSRuntime* rt) const {
@@ -112,12 +117,14 @@ void* BaselineEntry::canonicalNativeAddrFor(void* ptr) const {
return ptr;
}
-uint32_t BaselineEntry::callStackAtAddr(void* ptr, const char** results,
+uint32_t BaselineEntry::callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
MOZ_ASSERT(containsPointer(ptr));
MOZ_ASSERT(maxResults >= 1);
- results[0] = str();
+ labelResults[0] = str();
+ sourceIdResults[0] = scriptSource().scriptSource->id();
return 1;
}
@@ -126,7 +133,8 @@ void* BaselineInterpreterEntry::canonicalNativeAddrFor(void* ptr) const {
}
uint32_t BaselineInterpreterEntry::callStackAtAddr(void* ptr,
- const char** results,
+ const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
MOZ_CRASH("shouldn't be called for BaselineInterpreter entries");
}
@@ -149,11 +157,13 @@ bool RealmIndependentSharedEntry::callStackAtAddr(
}
uint32_t RealmIndependentSharedEntry::callStackAtAddr(
- void* ptr, const char** results, uint32_t maxResults) const {
+ void* ptr, const char** labelResults, uint32_t* sourceIdResults,
+ uint32_t maxResults) const {
MOZ_ASSERT(containsPointer(ptr));
MOZ_ASSERT(maxResults >= 1);
- results[0] = str();
+ labelResults[0] = str();
+ sourceIdResults[0] = 0;
return 1;
}
@@ -329,22 +339,28 @@ bool JitcodeGlobalEntry::isJitcodeMarkedFromAnyThread(JSRuntime* rt) {
}
uint32_t JitcodeGlobalEntry::callStackAtAddr(JSRuntime* rt, void* ptr,
- const char** results,
+ const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
switch (kind()) {
case Kind::Ion:
- return asIon().callStackAtAddr(ptr, results, maxResults);
+ return asIon().callStackAtAddr(ptr, labelResults, sourceIdResults,
+ maxResults);
case Kind::IonIC:
- return asIonIC().callStackAtAddr(rt, ptr, results, maxResults);
+ return asIonIC().callStackAtAddr(rt, ptr, labelResults, sourceIdResults,
+ maxResults);
case Kind::Baseline:
- return asBaseline().callStackAtAddr(ptr, results, maxResults);
+ return asBaseline().callStackAtAddr(ptr, labelResults, sourceIdResults,
+ maxResults);
case Kind::BaselineInterpreter:
- return asBaselineInterpreter().callStackAtAddr(ptr, results, maxResults);
+ return asBaselineInterpreter().callStackAtAddr(
+ ptr, labelResults, sourceIdResults, maxResults);
case Kind::Dummy:
- return asDummy().callStackAtAddr(rt, ptr, results, maxResults);
+ return asDummy().callStackAtAddr(rt, ptr, labelResults, sourceIdResults,
+ maxResults);
case Kind::RealmIndependentShared:
- return asRealmIndependentShared().callStackAtAddr(ptr, results,
- maxResults);
+ return asRealmIndependentShared().callStackAtAddr(
+ ptr, labelResults, sourceIdResults, maxResults);
}
MOZ_CRASH("Invalid kind");
}
@@ -957,12 +973,13 @@ bool JitcodeIonTable::WriteIonTable(CompactBufferWriter& writer,
JS::ProfiledFrameHandle::ProfiledFrameHandle(JSRuntime* rt,
js::jit::JitcodeGlobalEntry& entry,
void* addr, const char* label,
- uint32_t depth)
+ uint32_t sourceId, uint32_t depth)
: rt_(rt),
entry_(entry),
addr_(addr),
canonicalAddr_(nullptr),
label_(label),
+ sourceId_(sourceId),
depth_(depth) {
if (!canonicalAddr_) {
canonicalAddr_ = entry_.canonicalNativeAddrFor(rt_, addr_);
@@ -987,6 +1004,10 @@ JS_PUBLIC_API uint64_t JS::ProfiledFrameHandle::realmID() const {
return entry_.realmID(rt_);
}
+JS_PUBLIC_API uint32_t JS::ProfiledFrameHandle::sourceId() const {
+ return sourceId_;
+}
+
JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx,
void* addr) {
JSRuntime* rt = cx->runtime();
@@ -997,8 +1018,8 @@ JS_PUBLIC_API JS::ProfiledFrameRange JS::GetProfiledFrames(JSContext* cx,
ProfiledFrameRange result(rt, addr, entry);
if (entry) {
- result.depth_ = entry->callStackAtAddr(rt, addr, result.labels_,
- std::size(result.labels_));
+ result.depth_ = entry->callStackAtAddr(
+ rt, addr, result.labels_, result.sourceIds_, std::size(result.labels_));
}
return result;
}
@@ -1008,5 +1029,6 @@ JS::ProfiledFrameHandle JS::ProfiledFrameRange::Iter::operator*() const {
// and the depth we need to pass to ProfiledFrameHandle goes down.
uint32_t depth = range_.depth_ - 1 - index_;
return ProfiledFrameHandle(range_.rt_, *range_.entry_, range_.addr_,
- range_.labels_[depth], depth);
+ range_.labels_[depth], range_.sourceIds_[depth],
+ depth);
}
diff --git a/js/src/jit/JitcodeMap.h b/js/src/jit/JitcodeMap.h
@@ -195,9 +195,10 @@ class JitcodeGlobalEntry : public JitCodeRange {
void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const;
// Read the inline call stack at a given point in the native code and append
- // into the given results buffer. Innermost (script,pc) pair will be appended
- // first, and outermost appended last.
- uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
+ // into the given results buffer. Innermost script will be appended first, and
+ // outermost appended last.
+ uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
};
@@ -285,7 +286,8 @@ class IonEntry : public JitcodeGlobalEntry {
void* canonicalNativeAddrFor(void* ptr) const;
- uint32_t callStackAtAddr(void* ptr, const char** results,
+ uint32_t callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
uint64_t realmID() const { return realmId_; }
@@ -310,7 +312,8 @@ class IonICEntry : public JitcodeGlobalEntry {
void* canonicalNativeAddrFor(void* ptr) const;
- uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
+ uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
uint64_t realmID(JSRuntime* rt) const;
@@ -340,7 +343,8 @@ class BaselineEntry : public JitcodeGlobalEntry {
void* canonicalNativeAddrFor(void* ptr) const;
- uint32_t callStackAtAddr(void* ptr, const char** results,
+ uint32_t callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
uint64_t realmID() const { return realmId_; }
@@ -367,7 +371,8 @@ class RealmIndependentSharedEntry : public JitcodeGlobalEntry {
[[nodiscard]] bool callStackAtAddr(void* ptr, BytecodeLocationVector& results,
uint32_t* depth) const;
- uint32_t callStackAtAddr(void* ptr, const char** results,
+ uint32_t callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
uint64_t realmID() const;
@@ -382,7 +387,8 @@ class BaselineInterpreterEntry : public JitcodeGlobalEntry {
void* canonicalNativeAddrFor(void* ptr) const;
- uint32_t callStackAtAddr(void* ptr, const char** results,
+ uint32_t callStackAtAddr(void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const;
uint64_t realmID() const;
@@ -400,7 +406,8 @@ class DummyEntry : public JitcodeGlobalEntry {
return nullptr;
}
- uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
+ uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** labelResults,
+ uint32_t* sourceIdResults,
uint32_t maxResults) const {
return 0;
}
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
@@ -764,9 +764,10 @@ uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
// Extract the stack for the entry. Assume maximum inlining depth is <64
const char* labels[64];
+ uint32_t sourceIds[64];
uint32_t depth = entry->callStackAtAddr(cx_->runtime(),
jsJitIter().resumePCinCurrentFrame(),
- labels, std::size(labels));
+ labels, sourceIds, std::size(labels));
MOZ_ASSERT(depth < std::size(labels));
for (uint32_t i = 0; i < depth; i++) {
if (offset + i >= end) {
@@ -774,6 +775,7 @@ uint32_t JS::ProfilingFrameIterator::extractStack(Frame* frames,
}
frames[offset + i] = physicalFrame.value();
frames[offset + i].label = labels[i];
+ frames[offset + i].sourceId = sourceIds[i];
}
return depth;
diff --git a/tools/profiler/core/ProfileBuffer.h b/tools/profiler/core/ProfileBuffer.h
@@ -59,7 +59,9 @@ class ProfileBuffer final {
// for the given thread.
void AddJITInfoForRange(uint64_t aRangeStart, ProfilerThreadId aThreadId,
JSContext* aContext, JITFrameInfo& aJITFrameInfo,
- mozilla::ProgressLogger aProgressLogger) const;
+ mozilla::ProgressLogger aProgressLogger,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>*
+ aSourceIdToIndexMap = nullptr) const;
// Stream JSON for samples in the buffer to aWriter, using the supplied
// UniqueStacks object.
diff --git a/tools/profiler/core/ProfileBufferEntry.cpp b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -549,7 +549,9 @@ void UniqueStacks::StreamNonJITFrame(const FrameKey& aFrame) {
static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter,
UniqueJSONStrings& aUniqueStrings,
- const JS::ProfiledFrameHandle& aJITFrame) {
+ const JS::ProfiledFrameHandle& aJITFrame,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>*
+ aSourceIdToIndexMap = nullptr) {
enum Schema : uint32_t {
LOCATION = 0,
RELEVANT_FOR_JS = 1,
@@ -563,7 +565,18 @@ static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter,
AutoArraySchemaWithStringsWriter writer(aWriter, aUniqueStrings);
- writer.StringElement(LOCATION, MakeStringSpan(aJITFrame.label()));
+ uint32_t sourceId = aJITFrame.sourceId();
+ nsCString labelWithSourceIndex(aJITFrame.label());
+ if (sourceId && aSourceIdToIndexMap) {
+ auto index = aSourceIdToIndexMap->MaybeGet(sourceId);
+ if (index) {
+ labelWithSourceIndex.AppendLiteral("[");
+ labelWithSourceIndex.AppendInt(*index);
+ labelWithSourceIndex.AppendLiteral("]");
+ }
+ }
+ writer.StringElement(LOCATION, labelWithSourceIndex);
+
writer.BoolElement(RELEVANT_FOR_JS, false);
// It's okay to convert uint64_t to double here because DOM always creates IDs
@@ -587,20 +600,24 @@ static void StreamJITFrame(JSContext* aContext, SpliceableJSONWriter& aWriter,
writer.IntElement(SUBCATEGORY, info.mSubcategoryIndex);
}
-static nsCString JSONForJITFrame(JSContext* aContext,
- const JS::ProfiledFrameHandle& aJITFrame,
- UniqueJSONStrings& aUniqueStrings) {
+static nsCString JSONForJITFrame(
+ JSContext* aContext, const JS::ProfiledFrameHandle& aJITFrame,
+ UniqueJSONStrings& aUniqueStrings,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap =
+ nullptr) {
nsCString json;
JSONStringRefWriteFunc jw(json);
SpliceableJSONWriter writer(jw, aUniqueStrings.SourceFailureLatch());
- StreamJITFrame(aContext, writer, aUniqueStrings, aJITFrame);
+ StreamJITFrame(aContext, writer, aUniqueStrings, aJITFrame,
+ aSourceIdToIndexMap);
return json;
}
void JITFrameInfo::AddInfoForRange(
uint64_t aRangeStart, uint64_t aRangeEnd, JSContext* aCx,
const std::function<void(const std::function<void(void*)>&)>&
- aJITAddressProvider) {
+ aJITAddressProvider,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap) {
if (mLocalFailureLatchSource.Failed()) {
return;
}
@@ -635,7 +652,8 @@ void JITFrameInfo::AddInfoForRange(
if (!frameEntry) {
if (!jitFrameToFrameJSONMap.add(
frameEntry, jitFrameKey,
- JSONForJITFrame(aCx, handle, *mUniqueStrings))) {
+ JSONForJITFrame(aCx, handle, *mUniqueStrings,
+ aSourceIdToIndexMap))) {
mLocalFailureLatchSource.SetFailure(
"OOM in JITFrameInfo::AddInfoForRange adding jit->frame map");
return;
@@ -1742,8 +1760,9 @@ void ProfileBuffer::StreamSamplesAndMarkersToJSON(
void ProfileBuffer::AddJITInfoForRange(
uint64_t aRangeStart, ProfilerThreadId aThreadId, JSContext* aContext,
- JITFrameInfo& aJITFrameInfo,
- mozilla::ProgressLogger aProgressLogger) const {
+ JITFrameInfo& aJITFrameInfo, mozilla::ProgressLogger aProgressLogger,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap)
+ const {
// We can only process JitReturnAddr entries if we have a JSContext.
MOZ_RELEASE_ASSERT(aContext);
@@ -1837,7 +1856,8 @@ void ProfileBuffer::AddJITInfoForRange(
}
}
});
- });
+ },
+ aSourceIdToIndexMap);
}
void ProfileBuffer::StreamMarkersToJSON(
diff --git a/tools/profiler/core/ProfileBufferEntry.h b/tools/profiler/core/ProfileBufferEntry.h
@@ -177,7 +177,9 @@ class JITFrameInfo final {
void AddInfoForRange(
uint64_t aRangeStart, uint64_t aRangeEnd, JSContext* aCx,
const std::function<void(const std::function<void(void*)>&)>&
- aJITAddressProvider);
+ aJITAddressProvider,
+ const nsTHashMap<SourceId, IndexIntoSourceTable>* aSourceIdToIndexMap =
+ nullptr);
// Returns whether the information stored in this object is still relevant
// for any entries in the buffer.
diff --git a/tools/profiler/core/ProfiledThreadData.cpp b/tools/profiler/core/ProfiledThreadData.cpp
@@ -128,7 +128,8 @@ ProfiledThreadData::PrepareUniqueStacks(
*mBufferPositionWhenReceivedJSContext, mThreadInfo.ThreadId(), aCx,
jitFrameInfo,
aProgressLogger.CreateSubLoggerTo("Adding JIT info...", 90_pc,
- "Added JIT info"));
+ "Added JIT info"),
+ aSourceIdToIndexMap);
} else {
aProgressLogger.SetLocalProgress(90_pc, "No JIT info");
}