Bug 785922 - Add column information for JS frames to the profile.
MozReview-Commit-ID: 1PAWWhTmYpT
--- a/tools/profiler/core/ProfileBuffer.cpp
+++ b/tools/profiler/core/ProfileBuffer.cpp
@@ -63,17 +63,18 @@ void
ProfileBuffer::AddStoredMarker(ProfilerMarker *aStoredMarker)
{
aStoredMarker->SetPositionInBuffer(mRangeEnd);
mStoredMarkers.insert(aStoredMarker);
}
void
ProfileBuffer::CollectCodeLocation(
- const char* aLabel, const char* aStr, int aLineNumber,
+ const char* aLabel, const char* aStr,
+ const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber,
const Maybe<js::ProfileEntry::Category>& aCategory)
{
AddEntry(ProfileBufferEntry::Label(aLabel));
if (aStr) {
// Store the string using one or more DynamicStringFragment entries.
size_t strLen = strlen(aStr) + 1; // +1 for the null terminator
for (size_t j = 0; j < strLen; ) {
@@ -85,18 +86,22 @@ ProfileBuffer::CollectCodeLocation(
}
memcpy(chars, &aStr[j], len);
j += ProfileBufferEntry::kNumChars;
AddEntry(ProfileBufferEntry::DynamicStringFragment(chars));
}
}
- if (aLineNumber != -1) {
- AddEntry(ProfileBufferEntry::LineNumber(aLineNumber));
+ if (aLineNumber) {
+ AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber));
+ }
+
+ if (aColumnNumber) {
+ AddEntry(ProfileBufferEntry::ColumnNumber(*aColumnNumber));
}
if (aCategory.isSome()) {
AddEntry(ProfileBufferEntry::Category(int(*aCategory)));
}
}
void
@@ -144,31 +149,32 @@ void
ProfileBufferCollector::CollectJitReturnAddr(void* aAddr)
{
mBuf.AddEntry(ProfileBufferEntry::JitReturnAddr(aAddr));
}
void
ProfileBufferCollector::CollectWasmFrame(const char* aLabel)
{
- mBuf.CollectCodeLocation("", aLabel, -1, Nothing());
+ mBuf.CollectCodeLocation("", aLabel, Nothing(), Nothing(), Nothing());
}
void
ProfileBufferCollector::CollectPseudoEntry(const js::ProfileEntry& aEntry)
{
// WARNING: this function runs within the profiler's "critical section".
MOZ_ASSERT(aEntry.kind() == js::ProfileEntry::Kind::CPP_NORMAL ||
aEntry.kind() == js::ProfileEntry::Kind::JS_NORMAL);
const char* label = aEntry.label();
const char* dynamicString = aEntry.dynamicString();
bool isChromeJSEntry = false;
- int lineno = -1;
+ Maybe<uint32_t> line;
+ Maybe<uint32_t> column;
if (aEntry.isJs()) {
// There are two kinds of JS frames that get pushed onto the PseudoStack.
//
// - label = "", dynamic string = <something>
// - label = "js::RunScript", dynamic string = nullptr
//
// The line number is only interesting in the first case.
@@ -176,32 +182,34 @@ ProfileBufferCollector::CollectPseudoEnt
if (label[0] == '\0') {
MOZ_ASSERT(dynamicString);
// We call aEntry.script() repeatedly -- rather than storing the result in
// a local variable in order -- to avoid rooting hazards.
if (aEntry.script()) {
isChromeJSEntry = IsChromeJSScript(aEntry.script());
if (aEntry.pc()) {
- lineno = JS_PCToLineNumber(aEntry.script(), aEntry.pc());
+ unsigned col;
+ line = Some(JS_PCToLineNumber(aEntry.script(), aEntry.pc(), &col));
+ column = Some(col);
}
}
} else {
MOZ_ASSERT(strcmp(label, "js::RunScript") == 0 && !dynamicString);
}
} else {
MOZ_ASSERT(aEntry.isCpp());
- lineno = aEntry.line();
+ line = Some(aEntry.line());
}
if (dynamicString) {
// Adjust the dynamic string as necessary.
if (ProfilerFeature::HasPrivacy(mFeatures) && !isChromeJSEntry) {
dynamicString = "(private)";
} else if (strlen(dynamicString) >= ProfileBuffer::kMaxFrameKeyLength) {
dynamicString = "(too long)";
}
}
- mBuf.CollectCodeLocation(label, dynamicString, lineno,
+ mBuf.CollectCodeLocation(label, dynamicString, line, column,
Some(aEntry.category()));
}
--- a/tools/profiler/core/ProfileBuffer.h
+++ b/tools/profiler/core/ProfileBuffer.h
@@ -39,17 +39,19 @@ public:
// Add |aEntry| to the buffer, ignoring what kind of entry it is.
void AddEntry(const ProfileBufferEntry& aEntry);
// Add to the buffer a sample start (ThreadId) entry for aThreadId.
// Returns the position of the entry.
uint64_t AddThreadIdEntry(int aThreadId);
void CollectCodeLocation(
- const char* aLabel, const char* aStr, int aLineNumber,
+ const char* aLabel, const char* aStr,
+ const mozilla::Maybe<uint32_t>& aLineNumber,
+ const mozilla::Maybe<uint32_t>& aColumnNumber,
const mozilla::Maybe<js::ProfileEntry::Category>& aCategory);
// Maximum size of a frameKey string that we'll handle.
static const size_t kMaxFrameKeyLength = 512;
// Add JIT frame information to aJITFrameInfo for any JitReturnAddr entries
// that are currently in the buffer at or after aRangeStart, in samples
// for the given thread.
--- a/tools/profiler/core/ProfileBufferEntry.cpp
+++ b/tools/profiler/core/ProfileBufferEntry.cpp
@@ -338,16 +338,17 @@ JITFrameInfo::JITFrameInfo(const JITFram
}
}
bool
UniqueStacks::FrameKey::NormalFrameData::operator==(const NormalFrameData& aOther) const
{
return mLocation == aOther.mLocation &&
mLine == aOther.mLine &&
+ mColumn == aOther.mColumn &&
mCategory == aOther.mCategory;
}
bool
UniqueStacks::FrameKey::JITFrameData::operator==(const JITFrameData& aOther) const
{
return mCanonicalAddress == aOther.mCanonicalAddress &&
mJITDepth == aOther.mJITDepth;
@@ -361,16 +362,19 @@ UniqueStacks::FrameKey::Hash() const
const NormalFrameData& data = mData.as<NormalFrameData>();
hash = AddToHash(hash, 0);
if (!data.mLocation.IsEmpty()) {
hash = AddToHash(hash, HashString(data.mLocation.get()));
}
if (data.mLine.isSome()) {
hash = AddToHash(hash, *data.mLine);
}
+ if (data.mColumn.isSome()) {
+ hash = AddToHash(hash, *data.mColumn);
+ }
if (data.mCategory.isSome()) {
hash = AddToHash(hash, *data.mCategory);
}
} else {
const JITFrameData& data = mData.as<JITFrameData>();
hash = AddToHash(hash, 1);
hash = AddToHash(hash, data.mCanonicalAddress);
hash = AddToHash(hash, data.mJITDepth);
@@ -501,26 +505,30 @@ UniqueStacks::StreamNonJITFrame(const Fr
using NormalFrameData = FrameKey::NormalFrameData;
MOZ_RELEASE_ASSERT(aFrame.mData.is<NormalFrameData>());
enum Schema : uint32_t {
LOCATION = 0,
IMPLEMENTATION = 1,
OPTIMIZATIONS = 2,
LINE = 3,
- CATEGORY = 4
+ COLUMN = 4,
+ CATEGORY = 5
};
AutoArraySchemaWriter writer(mFrameTableWriter, *mUniqueStrings);
const NormalFrameData& data = aFrame.mData.as<NormalFrameData>();
writer.StringElement(LOCATION, data.mLocation.get());
if (data.mLine.isSome()) {
writer.IntElement(LINE, *data.mLine);
}
+ if (data.mColumn.isSome()) {
+ writer.IntElement(COLUMN, *data.mColumn);
+ }
if (data.mCategory.isSome()) {
writer.IntElement(CATEGORY, *data.mCategory);
}
}
static void
StreamJITFrameOptimizations(SpliceableJSONWriter& aWriter,
UniqueJSONStrings& aUniqueStrings,
@@ -619,17 +627,18 @@ StreamJITFrame(JSContext* aContext, Spli
UniqueJSONStrings& aUniqueStrings,
const JS::ProfiledFrameHandle& aJITFrame)
{
enum Schema : uint32_t {
LOCATION = 0,
IMPLEMENTATION = 1,
OPTIMIZATIONS = 2,
LINE = 3,
- CATEGORY = 4
+ COLUMN = 4,
+ CATEGORY = 5
};
AutoArraySchemaWriter writer(aWriter, aUniqueStrings);
writer.StringElement(LOCATION, aJITFrame.label());
JS::ProfilingFrameIterator::FrameKind frameKind = aJITFrame.frameKind();
MOZ_ASSERT(frameKind == JS::ProfilingFrameIterator::Frame_Ion ||
@@ -645,19 +654,21 @@ StreamJITFrame(JSContext* aContext, Spli
StreamJITFrameOptimizations(aWriter, aUniqueStrings, aContext,
aJITFrame);
});
}
JS::Rooted<JSScript*> script(aContext);
jsbytecode* pc;
aJITFrame.lookupScriptAndPc(script.address(), &pc);
- unsigned line = JS_PCToLineNumber(script, pc);
+ unsigned column;
+ unsigned line = JS_PCToLineNumber(script, pc, &column);
writer.IntElement(LINE, line);
+ writer.IntElement(COLUMN, column);
}
namespace {
struct CStringWriteFunc : public JSONWriteFunc
{
nsACString& mBuffer; // The struct must not outlive this buffer
explicit CStringWriteFunc(nsACString& aBuffer) : mBuffer(aBuffer) {}
@@ -1017,24 +1028,30 @@ ProfileBuffer::StreamSamplesToJSON(Splic
strbuf[kMaxFrameKeyLength - 1] = '\0';
Maybe<unsigned> line;
if (e.Has() && e.Get().IsLineNumber()) {
line = Some(unsigned(e.Get().u.mInt));
e.Next();
}
+ Maybe<unsigned> column;
+ if (e.Has() && e.Get().IsColumnNumber()) {
+ column = Some(unsigned(e.Get().u.mInt));
+ e.Next();
+ }
+
Maybe<unsigned> category;
if (e.Has() && e.Get().IsCategory()) {
category = Some(unsigned(e.Get().u.mInt));
e.Next();
}
stack = aUniqueStacks.AppendFrame(
- stack, UniqueStacks::FrameKey(strbuf.get(), line, category));
+ stack, UniqueStacks::FrameKey(strbuf.get(), line, column, category));
} else if (e.Get().IsJitReturnAddr()) {
numFrames++;
// A JIT frame may expand to multiple frames due to inlining.
void* pc = e.Get().u.mPtr;
const Maybe<nsTArray<UniqueStacks::FrameKey>>& frameKeys =
aUniqueStacks.LookupFramesForJITAddressFromBufferPos(pc, e.CurPos());
--- a/tools/profiler/core/ProfileBufferEntry.h
+++ b/tools/profiler/core/ProfileBufferEntry.h
@@ -33,16 +33,17 @@ class ProfilerMarker;
#define FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(macro) \
macro(Category, int) \
macro(CollectionStart, double) \
macro(CollectionEnd, double) \
macro(Label, const char*) \
macro(DynamicStringFragment, char*) /* char[kNumChars], really */ \
macro(JitReturnAddr, void*) \
macro(LineNumber, int) \
+ macro(ColumnNumber, int) \
macro(NativeLeafAddr, void*) \
macro(Marker, ProfilerMarker*) \
macro(Pause, double) \
macro(ResidentMemory, double) \
macro(Responsiveness, double) \
macro(Resume, double) \
macro(ThreadId, int) \
macro(Time, double) \
@@ -215,18 +216,19 @@ class UniqueStacks
public:
struct FrameKey {
explicit FrameKey(const char* aLocation)
: mData(NormalFrameData{
nsCString(aLocation), mozilla::Nothing(), mozilla::Nothing() })
{}
FrameKey(const char* aLocation, const mozilla::Maybe<unsigned>& aLine,
+ const mozilla::Maybe<unsigned>& aColumn,
const mozilla::Maybe<unsigned>& aCategory)
- : mData(NormalFrameData{ nsCString(aLocation), aLine, aCategory })
+ : mData(NormalFrameData{ nsCString(aLocation), aLine, aColumn, aCategory })
{}
FrameKey(void* aJITAddress, uint32_t aJITDepth, uint32_t aRangeIndex)
: mData(JITFrameData{ aJITAddress, aJITDepth, aRangeIndex })
{}
FrameKey(const FrameKey& aOther) = default;
@@ -235,16 +237,17 @@ public:
bool operator!=(const FrameKey& aOther) const { return !(*this == aOther); }
struct NormalFrameData {
bool operator==(const NormalFrameData& aOther) const;
bool operator!=(const NormalFrameData& aOther) const { return !(*this == aOther); }
nsCString mLocation;
mozilla::Maybe<unsigned> mLine;
+ mozilla::Maybe<unsigned> mColumn;
mozilla::Maybe<unsigned> mCategory;
};
struct JITFrameData {
bool operator==(const JITFrameData& aOther) const;
bool operator!=(const JITFrameData& aOther) const { return !(*this == aOther); }
void* mCanonicalAddress;
uint32_t mJITDepth;
--- a/tools/profiler/core/platform.cpp
+++ b/tools/profiler/core/platform.cpp
@@ -1534,17 +1534,17 @@ StreamTaskTracer(PSLockRef aLock, Splice
}
static void
StreamMetaJSCustomObject(PSLockRef aLock, SpliceableJSONWriter& aWriter,
bool aIsShuttingDown)
{
MOZ_RELEASE_ASSERT(CorePS::Exists() && ActivePS::Exists(aLock));
- aWriter.IntProperty("version", 9);
+ aWriter.IntProperty("version", 10);
// The "startTime" field holds the number of milliseconds since midnight
// January 1, 1970 GMT. This grotty code computes (Now - (Now -
// ProcessStartTime)) to convert CorePS::ProcessStartTime() into that form.
TimeDuration delta = TimeStamp::Now() - CorePS::ProcessStartTime();
aWriter.DoubleProperty(
"startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));