예제 #1
0
static
void addProfileEntry(ProfileStack *aStack, ThreadProfile &aProfile, int i)
{
  // First entry has tagName 's' (start)
  // Check for magic pointer bit 1 to indicate copy
  const char* sampleLabel = aStack->mStack[i].mLabel;
  if (aStack->mStack[i].isCopyLabel()) {
    // Store the string using 1 or more 'd' (dynamic) tags
    // that will happen to the preceding tag

    aProfile.addTag(ProfileEntry('c', ""));
    // Add one to store the null termination
    size_t strLen = strlen(sampleLabel) + 1;
    for (size_t j = 0; j < strLen;) {
      // Store as many characters in the void* as the platform allows
      char text[sizeof(void*)];
      for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
        text[pos] = sampleLabel[j+pos];
      }
      j += sizeof(void*)/sizeof(char);
      // Cast to *((void**) to pass the text data to a void*
      aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
    }
  } else {
    aProfile.addTag(ProfileEntry('c', sampleLabel));
  }
}
예제 #2
0
파일: TableTicker.cpp 프로젝트: mozilla/pjs
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
#ifndef XP_MACOSX
  uintptr_t thread = GetThreadHandle(platform_data());
  MOZ_ASSERT(thread);
#endif
  void* pc_array[1000];
  PCArray array = {
    pc_array,
    mozilla::ArrayLength(pc_array),
    0
  };

  // Start with the current function.
  StackWalkCallback(aSample->pc, &array);

#ifdef XP_MACOSX
  pthread_t pt = GetProfiledThread(platform_data());
  void *stackEnd = reinterpret_cast<void*>(-1);
  if (pt)
    stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
  nsresult rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
#else
  nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
