void ThreadProfile::IterateTags(IterateTagsCallback aCallback) { MOZ_ASSERT(aCallback); int readPos = mReadPos; while (readPos != mLastFlushPos) { // Number of tag consumed int incBy = 1; const ProfileEntry& entry = mEntries[readPos]; // Read ahead to the next tag, if it's a 'd' tag process it now const char* tagStringData = entry.mTagData; int readAheadPos = (readPos + 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(readPos, &incBy, tagBuff); } aCallback(entry, tagStringData); readPos = (readPos + incBy) % mEntrySize; } }
void ProfileBuffer::IterateTagsForThread(IterateTagsCallback aCallback, int aThreadId) { MOZ_ASSERT(aCallback); int readPos = mReadPos; int currentThreadID = -1; while (readPos != mWritePos) { const ProfileEntry& entry = mEntries[readPos]; if (entry.mTagName == 'T') { currentThreadID = entry.mTagInt; readPos = (readPos + 1) % mEntrySize; continue; } // Number of tags consumed int incBy = 1; // Read ahead to the next tag, if it's a 'd' tag process it now const char* tagStringData = entry.mTagData; int readAheadPos = (readPos + 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 != mWritePos && mEntries[readAheadPos].mTagName == 'd') { tagStringData = processDynamicTag(readPos, &incBy, tagBuff); } if (currentThreadID == aThreadId) { aCallback(entry, tagStringData); } readPos = (readPos + incBy) % mEntrySize; } }
void ThreadProfile::StreamJSObject(JSStreamWriter& b) { b.BeginObject(); // Thread meta data if (XRE_GetProcessType() == GeckoProcessType_Plugin) { // TODO Add the proper plugin name b.NameValue("name", "Plugin"); } else if (XRE_GetProcessType() == GeckoProcessType_Content) { // This isn't going to really help once we have multiple content // processes, but it'll do for now. b.NameValue("name", "Content"); } else { b.NameValue("name", Name()); } b.NameValue("tid", static_cast<int>(mThreadId)); b.Name("samples"); b.BeginArray(); bool sample = false; int readPos = mReadPos; while (readPos != mLastFlushPos) { // Number of tag consumed ProfileEntry entry = mEntries[readPos]; switch (entry.mTagName) { case 'r': { if (sample) { b.NameValue("responsiveness", entry.mTagFloat); } } break; case 'p': { if (sample) { b.NameValue("power", entry.mTagFloat); } } break; case 'R': { if (sample) { b.NameValue("rss", entry.mTagFloat); } } break; case 'U': { if (sample) { b.NameValue("uss", entry.mTagFloat); } } break; case 'f': { if (sample) { b.NameValue("frameNumber", entry.mTagInt); } } break; case 't': { if (sample) { b.NameValue("time", entry.mTagFloat); } } break; case 's': { // end the previous sample if there was one if (sample) { b.EndObject(); } // begin the next sample b.BeginObject(); sample = true; // Seek forward through the entire sample, looking for frames // this is an easier approach to reason about than adding more // control variables and cases to the loop that goes through the buffer once b.Name("frames"); b.BeginArray(); b.BeginObject(); 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, // 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].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(); } break; } readPos = (readPos + 1) % mEntrySize; } if (sample) { b.EndObject(); } b.EndArray(); b.Name("markers"); b.BeginArray(); readPos = mReadPos; while (readPos != mLastFlushPos) { ProfileEntry entry = mEntries[readPos]; if (entry.mTagName == 'm') { entry.getMarker()->StreamJSObject(b); } readPos = (readPos + 1) % mEntrySize; } b.EndArray(); b.EndObject(); }
JSCustomObject* ThreadProfile2::ToJSObject(JSContext *aCx) { JSObjectBuilder b(aCx); JSCustomObject *profile = b.CreateObject(); JSCustomArray *samples = b.CreateArray(); b.DefineProperty(profile, "samples", samples); JSCustomObject *sample = NULL; JSCustomArray *frames = NULL; int readPos = mReadPos; while (readPos != mLastFlushPos) { // Number of tag consumed int incBy = 1; ProfileEntry2 entry = mEntries[readPos]; // Read ahead to the next tag, if it's a 'd' tag process it now const char* tagStringData = entry.mTagData; int readAheadPos = (readPos + 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(readPos, &incBy, tagBuff); } switch (entry.mTagName) { case 's': sample = b.CreateObject(); b.DefineProperty(sample, "name", tagStringData); frames = b.CreateArray(); b.DefineProperty(sample, "frames", frames); b.ArrayPush(samples, sample); break; case 'r': { if (sample) { b.DefineProperty(sample, "responsiveness", entry.mTagFloat); } } break; case 'f': { if (sample) { b.DefineProperty(sample, "frameNumber", entry.mTagLine); } } break; case 't': { if (sample) { b.DefineProperty(sample, "time", entry.mTagFloat); } } break; case 'c': case 'l': { if (sample) { JSCustomObject *frame = b.CreateObject(); if (entry.mTagName == 'l') { // 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)entry.mTagPtr; snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc); b.DefineProperty(frame, "location", tagBuff); } else { b.DefineProperty(frame, "location", tagStringData); readAheadPos = (readPos + incBy) % mEntrySize; if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'n') { b.DefineProperty(frame, "line", mEntries[readAheadPos].mTagLine); incBy++; } } b.ArrayPush(frames, frame); } } } readPos = (readPos + incBy) % mEntrySize; } return profile; }
void ThreadProfile::BuildJSObject(Builder& b, typename Builder::ObjectHandle profile) { // Thread meta data if (XRE_GetProcessType() == GeckoProcessType_Plugin) { // TODO Add the proper plugin name b.DefineProperty(profile, "name", "Plugin"); } else { b.DefineProperty(profile, "name", mName); } b.DefineProperty(profile, "tid", static_cast<int>(mThreadId)); typename Builder::RootedArray samples(b.context(), b.CreateArray()); b.DefineProperty(profile, "samples", samples); typename Builder::RootedObject sample(b.context()); typename Builder::RootedArray frames(b.context()); typename Builder::RootedArray markers(b.context()); int readPos = mReadPos; while (readPos != mLastFlushPos) { // Number of tag consumed int incBy = 1; ProfileEntry entry = mEntries[readPos]; // Read ahead to the next tag, if it's a 'd' tag process it now const char* tagStringData = entry.mTagData; int readAheadPos = (readPos + 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(readPos, &incBy, tagBuff); } switch (entry.mTagName) { case 'm': { if (sample) { if (!markers) { markers = b.CreateArray(); b.DefineProperty(sample, "marker", markers); } entry.getMarker()->BuildJSObject(b, markers); } } break; case 'r': { if (sample) { b.DefineProperty(sample, "responsiveness", entry.mTagFloat); } } break; case 'p': { if (sample) { b.DefineProperty(sample, "power", entry.mTagFloat); } } break; case 'f': { if (sample) { b.DefineProperty(sample, "frameNumber", entry.mTagLine); } } break; case 't': { if (sample) { b.DefineProperty(sample, "time", entry.mTagFloat); } } break; case 's': sample = b.CreateObject(); b.DefineProperty(sample, "name", tagStringData); frames = b.CreateArray(); b.DefineProperty(sample, "frames", frames); b.ArrayPush(samples, sample); // Created lazily markers = nullptr; // Fall though to create a label for the 's' tag case 'c': case 'l': { if (sample) { typename Builder::RootedObject frame(b.context(), b.CreateObject()); if (entry.mTagName == 'l') { // 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)entry.mTagPtr; snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc); b.DefineProperty(frame, "location", tagBuff); } else { b.DefineProperty(frame, "location", tagStringData); readAheadPos = (readPos + incBy) % mEntrySize; if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'n') { b.DefineProperty(frame, "line", mEntries[readAheadPos].mTagLine); incBy++; } } b.ArrayPush(frames, frame); } } } readPos = (readPos + incBy) % mEntrySize; } }
void ProfileBuffer::StreamSamplesToJSObject(JSStreamWriter& b, int aThreadId, JSRuntime* rt) { b.BeginArray(); bool sample = false; int readPos = mReadPos; int currentThreadID = -1; while (readPos != mWritePos) { ProfileEntry entry = mEntries[readPos]; if (entry.mTagName == 'T') { currentThreadID = entry.mTagInt; } if (currentThreadID == aThreadId) { switch (entry.mTagName) { case 'r': { if (sample) { b.NameValue("responsiveness", entry.mTagFloat); } } break; case 'p': { if (sample) { b.NameValue("power", entry.mTagFloat); } } break; case 'R': { if (sample) { b.NameValue("rss", entry.mTagFloat); } } break; case 'U': { if (sample) { b.NameValue("uss", entry.mTagFloat); } } break; case 'f': { if (sample) { b.NameValue("frameNumber", entry.mTagInt); } } break; case 't': { if (sample) { b.NameValue("time", entry.mTagFloat); } } break; case 's': { // end the previous sample if there was one if (sample) { b.EndObject(); } // begin the next sample b.BeginObject(); sample = true; // Seek forward through the entire sample, looking for frames // this is an easier approach to reason about than adding more // control variables and cases to the loop that goes through the buffer once b.Name("frames"); b.BeginArray(); b.BeginObject(); b.NameValue("location", "(root)"); b.EndObject(); int framePos = (readPos + 1) % mEntrySize; ProfileEntry frame = mEntries[framePos]; while (framePos != mWritePos && frame.mTagName != 's' && frame.mTagName != 'T') { 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 != mWritePos && 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, // 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 != mWritePos && mEntries[readAheadPos].mTagName == 'n') { b.NameValue("line", mEntries[readAheadPos].mTagInt); incBy++; } readAheadPos = (framePos + incBy) % mEntrySize; if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'y') { b.NameValue("category", mEntries[readAheadPos].mTagInt); incBy++; } readAheadPos = (framePos + incBy) % mEntrySize; if (readAheadPos != mWritePos && mEntries[readAheadPos].mTagName == 'J') { void* pc = mEntries[readAheadPos].mTagPtr; // TODOshu: cannot stream tracked optimization info if // the JS engine has already shut down when streaming. if (rt) { JSScript *optsScript; jsbytecode *optsPC; b.Name("opts"); b.BeginArray(); StreamOptimizationTypeInfoOp typeInfoOp(b); JS::ForEachTrackedOptimizationTypeInfo(rt, pc, typeInfoOp); StreamOptimizationAttemptsOp attemptOp(b); JS::ForEachTrackedOptimizationAttempt(rt, pc, attemptOp, &optsScript, &optsPC); b.EndArray(); b.NameValue("optsLine", JS_PCToLineNumber(optsScript, optsPC)); } } b.EndObject(); } framePos = (framePos + incBy) % mEntrySize; } b.EndArray(); } break; } } readPos = (readPos + 1) % mEntrySize; } if (sample) { b.EndObject(); } b.EndArray(); }