Bug 1017790 - Expose category information in the profiler data, r=djvj
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -54,17 +54,20 @@ class ProfileEntry
enum Flags {
// Indicate whether a profile entry represents a CPP frame. If not set,
// a JS frame is assumed by default. You're not allowed to publicly
// change the frame type. Instead, call `setJsFrame` or `setCppFrame`.
IS_CPP_ENTRY = 0x01,
// Indicate that copying the frame label is not necessary when taking a
// sample of the pseudostack.
- FRAME_LABEL_COPY = 0x02
+ FRAME_LABEL_COPY = 0x02,
+
+ // Mask for removing all flags except the category information.
+ CATEGORY_MASK = ~IS_CPP_ENTRY & ~FRAME_LABEL_COPY
};
MOZ_BEGIN_NESTED_ENUM_CLASS(Category, uint32_t)
OTHER = 0x04,
CSS = 0x08,
JS = 0x10,
GC = 0x20,
CC = 0x40,
@@ -107,19 +110,23 @@ class ProfileEntry
}
void unsetFlag(uint32_t flag) volatile {
MOZ_ASSERT(flag != IS_CPP_ENTRY);
flags_ &= ~flag;
}
bool hasFlag(uint32_t flag) const volatile {
return bool(flags_ & flag);
}
+
uint32_t flags() const volatile {
return flags_;
}
+ uint32_t category() const volatile {
+ return flags_ & CATEGORY_MASK;
+ }
void *stackAddress() const volatile {
MOZ_ASSERT(!isJs());
return spOrScript;
}
JSScript *script() const volatile {
MOZ_ASSERT(isJs());
return (JSScript *)spOrScript;
--- a/tools/profiler/ProfileEntry.cpp
+++ b/tools/profiler/ProfileEntry.cpp
@@ -54,18 +54,18 @@ ProfileEntry::ProfileEntry(char aTagName
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress)
: mTagAddress(aTagAddress)
, mTagName(aTagName)
{ }
-ProfileEntry::ProfileEntry(char aTagName, int aTagLine)
- : mTagLine(aTagLine)
+ProfileEntry::ProfileEntry(char aTagName, int aTagInt)
+ : mTagInt(aTagInt)
, mTagName(aTagName)
{ }
ProfileEntry::ProfileEntry(char aTagName, char aTagChar)
: mTagChar(aTagChar)
, mTagName(aTagName)
{ }
@@ -89,28 +89,28 @@ void* ProfileEntry::get_tagPtr() {
void ProfileEntry::log()
{
// There is no compiler enforced mapping between tag chars
// and union variant fields, so the following was derived
// by looking through all the use points of TableTicker.cpp.
// mTagMarker (ProfilerMarker*) m
// mTagData (const char*) c,s
// mTagPtr (void*) d,l,L,B (immediate backtrace), S(start-of-stack)
- // mTagLine (int) n,f
+ // mTagInt (int) n,f,y
// mTagChar (char) h
// mTagFloat (double) r,t,p,R (resident memory)
switch (mTagName) {
case 'm':
LOGF("%c \"%s\"", mTagName, mTagMarker->GetMarkerName()); break;
case 'c': case 's':
LOGF("%c \"%s\"", mTagName, mTagData); break;
case 'd': case 'l': case 'L': case 'B': case 'S':
LOGF("%c %p", mTagName, mTagPtr); break;
- case 'n': case 'f':
- LOGF("%c %d", mTagName, mTagLine); break;
+ case 'n': case 'f': case 'y':
+ LOGF("%c %d", mTagName, mTagInt); break;
case 'h':
LOGF("%c \'%c\'", mTagName, mTagChar); break;
case 'r': case 't': case 'p': case 'R':
LOGF("%c %f", mTagName, mTagFloat); break;
default:
LOGF("'%c' unknown_tag", mTagName); break;
}
}
@@ -357,17 +357,17 @@ void ThreadProfile::StreamJSObject(JSStr
if (sample) {
b.NameValue("rss", entry.mTagFloat);
}
}
break;
case 'f':
{
if (sample) {
- b.NameValue("frameNumber", entry.mTagLine);
+ b.NameValue("frameNumber", entry.mTagInt);
}
}
break;
case 't':
{
if (sample) {
b.NameValue("time", entry.mTagFloat);
}
@@ -394,47 +394,55 @@ void ThreadProfile::StreamJSObject(JSStr
b.NameValue("location", "(root)");
b.EndObject();
int framePos = (readPos + 1) % mEntrySize;
ProfileEntry frame = mEntries[framePos];
while (framePos != mLastFlushPos && frame.mTagName != 's') {
int incBy = 1;
frame = mEntries[framePos];
+
// Read ahead to the next tag, if it's a 'd' tag process it now
const char* tagStringData = frame.mTagData;
int readAheadPos = (framePos + 1) % mEntrySize;
char tagBuff[DYNAMIC_MAX_STRING];
// Make sure the string is always null terminated if it fills up
// DYNAMIC_MAX_STRING-2
tagBuff[DYNAMIC_MAX_STRING-1] = '\0';
if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') {
tagStringData = processDynamicTag(framePos, &incBy, tagBuff);
}
// Write one frame. It can have either
// 1. only location - 'l' containing a memory address
- // 2. location and line number - 'c' followed by 'd's and an optional 'n'
+ // 2. location and line number - 'c' followed by 'd's,
+ // an optional 'n' and an optional 'y'
if (frame.mTagName == 'l') {
b.BeginObject();
// Bug 753041
// We need a double cast here to tell GCC that we don't want to sign
// extend 32-bit addresses starting with 0xFXXXXXX.
unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr;
snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc);
b.NameValue("location", tagBuff);
b.EndObject();
} else if (frame.mTagName == 'c') {
b.BeginObject();
b.NameValue("location", tagStringData);
readAheadPos = (framePos + incBy) % mEntrySize;
if (readAheadPos != mLastFlushPos &&
mEntries[readAheadPos].mTagName == 'n') {
- b.NameValue("line", mEntries[readAheadPos].mTagLine);
+ b.NameValue("line", mEntries[readAheadPos].mTagInt);
+ incBy++;
+ }
+ readAheadPos = (framePos + incBy) % mEntrySize;
+ if (readAheadPos != mLastFlushPos &&
+ mEntries[readAheadPos].mTagName == 'y') {
+ b.NameValue("category", mEntries[readAheadPos].mTagInt);
incBy++;
}
b.EndObject();
}
framePos = (framePos + incBy) % mEntrySize;
}
b.EndArray();
}
--- a/tools/profiler/ProfileEntry.h
+++ b/tools/profiler/ProfileEntry.h
@@ -56,17 +56,17 @@ private:
union {
const char* mTagData;
char mTagChars[sizeof(void*)];
void* mTagPtr;
ProfilerMarker* mTagMarker;
float mTagFloat;
Address mTagAddress;
uintptr_t mTagOffset;
- int mTagLine;
+ int mTagInt;
char mTagChar;
};
char mTagName;
};
#pragma pack(pop)
typedef void (*IterateTagsCallback)(const ProfileEntry& entry, const char* tagStringData);
--- a/tools/profiler/TableTicker.cpp
+++ b/tools/profiler/TableTicker.cpp
@@ -369,19 +369,28 @@ void addProfileEntry(volatile StackEntry
aProfile.addTag(ProfileEntry('c', sampleLabel));
// XXX: Bug 1010578. Don't assume a CPP entry and try to get the
// line for js entries as well.
if (entry.isCpp()) {
lineno = entry.line();
}
}
+
if (lineno != -1) {
aProfile.addTag(ProfileEntry('n', lineno));
}
+
+ uint32_t category = entry.category();
+ MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
+ MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
+
+ if (category) {
+ aProfile.addTag(ProfileEntry('y', (int)category));
+ }
}
#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
typedef struct {
void** array;
void** sp_array;
size_t size;
size_t count;
--- a/tools/profiler/tests/gtest/ThreadProfileTest.cpp
+++ b/tools/profiler/tests/gtest/ThreadProfileTest.cpp
@@ -33,17 +33,17 @@ TEST(ThreadProfile, InsertTagsNoWrap) {
int test_size = 50;
for (int i = 0; i < test_size; i++) {
tp.addTag(ProfileEntry('t', i));
}
ASSERT_TRUE(tp.mEntries != nullptr);
int readPos = tp.mReadPos;
while (readPos != tp.mWritePos) {
ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't');
- ASSERT_TRUE(tp.mEntries[readPos].mTagLine == readPos);
+ ASSERT_TRUE(tp.mEntries[readPos].mTagInt == readPos);
readPos = (readPos + 1) % tp.mEntrySize;
}
}
// See if wrapping works as it should in the basic case
TEST(ThreadProfile, InsertTagsWrap) {
PseudoStack stack;
Thread::tid_t tid = 1000;
@@ -56,14 +56,14 @@ TEST(ThreadProfile, InsertTagsWrap) {
tp.addTag(ProfileEntry('t', i));
}
ASSERT_TRUE(tp.mEntries != nullptr);
int readPos = tp.mReadPos;
int ctr = 0;
while (readPos != tp.mWritePos) {
ASSERT_TRUE(tp.mEntries[readPos].mTagName == 't');
// the first few tags were discarded when we wrapped
- ASSERT_TRUE(tp.mEntries[readPos].mTagLine == ctr + (test_size - tags));
+ ASSERT_TRUE(tp.mEntries[readPos].mTagInt == ctr + (test_size - tags));
ctr++;
readPos = (readPos + 1) % tp.mEntrySize;
}
}