#endif
  if (NS_SUCCEEDED(rv)) {
    aProfile.addTag(ProfileEntry('s', "(root)"));

    for (size_t i = array.count; i > 0; --i) {
      aProfile.addTag(ProfileEntry('l', (void*)array.array[i - 1]));
    }
  }
}
예제 #3
0
void TableTicker::Tick(TickSample* sample)
{
  // Marker(s) come before the sample
  ProfileStack* stack = mPrimaryThreadProfile.GetStack();
  for (int i = 0; stack->getMarker(i) != NULL; i++) {
    mPrimaryThreadProfile.addTag(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
      mPrimaryThreadProfile.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) || defined(USE_LIBUNWIND)
  if (mUseStackWalk) {
    doBacktrace(mPrimaryThreadProfile, sample);
  } else {
    doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
  }
#else
  doSampleStackTrace(stack, mPrimaryThreadProfile, sample);
#endif

  if (recordSample)
    mPrimaryThreadProfile.flush();

  if (!sLastTracerEvent.IsNull() && sample) {
    TimeDuration delta = sample->timestamp - sLastTracerEvent;
    mPrimaryThreadProfile.addTag(ProfileEntry('r', delta.ToMilliseconds()));
  }

  if (sample) {
    TimeDuration delta = sample->timestamp - mStartTime;
    mPrimaryThreadProfile.addTag(ProfileEntry('t', delta.ToMilliseconds()));
  }

  if (sLastFrameNumber != sFrameNumber) {
    mPrimaryThreadProfile.addTag(ProfileEntry('f', sFrameNumber));
    sLastFrameNumber = sFrameNumber;
  }
}
예제 #4
0
static
void addPseudoEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
                    PseudoStack *stack, void *lastpc)
{
    // Pseudo-frames with the ASMJS flag are just annotations and should not be
    // recorded in the profile.
    if (entry.hasFlag(StackEntry::ASMJS))
        return;

    int lineno = -1;

    // First entry has tagName 's' (start)
    // Check for magic pointer bit 1 to indicate copy
    const char* sampleLabel = entry.label();
    if (entry.isCopyLabel()) {
        // Store the string using 1 or more 'd' (dynamic) tags
        // that will happen to the preceding tag

        addDynamicTag(aProfile, 'c', sampleLabel);
        if (entry.isJs()) {
            if (!entry.pc()) {
                // The JIT only allows the top-most entry to have a nullptr pc
                MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
                // If stack-walking was disabled, then that's just unfortunate
                if (lastpc) {
                    jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                          lastpc);
                    if (jspc) {
                        lineno = JS_PCToLineNumber(entry.script(), jspc);
                    }
                }
            } else {
                lineno = JS_PCToLineNumber(entry.script(), entry.pc());
            }
        } else {
            lineno = entry.line();
        }
    } else {
        aProfile.addTag(ProfileEntry('c', sampleLabel));

        // XXX: Bug 1010578. Don't assume a CPP entry and try to get the
        // line for js entries as well.
        if (entry.isCpp()) {
            lineno = entry.line();
        }
    }

    if (lineno != -1) {
        aProfile.addTag(ProfileEntry('n', lineno));
    }

    uint32_t category = entry.category();
    MOZ_ASSERT(!(category & StackEntry::IS_CPP_ENTRY));
    MOZ_ASSERT(!(category & StackEntry::FRAME_LABEL_COPY));

    if (category) {
        aProfile.addTag(ProfileEntry('y', (int)category));
    }
}
예제 #5
0
void TableTicker::doBacktrace(ThreadProfile &aProfile, Address pc)
{
  void *array[100];
  int count = backtrace (array, 100);

  aProfile.addTag(ProfileEntry('s', "(root)", 0));

  for (int i = 0; i < count; i++) {
    if( (intptr_t)array[i] == -1 ) break;
    aProfile.addTag(ProfileEntry('l', (const char*)array[i]));
  }
}
예제 #6
0
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
  void *array[100];
  int count = backtrace (array, 100);

  aProfile.addTag(ProfileEntry('s', "(root)"));

  for (int i = 0; i < count; i++) {
    if( (intptr_t)array[i] == -1 ) break;
    aProfile.addTag(ProfileEntry('l', (void*)array[i]));
  }
}
예제 #7
0
static
void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
                     ProfileStack *stack, void *lastpc)
{
  int lineno = -1;

  // First entry has tagName 's' (start)
  // Check for magic pointer bit 1 to indicate copy
  const char* sampleLabel = entry.label();
  if (entry.isCopyLabel()) {
    // Store the string using 1 or more 'd' (dynamic) tags
    // that will happen to the preceding tag

    aProfile.addTag(ProfileEntry('c', ""));
    // Add one to store the null termination
    size_t strLen = strlen(sampleLabel) + 1;
    for (size_t j = 0; j < strLen;) {
      // Store as many characters in the void* as the platform allows
      char text[sizeof(void*)];
      for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
        text[pos] = sampleLabel[j+pos];
      }
      j += sizeof(void*)/sizeof(char);
      // Cast to *((void**) to pass the text data to a void*
      aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
    }
    if (entry.js()) {
      if (!entry.pc()) {
        // The JIT only allows the top-most entry to have a NULL pc
        MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
        // If stack-walking was disabled, then that's just unfortunate
        if (lastpc) {
          jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                lastpc);
          if (jspc) {
            lineno = JS_PCToLineNumber(NULL, entry.script(), jspc);
          }
        }
      } else {
        lineno = JS_PCToLineNumber(NULL, entry.script(), entry.pc());
      }
    } else {
      lineno = entry.line();
    }
  } else {
    aProfile.addTag(ProfileEntry('c', sampleLabel));
    lineno = entry.line();
  }
  if (lineno != -1) {
    aProfile.addTag(ProfileEntry('n', lineno));
  }
}
예제 #8
0
static
void doSampleStackTrace(ThreadProfile &aProfile, TickSample *aSample, bool aAddLeafAddresses)
{
  NativeStack nativeStack = { nullptr, nullptr, 0, 0 };
  mergeStacksIntoProfile(aProfile, aSample, nativeStack);

#ifdef ENABLE_SPS_LEAF_DATA
  if (aSample && aAddLeafAddresses) {
    aProfile.addTag(ProfileEntry('l', (void*)aSample->pc));
#ifdef ENABLE_ARM_LR_SAVING
    aProfile.addTag(ProfileEntry('L', (void*)aSample->lr));
#endif
  }
#endif
}
예제 #9
0
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
  void* pc_array[1000];
  size_t count = 0;

  unw_cursor_t cursor; unw_context_t uc;
  unw_word_t ip;
  unw_getcontext(&uc);

  // Dirty hack: replace the registers with values from the signal handler
  // We do this in order to avoid the overhead of walking up to reach the
  // signal handler frame, and the possibility that libunwind fails to
  // handle it correctly.
  unw_tdep_context_t *unw_ctx = reinterpret_cast<unw_tdep_context_t*> (&uc);
  mcontext_t& mcontext = reinterpret_cast<ucontext_t*> (aSample->context)->uc_mcontext;
#define REPLACE_REG(num) unw_ctx->regs[num] = mcontext.gregs[R##num]
  REPLACE_REG(0);
  REPLACE_REG(1);
  REPLACE_REG(2);
  REPLACE_REG(3);
  REPLACE_REG(4);
  REPLACE_REG(5);
  REPLACE_REG(6);
  REPLACE_REG(7);
  REPLACE_REG(8);
  REPLACE_REG(9);
  REPLACE_REG(10);
  REPLACE_REG(11);
  REPLACE_REG(12);
  REPLACE_REG(13);
  REPLACE_REG(14);
  REPLACE_REG(15);
