tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProfilingFrameIterator.h (10208B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: set ts=8 sts=2 et sw=2 tw=80:
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #ifndef js_ProfilingFrameIterator_h
      8 #define js_ProfilingFrameIterator_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/Attributes.h"
     12 #include "mozilla/Maybe.h"
     13 
     14 #include "jstypes.h"
     15 
     16 #include "js/GCAnnotations.h"
     17 #include "js/ProfilingCategory.h"
     18 #include "js/TypeDecls.h"
     19 
     20 namespace js {
     21 class Activation;
     22 namespace jit {
     23 class JitActivation;
     24 class JSJitProfilingFrameIterator;
     25 class JitcodeGlobalEntry;
     26 
     27 // Information about a single frame in a JIT call stack, returned by
     28 // JitcodeGlobalEntry::callStackAtAddr.
     29 struct CallStackFrameInfo {
     30  // The function name or label for this frame.
     31  const char* label;
     32  // The script source ID for this frame. Used to identify which script source
     33  // this frame belongs to.
     34  uint32_t sourceId;
     35 };
     36 
     37 }  // namespace jit
     38 namespace wasm {
     39 class ProfilingFrameIterator;
     40 }  // namespace wasm
     41 }  // namespace js
     42 
     43 namespace JS {
     44 
     45 // This iterator can be used to walk the stack of a thread suspended at an
     46 // arbitrary pc. To provide accurate results, profiling must have been enabled
     47 // (via EnableRuntimeProfilingStack) before executing the callstack being
     48 // unwound.
     49 //
     50 // Note that the caller must not do anything that could cause GC to happen while
     51 // the iterator is alive, since this could invalidate Ion code and cause its
     52 // contents to become out of date.
     53 class MOZ_NON_PARAM JS_PUBLIC_API ProfilingFrameIterator {
     54 public:
     55  enum class Kind : bool { JSJit, Wasm };
     56 
     57 private:
     58  JSContext* cx_;
     59  mozilla::Maybe<uint64_t> samplePositionInProfilerBuffer_;
     60  js::Activation* activation_;
     61  // For each JitActivation, this records the lowest (most recent) stack
     62  // address. This will usually be either the exitFP of the activation or the
     63  // frame or stack pointer of currently executing JIT/Wasm code. The Gecko
     64  // profiler uses this to skip native frames between the activation and
     65  // endStackAddress_.
     66  void* endStackAddress_ = nullptr;
     67  Kind kind_;
     68 
     69  static const unsigned StorageSpace = 9 * sizeof(void*);
     70  alignas(void*) unsigned char storage_[StorageSpace];
     71 
     72  void* storage() { return storage_; }
     73  const void* storage() const { return storage_; }
     74 
     75  js::wasm::ProfilingFrameIterator& wasmIter() {
     76    MOZ_ASSERT(!done());
     77    MOZ_ASSERT(isWasm());
     78    return *static_cast<js::wasm::ProfilingFrameIterator*>(storage());
     79  }
     80  const js::wasm::ProfilingFrameIterator& wasmIter() const {
     81    MOZ_ASSERT(!done());
     82    MOZ_ASSERT(isWasm());
     83    return *static_cast<const js::wasm::ProfilingFrameIterator*>(storage());
     84  }
     85 
     86  js::jit::JSJitProfilingFrameIterator& jsJitIter() {
     87    MOZ_ASSERT(!done());
     88    MOZ_ASSERT(isJSJit());
     89    return *static_cast<js::jit::JSJitProfilingFrameIterator*>(storage());
     90  }
     91 
     92  const js::jit::JSJitProfilingFrameIterator& jsJitIter() const {
     93    MOZ_ASSERT(!done());
     94    MOZ_ASSERT(isJSJit());
     95    return *static_cast<const js::jit::JSJitProfilingFrameIterator*>(storage());
     96  }
     97 
     98  void maybeSetEndStackAddress(void* addr) {
     99    // If endStackAddress_ has already been set, don't change it because we
    100    // want this to correspond to the most recent frame.
    101    if (!endStackAddress_) {
    102      endStackAddress_ = addr;
    103    }
    104  }
    105 
    106  void settleFrames();
    107  void settle();
    108 
    109 public:
    110  struct RegisterState {
    111    RegisterState()
    112        : pc(nullptr),
    113          sp(nullptr),
    114          fp(nullptr),
    115          unused1(nullptr),
    116          unused2(nullptr) {}
    117    void* pc;
    118    void* sp;
    119    void* fp;
    120    union {
    121      // Value of the LR register on ARM platforms.
    122      void* lr;
    123      // The return address during a tail call operation.
    124      // Note that for ARM is still the value of LR register.
    125      void* tempRA;
    126      // Undefined on non-ARM plaforms outside tail calls operations.
    127      void* unused1;
    128    };
    129    union {
    130      // The FP reference during a tail call operation.
    131      void* tempFP;
    132      // Undefined outside tail calls operations.
    133      void* unused2;
    134    };
    135  };
    136 
    137  ProfilingFrameIterator(
    138      JSContext* cx, const RegisterState& state,
    139      const mozilla::Maybe<uint64_t>& samplePositionInProfilerBuffer =
    140          mozilla::Nothing());
    141  ~ProfilingFrameIterator();
    142  void operator++();
    143  bool done() const { return !activation_; }
    144 
    145  // Assuming the stack grows down (we do), the return value:
    146  //  - always points into the stack
    147  //  - is weakly monotonically increasing (may be equal for successive frames)
    148  //  - will compare greater than newer native and psuedo-stack frame addresses
    149  //    and less than older native and psuedo-stack frame addresses
    150  // The exception is at the point of stack switching between the main stack
    151  // and a suspendable one (see WebAssembly JS Promise Integration proposal).
    152  void* stackAddress() const;
    153 
    154  enum FrameKind {
    155    Frame_BaselineInterpreter,
    156    Frame_Baseline,
    157    Frame_Ion,
    158    Frame_WasmBaseline,
    159    Frame_WasmIon,
    160    Frame_WasmOther,
    161  };
    162 
    163  struct Frame {
    164    FrameKind kind;
    165    void* stackAddress;
    166    union {
    167      void* returnAddress_;
    168      jsbytecode* interpreterPC_;
    169    };
    170    void* activation;
    171    void* endStackAddress;
    172    const char* label;
    173    JSScript* interpreterScript;
    174    uint64_t realmID;
    175    uint32_t sourceId;
    176 
    177   public:
    178    void* returnAddress() const {
    179      MOZ_ASSERT(kind != Frame_BaselineInterpreter);
    180      return returnAddress_;
    181    }
    182    jsbytecode* interpreterPC() const {
    183      MOZ_ASSERT(kind == Frame_BaselineInterpreter);
    184      return interpreterPC_;
    185    }
    186    ProfilingCategoryPair profilingCategory() const {
    187      switch (kind) {
    188        case FrameKind::Frame_BaselineInterpreter:
    189          return JS::ProfilingCategoryPair::JS_BaselineInterpret;
    190        case FrameKind::Frame_Baseline:
    191          return JS::ProfilingCategoryPair::JS_Baseline;
    192        case FrameKind::Frame_Ion:
    193          return JS::ProfilingCategoryPair::JS_IonMonkey;
    194        case FrameKind::Frame_WasmBaseline:
    195          return JS::ProfilingCategoryPair::JS_WasmBaseline;
    196        case FrameKind::Frame_WasmIon:
    197          return JS::ProfilingCategoryPair::JS_WasmIon;
    198        case FrameKind::Frame_WasmOther:
    199          return JS::ProfilingCategoryPair::JS_WasmOther;
    200      }
    201      MOZ_CRASH();
    202    }
    203  } JS_HAZ_GC_INVALIDATED;
    204 
    205  bool isWasm() const;
    206  bool isJSJit() const;
    207 
    208  uint32_t extractStack(Frame* frames, uint32_t offset, uint32_t end) const;
    209 
    210  mozilla::Maybe<Frame> getPhysicalFrameWithoutLabel() const;
    211 
    212  // Return the registers from the native caller frame.
    213  // Nothing{} if this iterator is NOT pointing at a native-to-JIT entry frame,
    214  // or if the information is not accessible/implemented on this platform.
    215  mozilla::Maybe<RegisterState> getCppEntryRegisters() const;
    216 
    217 private:
    218  mozilla::Maybe<Frame> getPhysicalFrameAndEntry(
    219      const js::jit::JitcodeGlobalEntry** entry) const;
    220 
    221  void iteratorConstruct(const RegisterState& state);
    222  void iteratorConstruct();
    223  void iteratorDestroy();
    224  bool iteratorDone();
    225 } JS_HAZ_GC_INVALIDATED;
    226 
    227 JS_PUBLIC_API bool IsProfilingEnabledForContext(JSContext* cx);
    228 
    229 /**
    230 * After each sample run, this method should be called with the current buffer
    231 * position at which the buffer contents start. This will update the
    232 * corresponding field on the JSRuntime.
    233 *
    234 * See the field |profilerSampleBufferRangeStart| on JSRuntime for documentation
    235 * about what this value is used for.
    236 */
    237 JS_PUBLIC_API void SetJSContextProfilerSampleBufferRangeStart(
    238    JSContext* cx, uint64_t rangeStart);
    239 
    240 class ProfiledFrameRange;
    241 
    242 // A handle to the underlying JitcodeGlobalEntry, so as to avoid repeated
    243 // lookups on JitcodeGlobalTable.
    244 class MOZ_STACK_CLASS ProfiledFrameHandle {
    245  friend class ProfiledFrameRange;
    246 
    247  JSRuntime* rt_;
    248  js::jit::JitcodeGlobalEntry& entry_;
    249  void* addr_;
    250  void* canonicalAddr_;
    251  const char* label_;
    252  uint32_t sourceId_;
    253  uint32_t depth_;
    254 
    255  ProfiledFrameHandle(JSRuntime* rt, js::jit::JitcodeGlobalEntry& entry,
    256                      void* addr, const char* label, uint32_t sourceId,
    257                      uint32_t depth);
    258 
    259 public:
    260  const char* label() const { return label_; }
    261  uint32_t depth() const { return depth_; }
    262  void* canonicalAddress() const { return canonicalAddr_; }
    263 
    264  JS_PUBLIC_API ProfilingFrameIterator::FrameKind frameKind() const;
    265 
    266  JS_PUBLIC_API uint64_t realmID() const;
    267 
    268  JS_PUBLIC_API uint32_t sourceId() const;
    269 };
    270 
    271 class ProfiledFrameRange {
    272 public:
    273  class Iter final {
    274   public:
    275    Iter(const ProfiledFrameRange& range, uint32_t index)
    276        : range_(range), index_(index) {}
    277 
    278    JS_PUBLIC_API ProfiledFrameHandle operator*() const;
    279 
    280    // Provide the bare minimum of iterator methods that are needed for
    281    // C++ ranged for loops.
    282    Iter& operator++() {
    283      ++index_;
    284      return *this;
    285    }
    286    bool operator==(const Iter& rhs) const { return index_ == rhs.index_; }
    287    bool operator!=(const Iter& rhs) const { return !(*this == rhs); }
    288 
    289   private:
    290    const ProfiledFrameRange& range_;
    291    uint32_t index_;
    292  };
    293 
    294  Iter begin() const { return Iter(*this, 0); }
    295  Iter end() const { return Iter(*this, depth_); }
    296 
    297 private:
    298  friend JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx,
    299                                                            void* addr);
    300 
    301  ProfiledFrameRange(JSRuntime* rt, void* addr,
    302                     js::jit::JitcodeGlobalEntry* entry)
    303      : rt_(rt), addr_(addr), entry_(entry), depth_(0) {}
    304 
    305  JSRuntime* rt_;
    306  void* addr_;
    307  js::jit::JitcodeGlobalEntry* entry_;
    308  // Maximum inlining depth. This must match InlineScriptTree::MaxDepth.
    309  // We can't use InlineScriptTree::MaxDepth directly here because this is a
    310  // public header and InlineScriptTree.h is private.
    311  static constexpr uint32_t MaxInliningDepth = 8;
    312  js::jit::CallStackFrameInfo frames_[MaxInliningDepth];
    313  uint32_t depth_;
    314 };
    315 
    316 // Returns a range that can be iterated over using C++ ranged for loops.
    317 JS_PUBLIC_API ProfiledFrameRange GetProfiledFrames(JSContext* cx, void* addr);
    318 
    319 }  // namespace JS
    320 
    321 #endif /* js_ProfilingFrameIterator_h */