void mozilla_sampler_stop() { if (!stack_key_initialized) mozilla_sampler_init(); TableTicker *t = tlsTicker.get(); if (!t) { return; } bool disableJS = t->ProfileJS(); t->Stop(); delete t; tlsTicker.set(NULL); ProfileStack *stack = tlsStack.get(); ASSERT(stack != NULL); if (disableJS) stack->disableJSSampling(); nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-stopped", nullptr); }
// Values are only honored on the first start void mozilla_sampler_start(int aProfileEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount) { if (!stack_key_initialized) mozilla_sampler_init(); ProfileStack *stack = tlsStack.get(); if (!stack) { ASSERT(false); return; } mozilla_sampler_stop(); TableTicker *t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL, aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY, stack, aFeatures, aFeatureCount); tlsTicker.set(t); t->Start(); if (t->ProfileJS()) stack->enableJSSampling(); nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) os->NotifyObservers(nullptr, "profiler-started", nullptr); }
void TableTicker::Tick(TickSample* sample) { // Marker(s) come before the sample ProfileStack* stack = mPrimaryThreadProfile.GetStack(); for (int i = 0; stack->getMarker(i) != NULL; i++) { mPrimaryThreadProfile.addTag(ProfileEntry('m', stack->getMarker(i))); } stack->mQueueClearMarker = true; bool recordSample = true; if (mJankOnly) { // if we are on a different event we can discard any temporary samples // we've kept around if (sLastSampledEventGeneration != sCurrentEventGeneration) { // XXX: we also probably want to add an entry to the profile to help // distinguish which samples are part of the same event. That, or record // the event generation in each sample mPrimaryThreadProfile.erase(); } sLastSampledEventGeneration = sCurrentEventGeneration; recordSample = false; // only record the events when we have a we haven't seen a tracer event for 100ms if (!sLastTracerEvent.IsNull()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; if (delta.ToMilliseconds() > 100.0) { recordSample = true; } } } #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK) || defined(USE_LIBUNWIND) if (mUseStackWalk) { doBacktrace(mPrimaryThreadProfile, sample); } else { doSampleStackTrace(stack, mPrimaryThreadProfile, sample); } #else doSampleStackTrace(stack, mPrimaryThreadProfile, sample); #endif if (recordSample) mPrimaryThreadProfile.flush(); if (!sLastTracerEvent.IsNull() && sample) { TimeDuration delta = sample->timestamp - sLastTracerEvent; mPrimaryThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds())); } if (sample) { TimeDuration delta = sample->timestamp - mStartTime; mPrimaryThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds())); } if (sLastFrameNumber != sFrameNumber) { mPrimaryThreadProfile.addTag(ProfileEntry('f', sFrameNumber)); sLastFrameNumber = sFrameNumber; } }
void mozilla_sampler_stop() { if (!stack_key_initialized) mozilla_sampler_init(); TableTicker *t = tlsTicker.get(); if (!t) { return; } bool disableJS = t->ProfileJS(); t->Stop(); delete t; tlsTicker.set(NULL); ProfileStack *stack = tlsStack.get(); ASSERT(stack != NULL); if (disableJS) stack->disableJSSampling(); }
// Values are only honored on the first start void mozilla_sampler_start(int aProfileEntries, int aInterval, const char** aFeatures, uint32_t aFeatureCount) { if (!stack_key_initialized) mozilla_sampler_init(); ProfileStack *stack = tlsStack.get(); if (!stack) { ASSERT(false); return; } mozilla_sampler_stop(); TableTicker *t = new TableTicker(aInterval, aProfileEntries, stack, aFeatures, aFeatureCount); tlsTicker.set(t); t->Start(); if (t->ProfileJS()) stack->enableJSSampling(); }
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample) { #ifndef XP_MACOSX uintptr_t thread = GetThreadHandle(platform_data()); MOZ_ASSERT(thread); #endif void* pc_array[1000]; void* sp_array[1000]; PCArray array = { pc_array, sp_array, mozilla::ArrayLength(pc_array), 0 }; // Start with the current function. StackWalkCallback(aSample->pc, aSample->sp, &array); #ifdef XP_MACOSX pthread_t pt = GetProfiledThread(platform_data()); void *stackEnd = reinterpret_cast<void*>(-1); if (pt) stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt)); nsresult rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd); #else nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread); #endif if (NS_SUCCEEDED(rv)) { aProfile.addTag(ProfileEntry('s', "(root)")); ProfileStack* stack = aProfile.GetStack(); int pseudoStackPos = 0; /* We have two stacks, the native C stack we extracted from unwinding, * and the pseudostack we managed during execution. We want to consolidate * the two in order. We do so by merging using the approximate stack address * when each entry was push. When pushing JS entry we may not now the stack * address in which case we have a NULL stack address in which case we assume * that it follows immediatly the previous element. * * C Stack | Address -- Pseudo Stack | Address * main() | 0x100 run_js() | 0x40 * start() | 0x80 jsCanvas() | NULL * timer() | 0x50 drawLine() | NULL * azure() | 0x10 * * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure() */ // i is the index in C stack starting at main and decreasing // pseudoStackPos is the position in the Pseudo stack starting // at the first frame (run_js in the example) and increasing. for (size_t i = array.count; i > 0; --i) { while (pseudoStackPos < stack->stackSize()) { volatile StackEntry& entry = stack->mStack[pseudoStackPos]; if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress()) break; addProfileEntry(entry, aProfile, stack, array.array[0]); pseudoStackPos++; } aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1])); } } }