#undef REPLACE_REG
  unw_init_local(&cursor, &uc);
  while (count < ArrayLength(pc_array) &&
         unw_step(&cursor) > 0) {
    unw_get_reg(&cursor, UNW_REG_IP, &ip);
    pc_array[count++] = reinterpret_cast<void*> (ip);
  }

  aProfile.addTag(ProfileEntry('s', "(root)"));
  for (size_t i = count; i > 0; --i) {
    aProfile.addTag(ProfileEntry('l', reinterpret_cast<void*>(pc_array[i - 1])));
  }
}
예제 #10
0
static
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
  // Sample
  // 's' tag denotes the start of a sample block
  // followed by 0 or more 'c' tags.
  for (int i = 0; i < aStack->mStackPointer; i++) {
    if (i == 0) {
      Address pc = 0;
      if (sample) {
        pc = sample->pc;
      }
      aProfile.addTag(ProfileEntry('s', aStack->mStack[i], pc));
    } else {
      aProfile.addTag(ProfileEntry('c', aStack->mStack[i]));
    }
  }
}
예제 #11
0
static
void doSampleStackTrace(PseudoStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
  // Sample
  // 's' tag denotes the start of a sample block
  // followed by 0 or more 'c' tags.
  aProfile.addTag(ProfileEntry('s', "(root)"));
  for (uint32_t i = 0; i < aStack->stackSize(); i++) {
    addProfileEntry(aStack->mStack[i], aProfile, aStack, nullptr);
  }
#ifdef ENABLE_SPS_LEAF_DATA
  if (sample) {
    aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
#ifdef ENABLE_ARM_LR_SAVING
    aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
#endif
  }
#endif
}
예제 #12
0
static
void addDynamicTag(ThreadProfile &aProfile, char aTagName, const char *aStr)
{
  aProfile.addTag(ProfileEntry(aTagName, ""));
  // Add one to store the null termination
  size_t strLen = strlen(aStr) + 1;
  for (size_t j = 0; j < strLen;) {
    // Store as many characters in the void* as the platform allows
    char text[sizeof(void*)];
    size_t len = sizeof(void*)/sizeof(char);
    if (j+len >= strLen) {
      len = strLen - j;
    }
    memcpy(text, &aStr[j], len);
    j += sizeof(void*)/sizeof(char);
    // Cast to *((void**) to pass the text data to a void*
    aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
  }
}
예제 #13
0
static
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
  // Sample
  // 's' tag denotes the start of a sample block
  // followed by 0 or more 'c' tags.
  aProfile.addTag(ProfileEntry('s', "(root)"));
  for (mozilla::sig_safe_t i = 0;
       i < aStack->mStackPointer && i < mozilla::ArrayLength(aStack->mStack);
       i++) {
    addProfileEntry(aStack, aProfile, i);
  }
#ifdef ENABLE_SPS_LEAF_DATA
  if (sample) {
    aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
#ifdef ENABLE_ARM_LR_SAVING
    aProfile.addTag(ProfileEntry('L', (void*)sample->lr));
#endif
  }
#endif
}
예제 #14
0
static
void addProfileEntry(volatile StackEntry &entry, ThreadProfile &aProfile,
                     PseudoStack *stack, void *lastpc)
{
  int lineno = -1;

  // First entry has tagName 's' (start)
  // Check for magic pointer bit 1 to indicate copy
  const char* sampleLabel = entry.label();
  if (entry.isCopyLabel()) {
    // Store the string using 1 or more 'd' (dynamic) tags
    // that will happen to the preceding tag

    addDynamicTag(aProfile, 'c', sampleLabel);
    if (entry.js()) {
      if (!entry.pc()) {
        // The JIT only allows the top-most entry to have a NULL pc
        MOZ_ASSERT(&entry == &stack->mStack[stack->stackSize() - 1]);
        // If stack-walking was disabled, then that's just unfortunate
        if (lastpc) {
          jsbytecode *jspc = js::ProfilingGetPC(stack->mRuntime, entry.script(),
                                                lastpc);
          if (jspc) {
            lineno = JS_PCToLineNumber(NULL, entry.script(), jspc);
          }
        }
      } else {
        lineno = JS_PCToLineNumber(NULL, entry.script(), entry.pc());
      }
    } else {
      lineno = entry.line();
    }
  } else {
    aProfile.addTag(ProfileEntry('c', sampleLabel));
    lineno = entry.line();
  }
  if (lineno != -1) {
    aProfile.addTag(ProfileEntry('n', lineno));
  }
}
예제 #15
0
static void mergeNativeBacktrace(ThreadProfile &aProfile, const PCArray &array) {
  aProfile.addTag(ProfileEntry('s', "(root)"));

  PseudoStack* stack = aProfile.GetPseudoStack();
  uint32_t pseudoStackPos = 0;

  /* We have two stacks, the native C stack we extracted from unwinding,
   * and the pseudostack we managed during execution. We want to consolidate
   * the two in order. We do so by merging using the approximate stack address
   * when each entry was push. When pushing JS entry we may not now the stack
   * address in which case we have a NULL stack address in which case we assume
   * that it follows immediatly the previous element.
   *
   *  C Stack | Address    --  Pseudo Stack | Address
   *  main()  | 0x100          run_js()     | 0x40
   *  start() | 0x80           jsCanvas()   | NULL
   *  timer() | 0x50           drawLine()   | NULL
   *  azure() | 0x10
   *
   * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure()
   */
  // i is the index in C stack starting at main and decreasing
  // pseudoStackPos is the position in the Pseudo stack starting
  // at the first frame (run_js in the example) and increasing.
  for (size_t i = array.count; i > 0; --i) {
    while (pseudoStackPos < stack->stackSize()) {
      volatile StackEntry& entry = stack->mStack[pseudoStackPos];

      if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress())
        break;

      addProfileEntry(entry, aProfile, stack, array.array[0]);
      pseudoStackPos++;
    }

    aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1]));
  }
}
예제 #16
0
파일: TableTicker.cpp 프로젝트: mozilla/pjs
static
void doSampleStackTrace(ProfileStack *aStack, ThreadProfile &aProfile, TickSample *sample)
{
  // Sample
  // 's' tag denotes the start of a sample block
  // followed by 0 or more 'c' tags.
  aProfile.addTag(ProfileEntry('s', "(root)"));
  for (mozilla::sig_safe_t i = 0; i < aStack->mStackPointer; i++) {
    // First entry has tagName 's' (start)
    // Check for magic pointer bit 1 to indicate copy
    const char* sampleLabel = aStack->mStack[i].mLabel;
    if (aStack->mStack[i].isCopyLabel()) {
      // Store the string using 1 or more 'd' (dynamic) tags
      // that will happen to the preceding tag

      aProfile.addTag(ProfileEntry('c', ""));
      // Add one to store the null termination
      size_t strLen = strlen(sampleLabel) + 1;
      for (size_t j = 0; j < strLen;) {
        // Store as many characters in the void* as the platform allows
        char text[sizeof(void*)];
        for (size_t pos = 0; pos < sizeof(void*) && j+pos < strLen; pos++) {
          text[pos] = sampleLabel[j+pos];
        }
        j += sizeof(void*);
        // Take '*((void**)(&text[0]))' to pass the char[] as a single void*
        aProfile.addTag(ProfileEntry('d', *((void**)(&text[0]))));
      }
    } else {
      aProfile.addTag(ProfileEntry('c', sampleLabel));
    }
  }
#ifdef ENABLE_SPS_LEAF_DATA
  if (sample) {
    aProfile.addTag(ProfileEntry('l', (void*)sample->pc));
  }
#endif
}
예제 #17
0
void TableTicker::doNativeBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
#ifdef XP_WIN
  /* For the purpose of SPS it is currently too expensive to call NS_StackWalk()
     on itself on Windows. For each call to NS_StackWalk(), we currently have to
     use another thread to suspend the calling thread, obtain its context, and
     walk its stack. We shouldn't do that every time we need a thread to obtain
     its own backtrace in SPS.*/
  if (aSample->isSamplingCurrentThread) {
    void* ptrs[100];
    USHORT count = RtlCaptureStackBackTrace(0, 100, ptrs, nullptr);
    aProfile.addTag(ProfileEntry('s', "(root)"));
    if (count) {
      USHORT i = count;
      do {
        --i;
        aProfile.addTag(ProfileEntry('l', ptrs[i]));
      } while (i != 0);
    }
    return;
  }
