tools/profiler/TableTicker.cpp

changeset 0
6474c204b198
     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 +

mercurial