WasmDebugFrame.cpp (5958B)
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 2021 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 #include "wasm/WasmDebugFrame.h" 20 21 #include "vm/EnvironmentObject.h" 22 #include "wasm/WasmBaselineCompile.h" 23 #include "wasm/WasmDebug.h" 24 #include "wasm/WasmInstance.h" 25 #include "wasm/WasmInstanceData.h" 26 #include "wasm/WasmStubs.h" 27 28 #include "vm/NativeObject-inl.h" 29 #include "wasm/WasmInstance-inl.h" 30 31 using namespace js; 32 using namespace js::jit; 33 using namespace js::wasm; 34 35 /* static */ 36 DebugFrame* DebugFrame::from(Frame* fp) { 37 MOZ_ASSERT(GetNearestEffectiveInstance(fp)->code().debugEnabled()); 38 auto* df = 39 reinterpret_cast<DebugFrame*>((uint8_t*)fp - DebugFrame::offsetOfFrame()); 40 MOZ_ASSERT(GetNearestEffectiveInstance(fp) == df->instance()); 41 return df; 42 } 43 44 void DebugFrame::alignmentStaticAsserts() { 45 // VS2017 doesn't consider offsetOfFrame() to be a constexpr, so we have 46 // to use offsetof directly. These asserts can't be at class-level 47 // because the type is incomplete. 48 49 static_assert(WasmStackAlignment >= Alignment, 50 "Aligned by ABI before pushing DebugFrame"); 51 #ifndef JS_CODEGEN_NONE 52 static_assert((offsetof(DebugFrame, frame_) + sizeof(Frame)) % Alignment == 0, 53 "Aligned after pushing DebugFrame"); 54 #endif 55 #ifdef JS_CODEGEN_ARM64 56 // This constraint may or may not be necessary. If you hit this because 57 // you've changed the frame size then feel free to remove it, but be extra 58 // aware of possible problems. 59 static_assert(sizeof(DebugFrame) % 16 == 0, "ARM64 SP alignment"); 60 #endif 61 } 62 63 Instance* DebugFrame::instance() { 64 return GetNearestEffectiveInstance(&frame_); 65 } 66 67 const Instance* DebugFrame::instance() const { 68 return GetNearestEffectiveInstance(&frame_); 69 } 70 71 GlobalObject* DebugFrame::global() { return &instance()->object()->global(); } 72 73 bool DebugFrame::hasGlobal(const GlobalObject* global) const { 74 return global == &instance()->objectUnbarriered()->global(); 75 } 76 77 JSObject* DebugFrame::environmentChain() { 78 return &global()->lexicalEnvironment(); 79 } 80 81 bool DebugFrame::getLocal(uint32_t localIndex, MutableHandleValue vp) { 82 ValTypeVector locals; 83 size_t argsLength; 84 StackResults stackResults; 85 if (!instance()->debug().debugGetLocalTypes(funcIndex(), &locals, &argsLength, 86 &stackResults)) { 87 return false; 88 } 89 90 ValTypeVector args; 91 MOZ_ASSERT(argsLength <= locals.length()); 92 if (!args.append(locals.begin(), argsLength)) { 93 return false; 94 } 95 ArgTypeVector abiArgs(args, stackResults); 96 97 BaseLocalIter iter(locals, abiArgs, /* debugEnabled = */ true); 98 while (!iter.done() && iter.index() < localIndex) { 99 iter++; 100 } 101 MOZ_ALWAYS_TRUE(!iter.done()); 102 103 uint8_t* frame = static_cast<uint8_t*>((void*)this) + offsetOfFrame(); 104 void* dataPtr = frame - iter.frameOffset(); 105 switch (iter.mirType()) { 106 case jit::MIRType::Int32: 107 vp.set(Int32Value(*static_cast<int32_t*>(dataPtr))); 108 break; 109 case jit::MIRType::Int64: 110 // Just display as a Number; it's ok if we lose some precision 111 vp.set(NumberValue((double)*static_cast<int64_t*>(dataPtr))); 112 break; 113 case jit::MIRType::Float32: 114 vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<float*>(dataPtr)))); 115 break; 116 case jit::MIRType::Double: 117 vp.set(NumberValue(JS::CanonicalizeNaN(*static_cast<double*>(dataPtr)))); 118 break; 119 case jit::MIRType::WasmAnyRef: 120 vp.set(((AnyRef*)dataPtr)->toJSValue()); 121 break; 122 #ifdef ENABLE_WASM_SIMD 123 case jit::MIRType::Simd128: 124 vp.set(NumberValue(0)); 125 break; 126 #endif 127 default: 128 MOZ_CRASH("local type"); 129 } 130 return true; 131 } 132 133 bool DebugFrame::updateReturnJSValue(JSContext* cx) { 134 MutableHandleValue rval = 135 MutableHandleValue::fromMarkedLocation(&cachedReturnJSValue_); 136 rval.setUndefined(); 137 flags_.hasCachedReturnJSValue = true; 138 ResultType resultType = ResultType::Vector( 139 instance()->codeMeta().getFuncType(funcIndex()).results()); 140 mozilla::Maybe<char*> stackResultsLoc; 141 if (ABIResultIter::HasStackResults(resultType)) { 142 stackResultsLoc = mozilla::Some(static_cast<char*>(stackResultsPointer_)); 143 } 144 DebugCodegen(DebugChannel::Function, 145 "wasm-function[%d] updateReturnJSValue [", funcIndex()); 146 bool ok = 147 ResultsToJSValue(cx, resultType, registerResults_, stackResultsLoc, rval); 148 DebugCodegen(DebugChannel::Function, "]\n"); 149 return ok; 150 } 151 152 void DebugFrame::discardReturnJSValue() { 153 MutableHandleValue rval = 154 MutableHandleValue::fromMarkedLocation(&cachedReturnJSValue_); 155 rval.setMagic(JS_OPTIMIZED_OUT); 156 flags_.hasCachedReturnJSValue = true; 157 } 158 159 HandleValue DebugFrame::returnValue() const { 160 MOZ_ASSERT(flags_.hasCachedReturnJSValue); 161 return HandleValue::fromMarkedLocation(&cachedReturnJSValue_); 162 } 163 164 void DebugFrame::clearReturnJSValue() { 165 flags_.hasCachedReturnJSValue = true; 166 cachedReturnJSValue_.setUndefined(); 167 } 168 169 void DebugFrame::observe(JSContext* cx) { 170 if (!flags_.observing) { 171 instance()->debug().adjustEnterAndLeaveFrameTrapsState( 172 cx, instance(), /* enabled = */ true); 173 flags_.observing = true; 174 } 175 } 176 177 void DebugFrame::leave(JSContext* cx) { 178 if (flags_.observing) { 179 instance()->debug().adjustEnterAndLeaveFrameTrapsState( 180 cx, instance(), /* enabled = */ false); 181 flags_.observing = false; 182 } 183 }