Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include <ostream>
6 #include <fstream>
7 #include <sstream>
8 #include <errno.h>
10 #include "ProfilerIOInterposeObserver.h"
11 #include "platform.h"
12 #include "PlatformMacros.h"
13 #include "prenv.h"
14 #include "mozilla/StaticPtr.h"
15 #include "mozilla/ThreadLocal.h"
16 #include "PseudoStack.h"
17 #include "TableTicker.h"
18 #include "UnwinderThread2.h"
19 #include "nsIObserverService.h"
20 #include "nsDirectoryServiceUtils.h"
21 #include "nsDirectoryServiceDefs.h"
22 #include "mozilla/Services.h"
23 #include "nsThreadUtils.h"
24 #include "ProfilerMarkers.h"
25 #include "nsXULAppAPI.h"
27 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
28 #include "AndroidBridge.h"
29 using namespace mozilla::widget::android;
30 #endif
32 mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
33 mozilla::ThreadLocal<TableTicker *> tlsTicker;
34 mozilla::ThreadLocal<void *> tlsStackTop;
35 // We need to track whether we've been initialized otherwise
36 // we end up using tlsStack without initializing it.
37 // Because tlsStack is totally opaque to us we can't reuse
38 // it as the flag itself.
39 bool stack_key_initialized;
41 TimeStamp sLastTracerEvent; // is raced on
42 TimeStamp sStartTime;
43 int sFrameNumber = 0;
44 int sLastFrameNumber = 0;
45 int sInitCount = 0; // Each init must have a matched shutdown.
46 static bool sIsProfiling = false; // is raced on
48 // env variables to control the profiler
49 const char* PROFILER_MODE = "MOZ_PROFILER_MODE";
50 const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL";
51 const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES";
52 const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN";
53 const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES";
55 /* used to keep track of the last event that we sampled during */
56 unsigned int sLastSampledEventGeneration = 0;
58 /* a counter that's incremented everytime we get responsiveness event
59 * note: it might also be worth trackplaing everytime we go around
60 * the event loop */
61 unsigned int sCurrentEventGeneration = 0;
62 /* we don't need to worry about overflow because we only treat the
63 * case of them being the same as special. i.e. we only run into
64 * a problem if 2^32 events happen between samples that we need
65 * to know are associated with different events */
67 std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
68 mozilla::Mutex* Sampler::sRegisteredThreadsMutex = nullptr;
70 TableTicker* Sampler::sActiveSampler;
72 static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
73 sInterposeObserver;
75 // The name that identifies the gecko thread for calls to
76 // profiler_register_thread. For all platform except metro
77 // the thread that calls mozilla_sampler_init is considered
78 // the gecko thread. With metro the gecko thread is
79 // registered later based on this thread name.
80 static const char * gGeckoThreadName = "GeckoMain";
82 void Sampler::Startup() {
83 sRegisteredThreads = new std::vector<ThreadInfo*>();
84 sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex");
85 }
87 void Sampler::Shutdown() {
88 while (sRegisteredThreads->size() > 0) {
89 delete sRegisteredThreads->back();
90 sRegisteredThreads->pop_back();
91 }
93 delete sRegisteredThreadsMutex;
94 delete sRegisteredThreads;
96 // UnregisterThread can be called after shutdown in XPCShell. Thus
97 // we need to point to null to ignore such a call after shutdown.
98 sRegisteredThreadsMutex = nullptr;
99 sRegisteredThreads = nullptr;
100 }
102 ThreadInfo::~ThreadInfo() {
103 free(mName);
105 if (mProfile)
106 delete mProfile;
108 Sampler::FreePlatformData(mPlatformData);
109 }
111 ProfilerMarker::ProfilerMarker(const char* aMarkerName,
112 ProfilerMarkerPayload* aPayload,
113 float aTime)
114 : mMarkerName(strdup(aMarkerName))
115 , mPayload(aPayload)
116 , mTime(aTime)
117 {
118 }
120 ProfilerMarker::~ProfilerMarker() {
121 free(mMarkerName);
122 delete mPayload;
123 }
125 void
126 ProfilerMarker::SetGeneration(int aGenID) {
127 mGenID = aGenID;
128 }
130 float
131 ProfilerMarker::GetTime() {
132 return mTime;
133 }
135 void ProfilerMarker::StreamJSObject(JSStreamWriter& b) const {
136 b.BeginObject();
137 b.NameValue("name", GetMarkerName());
138 // TODO: Store the callsite for this marker if available:
139 // if have location data
140 // b.NameValue(marker, "location", ...);
141 if (mPayload) {
142 b.Name("data");
143 mPayload->StreamPayload(b);
144 }
145 b.NameValue("time", mTime);
146 b.EndObject();
147 }
149 PendingMarkers::~PendingMarkers() {
150 clearMarkers();
151 if (mSignalLock != false) {
152 // We're releasing the pseudostack while it's still in use.
153 // The label macros keep a non ref counted reference to the
154 // stack to avoid a TLS. If these are not all cleared we will
155 // get a use-after-free so better to crash now.
156 abort();
157 }
158 }
160 void
161 PendingMarkers::addMarker(ProfilerMarker *aMarker) {
162 mSignalLock = true;
163 STORE_SEQUENCER();
165 MOZ_ASSERT(aMarker);
166 mPendingMarkers.insert(aMarker);
168 // Clear markers that have been overwritten
169 while (mStoredMarkers.peek() &&
170 mStoredMarkers.peek()->HasExpired(mGenID)) {
171 delete mStoredMarkers.popHead();
172 }
173 STORE_SEQUENCER();
174 mSignalLock = false;
175 }
177 void
178 PendingMarkers::updateGeneration(int aGenID) {
179 mGenID = aGenID;
180 }
182 void
183 PendingMarkers::addStoredMarker(ProfilerMarker *aStoredMarker) {
184 aStoredMarker->SetGeneration(mGenID);
185 mStoredMarkers.insert(aStoredMarker);
186 }
188 bool sps_version2()
189 {
190 static int version = 0; // Raced on, potentially
192 if (version == 0) {
193 bool allow2 = false; // Is v2 allowable on this platform?
194 # if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
195 || defined(SPS_PLAT_x86_linux)
196 allow2 = true;
197 # elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
198 || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android) \
199 || defined(SPS_PLAT_amd64_windows)
200 allow2 = false;
201 # else
202 # error "Unknown platform"
203 # endif
205 bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != nullptr; // Has v2 been requested?
207 bool elfhackd = false;
208 # if defined(USE_ELF_HACK)
209 bool elfhackd = true;
210 # endif
212 if (req2 && allow2) {
213 version = 2;
214 LOG("------------------- MOZ_PROFILER_NEW set -------------------");
215 } else if (req2 && !allow2) {
216 version = 1;
217 LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
218 LOG("---------- but is not available on this platform -----------");
219 } else if (req2 && elfhackd) {
220 version = 1;
221 LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
222 LOG("--- but this build was not done with --disable-elf-hack ----");
223 } else {
224 version = 1;
225 LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
226 }
227 }
228 return version == 2;
229 }
231 /* Has MOZ_PROFILER_VERBOSE been set? */
232 bool moz_profiler_verbose()
233 {
234 /* 0 = not checked, 1 = unset, 2 = set */
235 static int status = 0; // Raced on, potentially
237 if (status == 0) {
238 if (PR_GetEnv("MOZ_PROFILER_VERBOSE") != nullptr)
239 status = 2;
240 else
241 status = 1;
242 }
244 return status == 2;
245 }
247 static inline const char* name_UnwMode(UnwMode m)
248 {
249 switch (m) {
250 case UnwINVALID: return "invalid";
251 case UnwNATIVE: return "native";
252 case UnwPSEUDO: return "pseudo";
253 case UnwCOMBINED: return "combined";
254 default: return "??name_UnwMode??";
255 }
256 }
258 bool set_profiler_mode(const char* mode) {
259 if (mode) {
260 if (0 == strcmp(mode, "pseudo")) {
261 sUnwindMode = UnwPSEUDO;
262 return true;
263 }
264 else if (0 == strcmp(mode, "native") && is_native_unwinding_avail()) {
265 sUnwindMode = UnwNATIVE;
266 return true;
267 }
268 else if (0 == strcmp(mode, "combined") && is_native_unwinding_avail()) {
269 sUnwindMode = UnwCOMBINED;
270 return true;
271 } else {
272 return false;
273 }
274 }
276 return true;
277 }
279 bool set_profiler_interval(const char* interval) {
280 if (interval) {
281 errno = 0;
282 long int n = strtol(interval, (char**)nullptr, 10);
283 if (errno == 0 && n >= 1 && n <= 1000) {
284 sUnwindInterval = n;
285 return true;
286 }
287 return false;
288 }
290 return true;
291 }
293 bool set_profiler_entries(const char* entries) {
294 if (entries) {
295 errno = 0;
296 long int n = strtol(entries, (char**)nullptr, 10);
297 if (errno == 0 && n > 0) {
298 sProfileEntries = n;
299 return true;
300 }
301 return false;
302 }
304 return true;
305 }
307 bool set_profiler_scan(const char* scanCount) {
308 if (scanCount) {
309 errno = 0;
310 long int n = strtol(scanCount, (char**)nullptr, 10);
311 if (errno == 0 && n >= 0 && n <= 100) {
312 sUnwindStackScan = n;
313 return true;
314 }
315 return false;
316 }
318 return true;
319 }
321 bool is_native_unwinding_avail() {
322 # if defined(HAVE_NATIVE_UNWIND)
323 return true;
324 #else
325 return false;
326 #endif
327 }
329 // Read env vars at startup, so as to set sUnwindMode and sInterval.
330 void read_profiler_env_vars()
331 {
332 bool nativeAvail = is_native_unwinding_avail();
334 /* Set defaults */
335 sUnwindMode = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
336 sUnwindInterval = 0; /* We'll have to look elsewhere */
337 sProfileEntries = 0;
339 const char* stackMode = PR_GetEnv(PROFILER_MODE);
340 const char* interval = PR_GetEnv(PROFILER_INTERVAL);
341 const char* entries = PR_GetEnv(PROFILER_ENTRIES);
342 const char* scanCount = PR_GetEnv(PROFILER_STACK);
344 if (!set_profiler_mode(stackMode) ||
345 !set_profiler_interval(interval) ||
346 !set_profiler_entries(entries) ||
347 !set_profiler_scan(scanCount)) {
348 profiler_usage();
349 } else {
350 LOG( "SPS:");
351 LOGF("SPS: Unwind mode = %s", name_UnwMode(sUnwindMode));
352 LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
353 (int)sUnwindInterval);
354 LOGF("SPS: Entry store size = %d (zero means \"platform default\")",
355 (int)sProfileEntries);
356 LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
357 (int)sUnwindStackScan);
358 LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
359 LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
360 LOG( "SPS:");
361 }
362 }
364 void profiler_usage() {
365 LOG( "SPS: ");
366 LOG( "SPS: Environment variable usage:");
367 LOG( "SPS: ");
368 LOG( "SPS: MOZ_PROFILER_MODE=native for native unwind only");
369 LOG( "SPS: MOZ_PROFILER_MODE=pseudo for pseudo unwind only");
370 LOG( "SPS: MOZ_PROFILER_MODE=combined for combined native & pseudo unwind");
371 LOG( "SPS: If unset, default is 'combined' on native-capable");
372 LOG( "SPS: platforms, 'pseudo' on others.");
373 LOG( "SPS: ");
374 LOG( "SPS: MOZ_PROFILER_INTERVAL=<number> (milliseconds, 1 to 1000)");
375 LOG( "SPS: If unset, platform default is used.");
376 LOG( "SPS: ");
377 LOG( "SPS: MOZ_PROFILER_ENTRIES=<number> (count, minimum of 1)");
378 LOG( "SPS: If unset, platform default is used.");
379 LOG( "SPS: ");
380 LOG( "SPS: MOZ_PROFILER_VERBOSE");
381 LOG( "SPS: If set to any value, increases verbosity (recommended).");
382 LOG( "SPS: ");
383 LOG( "SPS: MOZ_PROFILER_STACK_SCAN=<number> (default is zero)");
384 LOG( "SPS: The number of dubious (stack-scanned) frames allowed");
385 LOG( "SPS: ");
386 LOG( "SPS: MOZ_PROFILER_NEW");
387 LOG( "SPS: Needs to be set to use LUL-based unwinding.");
388 LOG( "SPS: ");
389 LOG( "SPS: MOZ_PROFILER_LUL_TEST");
390 LOG( "SPS: If set to any value, runs LUL unit tests at startup of");
391 LOG( "SPS: the unwinder thread, and prints a short summary of results.");
392 LOG( "SPS: ");
393 LOGF("SPS: This platform %s native unwinding.",
394 is_native_unwinding_avail() ? "supports" : "does not support");
395 LOG( "SPS: ");
397 /* Re-set defaults */
398 sUnwindMode = is_native_unwinding_avail() ? UnwCOMBINED : UnwPSEUDO;
399 sUnwindInterval = 0; /* We'll have to look elsewhere */
400 sProfileEntries = 0;
401 sUnwindStackScan = 0;
403 LOG( "SPS:");
404 LOGF("SPS: Unwind mode = %s", name_UnwMode(sUnwindMode));
405 LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
406 (int)sUnwindInterval);
407 LOGF("SPS: Entry store size = %d (zero means \"platform default\")",
408 (int)sProfileEntries);
409 LOGF("SPS: UnwindStackScan = %d (max dubious frames per unwind).",
410 (int)sUnwindStackScan);
411 LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
412 LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
413 LOG( "SPS:");
415 return;
416 }
418 void set_tls_stack_top(void* stackTop)
419 {
420 // Round |stackTop| up to the end of the containing page. We may
421 // as well do this -- there's no danger of a fault, and we might
422 // get a few more base-of-the-stack frames as a result. This
423 // assumes that no target has a page size smaller than 4096.
424 uintptr_t stackTopR = (uintptr_t)stackTop;
425 if (stackTop) {
426 stackTopR = (stackTopR & ~(uintptr_t)4095) + (uintptr_t)4095;
427 }
428 tlsStackTop.set((void*)stackTopR);
429 }
431 bool is_main_thread_name(const char* aName) {
432 if (!aName) {
433 return false;
434 }
435 return strcmp(aName, gGeckoThreadName) == 0;
436 }
438 ////////////////////////////////////////////////////////////////////////
439 // BEGIN externally visible functions
441 void mozilla_sampler_init(void* stackTop)
442 {
443 sInitCount++;
445 if (stack_key_initialized)
446 return;
448 LOG("BEGIN mozilla_sampler_init");
449 if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) {
450 LOG("Failed to init.");
451 return;
452 }
453 stack_key_initialized = true;
455 Sampler::Startup();
457 PseudoStack *stack = new PseudoStack();
458 tlsPseudoStack.set(stack);
460 bool isMainThread = true;
461 #ifdef XP_WIN
462 // For metrofx, we'll register the main thread once it's created.
463 isMainThread = !(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro);
464 #endif
465 Sampler::RegisterCurrentThread(isMainThread ?
466 gGeckoThreadName : "Application Thread",
467 stack, isMainThread, stackTop);
469 // Read mode settings from MOZ_PROFILER_MODE and interval
470 // settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
471 // from MOZ_PROFILER_STACK_SCAN.
472 read_profiler_env_vars();
474 // platform specific initialization
475 OS::Startup();
477 // We can't open pref so we use an environment variable
478 // to know if we should trigger the profiler on startup
479 // NOTE: Default
480 const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
481 if (!val || !*val) {
482 return;
483 }
485 const char* features[] = {"js"
486 , "leaf"
487 #if defined(XP_WIN) || defined(XP_MACOSX) || (defined(SPS_ARCH_arm) && defined(linux))
488 , "stackwalk"
489 #endif
490 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
491 , "java"
492 #endif
493 };
494 profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
495 features, sizeof(features)/sizeof(const char*),
496 // TODO Add env variable to select threads
497 nullptr, 0);
498 LOG("END mozilla_sampler_init");
499 }
501 void mozilla_sampler_shutdown()
502 {
503 sInitCount--;
505 if (sInitCount > 0)
506 return;
508 // Save the profile on shutdown if requested.
509 TableTicker *t = tlsTicker.get();
510 if (t) {
511 const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
512 if (val) {
513 std::ofstream stream;
514 stream.open(val);
515 if (stream.is_open()) {
516 t->ToStreamAsJSON(stream);
517 stream.close();
518 }
519 }
520 }
522 profiler_stop();
524 Sampler::Shutdown();
526 // We can't delete the Stack because we can be between a
527 // sampler call_enter/call_exit point.
528 // TODO Need to find a safe time to delete Stack
529 }
531 void mozilla_sampler_save()
532 {
533 TableTicker *t = tlsTicker.get();
534 if (!t) {
535 return;
536 }
538 t->RequestSave();
539 // We're on the main thread already so we don't
540 // have to wait to handle the save request.
541 t->HandleSaveRequest();
542 }
544 char* mozilla_sampler_get_profile()
545 {
546 TableTicker *t = tlsTicker.get();
547 if (!t) {
548 return nullptr;
549 }
551 std::stringstream stream;
552 t->ToStreamAsJSON(stream);
553 char* profile = strdup(stream.str().c_str());
554 return profile;
555 }
557 JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
558 {
559 TableTicker *t = tlsTicker.get();
560 if (!t) {
561 return nullptr;
562 }
564 return t->ToJSObject(aCx);
565 }
567 void mozilla_sampler_save_profile_to_file(const char* aFilename)
568 {
569 TableTicker *t = tlsTicker.get();
570 if (!t) {
571 return;
572 }
574 std::ofstream stream;
575 stream.open(aFilename);
576 if (stream.is_open()) {
577 t->ToStreamAsJSON(stream);
578 stream.close();
579 LOGF("Saved to %s", aFilename);
580 } else {
581 LOG("Fail to open profile log file.");
582 }
583 }
586 const char** mozilla_sampler_get_features()
587 {
588 static const char* features[] = {
589 #if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
590 // Walk the C++ stack.
591 "stackwalk",
592 #endif
593 #if defined(ENABLE_SPS_LEAF_DATA)
594 // Include the C++ leaf node if not stackwalking. DevTools
595 // profiler doesn't want the native addresses.
596 "leaf",
597 #endif
598 #if !defined(SPS_OS_windows)
599 // Use a seperate thread of walking the stack.
600 "unwinder",
601 #endif
602 "java",
603 // Only record samples during periods of bad responsiveness
604 "jank",
605 // Tell the JS engine to emmit pseudostack entries in the
606 // pro/epilogue.
607 "js",
608 // Profile the registered secondary threads.
609 "threads",
610 // Do not include user-identifiable information
611 "privacy",
612 // Add main thread I/O to the profile
613 "mainthreadio",
614 #if defined(XP_WIN)
615 // Add power collection
616 "power",
617 #endif
618 nullptr
619 };
621 return features;
622 }
624 // Values are only honored on the first start
625 void mozilla_sampler_start(int aProfileEntries, double aInterval,
626 const char** aFeatures, uint32_t aFeatureCount,
627 const char** aThreadNameFilters, uint32_t aFilterCount)
629 {
630 LOG("BEGIN mozilla_sampler_start");
632 if (!stack_key_initialized)
633 profiler_init(nullptr);
635 /* If the sampling interval was set using env vars, use that
636 in preference to anything else. */
637 if (sUnwindInterval > 0)
638 aInterval = sUnwindInterval;
640 /* If the entry count was set using env vars, use that, too: */
641 if (sProfileEntries > 0)
642 aProfileEntries = sProfileEntries;
644 // Reset the current state if the profiler is running
645 profiler_stop();
647 TableTicker* t;
648 t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
649 aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
650 aFeatures, aFeatureCount,
651 aThreadNameFilters, aFilterCount);
652 if (t->HasUnwinderThread()) {
653 // Create the unwinder thread. ATM there is only one.
654 uwt__init();
655 }
657 tlsTicker.set(t);
658 t->Start();
659 if (t->ProfileJS() || t->InPrivacyMode()) {
660 mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
661 std::vector<ThreadInfo*> threads = t->GetRegisteredThreads();
663 for (uint32_t i = 0; i < threads.size(); i++) {
664 ThreadInfo* info = threads[i];
665 ThreadProfile* thread_profile = info->Profile();
666 if (!thread_profile) {
667 continue;
668 }
669 thread_profile->GetPseudoStack()->reinitializeOnResume();
670 if (t->ProfileJS()) {
671 thread_profile->GetPseudoStack()->enableJSSampling();
672 }
673 if (t->InPrivacyMode()) {
674 thread_profile->GetPseudoStack()->mPrivacyMode = true;
675 }
676 }
677 }
679 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
680 if (t->ProfileJava()) {
681 int javaInterval = aInterval;
682 // Java sampling doesn't accuratly keep up with 1ms sampling
683 if (javaInterval < 10) {
684 aInterval = 10;
685 }
686 mozilla::widget::android::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000);
687 }
688 #endif
690 if (t->AddMainThreadIO()) {
691 if (!sInterposeObserver) {
692 // Lazily create IO interposer observer
693 sInterposeObserver = new mozilla::ProfilerIOInterposeObserver();
694 }
695 mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
696 sInterposeObserver);
697 }
699 sIsProfiling = true;
701 if (Sampler::CanNotifyObservers()) {
702 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
703 if (os)
704 os->NotifyObservers(nullptr, "profiler-started", nullptr);
705 }
707 LOG("END mozilla_sampler_start");
708 }
710 void mozilla_sampler_stop()
711 {
712 LOG("BEGIN mozilla_sampler_stop");
714 if (!stack_key_initialized)
715 profiler_init(nullptr);
717 TableTicker *t = tlsTicker.get();
718 if (!t) {
719 LOG("END mozilla_sampler_stop-early");
720 return;
721 }
723 bool disableJS = t->ProfileJS();
724 bool unwinderThreader = t->HasUnwinderThread();
726 // Shut down and reap the unwinder thread. We have to do this
727 // before stopping the sampler, so as to guarantee that the unwinder
728 // thread doesn't try to access memory that the subsequent call to
729 // mozilla_sampler_stop causes to be freed.
730 if (unwinderThreader) {
731 uwt__stop();
732 }
734 t->Stop();
735 delete t;
736 tlsTicker.set(nullptr);
738 if (disableJS) {
739 PseudoStack *stack = tlsPseudoStack.get();
740 ASSERT(stack != nullptr);
741 stack->disableJSSampling();
742 }
744 if (unwinderThreader) {
745 uwt__deinit();
746 }
748 mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
749 sInterposeObserver);
750 sInterposeObserver = nullptr;
752 sIsProfiling = false;
754 if (Sampler::CanNotifyObservers()) {
755 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
756 if (os)
757 os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
758 }
760 LOG("END mozilla_sampler_stop");
761 }
763 bool mozilla_sampler_is_paused() {
764 if (Sampler::GetActiveSampler()) {
765 return Sampler::GetActiveSampler()->IsPaused();
766 } else {
767 return false;
768 }
769 }
771 void mozilla_sampler_pause() {
772 if (Sampler::GetActiveSampler()) {
773 Sampler::GetActiveSampler()->SetPaused(true);
774 }
775 }
777 void mozilla_sampler_resume() {
778 if (Sampler::GetActiveSampler()) {
779 Sampler::GetActiveSampler()->SetPaused(false);
780 }
781 }
783 bool mozilla_sampler_is_active()
784 {
785 return sIsProfiling;
786 }
788 static double sResponsivenessTimes[100];
789 static unsigned int sResponsivenessLoc = 0;
790 void mozilla_sampler_responsiveness(const TimeStamp& aTime)
791 {
792 if (!sLastTracerEvent.IsNull()) {
793 if (sResponsivenessLoc == 100) {
794 for(size_t i = 0; i < 100-1; i++) {
795 sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
796 }
797 sResponsivenessLoc--;
798 }
799 TimeDuration delta = aTime - sLastTracerEvent;
800 sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
801 }
802 sCurrentEventGeneration++;
804 sLastTracerEvent = aTime;
805 }
807 const double* mozilla_sampler_get_responsiveness()
808 {
809 return sResponsivenessTimes;
810 }
812 void mozilla_sampler_frame_number(int frameNumber)
813 {
814 sFrameNumber = frameNumber;
815 }
817 void mozilla_sampler_print_location2()
818 {
819 // FIXME
820 }
822 void mozilla_sampler_lock()
823 {
824 profiler_stop();
825 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
826 if (os)
827 os->NotifyObservers(nullptr, "profiler-locked", nullptr);
828 }
830 void mozilla_sampler_unlock()
831 {
832 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
833 if (os)
834 os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
835 }
837 bool mozilla_sampler_register_thread(const char* aName, void* stackTop)
838 {
839 #if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING)
840 // The only way to profile secondary threads on b2g
841 // is to build with profiling OR have the profiler
842 // running on startup.
843 if (!profiler_is_active()) {
844 return false;
845 }
846 #endif
848 PseudoStack* stack = new PseudoStack();
849 tlsPseudoStack.set(stack);
850 bool isMainThread = is_main_thread_name(aName);
851 return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
852 }
854 void mozilla_sampler_unregister_thread()
855 {
856 Sampler::UnregisterCurrentThread();
858 PseudoStack *stack = tlsPseudoStack.get();
859 if (!stack) {
860 return;
861 }
862 delete stack;
863 tlsPseudoStack.set(nullptr);
864 }
866 void mozilla_sampler_sleep_start() {
867 PseudoStack *stack = tlsPseudoStack.get();
868 if (stack == nullptr) {
869 return;
870 }
871 stack->setSleeping(1);
872 }
874 void mozilla_sampler_sleep_end() {
875 PseudoStack *stack = tlsPseudoStack.get();
876 if (stack == nullptr) {
877 return;
878 }
879 stack->setSleeping(0);
880 }
882 double mozilla_sampler_time(const TimeStamp& aTime)
883 {
884 if (!mozilla_sampler_is_active()) {
885 return 0.0;
886 }
887 TimeDuration delta = aTime - sStartTime;
888 return delta.ToMilliseconds();
889 }
891 double mozilla_sampler_time()
892 {
893 return mozilla_sampler_time(TimeStamp::Now());
894 }
896 ProfilerBacktrace* mozilla_sampler_get_backtrace()
897 {
898 if (!stack_key_initialized)
899 return nullptr;
901 // Don't capture a stack if we're not profiling
902 if (!profiler_is_active()) {
903 return nullptr;
904 }
906 // Don't capture a stack if we don't want to include personal information
907 if (profiler_in_privacy_mode()) {
908 return nullptr;
909 }
911 TableTicker* t = tlsTicker.get();
912 if (!t) {
913 return nullptr;
914 }
916 return new ProfilerBacktrace(t->GetBacktrace());
917 }
919 void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace)
920 {
921 delete aBacktrace;
922 }
924 void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
925 TracingMetadata aMetaData)
926 {
927 mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData));
928 }
930 void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
931 {
932 // Note that aPayload may be allocated by the caller, so we need to make sure
933 // that we free it at some point.
934 nsAutoPtr<ProfilerMarkerPayload> payload(aPayload);
936 if (!stack_key_initialized)
937 return;
939 // Don't insert a marker if we're not profiling to avoid
940 // the heap copy (malloc).
941 if (!profiler_is_active()) {
942 return;
943 }
945 // Don't add a marker if we don't want to include personal information
946 if (profiler_in_privacy_mode()) {
947 return;
948 }
950 PseudoStack *stack = tlsPseudoStack.get();
951 if (!stack) {
952 return;
953 }
954 TimeDuration delta = TimeStamp::Now() - sStartTime;
955 stack->addMarker(aMarker, payload.forget(), static_cast<float>(delta.ToMilliseconds()));
956 }
958 // END externally visible functions
959 ////////////////////////////////////////////////////////////////////////