browser_dbg-features-wasm.js (7013B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */ 4 5 /** 6 * This test covers all specifics of debugging WASM/WebAssembly files. 7 * 8 * WebAssembly is a binary file format for the Web. 9 * The binary files are loaded by Gecko and machine code runs. 10 * In order to debug them ThreadConfiguration's `observeWasm` is set to true, 11 * only once the debugger is opened. This will set DebuggerAPI's `allowUnobservedWasm` to false. 12 * Then, the engine will compute a different machine code with debugging instruction. 13 * This will use a lot more memory, but allow debugger to set breakpoint and break on WASM sources. 14 * 15 * This behavior introduces some limitations when opening the debugger against 16 * and already loaded page. The WASM file won't be debuggable. 17 */ 18 19 "use strict"; 20 21 add_task(async function () { 22 // Load the test page before opening the debugger so that WASM are built 23 // without debugging instructions. Opening the console still doesn't enable debugging instructions. 24 const tab = await addTab(EXAMPLE_URL + "doc-wasm-sourcemaps.html"); 25 const toolbox = await openToolboxForTab(tab, "webconsole"); 26 27 // Reload once again, while the console is opened. 28 // When opening the debugger, it will still miss the source content. 29 // To see the sources, we have to reload while the debugger has been opened. 30 await reloadBrowser(); 31 32 await toolbox.selectTool("jsdebugger"); 33 const dbg = createDebuggerContext(toolbox); 34 35 // When opening against an already loaded page, WASM source loading doesn't work 36 // And because of that we don't see sourcemap/original files. 37 await waitForSourcesInSourceTree(dbg, [ 38 "doc-wasm-sourcemaps.html", 39 "fib.wasm", 40 ]); 41 is(dbg.selectors.getSourceCount(), 2, "There are only these two sources"); 42 43 const source = findSource(dbg, "fib.wasm"); 44 is(source.isWasm, true, "The original source is flagged as Wasm source"); 45 46 // Note that there is no point in asserting breakable lines, 47 // as we aren't fetching any source. 48 await dbg.actions.selectLocation(createLocation({ source }), { 49 keepContext: false, 50 }); 51 is(getEditorContent(dbg), `Please refresh to debug this module`); 52 53 info("Reload and assert that WASM files are then debuggable"); 54 await reload(dbg, "doc-wasm-sourcemaps.html", "fib.wasm", "fib.c"); 55 56 info("After reloading, original file lines are breakable"); 57 // Ensure selecting the source before asserting breakable lines 58 // otherwise the gutter may not be yet updated 59 await selectSource(dbg, "fib.c"); 60 await assertLineIsBreakable(dbg, source.url, 14, true); 61 62 await waitForSourcesInSourceTree(dbg, [ 63 "doc-wasm-sourcemaps.html", 64 "fib.wasm", 65 "fib.c", 66 ]); 67 is(dbg.selectors.getSourceCount(), 3, "There is all these 3 sources"); 68 // (even if errno_location.c is still not functional) 69 70 // The line in the original C file, where the for() loop starts 71 const breakpointLine = 12; 72 assertTextContentOnLine(dbg, breakpointLine, "for (i = 0; i < n; i++) {"); 73 74 info("Register and trigger a breakpoint from the original source in C"); 75 await addBreakpoint(dbg, "fib.c", breakpointLine); 76 invokeInTab("runWasm"); 77 78 await waitForPausedInOriginalFileAndToggleMapScopes(dbg); 79 80 await assertPausedAtSourceAndLine( 81 dbg, 82 findSource(dbg, "fib.c").id, 83 breakpointLine 84 ); 85 await assertBreakpoint(dbg, breakpointLine); 86 // Capture the generated location line, so that we can better report 87 // when the binary code changed later in this test 88 const frames = dbg.selectors.getCurrentThreadFrames(); 89 const generatedLine = frames[0].generatedLocation.line; 90 91 assertFirstFrameTitleAndLocation(dbg, "(wasmcall)", "fib.c"); 92 93 await removeBreakpoint(dbg, findSource(dbg, "fib.c").id, breakpointLine); 94 await resume(dbg); 95 96 info( 97 "Now register and trigger the same breakpoint from the binary source file" 98 ); 99 const binarySource = findSource(dbg, "fib.wasm"); 100 101 // There is two lines, the hexadecimal one is the "virtual line" displayed in the gutter. 102 // While the decimal one is the line where the line appear in CodeMirror. 103 // So while we set the breakpoint on the decimal line in CodeMirror gutter, 104 // internaly, the engine sets the breakpoint on the "virtual line". 105 const virtualBinaryLine = 0x11a; 106 is( 107 "0x" + virtualBinaryLine.toString(16), 108 "0x" + generatedLine.toString(16), 109 "The hardcoded binary line (0x" + 110 generatedLine.toString(16) + 111 ") matches the mapped location when we set the breakpoint on the original line. If you rebuilt the binary, you may just need to update the virtualBinaryLine variable to the new location." 112 ); 113 114 await dbg.actions.selectLocation(createLocation({ source: binarySource }), { 115 keepContext: false, 116 }); 117 118 const binaryLine = wasmOffsetToLine(dbg, virtualBinaryLine); 119 120 // Make sure line is within viewport 121 await scrollEditorIntoView(dbg, binaryLine, 0); 122 await assertLineIsBreakable(dbg, binarySource.url, binaryLine, true); 123 124 await addBreakpoint(dbg, binarySource, virtualBinaryLine); 125 invokeInTab("runWasm"); 126 127 // We can't use waitForPaused test helper as the text content isn't displayed correctly 128 // so only assert that we are in paused state. 129 await waitForPaused(dbg); 130 // We don't try to assert paused line as there is two types of line in wasm 131 await assertPausedAtSourceAndLine(dbg, binarySource.id, virtualBinaryLine); 132 133 // Switch to original source 134 info( 135 "Manually switch to original C source as we set the breakpoint on binary source, we paused on it" 136 ); 137 await dbg.actions.jumpToMappedSelectedLocation(); 138 139 // But once we switch to original source, we should have the original text content and be able 140 // to do all classic assertions for paused state. 141 await waitForPausedInOriginalFileAndToggleMapScopes(dbg); 142 143 await assertPausedAtSourceAndLine( 144 dbg, 145 findSource(dbg, "fib.c").id, 146 breakpointLine 147 ); 148 149 info("Reselect the binary source"); 150 await dbg.actions.selectLocation(createLocation({ source: binarySource }), { 151 keepContext: false, 152 }); 153 154 assertFirstFrameTitleAndLocation(dbg, "(wasmcall)", "fib.wasm"); 155 156 // We can't use this method as it uses internaly the breakpoint line, which isn't the line in CodeMirror 157 // await assertPausedAtSourceAndLine(dbg, binarySource.id, binaryLine); 158 await assertBreakpoint(dbg, binaryLine); 159 160 await removeBreakpoint(dbg, binarySource.id, virtualBinaryLine); 161 await resume(dbg); 162 }); 163 164 function assertFirstFrameTitleAndLocation(dbg, title, location) { 165 const frames = findAllElements(dbg, "frames"); 166 const firstFrameTitle = frames[0].querySelector(".title").textContent; 167 is(firstFrameTitle, title, "First frame title is the expected one"); 168 const firstFrameLocation = frames[0].querySelector(".location").textContent; 169 is( 170 firstFrameLocation.includes(location), 171 true, 172 `First frame location '${firstFrameLocation}' includes '${location}'` 173 ); 174 }