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 +