WasmDebug.h (7675B)
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 2016 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_debug_h 20 #define wasm_debug_h 21 22 #include "js/ColumnNumber.h" // JS::LimitedColumnNumberOneOrigin 23 #include "js/HashTable.h" 24 #include "wasm/AsmJS.h" // CodeMetadataForAsmJS::SeenSet 25 #include "wasm/WasmCode.h" 26 #include "wasm/WasmCodegenTypes.h" 27 #include "wasm/WasmConstants.h" 28 #include "wasm/WasmExprType.h" 29 #include "wasm/WasmModule.h" 30 #include "wasm/WasmTypeDecls.h" 31 #include "wasm/WasmValType.h" 32 33 namespace js { 34 35 class Debugger; 36 class WasmBreakpointSite; 37 class WasmInstanceObject; 38 39 namespace wasm { 40 41 // The generated source location for the AST node/expression. The offset field 42 // refers an offset in an binary format file. 43 44 struct ExprLoc { 45 uint32_t lineno; 46 uint32_t column; 47 uint32_t offset; 48 ExprLoc() : lineno(0), column(0), offset(0) {} 49 ExprLoc(uint32_t lineno_, uint32_t column_, uint32_t offset_) 50 : lineno(lineno_), column(column_), offset(offset_) {} 51 }; 52 53 using StepperCounters = 54 HashMap<uint32_t, uint32_t, DefaultHasher<uint32_t>, SystemAllocPolicy>; 55 using WasmBreakpointSiteMap = 56 HashMap<uint32_t, WasmBreakpointSite*, DefaultHasher<uint32_t>, 57 SystemAllocPolicy>; 58 59 /* 60 * [SMDOC] Wasm debug traps 61 * 62 * There is a single debug-trap handler for the process, WasmHandleDebugTrap 63 * in WasmBuiltins.cpp. That function is invoked through the Debug Trap Stub, 64 * of which there is one per module, generated by GenerateDebugStub in 65 * WasmStubs.cpp. When any function in an instance needs to debug-trap for 66 * any reason (enter frame, leave frame, breakpoint, or single-stepping) then 67 * a pointer to the Debug Trap Stub is installed in the Instance. 68 * Debug-enabled code will look for this pointer and call it if (1) it is not 69 * null and (2) subject to filtering as follows. 70 * 71 * WasmHandleDebugTrap may therefore be called very frequently when any 72 * function in the instance is being debugged, and must filter the trap 73 * against the tables in the DebugState. It can make use of the return 74 * address for the call, which identifies the site uniquely. 75 * 76 * In order to greatly reduce the frequency of calls to the Debug Trap Stub, 77 * an array of flag bits, one per function, is attached to the instance. The 78 * code at the breakable point calls a stub at the end of the function (the 79 * Per Function Debug Stub) to check whether the bit is set for the function. 80 * If it is not set, the per-function stub can return to its caller 81 * immediately; if the bit is set, the per-function stub will jump to the 82 * installed (per-module) Debug Trap Stub. 83 * 84 * See also [SMDOC] "Wasm debug traps -- code details" 85 */ 86 87 class DebugState { 88 const SharedCode code_; 89 const SharedModule module_; 90 91 // State maintained when debugging is enabled. 92 93 bool enterFrameTrapsEnabled_; 94 uint32_t enterAndLeaveFrameTrapsCounter_; 95 WasmBreakpointSiteMap breakpointSites_; 96 StepperCounters stepperCounters_; 97 98 void enableDebuggingForFunction(Instance* instance, uint32_t funcIndex); 99 void disableDebuggingForFunction(Instance* instance, uint32_t funcIndex); 100 void enableDebugTrapping(Instance* instance); 101 void disableDebugTrapping(Instance* instance); 102 103 public: 104 DebugState(const Code& code, const Module& module); 105 106 void trace(JSTracer* trc); 107 void finalize(JS::GCContext* gcx); 108 109 const BytecodeSource& bytecode() const { return module_->debugBytecode(); } 110 111 [[nodiscard]] bool getLineOffsets(size_t lineno, Vector<uint32_t>* offsets); 112 [[nodiscard]] bool getAllColumnOffsets(Vector<ExprLoc>* offsets); 113 [[nodiscard]] bool getOffsetLocation( 114 uint32_t offset, uint32_t* lineno, 115 JS::LimitedColumnNumberOneOrigin* column); 116 117 // The Code can track enter/leave frame events. Any such event triggers 118 // debug trap. The enter/leave frame events enabled or disabled across 119 // all functions. 120 121 void adjustEnterAndLeaveFrameTrapsState(JSContext* cx, Instance* instance, 122 bool enabled); 123 void ensureEnterFrameTrapsState(JSContext* cx, Instance* instance, 124 bool enabled); 125 bool enterFrameTrapsEnabled() const { return enterFrameTrapsEnabled_; } 126 127 // When the Code is debugEnabled, individual breakpoints can be enabled or 128 // disabled at instruction offsets. 129 130 bool hasBreakpointTrapAtOffset(uint32_t offset); 131 void toggleBreakpointTrap(JSRuntime* rt, Instance* instance, uint32_t offset, 132 bool enabled); 133 WasmBreakpointSite* getBreakpointSite(uint32_t offset) const; 134 WasmBreakpointSite* getOrCreateBreakpointSite(JSContext* cx, 135 Instance* instance, 136 uint32_t offset); 137 bool hasBreakpointSite(uint32_t offset); 138 void destroyBreakpointSite(JS::GCContext* gcx, Instance* instance, 139 uint32_t offset); 140 void clearBreakpointsIn(JS::GCContext* gcx, WasmInstanceObject* instance, 141 js::Debugger* dbg, JSObject* handler); 142 143 // When the Code is debug-enabled, single-stepping mode can be toggled on 144 // the granularity of individual functions. 145 146 bool stepModeEnabled(uint32_t funcIndex) const; 147 [[nodiscard]] bool incrementStepperCount(JSContext* cx, Instance* instance, 148 uint32_t funcIndex); 149 void decrementStepperCount(JS::GCContext* gcx, Instance* instance, 150 uint32_t funcIndex); 151 152 // Stack inspection helpers. 153 154 [[nodiscard]] bool debugGetLocalTypes(uint32_t funcIndex, 155 ValTypeVector* locals, 156 size_t* argsLength, 157 StackResults* stackResults); 158 [[nodiscard]] bool getGlobal(Instance& instance, uint32_t globalIndex, 159 MutableHandleValue vp); 160 161 // Debug URL helpers. 162 163 [[nodiscard]] bool getSourceMappingURL(JSContext* cx, 164 MutableHandleString result) const; 165 166 // Accessors for commonly used elements of linked structures. 167 168 const CodeBlock& debugCode() const { return code_->debugCodeBlock(); } 169 const CodeSegment& debugSegment() const { 170 return *code_->debugCodeBlock().segment; 171 } 172 const CodeMetadata& codeMeta() const { return code_->codeMeta(); } 173 const CodeTailMetadata& codeTailMeta() const { return code_->codeTailMeta(); } 174 const CodeMetadataForAsmJS* codeMetaForAsmJS() const { 175 return code_->codeMetaForAsmJS(); 176 } 177 178 uint32_t funcToCodeRangeIndex(uint32_t funcIndex) const { 179 return debugCode().funcToCodeRange[funcIndex]; 180 } 181 182 // about:memory reporting: 183 184 void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, 185 CodeMetadata::SeenSet* seenCodeMeta, 186 CodeMetadataForAsmJS::SeenSet* seenCodeMetaForAsmJS, 187 Code::SeenSet* seenCode, size_t* code, size_t* data) const; 188 }; 189 190 using UniqueDebugState = UniquePtr<DebugState>; 191 192 } // namespace wasm 193 } // namespace js 194 195 #endif // wasm_debug_h