tools/profiler/TableTicker.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:65cd9aa095a1
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/. */
5
6 #include <string>
7 #include <stdio.h>
8 #include <fstream>
9 #include <sstream>
10 #include "GeckoProfiler.h"
11 #include "SaveProfileTask.h"
12 #include "ProfileEntry.h"
13 #include "SyncProfile.h"
14 #include "platform.h"
15 #include "nsThreadUtils.h"
16 #include "prenv.h"
17 #include "prtime.h"
18 #include "shared-libraries.h"
19 #include "mozilla/StackWalk.h"
20 #include "TableTicker.h"
21 #include "nsXULAppAPI.h"
22
23 // JSON
24 #include "JSStreamWriter.h"
25
26 // Meta
27 #include "nsXPCOM.h"
28 #include "nsXPCOMCID.h"
29 #include "nsIHttpProtocolHandler.h"
30 #include "nsServiceManagerUtils.h"
31 #include "nsIXULRuntime.h"
32 #include "nsIXULAppInfo.h"
33 #include "nsDirectoryServiceUtils.h"
34 #include "nsDirectoryServiceDefs.h"
35 #include "nsIObserverService.h"
36 #include "mozilla/Services.h"
37 #include "PlatformMacros.h"
38
39 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
40 #include "AndroidBridge.h"
41 #endif
42
43 // JS
44 #include "js/OldDebugAPI.h"
45
46 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
47 #define USE_NS_STACKWALK
48 #endif
49 #ifdef USE_NS_STACKWALK
50 #include "nsStackWalk.h"
51 #endif
52
53 #if defined(XP_WIN)
54 typedef CONTEXT tickcontext_t;
55 #elif defined(LINUX)
56 #include <ucontext.h>
57 typedef ucontext_t tickcontext_t;
58 #endif
59
60 #if defined(LINUX) || defined(XP_MACOSX)
61 #include <sys/types.h>
62 pid_t gettid();
63 #endif
64
65 #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK)
66 // Should also work on other Android and ARM Linux, but not tested there yet.
67 #define USE_EHABI_STACKWALK
68 #endif
69 #ifdef USE_EHABI_STACKWALK
70 #include "EHABIStackWalk.h"
71 #endif
72
73 using std::string;
74 using namespace mozilla;
75
76 #ifndef MAXPATHLEN
77 #ifdef PATH_MAX
78 #define MAXPATHLEN PATH_MAX
79 #elif defined(MAX_PATH)
80 #define MAXPATHLEN MAX_PATH
81 #elif defined(_MAX_PATH)
82 #define MAXPATHLEN _MAX_PATH
83 #elif defined(CCHMAXPATH)
84 #define MAXPATHLEN CCHMAXPATH
85 #else
86 #define MAXPATHLEN 1024
87 #endif
88 #endif
89
90 ///////////////////////////////////////////////////////////////////////
91 // BEGIN SaveProfileTask et al
92
93 std::string GetSharedLibraryInfoString();
94
95 void TableTicker::HandleSaveRequest()
96 {
97 if (!mSaveRequested)
98 return;
99 mSaveRequested = false;
100
101 // TODO: Use use the ipc/chromium Tasks here to support processes
102 // without XPCOM.
103 nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
104 NS_DispatchToMainThread(runnable);
105 }
106
107 void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
108 {
109 b.BeginObject();
110
111 b.NameValue("version", 2);
112 b.NameValue("interval", interval());
113 b.NameValue("stackwalk", mUseStackWalk);
114 b.NameValue("jank", mJankOnly);
115 b.NameValue("processType", XRE_GetProcessType());
116
117 TimeDuration delta = TimeStamp::Now() - sStartTime;
118 b.NameValue("startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
119
120 nsresult res;
121 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
122 if (!NS_FAILED(res)) {
123 nsAutoCString string;
124
125 res = http->GetPlatform(string);
126 if (!NS_FAILED(res))
127 b.NameValue("platform", string.Data());
128
129 res = http->GetOscpu(string);
130 if (!NS_FAILED(res))
131 b.NameValue("oscpu", string.Data());
132
133 res = http->GetMisc(string);
134 if (!NS_FAILED(res))
135 b.NameValue("misc", string.Data());
136 }
137
138 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
139 if (runtime) {
140 nsAutoCString string;
141
142 res = runtime->GetXPCOMABI(string);
143 if (!NS_FAILED(res))
144 b.NameValue("abi", string.Data());
145
146 res = runtime->GetWidgetToolkit(string);
147 if (!NS_FAILED(res))
148 b.NameValue("toolkit", string.Data());
149 }
150
151 nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
152 if (appInfo) {
153 nsAutoCString string;
154
155 res = appInfo->GetName(string);
156 if (!NS_FAILED(res))
157 b.NameValue("product", string.Data());
158 }
159
160 b.EndObject();
161 }
162
163 void TableTicker::ToStreamAsJSON(std::ostream& stream)
164 {
165 JSStreamWriter b(stream);
166 StreamJSObject(b);
167 }
168
169 JSObject* TableTicker::ToJSObject(JSContext *aCx)
170 {
171 JS::RootedValue val(aCx);
172 std::stringstream ss;
173 {
174 // Define a scope to prevent a moving GC during ~JSStreamWriter from
175 // trashing the return value.
176 JSStreamWriter b(ss);
177 StreamJSObject(b);
178 NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
179 JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
180 }
181 return &val.toObject();
182 }
183
184 struct SubprocessClosure {
185 SubprocessClosure(JSStreamWriter *aWriter)
186 : mWriter(aWriter)
187 {}
188
189 JSStreamWriter* mWriter;
190 };
191
192 void SubProcessCallback(const char* aProfile, void* aClosure)
193 {
194 // Called by the observer to get their profile data included
195 // as a sub profile
196 SubprocessClosure* closure = (SubprocessClosure*)aClosure;
197
198 // Add the string profile into the profile
199 closure->mWriter->Value(aProfile);
200 }
201
202
203 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
204 static
205 void BuildJavaThreadJSObject(JSStreamWriter& b)
206 {
207 b.BeginObject();
208
209 b.NameValue("name", "Java Main Thread");
210
211 b.Name("samples");
212 b.BeginArray();
213
214 // for each sample
215 for (int sampleId = 0; true; sampleId++) {
216 bool firstRun = true;
217 // for each frame
218 for (int frameId = 0; true; frameId++) {
219 nsCString result;
220 bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
221 // when we run out of frames, we stop looping
222 if (!hasFrame) {
223 // if we found at least one frame, we have objects to close
224 if (!firstRun) {
225 b.EndArray();
226 b.EndObject();
227 }
228 break;
229 }
230 // the first time around, open the sample object and frames array
231 if (firstRun) {
232 firstRun = false;
233
234 double sampleTime =
235 mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
236
237 b.BeginObject();
238 b.NameValue("time", sampleTime);
239
240 b.Name("frames");
241 b.BeginArray();
242 }
243 // add a frame to the sample
244 b.BeginObject();
245 b.NameValue("location", result.BeginReading());
246 b.EndObject();
247 }
248 // if we found no frames for this sample, we are done
249 if (firstRun) {
250 break;
251 }
252 }
253
254 b.EndArray();
255
256 b.EndObject();
257 }
258 #endif
259
260 void TableTicker::StreamJSObject(JSStreamWriter& b)
261 {
262 b.BeginObject();
263 // Put shared library info
264 b.NameValue("libs", GetSharedLibraryInfoString().c_str());
265
266 // Put meta data
267 b.Name("meta");
268 StreamMetaJSCustomObject(b);
269
270 // Lists the samples for each ThreadProfile
271 b.Name("threads");
272 b.BeginArray();
273
274 SetPaused(true);
275
276 {
277 mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
278
279 for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
280 // Thread not being profiled, skip it
281 if (!sRegisteredThreads->at(i)->Profile())
282 continue;
283
284 MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
285
286 sRegisteredThreads->at(i)->Profile()->StreamJSObject(b);
287 }
288 }
289
290 if (Sampler::CanNotifyObservers()) {
291 // Send a event asking any subprocesses (plugins) to
292 // give us their information
293 SubprocessClosure closure(&b);
294 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
295 if (os) {
296 nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
297 os->NotifyObservers(pse, "profiler-subprocess", nullptr);
298 }
299 }
300
301 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
302 if (ProfileJava()) {
303 mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
304
305 BuildJavaThreadJSObject(b);
306
307 mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
308 }
309 #endif
310
311 SetPaused(false);
312 b.EndArray();
313
314 b.EndObject();
315 }
316
317 // END SaveProfileTask et al
318 ////////////////////////////////////////////////////////////////////////
319
320 static
321 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
322 {
323 aProfile.addTag(ProfileEntry(aTagName, ""));
324 // Add one to store the null termination
325 size_t strLen = strlen(aStr) + 1;
326 for (size_t j = 0; j < strLen;) {
327 // Store as many characters in the void* as the platform allows
328 char text[sizeof(void*)];
329 size_t len = sizeof(void*)/sizeof(char);
330 if (j+len >= strLen) {
331 len = strLen - j;
332 }
333 memcpy(text, &aStr[j], len);
334 j += sizeof(void*)/sizeof(char);
335 // Cast to *((void**) to pass the text data to a void*
336 aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
337 }
338 }
339
340 static
341 void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
342 PseudoStack *stack, void *lastpc)
343 {
344 int lineno = -1;
345
346 // First entry has tagName 's' (start)
347 // Check for magic pointer bit 1 to indicate copy
348 const char* sampleLabel = entry.label();
349 if (entry.isCopyLabel()) {
350 // Store the string using 1 or more 'd' (dynamic) tags
351 // that will happen to the preceding tag
352
353 addDynamicTag(aProfile, 'c', sampleLabel);
354 if (entry.js()) {
355 if (!entry.pc()) {
356 // The JIT only allows the top-most entry to have a nullptr pc
357 MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
358 // If stack-walking was disabled, then that's just unfortunate
359 if (lastpc) {
360 jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
361 lastpc);
362 if (jspc) {
363 lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc);
364 }
365 }
366 } else {
367 lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
368 }
369 } else {
370 lineno = entry.line();
371 }
372 } else {
373 aProfile.addTag(ProfileEntry('c', sampleLabel));
374 lineno = entry.line();
375 }
376 if (lineno != -1) {
377 aProfile.addTag(ProfileEntry('n', lineno));
378 }
379 }
380
381 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
382 typedef struct {
383 void** array;
384 void** sp_array;
385 size_t size;
386 size_t count;
387 } PCArray;
388
389 static void mergeNativeBacktrace(ThreadProfile &aProfile, const PCArray &array) {
390 aProfile.addTag(ProfileEntry('s', "(root)"));
391
392 PseudoStack* stack = aProfile.GetPseudoStack();
393 uint32_t pseudoStackPos = 0;
394
395 /* We have two stacks, the native C stack we extracted from unwinding,
396 * and the pseudostack we managed during execution. We want to consolidate
397 * the two in order. We do so by merging using the approximate stack address
398 * when each entry was push. When pushing JS entry we may not now the stack
399 * address in which case we have a nullptr stack address in which case we assume
400 * that it follows immediatly the previous element.
401 *
402 * C Stack | Address -- Pseudo Stack | Address
403 * main() | 0x100 run_js() | 0x40
404 * start() | 0x80 jsCanvas() | nullptr
405 * timer() | 0x50 drawLine() | nullptr
406 * azure() | 0x10
407 *
408 * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure()
409 */
410 // i is the index in C stack starting at main and decreasing
411 // pseudoStackPos is the position in the Pseudo stack starting
412 // at the first frame (run_js in the example) and increasing.
413 for (size_t i = array.count; i > 0; --i) {
414 while (pseudoStackPos < stack->stackSize()) {
415 volatile StackEntry& entry = stack->mStack[pseudoStackPos];
416
417 if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress())
418 break;
419
420 addProfileEntry(entry, aProfile, stack, array.array[0]);
421 pseudoStackPos++;
422 }
423
424 aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1]));
425 }
426 }
427
428 #endif
429
430 #ifdef USE_NS_STACKWALK
431 static
432 void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
433 {
434 PCArray* array = static_cast<PCArray*>(aClosure);
435 MOZ_ASSERT(array->count < array->size);
436 array->sp_array[array->count] = aSP;
437 array->array[array->count] = aPC;
438 array->count++;
439 }
440
441 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
442 {
443 #ifndef XP_MACOSX
444 uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
445 MOZ_ASSERT(thread);
446 #endif
447 void* pc_array[1000];
448 void* sp_array[1000];
449 PCArray array = {
450 pc_array,
451 sp_array,
452 mozilla::ArrayLength(pc_array),
453 0
454 };
455
456 // Start with the current function.
457 StackWalkCallback(aSample->pc, aSample->sp, &array);
458
459 uint32_t maxFrames = uint32_t(array.size - array.count);
460 #ifdef XP_MACOSX
461 pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
462 void *stackEnd = reinterpret_cast<void*>(-1);
463 if (pt)
464 stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
465 nsresult rv = NS_OK;
466 if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
467 rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
468 maxFrames, &array,
469 reinterpret_cast<void**>(aSample->fp), stackEnd);
470 #else
471 void *platformData = nullptr;
472 #ifdef XP_WIN
473 if (aSample->isSamplingCurrentThread) {
474 // In this case we want NS_StackWalk to know that it's walking the
475 // current thread's stack, so we pass 0 as the thread handle.
476 thread = 0;
477 }
478 platformData = aSample->context;
479 #endif // XP_WIN
480
481 nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
482 &array, thread, platformData);
483 #endif
484 if (NS_SUCCEEDED(rv))
485 mergeNativeBacktrace(aProfile, array);
486 }
487 #endif
488
489 #ifdef USE_EHABI_STACKWALK
490 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
491 {
492 void *pc_array[1000];
493 void *sp_array[1000];
494 PCArray array = {
495 pc_array,
496 sp_array,
497 mozilla::ArrayLength(pc_array),
498 0
499 };
500
501 const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
502 mcontext_t savedContext;
503 PseudoStack *pseudoStack = aProfile.GetPseudoStack();
504
505 array.count = 0;
506 // The pseudostack contains an "EnterJIT" frame whenever we enter
507 // JIT code with profiling enabled; the stack pointer value points
508 // the saved registers. We use this to unwind resume unwinding
509 // after encounting JIT code.
510 for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
511 // The pseudostack grows towards higher indices, so we iterate
512 // backwards (from callee to caller).
513 volatile StackEntry &entry = pseudoStack->mStack[i - 1];
514 if (!entry.js() && strcmp(entry.label(), "EnterJIT") == 0) {
515 // Found JIT entry frame. Unwind up to that point (i.e., force
516 // the stack walk to stop before the block of saved registers;
517 // note that it yields nondecreasing stack pointers), then restore
518 // the saved state.
519 uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
520
521 array.count += EHABIStackWalk(*mcontext,
522 /* stackBase = */ vSP,
523 sp_array + array.count,
524 pc_array + array.count,
525 array.size - array.count);
526
527 memset(&savedContext, 0, sizeof(savedContext));
528 // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
529 savedContext.arm_r4 = *vSP++;
530 savedContext.arm_r5 = *vSP++;
531 savedContext.arm_r6 = *vSP++;
532 savedContext.arm_r7 = *vSP++;
533 savedContext.arm_r8 = *vSP++;
534 savedContext.arm_r9 = *vSP++;
535 savedContext.arm_r10 = *vSP++;
536 savedContext.arm_fp = *vSP++;
537 savedContext.arm_lr = *vSP++;
538 savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
539 savedContext.arm_pc = savedContext.arm_lr;
540 mcontext = &savedContext;
541 }
542 }
543
544 // Now unwind whatever's left (starting from either the last EnterJIT
545 // frame or, if no EnterJIT was found, the original registers).
546 array.count += EHABIStackWalk(*mcontext,
547 aProfile.GetStackTop(),
548 sp_array + array.count,
549 pc_array + array.count,
550 array.size - array.count);
551
552 mergeNativeBacktrace(aProfile, array);
553 }
554
555 #endif
556
557 static
558 void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
559 {
560 // Sample
561 // 's' tag denotes the start of a sample block
562 // followed by 0 or more 'c' tags.
563 aProfile.addTag(ProfileEntry('s', "(root)"));
564 for (uint32_t i = 0; i < aStack->stackSize(); i++) {
565 addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr);
566 }
567 #ifdef ENABLE_SPS_LEAF_DATA
568 if (sample) {
569 aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
570 #ifdef ENABLE_ARM_LR_SAVING
571 aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
572 #endif
573 }
574 #endif
575 }
576
577 void TableTicker::Tick(TickSample* sample)
578 {
579 if (HasUnwinderThread()) {
580 UnwinderTick(sample);
581 } else {
582 InplaceTick(sample);
583 }
584 }
585
586 void TableTicker::InplaceTick(TickSample* sample)
587 {
588 ThreadProfile& currThreadProfile = *sample->threadProfile;
589
590 PseudoStack* stack = currThreadProfile.GetPseudoStack();
591 bool recordSample = true;
592 #if defined(XP_WIN)
593 bool powerSample = false;
594 #endif
595
596 /* Don't process the PeudoStack's markers or honour jankOnly if we're
597 immediately sampling the current thread. */
598 if (!sample->isSamplingCurrentThread) {
599 // Marker(s) come before the sample
600 ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
601 while (pendingMarkersList && pendingMarkersList->peek()) {
602 ProfilerMarker* marker = pendingMarkersList->popHead();
603 stack->addStoredMarker(marker);
604 currThreadProfile.addTag(ProfileEntry('m', marker));
605 }
606 stack->updateGeneration(currThreadProfile.GetGenerationID());
607
608 #if defined(XP_WIN)
609 if (mProfilePower) {
610 mIntelPowerGadget->TakeSample();
611 powerSample = true;
612 }
613 #endif
614
615 if (mJankOnly) {
616 // if we are on a different event we can discard any temporary samples
617 // we've kept around
618 if (sLastSampledEventGeneration != sCurrentEventGeneration) {
619 // XXX: we also probably want to add an entry to the profile to help
620 // distinguish which samples are part of the same event. That, or record
621 // the event generation in each sample
622 currThreadProfile.erase();
623 }
624 sLastSampledEventGeneration = sCurrentEventGeneration;
625
626 recordSample = false;
627 // only record the events when we have a we haven't seen a tracer event for 100ms
628 if (!sLastTracerEvent.IsNull()) {
629 TimeDuration delta = sample->timestamp - sLastTracerEvent;
630 if (delta.ToMilliseconds() > 100.0) {
631 recordSample = true;
632 }
633 }
634 }
635 }
636
637 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
638 if (mUseStackWalk) {
639 doNativeBacktrace(currThreadProfile, sample);
640 } else {
641 doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
642 }
643 #else
644 doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
645 #endif
646
647 if (recordSample)
648 currThreadProfile.flush();
649
650 if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) {
651 TimeDuration delta = sample->timestamp - sLastTracerEvent;
652 currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())));
653 }
654
655 if (sample) {
656 TimeDuration delta = sample->timestamp - sStartTime;
657 currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())));
658 }
659
660 #if defined(XP_WIN)
661 if (powerSample) {
662 currThreadProfile.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
663 }
664 #endif
665
666 if (sLastFrameNumber != sFrameNumber) {
667 currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
668 sLastFrameNumber = sFrameNumber;
669 }
670 }
671
672 namespace {
673
674 SyncProfile* NewSyncProfile()
675 {
676 PseudoStack* stack = tlsPseudoStack.get();
677 if (!stack) {
678 MOZ_ASSERT(stack);
679 return nullptr;
680 }
681 Thread::tid_t tid = Thread::GetCurrentId();
682
683 SyncProfile* profile = new SyncProfile("SyncProfile",
684 GET_BACKTRACE_DEFAULT_ENTRY,
685 stack, tid, NS_IsMainThread());
686 return profile;
687 }
688
689 } // anonymous namespace
690
691 SyncProfile* TableTicker::GetBacktrace()
692 {
693 SyncProfile* profile = NewSyncProfile();
694
695 TickSample sample;
696 sample.threadProfile = profile;
697
698 #if defined(HAVE_NATIVE_UNWIND)
699 #if defined(XP_WIN) || defined(LINUX)
700 tickcontext_t context;
701 sample.PopulateContext(&context);
702 #elif defined(XP_MACOSX)
703 sample.PopulateContext(nullptr);
704 #endif
705 #endif
706
707 sample.isSamplingCurrentThread = true;
708 sample.timestamp = mozilla::TimeStamp::Now();
709
710 if (!HasUnwinderThread()) {
711 profile->BeginUnwind();
712 }
713
714 Tick(&sample);
715
716 if (!HasUnwinderThread()) {
717 profile->EndUnwind();
718 }
719
720 return profile;
721 }
722
723 static void print_callback(const ProfileEntry& entry, const char* tagStringData)
724 {
725 switch (entry.getTagName()) {
726 case 's':
727 case 'c':
728 printf_stderr(" %s\n", tagStringData);
729 }
730 }
731
732 void mozilla_sampler_print_location1()
733 {
734 if (!stack_key_initialized)
735 profiler_init(nullptr);
736
737 SyncProfile* syncProfile = NewSyncProfile();
738 if (!syncProfile) {
739 return;
740 }
741
742 syncProfile->BeginUnwind();
743 doSampleStackTrace(syncProfile->GetPseudoStack(), *syncProfile, nullptr);
744 syncProfile->EndUnwind();
745
746 printf_stderr("Backtrace:\n");
747 syncProfile->IterateTags(print_callback);
748 delete syncProfile;
749 }
750
751

mercurial