Bumping manifests a=b2g-bump
[gecko.git] / tools / profiler / TableTicker.cpp
blobc14ddc0827acb22c879239bc2d8c537b59e5a9d2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <algorithm>
7 #include <string>
8 #include <stdio.h>
9 #include <fstream>
10 #include <sstream>
11 #include "GeckoProfiler.h"
12 #include "SaveProfileTask.h"
13 #include "ProfileEntry.h"
14 #include "SyncProfile.h"
15 #include "platform.h"
16 #include "nsThreadUtils.h"
17 #include "prenv.h"
18 #include "prtime.h"
19 #include "shared-libraries.h"
20 #include "mozilla/StackWalk.h"
21 #include "TableTicker.h"
22 #include "nsXULAppAPI.h"
24 // JSON
25 #include "JSStreamWriter.h"
27 // Meta
28 #include "nsXPCOM.h"
29 #include "nsXPCOMCID.h"
30 #include "nsIHttpProtocolHandler.h"
31 #include "nsServiceManagerUtils.h"
32 #include "nsIXULRuntime.h"
33 #include "nsIXULAppInfo.h"
34 #include "nsDirectoryServiceUtils.h"
35 #include "nsDirectoryServiceDefs.h"
36 #include "nsIObserverService.h"
37 #include "mozilla/Services.h"
38 #include "PlatformMacros.h"
40 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
41 #include "AndroidBridge.h"
42 #endif
44 // JS
45 #include "js/OldDebugAPI.h"
46 #include "js/ProfilingFrameIterator.h"
48 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
49 #define USE_NS_STACKWALK
50 #endif
51 #ifdef USE_NS_STACKWALK
52 #include "nsStackWalk.h"
53 #endif
55 #if defined(XP_WIN)
56 typedef CONTEXT tickcontext_t;
57 #elif defined(LINUX)
58 #include <ucontext.h>
59 typedef ucontext_t tickcontext_t;
60 #endif
62 #if defined(LINUX) || defined(XP_MACOSX)
63 #include <sys/types.h>
64 pid_t gettid();
65 #endif
67 #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK)
68 // Should also work on other Android and ARM Linux, but not tested there yet.
69 #define USE_EHABI_STACKWALK
70 #endif
71 #ifdef USE_EHABI_STACKWALK
72 #include "EHABIStackWalk.h"
73 #endif
75 using std::string;
76 using namespace mozilla;
78 #ifndef MAXPATHLEN
79 #ifdef PATH_MAX
80 #define MAXPATHLEN PATH_MAX
81 #elif defined(MAX_PATH)
82 #define MAXPATHLEN MAX_PATH
83 #elif defined(_MAX_PATH)
84 #define MAXPATHLEN _MAX_PATH
85 #elif defined(CCHMAXPATH)
86 #define MAXPATHLEN CCHMAXPATH
87 #else
88 #define MAXPATHLEN 1024
89 #endif
90 #endif
92 ///////////////////////////////////////////////////////////////////////
93 // BEGIN SaveProfileTask et al
95 std::string GetSharedLibraryInfoString();
97 void TableTicker::HandleSaveRequest()
99 if (!mSaveRequested)
100 return;
101 mSaveRequested = false;
103 // TODO: Use use the ipc/chromium Tasks here to support processes
104 // without XPCOM.
105 nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
106 NS_DispatchToMainThread(runnable);
109 void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
111 b.BeginObject();
113 b.NameValue("version", 2);
114 b.NameValue("interval", interval());
115 b.NameValue("stackwalk", mUseStackWalk);
116 b.NameValue("jank", mJankOnly);
117 b.NameValue("processType", XRE_GetProcessType());
119 mozilla::TimeDuration delta = mozilla::TimeStamp::Now() - sStartTime;
120 b.NameValue("startTime", static_cast<double>(PR_Now()/1000.0 - delta.ToMilliseconds()));
122 nsresult res;
123 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
124 if (!NS_FAILED(res)) {
125 nsAutoCString string;
127 res = http->GetPlatform(string);
128 if (!NS_FAILED(res))
129 b.NameValue("platform", string.Data());
131 res = http->GetOscpu(string);
132 if (!NS_FAILED(res))
133 b.NameValue("oscpu", string.Data());
135 res = http->GetMisc(string);
136 if (!NS_FAILED(res))
137 b.NameValue("misc", string.Data());
140 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
141 if (runtime) {
142 nsAutoCString string;
144 res = runtime->GetXPCOMABI(string);
145 if (!NS_FAILED(res))
146 b.NameValue("abi", string.Data());
148 res = runtime->GetWidgetToolkit(string);
149 if (!NS_FAILED(res))
150 b.NameValue("toolkit", string.Data());
153 nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
154 if (appInfo) {
155 nsAutoCString string;
157 res = appInfo->GetName(string);
158 if (!NS_FAILED(res))
159 b.NameValue("product", string.Data());
162 b.EndObject();
165 void TableTicker::ToStreamAsJSON(std::ostream& stream)
167 JSStreamWriter b(stream);
168 StreamJSObject(b);
171 JSObject* TableTicker::ToJSObject(JSContext *aCx)
173 JS::RootedValue val(aCx);
174 std::stringstream ss;
176 // Define a scope to prevent a moving GC during ~JSStreamWriter from
177 // trashing the return value.
178 JSStreamWriter b(ss);
179 StreamJSObject(b);
180 NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
181 JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
183 return &val.toObject();
186 struct SubprocessClosure {
187 SubprocessClosure(JSStreamWriter *aWriter)
188 : mWriter(aWriter)
191 JSStreamWriter* mWriter;
194 void SubProcessCallback(const char* aProfile, void* aClosure)
196 // Called by the observer to get their profile data included
197 // as a sub profile
198 SubprocessClosure* closure = (SubprocessClosure*)aClosure;
200 // Add the string profile into the profile
201 closure->mWriter->Value(aProfile);
205 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
206 static
207 void BuildJavaThreadJSObject(JSStreamWriter& b)
209 b.BeginObject();
211 b.NameValue("name", "Java Main Thread");
213 b.Name("samples");
214 b.BeginArray();
216 // for each sample
217 for (int sampleId = 0; true; sampleId++) {
218 bool firstRun = true;
219 // for each frame
220 for (int frameId = 0; true; frameId++) {
221 nsCString result;
222 bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
223 // when we run out of frames, we stop looping
224 if (!hasFrame) {
225 // if we found at least one frame, we have objects to close
226 if (!firstRun) {
227 b.EndArray();
228 b.EndObject();
230 break;
232 // the first time around, open the sample object and frames array
233 if (firstRun) {
234 firstRun = false;
236 double sampleTime =
237 mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
239 b.BeginObject();
240 b.NameValue("time", sampleTime);
242 b.Name("frames");
243 b.BeginArray();
245 // add a frame to the sample
246 b.BeginObject();
247 b.NameValue("location", result.BeginReading());
248 b.EndObject();
250 // if we found no frames for this sample, we are done
251 if (firstRun) {
252 break;
256 b.EndArray();
258 b.EndObject();
260 #endif
262 void TableTicker::StreamJSObject(JSStreamWriter& b)
264 b.BeginObject();
265 // Put shared library info
266 b.NameValue("libs", GetSharedLibraryInfoString().c_str());
268 // Put meta data
269 b.Name("meta");
270 StreamMetaJSCustomObject(b);
272 // Lists the samples for each ThreadProfile
273 b.Name("threads");
274 b.BeginArray();
276 SetPaused(true);
279 mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
281 for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
282 // Thread not being profiled, skip it
283 if (!sRegisteredThreads->at(i)->Profile())
284 continue;
286 MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
288 sRegisteredThreads->at(i)->Profile()->StreamJSObject(b);
292 if (Sampler::CanNotifyObservers()) {
293 // Send a event asking any subprocesses (plugins) to
294 // give us their information
295 SubprocessClosure closure(&b);
296 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
297 if (os) {
298 nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
299 os->NotifyObservers(pse, "profiler-subprocess", nullptr);
303 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
304 if (ProfileJava()) {
305 mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
307 BuildJavaThreadJSObject(b);
309 mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
311 #endif
313 SetPaused(false);
314 b.EndArray();
316 b.EndObject();
319 // END SaveProfileTask et al
320 ////////////////////////////////////////////////////////////////////////
322 static
323 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
325 aProfile.addTag(ProfileEntry(aTagName, ""));
326 // Add one to store the null termination
327 size_t strLen = strlen(aStr) + 1;
328 for (size_t j = 0; j < strLen;) {
329 // Store as many characters in the void* as the platform allows
330 char text[sizeof(void*)];
331 size_t len = sizeof(void*)/sizeof(char);
332 if (j+len >= strLen) {
333 len = strLen - j;
335 memcpy(text, &aStr[j], len);
336 j += sizeof(void*)/sizeof(char);
337 // Cast to *((void**) to pass the text data to a void*
338 aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
342 static
343 void addPseudoEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
344 PseudoStack *stack, void *lastpc)
346 // Pseudo-frames with the ASMJS flag are just annotations and should not be
347 // recorded in the profile.
348 if (entry.hasFlag(StackEntry::ASMJS))
349 return;
351 int lineno = -1;
353 // First entry has tagName 's' (start)
354 // Check for magic pointer bit 1 to indicate copy
355 const char* sampleLabel = entry.label();
356 if (entry.isCopyLabel()) {
357 // Store the string using 1 or more 'd' (dynamic) tags
358 // that will happen to the preceding tag
360 addDynamicTag(aProfile, 'c', sampleLabel);
361 if (entry.isJs()) {
362 if (!entry.pc()) {
363 // The JIT only allows the top-most entry to have a nullptr pc
364 MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
365 // If stack-walking was disabled, then that's just unfortunate
366 if (lastpc) {
367 jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
368 lastpc);
369 if (jspc) {
370 lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc);
373 } else {
374 lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
376 } else {
377 lineno = entry.line();
379 } else {
380 aProfile.addTag(ProfileEntry('c', sampleLabel));
382 // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
383 // line for js entries as well.
384 if (entry.isCpp()) {
385 lineno = entry.line();
389 if (lineno != -1) {
390 aProfile.addTag(ProfileEntry('n', lineno));
393 uint32_t category = entry.category();
394 MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
395 MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));
397 if (category) {
398 aProfile.addTag(ProfileEntry('y', (int)category));
402 struct NativeStack
404 void** pc_array;
405 void** sp_array;
406 size_t size;
407 size_t count;
410 struct JSFrame
412 void* stackAddress;
413 const char* label;
416 static
417 void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
419 PseudoStack* pseudoStack = aProfile.GetPseudoStack();
420 volatile StackEntry *pseudoFrames = pseudoStack->mStack;
421 uint32_t pseudoCount = pseudoStack->stackSize();
423 // Make a copy of the JS stack into a JSFrame array. This is necessary since,
424 // like the native stack, the JS stack is iterated youngest-to-oldest and we
425 // need to iterate oldest-to-youngest when adding entries to aProfile.
427 JSFrame jsFrames[1000];
428 uint32_t jsCount = 0;
429 if (aSample && pseudoStack->mRuntime) {
430 JS::ProfilingFrameIterator::RegisterState registerState;
431 registerState.pc = aSample->pc;
432 registerState.sp = aSample->sp;
433 #ifdef ENABLE_ARM_LR_SAVING
434 registerState.lr = aSample->lr;
435 #endif
437 JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime, registerState);
438 for (; jsCount < mozilla::ArrayLength(jsFrames) && !jsIter.done(); ++jsCount, ++jsIter) {
439 jsFrames[jsCount].stackAddress = jsIter.stackAddress();
440 jsFrames[jsCount].label = jsIter.label();
444 // Start the sample with a root entry.
445 aProfile.addTag(ProfileEntry('s', "(root)"));
447 // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
448 // native arrays are ordered youngest-to-oldest. We must add frames to
449 // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
450 // and JS and native arrays backwards. Note: this means the terminating
451 // condition jsIndex and nativeIndex is being < 0.
452 uint32_t pseudoIndex = 0;
453 int32_t jsIndex = jsCount - 1;
454 int32_t nativeIndex = aNativeStack.count - 1;
456 // Iterate as long as there is at least one frame remaining.
457 while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
458 // There are 1 to 3 frames available. Find and add the oldest. Handle pseudo
459 // frames first, since there are two special cases that must be considered
460 // before everything else.
461 if (pseudoIndex != pseudoCount) {
462 volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
464 // isJs pseudo-stack frames assume the stackAddress of the preceding isCpp
465 // pseudo-stack frame. If we arrive at an isJs pseudo frame, we've already
466 // encountered the preceding isCpp stack frame and it was oldest, we can
467 // assume the isJs frame is oldest without checking other frames.
468 if (pseudoFrame.isJs()) {
469 addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
470 pseudoIndex++;
471 continue;
474 // Currently, only asm.js frames use the JS stack and Ion/Baseline/Interp
475 // frames use the pseudo stack. In the optimized asm.js->Ion call path, no
476 // isCpp frame is pushed, leading to the callstack:
477 // old | pseudo isCpp | asm.js | pseudo isJs | new
478 // Since there is no interleaving isCpp pseudo frame between the asm.js
479 // and isJs pseudo frame, the above isJs logic will render the callstack:
480 // old | pseudo isCpp | pseudo isJs | asm.js | new
481 // which is wrong. To deal with this, a pseudo isCpp frame pushed right
482 // before entering asm.js flagged with StackEntry::ASMJS. When we see this
483 // flag, we first push all the asm.js frames (up to the next frame with a
484 // stackAddress) before pushing the isJs frames. There is no Ion->asm.js
485 // fast path, so we don't have to worry about asm.js->Ion->asm.js.
487 // (This and the above isJs special cases can be removed once all JS
488 // execution modes switch from the pseudo stack to the JS stack.)
489 if (pseudoFrame.hasFlag(StackEntry::ASMJS)) {
490 void *stopStackAddress = nullptr;
491 for (uint32_t i = pseudoIndex + 1; i != pseudoCount; i++) {
492 if (pseudoFrames[i].isCpp()) {
493 stopStackAddress = pseudoFrames[i].stackAddress();
494 break;
498 if (nativeIndex >= 0) {
499 stopStackAddress = std::max(stopStackAddress, aNativeStack.sp_array[nativeIndex]);
502 while (jsIndex >= 0 && jsFrames[jsIndex].stackAddress > stopStackAddress) {
503 addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label);
504 jsIndex--;
507 pseudoIndex++;
508 continue;
511 // Finally, consider the normal case of a plain C++ pseudo-frame.
512 if ((jsIndex < 0 || pseudoFrame.stackAddress() > jsFrames[jsIndex].stackAddress) &&
513 (nativeIndex < 0 || pseudoFrame.stackAddress() > aNativeStack.sp_array[nativeIndex]))
515 // The (C++) pseudo-frame is the oldest.
516 addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
517 pseudoIndex++;
518 continue;
522 if (jsIndex >= 0) {
523 // Test whether the JS frame is the oldest.
524 JSFrame &jsFrame = jsFrames[jsIndex];
525 if ((pseudoIndex == pseudoCount || jsFrame.stackAddress > pseudoFrames[pseudoIndex].stackAddress()) &&
526 (nativeIndex < 0 || jsFrame.stackAddress > aNativeStack.sp_array[nativeIndex]))
528 // The JS frame is the oldest.
529 addDynamicTag(aProfile, 'c', jsFrame.label);
530 jsIndex--;
531 continue;
535 // If execution reaches this point, there must be a native frame and it must
536 // be the oldest.
537 MOZ_ASSERT(nativeIndex >= 0);
538 aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
539 nativeIndex--;
543 #ifdef USE_NS_STACKWALK
544 static
545 void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
547 NativeStack* nativeStack = static_cast<NativeStack*>(aClosure);
548 MOZ_ASSERT(nativeStack->count < nativeStack->size);
549 nativeStack->sp_array[nativeStack->count] = aSP;
550 nativeStack->pc_array[nativeStack->count] = aPC;
551 nativeStack->count++;
554 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
556 #ifndef XP_MACOSX
557 uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
558 MOZ_ASSERT(thread);
559 #endif
560 void* pc_array[1000];
561 void* sp_array[1000];
562 NativeStack nativeStack = {
563 pc_array,
564 sp_array,
565 mozilla::ArrayLength(pc_array),
569 // Start with the current function.
570 StackWalkCallback(aSample->pc, aSample->sp, &nativeStack);
572 uint32_t maxFrames = uint32_t(nativeStack.size - nativeStack.count);
573 #ifdef XP_MACOSX
574 pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
575 void *stackEnd = reinterpret_cast<void*>(-1);
576 if (pt)
577 stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
578 nsresult rv = NS_OK;
579 if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
580 rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
581 maxFrames, &nativeStack,
582 reinterpret_cast<void**>(aSample->fp), stackEnd);
583 #else
584 void *platformData = nullptr;
585 #ifdef XP_WIN
586 if (aSample->isSamplingCurrentThread) {
587 // In this case we want NS_StackWalk to know that it's walking the
588 // current thread's stack, so we pass 0 as the thread handle.
589 thread = 0;
591 platformData = aSample->context;
592 #endif // XP_WIN
594 nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
595 &nativeStack, thread, platformData);
596 #endif
597 if (NS_SUCCEEDED(rv))
598 mergeStacksIntoProfile(aProfile, aSample, nativeStack);
600 #endif
602 #ifdef USE_EHABI_STACKWALK
603 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
605 void *pc_array[1000];
606 void *sp_array[1000];
607 NativeStack nativeStack = {
608 pc_array,
609 sp_array,
610 mozilla::ArrayLength(pc_array),
614 const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
615 mcontext_t savedContext;
616 PseudoStack *pseudoStack = aProfile.GetPseudoStack();
618 nativeStack.count = 0;
619 // The pseudostack contains an "EnterJIT" frame whenever we enter
620 // JIT code with profiling enabled; the stack pointer value points
621 // the saved registers. We use this to unwind resume unwinding
622 // after encounting JIT code.
623 for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
624 // The pseudostack grows towards higher indices, so we iterate
625 // backwards (from callee to caller).
626 volatile StackEntry &entry = pseudoStack->mStack[i - 1];
627 if (!entry.isJs() && strcmp(entry.label(), "EnterJIT") == 0) {
628 // Found JIT entry frame. Unwind up to that point (i.e., force
629 // the stack walk to stop before the block of saved registers;
630 // note that it yields nondecreasing stack pointers), then restore
631 // the saved state.
632 uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
634 nativeStack.count += EHABIStackWalk(*mcontext,
635 /* stackBase = */ vSP,
636 sp_array + nativeStack.count,
637 pc_array + nativeStack.count,
638 nativeStack.size - nativeStack.count);
640 memset(&savedContext, 0, sizeof(savedContext));
641 // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
642 savedContext.arm_r4 = *vSP++;
643 savedContext.arm_r5 = *vSP++;
644 savedContext.arm_r6 = *vSP++;
645 savedContext.arm_r7 = *vSP++;
646 savedContext.arm_r8 = *vSP++;
647 savedContext.arm_r9 = *vSP++;
648 savedContext.arm_r10 = *vSP++;
649 savedContext.arm_fp = *vSP++;
650 savedContext.arm_lr = *vSP++;
651 savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
652 savedContext.arm_pc = savedContext.arm_lr;
653 mcontext = &savedContext;
657 // Now unwind whatever's left (starting from either the last EnterJIT
658 // frame or, if no EnterJIT was found, the original registers).
659 nativeStack.count += EHABIStackWalk(*mcontext,
660 aProfile.GetStackTop(),
661 sp_array + nativeStack.count,
662 pc_array + nativeStack.count,
663 nativeStack.size - nativeStack.count);
665 mergeStacksIntoProfile(aProfile, aSample, nativeStack);
668 #endif
670 static
671 void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddLeafAddresses)
673 NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
674 mergeStacksIntoProfile(aProfile, aSample, nativeStack);
676 #ifdef ENABLE_SPS_LEAF_DATA
677 if (aSample && aAddLeafAddresses) {
678 aProfile.addTag(ProfileEntry('l', (void*)aSample->pc));
679 #ifdef ENABLE_ARM_LR_SAVING
680 aProfile.addTag(ProfileEntry('L', (void*)aSample->lr));
681 #endif
683 #endif
686 void TableTicker::Tick(TickSample* sample)
688 if (HasUnwinderThread()) {
689 UnwinderTick(sample);
690 } else {
691 InplaceTick(sample);
695 void TableTicker::InplaceTick(TickSample* sample)
697 ThreadProfile& currThreadProfile = *sample->threadProfile;
699 PseudoStack* stack = currThreadProfile.GetPseudoStack();
700 stack->updateGeneration(currThreadProfile.GetGenerationID());
701 bool recordSample = true;
702 #if defined(XP_WIN)
703 bool powerSample = false;
704 #endif
706 /* Don't process the PeudoStack's markers or honour jankOnly if we're
707 immediately sampling the current thread. */
708 if (!sample->isSamplingCurrentThread) {
709 // Marker(s) come before the sample
710 ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
711 while (pendingMarkersList && pendingMarkersList->peek()) {
712 ProfilerMarker* marker = pendingMarkersList->popHead();
713 stack->addStoredMarker(marker);
714 currThreadProfile.addTag(ProfileEntry('m', marker));
717 #if defined(XP_WIN)
718 if (mProfilePower) {
719 mIntelPowerGadget->TakeSample();
720 powerSample = true;
722 #endif
724 if (mJankOnly) {
725 // if we are on a different event we can discard any temporary samples
726 // we've kept around
727 if (sLastSampledEventGeneration != sCurrentEventGeneration) {
728 // XXX: we also probably want to add an entry to the profile to help
729 // distinguish which samples are part of the same event. That, or record
730 // the event generation in each sample
731 currThreadProfile.erase();
733 sLastSampledEventGeneration = sCurrentEventGeneration;
735 recordSample = false;
736 // only record the events when we have a we haven't seen a tracer event for 100ms
737 if (!sLastTracerEvent.IsNull()) {
738 mozilla::TimeDuration delta = sample->timestamp - sLastTracerEvent;
739 if (delta.ToMilliseconds() > 100.0) {
740 recordSample = true;
746 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
747 if (mUseStackWalk) {
748 doNativeBacktrace(currThreadProfile, sample);
749 } else {
750 doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
752 #else
753 doSampleStackTrace(currThreadProfile, sample, mAddLeafAddresses);
754 #endif
756 if (recordSample)
757 currThreadProfile.flush();
759 if (sample && currThreadProfile.GetThreadResponsiveness()->HasData()) {
760 mozilla::TimeDuration delta = currThreadProfile.GetThreadResponsiveness()->GetUnresponsiveDuration(sample->timestamp);
761 currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())));
764 if (sample) {
765 mozilla::TimeDuration delta = sample->timestamp - sStartTime;
766 currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())));
769 // rssMemory is equal to 0 when we are not recording.
770 if (sample && sample->rssMemory != 0) {
771 currThreadProfile.addTag(ProfileEntry('R', static_cast<float>(sample->rssMemory)));
774 // ussMemory is equal to 0 when we are not recording.
775 if (sample && sample->ussMemory != 0) {
776 currThreadProfile.addTag(ProfileEntry('U', static_cast<float>(sample->ussMemory)));
779 #if defined(XP_WIN)
780 if (powerSample) {
781 currThreadProfile.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
783 #endif
785 if (sLastFrameNumber != sFrameNumber) {
786 currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
787 sLastFrameNumber = sFrameNumber;
791 namespace {
793 SyncProfile* NewSyncProfile()
795 PseudoStack* stack = tlsPseudoStack.get();
796 if (!stack) {
797 MOZ_ASSERT(stack);
798 return nullptr;
800 Thread::tid_t tid = Thread::GetCurrentId();
802 ThreadInfo* info = new ThreadInfo("SyncProfile", tid, NS_IsMainThread(), stack, nullptr);
803 SyncProfile* profile = new SyncProfile(info, GET_BACKTRACE_DEFAULT_ENTRY);
804 return profile;
807 } // anonymous namespace
809 SyncProfile* TableTicker::GetBacktrace()
811 SyncProfile* profile = NewSyncProfile();
813 TickSample sample;
814 sample.threadProfile = profile;
816 #if defined(HAVE_NATIVE_UNWIND)
817 #if defined(XP_WIN) || defined(LINUX)
818 tickcontext_t context;
819 sample.PopulateContext(&context);
820 #elif defined(XP_MACOSX)
821 sample.PopulateContext(nullptr);
822 #endif
823 #endif
825 sample.isSamplingCurrentThread = true;
826 sample.timestamp = mozilla::TimeStamp::Now();
828 if (!HasUnwinderThread()) {
829 profile->BeginUnwind();
832 Tick(&sample);
834 if (!HasUnwinderThread()) {
835 profile->EndUnwind();
838 return profile;
841 static void print_callback(const ProfileEntry& entry, const char* tagStringData)
843 switch (entry.getTagName()) {
844 case 's':
845 case 'c':
846 printf_stderr(" %s\n", tagStringData);
850 void mozilla_sampler_print_location1()
852 if (!stack_key_initialized)
853 profiler_init(nullptr);
855 SyncProfile* syncProfile = NewSyncProfile();
856 if (!syncProfile) {
857 return;
860 syncProfile->BeginUnwind();
861 doSampleStackTrace(*syncProfile, nullptr, false);
862 syncProfile->EndUnwind();
864 printf_stderr("Backtrace:\n");
865 syncProfile->IterateTags(print_callback);
866 ThreadInfo* info = syncProfile->GetThreadInfo();
867 delete syncProfile;
868 delete info;