#endif

#ifndef XP_MACOSX
  uintptr_t thread = GetThreadHandle(aSample->threadProfile->GetPlatformData());
  MOZ_ASSERT(thread);
#endif
  void* pc_array[1000];
  void* sp_array[1000];
  PCArray array = {
    pc_array,
    sp_array,
    mozilla::ArrayLength(pc_array),
    0
  };

  // Start with the current function.
  StackWalkCallback(aSample->pc, aSample->sp, &array);

  uint32_t maxFrames = uint32_t(array.size - array.count);
#ifdef XP_MACOSX
  pthread_t pt = GetProfiledThread(aSample->threadProfile->GetPlatformData());
  void *stackEnd = reinterpret_cast<void*>(-1);
  if (pt)
    stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
  nsresult rv = NS_OK;
  if (aSample->fp >= aSample->sp && aSample->fp <= stackEnd)
    rv = FramePointerStackWalk(StackWalkCallback, /* skipFrames */ 0,
                               maxFrames, &array,
                               reinterpret_cast<void**>(aSample->fp), stackEnd);
#else
  void *platformData = nullptr;
#ifdef XP_WIN
  platformData = aSample->context;
#endif // XP_WIN

  nsresult rv = NS_StackWalk(StackWalkCallback, /* skipFrames */ 0, maxFrames,
                             &array, thread, platformData);
#endif
  if (NS_SUCCEEDED(rv))
    mergeNativeBacktrace(aProfile, array);
}
예제 #18
0
static
void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
{
  PseudoStack* pseudoStack = aProfile.GetPseudoStack();
  volatile StackEntry *pseudoFrames = pseudoStack->mStack;
  uint32_t pseudoCount = pseudoStack->stackSize();

  // Make a copy of the JS stack into a JSFrame array. This is necessary since,
  // like the native stack, the JS stack is iterated youngest-to-oldest and we
  // need to iterate oldest-to-youngest when adding entries to aProfile.

  // Synchronous sampling reports an invalid buffer generation to
  // ProfilingFrameIterator to avoid incorrectly resetting the generation of
  // sampled JIT entries inside the JS engine. See note below concerning 'J'
  // entries.
  uint32_t startBufferGen;
  if (aSample->isSamplingCurrentThread) {
    startBufferGen = UINT32_MAX;
  } else {
    startBufferGen = aProfile.bufferGeneration();
  }
  uint32_t jsCount = 0;
#ifndef SPS_STANDALONE
  JS::ProfilingFrameIterator::Frame jsFrames[1000];
  // Only walk jit stack if profiling frame iterator is turned on.
  if (pseudoStack->mRuntime && JS::IsProfilingEnabledForRuntime(pseudoStack->mRuntime)) {
    AutoWalkJSStack autoWalkJSStack;
    const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);

    if (aSample && autoWalkJSStack.walkAllowed) {
      JS::ProfilingFrameIterator::RegisterState registerState;
      registerState.pc = aSample->pc;
      registerState.sp = aSample->sp;
#ifdef ENABLE_ARM_LR_SAVING
      registerState.lr = aSample->lr;
#endif

      JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime,
                                        registerState,
                                        startBufferGen);
      for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
        // See note below regarding 'J' entries.
        if (aSample->isSamplingCurrentThread || jsIter.isAsmJS()) {
          uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
          jsCount += extracted;
          if (jsCount == maxFrames)
            break;
        } else {
          mozilla::Maybe<JS::ProfilingFrameIterator::Frame> frame =
            jsIter.getPhysicalFrameWithoutLabel();
          if (frame.isSome())
            jsFrames[jsCount++] = frame.value();
        }
      }
    }
  }
