void ProfilerMarkerTracing::streamPayloadImp(JSStreamWriter& b) { b.BeginObject(); streamCommonProps("tracing", b); if (GetCategory()) { b.NameValue("category", GetCategory()); } if (GetMetaData() != TRACING_DEFAULT) { if (GetMetaData() == TRACING_INTERVAL_START) { b.NameValue("interval", "start"); } else if (GetMetaData() == TRACING_INTERVAL_END) { b.NameValue("interval", "end"); } } b.EndObject(); }
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"); mBuffer->StreamSamplesToJSObject(b, mThreadId, mPseudoStack->mRuntime); b.Name("markers"); mBuffer->StreamMarkersToJSObject(b, mThreadId); b.EndObject(); }
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(); }
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(); }
void TableTicker::StreamJSObject(JSStreamWriter& b) { b.BeginObject(); // Put shared library info b.NameValue("libs", GetSharedLibraryInfoString().c_str()); // Put meta data b.Name("meta"); StreamMetaJSCustomObject(b); // Data of TaskTracer doesn't belong in the circular buffer. if (TaskTracer()) { b.Name("tasktracer"); StreamTaskTracer(b); } // Lists the samples for each ThreadProfile b.Name("threads"); b.BeginArray(); SetPaused(true); { mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread not being profiled, skip it if (!sRegisteredThreads->at(i)->Profile()) continue; // Note that we intentionally include ThreadProfile which // have been marked for pending delete. MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); sRegisteredThreads->at(i)->Profile()->StreamJSObject(b); } } if (Sampler::CanNotifyObservers()) { // Send a event asking any subprocesses (plugins) to // give us their information SubprocessClosure closure(&b); nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) { nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure); os->NotifyObservers(pse, "profiler-subprocess", nullptr); } } #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) if (ProfileJava()) { mozilla::widget::GeckoJavaSampler::PauseJavaProfiling(); BuildJavaThreadJSObject(b); mozilla::widget::GeckoJavaSampler::UnpauseJavaProfiling(); } #endif SetPaused(false); b.EndArray(); b.EndObject(); }