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 +