#endif

  // Start the sample with a root entry.
  aProfile.addTag(ProfileEntry('s', "(root)"));

  // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
  // native arrays are ordered youngest-to-oldest. We must add frames to
  // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
  // and JS and native arrays backwards. Note: this means the terminating
  // condition jsIndex and nativeIndex is being < 0.
  uint32_t pseudoIndex = 0;
  int32_t jsIndex = jsCount - 1;
  int32_t nativeIndex = aNativeStack.count - 1;

  uint8_t *lastPseudoCppStackAddr = nullptr;

  // Iterate as long as there is at least one frame remaining.
  while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
    // There are 1 to 3 frames available. Find and add the oldest.

    uint8_t *pseudoStackAddr = nullptr;
    uint8_t *jsStackAddr = nullptr;
    uint8_t *nativeStackAddr = nullptr;

    if (pseudoIndex != pseudoCount) {
      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];

      if (pseudoFrame.isCpp())
        lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();

#ifndef SPS_STANDALONE
      // Skip any pseudo-stack JS frames which are marked isOSR
      // Pseudostack frames are marked isOSR when the JS interpreter
      // enters a jit frame on a loop edge (via on-stack-replacement,
      // or OSR).  To avoid both the pseudoframe and jit frame being
      // recorded (and showing up twice), the interpreter marks the
      // interpreter pseudostack entry with the OSR flag to ensure that
      // it doesn't get counted.
      if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
          pseudoIndex++;
          continue;
      }
#endif

      MOZ_ASSERT(lastPseudoCppStackAddr);
      pseudoStackAddr = lastPseudoCppStackAddr;
    }

#ifndef SPS_STANDALONE
    if (jsIndex >= 0)
      jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;
#endif

    if (nativeIndex >= 0)
      nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];

    // If there's a native stack entry which has the same SP as a
    // pseudo stack entry, pretend we didn't see the native stack
    // entry.  Ditto for a native stack entry which has the same SP as
    // a JS stack entry.  In effect this means pseudo or JS entries
    // trump conflicting native entries.
    if (nativeStackAddr && (pseudoStackAddr == nativeStackAddr || jsStackAddr == nativeStackAddr)) {
      nativeStackAddr = nullptr;
      nativeIndex--;
      MOZ_ASSERT(pseudoStackAddr || jsStackAddr);
    }

    // Sanity checks.
    MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
                                   pseudoStackAddr != nativeStackAddr);
    MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
                               jsStackAddr != nativeStackAddr);
    MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                   nativeStackAddr != jsStackAddr);

    // Check to see if pseudoStack frame is top-most.
    if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
      MOZ_ASSERT(pseudoIndex < pseudoCount);
      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
      addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
      pseudoIndex++;
      continue;
    }

#ifndef SPS_STANDALONE
    // Check to see if JS jit stack frame is top-most
    if (jsStackAddr > nativeStackAddr) {
      MOZ_ASSERT(jsIndex >= 0);
      const JS::ProfilingFrameIterator::Frame& jsFrame = jsFrames[jsIndex];

      // Stringifying non-asm.js JIT frames is delayed until streaming
      // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
      // store the JIT code address ('J') in the circular buffer.
      //
      // Note that we cannot do this when we are sychronously sampling the
      // current thread; that is, when called from profiler_get_backtrace. The
      // captured backtrace is usually externally stored for an indeterminate
      // amount of time, such as in nsRefreshDriver. Problematically, the
      // stored backtrace may be alive across a GC during which the profiler
      // itself is disabled. In that case, the JS engine is free to discard
      // its JIT code. This means that if we inserted such 'J' entries into
      // the buffer, nsRefreshDriver would now be holding on to a backtrace
      // with stale JIT code return addresses.
      if (aSample->isSamplingCurrentThread ||
          jsFrame.kind == JS::ProfilingFrameIterator::Frame_AsmJS) {
        addDynamicTag(aProfile, 'c', jsFrame.label);
      } else {
        MOZ_ASSERT(jsFrame.kind == JS::ProfilingFrameIterator::Frame_Ion ||
                   jsFrame.kind == JS::ProfilingFrameIterator::Frame_Baseline);
        aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
      }

      jsIndex--;
      continue;
    }
