static void BuildJavaThreadJSObject(JSStreamWriter& b) { b.BeginObject(); b.NameValue("name", "Java Main Thread"); b.Name("samples"); b.BeginArray(); // for each sample for (int sampleId = 0; true; sampleId++) { bool firstRun = true; // for each frame for (int frameId = 0; true; frameId++) { nsCString result; bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result); // when we run out of frames, we stop looping if (!hasFrame) { // if we found at least one frame, we have objects to close if (!firstRun) { b.EndArray(); b.EndObject(); } break; } // the first time around, open the sample object and frames array if (firstRun) { firstRun = false; double sampleTime = mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId); b.BeginObject(); b.NameValue("time", sampleTime); b.Name("frames"); b.BeginArray(); } // add a frame to the sample b.BeginObject(); b.NameValue("location", result.BeginReading()); b.EndObject(); } // if we found no frames for this sample, we are done if (firstRun) { break; } } b.EndArray(); b.EndObject(); }
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); // 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; 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::android::GeckoJavaSampler::PauseJavaProfiling(); BuildJavaThreadJSObject(b); mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling(); } #endif SetPaused(false); b.EndArray(); b.EndObject(); }
void TableTicker::StreamTaskTracer(JSStreamWriter& b) { b.BeginObject(); #ifdef MOZ_TASK_TRACER b.Name("data"); b.BeginArray(); nsAutoPtr<nsTArray<nsCString>> data( mozilla::tasktracer::GetLoggedData(sStartTime)); for (uint32_t i = 0; i < data->Length(); ++i) { b.Value((data->ElementAt(i)).get()); } b.EndArray(); b.Name("threads"); b.BeginArray(); mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); for (size_t i = 0; i < sRegisteredThreads->size(); i++) { // Thread meta data ThreadInfo* info = sRegisteredThreads->at(i); b.BeginObject(); if (XRE_GetProcessType() == GeckoProcessType_Plugin) { // TODO Add the proper plugin name b.NameValue("name", "Plugin"); } else { b.NameValue("name", info->Name()); } b.NameValue("tid", static_cast<int>(info->ThreadId())); b.EndObject(); } b.EndArray(); b.NameValue("start", static_cast<double>(mozilla::tasktracer::GetStartTime())); #endif b.EndObject(); }
void ProfileBuffer::StreamMarkersToJSObject(JSStreamWriter& b, int aThreadId) { b.BeginArray(); int readPos = mReadPos; int currentThreadID = -1; while (readPos != mWritePos) { ProfileEntry entry = mEntries[readPos]; if (entry.mTagName == 'T') { currentThreadID = entry.mTagInt; } else if (currentThreadID == aThreadId && entry.mTagName == 'm') { entry.getMarker()->StreamJSObject(b); } readPos = (readPos + 1) % mEntrySize; } b.EndArray(); }
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(); }