tor-browser

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

Activation.h (22474B)


      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 vm_Activation_h
      8 #define vm_Activation_h
      9 
     10 #include "mozilla/Assertions.h"  // MOZ_ASSERT
     11 #include "mozilla/Attributes.h"  // MOZ_RAII
     12 
     13 #include <stddef.h>  // size_t
     14 #include <stdint.h>  // uint8_t, uint32_t
     15 
     16 #include "jstypes.h"  // JS_PUBLIC_API
     17 
     18 #include "jit/CalleeToken.h"  // js::jit::CalleeToken
     19 #include "js/RootingAPI.h"    // JS::Handle, JS::Rooted
     20 #include "js/TypeDecls.h"     // jsbytecode
     21 #include "js/Value.h"         // JS::Value
     22 #include "vm/SavedFrame.h"    // js::SavedFrame
     23 #include "vm/Stack.h"         // js::InterpreterRegs
     24 
     25 struct JS_PUBLIC_API JSContext;
     26 
     27 class JSFunction;
     28 class JSObject;
     29 class JSScript;
     30 
     31 namespace JS {
     32 
     33 class CallArgs;
     34 class JS_PUBLIC_API Compartment;
     35 
     36 }  // namespace JS
     37 
     38 namespace js {
     39 
     40 class InterpreterActivation;
     41 
     42 namespace jit {
     43 class JitActivation;
     44 }  // namespace jit
     45 
     46 // [SMDOC] LiveSavedFrameCache: SavedFrame caching to minimize stack walking
     47 //
     48 // Since each SavedFrame object includes a 'parent' pointer to the SavedFrame
     49 // for its caller, if we could easily find the right SavedFrame for a given
     50 // stack frame, we wouldn't need to walk the rest of the stack. Traversing deep
     51 // stacks can be expensive, and when we're profiling or instrumenting code, we
     52 // may want to capture JavaScript stacks frequently, so such cases would benefit
     53 // if we could avoid walking the entire stack.
     54 //
     55 // We could have a cache mapping frame addresses to their SavedFrame objects,
     56 // but invalidating its entries would be a challenge. Popping a stack frame is
     57 // extremely performance-sensitive, and SpiderMonkey stack frames can be OSR'd,
     58 // thrown, rematerialized, and perhaps meet other fates; we would rather our
     59 // cache not depend on handling so many tricky cases.
     60 //
     61 // It turns out that we can keep the cache accurate by reserving a single bit in
     62 // the stack frame, which must be clear on any newly pushed frame. When we
     63 // insert an entry into the cache mapping a given frame address to its
     64 // SavedFrame, we set the bit in the frame. Then, we take care to probe the
     65 // cache only for frames whose bit is set; the bit tells us that the frame has
     66 // never left the stack, so its cache entry must be accurate, at least about
     67 // which function the frame is executing (the line may have changed; more about
     68 // that below). The code refers to this bit as the 'hasCachedSavedFrame' flag.
     69 //
     70 // We could manage such a cache replacing least-recently used entries, but we
     71 // can do better than that: the cache can be a stack, of which we need examine
     72 // only entries from the top.
     73 //
     74 // First, observe that stacks are walked from the youngest frame to the oldest,
     75 // but SavedFrame chains are built from oldest to youngest, to ensure common
     76 // tails are shared. This means that capturing a stack is necessarily a
     77 // two-phase process: walk the stack, and then build the SavedFrames.
     78 //
     79 // Naturally, the first time we capture the stack, the cache is empty, and we
     80 // must traverse the entire stack. As we build each SavedFrame, we push an entry
     81 // associating the frame's address to its SavedFrame on the cache, and set the
     82 // frame's bit. At the end, every frame has its bit set and an entry in the
     83 // cache.
     84 //
     85 // Then the program runs some more. Some, none, or all of the frames are popped.
     86 // Any new frames are pushed with their bit clear. Any frame with its bit set
     87 // has never left the stack. The cache is left untouched.
     88 //
     89 // For the next capture, we walk the stack up to the first frame with its bit
     90 // set, if there is one. Call it F; it must have a cache entry. We pop entries
     91 // from the cache - all invalid, because they are above F's entry, and hence
     92 // younger - until we find the entry matching F's address. Since F's bit is set,
     93 // we know it never left the stack, and hence that no younger frame could have
     94 // had a colliding address. And since the frame's bit was set when we pushed the
     95 // cache entry, we know the entry is still valid.
     96 //
     97 // F's cache entry's SavedFrame covers the rest of the stack, so we don't need
     98 // to walk the stack any further. Now we begin building SavedFrame objects for
     99 // the new frames, pushing cache entries, and setting bits on the frames. By the
    100 // end, the cache again covers the full stack, and every frame's bit is set.
    101 //
    102 // If we walk the stack to the end, and find no frame with its bit set, then the
    103 // entire cache is invalid. At this point, it must be emptied, so that the new
    104 // entries we are about to push are the only frames in the cache.
    105 //
    106 // For example, suppose we have the following stack (let 'A > B' mean "A called
    107 // B", so the frames are listed oldest first):
    108 //
    109 //     P  > Q  > R  > S          Initial stack, bits not set.
    110 //     P* > Q* > R* > S*         Capture a SavedFrame stack, set bits.
    111 //                               The cache now holds: P > Q > R > S.
    112 //     P* > Q* > R*              Return from S.
    113 //     P* > Q*                   Return from R.
    114 //     P* > Q* > T  > U          Call T and U. New frames have clear bits.
    115 //
    116 // If we capture the stack now, the cache still holds:
    117 //
    118 //     P  > Q  > R  > S
    119 //
    120 // As we traverse the stack, we'll cross U and T, and then find Q with its bit
    121 // set. We pop entries from the cache until we find the entry for Q; this
    122 // removes entries R and S, which were indeed invalid. In Q's cache entry, we
    123 // find the SavedFrame representing the stack P > Q. Now we build SavedFrames
    124 // for the new portion of the stack, pushing an entry for T and setting the bit
    125 // on the frame, and then doing the same for U. In the end, the call stack again
    126 // has bits set on all its frames:
    127 //
    128 //     P* > Q* > T* > U*         All frames are now in the cache.
    129 //
    130 // And the cache again holds entries for the entire stack:
    131 //
    132 //     P  > Q  > T  > U
    133 //
    134 // Details:
    135 //
    136 // - When we find a cache entry whose frame address matches our frame F, we know
    137 //   that F has never left the stack, but it may certainly be the case that
    138 //   execution took place in that frame, and that the current source position
    139 //   within F's function has changed. This means that the entry's SavedFrame,
    140 //   which records the source line and column as well as the function, is not
    141 //   correct. To detect this case, when we push a cache entry, we record the
    142 //   frame's pc. When consulting the cache, if a frame's address matches but its
    143 //   pc does not, then we pop the cache entry, clear the frame's bit, and
    144 //   continue walking the stack. The next stack frame will definitely hit: since
    145 //   its callee frame never left the stack, the calling frame never got the
    146 //   chance to execute.
    147 //
    148 // - Generators, at least conceptually, have long-lived stack frames that
    149 //   disappear from the stack when the generator yields, and reappear on the
    150 //   stack when the generator's 'next' method is called. When a generator's
    151 //   frame is placed again atop the stack, its bit must be cleared - for the
    152 //   purposes of the cache, treating the frame as a new frame - to respect the
    153 //   invariants we used to justify the algorithm above. Async function
    154 //   activations usually appear atop empty stacks, since they are invoked as a
    155 //   promise callback, but the same rule applies.
    156 //
    157 // - SpiderMonkey has many types of stack frames, and not all have a place to
    158 //   store a bit indicating a cached SavedFrame. But as long as we don't create
    159 //   cache entries for frames we can't mark, simply omitting them from the cache
    160 //   is harmless. Uncacheable frame types include inlined Ion frames and
    161 //   non-Debug wasm frames. The LiveSavedFrameCache::FramePtr type represents
    162 //   only pointers to frames that can be cached, so if you have a FramePtr, you
    163 //   don't need to further check the frame for cachability. FramePtr provides
    164 //   access to the hasCachedSavedFrame bit.
    165 //
    166 // - We actually break up the cache into one cache per Activation. Popping an
    167 //   activation invalidates all its cache entries, simply by freeing the cache
    168 //   altogether.
    169 //
    170 // - The entire chain of SavedFrames for a given stack capture is created in the
    171 //   compartment of the code that requested the capture, *not* in that of the
    172 //   frames it represents, so in general, different compartments may have
    173 //   different SavedFrame objects representing the same actual stack frame. The
    174 //   LiveSavedFrameCache simply records whichever SavedFrames were used in the
    175 //   most recent captures. When we find a cache hit, we check the entry's
    176 //   SavedFrame's compartment against the current compartment; if they do not
    177 //   match, we clear the entire cache.
    178 //
    179 //   This means that it is not always true that, if a frame's
    180 //   hasCachedSavedFrame bit is set, it must have an entry in the cache. The
    181 //   actual invariant is: either the cache is completely empty, or the frames'
    182 //   bits are trustworthy. This invariant holds even though capture can be
    183 //   interrupted at many places by OOM failures. Clearing the cache is a single,
    184 //   uninterruptible step. When we try to look up a frame whose bit is set and
    185 //   find an empty cache, we clear the frame's bit. And we only add the first
    186 //   frame to an empty cache once we've walked the stack all the way, so we know
    187 //   that all frames' bits are cleared by that point.
    188 //
    189 // - When the Debugger API evaluates an expression in some frame (the 'target
    190 //   frame'), it's SpiderMonkey's convention that the target frame be treated as
    191 //   the parent of the eval frame. In reality, of course, the eval frame is
    192 //   pushed on the top of the stack like any other frame, but stack captures
    193 //   simply jump straight over the intervening frames, so that the '.parent'
    194 //   property of a SavedFrame for the eval is the SavedFrame for the target.
    195 //   This is arranged by giving the eval frame an 'evalInFramePrev` link
    196 //   pointing to the target, which an ordinary FrameIter will notice and
    197 //   respect.
    198 //
    199 //   If the LiveSavedFrameCache were presented with stack traversals that
    200 //   skipped frames in this way, it would cause havoc. First, with no debugger
    201 //   eval frames present, capture the stack, populating the cache. Then push a
    202 //   debugger eval frame and capture again; the skipped frames to appear to be
    203 //   absent from the stack. Now pop the debugger eval frame, and capture a third
    204 //   time: the no-longer-skipped frames seem to reappear on the stack, with
    205 //   their cached bits still set.
    206 //
    207 //   The LiveSavedFrameCache assumes that the stack it sees is used in a
    208 //   stack-like fashion: if a frame has its bit set, it has never left the
    209 //   stack. To support this assumption, when the cache is in use, we do not skip
    210 //   the frames between a debugger eval frame an its target; we always traverse
    211 //   the entire stack, invalidating and populating the cache in the usual way.
    212 //   Instead, when we construct a SavedFrame for a debugger eval frame, we
    213 //   select the appropriate parent at that point: rather than the next-older
    214 //   frame, we find the SavedFrame for the eval's target frame. The skip appears
    215 //   in the SavedFrame chains, even as the traversal covers all the frames.
    216 //
    217 // - Rematerialized frames (see ../jit/RematerializedFrame.h) are always created
    218 //   with their hasCachedSavedFrame bits clear: although there may be extant
    219 //   SavedFrames built from the original IonMonkey frame, the Rematerialized
    220 //   frames will not have cache entries for them until they are traversed in a
    221 //   capture themselves.
    222 //
    223 //   This means that, oddly, it is not always true that, once we reach a frame
    224 //   with its hasCachedSavedFrame bit set, all its parents will have the bit set
    225 //   as well. However, clear bits under younger set bits will only occur on
    226 //   Rematerialized frames.
    227 class LiveSavedFrameCache {
    228 public:
    229  // The address of a live frame for which we can cache SavedFrames: it has a
    230  // 'hasCachedSavedFrame' bit we can examine and set, and can be converted to
    231  // a Key to index the cache.
    232  class FramePtr {
    233    // We use jit::CommonFrameLayout for both Baseline frames and Ion
    234    // physical frames.
    235    using Ptr = mozilla::Variant<InterpreterFrame*, jit::CommonFrameLayout*,
    236                                 jit::RematerializedFrame*, wasm::DebugFrame*>;
    237 
    238    Ptr ptr;
    239 
    240    template <typename Frame>
    241    explicit FramePtr(Frame ptr) : ptr(ptr) {}
    242 
    243    struct HasCachedMatcher;
    244    struct SetHasCachedMatcher;
    245    struct ClearHasCachedMatcher;
    246 
    247   public:
    248    // If iter's frame is of a type that can be cached, construct a FramePtr
    249    // for its frame. Otherwise, return Nothing.
    250    static inline mozilla::Maybe<FramePtr> create(const FrameIter& iter);
    251 
    252    inline bool hasCachedSavedFrame() const;
    253    inline void setHasCachedSavedFrame();
    254    inline void clearHasCachedSavedFrame();
    255 
    256    // Return true if this FramePtr refers to an interpreter frame.
    257    inline bool isInterpreterFrame() const {
    258      return ptr.is<InterpreterFrame*>();
    259    }
    260 
    261    // If this FramePtr is an interpreter frame, return a pointer to it.
    262    inline InterpreterFrame& asInterpreterFrame() const {
    263      return *ptr.as<InterpreterFrame*>();
    264    }
    265 
    266    // Return true if this FramePtr refers to a rematerialized frame.
    267    inline bool isRematerializedFrame() const {
    268      return ptr.is<jit::RematerializedFrame*>();
    269    }
    270 
    271    bool operator==(const FramePtr& rhs) const { return rhs.ptr == this->ptr; }
    272    bool operator!=(const FramePtr& rhs) const { return !(rhs == *this); }
    273  };
    274 
    275 private:
    276  // A key in the cache: the address of a frame, live or dead, for which we
    277  // can cache SavedFrames. Since the pointer may not be live, the only
    278  // operation this type permits is comparison.
    279  class Key {
    280    FramePtr framePtr;
    281 
    282   public:
    283    MOZ_IMPLICIT Key(const FramePtr& framePtr) : framePtr(framePtr) {}
    284 
    285    bool operator==(const Key& rhs) const {
    286      return rhs.framePtr == this->framePtr;
    287    }
    288    bool operator!=(const Key& rhs) const { return !(rhs == *this); }
    289  };
    290 
    291  struct Entry {
    292    const Key key;
    293    const jsbytecode* pc;
    294    HeapPtr<SavedFrame*> savedFrame;
    295 
    296    Entry(const Key& key, const jsbytecode* pc, SavedFrame* savedFrame)
    297        : key(key), pc(pc), savedFrame(savedFrame) {}
    298  };
    299 
    300  using EntryVector = Vector<Entry, 0, SystemAllocPolicy>;
    301  EntryVector* frames;
    302 
    303  LiveSavedFrameCache(const LiveSavedFrameCache&) = delete;
    304  LiveSavedFrameCache& operator=(const LiveSavedFrameCache&) = delete;
    305 
    306 public:
    307  explicit LiveSavedFrameCache() : frames(nullptr) {}
    308 
    309  LiveSavedFrameCache(LiveSavedFrameCache&& rhs) : frames(rhs.frames) {
    310    MOZ_ASSERT(this != &rhs, "self-move disallowed");
    311    rhs.frames = nullptr;
    312  }
    313 
    314  ~LiveSavedFrameCache() {
    315    if (frames) {
    316      js_delete(frames);
    317      frames = nullptr;
    318    }
    319  }
    320 
    321  bool initialized() const { return !!frames; }
    322  bool init(JSContext* cx) {
    323    frames = js_new<EntryVector>();
    324    if (!frames) {
    325      JS_ReportOutOfMemory(cx);
    326      return false;
    327    }
    328    return true;
    329  }
    330 
    331  void trace(JSTracer* trc);
    332 
    333  // Set |frame| to the cached SavedFrame corresponding to |framePtr| at |pc|.
    334  // |framePtr|'s hasCachedSavedFrame bit must be set. Remove all cache
    335  // entries for frames younger than that one.
    336  //
    337  // This may set |frame| to nullptr if |pc| is different from the pc supplied
    338  // when the cache entry was inserted. In this case, the cached SavedFrame
    339  // (probably) has the wrong source position. Entries for younger frames are
    340  // still removed. The next frame, if any, will be a cache hit.
    341  //
    342  // This may also set |frame| to nullptr if the cache was populated with
    343  // SavedFrame objects for a different compartment than cx's current
    344  // compartment. In this case, the entire cache is flushed.
    345  void find(JSContext* cx, FramePtr& framePtr, const jsbytecode* pc,
    346            MutableHandle<SavedFrame*> frame) const;
    347 
    348  // Search the cache for a frame matching |framePtr|, without removing any
    349  // entries. Return the matching saved frame, or nullptr if none is found.
    350  // This is used for resolving |evalInFramePrev| links.
    351  void findWithoutInvalidation(const FramePtr& framePtr,
    352                               MutableHandle<SavedFrame*> frame) const;
    353 
    354  // Push a cache entry mapping |framePtr| and |pc| to |savedFrame| on the top
    355  // of the cache's stack. You must insert entries for frames from oldest to
    356  // youngest. They must all be younger than the frame that the |find| method
    357  // found a hit for; or you must have cleared the entire cache with the
    358  // |clear| method.
    359  bool insert(JSContext* cx, FramePtr&& framePtr, const jsbytecode* pc,
    360              Handle<SavedFrame*> savedFrame);
    361 
    362  // Remove all entries from the cache.
    363  void clear() {
    364    if (frames) frames->clear();
    365  }
    366 };
    367 
    368 static_assert(
    369    sizeof(LiveSavedFrameCache) == sizeof(uintptr_t),
    370    "Every js::Activation has a LiveSavedFrameCache, so we need to be pretty "
    371    "careful "
    372    "about avoiding bloat. If you're adding members to LiveSavedFrameCache, "
    373    "maybe you "
    374    "should consider figuring out a way to make js::Activation have a "
    375    "LiveSavedFrameCache* instead of a Rooted<LiveSavedFrameCache>.");
    376 
    377 class Activation {
    378 protected:
    379  JSContext* cx_;
    380  JS::Compartment* compartment_;
    381  Activation* prev_;
    382  Activation* prevProfiling_;
    383 
    384  // Counter incremented by JS::HideScriptedCaller and decremented by
    385  // JS::UnhideScriptedCaller. If > 0 for the top activation,
    386  // DescribeScriptedCaller will return null instead of querying that
    387  // activation, which should prompt the caller to consult embedding-specific
    388  // data structures instead.
    389  size_t hideScriptedCallerCount_;
    390 
    391  // The cache of SavedFrame objects we have already captured when walking
    392  // this activation's stack.
    393  JS::Rooted<LiveSavedFrameCache> frameCache_;
    394 
    395  // Youngest saved frame of an async stack that will be iterated during stack
    396  // capture in place of the actual stack of previous activations. Note that
    397  // the stack of this activation is captured entirely before this is used.
    398  //
    399  // Usually this is nullptr, meaning that normal stack capture will occur.
    400  // When this is set, the stack of any previous activation is ignored.
    401  JS::Rooted<SavedFrame*> asyncStack_;
    402 
    403  // Value of asyncCause to be attached to asyncStack_.
    404  const char* asyncCause_;
    405 
    406  // True if the async call was explicitly requested, e.g. via
    407  // callFunctionWithAsyncStack.
    408  bool asyncCallIsExplicit_;
    409 
    410  enum Kind { Interpreter, Jit };
    411  Kind kind_;
    412 
    413  inline Activation(JSContext* cx, Kind kind);
    414  inline ~Activation();
    415 
    416 public:
    417  JSContext* cx() const { return cx_; }
    418  JS::Compartment* compartment() const { return compartment_; }
    419  Activation* prev() const { return prev_; }
    420  Activation* prevProfiling() const { return prevProfiling_; }
    421  inline Activation* mostRecentProfiling();
    422 
    423  bool isInterpreter() const { return kind_ == Interpreter; }
    424  bool isJit() const { return kind_ == Jit; }
    425  inline bool hasWasmExitFP() const;
    426 
    427  inline bool isProfiling() const;
    428  void registerProfiling();
    429  void unregisterProfiling();
    430 
    431  InterpreterActivation* asInterpreter() const {
    432    MOZ_ASSERT(isInterpreter());
    433    return (InterpreterActivation*)this;
    434  }
    435  jit::JitActivation* asJit() const {
    436    MOZ_ASSERT(isJit());
    437    return (jit::JitActivation*)this;
    438  }
    439 
    440  void hideScriptedCaller() { hideScriptedCallerCount_++; }
    441  void unhideScriptedCaller() {
    442    MOZ_ASSERT(hideScriptedCallerCount_ > 0);
    443    hideScriptedCallerCount_--;
    444  }
    445  bool scriptedCallerIsHidden() const { return hideScriptedCallerCount_ > 0; }
    446 
    447  SavedFrame* asyncStack() { return asyncStack_; }
    448 
    449  const char* asyncCause() const { return asyncCause_; }
    450 
    451  bool asyncCallIsExplicit() const { return asyncCallIsExplicit_; }
    452 
    453  inline LiveSavedFrameCache* getLiveSavedFrameCache(JSContext* cx);
    454  void clearLiveSavedFrameCache() { frameCache_.get().clear(); }
    455 
    456 private:
    457  Activation(const Activation& other) = delete;
    458  void operator=(const Activation& other) = delete;
    459 };
    460 
    461 // This variable holds a special opcode value which is greater than all normal
    462 // opcodes, and is chosen such that the bitwise or of this value with any
    463 // opcode is this value.
    464 constexpr jsbytecode EnableInterruptsPseudoOpcode = -1;
    465 
    466 static_assert(EnableInterruptsPseudoOpcode >= JSOP_LIMIT,
    467              "EnableInterruptsPseudoOpcode must be greater than any opcode");
    468 static_assert(
    469    EnableInterruptsPseudoOpcode == jsbytecode(-1),
    470    "EnableInterruptsPseudoOpcode must be the maximum jsbytecode value");
    471 
    472 class InterpreterFrameIterator;
    473 class RunState;
    474 
    475 class InterpreterActivation : public Activation {
    476  friend class js::InterpreterFrameIterator;
    477 
    478  InterpreterRegs regs_;
    479  InterpreterFrame* entryFrame_;
    480  size_t opMask_;  // For debugger interrupts, see js::Interpret.
    481 
    482 #ifdef DEBUG
    483  size_t oldFrameCount_;
    484 #endif
    485 
    486 public:
    487  inline InterpreterActivation(RunState& state, JSContext* cx,
    488                               InterpreterFrame* entryFrame);
    489  inline ~InterpreterActivation();
    490 
    491  inline bool pushInlineFrame(const JS::CallArgs& args,
    492                              JS::Handle<JSScript*> script,
    493                              MaybeConstruct constructing);
    494  inline void popInlineFrame(InterpreterFrame* frame);
    495 
    496  inline bool resumeGeneratorFrame(JS::Handle<JSFunction*> callee,
    497                                   JS::Handle<JSObject*> envChain);
    498 
    499  InterpreterFrame* current() const { return regs_.fp(); }
    500  InterpreterRegs& regs() { return regs_; }
    501  InterpreterFrame* entryFrame() const { return entryFrame_; }
    502  size_t opMask() const { return opMask_; }
    503 
    504  bool isProfiling() const { return false; }
    505 
    506  // If this js::Interpret frame is running |script|, enable interrupts.
    507  void enableInterruptsIfRunning(JSScript* script) {
    508    if (regs_.fp()->script() == script) {
    509      enableInterruptsUnconditionally();
    510    }
    511  }
    512  void enableInterruptsUnconditionally() {
    513    opMask_ = EnableInterruptsPseudoOpcode;
    514  }
    515  void clearInterruptsMask() { opMask_ = 0; }
    516 };
    517 
    518 // Iterates over a thread's activation list.
    519 class ActivationIterator {
    520 protected:
    521  Activation* activation_;
    522 
    523 public:
    524  explicit ActivationIterator(JSContext* cx);
    525 
    526  ActivationIterator& operator++();
    527 
    528  Activation* operator->() const { return activation_; }
    529  Activation* activation() const { return activation_; }
    530  bool done() const { return activation_ == nullptr; }
    531 };
    532 
    533 }  // namespace js
    534 
    535 #endif  // vm_Activation_h