Bug 1017790 - Expose category information in the profiler data, r=djvj
authorVictor Porof <vporof@mozilla.com>
Wed, 04 Jun 2014 14:37:49 -0400
changeset 186581 748b4ffb759e97e4495d6f92948c9f8fed585155
parent 186580 d185d33cfce5abbbb03157af9f471570478476c3
child 186582 a9b2fcd44bec187638a881eda547731d268a0f61
push idunknown
push userunknown
push dateunknown
reviewersdjvj
bugs1017790
milestone32.0a1
Bug 1017790 - Expose category information in the profiler data, r=djvj
js/public/ProfilingStack.h
tools/profiler/ProfileEntry.cpp
tools/profiler/ProfileEntry.h
tools/profiler/TableTicker.cpp
tools/profiler/tests/gtest/ThreadProfileTest.cpp
--- 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;
   }
 }