tools/profiler/BreakpadSampler.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 // System
michael@0 7 #include <string>
michael@0 8 #include <stdio.h>
michael@0 9 #include <errno.h>
michael@0 10 #include <ostream>
michael@0 11 #include <fstream>
michael@0 12 #include <sstream>
michael@0 13
michael@0 14 // Profiler
michael@0 15 #include "PlatformMacros.h"
michael@0 16 #include "GeckoProfiler.h"
michael@0 17 #include "platform.h"
michael@0 18 #include "nsXULAppAPI.h"
michael@0 19 #include "nsThreadUtils.h"
michael@0 20 #include "prenv.h"
michael@0 21 #include "shared-libraries.h"
michael@0 22 #include "mozilla/StackWalk.h"
michael@0 23 #include "ProfileEntry.h"
michael@0 24 #include "SyncProfile.h"
michael@0 25 #include "SaveProfileTask.h"
michael@0 26 #include "UnwinderThread2.h"
michael@0 27 #include "TableTicker.h"
michael@0 28
michael@0 29 // Meta
michael@0 30 #include "nsXPCOM.h"
michael@0 31 #include "nsXPCOMCID.h"
michael@0 32 #include "nsIHttpProtocolHandler.h"
michael@0 33 #include "nsServiceManagerUtils.h"
michael@0 34 #include "nsIXULRuntime.h"
michael@0 35 #include "nsIXULAppInfo.h"
michael@0 36 #include "nsDirectoryServiceUtils.h"
michael@0 37 #include "nsDirectoryServiceDefs.h"
michael@0 38 #include "nsIObserverService.h"
michael@0 39 #include "mozilla/Services.h"
michael@0 40
michael@0 41 // JS
michael@0 42 #include "js/OldDebugAPI.h"
michael@0 43
michael@0 44 // This file's exports are listed in GeckoProfilerImpl.h.
michael@0 45
michael@0 46 /* These will be set to something sensible before we take the first
michael@0 47 sample. */
michael@0 48 UnwMode sUnwindMode = UnwINVALID;
michael@0 49 int sUnwindInterval = 0;
michael@0 50 int sUnwindStackScan = 0;
michael@0 51 int sProfileEntries = 0;
michael@0 52
michael@0 53 using std::string;
michael@0 54 using namespace mozilla;
michael@0 55
michael@0 56 #if _MSC_VER
michael@0 57 #define snprintf _snprintf
michael@0 58 #endif
michael@0 59
michael@0 60
michael@0 61 ////////////////////////////////////////////////////////////////////////
michael@0 62 // BEGIN take samples.
michael@0 63 // Everything in this section RUNS IN SIGHANDLER CONTEXT
michael@0 64
michael@0 65 // RUNS IN SIGHANDLER CONTEXT
michael@0 66 static
michael@0 67 void genProfileEntry(/*MODIFIED*/UnwinderThreadBuffer* utb,
michael@0 68 volatile StackEntry &entry,
michael@0 69 PseudoStack *stack, void *lastpc)
michael@0 70 {
michael@0 71 int lineno = -1;
michael@0 72
michael@0 73 // Add a pseudostack-entry start label
michael@0 74 utb__addEntry( utb, ProfileEntry('h', 'P') );
michael@0 75 // And the SP value, if it is non-zero
michael@0 76 if (entry.stackAddress() != 0) {
michael@0 77 utb__addEntry( utb, ProfileEntry('S', entry.stackAddress()) );
michael@0 78 }
michael@0 79
michael@0 80 // First entry has tagName 's' (start)
michael@0 81 // Check for magic pointer bit 1 to indicate copy
michael@0 82 const char* sampleLabel = entry.label();
michael@0 83 if (entry.isCopyLabel()) {
michael@0 84 // Store the string using 1 or more 'd' (dynamic) tags
michael@0 85 // that will happen to the preceding tag
michael@0 86
michael@0 87 utb__addEntry( utb, ProfileEntry('c', "") );
michael@0 88 // Add one to store the null termination
michael@0 89 size_t strLen = strlen(sampleLabel) + 1;
michael@0 90 for (size_t j = 0; j < strLen;) {
michael@0 91 // Store as many characters in the void* as the platform allows
michael@0 92 char text[sizeof(void*)];
michael@0 93 for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
michael@0 94 text[pos] = sampleLabel[j+pos];
michael@0 95 }
michael@0 96 j += sizeof(void*)/sizeof(char);
michael@0 97 // Cast to *((void**) to pass the text data to a void*
michael@0 98 utb__addEntry( utb, ProfileEntry('d', *((void**)(&text[0]))) );
michael@0 99 }
michael@0 100 if (entry.js()) {
michael@0 101 if (!entry.pc()) {
michael@0 102 // The JIT only allows the top-most entry to have a nullptr pc
michael@0 103 MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
michael@0 104 // If stack-walking was disabled, then that's just unfortunate
michael@0 105 if (lastpc) {
michael@0 106 jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
michael@0 107 lastpc);
michael@0 108 if (jspc) {
michael@0 109 lineno = JS_PCToLineNumber(nullptr, entry.script(), jspc);
michael@0 110 }
michael@0 111 }
michael@0 112 } else {
michael@0 113 lineno = JS_PCToLineNumber(nullptr, entry.script(), entry.pc());
michael@0 114 }
michael@0 115 } else {
michael@0 116 lineno = entry.line();
michael@0 117 }
michael@0 118 } else {
michael@0 119 utb__addEntry( utb, ProfileEntry('c', sampleLabel) );
michael@0 120 lineno = entry.line();
michael@0 121 }
michael@0 122 if (lineno != -1) {
michael@0 123 utb__addEntry( utb, ProfileEntry('n', lineno) );
michael@0 124 }
michael@0 125
michael@0 126 // Add a pseudostack-entry end label
michael@0 127 utb__addEntry( utb, ProfileEntry('h', 'Q') );
michael@0 128 }
michael@0 129
michael@0 130 // RUNS IN SIGHANDLER CONTEXT
michael@0 131 // Generate pseudo-backtrace entries and put them in |utb|, with
michael@0 132 // the order outermost frame first.
michael@0 133 void genPseudoBacktraceEntries(/*MODIFIED*/UnwinderThreadBuffer* utb,
michael@0 134 PseudoStack *aStack, TickSample *sample)
michael@0 135 {
michael@0 136 // Call genProfileEntry to generate tags for each profile
michael@0 137 // entry. Each entry will be bounded by a 'h' 'P' tag to
michael@0 138 // mark the start and a 'h' 'Q' tag to mark the end.
michael@0 139 uint32_t nInStack = aStack->stackSize();
michael@0 140 for (uint32_t i = 0; i < nInStack; i++) {
michael@0 141 genProfileEntry(utb, aStack->mStack[i], aStack, nullptr);
michael@0 142 }
michael@0 143 # ifdef ENABLE_SPS_LEAF_DATA
michael@0 144 if (sample) {
michael@0 145 utb__addEntry( utb, ProfileEntry('l', (void*)sample->pc) );
michael@0 146 # ifdef ENABLE_ARM_LR_SAVING
michael@0 147 utb__addEntry( utb, ProfileEntry('L', (void*)sample->lr) );
michael@0 148 # endif
michael@0 149 }
michael@0 150 # endif
michael@0 151 }
michael@0 152
michael@0 153 // RUNS IN SIGHANDLER CONTEXT
michael@0 154 static
michael@0 155 void populateBuffer(UnwinderThreadBuffer* utb, TickSample* sample,
michael@0 156 UTB_RELEASE_FUNC releaseFunction, bool jankOnly)
michael@0 157 {
michael@0 158 ThreadProfile& sampledThreadProfile = *sample->threadProfile;
michael@0 159 PseudoStack* stack = sampledThreadProfile.GetPseudoStack();
michael@0 160
michael@0 161 /* Manufacture the ProfileEntries that we will give to the unwinder
michael@0 162 thread, and park them in |utb|. */
michael@0 163 bool recordSample = true;
michael@0 164
michael@0 165 /* Don't process the PeudoStack's markers or honour jankOnly if we're
michael@0 166 immediately sampling the current thread. */
michael@0 167 if (!sample->isSamplingCurrentThread) {
michael@0 168 // LinkedUWTBuffers before markers
michael@0 169 UWTBufferLinkedList* syncBufs = stack->getLinkedUWTBuffers();
michael@0 170 while (syncBufs && syncBufs->peek()) {
michael@0 171 LinkedUWTBuffer* syncBuf = syncBufs->popHead();
michael@0 172 utb__addEntry(utb, ProfileEntry('B', syncBuf->GetBuffer()));
michael@0 173 }
michael@0 174 // Marker(s) come before the sample
michael@0 175 ProfilerMarkerLinkedList* pendingMarkersList = stack->getPendingMarkers();
michael@0 176 while (pendingMarkersList && pendingMarkersList->peek()) {
michael@0 177 ProfilerMarker* marker = pendingMarkersList->popHead();
michael@0 178 stack->addStoredMarker(marker);
michael@0 179 utb__addEntry( utb, ProfileEntry('m', marker) );
michael@0 180 }
michael@0 181 stack->updateGeneration(sampledThreadProfile.GetGenerationID());
michael@0 182 if (jankOnly) {
michael@0 183 // if we are on a different event we can discard any temporary samples
michael@0 184 // we've kept around
michael@0 185 if (sLastSampledEventGeneration != sCurrentEventGeneration) {
michael@0 186 // XXX: we also probably want to add an entry to the profile to help
michael@0 187 // distinguish which samples are part of the same event. That, or record
michael@0 188 // the event generation in each sample
michael@0 189 sampledThreadProfile.erase();
michael@0 190 }
michael@0 191 sLastSampledEventGeneration = sCurrentEventGeneration;
michael@0 192
michael@0 193 recordSample = false;
michael@0 194 // only record the events when we have a we haven't seen a tracer
michael@0 195 // event for 100ms
michael@0 196 if (!sLastTracerEvent.IsNull()) {
michael@0 197 TimeDuration delta = sample->timestamp - sLastTracerEvent;
michael@0 198 if (delta.ToMilliseconds() > 100.0) {
michael@0 199 recordSample = true;
michael@0 200 }
michael@0 201 }
michael@0 202 }
michael@0 203 }
michael@0 204
michael@0 205 // JRS 2012-Sept-27: this logic used to involve mUseStackWalk.
michael@0 206 // That should be reinstated, but for the moment, use the
michael@0 207 // settings in sUnwindMode and sUnwindInterval.
michael@0 208 // Add a native-backtrace request, or add pseudo backtrace entries,
michael@0 209 // or both.
michael@0 210 switch (sUnwindMode) {
michael@0 211 case UnwNATIVE: /* Native only */
michael@0 212 // add a "do native stack trace now" hint. This will be actioned
michael@0 213 // by the unwinder thread as it processes the entries in this
michael@0 214 // sample.
michael@0 215 utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
michael@0 216 break;
michael@0 217 case UnwPSEUDO: /* Pseudo only */
michael@0 218 /* Add into |utb|, the pseudo backtrace entries */
michael@0 219 genPseudoBacktraceEntries(utb, stack, sample);
michael@0 220 break;
michael@0 221 case UnwCOMBINED: /* Both Native and Pseudo */
michael@0 222 utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) );
michael@0 223 genPseudoBacktraceEntries(utb, stack, sample);
michael@0 224 break;
michael@0 225 case UnwINVALID:
michael@0 226 default:
michael@0 227 MOZ_CRASH();
michael@0 228 }
michael@0 229
michael@0 230 if (recordSample) {
michael@0 231 // add a "flush now" hint
michael@0 232 utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) );
michael@0 233 }
michael@0 234
michael@0 235 // Add any extras
michael@0 236 if (!sLastTracerEvent.IsNull() && sample) {
michael@0 237 TimeDuration delta = sample->timestamp - sLastTracerEvent;
michael@0 238 utb__addEntry( utb, ProfileEntry('r', static_cast<float>(delta.ToMilliseconds())) );
michael@0 239 }
michael@0 240
michael@0 241 if (sample) {
michael@0 242 TimeDuration delta = sample->timestamp - sStartTime;
michael@0 243 utb__addEntry( utb, ProfileEntry('t', static_cast<float>(delta.ToMilliseconds())) );
michael@0 244 }
michael@0 245
michael@0 246 if (sLastFrameNumber != sFrameNumber) {
michael@0 247 utb__addEntry( utb, ProfileEntry('f', sFrameNumber) );
michael@0 248 sLastFrameNumber = sFrameNumber;
michael@0 249 }
michael@0 250
michael@0 251 /* So now we have, in |utb|, the complete set of entries we want to
michael@0 252 push into the circular buffer. This may also include a 'h' 'F'
michael@0 253 entry, which is "flush now" hint, and/or a 'h' 'N' entry, which
michael@0 254 is a "generate a native backtrace and add it to the buffer right
michael@0 255 now" hint. Hand them off to the helper thread, together with
michael@0 256 stack and register context needed to do a native unwind, if that
michael@0 257 is currently enabled. */
michael@0 258
michael@0 259 /* If a native unwind has been requested, we'll start it off using
michael@0 260 the context obtained from the signal handler, to avoid the
michael@0 261 problem of having to unwind through the signal frame itself. */
michael@0 262
michael@0 263 /* On Linux and Android, the initial register state is in the
michael@0 264 supplied sample->context. But on MacOS it's not, so we have to
michael@0 265 fake it up here (sigh). */
michael@0 266 if (sUnwindMode == UnwNATIVE || sUnwindMode == UnwCOMBINED) {
michael@0 267 # if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \
michael@0 268 || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android)
michael@0 269 void* ucV = (void*)sample->context;
michael@0 270 # elif defined(SPS_PLAT_amd64_darwin)
michael@0 271 struct __darwin_mcontext64 mc;
michael@0 272 memset(&mc, 0, sizeof(mc));
michael@0 273 ucontext_t uc;
michael@0 274 memset(&uc, 0, sizeof(uc));
michael@0 275 uc.uc_mcontext = &mc;
michael@0 276 mc.__ss.__rip = (uint64_t)sample->pc;
michael@0 277 mc.__ss.__rsp = (uint64_t)sample->sp;
michael@0 278 mc.__ss.__rbp = (uint64_t)sample->fp;
michael@0 279 void* ucV = (void*)&uc;
michael@0 280 # elif defined(SPS_PLAT_x86_darwin)
michael@0 281 struct __darwin_mcontext32 mc;
michael@0 282 memset(&mc, 0, sizeof(mc));
michael@0 283 ucontext_t uc;
michael@0 284 memset(&uc, 0, sizeof(uc));
michael@0 285 uc.uc_mcontext = &mc;
michael@0 286 mc.__ss.__eip = (uint32_t)sample->pc;
michael@0 287 mc.__ss.__esp = (uint32_t)sample->sp;
michael@0 288 mc.__ss.__ebp = (uint32_t)sample->fp;
michael@0 289 void* ucV = (void*)&uc;
michael@0 290 # elif defined(SPS_OS_windows)
michael@0 291 /* Totally fake this up so it at least builds. No idea if we can
michael@0 292 even ever get here on Windows. */
michael@0 293 void* ucV = nullptr;
michael@0 294 # else
michael@0 295 # error "Unsupported platform"
michael@0 296 # endif
michael@0 297 releaseFunction(&sampledThreadProfile, utb, ucV);
michael@0 298 } else {
michael@0 299 releaseFunction(&sampledThreadProfile, utb, nullptr);
michael@0 300 }
michael@0 301 }
michael@0 302
michael@0 303 static
michael@0 304 void sampleCurrent(TickSample* sample)
michael@0 305 {
michael@0 306 // This variant requires sample->threadProfile to be set
michael@0 307 MOZ_ASSERT(sample->threadProfile);
michael@0 308 LinkedUWTBuffer* syncBuf = utb__acquire_sync_buffer(tlsStackTop.get());
michael@0 309 if (!syncBuf) {
michael@0 310 return;
michael@0 311 }
michael@0 312 SyncProfile* syncProfile = sample->threadProfile->AsSyncProfile();
michael@0 313 MOZ_ASSERT(syncProfile);
michael@0 314 if (!syncProfile->SetUWTBuffer(syncBuf)) {
michael@0 315 utb__release_sync_buffer(syncBuf);
michael@0 316 return;
michael@0 317 }
michael@0 318 UnwinderThreadBuffer* utb = syncBuf->GetBuffer();
michael@0 319 populateBuffer(utb, sample, &utb__finish_sync_buffer, false);
michael@0 320 }
michael@0 321
michael@0 322 // RUNS IN SIGHANDLER CONTEXT
michael@0 323 void TableTicker::UnwinderTick(TickSample* sample)
michael@0 324 {
michael@0 325 if (sample->isSamplingCurrentThread) {
michael@0 326 sampleCurrent(sample);
michael@0 327 return;
michael@0 328 }
michael@0 329
michael@0 330 if (!sample->threadProfile) {
michael@0 331 // Platform doesn't support multithread, so use the main thread profile we created
michael@0 332 sample->threadProfile = GetPrimaryThreadProfile();
michael@0 333 }
michael@0 334
michael@0 335 /* Get hold of an empty inter-thread buffer into which to park
michael@0 336 the ProfileEntries for this sample. */
michael@0 337 UnwinderThreadBuffer* utb = uwt__acquire_empty_buffer();
michael@0 338
michael@0 339 /* This could fail, if no buffers are currently available, in which
michael@0 340 case we must give up right away. We cannot wait for a buffer to
michael@0 341 become available, as that risks deadlock. */
michael@0 342 if (!utb)
michael@0 343 return;
michael@0 344
michael@0 345 populateBuffer(utb, sample, &uwt__release_full_buffer, mJankOnly);
michael@0 346 }
michael@0 347
michael@0 348 // END take samples
michael@0 349 ////////////////////////////////////////////////////////////////////////
michael@0 350

mercurial