tools/profiler/platform.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

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.

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

mercurial