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