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  *
4  * Copyright 2014 Mozilla Foundation
5  *
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 #ifndef wasm_frame_iter_h
20 #define wasm_frame_iter_h
21 
22 #include "js/ProfilingFrameIterator.h"
23 #include "js/TypeDecls.h"
24 
25 namespace js {
26 
27 namespace jit {
28 class JitActivation;
29 class MacroAssembler;
30 struct Register;
31 enum class FrameType;
32 }  // namespace jit
33 
34 namespace wasm {
35 
36 class Code;
37 class CodeRange;
38 class DebugFrame;
39 struct TlsData;
40 class TypeIdDesc;
41 class Instance;
42 
43 struct CallableOffsets;
44 struct FuncOffsets;
45 struct Offsets;
46 class Frame;
47 
48 using RegisterState = JS::ProfilingFrameIterator::RegisterState;
49 
50 // Iterates over a linear group of wasm frames of a single wasm JitActivation,
51 // called synchronously from C++ in the wasm thread. It will stop at the first
52 // frame that is not of the same kind, or at the end of an activation.
53 //
54 // If you want to handle every kind of frames (including JS jit frames), use
55 // JitFrameIter.
56 
57 class WasmFrameIter {
58  public:
59   enum class Unwind { True, False };
60   static constexpr uint32_t ColumnBit = 1u << 31;
61 
62  private:
63   jit::JitActivation* activation_;
64   const Code* code_;
65   const CodeRange* codeRange_;
66   unsigned lineOrBytecode_;
67   Frame* fp_;
68   TlsData* tls_;
69   uint8_t* unwoundIonCallerFP_;
70   jit::FrameType unwoundIonFrameType_;
71   Unwind unwind_;
72   void** unwoundAddressOfReturnAddress_;
73   uint8_t* resumePCinCurrentFrame_;
74 
75   void popFrame();
76 
77  public:
78   // See comment above this class definition.
79   explicit WasmFrameIter(jit::JitActivation* activation, Frame* fp = nullptr);
activation()80   const jit::JitActivation* activation() const { return activation_; }
setUnwind(Unwind unwind)81   void setUnwind(Unwind unwind) { unwind_ = unwind; }
82   void operator++();
83   bool done() const;
84   const char* filename() const;
85   const char16_t* displayURL() const;
86   bool mutedErrors() const;
87   JSAtom* functionDisplayAtom() const;
88   unsigned lineOrBytecode() const;
89   uint32_t funcIndex() const;
90   unsigned computeLine(uint32_t* column) const;
codeRange()91   const CodeRange* codeRange() const { return codeRange_; }
92   Instance* instance() const;
93   void** unwoundAddressOfReturnAddress() const;
94   bool debugEnabled() const;
95   DebugFrame* debugFrame() const;
96   jit::FrameType unwoundIonFrameType() const;
unwoundIonCallerFP()97   uint8_t* unwoundIonCallerFP() const { return unwoundIonCallerFP_; }
frame()98   Frame* frame() const { return fp_; }
tls()99   TlsData* tls() const { return tls_; }
100 
101   // Returns the address of the next instruction that will execute in this
102   // frame, once control returns to this frame.
103   uint8_t* resumePCinCurrentFrame() const;
104 };
105 
106 enum class SymbolicAddress;
107 
108 // An ExitReason describes the possible reasons for leaving compiled wasm
109 // code or the state of not having left compiled wasm code
110 // (ExitReason::None). It is either a known reason, or a enumeration to a native
111 // function that is used for better display in the profiler.
112 class ExitReason {
113  public:
114   enum class Fixed : uint32_t {
115     None,             // default state, the pc is in wasm code
116     FakeInterpEntry,  // slow-path entry call from C++ WasmCall()
117     ImportJit,        // fast-path call directly into JIT code
118     ImportInterp,     // slow-path call into C++ Invoke()
119     BuiltinNative,    // fast-path call directly into native C++ code
120     Trap,             // call to trap handler
121     DebugTrap         // call to debug trap handler
122   };
123 
124  private:
125   uint32_t payload_;
126 
ExitReason()127   ExitReason() : ExitReason(Fixed::None) {}
128 
129  public:
ExitReason(Fixed exitReason)130   MOZ_IMPLICIT ExitReason(Fixed exitReason)
131       : payload_(0x0 | (uint32_t(exitReason) << 1)) {
132     MOZ_ASSERT(isFixed());
133     MOZ_ASSERT_IF(isNone(), payload_ == 0);
134   }
135 
ExitReason(SymbolicAddress sym)136   explicit ExitReason(SymbolicAddress sym)
137       : payload_(0x1 | (uint32_t(sym) << 1)) {
138     MOZ_ASSERT(uint32_t(sym) <= (UINT32_MAX << 1), "packing constraints");
139     MOZ_ASSERT(!isFixed());
140   }
141 
Decode(uint32_t payload)142   static ExitReason Decode(uint32_t payload) {
143     ExitReason reason;
144     reason.payload_ = payload;
145     return reason;
146   }
147 
None()148   static ExitReason None() { return ExitReason(ExitReason::Fixed::None); }
149 
isFixed()150   bool isFixed() const { return (payload_ & 0x1) == 0; }
isNone()151   bool isNone() const { return isFixed() && fixed() == Fixed::None; }
isNative()152   bool isNative() const {
153     return !isFixed() || fixed() == Fixed::BuiltinNative;
154   }
isInterpEntry()155   bool isInterpEntry() const {
156     return isFixed() && fixed() == Fixed::FakeInterpEntry;
157   }
158 
encode()159   uint32_t encode() const { return payload_; }
fixed()160   Fixed fixed() const {
161     MOZ_ASSERT(isFixed());
162     return Fixed(payload_ >> 1);
163   }
symbolic()164   SymbolicAddress symbolic() const {
165     MOZ_ASSERT(!isFixed());
166     return SymbolicAddress(payload_ >> 1);
167   }
168 };
169 
170 // Iterates over the frames of a single wasm JitActivation, given an
171 // asynchronously-profiled thread's state.
172 class ProfilingFrameIterator {
173   const Code* code_;
174   const CodeRange* codeRange_;
175   uint8_t* callerFP_;
176   void* callerPC_;
177   void* stackAddress_;
178   uint8_t* unwoundIonCallerFP_;
179   ExitReason exitReason_;
180 
181   void initFromExitFP(const Frame* fp);
182 
183  public:
184   ProfilingFrameIterator();
185 
186   // Start unwinding at a non-innermost activation that has necessarily been
187   // exited from wasm code (and thus activation.hasWasmExitFP).
188   explicit ProfilingFrameIterator(const jit::JitActivation& activation);
189 
190   // Start unwinding at a group of wasm frames after unwinding an inner group
191   // of JSJit frames.
192   explicit ProfilingFrameIterator(const Frame* fp);
193 
194   // Start unwinding at the innermost activation given the register state when
195   // the thread was suspended.
196   ProfilingFrameIterator(const jit::JitActivation& activation,
197                          const RegisterState& state);
198 
199   void operator++();
done()200   bool done() const { return !codeRange_ && exitReason_.isNone(); }
201 
stackAddress()202   void* stackAddress() const {
203     MOZ_ASSERT(!done());
204     return stackAddress_;
205   }
unwoundIonCallerFP()206   uint8_t* unwoundIonCallerFP() const {
207     MOZ_ASSERT(done());
208     return unwoundIonCallerFP_;
209   }
210   const char* label() const;
211 };
212 
213 // Prologue/epilogue code generation
214 
215 void SetExitFP(jit::MacroAssembler& masm, ExitReason reason,
216                jit::Register scratch);
217 void ClearExitFP(jit::MacroAssembler& masm, jit::Register scratch);
218 
219 void GenerateExitPrologue(jit::MacroAssembler& masm, unsigned framePushed,
220                           ExitReason reason, CallableOffsets* offsets);
221 void GenerateExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed,
222                           ExitReason reason, CallableOffsets* offsets);
223 
224 void GenerateJitExitPrologue(jit::MacroAssembler& masm, unsigned framePushed,
225                              CallableOffsets* offsets);
226 void GenerateJitExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed,
227                              CallableOffsets* offsets);
228 
229 void GenerateJitEntryPrologue(jit::MacroAssembler& masm, Offsets* offsets);
230 
231 void GenerateFunctionPrologue(jit::MacroAssembler& masm,
232                               const TypeIdDesc& funcTypeId,
233                               const mozilla::Maybe<uint32_t>& tier1FuncIndex,
234                               FuncOffsets* offsets);
235 void GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed,
236                               FuncOffsets* offsets);
237 
238 // Iterates through frames for either possible cross-instance call or an entry
239 // stub to obtain tls that corresponds to the passed fp.
240 const TlsData* GetNearestEffectiveTls(const Frame* fp);
241 TlsData* GetNearestEffectiveTls(Frame* fp);
242 
243 // Describes register state and associated code at a given call frame.
244 
245 struct UnwindState {
246   uint8_t* fp;
247   void* pc;
248   const Code* code;
249   const CodeRange* codeRange;
UnwindStateUnwindState250   UnwindState() : fp(nullptr), pc(nullptr), code(nullptr), codeRange(nullptr) {}
251 };
252 
253 // Ensures the register state at a call site is consistent: pc must be in the
254 // code range of the code described by fp. This prevents issues when using
255 // the values of pc/fp, especially at call sites boundaries, where the state
256 // hasn't fully transitioned from the caller's to the callee's.
257 //
258 // unwoundCaller is set to true if we were in a transitional state and had to
259 // rewind to the caller's frame instead of the current frame.
260 //
261 // Returns true if it was possible to get to a clear state, or false if the
262 // frame should be ignored.
263 
264 bool StartUnwinding(const RegisterState& registers, UnwindState* unwindState,
265                     bool* unwoundCaller);
266 
267 }  // namespace wasm
268 }  // namespace js
269 
270 #endif  // wasm_frame_iter_h
271