tools/profiler/platform.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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 ////////////////////////////////////////////////////////////////////////

mercurial