michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "GeckoProfiler.h" michael@0: #include "SaveProfileTask.h" michael@0: #include "ProfileEntry.h" michael@0: #include "SyncProfile.h" michael@0: #include "platform.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "prenv.h" michael@0: #include "prtime.h" michael@0: #include "shared-libraries.h" michael@0: #include "mozilla/StackWalk.h" michael@0: #include "TableTicker.h" michael@0: #include "nsXULAppAPI.h" michael@0: michael@0: // JSON michael@0: #include "JSStreamWriter.h" michael@0: michael@0: // Meta michael@0: #include "nsXPCOM.h" michael@0: #include "nsXPCOMCID.h" michael@0: #include "nsIHttpProtocolHandler.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "nsIXULRuntime.h" michael@0: #include "nsIXULAppInfo.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsIObserverService.h" michael@0: #include "mozilla/Services.h" michael@0: #include "PlatformMacros.h" michael@0: michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: #include "AndroidBridge.h" michael@0: #endif michael@0: michael@0: // JS michael@0: #include "js/OldDebugAPI.h" michael@0: michael@0: #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN)) michael@0: #define USE_NS_STACKWALK michael@0: #endif michael@0: #ifdef USE_NS_STACKWALK michael@0: #include "nsStackWalk.h" michael@0: #endif michael@0: michael@0: #if defined(XP_WIN) michael@0: typedef CONTEXT tickcontext_t; michael@0: #elif defined(LINUX) michael@0: #include michael@0: typedef ucontext_t tickcontext_t; michael@0: #endif michael@0: michael@0: #if defined(LINUX) || defined(XP_MACOSX) michael@0: #include michael@0: pid_t gettid(); michael@0: #endif michael@0: michael@0: #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK) michael@0: // Should also work on other Android and ARM Linux, but not tested there yet. michael@0: #define USE_EHABI_STACKWALK michael@0: #endif michael@0: #ifdef USE_EHABI_STACKWALK michael@0: #include "EHABIStackWalk.h" michael@0: #endif michael@0: michael@0: using std::string; michael@0: using namespace mozilla; michael@0: michael@0: #ifndef MAXPATHLEN michael@0: #ifdef PATH_MAX michael@0: #define MAXPATHLEN PATH_MAX michael@0: #elif defined(MAX_PATH) michael@0: #define MAXPATHLEN MAX_PATH michael@0: #elif defined(_MAX_PATH) michael@0: #define MAXPATHLEN _MAX_PATH michael@0: #elif defined(CCHMAXPATH) michael@0: #define MAXPATHLEN CCHMAXPATH michael@0: #else michael@0: #define MAXPATHLEN 1024 michael@0: #endif michael@0: #endif michael@0: michael@0: /////////////////////////////////////////////////////////////////////// michael@0: // BEGIN SaveProfileTask et al michael@0: michael@0: std::string GetSharedLibraryInfoString(); michael@0: michael@0: void TableTicker::HandleSaveRequest() michael@0: { michael@0: if (!mSaveRequested) michael@0: return; michael@0: mSaveRequested = false; michael@0: michael@0: // TODO: Use use the ipc/chromium Tasks here to support processes michael@0: // without XPCOM. michael@0: nsCOMPtr runnable = new SaveProfileTask(); michael@0: NS_DispatchToMainThread(runnable); michael@0: } michael@0: michael@0: void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b) michael@0: { michael@0: b.BeginObject(); michael@0: michael@0: b.NameValue("version", 2); michael@0: b.NameValue("interval", interval()); michael@0: b.NameValue("stackwalk", mUseStackWalk); michael@0: b.NameValue("jank", mJankOnly); michael@0: b.NameValue("processType", XRE_GetProcessType()); michael@0: michael@0: TimeDuration delta = TimeStamp::Now() - sStartTime; michael@0: b.NameValue("startTime", static_cast(PR_Now()/1000.0 - delta.ToMilliseconds())); michael@0: michael@0: nsresult res; michael@0: nsCOMPtr http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res); michael@0: if (!NS_FAILED(res)) { michael@0: nsAutoCString string; michael@0: michael@0: res = http->GetPlatform(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("platform", string.Data()); michael@0: michael@0: res = http->GetOscpu(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("oscpu", string.Data()); michael@0: michael@0: res = http->GetMisc(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("misc", string.Data()); michael@0: } michael@0: michael@0: nsCOMPtr runtime = do_GetService("@mozilla.org/xre/runtime;1"); michael@0: if (runtime) { michael@0: nsAutoCString string; michael@0: michael@0: res = runtime->GetXPCOMABI(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("abi", string.Data()); michael@0: michael@0: res = runtime->GetWidgetToolkit(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("toolkit", string.Data()); michael@0: } michael@0: michael@0: nsCOMPtr appInfo = do_GetService("@mozilla.org/xre/app-info;1"); michael@0: if (appInfo) { michael@0: nsAutoCString string; michael@0: michael@0: res = appInfo->GetName(string); michael@0: if (!NS_FAILED(res)) michael@0: b.NameValue("product", string.Data()); michael@0: } michael@0: michael@0: b.EndObject(); michael@0: } michael@0: michael@0: void TableTicker::ToStreamAsJSON(std::ostream& stream) michael@0: { michael@0: JSStreamWriter b(stream); michael@0: StreamJSObject(b); michael@0: } michael@0: michael@0: JSObject* TableTicker::ToJSObject(JSContext *aCx) michael@0: { michael@0: JS::RootedValue val(aCx); michael@0: std::stringstream ss; michael@0: { michael@0: // Define a scope to prevent a moving GC during ~JSStreamWriter from michael@0: // trashing the return value. michael@0: JSStreamWriter b(ss); michael@0: StreamJSObject(b); michael@0: NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str())); michael@0: JS_ParseJSON(aCx, static_cast(js_string.get()), js_string.Length(), &val); michael@0: } michael@0: return &val.toObject(); michael@0: } michael@0: michael@0: struct SubprocessClosure { michael@0: SubprocessClosure(JSStreamWriter *aWriter) michael@0: : mWriter(aWriter) michael@0: {} michael@0: michael@0: JSStreamWriter* mWriter; michael@0: }; michael@0: michael@0: void SubProcessCallback(const char* aProfile, void* aClosure) michael@0: { michael@0: // Called by the observer to get their profile data included michael@0: // as a sub profile michael@0: SubprocessClosure* closure = (SubprocessClosure*)aClosure; michael@0: michael@0: // Add the string profile into the profile michael@0: closure->mWriter->Value(aProfile); michael@0: } michael@0: michael@0: michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: static michael@0: void BuildJavaThreadJSObject(JSStreamWriter& b) michael@0: { michael@0: b.BeginObject(); michael@0: michael@0: b.NameValue("name", "Java Main Thread"); michael@0: michael@0: b.Name("samples"); michael@0: b.BeginArray(); michael@0: michael@0: // for each sample michael@0: for (int sampleId = 0; true; sampleId++) { michael@0: bool firstRun = true; michael@0: // for each frame michael@0: for (int frameId = 0; true; frameId++) { michael@0: nsCString result; michael@0: bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result); michael@0: // when we run out of frames, we stop looping michael@0: if (!hasFrame) { michael@0: // if we found at least one frame, we have objects to close michael@0: if (!firstRun) { michael@0: b.EndArray(); michael@0: b.EndObject(); michael@0: } michael@0: break; michael@0: } michael@0: // the first time around, open the sample object and frames array michael@0: if (firstRun) { michael@0: firstRun = false; michael@0: michael@0: double sampleTime = michael@0: mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId); michael@0: michael@0: b.BeginObject(); michael@0: b.NameValue("time", sampleTime); michael@0: michael@0: b.Name("frames"); michael@0: b.BeginArray(); michael@0: } michael@0: // add a frame to the sample michael@0: b.BeginObject(); michael@0: b.NameValue("location", result.BeginReading()); michael@0: b.EndObject(); michael@0: } michael@0: // if we found no frames for this sample, we are done michael@0: if (firstRun) { michael@0: break; michael@0: } michael@0: } michael@0: michael@0: b.EndArray(); michael@0: michael@0: b.EndObject(); michael@0: } michael@0: #endif michael@0: michael@0: void TableTicker::StreamJSObject(JSStreamWriter& b) michael@0: { michael@0: b.BeginObject(); michael@0: // Put shared library info michael@0: b.NameValue("libs", GetSharedLibraryInfoString().c_str()); michael@0: michael@0: // Put meta data michael@0: b.Name("meta"); michael@0: StreamMetaJSCustomObject(b); michael@0: michael@0: // Lists the samples for each ThreadProfile michael@0: b.Name("threads"); michael@0: b.BeginArray(); michael@0: michael@0: SetPaused(true); michael@0: michael@0: { michael@0: mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); michael@0: michael@0: for (size_t i = 0; i < sRegisteredThreads->size(); i++) { michael@0: // Thread not being profiled, skip it michael@0: if (!sRegisteredThreads->at(i)->Profile()) michael@0: continue; michael@0: michael@0: MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); michael@0: michael@0: sRegisteredThreads->at(i)->Profile()->StreamJSObject(b); michael@0: } michael@0: } michael@0: michael@0: if (Sampler::CanNotifyObservers()) { michael@0: // Send a event asking any subprocesses (plugins) to michael@0: // give us their information michael@0: SubprocessClosure closure(&b); michael@0: nsCOMPtr os = mozilla::services::GetObserverService(); michael@0: if (os) { michael@0: nsRefPtr pse = new ProfileSaveEvent(SubProcessCallback, &closure); michael@0: os->NotifyObservers(pse, "profiler-subprocess", nullptr); michael@0: } michael@0: } michael@0: michael@0: #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) michael@0: if (ProfileJava()) { michael@0: mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling(); michael@0: michael@0: BuildJavaThreadJSObject(b); michael@0: michael@0: mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling(); michael@0: } michael@0: #endif michael@0: michael@0: SetPaused(false); michael@0: b.EndArray(); michael@0: michael@0: b.EndObject(); michael@0: } michael@0: michael@0: // END SaveProfileTask et al michael@0: //////////////////////////////////////////////////////////////////////// michael@0: michael@0: static michael@0: void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr) michael@0: { michael@0: aProfile.addTag(ProfileEntry(aTagName, "")); michael@0: // Add one to store the null termination michael@0: size_t strLen = strlen(aStr) + 1; michael@0: for (size_t j = 0; j < strLen;) { michael@0: // Store as many characters in the void* as the platform allows michael@0: char text[sizeof(void*)]; michael@0: size_t len = sizeof(void*)/sizeof(char); michael@0: if (j+len >= strLen) { michael@0: len = strLen - j; michael@0: } michael@0: memcpy(text, &aStr[j], len); michael@0: j += sizeof(void*)/sizeof(char); michael@0: // Cast to *((void**) to pass the text data to a void* michael@0: aProfile.addTag(ProfileEntry('d', *((void**)(&text[0])))); michael@0: } michael@0: } michael@0: michael@0: static michael@0: void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile, michael@0: PseudoStack *stack, void *lastpc) michael@0: { michael@0: int lineno = -1; michael@0: michael@0: // First entry has tagName 's' (start) michael@0: // Check for magic pointer bit 1 to indicate copy michael@0: const char* sampleLabel = entry.label(); michael@0: if (entry.isCopyLabel()) { michael@0: // Store the string using 1 or more 'd' (dynamic) tags michael@0: // that will happen to the preceding tag michael@0: michael@0: addDynamicTag(aProfile, 'c', sampleLabel); michael@0: if (entry.js()) { michael@0: if (!entry.pc()) { michael@0: // The JIT only allows the top-most entry to have a nullptr pc michael@0: MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]); michael@0: // If stack-walking was disabled, then that's just unfortunate michael@0: if (lastpc) { michael@0: jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(), michael@0: lastpc); michael@0: if (jspc) { michael@0: lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc); michael@0: } michael@0: } michael@0: } else { michael@0: lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc()); michael@0: } michael@0: } else { michael@0: lineno = entry.line(); michael@0: } michael@0: } else { michael@0: aProfile.addTag(ProfileEntry('c', sampleLabel)); michael@0: lineno = entry.line(); michael@0: } michael@0: if (lineno != -1) { michael@0: aProfile.addTag(ProfileEntry('n', lineno)); michael@0: } michael@0: } michael@0: michael@0: #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) michael@0: typedef struct { michael@0: void** array; michael@0: void** sp_array; michael@0: size_t size; michael@0: size_t count; michael@0: } PCArray; michael@0: michael@0: static void mergeNativeBacktrace(ThreadProfile &aProfile, const PCArray &array) { michael@0: aProfile.addTag(ProfileEntry('s', "(root)")); michael@0: michael@0: PseudoStack* stack = aProfile.GetPseudoStack(); michael@0: uint32_t pseudoStackPos = 0; michael@0: michael@0: /* We have two stacks, the native C stack we extracted from unwinding, michael@0: * and the pseudostack we managed during execution. We want to consolidate michael@0: * the two in order. We do so by merging using the approximate stack address michael@0: * when each entry was push. When pushing JS entry we may not now the stack michael@0: * address in which case we have a nullptr stack address in which case we assume michael@0: * that it follows immediatly the previous element. michael@0: * michael@0: * C Stack | Address -- Pseudo Stack | Address michael@0: * main() | 0x100 run_js() | 0x40 michael@0: * start() | 0x80 jsCanvas() | nullptr michael@0: * timer() | 0x50 drawLine() | nullptr michael@0: * azure() | 0x10 michael@0: * michael@0: * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure() michael@0: */ michael@0: // i is the index in C stack starting at main and decreasing michael@0: // pseudoStackPos is the position in the Pseudo stack starting michael@0: // at the first frame (run_js in the example) and increasing. michael@0: for (size_t i = array.count; i > 0; --i) { michael@0: while (pseudoStackPos < stack->stackSize()) { michael@0: volatile StackEntry& entry = stack->mStack[pseudoStackPos]; michael@0: michael@0: if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress()) michael@0: break; michael@0: michael@0: addProfileEntry(entry, aProfile, stack, array.array[0]); michael@0: pseudoStackPos++; michael@0: } michael@0: michael@0: aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1])); michael@0: } michael@0: } michael@0: michael@0: #endif michael@0: michael@0: #ifdef USE_NS_STACKWALK michael@0: static michael@0: void StackWalkCallback(void* aPC, void* aSP, void* aClosure) michael@0: { michael@0: PCArray* array = static_cast(aClosure); michael@0: MOZ_ASSERT(array->count < array->size); michael@0: array->sp_array[array->count] = aSP; michael@0: array->array[array->count] = aPC; michael@0: array->count++; michael@0: } michael@0: michael@0: void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample) michael@0: { michael@0: #ifndef XP_MACOSX michael@0: uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData()); michael@0: MOZ_ASSERT(thread); michael@0: #endif michael@0: void* pc_array[1000]; michael@0: void* sp_array[1000]; michael@0: PCArray array = { michael@0: pc_array, michael@0: sp_array, michael@0: mozilla::ArrayLength(pc_array), michael@0: 0 michael@0: }; michael@0: michael@0: // Start with the current function. michael@0: StackWalkCallback(aSample->pc, aSample->sp, &array); michael@0: michael@0: uint32_t maxFrames = uint32_t(array.size - array.count); michael@0: #ifdef XP_MACOSX michael@0: pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData()); michael@0: void *stackEnd = reinterpret_cast(-1); michael@0: if (pt) michael@0: stackEnd = static_cast(pthread_get_stackaddr_np(pt)); michael@0: nsresult rv = NS_OK; michael@0: if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd) michael@0: rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, michael@0: maxFrames, &array, michael@0: reinterpret_cast(aSample->fp), stackEnd); michael@0: #else michael@0: void *platformData = nullptr; michael@0: #ifdef XP_WIN michael@0: if (aSample->isSamplingCurrentThread) { michael@0: // In this case we want NS_StackWalk to know that it's walking the michael@0: // current thread's stack, so we pass 0 as the thread handle. michael@0: thread = 0; michael@0: } michael@0: platformData = aSample->context; michael@0: #endif // XP_WIN michael@0: michael@0: nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, michael@0: &array, thread, platformData); michael@0: #endif michael@0: if (NS_SUCCEEDED(rv)) michael@0: mergeNativeBacktrace(aProfile, array); michael@0: } michael@0: #endif michael@0: michael@0: #ifdef USE_EHABI_STACKWALK michael@0: void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample) michael@0: { michael@0: void *pc_array[1000]; michael@0: void *sp_array[1000]; michael@0: PCArray array = { michael@0: pc_array, michael@0: sp_array, michael@0: mozilla::ArrayLength(pc_array), michael@0: 0 michael@0: }; michael@0: michael@0: const mcontext_t *mcontext = &reinterpret_cast(aSample->context)->uc_mcontext; michael@0: mcontext_t savedContext; michael@0: PseudoStack *pseudoStack = aProfile.GetPseudoStack(); michael@0: michael@0: array.count = 0; michael@0: // The pseudostack contains an "EnterJIT" frame whenever we enter michael@0: // JIT code with profiling enabled; the stack pointer value points michael@0: // the saved registers. We use this to unwind resume unwinding michael@0: // after encounting JIT code. michael@0: for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) { michael@0: // The pseudostack grows towards higher indices, so we iterate michael@0: // backwards (from callee to caller). michael@0: volatile StackEntry &entry = pseudoStack->mStack[i - 1]; michael@0: if (!entry.js() && strcmp(entry.label(), "EnterJIT") == 0) { michael@0: // Found JIT entry frame. Unwind up to that point (i.e., force michael@0: // the stack walk to stop before the block of saved registers; michael@0: // note that it yields nondecreasing stack pointers), then restore michael@0: // the saved state. michael@0: uint32_t *vSP = reinterpret_cast(entry.stackAddress()); michael@0: michael@0: array.count += EHABIStackWalk(*mcontext, michael@0: /* stackBase = */ vSP, michael@0: sp_array + array.count, michael@0: pc_array + array.count, michael@0: array.size - array.count); michael@0: michael@0: memset(&savedContext, 0, sizeof(savedContext)); michael@0: // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp michael@0: savedContext.arm_r4 = *vSP++; michael@0: savedContext.arm_r5 = *vSP++; michael@0: savedContext.arm_r6 = *vSP++; michael@0: savedContext.arm_r7 = *vSP++; michael@0: savedContext.arm_r8 = *vSP++; michael@0: savedContext.arm_r9 = *vSP++; michael@0: savedContext.arm_r10 = *vSP++; michael@0: savedContext.arm_fp = *vSP++; michael@0: savedContext.arm_lr = *vSP++; michael@0: savedContext.arm_sp = reinterpret_cast(vSP); michael@0: savedContext.arm_pc = savedContext.arm_lr; michael@0: mcontext = &savedContext; michael@0: } michael@0: } michael@0: michael@0: // Now unwind whatever's left (starting from either the last EnterJIT michael@0: // frame or, if no EnterJIT was found, the original registers). michael@0: array.count += EHABIStackWalk(*mcontext, michael@0: aProfile.GetStackTop(), michael@0: sp_array + array.count, michael@0: pc_array + array.count, michael@0: array.size - array.count); michael@0: michael@0: mergeNativeBacktrace(aProfile, array); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: static michael@0: void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample) michael@0: { michael@0: // Sample michael@0: // 's' tag denotes the start of a sample block michael@0: // followed by 0 or more 'c' tags. michael@0: aProfile.addTag(ProfileEntry('s', "(root)")); michael@0: for (uint32_t i = 0; i < aStack->stackSize(); i++) { michael@0: addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr); michael@0: } michael@0: #ifdef ENABLE_SPS_LEAF_DATA michael@0: if (sample) { michael@0: aProfile.addTag(ProfileEntry('l', (void*)sample->pc)); michael@0: #ifdef ENABLE_ARM_LR_SAVING michael@0: aProfile.addTag(ProfileEntry('L', (void*)sample->lr)); michael@0: #endif michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: void TableTicker::Tick(TickSample* sample) michael@0: { michael@0: if (HasUnwinderThread()) { michael@0: UnwinderTick(sample); michael@0: } else { michael@0: InplaceTick(sample); michael@0: } michael@0: } michael@0: michael@0: void TableTicker::InplaceTick(TickSample* sample) michael@0: { michael@0: ThreadProfile& currThreadProfile = *sample->threadProfile; michael@0: michael@0: PseudoStack* stack = currThreadProfile.GetPseudoStack(); michael@0: bool recordSample = true; michael@0: #if defined(XP_WIN) michael@0: bool powerSample = false; michael@0: #endif michael@0: michael@0: /* Don't process the PeudoStack's markers or honour jankOnly if we're michael@0: immediately sampling the current thread. */ michael@0: if (!sample->isSamplingCurrentThread) { michael@0: // Marker(s) come before the sample michael@0: ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); michael@0: while (pendingMarkersList && pendingMarkersList->peek()) { michael@0: ProfilerMarker* marker = pendingMarkersList->popHead(); michael@0: stack->addStoredMarker(marker); michael@0: currThreadProfile.addTag(ProfileEntry('m', marker)); michael@0: } michael@0: stack->updateGeneration(currThreadProfile.GetGenerationID()); michael@0: michael@0: #if defined(XP_WIN) michael@0: if (mProfilePower) { michael@0: mIntelPowerGadget->TakeSample(); michael@0: powerSample = true; michael@0: } michael@0: #endif michael@0: michael@0: if (mJankOnly) { michael@0: // if we are on a different event we can discard any temporary samples michael@0: // we've kept around michael@0: if (sLastSampledEventGeneration != sCurrentEventGeneration) { michael@0: // XXX: we also probably want to add an entry to the profile to help michael@0: // distinguish which samples are part of the same event. That, or record michael@0: // the event generation in each sample michael@0: currThreadProfile.erase(); michael@0: } michael@0: sLastSampledEventGeneration = sCurrentEventGeneration; michael@0: michael@0: recordSample = false; michael@0: // only record the events when we have a we haven't seen a tracer event for 100ms michael@0: if (!sLastTracerEvent.IsNull()) { michael@0: TimeDuration delta = sample->timestamp - sLastTracerEvent; michael@0: if (delta.ToMilliseconds() > 100.0) { michael@0: recordSample = true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) michael@0: if (mUseStackWalk) { michael@0: doNativeBacktrace(currThreadProfile, sample); michael@0: } else { michael@0: doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); michael@0: } michael@0: #else michael@0: doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); michael@0: #endif michael@0: michael@0: if (recordSample) michael@0: currThreadProfile.flush(); michael@0: michael@0: if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) { michael@0: TimeDuration delta = sample->timestamp - sLastTracerEvent; michael@0: currThreadProfile.addTag(ProfileEntry('r', static_cast(delta.ToMilliseconds()))); michael@0: } michael@0: michael@0: if (sample) { michael@0: TimeDuration delta = sample->timestamp - sStartTime; michael@0: currThreadProfile.addTag(ProfileEntry('t', static_cast(delta.ToMilliseconds()))); michael@0: } michael@0: michael@0: #if defined(XP_WIN) michael@0: if (powerSample) { michael@0: currThreadProfile.addTag(ProfileEntry('p', static_cast(mIntelPowerGadget->GetTotalPackagePowerInWatts()))); michael@0: } michael@0: #endif michael@0: michael@0: if (sLastFrameNumber != sFrameNumber) { michael@0: currThreadProfile.addTag(ProfileEntry('f', sFrameNumber)); michael@0: sLastFrameNumber = sFrameNumber; michael@0: } michael@0: } michael@0: michael@0: namespace { michael@0: michael@0: SyncProfile* NewSyncProfile() michael@0: { michael@0: PseudoStack* stack = tlsPseudoStack.get(); michael@0: if (!stack) { michael@0: MOZ_ASSERT(stack); michael@0: return nullptr; michael@0: } michael@0: Thread::tid_t tid = Thread::GetCurrentId(); michael@0: michael@0: SyncProfile* profile = new SyncProfile("SyncProfile", michael@0: GET_BACKTRACE_DEFAULT_ENTRY, michael@0: stack, tid, NS_IsMainThread()); michael@0: return profile; michael@0: } michael@0: michael@0: } // anonymous namespace michael@0: michael@0: SyncProfile* TableTicker::GetBacktrace() michael@0: { michael@0: SyncProfile* profile = NewSyncProfile(); michael@0: michael@0: TickSample sample; michael@0: sample.threadProfile = profile; michael@0: michael@0: #if defined(HAVE_NATIVE_UNWIND) michael@0: #if defined(XP_WIN) || defined(LINUX) michael@0: tickcontext_t context; michael@0: sample.PopulateContext(&context); michael@0: #elif defined(XP_MACOSX) michael@0: sample.PopulateContext(nullptr); michael@0: #endif michael@0: #endif michael@0: michael@0: sample.isSamplingCurrentThread = true; michael@0: sample.timestamp = mozilla::TimeStamp::Now(); michael@0: michael@0: if (!HasUnwinderThread()) { michael@0: profile->BeginUnwind(); michael@0: } michael@0: michael@0: Tick(&sample); michael@0: michael@0: if (!HasUnwinderThread()) { michael@0: profile->EndUnwind(); michael@0: } michael@0: michael@0: return profile; michael@0: } michael@0: michael@0: static void print_callback(const ProfileEntry& entry, const char* tagStringData) michael@0: { michael@0: switch (entry.getTagName()) { michael@0: case 's': michael@0: case 'c': michael@0: printf_stderr(" %s\n", tagStringData); michael@0: } michael@0: } michael@0: michael@0: void mozilla_sampler_print_location1() michael@0: { michael@0: if (!stack_key_initialized) michael@0: profiler_init(nullptr); michael@0: michael@0: SyncProfile* syncProfile = NewSyncProfile(); michael@0: if (!syncProfile) { michael@0: return; michael@0: } michael@0: michael@0: syncProfile->BeginUnwind(); michael@0: doSampleStackTrace(syncProfile->GetPseudoStack(), *syncProfile, nullptr); michael@0: syncProfile->EndUnwind(); michael@0: michael@0: printf_stderr("Backtrace:\n"); michael@0: syncProfile->IterateTags(print_callback); michael@0: delete syncProfile; michael@0: } michael@0: michael@0: