// RUNS IN SIGHANDLER CONTEXT void TableTicker::UnwinderTick(TickSample* sample) { if (!sample->threadProfile) { // Platform doesn't support multithread, so use the main thread profile we created sample->threadProfile = GetPrimaryThreadProfile(); } ThreadProfile& currThreadProfile = *sample->threadProfile; /* Get hold of an empty inter-thread buffer into which to park the ProfileEntries for this sample. */ UnwinderThreadBuffer* utb = uwt__acquire_empty_buffer(); /* This could fail, if no buffers are currently available, in which case we must give up right away. We cannot wait for a buffer to become available, as that risks deadlock. */ if (!utb) return; /* Manufacture the ProfileEntries that we will give to the unwinder thread, and park them in |utb|. */ // Marker(s) come before the sample PseudoStack* stack = currThreadProfile.GetPseudoStack(); for (int i = 0; stack->getMarker(i) != NULL; i++) { utb__addEntry( utb, ProfileEntry('m', stack->getMarker(i)) ); } stack->mQueueClearMarker = true; bool recordSample = true; if (mJankOnly) { // if we are on a different event we can discard any temporary samples // we've kept around if (sLastSampledEventGeneration != sCurrentEventGeneration) { // XXX: we also probably want to add an entry to the profile to help // distinguish which samples are part of the same event. That, or record // the event generation in each sample currThreadProfile.erase(); } sLastSampledEventGeneration = sCurrentEventGeneration; recordSample = false; // only record the events when we have a we haven't seen a tracer // event for 100ms if (!sLastTracerEvent.IsNull()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; if (delta.ToMilliseconds() > 100.0) { recordSample = true; } } } // JRS 2012-Sept-27: this logic used to involve mUseStackWalk. // That should be reinstated, but for the moment, use the // settings in sUnwindMode and sUnwindInterval. // Add a native-backtrace request, or add pseudo backtrace entries, // or both. switch (sUnwindMode) { case UnwNATIVE: /* Native only */ // add a "do native stack trace now" hint. This will be actioned // by the unwinder thread as it processes the entries in this // sample. utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) ); break; case UnwPSEUDO: /* Pseudo only */ /* Add into |utb|, the pseudo backtrace entries */ genPseudoBacktraceEntries(utb, stack, sample); break; case UnwCOMBINED: /* Both Native and Pseudo */ utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'N'/*native-trace*/) ); genPseudoBacktraceEntries(utb, stack, sample); break; case UnwINVALID: default: MOZ_CRASH(); } if (recordSample) { // add a "flush now" hint utb__addEntry( utb, ProfileEntry('h'/*hint*/, 'F'/*flush*/) ); } // Add any extras if (!sLastTracerEvent.IsNull() && sample) { TimeDuration delta = sample->timestamp - sLastTracerEvent; utb__addEntry( utb, ProfileEntry('r', delta.ToMilliseconds()) ); } if (sample) { TimeDuration delta = sample->timestamp - sStartTime; utb__addEntry( utb, ProfileEntry('t', delta.ToMilliseconds()) ); } if (sLastFrameNumber != sFrameNumber) { utb__addEntry( utb, ProfileEntry('f', sFrameNumber) ); sLastFrameNumber = sFrameNumber; } /* So now we have, in |utb|, the complete set of entries we want to push into the circular buffer. This may also include a 'h' 'F' entry, which is "flush now" hint, and/or a 'h' 'N' entry, which is a "generate a native backtrace and add it to the buffer right now" hint. Hand them off to the helper thread, together with stack and register context needed to do a native unwind, if that is currently enabled. */ /* If a native unwind has been requested, we'll start it off using the context obtained from the signal handler, to avoid the problem of having to unwind through the signal frame itself. */ /* On Linux and Android, the initial register state is in the supplied sample->context. But on MacOS it's not, so we have to fake it up here (sigh). */ if (sUnwindMode == UnwNATIVE || sUnwindMode == UnwCOMBINED) { # if defined(SPS_PLAT_amd64_linux) || defined(SPS_PLAT_arm_android) \ || defined(SPS_PLAT_x86_linux) || defined(SPS_PLAT_x86_android) void* ucV = (void*)sample->context; # elif defined(SPS_PLAT_amd64_darwin) struct __darwin_mcontext64 mc; memset(&mc, 0, sizeof(mc)); ucontext_t uc; memset(&uc, 0, sizeof(uc)); uc.uc_mcontext = &mc; mc.__ss.__rip = (uint64_t)sample->pc; mc.__ss.__rsp = (uint64_t)sample->sp; mc.__ss.__rbp = (uint64_t)sample->fp; void* ucV = (void*)&uc; # elif defined(SPS_PLAT_x86_darwin) struct __darwin_mcontext32 mc; memset(&mc, 0, sizeof(mc)); ucontext_t uc; memset(&uc, 0, sizeof(uc)); uc.uc_mcontext = &mc; mc.__ss.__eip = (uint32_t)sample->pc; mc.__ss.__esp = (uint32_t)sample->sp; mc.__ss.__ebp = (uint32_t)sample->fp; void* ucV = (void*)&uc; # elif defined(SPS_OS_windows) /* Totally fake this up so it at least builds. No idea if we can even ever get here on Windows. */ void* ucV = NULL; # else # error "Unsupported platform" # endif uwt__release_full_buffer(&currThreadProfile, utb, ucV); } else { uwt__release_full_buffer(&currThreadProfile, utb, NULL); } }
void TableTicker::InplaceTick(TickSample* sample) { ThreadProfile& currThreadProfile = *sample->threadProfile; // Marker(s) come before the sample PseudoStack* stack = currThreadProfile.GetPseudoStack(); for (int i = 0; stack->getMarker(i) != NULL; i++) { addDynamicTag(currThreadProfile, 'm', stack->getMarker(i)); } stack->mQueueClearMarker = true; bool recordSample = true; if (mJankOnly) { // if we are on a different event we can discard any temporary samples // we've kept around if (sLastSampledEventGeneration != sCurrentEventGeneration) { // XXX: we also probably want to add an entry to the profile to help // distinguish which samples are part of the same event. That, or record // the event generation in each sample currThreadProfile.erase(); } sLastSampledEventGeneration = sCurrentEventGeneration; recordSample = false; // only record the events when we have a we haven't seen a tracer event for 100ms if (!sLastTracerEvent.IsNull()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; if (delta.ToMilliseconds() > 100.0) { recordSample = true; } } } #if defined(USE_BACKTRACE) || defined(USE_NS_STACKWALK) if (mUseStackWalk) { doNativeBacktrace(currThreadProfile, sample); } else { doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); } #else doSampleStackTrace(stack, currThreadProfile, mAddLeafAddresses ? sample : nullptr); #endif if (recordSample) currThreadProfile.flush(); if (!sLastTracerEvent.IsNull() && sample && currThreadProfile.IsMainThread()) { TimeDuration delta = sample->timestamp - sLastTracerEvent; currThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds())); } if (sample) { TimeDuration delta = sample->timestamp - sStartTime; currThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds())); } if (sLastFrameNumber != sFrameNumber) { currThreadProfile.addTag(ProfileEntry('f', sFrameNumber)); sLastFrameNumber = sFrameNumber; } }