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/. */
11 #include "GeckoProfiler.h"
12 #include "SaveProfileTask.h"
13 #include "ProfileEntry.h"
14 #include "SyncProfile.h"
16 #include "nsThreadUtils.h"
19 #include "shared-libraries.h"
20 #include "mozilla/StackWalk.h"
21 #include "TableTicker.h"
22 #include "nsXULAppAPI.h"
25 #include "JSStreamWriter.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"
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
51 #ifdef USE_NS_STACKWALK
52 #include "nsStackWalk.h"
56 typedef CONTEXT tickcontext_t
;
59 typedef ucontext_t tickcontext_t
;
62 #if defined(LINUX) || defined(XP_MACOSX)
63 #include <sys/types.h>
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
71 #ifdef USE_EHABI_STACKWALK
72 #include "EHABIStackWalk.h"
76 using namespace mozilla
;
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
88 #define MAXPATHLEN 1024
92 ///////////////////////////////////////////////////////////////////////
93 // BEGIN SaveProfileTask et al
95 std::string
GetSharedLibraryInfoString();
97 void TableTicker::HandleSaveRequest()
101 mSaveRequested
= false;
103 // TODO: Use use the ipc/chromium Tasks here to support processes
105 nsCOMPtr
<nsIRunnable
> runnable
= new SaveProfileTask();
106 NS_DispatchToMainThread(runnable
);
109 void TableTicker::StreamMetaJSCustomObject(JSStreamWriter
& b
)
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()));
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
);
129 b
.NameValue("platform", string
.Data());
131 res
= http
->GetOscpu(string
);
133 b
.NameValue("oscpu", string
.Data());
135 res
= http
->GetMisc(string
);
137 b
.NameValue("misc", string
.Data());
140 nsCOMPtr
<nsIXULRuntime
> runtime
= do_GetService("@mozilla.org/xre/runtime;1");
142 nsAutoCString string
;
144 res
= runtime
->GetXPCOMABI(string
);
146 b
.NameValue("abi", string
.Data());
148 res
= runtime
->GetWidgetToolkit(string
);
150 b
.NameValue("toolkit", string
.Data());
153 nsCOMPtr
<nsIXULAppInfo
> appInfo
= do_GetService("@mozilla.org/xre/app-info;1");
155 nsAutoCString string
;
157 res
= appInfo
->GetName(string
);
159 b
.NameValue("product", string
.Data());
165 void TableTicker::ToStreamAsJSON(std::ostream
& stream
)
167 JSStreamWriter
b(stream
);
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
);
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
)
191 JSStreamWriter
* mWriter
;
194 void SubProcessCallback(const char* aProfile
, void* aClosure
)
196 // Called by the observer to get their profile data included
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)
207 void BuildJavaThreadJSObject(JSStreamWriter
& b
)
211 b
.NameValue("name", "Java Main Thread");
217 for (int sampleId
= 0; true; sampleId
++) {
218 bool firstRun
= true;
220 for (int frameId
= 0; true; frameId
++) {
222 bool hasFrame
= AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId
, frameId
, result
);
223 // when we run out of frames, we stop looping
225 // if we found at least one frame, we have objects to close
232 // the first time around, open the sample object and frames array
237 mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId
);
240 b
.NameValue("time", sampleTime
);
245 // add a frame to the sample
247 b
.NameValue("location", result
.BeginReading());
250 // if we found no frames for this sample, we are done
262 void TableTicker::StreamJSObject(JSStreamWriter
& b
)
265 // Put shared library info
266 b
.NameValue("libs", GetSharedLibraryInfoString().c_str());
270 StreamMetaJSCustomObject(b
);
272 // Lists the samples for each ThreadProfile
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())
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();
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)
305 mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
307 BuildJavaThreadJSObject(b
);
309 mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
319 // END SaveProfileTask et al
320 ////////////////////////////////////////////////////////////////////////
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
) {
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]))));
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
))
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
);
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
367 jsbytecode
*jspc
= js::ProfilingGetPC(stack
->mRuntime
, entry
.script(),
370 lineno
= JS_PCToLineNumber(nullptr, entry
.script(), jspc
);
374 lineno
= JS_PCToLineNumber(nullptr, entry
.script(), entry
.pc());
377 lineno
= entry
.line();
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.
385 lineno
= entry
.line();
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
));
398 aProfile
.addTag(ProfileEntry('y', (int)category
));
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
;
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);
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();
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
);
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);
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
);
535 // If execution reaches this point, there must be a native frame and it must
537 MOZ_ASSERT(nativeIndex
>= 0);
538 aProfile
.addTag(ProfileEntry('l', (void*)aNativeStack
.pc_array
[nativeIndex
]));
543 #ifdef USE_NS_STACKWALK
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
)
557 uintptr_t thread
= GetThreadHandle(aSample
->threadProfile
->GetPlatformData());
560 void* pc_array
[1000];
561 void* sp_array
[1000];
562 NativeStack nativeStack
= {
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
);
574 pthread_t pt
= GetProfiledThread(aSample
->threadProfile
->GetPlatformData());
575 void *stackEnd
= reinterpret_cast<void*>(-1);
577 stackEnd
= static_cast<char*>(pthread_get_stackaddr_np(pt
));
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
);
584 void *platformData
= nullptr;
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.
591 platformData
= aSample
->context
;
594 nsresult rv
= NS_StackWalk(StackWalkCallback
, /* skipFrames */ 0, maxFrames
,
595 &nativeStack
, thread
, platformData
);
597 if (NS_SUCCEEDED(rv
))
598 mergeStacksIntoProfile(aProfile
, aSample
, nativeStack
);
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
= {
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
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
);
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
));
686 void TableTicker::Tick(TickSample
* sample
)
688 if (HasUnwinderThread()) {
689 UnwinderTick(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;
703 bool powerSample
= false;
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
));
719 mIntelPowerGadget
->TakeSample();
725 // if we are on a different event we can discard any temporary samples
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) {
746 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
748 doNativeBacktrace(currThreadProfile
, sample
);
750 doSampleStackTrace(currThreadProfile
, sample
, mAddLeafAddresses
);
753 doSampleStackTrace(currThreadProfile
, sample
, mAddLeafAddresses
);
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())));
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
)));
781 currThreadProfile
.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget
->GetTotalPackagePowerInWatts())));
785 if (sLastFrameNumber
!= sFrameNumber
) {
786 currThreadProfile
.addTag(ProfileEntry('f', sFrameNumber
));
787 sLastFrameNumber
= sFrameNumber
;
793 SyncProfile
* NewSyncProfile()
795 PseudoStack
* stack
= tlsPseudoStack
.get();
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
);
807 } // anonymous namespace
809 SyncProfile
* TableTicker::GetBacktrace()
811 SyncProfile
* profile
= NewSyncProfile();
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);
825 sample
.isSamplingCurrentThread
= true;
826 sample
.timestamp
= mozilla::TimeStamp::Now();
828 if (!HasUnwinderThread()) {
829 profile
->BeginUnwind();
834 if (!HasUnwinderThread()) {
835 profile
->EndUnwind();
841 static void print_callback(const ProfileEntry
& entry
, const char* tagStringData
)
843 switch (entry
.getTagName()) {
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();
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();