tools/profiler/TableTicker.cpp

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

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

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

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #include <string>
michael@0 7 #include <stdio.h>
michael@0 8 #include <fstream>
michael@0 9 #include <sstream>
michael@0 10 #include "GeckoProfiler.h"
michael@0 11 #include "SaveProfileTask.h"
michael@0 12 #include "ProfileEntry.h"
michael@0 13 #include "SyncProfile.h"
michael@0 14 #include "platform.h"
michael@0 15 #include "nsThreadUtils.h"
michael@0 16 #include "prenv.h"
michael@0 17 #include "prtime.h"
michael@0 18 #include "shared-libraries.h"
michael@0 19 #include "mozilla/StackWalk.h"
michael@0 20 #include "TableTicker.h"
michael@0 21 #include "nsXULAppAPI.h"
michael@0 22
michael@0 23 // JSON
michael@0 24 #include "JSStreamWriter.h"
michael@0 25
michael@0 26 // Meta
michael@0 27 #include "nsXPCOM.h"
michael@0 28 #include "nsXPCOMCID.h"
michael@0 29 #include "nsIHttpProtocolHandler.h"
michael@0 30 #include "nsServiceManagerUtils.h"
michael@0 31 #include "nsIXULRuntime.h"
michael@0 32 #include "nsIXULAppInfo.h"
michael@0 33 #include "nsDirectoryServiceUtils.h"
michael@0 34 #include "nsDirectoryServiceDefs.h"
michael@0 35 #include "nsIObserverService.h"
michael@0 36 #include "mozilla/Services.h"
michael@0 37 #include "PlatformMacros.h"
michael@0 38
michael@0 39 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
michael@0 40 #include "AndroidBridge.h"
michael@0 41 #endif
michael@0 42
michael@0 43 // JS
michael@0 44 #include "js/OldDebugAPI.h"
michael@0 45
michael@0 46 #if defined(MOZ_PROFILING) && (defined(XP_MACOSX) || defined(XP_WIN))
michael@0 47 #define USE_NS_STACKWALK
michael@0 48 #endif
michael@0 49 #ifdef USE_NS_STACKWALK
michael@0 50 #include "nsStackWalk.h"
michael@0 51 #endif
michael@0 52
michael@0 53 #if defined(XP_WIN)
michael@0 54 typedef CONTEXT tickcontext_t;
michael@0 55 #elif defined(LINUX)
michael@0 56 #include <ucontext.h>
michael@0 57 typedef ucontext_t tickcontext_t;
michael@0 58 #endif
michael@0 59
michael@0 60 #if defined(LINUX) || defined(XP_MACOSX)
michael@0 61 #include <sys/types.h>
michael@0 62 pid_t gettid();
michael@0 63 #endif
michael@0 64
michael@0 65 #if defined(SPS_ARCH_arm) && defined(MOZ_WIDGET_GONK)
michael@0 66 // Should also work on other Android and ARM Linux, but not tested there yet.
michael@0 67 #define USE_EHABI_STACKWALK
michael@0 68 #endif
michael@0 69 #ifdef USE_EHABI_STACKWALK
michael@0 70 #include "EHABIStackWalk.h"
michael@0 71 #endif
michael@0 72
michael@0 73 using std::string;
michael@0 74 using namespace mozilla;
michael@0 75
michael@0 76 #ifndef MAXPATHLEN
michael@0 77 #ifdef PATH_MAX
michael@0 78 #define MAXPATHLEN PATH_MAX
michael@0 79 #elif defined(MAX_PATH)
michael@0 80 #define MAXPATHLEN MAX_PATH
michael@0 81 #elif defined(_MAX_PATH)
michael@0 82 #define MAXPATHLEN _MAX_PATH
michael@0 83 #elif defined(CCHMAXPATH)
michael@0 84 #define MAXPATHLEN CCHMAXPATH
michael@0 85 #else
michael@0 86 #define MAXPATHLEN 1024
michael@0 87 #endif
michael@0 88 #endif
michael@0 89
michael@0 90 ///////////////////////////////////////////////////////////////////////
michael@0 91 // BEGIN SaveProfileTask et al
michael@0 92
michael@0 93 std::string GetSharedLibraryInfoString();
michael@0 94
michael@0 95 void TableTicker::HandleSaveRequest()
michael@0 96 {
michael@0 97 if (!mSaveRequested)
michael@0 98 return;
michael@0 99 mSaveRequested = false;
michael@0 100
michael@0 101 // TODO: Use use the ipc/chromium Tasks here to support processes
michael@0 102 // without XPCOM.
michael@0 103 nsCOMPtr<nsIRunnable> runnable = new SaveProfileTask();
michael@0 104 NS_DispatchToMainThread(runnable);
michael@0 105 }
michael@0 106
michael@0 107 void TableTicker::StreamMetaJSCustomObject(JSStreamWriter& b)
michael@0 108 {
michael@0 109 b.BeginObject();
michael@0 110
michael@0 111 b.NameValue("version", 2);
michael@0 112 b.NameValue("interval", interval());
michael@0 113 b.NameValue("stackwalk", mUseStackWalk);
michael@0 114 b.NameValue("jank", mJankOnly);
michael@0 115 b.NameValue("processType", XRE_GetProcessType());
michael@0 116
michael@0 117 TimeDuration delta = TimeStamp::Now() - sStartTime;
michael@0 118 b.NameValue("startTime", static_cast<float>(PR_Now()/1000.0 - delta.ToMilliseconds()));
michael@0 119
michael@0 120 nsresult res;
michael@0 121 nsCOMPtr<nsIHttpProtocolHandler> http = do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "http", &res);
michael@0 122 if (!NS_FAILED(res)) {
michael@0 123 nsAutoCString string;
michael@0 124
michael@0 125 res = http->GetPlatform(string);
michael@0 126 if (!NS_FAILED(res))
michael@0 127 b.NameValue("platform", string.Data());
michael@0 128
michael@0 129 res = http->GetOscpu(string);
michael@0 130 if (!NS_FAILED(res))
michael@0 131 b.NameValue("oscpu", string.Data());
michael@0 132
michael@0 133 res = http->GetMisc(string);
michael@0 134 if (!NS_FAILED(res))
michael@0 135 b.NameValue("misc", string.Data());
michael@0 136 }
michael@0 137
michael@0 138 nsCOMPtr<nsIXULRuntime> runtime = do_GetService("@mozilla.org/xre/runtime;1");
michael@0 139 if (runtime) {
michael@0 140 nsAutoCString string;
michael@0 141
michael@0 142 res = runtime->GetXPCOMABI(string);
michael@0 143 if (!NS_FAILED(res))
michael@0 144 b.NameValue("abi", string.Data());
michael@0 145
michael@0 146 res = runtime->GetWidgetToolkit(string);
michael@0 147 if (!NS_FAILED(res))
michael@0 148 b.NameValue("toolkit", string.Data());
michael@0 149 }
michael@0 150
michael@0 151 nsCOMPtr<nsIXULAppInfo> appInfo = do_GetService("@mozilla.org/xre/app-info;1");
michael@0 152 if (appInfo) {
michael@0 153 nsAutoCString string;
michael@0 154
michael@0 155 res = appInfo->GetName(string);
michael@0 156 if (!NS_FAILED(res))
michael@0 157 b.NameValue("product", string.Data());
michael@0 158 }
michael@0 159
michael@0 160 b.EndObject();
michael@0 161 }
michael@0 162
michael@0 163 void TableTicker::ToStreamAsJSON(std::ostream& stream)
michael@0 164 {
michael@0 165 JSStreamWriter b(stream);
michael@0 166 StreamJSObject(b);
michael@0 167 }
michael@0 168
michael@0 169 JSObject* TableTicker::ToJSObject(JSContext *aCx)
michael@0 170 {
michael@0 171 JS::RootedValue val(aCx);
michael@0 172 std::stringstream ss;
michael@0 173 {
michael@0 174 // Define a scope to prevent a moving GC during ~JSStreamWriter from
michael@0 175 // trashing the return value.
michael@0 176 JSStreamWriter b(ss);
michael@0 177 StreamJSObject(b);
michael@0 178 NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str()));
michael@0 179 JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val);
michael@0 180 }
michael@0 181 return &val.toObject();
michael@0 182 }
michael@0 183
michael@0 184 struct SubprocessClosure {
michael@0 185 SubprocessClosure(JSStreamWriter *aWriter)
michael@0 186 : mWriter(aWriter)
michael@0 187 {}
michael@0 188
michael@0 189 JSStreamWriter* mWriter;
michael@0 190 };
michael@0 191
michael@0 192 void SubProcessCallback(const char* aProfile, void* aClosure)
michael@0 193 {
michael@0 194 // Called by the observer to get their profile data included
michael@0 195 // as a sub profile
michael@0 196 SubprocessClosure* closure = (SubprocessClosure*)aClosure;
michael@0 197
michael@0 198 // Add the string profile into the profile
michael@0 199 closure->mWriter->Value(aProfile);
michael@0 200 }
michael@0 201
michael@0 202
michael@0 203 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
michael@0 204 static
michael@0 205 void BuildJavaThreadJSObject(JSStreamWriter& b)
michael@0 206 {
michael@0 207 b.BeginObject();
michael@0 208
michael@0 209 b.NameValue("name", "Java Main Thread");
michael@0 210
michael@0 211 b.Name("samples");
michael@0 212 b.BeginArray();
michael@0 213
michael@0 214 // for each sample
michael@0 215 for (int sampleId = 0; true; sampleId++) {
michael@0 216 bool firstRun = true;
michael@0 217 // for each frame
michael@0 218 for (int frameId = 0; true; frameId++) {
michael@0 219 nsCString result;
michael@0 220 bool hasFrame = AndroidBridge::Bridge()->GetFrameNameJavaProfiling(0, sampleId, frameId, result);
michael@0 221 // when we run out of frames, we stop looping
michael@0 222 if (!hasFrame) {
michael@0 223 // if we found at least one frame, we have objects to close
michael@0 224 if (!firstRun) {
michael@0 225 b.EndArray();
michael@0 226 b.EndObject();
michael@0 227 }
michael@0 228 break;
michael@0 229 }
michael@0 230 // the first time around, open the sample object and frames array
michael@0 231 if (firstRun) {
michael@0 232 firstRun = false;
michael@0 233
michael@0 234 double sampleTime =
michael@0 235 mozilla::widget::android::GeckoJavaSampler::GetSampleTimeJavaProfiling(0, sampleId);
michael@0 236
michael@0 237 b.BeginObject();
michael@0 238 b.NameValue("time", sampleTime);
michael@0 239
michael@0 240 b.Name("frames");
michael@0 241 b.BeginArray();
michael@0 242 }
michael@0 243 // add a frame to the sample
michael@0 244 b.BeginObject();
michael@0 245 b.NameValue("location", result.BeginReading());
michael@0 246 b.EndObject();
michael@0 247 }
michael@0 248 // if we found no frames for this sample, we are done
michael@0 249 if (firstRun) {
michael@0 250 break;
michael@0 251 }
michael@0 252 }
michael@0 253
michael@0 254 b.EndArray();
michael@0 255
michael@0 256 b.EndObject();
michael@0 257 }
michael@0 258 #endif
michael@0 259
michael@0 260 void TableTicker::StreamJSObject(JSStreamWriter& b)
michael@0 261 {
michael@0 262 b.BeginObject();
michael@0 263 // Put shared library info
michael@0 264 b.NameValue("libs", GetSharedLibraryInfoString().c_str());
michael@0 265
michael@0 266 // Put meta data
michael@0 267 b.Name("meta");
michael@0 268 StreamMetaJSCustomObject(b);
michael@0 269
michael@0 270 // Lists the samples for each ThreadProfile
michael@0 271 b.Name("threads");
michael@0 272 b.BeginArray();
michael@0 273
michael@0 274 SetPaused(true);
michael@0 275
michael@0 276 {
michael@0 277 mozilla::MutexAutoLock lock(*sRegisteredThreadsMutex);
michael@0 278
michael@0 279 for (size_t i = 0; i < sRegisteredThreads->size(); i++) {
michael@0 280 // Thread not being profiled, skip it
michael@0 281 if (!sRegisteredThreads->at(i)->Profile())
michael@0 282 continue;
michael@0 283
michael@0 284 MutexAutoLock lock(*sRegisteredThreads->at(i)->Profile()->GetMutex());
michael@0 285
michael@0 286 sRegisteredThreads->at(i)->Profile()->StreamJSObject(b);
michael@0 287 }
michael@0 288 }
michael@0 289
michael@0 290 if (Sampler::CanNotifyObservers()) {
michael@0 291 // Send a event asking any subprocesses (plugins) to
michael@0 292 // give us their information
michael@0 293 SubprocessClosure closure(&b);
michael@0 294 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
michael@0 295 if (os) {
michael@0 296 nsRefPtr<ProfileSaveEvent> pse = new ProfileSaveEvent(SubProcessCallback, &closure);
michael@0 297 os->NotifyObservers(pse, "profiler-subprocess", nullptr);
michael@0 298 }
michael@0 299 }
michael@0 300
michael@0 301 #if defined(SPS_OS_android) && !defined(MOZ_WIDGET_GONK)
michael@0 302 if (ProfileJava()) {
michael@0 303 mozilla::widget::android::GeckoJavaSampler::PauseJavaProfiling();
michael@0 304
michael@0 305 BuildJavaThreadJSObject(b);
michael@0 306
michael@0 307 mozilla::widget::android::GeckoJavaSampler::UnpauseJavaProfiling();
michael@0 308 }
michael@0 309 #endif
michael@0 310
michael@0 311 SetPaused(false);
michael@0 312 b.EndArray();
michael@0 313
michael@0 314 b.EndObject();
michael@0 315 }
michael@0 316
michael@0 317 // END SaveProfileTask et al
michael@0 318 ////////////////////////////////////////////////////////////////////////
michael@0 319
michael@0 320 static
michael@0 321 void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
michael@0 322 {
michael@0 323 aProfile.addTag(ProfileEntry(aTagName, ""));
michael@0 324 // Add one to store the null termination
michael@0 325 size_t strLen = strlen(aStr) + 1;
michael@0 326 for (size_t j = 0; j < strLen;) {
michael@0 327 // Store as many characters in the void* as the platform allows
michael@0 328 char text[sizeof(void*)];
michael@0 329 size_t len = sizeof(void*)/sizeof(char);
michael@0 330 if (j+len >= strLen) {
michael@0 331 len = strLen - j;
michael@0 332 }
michael@0 333 memcpy(text, &aStr[j], len);
michael@0 334 j += sizeof(void*)/sizeof(char);
michael@0 335 // Cast to *((void**) to pass the text data to a void*
michael@0 336 aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
michael@0 337 }
michael@0 338 }
michael@0 339
michael@0 340 static
michael@0 341 void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
michael@0 342 PseudoStack *stack, void *lastpc)
michael@0 343 {
michael@0 344 int lineno = -1;
michael@0 345
michael@0 346 // First entry has tagName 's' (start)
michael@0 347 // Check for magic pointer bit 1 to indicate copy
michael@0 348 const char* sampleLabel = entry.label();
michael@0 349 if (entry.isCopyLabel()) {
michael@0 350 // Store the string using 1 or more 'd' (dynamic) tags
michael@0 351 // that will happen to the preceding tag
michael@0 352
michael@0 353 addDynamicTag(aProfile, 'c', sampleLabel);
michael@0 354 if (entry.js()) {
michael@0 355 if (!entry.pc()) {
michael@0 356 // The JIT only allows the top-most entry to have a nullptr pc
michael@0 357 MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
michael@0 358 // If stack-walking was disabled, then that's just unfortunate
michael@0 359 if (lastpc) {
michael@0 360 jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
michael@0 361 lastpc);
michael@0 362 if (jspc) {
michael@0 363 lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc);
michael@0 364 }
michael@0 365 }
michael@0 366 } else {
michael@0 367 lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
michael@0 368 }
michael@0 369 } else {
michael@0 370 lineno = entry.line();
michael@0 371 }
michael@0 372 } else {
michael@0 373 aProfile.addTag(ProfileEntry('c', sampleLabel));
michael@0 374 lineno = entry.line();
michael@0 375 }
michael@0 376 if (lineno != -1) {
michael@0 377 aProfile.addTag(ProfileEntry('n', lineno));
michael@0 378 }
michael@0 379 }
michael@0 380
michael@0 381 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
michael@0 382 typedef struct {
michael@0 383 void** array;
michael@0 384 void** sp_array;
michael@0 385 size_t size;
michael@0 386 size_t count;
michael@0 387 } PCArray;
michael@0 388
michael@0 389 static void mergeNativeBacktrace(ThreadProfile &aProfile, const PCArray &array) {
michael@0 390 aProfile.addTag(ProfileEntry('s', "(root)"));
michael@0 391
michael@0 392 PseudoStack* stack = aProfile.GetPseudoStack();
michael@0 393 uint32_t pseudoStackPos = 0;
michael@0 394
michael@0 395 /* We have two stacks, the native C stack we extracted from unwinding,
michael@0 396 * and the pseudostack we managed during execution. We want to consolidate
michael@0 397 * the two in order. We do so by merging using the approximate stack address
michael@0 398 * when each entry was push. When pushing JS entry we may not now the stack
michael@0 399 * address in which case we have a nullptr stack address in which case we assume
michael@0 400 * that it follows immediatly the previous element.
michael@0 401 *
michael@0 402 * C Stack | Address -- Pseudo Stack | Address
michael@0 403 * main() | 0x100 run_js() | 0x40
michael@0 404 * start() | 0x80 jsCanvas() | nullptr
michael@0 405 * timer() | 0x50 drawLine() | nullptr
michael@0 406 * azure() | 0x10
michael@0 407 *
michael@0 408 * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure()
michael@0 409 */
michael@0 410 // i is the index in C stack starting at main and decreasing
michael@0 411 // pseudoStackPos is the position in the Pseudo stack starting
michael@0 412 // at the first frame (run_js in the example) and increasing.
michael@0 413 for (size_t i = array.count; i > 0; --i) {
michael@0 414 while (pseudoStackPos < stack->stackSize()) {
michael@0 415 volatile StackEntry& entry = stack->mStack[pseudoStackPos];
michael@0 416
michael@0 417 if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress())
michael@0 418 break;
michael@0 419
michael@0 420 addProfileEntry(entry, aProfile, stack, array.array[0]);
michael@0 421 pseudoStackPos++;
michael@0 422 }
michael@0 423
michael@0 424 aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1]));
michael@0 425 }
michael@0 426 }
michael@0 427
michael@0 428 #endif
michael@0 429
michael@0 430 #ifdef USE_NS_STACKWALK
michael@0 431 static
michael@0 432 void StackWalkCallback(void* aPC, void* aSP, void* aClosure)
michael@0 433 {
michael@0 434 PCArray* array = static_cast<PCArray*>(aClosure);
michael@0 435 MOZ_ASSERT(array->count < array->size);
michael@0 436 array->sp_array[array->count] = aSP;
michael@0 437 array->array[array->count] = aPC;
michael@0 438 array->count++;
michael@0 439 }
michael@0 440
michael@0 441 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
michael@0 442 {
michael@0 443 #ifndef XP_MACOSX
michael@0 444 uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
michael@0 445 MOZ_ASSERT(thread);
michael@0 446 #endif
michael@0 447 void* pc_array[1000];
michael@0 448 void* sp_array[1000];
michael@0 449 PCArray array = {
michael@0 450 pc_array,
michael@0 451 sp_array,
michael@0 452 mozilla::ArrayLength(pc_array),
michael@0 453 0
michael@0 454 };
michael@0 455
michael@0 456 // Start with the current function.
michael@0 457 StackWalkCallback(aSample->pc, aSample->sp, &array);
michael@0 458
michael@0 459 uint32_t maxFrames = uint32_t(array.size - array.count);
michael@0 460 #ifdef XP_MACOSX
michael@0 461 pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
michael@0 462 void *stackEnd = reinterpret_cast<void*>(-1);
michael@0 463 if (pt)
michael@0 464 stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
michael@0 465 nsresult rv = NS_OK;
michael@0 466 if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
michael@0 467 rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
michael@0 468 maxFrames, &array,
michael@0 469 reinterpret_cast<void**>(aSample->fp), stackEnd);
michael@0 470 #else
michael@0 471 void *platformData = nullptr;
michael@0 472 #ifdef XP_WIN
michael@0 473 if (aSample->isSamplingCurrentThread) {
michael@0 474 // In this case we want NS_StackWalk to know that it's walking the
michael@0 475 // current thread's stack, so we pass 0 as the thread handle.
michael@0 476 thread = 0;
michael@0 477 }
michael@0 478 platformData = aSample->context;
michael@0 479 #endif // XP_WIN
michael@0 480
michael@0 481 nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
michael@0 482 &array, thread, platformData);
michael@0 483 #endif
michael@0 484 if (NS_SUCCEEDED(rv))
michael@0 485 mergeNativeBacktrace(aProfile, array);
michael@0 486 }
michael@0 487 #endif
michael@0 488
michael@0 489 #ifdef USE_EHABI_STACKWALK
michael@0 490 void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
michael@0 491 {
michael@0 492 void *pc_array[1000];
michael@0 493 void *sp_array[1000];
michael@0 494 PCArray array = {
michael@0 495 pc_array,
michael@0 496 sp_array,
michael@0 497 mozilla::ArrayLength(pc_array),
michael@0 498 0
michael@0 499 };
michael@0 500
michael@0 501 const mcontext_t *mcontext = &reinterpret_cast<ucontext_t *>(aSample->context)->uc_mcontext;
michael@0 502 mcontext_t savedContext;
michael@0 503 PseudoStack *pseudoStack = aProfile.GetPseudoStack();
michael@0 504
michael@0 505 array.count = 0;
michael@0 506 // The pseudostack contains an "EnterJIT" frame whenever we enter
michael@0 507 // JIT code with profiling enabled; the stack pointer value points
michael@0 508 // the saved registers. We use this to unwind resume unwinding
michael@0 509 // after encounting JIT code.
michael@0 510 for (uint32_t i = pseudoStack->stackSize(); i > 0; --i) {
michael@0 511 // The pseudostack grows towards higher indices, so we iterate
michael@0 512 // backwards (from callee to caller).
michael@0 513 volatile StackEntry &entry = pseudoStack->mStack[i - 1];
michael@0 514 if (!entry.js() && strcmp(entry.label(), "EnterJIT") == 0) {
michael@0 515 // Found JIT entry frame. Unwind up to that point (i.e., force
michael@0 516 // the stack walk to stop before the block of saved registers;
michael@0 517 // note that it yields nondecreasing stack pointers), then restore
michael@0 518 // the saved state.
michael@0 519 uint32_t *vSP = reinterpret_cast<uint32_t*>(entry.stackAddress());
michael@0 520
michael@0 521 array.count += EHABIStackWalk(*mcontext,
michael@0 522 /* stackBase = */ vSP,
michael@0 523 sp_array + array.count,
michael@0 524 pc_array + array.count,
michael@0 525 array.size - array.count);
michael@0 526
michael@0 527 memset(&savedContext, 0, sizeof(savedContext));
michael@0 528 // See also: struct EnterJITStack in js/src/jit/arm/Trampoline-arm.cpp
michael@0 529 savedContext.arm_r4 = *vSP++;
michael@0 530 savedContext.arm_r5 = *vSP++;
michael@0 531 savedContext.arm_r6 = *vSP++;
michael@0 532 savedContext.arm_r7 = *vSP++;
michael@0 533 savedContext.arm_r8 = *vSP++;
michael@0 534 savedContext.arm_r9 = *vSP++;
michael@0 535 savedContext.arm_r10 = *vSP++;
michael@0 536 savedContext.arm_fp = *vSP++;
michael@0 537 savedContext.arm_lr = *vSP++;
michael@0 538 savedContext.arm_sp = reinterpret_cast<uint32_t>(vSP);
michael@0 539 savedContext.arm_pc = savedContext.arm_lr;
michael@0 540 mcontext = &savedContext;
michael@0 541 }
michael@0 542 }
michael@0 543
michael@0 544 // Now unwind whatever's left (starting from either the last EnterJIT
michael@0 545 // frame or, if no EnterJIT was found, the original registers).
michael@0 546 array.count += EHABIStackWalk(*mcontext,
michael@0 547 aProfile.GetStackTop(),
michael@0 548 sp_array + array.count,
michael@0 549 pc_array + array.count,
michael@0 550 array.size - array.count);
michael@0 551
michael@0 552 mergeNativeBacktrace(aProfile, array);
michael@0 553 }
michael@0 554
michael@0 555 #endif
michael@0 556
michael@0 557 static
michael@0 558 void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
michael@0 559 {
michael@0 560 // Sample
michael@0 561 // 's' tag denotes the start of a sample block
michael@0 562 // followed by 0 or more 'c' tags.
michael@0 563 aProfile.addTag(ProfileEntry('s', "(root)"));
michael@0 564 for (uint32_t i = 0; i < aStack->stackSize(); i++) {
michael@0 565 addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr);
michael@0 566 }
michael@0 567 #ifdef ENABLE_SPS_LEAF_DATA
michael@0 568 if (sample) {
michael@0 569 aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
michael@0 570 #ifdef ENABLE_ARM_LR_SAVING
michael@0 571 aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
michael@0 572 #endif
michael@0 573 }
michael@0 574 #endif
michael@0 575 }
michael@0 576
michael@0 577 void TableTicker::Tick(TickSample* sample)
michael@0 578 {
michael@0 579 if (HasUnwinderThread()) {
michael@0 580 UnwinderTick(sample);
michael@0 581 } else {
michael@0 582 InplaceTick(sample);
michael@0 583 }
michael@0 584 }
michael@0 585
michael@0 586 void TableTicker::InplaceTick(TickSample* sample)
michael@0 587 {
michael@0 588 ThreadProfile& currThreadProfile = *sample->threadProfile;
michael@0 589
michael@0 590 PseudoStack* stack = currThreadProfile.GetPseudoStack();
michael@0 591 bool recordSample = true;
michael@0 592 #if defined(XP_WIN)
michael@0 593 bool powerSample = false;
michael@0 594 #endif
michael@0 595
michael@0 596 /* Don't process the PeudoStack's markers or honour jankOnly if we're
michael@0 597 immediately sampling the current thread. */
michael@0 598 if (!sample->isSamplingCurrentThread) {
michael@0 599 // Marker(s) come before the sample
michael@0 600 ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
michael@0 601 while (pendingMarkersList && pendingMarkersList->peek()) {
michael@0 602 ProfilerMarker* marker = pendingMarkersList->popHead();
michael@0 603 stack->addStoredMarker(marker);
michael@0 604 currThreadProfile.addTag(ProfileEntry('m', marker));
michael@0 605 }
michael@0 606 stack->updateGeneration(currThreadProfile.GetGenerationID());
michael@0 607
michael@0 608 #if defined(XP_WIN)
michael@0 609 if (mProfilePower) {
michael@0 610 mIntelPowerGadget->TakeSample();
michael@0 611 powerSample = true;
michael@0 612 }
michael@0 613 #endif
michael@0 614
michael@0 615 if (mJankOnly) {
michael@0 616 // if we are on a different event we can discard any temporary samples
michael@0 617 // we've kept around
michael@0 618 if (sLastSampledEventGeneration != sCurrentEventGeneration) {
michael@0 619 // XXX: we also probably want to add an entry to the profile to help
michael@0 620 // distinguish which samples are part of the same event. That, or record
michael@0 621 // the event generation in each sample
michael@0 622 currThreadProfile.erase();
michael@0 623 }
michael@0 624 sLastSampledEventGeneration = sCurrentEventGeneration;
michael@0 625
michael@0 626 recordSample = false;
michael@0 627 // only record the events when we have a we haven't seen a tracer event for 100ms
michael@0 628 if (!sLastTracerEvent.IsNull()) {
michael@0 629 TimeDuration delta = sample->timestamp - sLastTracerEvent;
michael@0 630 if (delta.ToMilliseconds() > 100.0) {
michael@0 631 recordSample = true;
michael@0 632 }
michael@0 633 }
michael@0 634 }
michael@0 635 }
michael@0 636
michael@0 637 #if defined(USE_NS_STACKWALK) || defined(USE_EHABI_STACKWALK)
michael@0 638 if (mUseStackWalk) {
michael@0 639 doNativeBacktrace(currThreadProfile, sample);
michael@0 640 } else {
michael@0 641 doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
michael@0 642 }
michael@0 643 #else
michael@0 644 doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr);
michael@0 645 #endif
michael@0 646
michael@0 647 if (recordSample)
michael@0 648 currThreadProfile.flush();
michael@0 649
michael@0 650 if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) {
michael@0 651 TimeDuration delta = sample->timestamp - sLastTracerEvent;
michael@0 652 currThreadProfile.addTag(ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())));
michael@0 653 }
michael@0 654
michael@0 655 if (sample) {
michael@0 656 TimeDuration delta = sample->timestamp - sStartTime;
michael@0 657 currThreadProfile.addTag(ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())));
michael@0 658 }
michael@0 659
michael@0 660 #if defined(XP_WIN)
michael@0 661 if (powerSample) {
michael@0 662 currThreadProfile.addTag(ProfileEntry('p', static_cast<float>(mIntelPowerGadget->GetTotalPackagePowerInWatts())));
michael@0 663 }
michael@0 664 #endif
michael@0 665
michael@0 666 if (sLastFrameNumber != sFrameNumber) {
michael@0 667 currThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
michael@0 668 sLastFrameNumber = sFrameNumber;
michael@0 669 }
michael@0 670 }
michael@0 671
michael@0 672 namespace {
michael@0 673
michael@0 674 SyncProfile* NewSyncProfile()
michael@0 675 {
michael@0 676 PseudoStack* stack = tlsPseudoStack.get();
michael@0 677 if (!stack) {
michael@0 678 MOZ_ASSERT(stack);
michael@0 679 return nullptr;
michael@0 680 }
michael@0 681 Thread::tid_t tid = Thread::GetCurrentId();
michael@0 682
michael@0 683 SyncProfile* profile = new SyncProfile("SyncProfile",
michael@0 684 GET_BACKTRACE_DEFAULT_ENTRY,
michael@0 685 stack, tid, NS_IsMainThread());
michael@0 686 return profile;
michael@0 687 }
michael@0 688
michael@0 689 } // anonymous namespace
michael@0 690
michael@0 691 SyncProfile* TableTicker::GetBacktrace()
michael@0 692 {
michael@0 693 SyncProfile* profile = NewSyncProfile();
michael@0 694
michael@0 695 TickSample sample;
michael@0 696 sample.threadProfile = profile;
michael@0 697
michael@0 698 #if defined(HAVE_NATIVE_UNWIND)
michael@0 699 #if defined(XP_WIN) || defined(LINUX)
michael@0 700 tickcontext_t context;
michael@0 701 sample.PopulateContext(&context);
michael@0 702 #elif defined(XP_MACOSX)
michael@0 703 sample.PopulateContext(nullptr);
michael@0 704 #endif
michael@0 705 #endif
michael@0 706
michael@0 707 sample.isSamplingCurrentThread = true;
michael@0 708 sample.timestamp = mozilla::TimeStamp::Now();
michael@0 709
michael@0 710 if (!HasUnwinderThread()) {
michael@0 711 profile->BeginUnwind();
michael@0 712 }
michael@0 713
michael@0 714 Tick(&sample);
michael@0 715
michael@0 716 if (!HasUnwinderThread()) {
michael@0 717 profile->EndUnwind();
michael@0 718 }
michael@0 719
michael@0 720 return profile;
michael@0 721 }
michael@0 722
michael@0 723 static void print_callback(const ProfileEntry& entry, const char* tagStringData)
michael@0 724 {
michael@0 725 switch (entry.getTagName()) {
michael@0 726 case 's':
michael@0 727 case 'c':
michael@0 728 printf_stderr(" %s\n", tagStringData);
michael@0 729 }
michael@0 730 }
michael@0 731
michael@0 732 void mozilla_sampler_print_location1()
michael@0 733 {
michael@0 734 if (!stack_key_initialized)
michael@0 735 profiler_init(nullptr);
michael@0 736
michael@0 737 SyncProfile* syncProfile = NewSyncProfile();
michael@0 738 if (!syncProfile) {
michael@0 739 return;
michael@0 740 }
michael@0 741
michael@0 742 syncProfile->BeginUnwind();
michael@0 743 doSampleStackTrace(syncProfile->GetPseudoStack(), *syncProfile, nullptr);
michael@0 744 syncProfile->EndUnwind();
michael@0 745
michael@0 746 printf_stderr("Backtrace:\n");
michael@0 747 syncProfile->IterateTags(print_callback);
michael@0 748 delete syncProfile;
michael@0 749 }
michael@0 750
michael@0 751

mercurial