#endif

    // If we reach here, there must be a native stack entry and it must be the
    // greatest entry.
    if (nativeStackAddr) {
      MOZ_ASSERT(nativeIndex >= 0);
      aProfile
        .addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
    }
    if (nativeIndex >= 0) {
      nativeIndex--;
    }
  }

#ifndef SPS_STANDALONE
  // Update the JS runtime with the current profile sample buffer generation.
  //
  // Do not do this for synchronous sampling, which create their own
  // ProfileBuffers.
  if (!aSample->isSamplingCurrentThread && pseudoStack->mRuntime) {
    MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
    uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;
    JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime,
                                               aProfile.bufferGeneration(),
                                               lapCount);
  }
#endif
}
예제 #19
0
static
void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
{
    PseudoStack* pseudoStack = aProfile.GetPseudoStack();
    volatile StackEntry *pseudoFrames = pseudoStack->mStack;
    uint32_t pseudoCount = pseudoStack->stackSize();

    // Make a copy of the JS stack into a JSFrame array. This is necessary since,
    // like the native stack, the JS stack is iterated youngest-to-oldest and we
    // need to iterate oldest-to-youngest when adding entries to aProfile.

    JSFrame jsFrames[1000];
    uint32_t jsCount = 0;
    if (aSample && pseudoStack->mRuntime) {
        JS::ProfilingFrameIterator::RegisterState registerState;
        registerState.pc = aSample->pc;
        registerState.sp = aSample->sp;
#ifdef ENABLE_ARM_LR_SAVING
        registerState.lr = aSample->lr;
#endif

        JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime, registerState);
        for (; jsCount < mozilla::ArrayLength(jsFrames) && !jsIter.done(); ++jsCount, ++jsIter) {
            jsFrames[jsCount].stackAddress = jsIter.stackAddress();
            jsFrames[jsCount].label = jsIter.label();
        }
    }

    // Start the sample with a root entry.
    aProfile.addTag(ProfileEntry('s', "(root)"));

    // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
    // native arrays are ordered youngest-to-oldest. We must add frames to
    // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
    // and JS and native arrays backwards. Note: this means the terminating
    // condition jsIndex and nativeIndex is being < 0.
    uint32_t pseudoIndex = 0;
    int32_t jsIndex = jsCount - 1;
    int32_t nativeIndex = aNativeStack.count - 1;

    // Iterate as long as there is at least one frame remaining.
    while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
        // There are 1 to 3 frames available. Find and add the oldest. Handle pseudo
        // frames first, since there are two special cases that must be considered
        // before everything else.
        if (pseudoIndex != pseudoCount) {
            volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];

            // isJs pseudo-stack frames assume the stackAddress of the preceding isCpp
            // pseudo-stack frame. If we arrive at an isJs pseudo frame, we've already
            // encountered the preceding isCpp stack frame and it was oldest, we can
            // assume the isJs frame is oldest without checking other frames.
            if (pseudoFrame.isJs()) {
                addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
                pseudoIndex++;
                continue;
            }

            // Currently, only asm.js frames use the JS stack and Ion/Baseline/Interp
            // frames use the pseudo stack. In the optimized asm.js->Ion call path, no
            // isCpp frame is pushed, leading to the callstack:
            //   old | pseudo isCpp | asm.js | pseudo isJs | new
            // Since there is no interleaving isCpp pseudo frame between the asm.js
            // and isJs pseudo frame, the above isJs logic will render the callstack:
            //   old | pseudo isCpp | pseudo isJs | asm.js | new
            // which is wrong. To deal with this, a pseudo isCpp frame pushed right
            // before entering asm.js flagged with StackEntry::ASMJS. When we see this
            // flag, we first push all the asm.js frames (up to the next frame with a
            // stackAddress) before pushing the isJs frames. There is no Ion->asm.js
            // fast path, so we don't have to worry about asm.js->Ion->asm.js.
            //
            // (This and the above isJs special cases can be removed once all JS
            // execution modes switch from the pseudo stack to the JS stack.)
            if (pseudoFrame.hasFlag(StackEntry::ASMJS)) {
                void *stopStackAddress = nullptr;
                for (uint32_t i = pseudoIndex + 1; i != pseudoCount; i++) {
                    if (pseudoFrames[i].isCpp()) {
                        stopStackAddress = pseudoFrames[i].stackAddress();
                        break;
                    }
                }

                if (nativeIndex >= 0) {
                    stopStackAddress = std::max(stopStackAddress, aNativeStack.sp_array[nativeIndex]);
                }

                while (jsIndex >= 0 && jsFrames[jsIndex].stackAddress > stopStackAddress) {
                    addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label);
                    jsIndex--;
                }

                pseudoIndex++;
                continue;
            }

            // Finally, consider the normal case of a plain C++ pseudo-frame.
            if ((jsIndex < 0 || pseudoFrame.stackAddress() > jsFrames[jsIndex].stackAddress) &&
                    (nativeIndex < 0 || pseudoFrame.stackAddress() > aNativeStack.sp_array[nativeIndex]))
            {
                // The (C++) pseudo-frame is the oldest.
                addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
                pseudoIndex++;
                continue;
            }
        }

        if (jsIndex >= 0) {
            // Test whether the JS frame is the oldest.
            JSFrame &jsFrame = jsFrames[jsIndex];
            if ((pseudoIndex == pseudoCount || jsFrame.stackAddress > pseudoFrames[pseudoIndex].stackAddress()) &&
                    (nativeIndex < 0 || jsFrame.stackAddress > aNativeStack.sp_array[nativeIndex]))
            {
                // The JS frame is the oldest.
                addDynamicTag(aProfile, 'c', jsFrame.label);
                jsIndex--;
                continue;
            }
        }

        // If execution reaches this point, there must be a native frame and it must
        // be the oldest.
        MOZ_ASSERT(nativeIndex >= 0);
        aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
        nativeIndex--;
    }
}
예제 #20
0
void TableTicker::doBacktrace(ThreadProfile &aProfile, TickSample* aSample)
{
#ifndef XP_MACOSX
  uintptr_t thread = GetThreadHandle(platform_data());
  MOZ_ASSERT(thread);
#endif
  void* pc_array[1000];
  void* sp_array[1000];
  PCArray array = {
    pc_array,
    sp_array,
    mozilla::ArrayLength(pc_array),
    0
  };

  // Start with the current function.
  StackWalkCallback(aSample->pc, aSample->sp, &array);

#ifdef XP_MACOSX
  pthread_t pt = GetProfiledThread(platform_data());
  void *stackEnd = reinterpret_cast<void*>(-1);
  if (pt)
    stackEnd = static_cast<char*>(pthread_get_stackaddr_np(pt));
  nsresult rv = FramePointerStackWalk(StackWalkCallback, 0, &array, reinterpret_cast<void**>(aSample->fp), stackEnd);
#else
  nsresult rv = NS_StackWalk(StackWalkCallback, 0, &array, thread);
#endif
  if (NS_SUCCEEDED(rv)) {
    aProfile.addTag(ProfileEntry('s', "(root)"));

    ProfileStack* stack = aProfile.GetStack();
    int pseudoStackPos = 0;

    /* We have two stacks, the native C stack we extracted from unwinding,
     * and the pseudostack we managed during execution. We want to consolidate
     * the two in order. We do so by merging using the approximate stack address
     * when each entry was push. When pushing JS entry we may not now the stack
     * address in which case we have a NULL stack address in which case we assume
     * that it follows immediatly the previous element.
     *
     *  C Stack | Address    --  Pseudo Stack | Address
     *  main()  | 0x100          run_js()     | 0x40
     *  start() | 0x80           jsCanvas()   | NULL
     *  timer() | 0x50           drawLine()   | NULL
     *  azure() | 0x10
     *
     * Merged: main(), start(), timer(), run_js(), jsCanvas(), drawLine(), azure()
     */
    // i is the index in C stack starting at main and decreasing
    // pseudoStackPos is the position in the Pseudo stack starting
    // at the first frame (run_js in the example) and increasing.
    for (size_t i = array.count; i > 0; --i) {
      while (pseudoStackPos < stack->stackSize()) {
        volatile StackEntry& entry = stack->mStack[pseudoStackPos];

        if (entry.stackAddress() < array.sp_array[i-1] && entry.stackAddress())
          break;

        addProfileEntry(entry, aProfile, stack, array.array[0]);
        pseudoStackPos++;
      }

      aProfile.addTag(ProfileEntry('l', (void*)array.array[i-1]));
    }
  }
}
예제 #21
0
static
void mergeStacksIntoProfile(ThreadProfile& aProfile, TickSample* aSample, NativeStack& aNativeStack)
{
  PseudoStack* pseudoStack = aProfile.GetPseudoStack();
  volatile StackEntry *pseudoFrames = pseudoStack->mStack;
  uint32_t pseudoCount = pseudoStack->stackSize();

  // Make a copy of the JS stack into a JSFrame array. This is necessary since,
  // like the native stack, the JS stack is iterated youngest-to-oldest and we
  // need to iterate oldest-to-youngest when adding entries to aProfile.

  uint32_t startBufferGen = aProfile.bufferGeneration();
  uint32_t jsCount = 0;
  JS::ProfilingFrameIterator::Frame jsFrames[1000];
  // Only walk jit stack if profiling frame iterator is turned on.
  if (pseudoStack->mRuntime && JS::IsProfilingEnabledForRuntime(pseudoStack->mRuntime)) {
    AutoWalkJSStack autoWalkJSStack;
    const uint32_t maxFrames = mozilla::ArrayLength(jsFrames);

    if (aSample && autoWalkJSStack.walkAllowed) {
      JS::ProfilingFrameIterator::RegisterState registerState;
      registerState.pc = aSample->pc;
      registerState.sp = aSample->sp;
#ifdef ENABLE_ARM_LR_SAVING
      registerState.lr = aSample->lr;
#endif

      JS::ProfilingFrameIterator jsIter(pseudoStack->mRuntime,
                                        registerState,
                                        startBufferGen);
      for (; jsCount < maxFrames && !jsIter.done(); ++jsIter) {
        uint32_t extracted = jsIter.extractStack(jsFrames, jsCount, maxFrames);
        MOZ_ASSERT(extracted <= (maxFrames - jsCount));
        jsCount += extracted;
        if (jsCount == maxFrames)
          break;
      }
    }
  }

  // Start the sample with a root entry.
  aProfile.addTag(ProfileEntry('s', "(root)"));

  // While the pseudo-stack array is ordered oldest-to-youngest, the JS and
  // native arrays are ordered youngest-to-oldest. We must add frames to
  // aProfile oldest-to-youngest. Thus, iterate over the pseudo-stack forwards
  // and JS and native arrays backwards. Note: this means the terminating
  // condition jsIndex and nativeIndex is being < 0.
  uint32_t pseudoIndex = 0;
  int32_t jsIndex = jsCount - 1;
  int32_t nativeIndex = aNativeStack.count - 1;

  uint8_t *lastPseudoCppStackAddr = nullptr;

  // Iterate as long as there is at least one frame remaining.
  while (pseudoIndex != pseudoCount || jsIndex >= 0 || nativeIndex >= 0) {
    // There are 1 to 3 frames available. Find and add the oldest.

    uint8_t *pseudoStackAddr = nullptr;
    uint8_t *jsStackAddr = nullptr;
    uint8_t *nativeStackAddr = nullptr;

    if (pseudoIndex != pseudoCount) {
      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];

      if (pseudoFrame.isCpp())
        lastPseudoCppStackAddr = (uint8_t *) pseudoFrame.stackAddress();

      // Skip any pseudo-stack JS frames which are marked isOSR
      // Pseudostack frames are marked isOSR when the JS interpreter
      // enters a jit frame on a loop edge (via on-stack-replacement,
      // or OSR).  To avoid both the pseudoframe and jit frame being
      // recorded (and showing up twice), the interpreter marks the
      // interpreter pseudostack entry with the OSR flag to ensure that
      // it doesn't get counted.
      if (pseudoFrame.isJs() && pseudoFrame.isOSR()) {
          pseudoIndex++;
          continue;
      }

      MOZ_ASSERT(lastPseudoCppStackAddr);
      pseudoStackAddr = lastPseudoCppStackAddr;
    }

    if (jsIndex >= 0)
      jsStackAddr = (uint8_t *) jsFrames[jsIndex].stackAddress;

    if (nativeIndex >= 0)
      nativeStackAddr = (uint8_t *) aNativeStack.sp_array[nativeIndex];

    // Sanity checks.
    MOZ_ASSERT_IF(pseudoStackAddr, pseudoStackAddr != jsStackAddr &&
                                   pseudoStackAddr != nativeStackAddr);
    MOZ_ASSERT_IF(jsStackAddr, jsStackAddr != pseudoStackAddr &&
                               jsStackAddr != nativeStackAddr);
    MOZ_ASSERT_IF(nativeStackAddr, nativeStackAddr != pseudoStackAddr &&
                                   nativeStackAddr != jsStackAddr);

    // Check to see if pseudoStack frame is top-most.
    if (pseudoStackAddr > jsStackAddr && pseudoStackAddr > nativeStackAddr) {
      MOZ_ASSERT(pseudoIndex < pseudoCount);
      volatile StackEntry &pseudoFrame = pseudoFrames[pseudoIndex];
      addPseudoEntry(pseudoFrame, aProfile, pseudoStack, nullptr);
      pseudoIndex++;
      continue;
    }

    // Check to see if JS jit stack frame is top-most
    if (jsStackAddr > nativeStackAddr) {
      MOZ_ASSERT(jsIndex >= 0);
      addDynamicTag(aProfile, 'c', jsFrames[jsIndex].label);

      // Stringifying optimization information is delayed until streaming
      // time. To re-lookup the entry in the JitcodeGlobalTable, we need to
      // store the JIT code address ('J') in the circular buffer.
      if (jsFrames[jsIndex].hasTrackedOptimizations) {
        aProfile.addTag(ProfileEntry('J', jsFrames[jsIndex].returnAddress));
      }

      jsIndex--;
      continue;
    }

    // If we reach here, there must be a native stack entry and it must be the
    // greatest entry.
    MOZ_ASSERT(nativeStackAddr);
    MOZ_ASSERT(nativeIndex >= 0);
    aProfile.addTag(ProfileEntry('l', (void*)aNativeStack.pc_array[nativeIndex]));
    nativeIndex--;
  }

  MOZ_ASSERT(aProfile.bufferGeneration() >= startBufferGen);
  uint32_t lapCount = aProfile.bufferGeneration() - startBufferGen;

  // Update the JS runtime with the current profile sample buffer generation.
  if (pseudoStack->mRuntime) {
    JS::UpdateJSRuntimeProfilerSampleBufferGen(pseudoStack->mRuntime,
                                               aProfile.bufferGeneration(),
                                               lapCount);
  }
}