UNPKG

chrome-devtools-frontend

Version:
220 lines (188 loc) 7.72 kB
// Copyright 2023 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import type {Chrome} from '../../../extension-api/ExtensionAPI.js'; import {createEmbindPool} from '../src/DWARFSymbols.js'; import type * as LLDBEvalTests from './LLDBEvalTests.js'; import loadModule from './LLDBEvalTests.js'; import {Debugger} from './RealBackend.js'; import {createWorkerPlugin, makeURL, remoteObject} from './TestUtils.js'; const WASM_URL = makeURL('/build/tests/inputs/lldb_eval_inputs.wasm'); class LLDBEvalDebugger implements LLDBEvalTests.Debugger { #debugger: Debugger; #plugin: Chrome.DevTools.LanguageExtensionPlugin; constructor(dbg: Debugger, plugin: Chrome.DevTools.LanguageExtensionPlugin) { this.#debugger = dbg; this.#plugin = plugin; } static async create(): Promise<LLDBEvalDebugger> { const dbg = await Debugger.create(); const plugin = await createWorkerPlugin(dbg); return new LLDBEvalDebugger(dbg, plugin); } private async stringify(result: Chrome.DevTools.RemoteObject|Chrome.DevTools.ForeignObject): Promise<string> { if (!this.#plugin.getProperties) { throw new Error('getProperties not implemented'); } if (result.type === 'reftype') { return 'reftype'; } if (result.objectId) { const properties = await this.#plugin.getProperties(result.objectId); if (properties.length === 1) { const [{name}] = properties; if (name.startsWith('0x')) { return `0x${name.substring(2).padStart(8, '0')}`; } } } if (result.description === 'std::nullptr_t') { return '0x00000000'; } if (Object.is(result.value, -0)) { return '-0'; } if (result.value === -Infinity) { return '-Inf'; } if (result.value === Infinity) { return '+Inf'; } return result.description ?? `${result.value}`; } async evaluate(expr: string): Promise<LLDBEvalTests.EvalResult> { const {callFrame, rawLocation} = await this.#debugger.waitForPause(); if (!this.#plugin.evaluate) { throw new Error('Not implemented'); } try { const resultObject = await this.#plugin.evaluate(expr, rawLocation, this.#debugger.stopIdForCallFrame(callFrame)); if (!resultObject) { return {error: `Could not evaluate expression '${expr}'`}; } const result = await this.stringify(resultObject); return {result}; } catch (e) { return {error: `${e}`}; } } async exit(): Promise<void> { if (this.#debugger.isPaused()) { await this.#debugger.clearBreakpoints(); await this.#debugger.resume(); const rawModuleId = await this.#debugger.waitForScript(WASM_URL); await this.#plugin.removeRawModule(rawModuleId); } } async runToLine(line: string): Promise<void> { const page = this.#debugger.page('./lldb_eval_inputs.js'); await page.open(); const rawModuleId = await this.#debugger.waitForScript(WASM_URL); const url = makeURL('/build/tests/inputs/lldb_eval_inputs.wasm.debug.wasm'); const sources = await this.#plugin.addRawModule(rawModuleId, '', {url}); if ('missingSymbolFiles' in sources) { throw new Error('Unexpected missing symbol files'); } const sourceFileURL = sources.find(s => s.endsWith('test_binary.cc')); if (!sourceFileURL) { throw new Error('test_binary.cc source not found'); } const breakpoint = await this.#debugger.setBreakpointOnSourceLine(line, new URL(sourceFileURL), this.#plugin, rawModuleId); const goPromise = page.go(); const pauseOrExitcode = await Promise.race([goPromise, this.#debugger.waitForPause()]); if (typeof pauseOrExitcode === 'number') { throw new Error('Program terminated before all breakpoints were hit.'); } const {rawLocation} = pauseOrExitcode; const [sourceLocation] = await this.#plugin.rawLocationToSourceLocation(rawLocation); if (sourceLocation?.lineNumber !== breakpoint.lineNumber) { throw new Error( `Paused on unexpected line ${sourceLocation?.lineNumber}. Breakpoint was set on ${breakpoint.lineNumber}.`); } } close(): Promise<void> { return this.#debugger.close(); } } describe('Interpreter', () => { it('passes the lldb-eval test suite.', async () => { const lldbEval = await loadModule(); const debug = await LLDBEvalDebugger.create(); const {manage, flush} = createEmbindPool(); try { const argv = manage(new lldbEval.StringArray()); const skippedTests = [ 'EvalTest.TestTemplateTypes', 'EvalTest.TestUnscopedEnumNegation', 'EvalTest.TestUniquePtrDeref', 'EvalTest.TestUniquePtrCompare', ]; argv.push_back(`--gtest_filter=-${skippedTests.join(':')}`); const exitCode = await lldbEval.runTests(debug, argv); assert.strictEqual(exitCode, 0, 'gtest test suite failed'); } finally { flush(); } }); it('can do basic arithmetic.', async () => { const debug = await Debugger.create(); const page = debug.page('./addresses_main.js'); await page.open(); const wasmUrl = makeURL('/build/tests/inputs/addresses_main.wasm'); const rawModuleId = await debug.waitForScript(wasmUrl); const plugin = await createWorkerPlugin(debug); const url = makeURL('/build/tests/inputs/addresses_main.wasm.debug.wasm'); const sources = await plugin.addRawModule(rawModuleId, '', {url}); if ('missingSymbolFiles' in sources) { throw new Error('Unexpected missing symbol files'); } const sourceFileURL = sources.find(s => s.endsWith('addresses.cc')); if (!sourceFileURL) { throw new Error('addresses.cc source not found'); } const {lineNumber} = await debug.setBreakpointOnSourceLine( '// BREAK(ArrayMembersTest)', new URL(sourceFileURL), plugin, rawModuleId); const goPromise = page.go(); const pauseOrExitcode = await Promise.race([debug.waitForPause(), goPromise]); if (typeof pauseOrExitcode === 'number') { throw new Error('Program terminated before all breakpoints were hit.'); } const {callFrame, rawLocation} = pauseOrExitcode; const [sourceLocation] = await plugin.rawLocationToSourceLocation(rawLocation); if (sourceLocation?.lineNumber !== lineNumber) { throw new Error('Paused at an unexpected location. Have not set a breakpoint here.'); } const variables = await plugin.listVariablesInScope(rawLocation); expect(variables.map(v => v.name).sort()).to.deep.equal(['n', 'sum', 'x']); if (!plugin.evaluate) { throw new Error('evaluate is undefined'); } { const {value} = remoteObject(await plugin.evaluate('n + sum', rawLocation, debug.stopIdForCallFrame(callFrame))); expect(value).to.eql(55); } { const {value} = remoteObject(await plugin.evaluate('(wchar_t)0x41414141', rawLocation, debug.stopIdForCallFrame(callFrame))); expect(value).to.eql('U+41414141'); } { const {value} = remoteObject(await plugin.evaluate('(char16_t)0x4141', rawLocation, debug.stopIdForCallFrame(callFrame))); expect(value).to.eql('䅁'); } { const {value} = remoteObject(await plugin.evaluate('(char32_t)0x41414141', rawLocation, debug.stopIdForCallFrame(callFrame))); expect(value).to.eql('U+41414141'); } { const {value} = remoteObject(await plugin.evaluate('(char32_t)0x4141', rawLocation, debug.stopIdForCallFrame(callFrame))); expect(value).to.eql('䅁'); } await debug.resume(); await debug.close(); }); });