tools/profiler/platform.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/profiler/platform.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,961 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +#include <ostream>
     1.9 +#include <fstream>
    1.10 +#include <sstream>
    1.11 +#include <errno.h>
    1.12 +
    1.13 +#include "ProfilerIOInterposeObserver.h"
    1.14 +#include "platform.h"
    1.15 +#include "PlatformMacros.h"
    1.16 +#include "prenv.h"
    1.17 +#include "mozilla/StaticPtr.h"
    1.18 +#include "mozilla/ThreadLocal.h"
    1.19 +#include "PseudoStack.h"
    1.20 +#include "TableTicker.h"
    1.21 +#include "UnwinderThread2.h"
    1.22 +#include "nsIObserverService.h"
    1.23 +#include "nsDirectoryServiceUtils.h"
    1.24 +#include "nsDirectoryServiceDefs.h"
    1.25 +#include "mozilla/Services.h"
    1.26 +#include "nsThreadUtils.h"
    1.27 +#include "ProfilerMarkers.h"
    1.28 +#include "nsXULAppAPI.h"
    1.29 +
    1.30 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
    1.31 +  #include "AndroidBridge.h"
    1.32 +  using namespace mozilla::widget::android;
    1.33 +#endif
    1.34 +
    1.35 +mozilla::ThreadLocal<PseudoStack *> tlsPseudoStack;
    1.36 +mozilla::ThreadLocal<TableTicker *> tlsTicker;
    1.37 +mozilla::ThreadLocal<void *> tlsStackTop;
    1.38 +// We need to track whether we've been initialized otherwise
    1.39 +// we end up using tlsStack without initializing it.
    1.40 +// Because tlsStack is totally opaque to us we can't reuse
    1.41 +// it as the flag itself.
    1.42 +bool stack_key_initialized;
    1.43 +
    1.44 +TimeStamp   sLastTracerEvent; // is raced on
    1.45 +TimeStamp   sStartTime;
    1.46 +int         sFrameNumber = 0;
    1.47 +int         sLastFrameNumber = 0;
    1.48 +int         sInitCount = 0; // Each init must have a matched shutdown.
    1.49 +static bool sIsProfiling = false; // is raced on
    1.50 +
    1.51 +// env variables to control the profiler
    1.52 +const char* PROFILER_MODE = "MOZ_PROFILER_MODE";
    1.53 +const char* PROFILER_INTERVAL = "MOZ_PROFILER_INTERVAL";
    1.54 +const char* PROFILER_ENTRIES = "MOZ_PROFILER_ENTRIES";
    1.55 +const char* PROFILER_STACK = "MOZ_PROFILER_STACK_SCAN";
    1.56 +const char* PROFILER_FEATURES = "MOZ_PROFILING_FEATURES";
    1.57 +
    1.58 +/* used to keep track of the last event that we sampled during */
    1.59 +unsigned int sLastSampledEventGeneration = 0;
    1.60 +
    1.61 +/* a counter that's incremented everytime we get responsiveness event
    1.62 + * note: it might also be worth trackplaing everytime we go around
    1.63 + * the event loop */
    1.64 +unsigned int sCurrentEventGeneration = 0;
    1.65 +/* we don't need to worry about overflow because we only treat the
    1.66 + * case of them being the same as special. i.e. we only run into
    1.67 + * a problem if 2^32 events happen between samples that we need
    1.68 + * to know are associated with different events */
    1.69 +
    1.70 +std::vector<ThreadInfo*>* Sampler::sRegisteredThreads = nullptr;
    1.71 +mozilla::Mutex* Sampler::sRegisteredThreadsMutex = nullptr;
    1.72 +
    1.73 +TableTicker* Sampler::sActiveSampler;
    1.74 +
    1.75 +static mozilla::StaticAutoPtr<mozilla::ProfilerIOInterposeObserver>
    1.76 +                                                            sInterposeObserver;
    1.77 +
    1.78 +// The name that identifies the gecko thread for calls to
    1.79 +// profiler_register_thread. For all platform except metro
    1.80 +// the thread that calls mozilla_sampler_init is considered
    1.81 +// the gecko thread.  With metro the gecko thread is
    1.82 +// registered later based on this thread name.
    1.83 +static const char * gGeckoThreadName = "GeckoMain";
    1.84 +
    1.85 +void Sampler::Startup() {
    1.86 +  sRegisteredThreads = new std::vector<ThreadInfo*>();
    1.87 +  sRegisteredThreadsMutex = new mozilla::Mutex("sRegisteredThreads mutex");
    1.88 +}
    1.89 +
    1.90 +void Sampler::Shutdown() {
    1.91 +  while (sRegisteredThreads->size() > 0) {
    1.92 +    delete sRegisteredThreads->back();
    1.93 +    sRegisteredThreads->pop_back();
    1.94 +  }
    1.95 +
    1.96 +  delete sRegisteredThreadsMutex;
    1.97 +  delete sRegisteredThreads;
    1.98 +
    1.99 +  // UnregisterThread can be called after shutdown in XPCShell. Thus
   1.100 +  // we need to point to null to ignore such a call after shutdown.
   1.101 +  sRegisteredThreadsMutex = nullptr;
   1.102 +  sRegisteredThreads = nullptr;
   1.103 +}
   1.104 +
   1.105 +ThreadInfo::~ThreadInfo() {
   1.106 +  free(mName);
   1.107 +
   1.108 +  if (mProfile)
   1.109 +    delete mProfile;
   1.110 +
   1.111 +  Sampler::FreePlatformData(mPlatformData);
   1.112 +}
   1.113 +
   1.114 +ProfilerMarker::ProfilerMarker(const char* aMarkerName,
   1.115 +    ProfilerMarkerPayload* aPayload,
   1.116 +    float aTime)
   1.117 +  : mMarkerName(strdup(aMarkerName))
   1.118 +  , mPayload(aPayload)
   1.119 +  , mTime(aTime)
   1.120 +{
   1.121 +}
   1.122 +
   1.123 +ProfilerMarker::~ProfilerMarker() {
   1.124 +  free(mMarkerName);
   1.125 +  delete mPayload;
   1.126 +}
   1.127 +
   1.128 +void
   1.129 +ProfilerMarker::SetGeneration(int aGenID) {
   1.130 +  mGenID = aGenID;
   1.131 +}
   1.132 +
   1.133 +float
   1.134 +ProfilerMarker::GetTime() {
   1.135 +  return mTime;
   1.136 +}
   1.137 +
   1.138 +void ProfilerMarker::StreamJSObject(JSStreamWriter& b) const {
   1.139 +  b.BeginObject();
   1.140 +    b.NameValue("name", GetMarkerName());
   1.141 +    // TODO: Store the callsite for this marker if available:
   1.142 +    // if have location data
   1.143 +    //   b.NameValue(marker, "location", ...);
   1.144 +    if (mPayload) {
   1.145 +      b.Name("data");
   1.146 +      mPayload->StreamPayload(b);
   1.147 +    }
   1.148 +    b.NameValue("time", mTime);
   1.149 +  b.EndObject();
   1.150 +}
   1.151 +
   1.152 +PendingMarkers::~PendingMarkers() {
   1.153 +  clearMarkers();
   1.154 +  if (mSignalLock != false) {
   1.155 +    // We're releasing the pseudostack while it's still in use.
   1.156 +    // The label macros keep a non ref counted reference to the
   1.157 +    // stack to avoid a TLS. If these are not all cleared we will
   1.158 +    // get a use-after-free so better to crash now.
   1.159 +    abort();
   1.160 +  }
   1.161 +}
   1.162 +
   1.163 +void
   1.164 +PendingMarkers::addMarker(ProfilerMarker *aMarker) {
   1.165 +  mSignalLock = true;
   1.166 +  STORE_SEQUENCER();
   1.167 +
   1.168 +  MOZ_ASSERT(aMarker);
   1.169 +  mPendingMarkers.insert(aMarker);
   1.170 +
   1.171 +  // Clear markers that have been overwritten
   1.172 +  while (mStoredMarkers.peek() &&
   1.173 +         mStoredMarkers.peek()->HasExpired(mGenID)) {
   1.174 +    delete mStoredMarkers.popHead();
   1.175 +  } 
   1.176 +  STORE_SEQUENCER();
   1.177 +  mSignalLock = false;
   1.178 +}
   1.179 +
   1.180 +void
   1.181 +PendingMarkers::updateGeneration(int aGenID) {
   1.182 +  mGenID = aGenID;
   1.183 +}
   1.184 +
   1.185 +void
   1.186 +PendingMarkers::addStoredMarker(ProfilerMarker *aStoredMarker) {
   1.187 +  aStoredMarker->SetGeneration(mGenID);
   1.188 +  mStoredMarkers.insert(aStoredMarker);
   1.189 +}
   1.190 +
   1.191 +bool sps_version2()
   1.192 +{
   1.193 +  static int version = 0; // Raced on, potentially
   1.194 +
   1.195 +  if (version == 0) {
   1.196 +    bool allow2 = false; // Is v2 allowable on this platform?
   1.197 +#   if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
   1.198 +       || defined(SPS_PLAT_x86_linux)
   1.199 +    allow2 = true;
   1.200 +#   elif defined(SPS_PLAT_amd64_darwin) || defined(SPS_PLAT_x86_darwin) \
   1.201 +         || defined(SPS_PLAT_x86_windows) || defined(SPS_PLAT_x86_android) \
   1.202 +         || defined(SPS_PLAT_amd64_windows)
   1.203 +    allow2 = false;
   1.204 +#   else
   1.205 +#     error "Unknown platform"
   1.206 +#   endif
   1.207 +
   1.208 +    bool req2 = PR_GetEnv("MOZ_PROFILER_NEW") != nullptr; // Has v2 been requested?
   1.209 +
   1.210 +    bool elfhackd = false;
   1.211 +#   if defined(USE_ELF_HACK)
   1.212 +    bool elfhackd = true;
   1.213 +#   endif
   1.214 +
   1.215 +    if (req2 && allow2) {
   1.216 +      version = 2;
   1.217 +      LOG("------------------- MOZ_PROFILER_NEW set -------------------");
   1.218 +    } else if (req2 && !allow2) {
   1.219 +      version = 1;
   1.220 +      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
   1.221 +      LOG("---------- but is not available on this platform -----------");
   1.222 +    } else if (req2 && elfhackd) {
   1.223 +      version = 1;
   1.224 +      LOG("--------------- MOZ_PROFILER_NEW requested, ----------------");
   1.225 +      LOG("--- but this build was not done with --disable-elf-hack ----");
   1.226 +    } else {
   1.227 +      version = 1;
   1.228 +      LOG("----------------- MOZ_PROFILER_NEW not set -----------------");
   1.229 +    }
   1.230 +  }
   1.231 +  return version == 2;
   1.232 +}
   1.233 +
   1.234 +/* Has MOZ_PROFILER_VERBOSE been set? */
   1.235 +bool moz_profiler_verbose()
   1.236 +{
   1.237 +  /* 0 = not checked, 1 = unset, 2 = set */
   1.238 +  static int status = 0; // Raced on, potentially
   1.239 +
   1.240 +  if (status == 0) {
   1.241 +    if (PR_GetEnv("MOZ_PROFILER_VERBOSE") != nullptr)
   1.242 +      status = 2;
   1.243 +    else
   1.244 +      status = 1;
   1.245 +  }
   1.246 +
   1.247 +  return status == 2;
   1.248 +}
   1.249 +
   1.250 +static inline const char* name_UnwMode(UnwMode m)
   1.251 +{
   1.252 +  switch (m) {
   1.253 +    case UnwINVALID:  return "invalid";
   1.254 +    case UnwNATIVE:   return "native";
   1.255 +    case UnwPSEUDO:   return "pseudo";
   1.256 +    case UnwCOMBINED: return "combined";
   1.257 +    default:          return "??name_UnwMode??";
   1.258 +  }
   1.259 +}
   1.260 +
   1.261 +bool set_profiler_mode(const char* mode) {
   1.262 +  if (mode) {
   1.263 +    if (0 == strcmp(mode, "pseudo")) {
   1.264 +      sUnwindMode = UnwPSEUDO;
   1.265 +      return true;
   1.266 +    }
   1.267 +    else if (0 == strcmp(mode, "native") && is_native_unwinding_avail()) {
   1.268 +      sUnwindMode = UnwNATIVE;
   1.269 +      return true;
   1.270 +    }
   1.271 +    else if (0 == strcmp(mode, "combined") && is_native_unwinding_avail()) {
   1.272 +      sUnwindMode = UnwCOMBINED;
   1.273 +      return true;
   1.274 +    } else {
   1.275 +      return false;
   1.276 +    }
   1.277 +  }
   1.278 +
   1.279 +  return true;
   1.280 +}
   1.281 +
   1.282 +bool set_profiler_interval(const char* interval) {
   1.283 +  if (interval) {
   1.284 +    errno = 0;
   1.285 +    long int n = strtol(interval, (char**)nullptr, 10);
   1.286 +    if (errno == 0 && n >= 1 && n <= 1000) {
   1.287 +      sUnwindInterval = n;
   1.288 +      return true;
   1.289 +    }
   1.290 +    return false;
   1.291 +  }
   1.292 +
   1.293 +  return true;
   1.294 +}
   1.295 +
   1.296 +bool set_profiler_entries(const char* entries) {
   1.297 +  if (entries) {
   1.298 +    errno = 0;
   1.299 +    long int n = strtol(entries, (char**)nullptr, 10);
   1.300 +    if (errno == 0 && n > 0) {
   1.301 +      sProfileEntries = n;
   1.302 +      return true;
   1.303 +    }
   1.304 +    return false;
   1.305 +  }
   1.306 +
   1.307 +  return true;
   1.308 +}
   1.309 +
   1.310 +bool set_profiler_scan(const char* scanCount) {
   1.311 +  if (scanCount) {
   1.312 +    errno = 0;
   1.313 +    long int n = strtol(scanCount, (char**)nullptr, 10);
   1.314 +    if (errno == 0 && n >= 0 && n <= 100) {
   1.315 +      sUnwindStackScan = n;
   1.316 +      return true;
   1.317 +    }
   1.318 +    return false;
   1.319 +  }
   1.320 +
   1.321 +  return true;
   1.322 +}
   1.323 +
   1.324 +bool is_native_unwinding_avail() {
   1.325 +# if defined(HAVE_NATIVE_UNWIND)
   1.326 +  return true;
   1.327 +#else
   1.328 +  return false;
   1.329 +#endif
   1.330 +}
   1.331 +
   1.332 +// Read env vars at startup, so as to set sUnwindMode and sInterval.
   1.333 +void read_profiler_env_vars()
   1.334 +{
   1.335 +  bool nativeAvail = is_native_unwinding_avail();
   1.336 +
   1.337 +  /* Set defaults */
   1.338 +  sUnwindMode     = nativeAvail ? UnwCOMBINED : UnwPSEUDO;
   1.339 +  sUnwindInterval = 0;  /* We'll have to look elsewhere */
   1.340 +  sProfileEntries = 0;
   1.341 +
   1.342 +  const char* stackMode = PR_GetEnv(PROFILER_MODE);
   1.343 +  const char* interval = PR_GetEnv(PROFILER_INTERVAL);
   1.344 +  const char* entries = PR_GetEnv(PROFILER_ENTRIES);
   1.345 +  const char* scanCount = PR_GetEnv(PROFILER_STACK);
   1.346 +
   1.347 +  if (!set_profiler_mode(stackMode) ||
   1.348 +      !set_profiler_interval(interval) ||
   1.349 +      !set_profiler_entries(entries) ||
   1.350 +      !set_profiler_scan(scanCount)) {
   1.351 +      profiler_usage();
   1.352 +  } else {
   1.353 +    LOG( "SPS:");
   1.354 +    LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
   1.355 +    LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
   1.356 +        (int)sUnwindInterval);
   1.357 +    LOGF("SPS: Entry store size  = %d (zero means \"platform default\")",
   1.358 +        (int)sProfileEntries);
   1.359 +    LOGF("SPS: UnwindStackScan   = %d (max dubious frames per unwind).",
   1.360 +        (int)sUnwindStackScan);
   1.361 +    LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
   1.362 +    LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
   1.363 +    LOG( "SPS:");
   1.364 +  }
   1.365 +}
   1.366 +
   1.367 +void profiler_usage() {
   1.368 +  LOG( "SPS: ");
   1.369 +  LOG( "SPS: Environment variable usage:");
   1.370 +  LOG( "SPS: ");
   1.371 +  LOG( "SPS:   MOZ_PROFILER_MODE=native    for native unwind only");
   1.372 +  LOG( "SPS:   MOZ_PROFILER_MODE=pseudo    for pseudo unwind only");
   1.373 +  LOG( "SPS:   MOZ_PROFILER_MODE=combined  for combined native & pseudo unwind");
   1.374 +  LOG( "SPS:   If unset, default is 'combined' on native-capable");
   1.375 +  LOG( "SPS:     platforms, 'pseudo' on others.");
   1.376 +  LOG( "SPS: ");
   1.377 +  LOG( "SPS:   MOZ_PROFILER_INTERVAL=<number>   (milliseconds, 1 to 1000)");
   1.378 +  LOG( "SPS:   If unset, platform default is used.");
   1.379 +  LOG( "SPS: ");
   1.380 +  LOG( "SPS:   MOZ_PROFILER_ENTRIES=<number>    (count, minimum of 1)");
   1.381 +  LOG( "SPS:   If unset, platform default is used.");
   1.382 +  LOG( "SPS: ");
   1.383 +  LOG( "SPS:   MOZ_PROFILER_VERBOSE");
   1.384 +  LOG( "SPS:   If set to any value, increases verbosity (recommended).");
   1.385 +  LOG( "SPS: ");
   1.386 +  LOG( "SPS:   MOZ_PROFILER_STACK_SCAN=<number>   (default is zero)");
   1.387 +  LOG( "SPS:   The number of dubious (stack-scanned) frames allowed");
   1.388 +  LOG( "SPS: ");
   1.389 +  LOG( "SPS:   MOZ_PROFILER_NEW");
   1.390 +  LOG( "SPS:   Needs to be set to use LUL-based unwinding.");
   1.391 +  LOG( "SPS: ");
   1.392 +  LOG( "SPS:   MOZ_PROFILER_LUL_TEST");
   1.393 +  LOG( "SPS:   If set to any value, runs LUL unit tests at startup of");
   1.394 +  LOG( "SPS:   the unwinder thread, and prints a short summary of results.");
   1.395 +  LOG( "SPS: ");
   1.396 +  LOGF("SPS:   This platform %s native unwinding.",
   1.397 +       is_native_unwinding_avail() ? "supports" : "does not support");
   1.398 +  LOG( "SPS: ");
   1.399 +
   1.400 +  /* Re-set defaults */
   1.401 +  sUnwindMode       = is_native_unwinding_avail() ? UnwCOMBINED : UnwPSEUDO;
   1.402 +  sUnwindInterval   = 0;  /* We'll have to look elsewhere */
   1.403 +  sProfileEntries   = 0;
   1.404 +  sUnwindStackScan  = 0;
   1.405 +
   1.406 +  LOG( "SPS:");
   1.407 +  LOGF("SPS: Unwind mode       = %s", name_UnwMode(sUnwindMode));
   1.408 +  LOGF("SPS: Sampling interval = %d ms (zero means \"platform default\")",
   1.409 +       (int)sUnwindInterval);
   1.410 +  LOGF("SPS: Entry store size  = %d (zero means \"platform default\")",
   1.411 +       (int)sProfileEntries);
   1.412 +  LOGF("SPS: UnwindStackScan   = %d (max dubious frames per unwind).",
   1.413 +       (int)sUnwindStackScan);
   1.414 +  LOG( "SPS: Use env var MOZ_PROFILER_MODE=help for further information.");
   1.415 +  LOG( "SPS: Note that MOZ_PROFILER_MODE=help sets all values to defaults.");
   1.416 +  LOG( "SPS:");
   1.417 +
   1.418 +  return;
   1.419 +}
   1.420 +
   1.421 +void set_tls_stack_top(void* stackTop)
   1.422 +{
   1.423 +  // Round |stackTop| up to the end of the containing page.  We may
   1.424 +  // as well do this -- there's no danger of a fault, and we might
   1.425 +  // get a few more base-of-the-stack frames as a result.  This
   1.426 +  // assumes that no target has a page size smaller than 4096.
   1.427 +  uintptr_t stackTopR = (uintptr_t)stackTop;
   1.428 +  if (stackTop) {
   1.429 +    stackTopR = (stackTopR & ~(uintptr_t)4095) + (uintptr_t)4095;
   1.430 +  }
   1.431 +  tlsStackTop.set((void*)stackTopR);
   1.432 +}
   1.433 +
   1.434 +bool is_main_thread_name(const char* aName) {
   1.435 +  if (!aName) {
   1.436 +    return false;
   1.437 +  }
   1.438 +  return strcmp(aName, gGeckoThreadName) == 0;
   1.439 +}
   1.440 +
   1.441 +////////////////////////////////////////////////////////////////////////
   1.442 +// BEGIN externally visible functions
   1.443 +
   1.444 +void mozilla_sampler_init(void* stackTop)
   1.445 +{
   1.446 +  sInitCount++;
   1.447 +
   1.448 +  if (stack_key_initialized)
   1.449 +    return;
   1.450 +
   1.451 +  LOG("BEGIN mozilla_sampler_init");
   1.452 +  if (!tlsPseudoStack.init() || !tlsTicker.init() || !tlsStackTop.init()) {
   1.453 +    LOG("Failed to init.");
   1.454 +    return;
   1.455 +  }
   1.456 +  stack_key_initialized = true;
   1.457 +
   1.458 +  Sampler::Startup();
   1.459 +
   1.460 +  PseudoStack *stack = new PseudoStack();
   1.461 +  tlsPseudoStack.set(stack);
   1.462 +
   1.463 +  bool isMainThread = true;
   1.464 +#ifdef XP_WIN
   1.465 +  // For metrofx, we'll register the main thread once it's created.
   1.466 +  isMainThread = !(XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro);
   1.467 +#endif
   1.468 +  Sampler::RegisterCurrentThread(isMainThread ?
   1.469 +                                   gGeckoThreadName : "Application Thread",
   1.470 +                                 stack, isMainThread, stackTop);
   1.471 +
   1.472 +  // Read mode settings from MOZ_PROFILER_MODE and interval
   1.473 +  // settings from MOZ_PROFILER_INTERVAL and stack-scan threshhold
   1.474 +  // from MOZ_PROFILER_STACK_SCAN.
   1.475 +  read_profiler_env_vars();
   1.476 +
   1.477 +  // platform specific initialization
   1.478 +  OS::Startup();
   1.479 +
   1.480 +  // We can't open pref so we use an environment variable
   1.481 +  // to know if we should trigger the profiler on startup
   1.482 +  // NOTE: Default
   1.483 +  const char *val = PR_GetEnv("MOZ_PROFILER_STARTUP");
   1.484 +  if (!val || !*val) {
   1.485 +    return;
   1.486 +  }
   1.487 +
   1.488 +  const char* features[] = {"js"
   1.489 +                         , "leaf"
   1.490 +#if defined(XP_WIN) || defined(XP_MACOSX) || (defined(SPS_ARCH_arm) && defined(linux))
   1.491 +                         , "stackwalk"
   1.492 +#endif
   1.493 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   1.494 +                         , "java"
   1.495 +#endif
   1.496 +                         };
   1.497 +  profiler_start(PROFILE_DEFAULT_ENTRY, PROFILE_DEFAULT_INTERVAL,
   1.498 +                         features, sizeof(features)/sizeof(const char*),
   1.499 +                         // TODO Add env variable to select threads
   1.500 +                         nullptr, 0);
   1.501 +  LOG("END   mozilla_sampler_init");
   1.502 +}
   1.503 +
   1.504 +void mozilla_sampler_shutdown()
   1.505 +{
   1.506 +  sInitCount--;
   1.507 +
   1.508 +  if (sInitCount > 0)
   1.509 +    return;
   1.510 +
   1.511 +  // Save the profile on shutdown if requested.
   1.512 +  TableTicker *t = tlsTicker.get();
   1.513 +  if (t) {
   1.514 +    const char *val = PR_GetEnv("MOZ_PROFILER_SHUTDOWN");
   1.515 +    if (val) {
   1.516 +      std::ofstream stream;
   1.517 +      stream.open(val);
   1.518 +      if (stream.is_open()) {
   1.519 +        t->ToStreamAsJSON(stream);
   1.520 +        stream.close();
   1.521 +      }
   1.522 +    }
   1.523 +  }
   1.524 +
   1.525 +  profiler_stop();
   1.526 +
   1.527 +  Sampler::Shutdown();
   1.528 +
   1.529 +  // We can't delete the Stack because we can be between a
   1.530 +  // sampler call_enter/call_exit point.
   1.531 +  // TODO Need to find a safe time to delete Stack
   1.532 +}
   1.533 +
   1.534 +void mozilla_sampler_save()
   1.535 +{
   1.536 +  TableTicker *t = tlsTicker.get();
   1.537 +  if (!t) {
   1.538 +    return;
   1.539 +  }
   1.540 +
   1.541 +  t->RequestSave();
   1.542 +  // We're on the main thread already so we don't
   1.543 +  // have to wait to handle the save request.
   1.544 +  t->HandleSaveRequest();
   1.545 +}
   1.546 +
   1.547 +char* mozilla_sampler_get_profile()
   1.548 +{
   1.549 +  TableTicker *t = tlsTicker.get();
   1.550 +  if (!t) {
   1.551 +    return nullptr;
   1.552 +  }
   1.553 +
   1.554 +  std::stringstream stream;
   1.555 +  t->ToStreamAsJSON(stream);
   1.556 +  char* profile = strdup(stream.str().c_str());
   1.557 +  return profile;
   1.558 +}
   1.559 +
   1.560 +JSObject *mozilla_sampler_get_profile_data(JSContext *aCx)
   1.561 +{
   1.562 +  TableTicker *t = tlsTicker.get();
   1.563 +  if (!t) {
   1.564 +    return nullptr;
   1.565 +  }
   1.566 +
   1.567 +  return t->ToJSObject(aCx);
   1.568 +}
   1.569 +
   1.570 +void mozilla_sampler_save_profile_to_file(const char* aFilename)
   1.571 +{
   1.572 +  TableTicker *t = tlsTicker.get();
   1.573 +  if (!t) {
   1.574 +    return;
   1.575 +  }
   1.576 +
   1.577 +  std::ofstream stream;
   1.578 +  stream.open(aFilename);
   1.579 +  if (stream.is_open()) {
   1.580 +    t->ToStreamAsJSON(stream);
   1.581 +    stream.close();
   1.582 +    LOGF("Saved to %s", aFilename);
   1.583 +  } else {
   1.584 +    LOG("Fail to open profile log file.");
   1.585 +  }
   1.586 +}
   1.587 +
   1.588 +
   1.589 +const char** mozilla_sampler_get_features()
   1.590 +{
   1.591 +  static const char* features[] = {
   1.592 +#if defined(MOZ_PROFILING) && defined(HAVE_NATIVE_UNWIND)
   1.593 +    // Walk the C++ stack.
   1.594 +    "stackwalk",
   1.595 +#endif
   1.596 +#if defined(ENABLE_SPS_LEAF_DATA)
   1.597 +    // Include the C++ leaf node if not stackwalking. DevTools
   1.598 +    // profiler doesn't want the native addresses.
   1.599 +    "leaf",
   1.600 +#endif
   1.601 +#if !defined(SPS_OS_windows)
   1.602 +    // Use a seperate thread of walking the stack.
   1.603 +    "unwinder",
   1.604 +#endif
   1.605 +    "java",
   1.606 +    // Only record samples during periods of bad responsiveness
   1.607 +    "jank",
   1.608 +    // Tell the JS engine to emmit pseudostack entries in the
   1.609 +    // pro/epilogue.
   1.610 +    "js",
   1.611 +    // Profile the registered secondary threads.
   1.612 +    "threads",
   1.613 +    // Do not include user-identifiable information
   1.614 +    "privacy",
   1.615 +    // Add main thread I/O to the profile
   1.616 +    "mainthreadio",
   1.617 +#if defined(XP_WIN)
   1.618 +    // Add power collection
   1.619 +    "power",
   1.620 +#endif
   1.621 +    nullptr
   1.622 +  };
   1.623 +
   1.624 +  return features;
   1.625 +}
   1.626 +
   1.627 +// Values are only honored on the first start
   1.628 +void mozilla_sampler_start(int aProfileEntries, double aInterval,
   1.629 +                           const char** aFeatures, uint32_t aFeatureCount,
   1.630 +                           const char** aThreadNameFilters, uint32_t aFilterCount)
   1.631 +
   1.632 +{
   1.633 +  LOG("BEGIN mozilla_sampler_start");
   1.634 +
   1.635 +  if (!stack_key_initialized)
   1.636 +    profiler_init(nullptr);
   1.637 +
   1.638 +  /* If the sampling interval was set using env vars, use that
   1.639 +     in preference to anything else. */
   1.640 +  if (sUnwindInterval > 0)
   1.641 +    aInterval = sUnwindInterval;
   1.642 +
   1.643 +  /* If the entry count was set using env vars, use that, too: */
   1.644 +  if (sProfileEntries > 0)
   1.645 +    aProfileEntries = sProfileEntries;
   1.646 +
   1.647 +  // Reset the current state if the profiler is running
   1.648 +  profiler_stop();
   1.649 +
   1.650 +  TableTicker* t;
   1.651 +  t = new TableTicker(aInterval ? aInterval : PROFILE_DEFAULT_INTERVAL,
   1.652 +                      aProfileEntries ? aProfileEntries : PROFILE_DEFAULT_ENTRY,
   1.653 +                      aFeatures, aFeatureCount,
   1.654 +                      aThreadNameFilters, aFilterCount);
   1.655 +  if (t->HasUnwinderThread()) {
   1.656 +    // Create the unwinder thread.  ATM there is only one.
   1.657 +    uwt__init();
   1.658 +  }
   1.659 +
   1.660 +  tlsTicker.set(t);
   1.661 +  t->Start();
   1.662 +  if (t->ProfileJS() || t->InPrivacyMode()) {
   1.663 +      mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex);
   1.664 +      std::vector<ThreadInfo*> threads = t->GetRegisteredThreads();
   1.665 +
   1.666 +      for (uint32_t i = 0; i < threads.size(); i++) {
   1.667 +        ThreadInfo* info = threads[i];
   1.668 +        ThreadProfile* thread_profile = info->Profile();
   1.669 +        if (!thread_profile) {
   1.670 +          continue;
   1.671 +        }
   1.672 +        thread_profile->GetPseudoStack()->reinitializeOnResume();
   1.673 +        if (t->ProfileJS()) {
   1.674 +          thread_profile->GetPseudoStack()->enableJSSampling();
   1.675 +        }
   1.676 +        if (t->InPrivacyMode()) {
   1.677 +          thread_profile->GetPseudoStack()->mPrivacyMode = true;
   1.678 +        }
   1.679 +      }
   1.680 +  }
   1.681 +
   1.682 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
   1.683 +  if (t->ProfileJava()) {
   1.684 +    int javaInterval = aInterval;
   1.685 +    // Java sampling doesn't accuratly keep up with 1ms sampling
   1.686 +    if (javaInterval < 10) {
   1.687 +      aInterval = 10;
   1.688 +    }
   1.689 +    mozilla::widget::android::GeckoJavaSampler::StartJavaProfiling(javaInterval, 1000);
   1.690 +  }
   1.691 +#endif
   1.692 +
   1.693 +  if (t->AddMainThreadIO()) {
   1.694 +    if (!sInterposeObserver) {
   1.695 +      // Lazily create IO interposer observer
   1.696 +      sInterposeObserver = new mozilla::ProfilerIOInterposeObserver();
   1.697 +    }
   1.698 +    mozilla::IOInterposer::Register(mozilla::IOInterposeObserver::OpAll,
   1.699 +                                    sInterposeObserver);
   1.700 +  }
   1.701 +
   1.702 +  sIsProfiling = true;
   1.703 +
   1.704 +  if (Sampler::CanNotifyObservers()) {
   1.705 +    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.706 +    if (os)
   1.707 +      os->NotifyObservers(nullptr, "profiler-started", nullptr);
   1.708 +  }
   1.709 +
   1.710 +  LOG("END   mozilla_sampler_start");
   1.711 +}
   1.712 +
   1.713 +void mozilla_sampler_stop()
   1.714 +{
   1.715 +  LOG("BEGIN mozilla_sampler_stop");
   1.716 +
   1.717 +  if (!stack_key_initialized)
   1.718 +    profiler_init(nullptr);
   1.719 +
   1.720 +  TableTicker *t = tlsTicker.get();
   1.721 +  if (!t) {
   1.722 +    LOG("END   mozilla_sampler_stop-early");
   1.723 +    return;
   1.724 +  }
   1.725 +
   1.726 +  bool disableJS = t->ProfileJS();
   1.727 +  bool unwinderThreader = t->HasUnwinderThread();
   1.728 +
   1.729 +  // Shut down and reap the unwinder thread.  We have to do this
   1.730 +  // before stopping the sampler, so as to guarantee that the unwinder
   1.731 +  // thread doesn't try to access memory that the subsequent call to
   1.732 +  // mozilla_sampler_stop causes to be freed.
   1.733 +  if (unwinderThreader) {
   1.734 +    uwt__stop();
   1.735 +  }
   1.736 +
   1.737 +  t->Stop();
   1.738 +  delete t;
   1.739 +  tlsTicker.set(nullptr);
   1.740 +
   1.741 +  if (disableJS) {
   1.742 +    PseudoStack *stack = tlsPseudoStack.get();
   1.743 +    ASSERT(stack != nullptr);
   1.744 +    stack->disableJSSampling();
   1.745 +  }
   1.746 +
   1.747 +  if (unwinderThreader) {
   1.748 +    uwt__deinit();
   1.749 +  }
   1.750 +
   1.751 +  mozilla::IOInterposer::Unregister(mozilla::IOInterposeObserver::OpAll,
   1.752 +                                    sInterposeObserver);
   1.753 +  sInterposeObserver = nullptr;
   1.754 +
   1.755 +  sIsProfiling = false;
   1.756 +
   1.757 +  if (Sampler::CanNotifyObservers()) {
   1.758 +    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.759 +    if (os)
   1.760 +      os->NotifyObservers(nullptr, "profiler-stopped", nullptr);
   1.761 +  }
   1.762 +
   1.763 +  LOG("END   mozilla_sampler_stop");
   1.764 +}
   1.765 +
   1.766 +bool mozilla_sampler_is_paused() {
   1.767 +  if (Sampler::GetActiveSampler()) {
   1.768 +    return Sampler::GetActiveSampler()->IsPaused();
   1.769 +  } else {
   1.770 +    return false;
   1.771 +  }
   1.772 +}
   1.773 +
   1.774 +void mozilla_sampler_pause() {
   1.775 +  if (Sampler::GetActiveSampler()) {
   1.776 +    Sampler::GetActiveSampler()->SetPaused(true);
   1.777 +  }
   1.778 +}
   1.779 +
   1.780 +void mozilla_sampler_resume() {
   1.781 +  if (Sampler::GetActiveSampler()) {
   1.782 +    Sampler::GetActiveSampler()->SetPaused(false);
   1.783 +  }
   1.784 +}
   1.785 +
   1.786 +bool mozilla_sampler_is_active()
   1.787 +{
   1.788 +  return sIsProfiling;
   1.789 +}
   1.790 +
   1.791 +static double sResponsivenessTimes[100];
   1.792 +static unsigned int sResponsivenessLoc = 0;
   1.793 +void mozilla_sampler_responsiveness(const TimeStamp& aTime)
   1.794 +{
   1.795 +  if (!sLastTracerEvent.IsNull()) {
   1.796 +    if (sResponsivenessLoc == 100) {
   1.797 +      for(size_t i = 0; i < 100-1; i++) {
   1.798 +        sResponsivenessTimes[i] = sResponsivenessTimes[i+1];
   1.799 +      }
   1.800 +      sResponsivenessLoc--;
   1.801 +    }
   1.802 +    TimeDuration delta = aTime - sLastTracerEvent;
   1.803 +    sResponsivenessTimes[sResponsivenessLoc++] = delta.ToMilliseconds();
   1.804 +  }
   1.805 +  sCurrentEventGeneration++;
   1.806 +
   1.807 +  sLastTracerEvent = aTime;
   1.808 +}
   1.809 +
   1.810 +const double* mozilla_sampler_get_responsiveness()
   1.811 +{
   1.812 +  return sResponsivenessTimes;
   1.813 +}
   1.814 +
   1.815 +void mozilla_sampler_frame_number(int frameNumber)
   1.816 +{
   1.817 +  sFrameNumber = frameNumber;
   1.818 +}
   1.819 +
   1.820 +void mozilla_sampler_print_location2()
   1.821 +{
   1.822 +  // FIXME
   1.823 +}
   1.824 +
   1.825 +void mozilla_sampler_lock()
   1.826 +{
   1.827 +  profiler_stop();
   1.828 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.829 +  if (os)
   1.830 +    os->NotifyObservers(nullptr, "profiler-locked", nullptr);
   1.831 +}
   1.832 +
   1.833 +void mozilla_sampler_unlock()
   1.834 +{
   1.835 +  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
   1.836 +  if (os)
   1.837 +    os->NotifyObservers(nullptr, "profiler-unlocked", nullptr);
   1.838 +}
   1.839 +
   1.840 +bool mozilla_sampler_register_thread(const char* aName, void* stackTop)
   1.841 +{
   1.842 +#if defined(MOZ_WIDGET_GONK) && !defined(MOZ_PROFILING)
   1.843 +  // The only way to profile secondary threads on b2g
   1.844 +  // is to build with profiling OR have the profiler
   1.845 +  // running on startup.
   1.846 +  if (!profiler_is_active()) {
   1.847 +    return false;
   1.848 +  }
   1.849 +#endif
   1.850 +
   1.851 +  PseudoStack* stack = new PseudoStack();
   1.852 +  tlsPseudoStack.set(stack);
   1.853 +  bool isMainThread = is_main_thread_name(aName);
   1.854 +  return Sampler::RegisterCurrentThread(aName, stack, isMainThread, stackTop);
   1.855 +}
   1.856 +
   1.857 +void mozilla_sampler_unregister_thread()
   1.858 +{
   1.859 +  Sampler::UnregisterCurrentThread();
   1.860 +
   1.861 +  PseudoStack *stack = tlsPseudoStack.get();
   1.862 +  if (!stack) {
   1.863 +    return;
   1.864 +  }
   1.865 +  delete stack;
   1.866 +  tlsPseudoStack.set(nullptr);
   1.867 +}
   1.868 +
   1.869 +void mozilla_sampler_sleep_start() {
   1.870 +    PseudoStack *stack = tlsPseudoStack.get();
   1.871 +    if (stack == nullptr) {
   1.872 +      return;
   1.873 +    }
   1.874 +    stack->setSleeping(1);
   1.875 +}
   1.876 +
   1.877 +void mozilla_sampler_sleep_end() {
   1.878 +    PseudoStack *stack = tlsPseudoStack.get();
   1.879 +    if (stack == nullptr) {
   1.880 +      return;
   1.881 +    }
   1.882 +    stack->setSleeping(0);
   1.883 +}
   1.884 +
   1.885 +double mozilla_sampler_time(const TimeStamp& aTime)
   1.886 +{
   1.887 +  if (!mozilla_sampler_is_active()) {
   1.888 +    return 0.0;
   1.889 +  }
   1.890 +  TimeDuration delta = aTime - sStartTime;
   1.891 +  return delta.ToMilliseconds();
   1.892 +}
   1.893 +
   1.894 +double mozilla_sampler_time()
   1.895 +{
   1.896 +  return mozilla_sampler_time(TimeStamp::Now());
   1.897 +}
   1.898 +
   1.899 +ProfilerBacktrace* mozilla_sampler_get_backtrace()
   1.900 +{
   1.901 +  if (!stack_key_initialized)
   1.902 +    return nullptr;
   1.903 +
   1.904 +  // Don't capture a stack if we're not profiling
   1.905 +  if (!profiler_is_active()) {
   1.906 +    return nullptr;
   1.907 +  }
   1.908 +
   1.909 +  // Don't capture a stack if we don't want to include personal information
   1.910 +  if (profiler_in_privacy_mode()) {
   1.911 +    return nullptr;
   1.912 +  }
   1.913 +
   1.914 +  TableTicker* t = tlsTicker.get();
   1.915 +  if (!t) {
   1.916 +    return nullptr;
   1.917 +  }
   1.918 +
   1.919 +  return new ProfilerBacktrace(t->GetBacktrace());
   1.920 +}
   1.921 +
   1.922 +void mozilla_sampler_free_backtrace(ProfilerBacktrace* aBacktrace)
   1.923 +{
   1.924 +  delete aBacktrace;
   1.925 +}
   1.926 +
   1.927 +void mozilla_sampler_tracing(const char* aCategory, const char* aInfo,
   1.928 +                             TracingMetadata aMetaData)
   1.929 +{
   1.930 +  mozilla_sampler_add_marker(aInfo, new ProfilerMarkerTracing(aCategory, aMetaData));
   1.931 +}
   1.932 +
   1.933 +void mozilla_sampler_add_marker(const char *aMarker, ProfilerMarkerPayload *aPayload)
   1.934 +{
   1.935 +  // Note that aPayload may be allocated by the caller, so we need to make sure
   1.936 +  // that we free it at some point.
   1.937 +  nsAutoPtr<ProfilerMarkerPayload> payload(aPayload);
   1.938 +
   1.939 +  if (!stack_key_initialized)
   1.940 +    return;
   1.941 +
   1.942 +  // Don't insert a marker if we're not profiling to avoid
   1.943 +  // the heap copy (malloc).
   1.944 +  if (!profiler_is_active()) {
   1.945 +    return;
   1.946 +  }
   1.947 +
   1.948 +  // Don't add a marker if we don't want to include personal information
   1.949 +  if (profiler_in_privacy_mode()) {
   1.950 +    return;
   1.951 +  }
   1.952 +
   1.953 +  PseudoStack *stack = tlsPseudoStack.get();
   1.954 +  if (!stack) {
   1.955 +    return;
   1.956 +  }
   1.957 +  TimeDuration delta = TimeStamp::Now() - sStartTime;
   1.958 +  stack->addMarker(aMarker, payload.forget(), static_cast<float>(delta.ToMilliseconds()));
   1.959 +}
   1.960 +
   1.961 +// END externally visible functions
   1.962 +////////////////////////////////////////////////////////////////////////
   1.963 +
   1.964 +

mercurial