1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/tools/profiler/TableTicker.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,751 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include <string> 1.10 +#include <stdio.h> 1.11 +#include <fstream> 1.12 +#include <sstream> 1.13 +#include "GeckoProfiler.h" 1.14 +#include "SaveProfileTask.h" 1.15 +#include "ProfileEntry.h" 1.16 +#include "SyncProfile.h" 1.17 +#include "platform.h" 1.18 +#include "nsThreadUtils.h" 1.19 +#include "prenv.h" 1.20 +#include "prtime.h" 1.21 +#include "shared-libraries.h" 1.22 +#include "mozilla/StackWalk.h" 1.23 +#include "TableTicker.h" 1.24 +#include "nsXULAppAPI.h" 1.25 + 1.26 +// JSON 1.27 +#include "JSStreamWriter.h" 1.28 + 1.29 +// Meta 1.30 +#include "nsXPCOM.h" 1.31 +#include "nsXPCOMCID.h" 1.32 +#include "nsIHttpProtocolHandler.h" 1.33 +#include "nsServiceManagerUtils.h" 1.34 +#include "nsIXULRuntime.h" 1.35 +#include "nsIXULAppInfo.h" 1.36 +#include "nsDirectoryServiceUtils.h" 1.37 +#include "nsDirectoryServiceDefs.h" 1.38 +#include "nsIObserverService.h" 1.39 +#include "mozilla/Services.h" 1.40 +#include "PlatformMacros.h" 1.41 + 1.42 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.43 + #include "AndroidBridge.h" 1.44 +#endif 1.45 + 1.46 +// JS 1.47 +#include "js/OldDebugAPI.h" 1.48 + 1.49 +#if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN)) 1.50 + #define USE_NS_STACKWALK 1.51 +#endif 1.52 +#ifdef USE_NS_STACKWALK 1.53 + #include "nsStackWalk.h" 1.54 +#endif 1.55 + 1.56 +#if defined(XP_WIN) 1.57 +typedef CONTEXT tickcontext_t; 1.58 +#elif defined(LINUX) 1.59 +#include <ucontext.h> 1.60 +typedef ucontext_t tickcontext_t; 1.61 +#endif 1.62 + 1.63 +#if defined(LINUX) || defined(XP_MACOSX) 1.64 +#include <sys/types.h> 1.65 +pid_t gettid(); 1.66 +#endif 1.67 + 1.68 +#if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK) 1.69 + // Should also work on other Android and ARM Linux, but not tested there yet. 1.70 + #define USE_EHABI_STACKWALK 1.71 +#endif 1.72 +#ifdef USE_EHABI_STACKWALK 1.73 + #include "EHABIStackWalk.h" 1.74 +#endif 1.75 + 1.76 +using std::string; 1.77 +using namespace mozilla; 1.78 + 1.79 +#ifndef MAXPATHLEN 1.80 + #ifdef PATH_MAX 1.81 + #define MAXPATHLEN PATH_MAX 1.82 + #elif defined(MAX_PATH) 1.83 + #define MAXPATHLEN MAX_PATH 1.84 + #elif defined(_MAX_PATH) 1.85 + #define MAXPATHLEN _MAX_PATH 1.86 + #elif defined(CCHMAXPATH) 1.87 + #define MAXPATHLEN CCHMAXPATH 1.88 + #else 1.89 + #define MAXPATHLEN 1024 1.90 + #endif 1.91 +#endif 1.92 + 1.93 +/////////////////////////////////////////////////////////////////////// 1.94 +// BEGIN SaveProfileTask et al 1.95 + 1.96 +std::string GetSharedLibraryInfoString(); 1.97 + 1.98 +void TableTicker::HandleSaveRequest() 1.99 +{ 1.100 + if (!mSaveRequested) 1.101 + return; 1.102 + mSaveRequested = false; 1.103 + 1.104 + // TODO: Use use the ipc/chromium Tasks here to support processes 1.105 + // without XPCOM. 1.106 + nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask(); 1.107 + NS_DispatchToMainThread(runnable); 1.108 +} 1.109 + 1.110 +void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b) 1.111 +{ 1.112 + b.BeginObject(); 1.113 + 1.114 + b.NameValue("version", 2); 1.115 + b.NameValue("interval", interval()); 1.116 + b.NameValue("stackwalk", mUseStackWalk); 1.117 + b.NameValue("jank", mJankOnly); 1.118 + b.NameValue("processType", XRE_GetProcessType()); 1.119 + 1.120 + TimeDuration delta = TimeStamp::Now() - sStartTime; 1.121 + b.NameValue("startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds())); 1.122 + 1.123 + nsresult res; 1.124 + nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res); 1.125 + if (!NS_FAILED(res)) { 1.126 + nsAutoCString string; 1.127 + 1.128 + res = http->GetPlatform(string); 1.129 + if (!NS_FAILED(res)) 1.130 + b.NameValue("platform", string.Data()); 1.131 + 1.132 + res = http->GetOscpu(string); 1.133 + if (!NS_FAILED(res)) 1.134 + b.NameValue("oscpu", string.Data()); 1.135 + 1.136 + res = http->GetMisc(string); 1.137 + if (!NS_FAILED(res)) 1.138 + b.NameValue("misc", string.Data()); 1.139 + } 1.140 + 1.141 + nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1"); 1.142 + if (runtime) { 1.143 + nsAutoCString string; 1.144 + 1.145 + res = runtime->GetXPCOMABI(string); 1.146 + if (!NS_FAILED(res)) 1.147 + b.NameValue("abi", string.Data()); 1.148 + 1.149 + res = runtime->GetWidgetToolkit(string); 1.150 + if (!NS_FAILED(res)) 1.151 + b.NameValue("toolkit", string.Data()); 1.152 + } 1.153 + 1.154 + nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1"); 1.155 + if (appInfo) { 1.156 + nsAutoCString string; 1.157 + 1.158 + res = appInfo->GetName(string); 1.159 + if (!NS_FAILED(res)) 1.160 + b.NameValue("product", string.Data()); 1.161 + } 1.162 + 1.163 + b.EndObject(); 1.164 +} 1.165 + 1.166 +void TableTicker::ToStreamAsJSON(std::ostream& stream) 1.167 +{ 1.168 + JSStreamWriter b(stream); 1.169 + StreamJSObject(b); 1.170 +} 1.171 + 1.172 +JSObject* TableTicker::ToJSObject(JSContext *aCx) 1.173 +{ 1.174 + JS::RootedValue val(aCx); 1.175 + std::stringstream ss; 1.176 + { 1.177 + // Define a scope to prevent a moving GC during ~JSStreamWriter from 1.178 + // trashing the return value. 1.179 + JSStreamWriter b(ss); 1.180 + StreamJSObject(b); 1.181 + NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str())); 1.182 + JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val); 1.183 + } 1.184 + return &val.toObject(); 1.185 +} 1.186 + 1.187 +struct SubprocessClosure { 1.188 + SubprocessClosure(JSStreamWriter *aWriter) 1.189 + : mWriter(aWriter) 1.190 + {} 1.191 + 1.192 + JSStreamWriter* mWriter; 1.193 +}; 1.194 + 1.195 +void SubProcessCallback(const char* aProfile, void* aClosure) 1.196 +{ 1.197 + // Called by the observer to get their profile data included 1.198 + // as a sub profile 1.199 + SubprocessClosure* closure = (SubprocessClosure*)aClosure; 1.200 + 1.201 + // Add the string profile into the profile 1.202 + closure->mWriter->Value(aProfile); 1.203 +} 1.204 + 1.205 + 1.206 +#if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.207 +static 1.208 +void BuildJavaThreadJSObject(JSStreamWriter& b) 1.209 +{ 1.210 + b.BeginObject(); 1.211 + 1.212 + b.NameValue("name", "Java Main Thread"); 1.213 + 1.214 + b.Name("samples"); 1.215 + b.BeginArray(); 1.216 + 1.217 + // for each sample 1.218 + for (int sampleId = 0; true; sampleId++) { 1.219 + bool firstRun = true; 1.220 + // for each frame 1.221 + for (int frameId = 0; true; frameId++) { 1.222 + nsCString result; 1.223 + bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result); 1.224 + // when we run out of frames, we stop looping 1.225 + if (!hasFrame) { 1.226 + // if we found at least one frame, we have objects to close 1.227 + if (!firstRun) { 1.228 + b.EndArray(); 1.229 + b.EndObject(); 1.230 + } 1.231 + break; 1.232 + } 1.233 + // the first time around, open the sample object and frames array 1.234 + if (firstRun) { 1.235 + firstRun = false; 1.236 + 1.237 + double sampleTime = 1.238 + mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId); 1.239 + 1.240 + b.BeginObject(); 1.241 + b.NameValue("time", sampleTime); 1.242 + 1.243 + b.Name("frames"); 1.244 + b.BeginArray(); 1.245 + } 1.246 + // add a frame to the sample 1.247 + b.BeginObject(); 1.248 + b.NameValue("location", result.BeginReading()); 1.249 + b.EndObject(); 1.250 + } 1.251 + // if we found no frames for this sample, we are done 1.252 + if (firstRun) { 1.253 + break; 1.254 + } 1.255 + } 1.256 + 1.257 + b.EndArray(); 1.258 + 1.259 + b.EndObject(); 1.260 +} 1.261 +#endif 1.262 + 1.263 +void TableTicker::StreamJSObject(JSStreamWriter& b) 1.264 +{ 1.265 + b.BeginObject(); 1.266 + // Put shared library info 1.267 + b.NameValue("libs", GetSharedLibraryInfoString().c_str()); 1.268 + 1.269 + // Put meta data 1.270 + b.Name("meta"); 1.271 + StreamMetaJSCustomObject(b); 1.272 + 1.273 + // Lists the samples for each ThreadProfile 1.274 + b.Name("threads"); 1.275 + b.BeginArray(); 1.276 + 1.277 + SetPaused(true); 1.278 + 1.279 + { 1.280 + mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex); 1.281 + 1.282 + for (size_t i = 0; i < sRegisteredThreads->size(); i++) { 1.283 + // Thread not being profiled, skip it 1.284 + if (!sRegisteredThreads->at(i)->Profile()) 1.285 + continue; 1.286 + 1.287 + MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex()); 1.288 + 1.289 + sRegisteredThreads->at(i)->Profile()->StreamJSObject(b); 1.290 + } 1.291 + } 1.292 + 1.293 + if (Sampler::CanNotifyObservers()) { 1.294 + // Send a event asking any subprocesses (plugins) to 1.295 + // give us their information 1.296 + SubprocessClosure closure(&b); 1.297 + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); 1.298 + if (os) { 1.299 + nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure); 1.300 + os->NotifyObservers(pse, "profiler-subprocess", nullptr); 1.301 + } 1.302 + } 1.303 + 1.304 + #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK) 1.305 + if (ProfileJava()) { 1.306 + mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling(); 1.307 + 1.308 + BuildJavaThreadJSObject(b); 1.309 + 1.310 + mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling(); 1.311 + } 1.312 + #endif 1.313 + 1.314 + SetPaused(false); 1.315 + b.EndArray(); 1.316 + 1.317 + b.EndObject(); 1.318 +} 1.319 + 1.320 +// END SaveProfileTask et al 1.321 +//////////////////////////////////////////////////////////////////////// 1.322 + 1.323 +static 1.324 +void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr) 1.325 +{ 1.326 + aProfile.addTag(ProfileEntry(aTagName, "")); 1.327 + // Add one to store the null termination 1.328 + size_t strLen = strlen(aStr) + 1; 1.329 + for (size_t j = 0; j < strLen;) { 1.330 + // Store as many characters in the void* as the platform allows 1.331 + char text[sizeof(void*)]; 1.332 + size_t len = sizeof(void*)/sizeof(char); 1.333 + if (j+len >= strLen) { 1.334 + len = strLen - j; 1.335 + } 1.336 + memcpy(text, &aStr[j], len); 1.337 + j += sizeof(void*)/sizeof(char); 1.338 + // Cast to *((void**) to pass the text data to a void* 1.339 + aProfile.addTag(ProfileEntry('d', *((void**)(&text[0])))); 1.340 + } 1.341 +} 1.342 + 1.343 +static 1.344 +void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile, 1.345 + PseudoStack *stack, void *lastpc) 1.346 +{ 1.347 + int lineno = -1; 1.348 + 1.349 + // First entry has tagName 's' (start) 1.350 + // Check for magic pointer bit 1 to indicate copy 1.351 + const char* sampleLabel = entry.label(); 1.352 + if (entry.isCopyLabel()) { 1.353 + // Store the string using 1 or more 'd' (dynamic) tags 1.354 + // that will happen to the preceding tag 1.355 + 1.356 + addDynamicTag(aProfile, 'c', sampleLabel); 1.357 + if (entry.js()) { 1.358 + if (!entry.pc()) { 1.359 + // The JIT only allows the top-most entry to have a nullptr pc 1.360 + MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]); 1.361 + // If stack-walking was disabled, then that's just unfortunate 1.362 + if (lastpc) { 1.363 + jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(), 1.364 + lastpc); 1.365 + if (jspc) { 1.366 + lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc); 1.367 + } 1.368 + } 1.369 + } else { 1.370 + lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc()); 1.371 + } 1.372 + } else { 1.373 + lineno = entry.line(); 1.374 + } 1.375 + } else { 1.376 + aProfile.addTag(ProfileEntry('c', sampleLabel)); 1.377 + lineno = entry.line(); 1.378 + } 1.379 + if (lineno != -1) { 1.380 + aProfile.addTag(ProfileEntry('n', lineno)); 1.381 + } 1.382 +} 1.383 + 1.384 +#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) 1.385 +typedef struct { 1.386 + void** array; 1.387 + void** sp_array; 1.388 + size_t size; 1.389 + size_t count; 1.390 +} PCArray; 1.391 + 1.392 +static void mergeNativeBacktrace(ThreadProfile &aProfile, const PCArray &array) { 1.393 + aProfile.addTag(ProfileEntry('s', "(root)")); 1.394 + 1.395 + PseudoStack* stack = aProfile.GetPseudoStack(); 1.396 + uint32_t pseudoStackPos = 0; 1.397 + 1.398 + /* We have two stacks, the native C stack we extracted from unwinding, 1.399 + * and the pseudostack we managed during execution. We want to consolidate 1.400 + * the two in order. We do so by merging using the approximate stack address 1.401 + * when each entry was push. When pushing JS entry we may not now the stack 1.402 + * address in which case we have a nullptr stack address in which case we assume 1.403 + * that it follows immediatly the previous element. 1.404 + * 1.405 + * C Stack | Address -- Pseudo Stack | Address 1.406 + * main() | 0x100 run_js() | 0x40 1.407 + * start() | 0x80 jsCanvas() | nullptr 1.408 + * timer() | 0x50 drawLine() | nullptr 1.409 + * azure() | 0x10 1.410 + * 1.411 + * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure() 1.412 + */ 1.413 + // i is the index in C stack starting at main and decreasing 1.414 + // pseudoStackPos is the position in the Pseudo stack starting 1.415 + // at the first frame (run_js in the example) and increasing. 1.416 + for (size_t i = array.count; i > 0; --i) { 1.417 + while (pseudoStackPos < stack->stackSize()) { 1.418 + volatile StackEntry& entry = stack->mStack[pseudoStackPos]; 1.419 + 1.420 + if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress()) 1.421 + break; 1.422 + 1.423 + addProfileEntry(entry, aProfile, stack, array.array[0]); 1.424 + pseudoStackPos++; 1.425 + } 1.426 + 1.427 + aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1])); 1.428 + } 1.429 +} 1.430 + 1.431 +#endif 1.432 + 1.433 +#ifdef USE_NS_STACKWALK 1.434 +static 1.435 +void StackWalkCallback(void* aPC, void* aSP, void* aClosure) 1.436 +{ 1.437 + PCArray* array = static_cast<PCArray*>(aClosure); 1.438 + MOZ_ASSERT(array->count < array->size); 1.439 + array->sp_array[array->count] = aSP; 1.440 + array->array[array->count] = aPC; 1.441 + array->count++; 1.442 +} 1.443 + 1.444 +void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample) 1.445 +{ 1.446 +#ifndef XP_MACOSX 1.447 + uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData()); 1.448 + MOZ_ASSERT(thread); 1.449 +#endif 1.450 + void* pc_array[1000]; 1.451 + void* sp_array[1000]; 1.452 + PCArray array = { 1.453 + pc_array, 1.454 + sp_array, 1.455 + mozilla::ArrayLength(pc_array), 1.456 + 0 1.457 + }; 1.458 + 1.459 + // Start with the current function. 1.460 + StackWalkCallback(aSample->pc, aSample->sp, &array); 1.461 + 1.462 + uint32_t maxFrames = uint32_t(array.size - array.count); 1.463 +#ifdef XP_MACOSX 1.464 + pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData()); 1.465 + void *stackEnd = reinterpret_cast<void*>(-1); 1.466 + if (pt) 1.467 + stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt)); 1.468 + nsresult rv = NS_OK; 1.469 + if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd) 1.470 + rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0, 1.471 + maxFrames, &array, 1.472 + reinterpret_cast<void**>(aSample->fp), stackEnd); 1.473 +#else 1.474 + void *platformData = nullptr; 1.475 +#ifdef XP_WIN 1.476 + if (aSample->isSamplingCurrentThread) { 1.477 + // In this case we want NS_StackWalk to know that it's walking the 1.478 + // current thread's stack, so we pass 0 as the thread handle. 1.479 + thread = 0; 1.480 + } 1.481 + platformData = aSample->context; 1.482 +#endif // XP_WIN 1.483 + 1.484 + nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames, 1.485 + &array, thread, platformData); 1.486 +#endif 1.487 + if (NS_SUCCEEDED(rv)) 1.488 + mergeNativeBacktrace(aProfile, array); 1.489 +} 1.490 +#endif 1.491 + 1.492 +#ifdef USE_EHABI_STACKWALK 1.493 +void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample) 1.494 +{ 1.495 + void *pc_array[1000]; 1.496 + void *sp_array[1000]; 1.497 + PCArray array = { 1.498 + pc_array, 1.499 + sp_array, 1.500 + mozilla::ArrayLength(pc_array), 1.501 + 0 1.502 + }; 1.503 + 1.504 + const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext; 1.505 + mcontext_t savedContext; 1.506 + PseudoStack *pseudoStack = aProfile.GetPseudoStack(); 1.507 + 1.508 + array.count = 0; 1.509 + // The pseudostack contains an "EnterJIT" frame whenever we enter 1.510 + // JIT code with profiling enabled; the stack pointer value points 1.511 + // the saved registers. We use this to unwind resume unwinding 1.512 + // after encounting JIT code. 1.513 + for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) { 1.514 + // The pseudostack grows towards higher indices, so we iterate 1.515 + // backwards (from callee to caller). 1.516 + volatile StackEntry &entry = pseudoStack->mStack[i - 1]; 1.517 + if (!entry.js() && strcmp(entry.label(), "EnterJIT") == 0) { 1.518 + // Found JIT entry frame. Unwind up to that point (i.e., force 1.519 + // the stack walk to stop before the block of saved registers; 1.520 + // note that it yields nondecreasing stack pointers), then restore 1.521 + // the saved state. 1.522 + uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress()); 1.523 + 1.524 + array.count += EHABIStackWalk(*mcontext, 1.525 + /* stackBase = */ vSP, 1.526 + sp_array + array.count, 1.527 + pc_array + array.count, 1.528 + array.size - array.count); 1.529 + 1.530 + memset(&savedContext, 0, sizeof(savedContext)); 1.531 + // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp 1.532 + savedContext.arm_r4 = *vSP++; 1.533 + savedContext.arm_r5 = *vSP++; 1.534 + savedContext.arm_r6 = *vSP++; 1.535 + savedContext.arm_r7 = *vSP++; 1.536 + savedContext.arm_r8 = *vSP++; 1.537 + savedContext.arm_r9 = *vSP++; 1.538 + savedContext.arm_r10 = *vSP++; 1.539 + savedContext.arm_fp = *vSP++; 1.540 + savedContext.arm_lr = *vSP++; 1.541 + savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP); 1.542 + savedContext.arm_pc = savedContext.arm_lr; 1.543 + mcontext = &savedContext; 1.544 + } 1.545 + } 1.546 + 1.547 + // Now unwind whatever's left (starting from either the last EnterJIT 1.548 + // frame or, if no EnterJIT was found, the original registers). 1.549 + array.count += EHABIStackWalk(*mcontext, 1.550 + aProfile.GetStackTop(), 1.551 + sp_array + array.count, 1.552 + pc_array + array.count, 1.553 + array.size - array.count); 1.554 + 1.555 + mergeNativeBacktrace(aProfile, array); 1.556 +} 1.557 + 1.558 +#endif 1.559 + 1.560 +static 1.561 +void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample) 1.562 +{ 1.563 + // Sample 1.564 + // 's' tag denotes the start of a sample block 1.565 + // followed by 0 or more 'c' tags. 1.566 + aProfile.addTag(ProfileEntry('s', "(root)")); 1.567 + for (uint32_t i = 0; i < aStack->stackSize(); i++) { 1.568 + addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr); 1.569 + } 1.570 +#ifdef ENABLE_SPS_LEAF_DATA 1.571 + if (sample) { 1.572 + aProfile.addTag(ProfileEntry('l', (void*)sample->pc)); 1.573 +#ifdef ENABLE_ARM_LR_SAVING 1.574 + aProfile.addTag(ProfileEntry('L', (void*)sample->lr)); 1.575 +#endif 1.576 + } 1.577 +#endif 1.578 +} 1.579 + 1.580 +void TableTicker::Tick(TickSample* sample) 1.581 +{ 1.582 + if (HasUnwinderThread()) { 1.583 + UnwinderTick(sample); 1.584 + } else { 1.585 + InplaceTick(sample); 1.586 + } 1.587 +} 1.588 + 1.589 +void TableTicker::InplaceTick(TickSample* sample) 1.590 +{ 1.591 + ThreadProfile& currThreadProfile = *sample->threadProfile; 1.592 + 1.593 + PseudoStack* stack = currThreadProfile.GetPseudoStack(); 1.594 + bool recordSample = true; 1.595 +#if defined(XP_WIN) 1.596 + bool powerSample = false; 1.597 +#endif 1.598 + 1.599 + /* Don't process the PeudoStack's markers or honour jankOnly if we're 1.600 + immediately sampling the current thread. */ 1.601 + if (!sample->isSamplingCurrentThread) { 1.602 + // Marker(s) come before the sample 1.603 + ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers(); 1.604 + while (pendingMarkersList && pendingMarkersList->peek()) { 1.605 + ProfilerMarker* marker = pendingMarkersList->popHead(); 1.606 + stack->addStoredMarker(marker); 1.607 + currThreadProfile.addTag(ProfileEntry('m', marker)); 1.608 + } 1.609 + stack->updateGeneration(currThreadProfile.GetGenerationID()); 1.610 + 1.611 +#if defined(XP_WIN) 1.612 + if (mProfilePower) { 1.613 + mIntelPowerGadget->TakeSample(); 1.614 + powerSample = true; 1.615 + } 1.616 +#endif 1.617 + 1.618 + if (mJankOnly) { 1.619 + // if we are on a different event we can discard any temporary samples 1.620 + // we've kept around 1.621 + if (sLastSampledEventGeneration != sCurrentEventGeneration) { 1.622 + // XXX: we also probably want to add an entry to the profile to help 1.623 + // distinguish which samples are part of the same event. That, or record 1.624 + // the event generation in each sample 1.625 + currThreadProfile.erase(); 1.626 + } 1.627 + sLastSampledEventGeneration = sCurrentEventGeneration; 1.628 + 1.629 + recordSample = false; 1.630 + // only record the events when we have a we haven't seen a tracer event for 100ms 1.631 + if (!sLastTracerEvent.IsNull()) { 1.632 + TimeDuration delta = sample->timestamp - sLastTracerEvent; 1.633 + if (delta.ToMilliseconds() > 100.0) { 1.634 + recordSample = true; 1.635 + } 1.636 + } 1.637 + } 1.638 + } 1.639 + 1.640 +#if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK) 1.641 + if (mUseStackWalk) { 1.642 + doNativeBacktrace(currThreadProfile, sample); 1.643 + } else { 1.644 + doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); 1.645 + } 1.646 +#else 1.647 + doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); 1.648 +#endif 1.649 + 1.650 + if (recordSample) 1.651 + currThreadProfile.flush(); 1.652 + 1.653 + if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) { 1.654 + TimeDuration delta = sample->timestamp - sLastTracerEvent; 1.655 + currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds()))); 1.656 + } 1.657 + 1.658 + if (sample) { 1.659 + TimeDuration delta = sample->timestamp - sStartTime; 1.660 + currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds()))); 1.661 + } 1.662 + 1.663 +#if defined(XP_WIN) 1.664 + if (powerSample) { 1.665 + currThreadProfile.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget->GetTotalPackagePowerInWatts()))); 1.666 + } 1.667 +#endif 1.668 + 1.669 + if (sLastFrameNumber != sFrameNumber) { 1.670 + currThreadProfile.addTag(ProfileEntry('f', sFrameNumber)); 1.671 + sLastFrameNumber = sFrameNumber; 1.672 + } 1.673 +} 1.674 + 1.675 +namespace { 1.676 + 1.677 +SyncProfile* NewSyncProfile() 1.678 +{ 1.679 + PseudoStack* stack = tlsPseudoStack.get(); 1.680 + if (!stack) { 1.681 + MOZ_ASSERT(stack); 1.682 + return nullptr; 1.683 + } 1.684 + Thread::tid_t tid = Thread::GetCurrentId(); 1.685 + 1.686 + SyncProfile* profile = new SyncProfile("SyncProfile", 1.687 + GET_BACKTRACE_DEFAULT_ENTRY, 1.688 + stack, tid, NS_IsMainThread()); 1.689 + return profile; 1.690 +} 1.691 + 1.692 +} // anonymous namespace 1.693 + 1.694 +SyncProfile* TableTicker::GetBacktrace() 1.695 +{ 1.696 + SyncProfile* profile = NewSyncProfile(); 1.697 + 1.698 + TickSample sample; 1.699 + sample.threadProfile = profile; 1.700 + 1.701 +#if defined(HAVE_NATIVE_UNWIND) 1.702 +#if defined(XP_WIN) || defined(LINUX) 1.703 + tickcontext_t context; 1.704 + sample.PopulateContext(&context); 1.705 +#elif defined(XP_MACOSX) 1.706 + sample.PopulateContext(nullptr); 1.707 +#endif 1.708 +#endif 1.709 + 1.710 + sample.isSamplingCurrentThread = true; 1.711 + sample.timestamp = mozilla::TimeStamp::Now(); 1.712 + 1.713 + if (!HasUnwinderThread()) { 1.714 + profile->BeginUnwind(); 1.715 + } 1.716 + 1.717 + Tick(&sample); 1.718 + 1.719 + if (!HasUnwinderThread()) { 1.720 + profile->EndUnwind(); 1.721 + } 1.722 + 1.723 + return profile; 1.724 +} 1.725 + 1.726 +static void print_callback(const ProfileEntry& entry, const char* tagStringData) 1.727 +{ 1.728 + switch (entry.getTagName()) { 1.729 + case 's': 1.730 + case 'c': 1.731 + printf_stderr(" %s\n", tagStringData); 1.732 + } 1.733 +} 1.734 + 1.735 +void mozilla_sampler_print_location1() 1.736 +{ 1.737 + if (!stack_key_initialized) 1.738 + profiler_init(nullptr); 1.739 + 1.740 + SyncProfile* syncProfile = NewSyncProfile(); 1.741 + if (!syncProfile) { 1.742 + return; 1.743 + } 1.744 + 1.745 + syncProfile->BeginUnwind(); 1.746 + doSampleStackTrace(syncProfile->GetPseudoStack(), *syncProfile, nullptr); 1.747 + syncProfile->EndUnwind(); 1.748 + 1.749 + printf_stderr("Backtrace:\n"); 1.750 + syncProfile->IterateTags(print_callback); 1.751 + delete syncProfile; 1.752 +} 1.753 + 1.754 +