tools/profiler/BreakpadSampler.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/profiler/BreakpadSampler.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,350 @@
     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 +// System
    1.10 +#include <string>
    1.11 +#include <stdio.h>
    1.12 +#include <errno.h>
    1.13 +#include <ostream>
    1.14 +#include <fstream>
    1.15 +#include <sstream>
    1.16 +
    1.17 +// Profiler
    1.18 +#include "PlatformMacros.h"
    1.19 +#include "GeckoProfiler.h"
    1.20 +#include "platform.h"
    1.21 +#include "nsXULAppAPI.h"
    1.22 +#include "nsThreadUtils.h"
    1.23 +#include "prenv.h"
    1.24 +#include "shared-libraries.h"
    1.25 +#include "mozilla/StackWalk.h"
    1.26 +#include "ProfileEntry.h"
    1.27 +#include "SyncProfile.h"
    1.28 +#include "SaveProfileTask.h"
    1.29 +#include "UnwinderThread2.h"
    1.30 +#include "TableTicker.h"
    1.31 +
    1.32 +// Meta
    1.33 +#include "nsXPCOM.h"
    1.34 +#include "nsXPCOMCID.h"
    1.35 +#include "nsIHttpProtocolHandler.h"
    1.36 +#include "nsServiceManagerUtils.h"
    1.37 +#include "nsIXULRuntime.h"
    1.38 +#include "nsIXULAppInfo.h"
    1.39 +#include "nsDirectoryServiceUtils.h"
    1.40 +#include "nsDirectoryServiceDefs.h"
    1.41 +#include "nsIObserverService.h"
    1.42 +#include "mozilla/Services.h"
    1.43 +
    1.44 +// JS
    1.45 +#include "js/OldDebugAPI.h"
    1.46 +
    1.47 +// This file's exports are listed in GeckoProfilerImpl.h.
    1.48 +
    1.49 +/* These will be set to something sensible before we take the first
    1.50 +   sample. */
    1.51 +UnwMode sUnwindMode      = UnwINVALID;
    1.52 +int     sUnwindInterval  = 0;
    1.53 +int     sUnwindStackScan = 0;
    1.54 +int     sProfileEntries  = 0;
    1.55 +
    1.56 +using std::string;
    1.57 +using namespace mozilla;
    1.58 +
    1.59 +#if _MSC_VER
    1.60 + #define snprintf _snprintf
    1.61 +#endif
    1.62 +
    1.63 +
    1.64 +////////////////////////////////////////////////////////////////////////
    1.65 +// BEGIN take samples.
    1.66 +// Everything in this section RUNS IN SIGHANDLER CONTEXT
    1.67 +
    1.68 +// RUNS IN SIGHANDLER CONTEXT
    1.69 +static
    1.70 +void genProfileEntry(/*MODIFIED*/UnwinderThreadBuffer* utb,
    1.71 +                     volatile StackEntry &entry,
    1.72 +                     PseudoStack *stack, void *lastpc)
    1.73 +{
    1.74 +  int lineno = -1;
    1.75 +
    1.76 +  // Add a pseudostack-entry start label
    1.77 +  utb__addEntry( utb, ProfileEntry('h', 'P') );
    1.78 +  // And the SP value, if it is non-zero
    1.79 +  if (entry.stackAddress() != 0) {
    1.80 +    utb__addEntry( utb, ProfileEntry('S', entry.stackAddress()) );
    1.81 +  }
    1.82 +
    1.83 +  // First entry has tagName 's' (start)
    1.84 +  // Check for magic pointer bit 1 to indicate copy
    1.85 +  const char* sampleLabel = entry.label();
    1.86 +  if (entry.isCopyLabel()) {
    1.87 +    // Store the string using 1 or more 'd' (dynamic) tags
    1.88 +    // that will happen to the preceding tag
    1.89 +
    1.90 +    utb__addEntry( utb, ProfileEntry('c', "") );
    1.91 +    // Add one to store the null termination
    1.92 +    size_t strLen = strlen(sampleLabel) + 1;
    1.93 +    for (size_t j = 0; j < strLen;) {
    1.94 +      // Store as many characters in the void* as the platform allows
    1.95 +      char text[sizeof(void*)];
    1.96 +      for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
    1.97 +        text[pos] = sampleLabel[j+pos];
    1.98 +      }
    1.99 +      j += sizeof(void*)/sizeof(char);
   1.100 +      // Cast to *((void**) to pass the text data to a void*
   1.101 +      utb__addEntry( utb, ProfileEntry('d', *((void**)(&text[0]))) );
   1.102 +    }
   1.103 +    if (entry.js()) {
   1.104 +      if (!entry.pc()) {
   1.105 +        // The JIT only allows the top-most entry to have a nullptr pc
   1.106 +        MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
   1.107 +        // If stack-walking was disabled, then that's just unfortunate
   1.108 +        if (lastpc) {
   1.109 +          jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
   1.110 +                                                lastpc);
   1.111 +          if (jspc) {
   1.112 +            lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc);
   1.113 +          }
   1.114 +        }
   1.115 +      } else {
   1.116 +        lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
   1.117 +      }
   1.118 +    } else {
   1.119 +      lineno = entry.line();
   1.120 +    }
   1.121 +  } else {
   1.122 +    utb__addEntry( utb, ProfileEntry('c', sampleLabel) );
   1.123 +    lineno = entry.line();
   1.124 +  }
   1.125 +  if (lineno != -1) {
   1.126 +    utb__addEntry( utb, ProfileEntry('n', lineno) );
   1.127 +  }
   1.128 +
   1.129 +  // Add a pseudostack-entry end label
   1.130 +  utb__addEntry( utb, ProfileEntry('h', 'Q') );
   1.131 +}
   1.132 +
   1.133 +// RUNS IN SIGHANDLER CONTEXT
   1.134 +// Generate pseudo-backtrace entries and put them in |utb|, with
   1.135 +// the order outermost frame first.
   1.136 +void genPseudoBacktraceEntries(/*MODIFIED*/UnwinderThreadBuffer* utb,
   1.137 +                               PseudoStack *aStack, TickSample *sample)
   1.138 +{
   1.139 +  // Call genProfileEntry to generate tags for each profile
   1.140 +  // entry.  Each entry will be bounded by a 'h' 'P' tag to
   1.141 +  // mark the start and a 'h' 'Q' tag to mark the end.
   1.142 +  uint32_t nInStack = aStack->stackSize();
   1.143 +  for (uint32_t i = 0; i < nInStack; i++) {
   1.144 +    genProfileEntry(utb, aStack->mStack[i], aStack, nullptr);
   1.145 +  }
   1.146 +# ifdef ENABLE_SPS_LEAF_DATA
   1.147 +  if (sample) {
   1.148 +    utb__addEntry( utb, ProfileEntry('l', (void*)sample->pc) );
   1.149 +#   ifdef ENABLE_ARM_LR_SAVING
   1.150 +    utb__addEntry( utb, ProfileEntry('L', (void*)sample->lr) );
   1.151 +#   endif
   1.152 +  }
   1.153 +# endif
   1.154 +}
   1.155 +
   1.156 +// RUNS IN SIGHANDLER CONTEXT
   1.157 +static
   1.158 +void populateBuffer(UnwinderThreadBuffer* utb, TickSample* sample,
   1.159 +                    UTB_RELEASE_FUNC releaseFunction, bool jankOnly)
   1.160 +{
   1.161 +  ThreadProfile& sampledThreadProfile = *sample->threadProfile;
   1.162 +  PseudoStack* stack = sampledThreadProfile.GetPseudoStack();
   1.163 +
   1.164 +  /* Manufacture the ProfileEntries that we will give to the unwinder
   1.165 +     thread, and park them in |utb|. */
   1.166 +  bool recordSample = true;
   1.167 +
   1.168 +  /* Don't process the PeudoStack's markers or honour jankOnly if we're
   1.169 +     immediately sampling the current thread. */
   1.170 +  if (!sample->isSamplingCurrentThread) {
   1.171 +    // LinkedUWTBuffers before markers
   1.172 +    UWTBufferLinkedList* syncBufs = stack->getLinkedUWTBuffers();
   1.173 +    while (syncBufs && syncBufs->peek()) {
   1.174 +      LinkedUWTBuffer* syncBuf = syncBufs->popHead();
   1.175 +      utb__addEntry(utb, ProfileEntry('B', syncBuf->GetBuffer()));
   1.176 +    }
   1.177 +    // Marker(s) come before the sample
   1.178 +    ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
   1.179 +    while (pendingMarkersList && pendingMarkersList->peek()) {
   1.180 +      ProfilerMarker* marker = pendingMarkersList->popHead();
   1.181 +      stack->addStoredMarker(marker);
   1.182 +      utb__addEntry( utb, ProfileEntry('m', marker) );
   1.183 +    }
   1.184 +    stack->updateGeneration(sampledThreadProfile.GetGenerationID());
   1.185 +    if (jankOnly) {
   1.186 +      // if we are on a different event we can discard any temporary samples
   1.187 +      // we've kept around
   1.188 +      if (sLastSampledEventGeneration != sCurrentEventGeneration) {
   1.189 +        // XXX: we also probably want to add an entry to the profile to help
   1.190 +        // distinguish which samples are part of the same event. That, or record
   1.191 +        // the event generation in each sample
   1.192 +        sampledThreadProfile.erase();
   1.193 +      }
   1.194 +      sLastSampledEventGeneration = sCurrentEventGeneration;
   1.195 +
   1.196 +      recordSample = false;
   1.197 +      // only record the events when we have a we haven't seen a tracer
   1.198 +      // event for 100ms
   1.199 +      if (!sLastTracerEvent.IsNull()) {
   1.200 +        TimeDuration delta = sample->timestamp - sLastTracerEvent;
   1.201 +        if (delta.ToMilliseconds() > 100.0) {
   1.202 +            recordSample = true;
   1.203 +        }
   1.204 +      }
   1.205 +    }
   1.206 +  }
   1.207 +
   1.208 +  // JRS 2012-Sept-27: this logic used to involve mUseStackWalk.
   1.209 +  // That should be reinstated, but for the moment, use the
   1.210 +  // settings in sUnwindMode and sUnwindInterval.
   1.211 +  // Add a native-backtrace request, or add pseudo backtrace entries,
   1.212 +  // or both.
   1.213 +  switch (sUnwindMode) {
   1.214 +    case UnwNATIVE: /* Native only */
   1.215 +      // add a "do native stack trace now" hint.  This will be actioned
   1.216 +      // by the unwinder thread as it processes the entries in this
   1.217 +      // sample.
   1.218 +      utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
   1.219 +      break;
   1.220 +    case UnwPSEUDO: /* Pseudo only */
   1.221 +      /* Add into |utb|, the pseudo backtrace entries */
   1.222 +      genPseudoBacktraceEntries(utb, stack, sample);
   1.223 +      break;
   1.224 +    case UnwCOMBINED: /* Both Native and Pseudo */
   1.225 +      utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
   1.226 +      genPseudoBacktraceEntries(utb, stack, sample);
   1.227 +      break;
   1.228 +    case UnwINVALID:
   1.229 +    default:
   1.230 +      MOZ_CRASH();
   1.231 +  }
   1.232 +
   1.233 +  if (recordSample) {    
   1.234 +    // add a "flush now" hint
   1.235 +    utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) );
   1.236 +  }
   1.237 +
   1.238 +  // Add any extras
   1.239 +  if (!sLastTracerEvent.IsNull() && sample) {
   1.240 +    TimeDuration delta = sample->timestamp - sLastTracerEvent;
   1.241 +    utb__addEntry( utb, ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())) );
   1.242 +  }
   1.243 +
   1.244 +  if (sample) {
   1.245 +    TimeDuration delta = sample->timestamp - sStartTime;
   1.246 +    utb__addEntry( utb, ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())) );
   1.247 +  }
   1.248 +
   1.249 +  if (sLastFrameNumber != sFrameNumber) {
   1.250 +    utb__addEntry( utb, ProfileEntry('f', sFrameNumber) );
   1.251 +    sLastFrameNumber = sFrameNumber;
   1.252 +  }
   1.253 +
   1.254 +  /* So now we have, in |utb|, the complete set of entries we want to
   1.255 +     push into the circular buffer.  This may also include a 'h' 'F'
   1.256 +     entry, which is "flush now" hint, and/or a 'h' 'N' entry, which
   1.257 +     is a "generate a native backtrace and add it to the buffer right
   1.258 +     now" hint.  Hand them off to the helper thread, together with
   1.259 +     stack and register context needed to do a native unwind, if that
   1.260 +     is currently enabled. */
   1.261 +
   1.262 +  /* If a native unwind has been requested, we'll start it off using
   1.263 +     the context obtained from the signal handler, to avoid the
   1.264 +     problem of having to unwind through the signal frame itself. */
   1.265 +
   1.266 +  /* On Linux and Android, the initial register state is in the
   1.267 +     supplied sample->context.  But on MacOS it's not, so we have to
   1.268 +     fake it up here (sigh). */
   1.269 +  if (sUnwindMode == UnwNATIVE || sUnwindMode == UnwCOMBINED) {
   1.270 +#   if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
   1.271 +       || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
   1.272 +    void* ucV = (void*)sample->context;
   1.273 +#   elif defined(SPS_PLAT_amd64_darwin)
   1.274 +    struct __darwin_mcontext64 mc;
   1.275 +    memset(&mc, 0, sizeof(mc));
   1.276 +    ucontext_t uc;
   1.277 +    memset(&uc, 0, sizeof(uc));
   1.278 +    uc.uc_mcontext = &mc;
   1.279 +    mc.__ss.__rip = (uint64_t)sample->pc;
   1.280 +    mc.__ss.__rsp = (uint64_t)sample->sp;
   1.281 +    mc.__ss.__rbp = (uint64_t)sample->fp;
   1.282 +    void* ucV = (void*)&uc;
   1.283 +#   elif defined(SPS_PLAT_x86_darwin)
   1.284 +    struct __darwin_mcontext32 mc;
   1.285 +    memset(&mc, 0, sizeof(mc));
   1.286 +    ucontext_t uc;
   1.287 +    memset(&uc, 0, sizeof(uc));
   1.288 +    uc.uc_mcontext = &mc;
   1.289 +    mc.__ss.__eip = (uint32_t)sample->pc;
   1.290 +    mc.__ss.__esp = (uint32_t)sample->sp;
   1.291 +    mc.__ss.__ebp = (uint32_t)sample->fp;
   1.292 +    void* ucV = (void*)&uc;
   1.293 +#   elif defined(SPS_OS_windows)
   1.294 +    /* Totally fake this up so it at least builds.  No idea if we can
   1.295 +       even ever get here on Windows. */
   1.296 +    void* ucV = nullptr;
   1.297 +#   else
   1.298 +#     error "Unsupported platform"
   1.299 +#   endif
   1.300 +    releaseFunction(&sampledThreadProfile, utb, ucV);
   1.301 +  } else {
   1.302 +    releaseFunction(&sampledThreadProfile, utb, nullptr);
   1.303 +  }
   1.304 +}
   1.305 +
   1.306 +static
   1.307 +void sampleCurrent(TickSample* sample)
   1.308 +{
   1.309 +  // This variant requires sample->threadProfile to be set
   1.310 +  MOZ_ASSERT(sample->threadProfile);
   1.311 +  LinkedUWTBuffer* syncBuf = utb__acquire_sync_buffer(tlsStackTop.get());
   1.312 +  if (!syncBuf) {
   1.313 +    return;
   1.314 +  }
   1.315 +  SyncProfile* syncProfile = sample->threadProfile->AsSyncProfile();
   1.316 +  MOZ_ASSERT(syncProfile);
   1.317 +  if (!syncProfile->SetUWTBuffer(syncBuf)) {
   1.318 +    utb__release_sync_buffer(syncBuf);
   1.319 +    return;
   1.320 +  }
   1.321 +  UnwinderThreadBuffer* utb = syncBuf->GetBuffer();
   1.322 +  populateBuffer(utb, sample, &utb__finish_sync_buffer, false);
   1.323 +}
   1.324 +
   1.325 +// RUNS IN SIGHANDLER CONTEXT
   1.326 +void TableTicker::UnwinderTick(TickSample* sample)
   1.327 +{
   1.328 +  if (sample->isSamplingCurrentThread) {
   1.329 +    sampleCurrent(sample);
   1.330 +    return;
   1.331 +  }
   1.332 +
   1.333 +  if (!sample->threadProfile) {
   1.334 +    // Platform doesn't support multithread, so use the main thread profile we created
   1.335 +    sample->threadProfile = GetPrimaryThreadProfile();
   1.336 +  }
   1.337 +
   1.338 +  /* Get hold of an empty inter-thread buffer into which to park
   1.339 +     the ProfileEntries for this sample. */
   1.340 +  UnwinderThreadBuffer* utb = uwt__acquire_empty_buffer();
   1.341 +
   1.342 +  /* This could fail, if no buffers are currently available, in which
   1.343 +     case we must give up right away.  We cannot wait for a buffer to
   1.344 +     become available, as that risks deadlock. */
   1.345 +  if (!utb)
   1.346 +    return;
   1.347 +
   1.348 +  populateBuffer(utb, sample, &uwt__release_full_buffer, mJankOnly);
   1.349 +}
   1.350 +
   1.351 +// END take samples
   1.352 +////////////////////////////////////////////////////////////////////////
   1.353 +

mercurial