diff --git a/eslint.config.mjs b/eslint.config.mjs index bcaf8e94..15cf174b 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -39,6 +39,11 @@ export default tseslint.config( rules: { '@typescript-eslint/no-base-to-string': 'off', // 1 instance + '@typescript-eslint/prefer-promise-reject-errors': ['error', { + // for catch (e) { reject(e); } scenarios, until promises are refactored + allowThrowingAny: true, + allowThrowingUnknown: true, + }], '@stylistic/indent-binary-ops': 'off', // this is a weird rule '@stylistic/max-len': ['error', { @@ -64,25 +69,25 @@ export default tseslint.config( // the following rules are being heavily violated in the current codebase, // we should work on being able to enable them... rules: { - '@typescript-eslint/no-unsafe-member-access': 'off', // 742 instances - '@typescript-eslint/no-unsafe-call': 'off', // 432 instances - '@typescript-eslint/no-unsafe-assignment': 'off', // 429 instances - '@typescript-eslint/no-unsafe-argument': 'off', // 401 instances - '@typescript-eslint/no-explicit-any': 'off', // 226 instances - '@typescript-eslint/no-unused-vars': 'off', // 204 instances - '@typescript-eslint/no-unsafe-return': 'off', // 83 instances - '@typescript-eslint/no-misused-promises': 'off', // 57 instances - '@typescript-eslint/no-floating-promises': 'off', // 55 instances - 'no-useless-escape': 'off', // 38 instances - '@typescript-eslint/prefer-promise-reject-errors': 'off', // 36 instances - 'no-async-promise-executor': 'off', // 29 instances - '@typescript-eslint/no-require-imports': 'off', // 24 instances - 'no-cond-assign': 'off', // 21 instances - '@typescript-eslint/require-await': 'off', // 11 instances + '@typescript-eslint/no-unsafe-member-access': 'off', // 655 instances + '@typescript-eslint/no-unsafe-call': 'off', // 381 instances + '@typescript-eslint/no-unsafe-assignment': 'off', // 354 instances + '@typescript-eslint/no-unsafe-argument': 'off', // 309 instances + '@typescript-eslint/no-explicit-any': 'off', // 187 instances + '@typescript-eslint/no-unused-vars': 'off', // 169 instances + '@typescript-eslint/no-misused-promises': 'off', // 53 instances + '@typescript-eslint/no-floating-promises': 'off', // 48 instances + 'no-async-promise-executor': 'off', // 25 instances } }, { files: ['**/*.{js,mjs}'], extends: [tseslint.configs.disableTypeChecked], }, + { + files: ['**/*.js'], + rules: { + '@typescript-eslint/no-require-imports': 'off', + } + } ); diff --git a/package-lock.json b/package-lock.json index ce4395da..4d0f2d6f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,6 @@ }, "devDependencies": { "@stylistic/eslint-plugin": "^2.13.0", - "@types/binary-parser": "^1.5.5", "@types/mocha": "^10.0.10", "@types/node": "16.x", "@types/vscode": "^1.69.0", @@ -883,16 +882,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/@types/binary-parser": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@types/binary-parser/-/binary-parser-1.5.5.tgz", - "integrity": "sha512-HL2Q9Q0JPlgQQX7JFgCIkofUw6jfUnP4WoBRlgF4yhPxLN2hfzaN5VNBQBq0cHD3+V4WBlJQuf/Lk2zdCKuGAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", diff --git a/package.json b/package.json index 79d68635..1ac6ead4 100644 --- a/package.json +++ b/package.json @@ -3046,7 +3046,6 @@ "description": "ARM Cortex-M GDB Debugger support for VSCode", "devDependencies": { "@stylistic/eslint-plugin": "^2.13.0", - "@types/binary-parser": "^1.5.5", "@types/mocha": "^10.0.10", "@types/node": "16.x", "@types/vscode": "^1.69.0", diff --git a/src/backend/backend.ts b/src/backend/backend.ts index 71b973a2..b4b55e59 100644 --- a/src/backend/backend.ts +++ b/src/backend/backend.ts @@ -184,7 +184,7 @@ export const MIError: MIErrorConstructor = class MIError { public readonly source: string; public constructor(message: string, source: string) { Object.defineProperty(this, 'name', { - get: () => (this.constructor as any).name + get: () => this.constructor.name }); Object.defineProperty(this, 'message', { get: () => message diff --git a/src/backend/disasm.ts b/src/backend/disasm.ts index fa15aaf1..b85788d6 100644 --- a/src/backend/disasm.ts +++ b/src/backend/disasm.ts @@ -298,7 +298,7 @@ export class GdbDisassembler { const regex = RegExp(/^[0-9]+\s+([^\s])\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+([^\r\n]*)/mgi); // Num Enb Low Addr High Addr Attrs // 1 y 0x10000000 0x10100000 flash blocksize 0x200 nocache - while (match = regex.exec(str)) { + while ((match = regex.exec(str))) { const [flag, lowAddr, highAddr, attrsStr] = match.slice(1, 5); if (flag === 'y') { const nHighAddr = parseInt(highAddr); @@ -499,7 +499,7 @@ export class GdbDisassembler { const endAddress = range.qEnd; const validationAddr = range.verify; // To annotate questionable instructions. Too lazy to do on per instruction basis - return new Promise(async (resolve) => { + return new Promise((resolve) => { let iter = 0; const maxTries = Math.ceil((this.maxInstrSize - this.minInstrSize) / this.instrMultiple); const doWork = () => { diff --git a/src/backend/gdb_expansion.ts b/src/backend/gdb_expansion.ts index 42378e50..a253d29e 100644 --- a/src/backend/gdb_expansion.ts +++ b/src/backend/gdb_expansion.ts @@ -1,8 +1,8 @@ import { MINode } from './mi_parse'; -const resultRegex = /^([a-zA-Z_\-][a-zA-Z0-9_\-]*|\[\d+\])\s*=\s*/; -const variableRegex = /^[a-zA-Z_\-][a-zA-Z0-9_\-]*/; -const errorRegex = /^\<.+?\>/; +const resultRegex = /^([a-zA-Z_-][a-zA-Z0-9_-]*|\[\d+\])\s*=\s*/; +const variableRegex = /^[a-zA-Z_-][a-zA-Z0-9_-]*/; +const errorRegex = /^<.+?>/; const referenceStringRegex = /^(0x[0-9a-fA-F]+\s*)"/; const referenceRegex = /^0x[0-9a-fA-F]+/; const nullpointerRegex = /^0x0+\b/; @@ -18,13 +18,13 @@ export function isExpandable(value: string): number { if (value[0] === '{') { return 1; } // object if (value.startsWith('true')) { return 0; } if (value.startsWith('false')) { return 0; } - if (match = nullpointerRegex.exec(value)) { return 0; } - if (match = referenceStringRegex.exec(value)) { return 0; } - if (match = referenceRegex.exec(value)) { return 2; } // reference - if (match = charRegex.exec(value)) { return 0; } - if (match = numberRegex.exec(value)) { return 0; } - if (match = variableRegex.exec(value)) { return 0; } - if (match = errorRegex.exec(value)) { return 0; } + if ((match = nullpointerRegex.exec(value))) { return 0; } + if ((match = referenceStringRegex.exec(value))) { return 0; } + if ((match = referenceRegex.exec(value))) { return 2; } // reference + if ((match = charRegex.exec(value))) { return 0; } + if ((match = numberRegex.exec(value))) { return 0; } + if ((match = variableRegex.exec(value))) { return 0; } + if ((match = errorRegex.exec(value))) { return 0; } return 0; } @@ -86,7 +86,7 @@ export function expandValue(variableCreate: (value: string | object, opts?: { ar return prefix + namespace; }; - function parseTupleOrList() { + function parseTupleOrList(): unknown { value = value.trim(); if (value[0] !== '{') { return undefined; @@ -101,7 +101,7 @@ export function expandValue(variableCreate: (value: string | object, opts?: { ar value = value.substr(3).trim(); if (value[0] === '}') { value = value.substr(1).trim(); - return '<...>' as any; + return '<...>'; } } const eqPos = value.indexOf('='); @@ -136,7 +136,7 @@ export function expandValue(variableCreate: (value: string | object, opts?: { ar if (result) { const results = []; results.push(result); - while (result = parseCommaResult(true)) { + while ((result = parseCommaResult(true))) { results.push(result); } value = value.substr(1).trim(); // } @@ -146,7 +146,7 @@ export function expandValue(variableCreate: (value: string | object, opts?: { ar return undefined; }; - function parsePrimitive() { + function parsePrimitive(): unknown { let primitive: any; let match; value = value.trim(); @@ -158,26 +158,26 @@ export function expandValue(variableCreate: (value: string | object, opts?: { ar } else if (value.startsWith('false')) { primitive = 'false'; value = value.substr(5).trim(); - } else if (match = nullpointerRegex.exec(value)) { + } else if ((match = nullpointerRegex.exec(value))) { primitive = ''; value = value.substr(match[0].length).trim(); - } else if (match = referenceStringRegex.exec(value)) { + } else if ((match = referenceStringRegex.exec(value))) { value = value.substr(match[1].length).trim(); primitive = parseCString(); - } else if (match = referenceRegex.exec(value)) { + } else if ((match = referenceRegex.exec(value))) { primitive = '*' + match[0]; value = value.substr(match[0].length).trim(); - } else if (match = charRegex.exec(value)) { + } else if ((match = charRegex.exec(value))) { primitive = match[1]; value = value.substr(match[0].length - 1); primitive += ' ' + parseCString(); - } else if (match = numberRegex.exec(value)) { + } else if ((match = numberRegex.exec(value))) { primitive = match[0]; value = value.substr(match[0].length).trim(); - } else if (match = variableRegex.exec(value)) { + } else if ((match = variableRegex.exec(value))) { primitive = match[0]; value = value.substr(match[0].length).trim(); - } else if (match = errorRegex.exec(value)) { + } else if ((match = errorRegex.exec(value))) { primitive = match[0]; value = value.substr(match[0].length).trim(); } else { diff --git a/src/backend/linux/console.ts b/src/backend/linux/console.ts deleted file mode 100644 index 066fb035..00000000 --- a/src/backend/linux/console.ts +++ /dev/null @@ -1,24 +0,0 @@ -import * as ChildProcess from 'child_process'; -import * as fs from 'fs'; - -export function spawnTerminalEmulator(preferedEmulator: string): Thenable { - return new Promise((resolve, reject) => { - const ttyFileOutput = '/tmp/vscode-gdb-tty-0' + Math.floor(Math.random() * 100000000).toString(36); - ChildProcess.spawn(preferedEmulator || 'x-terminal-emulator', ['-e', 'sh -c "tty > ' + ttyFileOutput + ' && sleep 4294967294"']); - let it = 0; - const interval = setInterval(() => { - if (fs.existsSync(ttyFileOutput)) { - clearInterval(interval); - const tty = fs.readFileSync(ttyFileOutput).toString('utf8'); - fs.unlink(ttyFileOutput, (err) => { - console.log('Error unlinking terminal session'); - }); - return resolve(tty); - } - it++; - if (it > 500) { - reject(); - } - }, 10); - }); -} diff --git a/src/backend/mi2/mi2.ts b/src/backend/mi2/mi2.ts index 3447b343..91819bd6 100644 --- a/src/backend/mi2/mi2.ts +++ b/src/backend/mi2/mi2.ts @@ -32,7 +32,7 @@ export function escape(str: string) { return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); } -const nonOutput = /^(?:\d*|undefined)[\*\+\=]|[\~\@\&\^]/; +const nonOutput = /^(?:\d*|undefined)[*+=]|[~@&^]/; const gdbMatch = /(?:\d*|undefined)\(gdb\)/; const numRegex = /\d+/; @@ -908,7 +908,7 @@ export class MI2 extends EventEmitter implements IBackend { const thFr = ((threadId !== undefined) && (frameId !== undefined)) ? `--thread ${threadId} --frame ${frameId}` : ''; const createResp = await this.sendCommand(`var-create ${thFr} ${name} ${scope} "${expression}"`); - let overrideVal = null; + let overrideVal: string = null; if (fmt && name !== '-') { const formatResp = await this.sendCommand(`var-set-format ${name} ${MI2.FORMAT_SPEC_MAP[fmt]}`); overrideVal = formatResp.result('value'); diff --git a/src/backend/mi_parse.ts b/src/backend/mi_parse.ts index 49bb8d94..45e2a24d 100644 --- a/src/backend/mi_parse.ts +++ b/src/backend/mi_parse.ts @@ -62,7 +62,7 @@ export class MINode implements MIInfo { if (!start) { return undefined; } - const pathRegex = /^\.?([a-zA-Z_\-][a-zA-Z0-9_\-]*)/; + const pathRegex = /^\.?([a-zA-Z_-][a-zA-Z0-9_-]*)/; const indexRegex = /^\[(\d+)\](?:$|\.)/; path = path.trim(); if (!path) { return start; } @@ -136,11 +136,11 @@ export class MINode implements MIInfo { } const tokenRegex = /^\d+/; -const outOfBandRecordRegex = /^(?:(\d*|undefined)([\*\+\=])|([\~\@\&]))/; +const outOfBandRecordRegex = /^(?:(\d*|undefined)([*+=])|([~@&]))/; const resultRecordRegex = /^(\d*)\^(done|running|connected|error|exit)/; const newlineRegex = /^\r\n?/; const endRegex = /^\(gdb\)\r\n?/; -const variableRegex = /^([a-zA-Z_\-][a-zA-Z0-9_\-]*)/; +const variableRegex = /^([a-zA-Z_-][a-zA-Z0-9_-]*)/; const asyncClassRegex = /^(.*?),/; export function parseMI(output: string): MINode { @@ -195,7 +195,7 @@ export function parseMI(output: string): MINode { remaining = remaining.substr(1); stringEnd++; } - let str; + let str: string; try { str = parseString(output.substr(0, stringEnd)); } catch (e) { @@ -205,7 +205,7 @@ export function parseMI(output: string): MINode { return str; }; - function parseTupleOrList() { + function parseTupleOrList(): unknown[] { if (output[0] !== '{' && output[0] !== '[') { return undefined; } @@ -233,7 +233,7 @@ export function parseMI(output: string): MINode { if (result) { const results = []; results.push(result); - while (result = parseCommaResult()) { + while ((result = parseCommaResult())) { results.push(result); } output = output.substr(1); // } @@ -243,7 +243,7 @@ export function parseMI(output: string): MINode { return undefined; }; - function parseValue() { + function parseValue(): unknown { if (output[0] === '"') { return parseCString(); } else if (output[0] === '{' || output[0] === '[') { @@ -281,7 +281,7 @@ export function parseMI(output: string): MINode { let match; - while (match = outOfBandRecordRegex.exec(output)) { + while ((match = outOfBandRecordRegex.exec(output))) { output = output.substr(match[0].length); if (match[1] && token === undefined && match[1] !== 'undefined') { token = parseInt(match[1]); @@ -297,7 +297,7 @@ export function parseMI(output: string): MINode { output: [] }; let result; - while (result = parseCommaResult()) { + while ((result = parseCommaResult())) { asyncRecord.output.push(result); } outOfBandRecord.push(asyncRecord); @@ -313,7 +313,7 @@ export function parseMI(output: string): MINode { output = output.replace(newlineRegex, ''); } - if (match = resultRecordRegex.exec(output)) { + if ((match = resultRecordRegex.exec(output))) { output = output.substr(match[0].length); if (match[1] && token === undefined) { token = parseInt(match[1]); @@ -323,7 +323,7 @@ export function parseMI(output: string): MINode { results: [] }; let result; - while (result = parseCommaResult()) { + while ((result = parseCommaResult())) { resultRecords.results.push(result); } diff --git a/src/backend/symbols.ts b/src/backend/symbols.ts index 74f2f676..5bda19f6 100644 --- a/src/backend/symbols.ts +++ b/src/backend/symbols.ts @@ -9,7 +9,7 @@ import { GDBDebugSession } from '../gdb'; import { hexFormat } from '../frontend/utils'; import { MINode } from './mi_parse'; -const OBJDUMP_SYMBOL_RE = RegExp(/^([0-9a-f]{8})\s([lg\ !])([w\ ])([C\ ])([W\ ])([I\ ])([dD\ ])([FfO\ ])\s(.*?)\t([0-9a-f]+)\s(.*)$/); +const OBJDUMP_SYMBOL_RE = RegExp(/^([0-9a-f]{8})\s([lg !])([w ])([C ])([W ])([I ])([dD ])([FfO ])\s(.*?)\t([0-9a-f]+)\s(.*)$/); const NM_SYMBOL_RE = RegExp(/^([0-9a-f]+).*\t(.+):[0-9]+/); // For now, we only need two things const debugConsoleLogging = false; const TYPE_MAP: { [id: string]: SymbolType } = { @@ -305,7 +305,7 @@ export class SymbolTable { }); } - private rttSymbol; + private rttSymbol: SymbolInformation; public readonly rttSymbolName = '_SEGGER_RTT'; private addSymbol(sym: SymbolInformation) { if ((sym.length === 0) && /^\$[atdbfpm]$/.test(sym.name)) { diff --git a/src/bmp.ts b/src/bmp.ts index 878780f8..ebe07cfd 100644 --- a/src/bmp.ts +++ b/src/bmp.ts @@ -64,7 +64,7 @@ export class BMPServerController extends EventEmitter implements GDBServerContro } public attachCommands(): string[] { - const commands = []; + const commands: string[] = []; return commands; } @@ -76,7 +76,7 @@ export class BMPServerController extends EventEmitter implements GDBServerContro } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled) { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); diff --git a/src/common.ts b/src/common.ts index 6887ba47..ba16c945 100644 --- a/src/common.ts +++ b/src/common.ts @@ -10,7 +10,7 @@ import * as stream from 'stream'; import * as path from 'path'; import { GDBDebugSession } from './gdb'; import { CreateReadStreamOptions } from 'fs/promises'; -const readline = require('readline'); +import * as readline from 'readline'; export enum ADAPTER_DEBUG_MODE { NONE = 'none', @@ -499,7 +499,7 @@ export function createPortName(procNum: number, prefix: string = 'gdbPort'): str } export function getAnyFreePort(preferred: number): Promise { - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { function findFreePorts() { const portFinderOpts = { min: 60000, max: 62000, retrieve: 1, consecutive: false }; TcpPortScanner.findFreePorts(portFinderOpts, GDBServer.LOCALHOST).then((ports) => { @@ -569,7 +569,7 @@ export function toStringDecHexOctBin(val: number /* should be an integer */): st export function parseHostPort(hostPort: string) { let port: number; let host = '127.0.0.1'; - const match = hostPort.match(/(.*)\:([0-9]+)/); + const match = hostPort.match(/(.*):([0-9]+)/); if (match) { host = match[1] ? match[1] : host; port = parseInt(match[2], 10); @@ -706,7 +706,7 @@ export class HrTimer { // where will need to put the string in quotes as a precaution. This is more a printing // aid rather an using for an API export function quoteShellAndCmdChars(s): string { - const quote = /[\s\"\*\[\]!@#$%^&*\(\)\\:]/g.test(s) ? '"' : ''; + const quote = /[\s"*[\]!@#$%^&*()\\:]/g.test(s) ? '"' : ''; s = s.replace(/"/g, '\\"').replace(/\\/g, '\\\\'); return quote + s.replace(/"/g, '\\"') + quote; } @@ -865,7 +865,7 @@ export class SpawnLineReader extends EventEmitter { const rl = readline.createInterface({ input: rStream, crlfDelay: Infinity, - console: false + terminal: false, }); rl.on('line', (line) => { if (this.callback) { diff --git a/src/docgen.ts b/src/docgen.ts index d1b4ee8d..7da363d1 100644 --- a/src/docgen.ts +++ b/src/docgen.ts @@ -13,10 +13,10 @@ function handleObject(obj: any, prop: string, appliesTo, stream: fs.WriteStream) } } -function getType(obj: any) { +function getType(obj: any): string { const pipe = ' | '; if (Array.isArray(obj.type)) { - return obj.type.join(pipe); + return obj.type.join(pipe) as string; } if (obj.properties) { return 'object'; @@ -34,7 +34,7 @@ function getType(obj: any) { } else if (obj.anyOf || obj.oneOf) { return newFunction(obj); } else if (obj.type) { - return obj.type; + return obj.type as string; } else { return '??'; } diff --git a/src/frontend/configprovider.ts b/src/frontend/configprovider.ts index 50b9d889..063f26ad 100644 --- a/src/frontend/configprovider.ts +++ b/src/frontend/configprovider.ts @@ -302,7 +302,7 @@ export class CortexDebugConfigurationProvider implements vscode.DebugConfigurati config.executable = path.normalize(exe).replace(/\\/g, '/'); } const def = defSymbolFile(config.executable); - const symFiles: SymbolFile[] = config.symbolFiles?.map((v) => typeof v === 'string' ? defSymbolFile(v) : v) || [def]; + const symFiles: SymbolFile[] = config.symbolFiles?.map((v) => typeof v === 'string' ? defSymbolFile(v) : v as SymbolFile) || [def]; if (!symFiles || (symFiles.length === 0)) { vscode.window.showWarningMessage('No "executable" or "symbolFiles" specified. We will try to run program without symbols'); } else { diff --git a/src/frontend/cortex_debug_session.ts b/src/frontend/cortex_debug_session.ts index 72d94506..3fbc755a 100644 --- a/src/frontend/cortex_debug_session.ts +++ b/src/frontend/cortex_debug_session.ts @@ -27,7 +27,7 @@ export class CDebugSession { } } - public getRoot() { + public getRoot(): CDebugSession { return this.parent && this.parent.parent ? this.parent.getRoot() : this; } diff --git a/src/frontend/extension.ts b/src/frontend/extension.ts index 2c018e75..a9902501 100644 --- a/src/frontend/extension.ts +++ b/src/frontend/extension.ts @@ -23,7 +23,6 @@ import { GDBServerConsole } from './server_console'; import { CDebugSession, CDebugChainedSessionItem } from './cortex_debug_session'; import { ServerConsoleLog } from '../backend/server'; -const commandExistsSync = require('command-exists').sync; interface SVDInfo { expression: RegExp; path: string; @@ -244,7 +243,7 @@ export class CortexDebugExtension { { title: 'Cancel' } - ).then(async (v) => { + ).then((v) => { if (v && (v.title === installExt)) { vscode.commands.executeCommand('workbench.extensions.installExtension', 'mcu-debug.memory-view'); } @@ -253,7 +252,7 @@ export class CortexDebugExtension { } private examineMemoryLegacy() { - function validateValue(address) { + function validateValue(address: string) { if (/^0x[0-9a-f]{1,8}$/i.test(address)) { return address; } else if (/^[0-9]+$/i.test(address)) { @@ -649,7 +648,7 @@ export class CortexDebugExtension { return def; } - private getCurrentArgs(session: vscode.DebugSession): ConfigurationArguments | vscode.DebugConfiguration { + private getCurrentArgs(session: vscode.DebugSession): ConfigurationArguments { if (!session) { session = vscode.debug.activeDebugSession; if (!session || (session.type !== 'cortex-debug')) { @@ -658,19 +657,14 @@ export class CortexDebugExtension { } const ourSession = CDebugSession.FindSession(session); if (ourSession) { - return ourSession.config; + return ourSession.config as ConfigurationArguments; } - return session.configuration; - } - - private getCurrentProp(session: vscode.DebugSession, prop: string) { - const args = this.getCurrentArgs(session); - return args ? args[prop] : undefined; + return session.configuration as unknown as ConfigurationArguments; } // Assuming 'session' valid and it a cortex-debug session private isDebugging(session: vscode.DebugSession) { - const noDebug = this.getCurrentProp(session, 'noDebug'); + const { noDebug } = this.getCurrentArgs(session); return (noDebug !== true); // If it is exactly equal to 'true' we are doing a 'run without debugging' } diff --git a/src/frontend/memory_content_provider.ts b/src/frontend/memory_content_provider.ts index 3cfc2f49..dcefea78 100644 --- a/src/frontend/memory_content_provider.ts +++ b/src/frontend/memory_content_provider.ts @@ -187,7 +187,7 @@ export class MemoryContentProvider implements vscode.TextDocumentContentProvider let endPos = this.getPosition(endOffset, ascii); endPos = new vscode.Position(endPos.line, endPos.character + (ascii ? 1 : 2)); - const ranges = []; + const ranges: vscode.Range[] = []; const firstOffset = ascii ? this.firstAsciiPos : this.firstBytePos; const lastOffset = ascii ? this.lastAsciiPos : this.lastBytePos; for (let i = startPos.line; i <= endPos.line; ++i) { diff --git a/src/frontend/memreadutils.ts b/src/frontend/memreadutils.ts index bca69896..8eb552b9 100644 --- a/src/frontend/memreadutils.ts +++ b/src/frontend/memreadutils.ts @@ -36,7 +36,7 @@ export class MemReadUtils { }); return new Promise(async (resolve, reject) => { - const results = await Promise.all(promises.map((p) => p.catch((e) => e))); + const results = await Promise.all(promises.map((p) => p.catch((e) => {}))); const errs: string[] = []; results.map((e) => { if (e instanceof Error) { diff --git a/src/frontend/pty.ts b/src/frontend/pty.ts index 0e4b6f0f..3c43702b 100644 --- a/src/frontend/pty.ts +++ b/src/frontend/pty.ts @@ -1,4 +1,4 @@ -import EventEmitter = require('events'); +import { EventEmitter } from 'events'; import * as os from 'os'; import * as vscode from 'vscode'; import { ResettableTimeout, TerminalInputMode } from '../common'; diff --git a/src/frontend/rtos/rtos-chibios.ts b/src/frontend/rtos/rtos-chibios.ts deleted file mode 100644 index 909b370f..00000000 --- a/src/frontend/rtos/rtos-chibios.ts +++ /dev/null @@ -1,374 +0,0 @@ -import * as vscode from 'vscode'; -import { DebugProtocol } from '@vscode/debugprotocol'; -import * as RTOSCommon from './rtos-common'; -import { hexFormat } from '../utils'; -import { HrTimer, toStringDecHexOctBin } from '../../common'; - -// We will have two rows of headers for ChibiOS and the table below describes -// the columns headers for the two rows and the width of each column as a fraction -// of the overall space. -enum DisplayFields { - ID, - THREAD_DESCRIPTION, - FLAGS, - REFS, - TIME, - WTOBJP, - STATS_N, - STATS_WORST, - STATS_CUMULATIVE, - STACK_TOP, - STACK_END, - STACK_MIN_FREE -} - -enum chThreadState { - READY = 0, - CURRENT, - STARTED, - SUSPENDED, - QUEUED, - WTSEM, - WTMTX, - WTCOND, - SLEEPING, - WTEXIT, - WTOREVT, - WTANDEVT, - SNDMSGQ, - SNDMSG, - WTMSG, - FINAL, - UNKNOWN, - _SIZE -} - -const colNumType = RTOSCommon.ColTypeEnum.colTypeNumeric; -const ChibiOSItems: { [key: string]: RTOSCommon.DisplayColumnItem } = {}; - -ChibiOSItems[DisplayFields[DisplayFields.ID]] = { width: 2, headerRow1: '', headerRow2: 'id', colType: colNumType }; -ChibiOSItems[DisplayFields[DisplayFields.THREAD_DESCRIPTION]] = { width: 14, headerRow1: '', headerRow2: 'Thread', colGapBefore: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.FLAGS]] = { width: 2, headerRow1: '', headerRow2: 'Flags', colGapAfter: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.REFS]] = { width: 4, headerRow1: '', headerRow2: 'Refs', colGapBefore: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.TIME]] = { width: 3, headerRow1: '', headerRow2: 'Time', colType: colNumType }; -ChibiOSItems[DisplayFields[DisplayFields.WTOBJP]] = { width: 4, headerRow1: 'Wait', headerRow2: 'Obj/Msg', colGapBefore: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.STATS_N]] = { width: 4, headerRow1: 'Stats', headerRow2: 'Switches', colType: colNumType }; -ChibiOSItems[DisplayFields[DisplayFields.STATS_WORST]] = { width: 4, headerRow1: '', headerRow2: 'Worst Path', colType: colNumType }; -ChibiOSItems[DisplayFields[DisplayFields.STATS_CUMULATIVE]] = { width: 4, headerRow1: '', headerRow2: 'Cumulative Time', colType: colNumType }; -ChibiOSItems[DisplayFields[DisplayFields.STACK_TOP]] = { width: 4, headerRow1: 'Stack', headerRow2: 'Top', colGapBefore: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.STACK_END]] = { width: 4, headerRow1: '', headerRow2: 'End', colGapBefore: 1 }; -ChibiOSItems[DisplayFields[DisplayFields.STACK_MIN_FREE]] = { width: 3, headerRow1: '', headerRow2: 'Min. free', colType: colNumType }; - -const DisplayFieldNames: string[] = Object.keys(ChibiOSItems); - -function getThreadStateName(s: number): string { - if ((s as chThreadState) < chThreadState._SIZE) { - return chThreadState[s]; - } - - return chThreadState[chThreadState._SIZE - 1]; -} - -function getCString(s: string, nullValue: string = ''): string { - const matchName = s.match(/"([^*]*)"$/); - return matchName ? matchName[1] : nullValue; -} - -function getNumber(s: string): number { - return (s ? parseInt(s) : 0); -} - -function getNumberNVL(s: string, nullValue: number): number { - return (s ? parseInt(s) : nullValue); -} - -function nvl(v: any, nullValue: any) { - if ((v === undefined) || (v === null)) { - return nullValue; - } - - return v; -} - -export class RTOSChibiOS extends RTOSCommon.RTOSBase { - // We keep a bunch of variable references (essentially pointers) that we can use to query for values - // Since all of them are global variable, we only need to create them once per session. These are - // similar to Watch/Hover variables - private chRlistCurrent: RTOSCommon.RTOSVarHelper; - private chReglist: RTOSCommon.RTOSVarHelper; - - private rlistCurrent: number; - private threadOffset: number; - private smp: boolean = false; - - private stale: boolean; - private foundThreads: RTOSCommon.RTOSThreadInfo[] = []; - private finalThreads: RTOSCommon.RTOSThreadInfo[] = []; - private timeInfo: string; - private helpHtml: string = undefined; - - // Need to do a TON of testing for stack growing the other direction - private stackIncrements = -1; - - private readonly maxThreads = 1024; - - constructor(public session: vscode.DebugSession) { - super(session, 'ChibiOS'); - } - - public async tryDetect(useFrameId: number): Promise { - this.progStatus = 'stopped'; - try { - if (this.status === 'none') { - // We only get references to all the interesting variables. Note that any one of the following can fail - // and the caller may try again until we know that it definitely passed or failed. Note that while we - // re-try everything, we do remember what already had succeeded and don't waste time trying again. That - // is how this.getVarIfEmpty() works - try { - this.chReglist = await this.getVarIfEmpty(this.chReglist, useFrameId, '(uint32_t) &ch_system.reglist', false); - this.smp = true; - } catch (e) { - this.chReglist = await this.getVarIfEmpty(this.chReglist, useFrameId, '(uint32_t) &ch0.reglist', false); - } - - this.chRlistCurrent = await this.getVarIfEmpty(this.chRlistCurrent, useFrameId, 'ch0.rlist.current', false); - this.threadOffset = parseInt(await this.getExprVal('((char *)(&((thread_t *)0)->rqueue) - (char *)0)', useFrameId)); - this.status = 'initialized'; - } - return this; - } catch (e) { - if (e instanceof RTOSCommon.ShouldRetry) { - console.error(e.message); - } else { - this.status = 'failed'; - this.failedWhy = e; - } - return this; - } - } - - protected createHmlHelp(th: RTOSCommon.RTOSThreadInfo, thInfo: object) { - if (this.helpHtml === undefined) { - this.helpHtml = ''; - // TODO: add help html - } - } - - public refresh(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (this.progStatus !== 'stopped') { - resolve(); - return; - } - - const timer = new HrTimer(); - this.stale = true; - this.timeInfo = (new Date()).toISOString(); - this.foundThreads = []; - this.finalThreads = []; - - this.chRlistCurrent.getValue(frameId).then(async (rlistCurrentStr) => { - try { - this.rlistCurrent = getNumberNVL(rlistCurrentStr, 0); - - if (0 !== this.rlistCurrent) { - // TODO: add global info: panic message, irs cnt... - - await this.getThreadInfo(this.chReglist, frameId); - this.finalThreads = [...this.foundThreads]; - } else { - this.finalThreads = []; - } - - this.stale = false; - this.timeInfo += ' in ' + timer.deltaMs() + ' ms'; - resolve(); - } catch (e) { - resolve(); - console.error('ChibiOS.refresh() failed: ', e); - } - }, (reason) => { - resolve(); - console.error('ChibiOS.refresh() failed: ', reason); - }); - }); - } - - private getThreadInfo(reglist: RTOSCommon.RTOSVarHelper, frameId: number): Promise { - return new Promise((resolve, reject) => { - if (!reglist) { - resolve(); - return; - } - - if (this.progStatus !== 'stopped') { - reject(new Error('Busy')); - return; - } - - reglist.getValue(frameId).then(async (obj) => { - try { - const reglistHeader = obj ? parseInt(obj) : 0; - - if (reglistHeader && 0 !== reglistHeader) { - let nextEntry = await this.getExprValChildrenObj('(ch_queue_t *)' + reglistHeader, frameId); - let currentReglist = getNumber(nextEntry['next-val']); - let i = 0; - - // TODO: add reglist integrity check - - do { - const currentThreadAddr = currentReglist - this.threadOffset; - const currentThread = await this.getExprValChildrenObj('(thread_t *) ' + currentThreadAddr, frameId); - const currentThreadPqueue = await this.getExprValChildrenObj('((thread_t *) ' + currentThreadAddr + ')->hdr.pqueue', frameId); - const currentThreadStateDetails = await this.getVarChildrenObj(currentThread['u-ref'], 'u'); - const currentThreadStats = await this.getVarChildrenObj(currentThread['stats-ref'], 'stats'); - - const threadRunning = (currentThreadAddr === this.rlistCurrent); - const threadName = getCString(currentThread['name-val'], ''); - const threadState = getThreadStateName(getNumberNVL(currentThread['state-val'], chThreadState._SIZE)); - const threadFlags = getNumberNVL(currentThread['flags-val'], 0); - const threadPrio = getNumberNVL(currentThreadPqueue['prio-val'], 0); - const threadRefs = getNumberNVL(currentThread['refs-val'], 0); - const threadTime = nvl(currentThread['time-val'], '-'); - const threadWaitForObj = currentThreadStateDetails['wtobjp-val']; - const threadStatsN = currentThreadStats['n-val']; - const threadStatsWorst = currentThreadStats['worst-val']; - const threadStatsCumulative = currentThreadStats['cumulative-val']; - - const stackInfo = await this.getStackInfo(currentThread); - - let stackMinFree: string; - - if (stackInfo.stackPeak) { - if (stackInfo.stackPeak === -1) { - stackMinFree = 'overflow'; - } else { - stackMinFree = stackInfo.stackPeak.toString(); - } - } else { - stackMinFree = '-'; - } - - i++; - - const display: { [key: string]: RTOSCommon.DisplayRowItem } = {}; - const mySetter = (x: DisplayFields, text: string, value?: any) => { - display[DisplayFieldNames[x]] = { text, value }; - }; - - mySetter(DisplayFields.ID, i.toString()); - mySetter(DisplayFields.THREAD_DESCRIPTION, - threadName + '@' + hexFormat(currentThreadAddr) + ' ' + threadState + ' [P:' + threadPrio + ']'); - mySetter(DisplayFields.FLAGS, hexFormat(threadFlags, 2)); - mySetter(DisplayFields.REFS, hexFormat(threadRefs)); - mySetter(DisplayFields.TIME, threadTime); - mySetter(DisplayFields.WTOBJP, hexFormat(threadWaitForObj)); - mySetter(DisplayFields.STATS_N, threadStatsN); - mySetter(DisplayFields.STATS_WORST, threadStatsWorst); - mySetter(DisplayFields.STATS_CUMULATIVE, threadStatsCumulative); - mySetter(DisplayFields.STACK_TOP, stackInfo.stackTop !== 0 ? hexFormat(stackInfo.stackTop) : '-'); - mySetter(DisplayFields.STACK_END, stackInfo.stackEnd !== 0 ? hexFormat(stackInfo.stackEnd) : '-'); - mySetter(DisplayFields.STACK_MIN_FREE, stackMinFree); - - const threadInfo: RTOSCommon.RTOSThreadInfo = { - display: display, stackInfo: stackInfo, running: threadRunning - }; - - this.foundThreads.push(threadInfo); - this.createHmlHelp(threadInfo, currentThread); - - nextEntry = await this.getExprValChildrenObj('(ch_queue_t *)' + currentReglist, frameId); - currentReglist = nextEntry['next-val'] ? parseInt(nextEntry['next-val']) : 0; - console.log('Got', currentReglist); - } while (reglistHeader !== currentReglist); - console.log('Done'); - } else { - // TODO: add error message - reglist header not found - } - - resolve(); - } catch (e) { - console.log('ChibiOS.getThreadInfo() error', e); - } - }, (e) => { - reject(e); - }); - }); - } - - protected async getStackInfo(thInfo: any) { - const stackInfo: RTOSCommon.RTOSStackInfo = { - stackStart: 0, - stackTop: 0, - stackPeak: null - }; - - const currentThreadCtx = await this.getVarChildrenObj(thInfo['ctx-ref'], 'ctx'); - const currentThreadCtxRegs = await this.getVarChildrenObj(currentThreadCtx['sp-ref'], 'sp'); - - stackInfo.stackTop = getNumberNVL('r13-val' in currentThreadCtxRegs ? currentThreadCtxRegs['r13-val'] : currentThreadCtx['sp-val'], 0); - stackInfo.stackEnd = getNumberNVL(thInfo['wabase-val'], 0); - - if (stackInfo.stackTop === 0 || stackInfo.stackEnd === 0) { - stackInfo.stackFree = null; - stackInfo.stackPeak = null; - } else if (stackInfo.stackTop < stackInfo.stackEnd) { - stackInfo.stackFree = -1; - stackInfo.stackPeak = -1; - } else { - stackInfo.stackFree = stackInfo.stackTop - stackInfo.stackEnd; - - /* check stack peak */ - try { - const stackData = await this.session.customRequest( - 'readMemory', - { - memoryReference: hexFormat(Math.min(stackInfo.stackTop, stackInfo.stackEnd)), - count: Math.abs(stackInfo.stackTop - stackInfo.stackEnd) - } - ); - - const buf = Buffer.from(stackData.data, 'base64'); - const bytes = new Uint8Array(buf); - - stackInfo.stackPeak = 0; - while ((stackInfo.stackPeak < bytes.length) && (bytes[stackInfo.stackPeak] === 0x55)) { - stackInfo.stackPeak++; - } - } catch (e) { - console.log(e); - } - } - - return stackInfo; - } - - public lastValidHtmlContent: RTOSCommon.HtmlInfo = { html: '', css: '' }; - public getHTML(): RTOSCommon.HtmlInfo { - const htmlContent: RTOSCommon.HtmlInfo = { - html: '', css: '' - }; - // WARNING: This stuff is super fragile. Once we know how this works, then we should refactor this - if (this.status === 'none') { - htmlContent.html = '

RTOS not yet fully initialized. Will occur next time program pauses

\n'; - return htmlContent; - } else if (this.stale) { - const lastHtmlInfo = this.lastValidHtmlContent; - htmlContent.html = '

Unable to collect full RTOS information.

\n' + lastHtmlInfo.html; - htmlContent.css = lastHtmlInfo.css; - return htmlContent; - } else if (this.finalThreads.length === 0) { - htmlContent.html = `

No ${this.name} threads detected, perhaps RTOS not yet initialized or tasks yet to be created!

\n`; - return htmlContent; - } - - const ret = this.getHTMLCommon(DisplayFieldNames, ChibiOSItems, this.finalThreads, this.timeInfo); - htmlContent.html = ret.html + (this.helpHtml || ''); - htmlContent.css = ret.css; - - this.lastValidHtmlContent = htmlContent; - // console.log(this.lastValidHtmlContent.html); - return this.lastValidHtmlContent; - } -} diff --git a/src/frontend/rtos/rtos-common.ts b/src/frontend/rtos/rtos-common.ts deleted file mode 100644 index 05831661..00000000 --- a/src/frontend/rtos/rtos-common.ts +++ /dev/null @@ -1,508 +0,0 @@ -import * as vscode from 'vscode'; -import { DebugProtocol } from '@vscode/debugprotocol'; - -export const traceVars = false; - -export interface RTOSStackInfo { - stackStart: number; - stackTop: number; - stackEnd?: number; - stackSize?: number; - stackUsed?: number; - stackFree?: number; - stackPeak?: number; - bytes?: Uint8Array; -} - -// It is a bitfield because Link and Collapse are oddballs and do not imply right/left/center justified -// Please follow the conventions so that the look and feel is consistent across RTOSes contributed by -// multiple folks -// Note: The table we produce should still look good when expanding/shrinking in the horz direction.. Things -// should not run into each other. Not easy, but do your best and test it -export enum ColTypeEnum { - /* eslint-disable @stylistic/no-multi-spaces */ - colTypeNormal = 0, // Will be left justified. Use for Text fields, fixed width hex values, etc. - colTypePercentage = 1 << 0, // Will be centered with a % bar - colTypeNumeric = 1 << 1, // Will be right justified - colTypeLink = 1 << 2, // TODO: mark it as a link to do something additional. Not totally functional - colTypeCollapse = 1 << 3 // Items will be collapsible - /* eslint-enable */ -} - -export interface DisplayColumnItem { - width: number; - headerRow1: string; - headerRow2: string; - fieldName?: string; - colType?: ColTypeEnum; - colSpaceFillThreshold?: number; // This makes it fixed width (padded with spaces) unless value width is larger - colGapBefore?: number; // Use if the field is going to be left justified or fixed width - colGapAfter?: number; // Use if the field is going to be right justified or fixed width -} - -export interface DisplayRowItem { - text: string; - value?: any; -} - -export interface RTOSThreadInfo { - display: { [key: string]: DisplayRowItem }; // Each key is the string of the enum value - stackInfo: RTOSStackInfo; - running?: boolean; -} - -export interface HtmlInfo { - html: string; - css: string; -} - -export class ShouldRetry extends Error { - constructor(str: string) { - super('Busy or Error for expr ' + str); - } -} - -export abstract class RTOSBase { - public progStatus: 'started' | 'stopped' | 'running' | 'exited'; - public status: 'failed' | 'initialized' | 'none'; - public className: string; - protected exprValues: Map = new Map(); - protected failedWhy: any; // For debug - - protected constructor(public session: vscode.DebugSession, public readonly name) { - this.status = 'none'; - this.progStatus = 'started'; - this.className = this.name.replace(new RegExp('[^_a-zA-Z0-9-]', 'g'), ''); // Remove invalid CSS chars from name - } - - // - // When the promise resolves, check the 'status' property which starts out as 'none' - // 1. status set to 'initialized' to indicate RTOS has been detected - // 2. Could not detect an RTOS because session is busy (caller to try again). Status is unmodified. This may - // happen because user did a continue or a step - // 3. Failed to detect an RTOS in which case, status is 'failed' and the host should no longer try use this instance. - // - public abstract tryDetect(useFrameId: number): Promise; - - private static reqCounter = 0; - public customRequest(cmd: string, arg: any, opt?: boolean): Thenable { - return new Promise(async (resolve, reject) => { - const c = ++RTOSBase.reqCounter; - if (traceVars) { - console.log(`${c} RTOS: request -> ${opt ? 'opt' : ''} ${cmd} ${JSON.stringify(arg)}`); - } - try { - const result = await this.session.customRequest(cmd, arg); - if (traceVars) { - console.log(`${c} RTOS: result <- ${JSON.stringify(result)}`); - } - resolve(result); - } catch (e) { - if (traceVars) { - console.log(`${c} RTOS: exception <- ${e}`); - } - reject(e); - } - }); - } - - public onStopped(frameId: number): Promise { - this.progStatus = 'stopped'; - return this.refresh(frameId); - } - - public onContinued(): void { - this.progStatus = 'running'; - } - - public onExited(): void { - this.progStatus = 'exited'; - } - - // Refresh the RTOS structures - public abstract refresh(frameId: number): Promise; - - // Return Html Info (html + style element content) that represents the RTOS state. - // Ideally, it should return a grid/table that is hosted in an upper level structure - public abstract getHTML(): HtmlInfo; - - // UTILITY functions for all RTOSes - protected async evalForVarRef( - prevValue: number, useFrameId: number, expr: string, optional?: boolean): Promise { - if (prevValue !== undefined) { - return prevValue; - } else if (this.progStatus !== 'stopped') { - return undefined; - } - const arg: DebugProtocol.EvaluateArguments = { - frameId: useFrameId, - expression: expr, - context: 'hover' - }; - const result = await this.customRequest('evaluate', arg, optional); - if (!result || (!optional && (result.variablesReference === 0))) { - throw new Error(`Failed to evaluate ${expr}`); - } - return result ? result.variablesReference : 0; - } - - protected async evalForVarValue( - useFrameId: number, expr: string): Promise { - const arg: DebugProtocol.EvaluateArguments = { - frameId: useFrameId, - expression: expr, - context: 'hover' - }; - const result = await this.customRequest('evaluate', arg); - const ret = result?.result; - return ret; - } - - protected getVarChildren(varRef: number, dbg: string): Promise { - return new Promise((resolve, reject) => { - if (this.progStatus !== 'stopped') { - return reject(new Error(`busy, failed to evaluate ${dbg}`)); - } else { - const arg: DebugProtocol.VariablesArguments = { - variablesReference: varRef - }; - this.customRequest('variables', arg).then((result: any) => { - if (!result || !result.variables || !result.variables.length) { - reject(Error(`Failed to evaluate variable ${arg.variablesReference} ${dbg}`)); - } else { - resolve(result.variables); - } - }, (e) => { - reject(e); - }); - } - }); - } - - protected getVarChildrenObj(varRef: number, dbg: string): Promise { - return new Promise((resolve, reject) => { - if ((varRef === undefined) || (varRef === 0)) { - resolve(null); - return; - } - this.getVarChildren(varRef, dbg).then((vars) => { - const obj = RTOSVarHelper.varsToObj(vars); - resolve(obj); - }, (e) => { - reject(e); - }); - }); - } - - // - // It will return (or throw) - // * The previous value if was already defined or session is busy. If session was busy, you can try again - // * If 'expr' is evaluated and a value found, then return an instance of `RTOSVarHelper` - // * If 'expr' is evaluated and but a value NOT found, then (should not attempt re-tries) - // * If optional, return null - // * If not optional, Throws an exception - // - // This function may have to be adjusted for other debuggers, for when there is an error. We know what our - // behavior is fairly good idea of what cppdbg does - protected async getVarIfEmpty(prev: RTOSVarHelper, fId: number, expr: string, opt?: boolean): Promise { - try { - if ((prev !== undefined) || (this.progStatus !== 'stopped')) { - return prev; - } - const tmp = new RTOSVarHelper(expr, this); - const success = await tmp.tryInitOrUpdate(fId, opt); - if (!success || (isNullOrUndefined(tmp.value) && (this.progStatus !== 'stopped'))) { - // It is most likely busy .... try again. Program status can change while we are querying - throw new ShouldRetry(expr); - } - if (isNullOrUndefined(tmp.value)) { - if (!opt) { - if (traceVars) { - console.error(`1. Throwing exception for variable ${expr}`); - } - throw Error(`${expr} not found`); - } - return null; - } - return tmp; - } catch (e) { - if (e instanceof ShouldRetry) { - throw e; - } - if (opt && (this.progStatus === 'stopped')) { - return null; // This optional item will never succeed. Return null to avoid retries - } - if (traceVars) { - console.error(`2. Throwing exception for variable ${expr}`); - } - throw new Error(`Failed to evaluate ${expr}: ${e.toString()}`); - } - } - - protected async getExprVal(expr: string, frameId: number): Promise { - let exprVar = this.exprValues.get(expr); - if (!exprVar) { - exprVar = new RTOSVarHelper(expr, this); - } - return exprVar.getValue(frameId); - } - - protected async getExprValChildren(expr: string, frameId: number): Promise { - let exprVar = this.exprValues.get(expr); - if (!exprVar) { - exprVar = new RTOSVarHelper(expr, this); - } - return exprVar.getVarChildren(frameId); - } - - protected getExprValChildrenObj(expr: string, frameId: number): Promise { - return new Promise(async (resolve, reject) => { - try { - const vars = await this.getExprValChildren(expr, frameId); - const obj = RTOSVarHelper.varsToObj(vars); - resolve(obj); - } catch (e) { - resolve(e); - } - }); - } - - protected getHTMLCommon( - displayFieldNames: string[], - RTOSDisplayColumn: { [key: string]: DisplayColumnItem }, - allThreads: RTOSThreadInfo[], - timeInfo: string): HtmlInfo { - const getAlignClasses = (key: string) => { - const colType: ColTypeEnum = RTOSDisplayColumn[key].colType; - let ret = ''; - if (colType & ColTypeEnum.colTypePercentage) { - ret += ' centerAlign'; - } - if (colType & ColTypeEnum.colTypeNumeric) { - ret += ' rightAlign'; - } - return ret; - }; - - const padText = (key: string, txt: string) => { - let needWSPreserve = false; - if ((RTOSDisplayColumn[key].colSpaceFillThreshold !== undefined) && (txt.length > 0)) { - txt = txt.padStart(RTOSDisplayColumn[key].colSpaceFillThreshold); - needWSPreserve = true; - } - const gapBefore = RTOSDisplayColumn[key]?.colGapBefore || 0; - if (gapBefore > 0) { - txt = ' '.repeat(gapBefore) + txt; - needWSPreserve = true; - } - const gapAfter = RTOSDisplayColumn[key]?.colGapAfter || 0; - if (gapAfter > 0) { - txt += ' '.repeat(gapAfter); - needWSPreserve = true; - } - if (needWSPreserve) { - txt = `
${txt}
`; - } - return txt; - }; - - const colFormat = displayFieldNames.map((key) => `${RTOSDisplayColumn[key].width}fr`).join(' '); - let table = `\n`; - let header = ''; - let style = ''; - let row = 1; - for (const thr of allThreads) { - const th = thr.display; - if (!header) { - let col = 1; - let have2ndRow = false; - const commonHeaderRowPart = ' \n'; - const commonHeaderCellPart = ' ${txt}\n`; - if (!have2ndRow) { have2ndRow = !!RTOSDisplayColumn[key].headerRow2; } - col++; - } - header += ' \n'; - } - - if (have2ndRow) { - col = 1; - header += commonHeaderRowPart; - for (const key of displayFieldNames) { - const txt = padText(key, RTOSDisplayColumn[key].headerRow2); - const additionalClasses = getAlignClasses(key); - header += `${commonHeaderCellPart}${additionalClasses}" grid-column="${col}">${txt}\n`; - col++; - } - header += ' \n'; - } - table += header; - } - - let col = 1; - const running = (thr.running === true) ? ' running' : ''; - const rowClass = `thread-row-${row}`; - table += ` \n`; - for (const key of displayFieldNames) { - const v = th[key]; - let txt = padText(key, v.text); - const lKey = key.toLowerCase(); - let additionalClasses = running + getAlignClasses(key); - if ((RTOSDisplayColumn[key].colType & ColTypeEnum.colTypePercentage)) { - if (v.value !== undefined) { - const rowValueNumber = parseFloat(v.value); - if (!isNaN(rowValueNumber)) { - const activeValueStr = Math.floor(rowValueNumber).toString(); - additionalClasses += ' backgroundPercent'; - style += `.${this.className}-grid .${rowClass} .threads-cell-${lKey}.backgroundPercent {\n` - + ` --rtosview-percentage-active: ${activeValueStr}%;\n}\n\n`; - } - } - } else if (RTOSDisplayColumn[key].colType & ColTypeEnum.colTypeLink) { - // We lose any padding/justification information. Deal with this later when start doing memory windows - // and try to preserve formatting. Disable for now - // txt = `${v.text}`; - } else if ((RTOSDisplayColumn[key].colType & ColTypeEnum.colTypeCollapse) && (v.value)) { - const length = Object.values(v.value).reduce((acc: number, cur: string[]) => acc + cur.length, 0); - if (typeof length === 'number' && length > 1) { - const descriptions = Object.keys(v.value).map((key) => `${key}: ${v.value[key].join(', ')}`).join('
'); - txt = `
${descriptions}
`; - } - } - - const cls = `class="${this.className}-cell threads-cell threads-cell-${lKey}${additionalClasses}"`; - table += ` ${txt}\n`; - col++; - } - table += '
\n'; - row++; - } - - table += '
\n'; - - let ret = table; - if (timeInfo) { - ret += `

Data collected at ${timeInfo}

\n`; - } - - const htmlContent: HtmlInfo = { - html: ret, css: style - }; - - return htmlContent; - } -} - -export class RTOSVarHelper { - public varReference: number; - public value: string; - - constructor(public expression: string, public rtos: RTOSBase) { - } - - public static varsToObj(vars: DebugProtocol.Variable[]) { - const obj = {}; - for (const v of vars) { - obj[v.name + '-val'] = v.value; - obj[v.name + '-ref'] = v.variablesReference; - obj[v.name + '-exp'] = v.evaluateName; - } - return obj; - } - - public async tryInitOrUpdate(useFrameId: number, opt?: boolean): Promise { - try { - if (this.rtos.progStatus !== 'stopped') { - return false; - } - const arg: DebugProtocol.EvaluateArguments = { - frameId: useFrameId, - expression: this.expression, - context: 'hover' - }; - this.value = undefined; - // We have to see what a debugger like cppdbg returns for failures or when busy. And, is hover the right thing to use - const result = await this.rtos.customRequest('evaluate', arg, opt); - this.value = result.result; - this.varReference = result.variablesReference; - return true; - } catch (e) { - const msg = e?.message as string; - if (msg) { - if ((msg === 'Busy') // Cortex-Debug - || (msg.includes('process is running'))) { // cppdbg - // For cppdbg, the whole message is 'Unable to perform this action because the process is running.' - return false; - } - } - throw e; - } - } - - public getValue(frameId: number): Promise { - return new Promise(async (resolve, reject) => { - if (this.rtos.progStatus !== 'stopped') { - return reject(new Error(`busy, failed on ${this.expression}`)); - } else { - this.tryInitOrUpdate(frameId).then((res) => { - if (!res) { - reject(new Error('failed to initialize/update')); - } else { - resolve(this.value); - } - }, (e) => { - reject(e); - }); - } - }); - } - - public getVarChildren(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (this.rtos.progStatus !== 'stopped') { - return reject(new Error(`busy, failed on ${this.expression}`)); - } else { - this.getValue(frameId).then((str) => { - if (!this.varReference || !str) { - reject(Error(`Failed to get variable reference for ${this.expression}`)); - return; - } - const arg: DebugProtocol.VariablesArguments = { - variablesReference: this.varReference - }; - this.rtos.customRequest('variables', arg).then((result: any) => { - if (!result || !result.variables || !result.variables.length) { - reject(Error(`Failed to evaluate variable ${this.expression} ${arg.variablesReference}`)); - } else { - resolve(result.variables); - } - }, (e) => { - reject(e); - }); - }, (e) => { - reject(e); - }); - } - }); - } - - public getVarChildrenObj(useFrameId: number): Promise { - return new Promise((resolve, reject) => { - this.getVarChildren(useFrameId).then((vars) => { - const obj = RTOSVarHelper.varsToObj(vars); - resolve(obj); - }, (e) => { - reject(e); - }); - }); - } -} - -function isNullOrUndefined(x) { - return (x === undefined) || (x === null); -} diff --git a/src/frontend/rtos/rtos-embos.ts b/src/frontend/rtos/rtos-embos.ts deleted file mode 100644 index 253a4c10..00000000 --- a/src/frontend/rtos/rtos-embos.ts +++ /dev/null @@ -1,661 +0,0 @@ -import * as vscode from 'vscode'; -import { DebugProtocol } from '@vscode/debugprotocol'; -import * as RTOSCommon from './rtos-common'; -import { hexFormat } from '../utils'; -import { HrTimer } from '../../common'; - -// We will have two rows of headers for embOS and the table below describes -// the columns headers for the two rows and the width of each column as a fraction -// of the overall space. -enum DisplayFields { - ID_Address, - TaskName, - Status, - Priority, - StackPercent, - StackPeakPercent -} - -const RTOSEMBOSItems: { [key: string]: RTOSCommon.DisplayColumnItem } = {}; -RTOSEMBOSItems[DisplayFields[DisplayFields.ID_Address]] = { width: 2, headerRow1: '', headerRow2: 'ID / Address' }; -RTOSEMBOSItems[DisplayFields[DisplayFields.TaskName]] = { width: 4, headerRow1: '', headerRow2: 'Name', colGapBefore: 1 }; -RTOSEMBOSItems[DisplayFields[DisplayFields.Status]] = { - width: 4, headerRow1: 'Thread', headerRow2: 'Status', colType: RTOSCommon.ColTypeEnum.colTypeCollapse -}; -RTOSEMBOSItems[DisplayFields[DisplayFields.Priority]] = { - width: 2, headerRow1: 'Priority', headerRow2: 'cur,base', colType: RTOSCommon.ColTypeEnum.colTypeNumeric, colGapAfter: 1 -}; -RTOSEMBOSItems[DisplayFields[DisplayFields.StackPercent]] = { - width: 4, headerRow1: 'Stack Usage', headerRow2: '% (Used B / Size B)', colType: RTOSCommon.ColTypeEnum.colTypePercentage -}; -RTOSEMBOSItems[DisplayFields[DisplayFields.StackPeakPercent]] = { - width: 4, headerRow1: 'Stack Peak Usage', headerRow2: '% (Peak B / Size B)', colType: RTOSCommon.ColTypeEnum.colTypePercentage -}; - -// TODO Maybe add a column with NumActivations and/or NumPreemptions which is available when OS_SUPPORT_STAT is set (not 0) -// TODO Maybe add a column with Load / ExecTotal / ExecLast which is available when OS_SUPPORT_PROFILE is set (not 0) - -const DisplayFieldNames: string[] = Object.keys(RTOSEMBOSItems); - -export class RTOSEmbOS extends RTOSCommon.RTOSBase { - // We keep a bunch of variable references (essentially pointers) that we can use to query for values - // Since all of them are global variable, we only need to create them once per session. These are - // similar to Watch/Hover variables - private OSGlobal: RTOSCommon.RTOSVarHelper; - private OSGlobalVal: any; - - private OSGlobalpTask: RTOSCommon.RTOSVarHelper; /* start of task linked list */ - private OSGlobalpObjNameRoot: RTOSCommon.RTOSVarHelper; /* start of object name linked list */ - - private pCurrentTaskVal: number; - - private taskCount: number; - - private stale: boolean; - private foundThreads: RTOSCommon.RTOSThreadInfo[] = []; - private finalThreads: RTOSCommon.RTOSThreadInfo[] = []; - private timeInfo: string; - private readonly maxThreads = 1024; - - private stackPattern = 0x00; - private stackIncrements = -1; /* negative numbers => high to low address growth on stack (OS_STACK_GROWS_TOWARD_HIGHER_ADDR = 0) */ - - private helpHtml: string = undefined; - - constructor(public session: vscode.DebugSession) { - super(session, 'embOS'); - - if (session.configuration.rtosViewConfig) { - if (session.configuration.rtosViewConfig.stackPattern) { - this.stackPattern = parseInt(session.configuration.rtosViewConfig.stackPattern); - } - - if (session.configuration.rtosViewConfig.stackGrowth) { - this.stackIncrements = parseInt(session.configuration.rtosViewConfig.stackGrowth); - } - } - } - - public async tryDetect(useFrameId: number): Promise { - this.progStatus = 'stopped'; - try { - if (this.status === 'none') { - // We only get references to all the interesting variables. Note that any one of the following can fail - // and the caller may try again until we know that it definitely passed or failed. Note that while we - // re-try everything, we do remember what already had succeeded and don't waste time trying again. That - // is how this.getVarIfEmpty() works - this.OSGlobal = await this.getVarIfEmpty(this.OSGlobal, useFrameId, 'OS_Global', false); - this.OSGlobalpTask = await this.getVarIfEmpty(this.OSGlobalpTask, useFrameId, 'OS_Global.pTask', false); - this.OSGlobalpObjNameRoot = await this.getVarIfEmpty(this.OSGlobalpObjNameRoot, useFrameId, 'OS_Global.pObjNameRoot', true); - - this.status = 'initialized'; - } - return this; - } catch (e) { - if (e instanceof RTOSCommon.ShouldRetry) { - console.error(e.message); - } else { - this.status = 'failed'; - this.failedWhy = e; - } - return this; - } - } - - protected createHmlHelp(th: RTOSCommon.RTOSThreadInfo, thInfo: object) { - if (this.helpHtml === undefined) { - this.helpHtml = ''; - try { - let ret: string = ''; - function strong(text: string) { - return `${text}`; - } - - if (!thInfo['sName-val']) { - ret += `Thread name missing: Enable ${strong('OS_SUPPORT_TRACKNAME')} or use library mode that enables it and - use ${strong('sName')} parameter on task creation in FW

`; - } - if (!th.stackInfo.stackSize) { - ret += `Stack Size & Peak missing: Enable ${strong('OS_SUPPORT_STACKCHECK')} or use library mode that enables it

`; - } - - if (ret) { - ret += 'Note: Make sure you consider the performance/resources impact for any changes to your FW.
\n'; - this.helpHtml = '\n' - + `

\n${ret}\n

\n`; - } - } catch (e) { - console.log(e); - } - } - } - - public refresh(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (this.progStatus !== 'stopped') { - resolve(); - return; - } - - const timer = new HrTimer(); - this.stale = true; - this.timeInfo = (new Date()).toISOString(); - - this.taskCount = Number.MAX_SAFE_INTEGER; - this.foundThreads = []; - - this.OSGlobal.getVarChildrenObj(frameId).then(async (varObj) => { - try { - this.OSGlobalVal = varObj; - - let isRunning: any = '0'; - - if ('IsRunning-val' in this.OSGlobalVal) { - isRunning = this.OSGlobalVal['IsRunning-val']; - } else { - /* older embOS versions do not have IsRunning struct member */ - isRunning = '1'; - } - - if (undefined !== isRunning && !isNaN(isRunning) && (0 !== parseInt(isRunning))) { - const taskList = this.OSGlobalVal['pTask-val']; - if (undefined !== taskList && (0 !== parseInt(taskList))) { - if (this.OSGlobalVal['pCurrentTask-val']) { - this.pCurrentTaskVal = parseInt(this.OSGlobalVal['pCurrentTask-val']); - } else { - this.pCurrentTaskVal = Number.MAX_SAFE_INTEGER; - } - - const objectNameEntries = await this.getObjectNameEntries(frameId); - - await this.getThreadInfo(this.OSGlobalpTask, objectNameEntries, frameId); - - this.foundThreads.sort((a, b) => parseInt(a.display[DisplayFieldNames[DisplayFields.ID_Address]].text) - - parseInt(b.display[DisplayFieldNames[DisplayFields.ID_Address]].text)); - - this.finalThreads = [...this.foundThreads]; - } else { - this.finalThreads = []; - } - } else { - this.finalThreads = []; - } - - this.stale = false; - this.timeInfo += ' in ' + timer.deltaMs() + ' ms'; - resolve(); - } catch (e) { - resolve(); - console.error('RTOSEMBOS.refresh() failed: ', e); - } - }, (reason) => { - resolve(); - console.error('RTOSEMBOS.refresh() failed: ', reason); - }); - }); - } - - private getThreadInfo(taskListEntry: RTOSCommon.RTOSVarHelper, objectNameEntries: Map, frameId: number): Promise { - return new Promise((resolve, reject) => { - if (!taskListEntry || !taskListEntry.varReference) { - resolve(); - return; - } - - if (this.progStatus !== 'stopped') { - reject(new Error('Busy')); - return; - } - - taskListEntry.getVarChildrenObj(frameId).then(async (obj) => { - try { - let curTaskObj = obj; - let thAddress = parseInt(taskListEntry.value); - - let threadCount = 1; - - do { - let thName = '???'; - - if (typeof curTaskObj['sName-val'] === 'string') { - const matchName = curTaskObj['sName-val'].match(/"([^*]*)"$/); - thName = matchName ? matchName[1] : curTaskObj['sName-val']; - } else if (typeof curTaskObj['Name-val'] === 'string') { /* older embOS versions used Name */ - const matchName = curTaskObj['Name-val'].match(/"([^*]*)"$/); - thName = matchName ? matchName[1] : curTaskObj['Name-val']; - } - - const threadRunning = (thAddress === this.pCurrentTaskVal); - const thStateObject = await this.analyzeTaskState(curTaskObj, objectNameEntries); - const stackInfo = await this.getStackInfo(curTaskObj, this.stackPattern); - - const display: { [key: string]: RTOSCommon.DisplayRowItem } = {}; - const mySetter = (x: DisplayFields, text: string, value?: any) => { - display[DisplayFieldNames[x]] = { text, value }; - }; - - mySetter(DisplayFields.ID_Address, hexFormat(thAddress)); - mySetter(DisplayFields.TaskName, thName); - mySetter(DisplayFields.Status, threadRunning ? 'RUNNING' : thStateObject.describe(), thStateObject.fullData()); - - const myHexNumStrCon = (hexNumberString: string): string => { - return parseInt(hexNumberString).toString(); - }; - - const prioString = `${myHexNumStrCon(curTaskObj['Priority-val'])},${myHexNumStrCon(curTaskObj['BasePrio-val'])}`; - mySetter(DisplayFields.Priority, prioString); - - if ((stackInfo.stackUsed !== undefined) && (stackInfo.stackSize !== undefined)) { - const stackPercentVal = Math.round((stackInfo.stackUsed / stackInfo.stackSize) * 100); - const stackPercentText = `${stackPercentVal} % (${stackInfo.stackUsed} / ${stackInfo.stackSize})`; - mySetter(DisplayFields.StackPercent, stackPercentText, stackPercentVal); - } else { - mySetter(DisplayFields.StackPercent, '?? %'); - } - - if ((stackInfo.stackPeak !== undefined) && (stackInfo.stackSize !== undefined)) { - const stackPeakPercentVal = Math.round((stackInfo.stackPeak / stackInfo.stackSize) * 100); - const stackPeakPercentText = `${stackPeakPercentVal.toString().padStart(3)} % (${stackInfo.stackPeak} / ${stackInfo.stackSize})`; - mySetter(DisplayFields.StackPeakPercent, stackPeakPercentText, stackPeakPercentVal); - } else { - mySetter(DisplayFields.StackPeakPercent, '?? %'); - } - - const thread: RTOSCommon.RTOSThreadInfo = { - display: display, stackInfo: stackInfo, running: threadRunning - }; - this.foundThreads.push(thread); - this.createHmlHelp(thread, curTaskObj); - - thAddress = parseInt(curTaskObj['pNext-val']); - if (0 !== thAddress) { - const nextThreadObj = await this.getVarChildrenObj(curTaskObj['pNext-ref'], 'pNext'); - curTaskObj = nextThreadObj; - threadCount++; - } - - if (threadCount > this.maxThreads) { - console.error(`Exceeded maximum number of allowed threads (${this.maxThreads})`); - break; - } - } while ((0 !== thAddress)); - - this.taskCount = threadCount; - - resolve(); - } catch (e) { - console.log('RTOSEMBOS.getThreadInfo() error', e); - } - }, (e) => { - reject(e); - }); - }); - } - - protected async analyzeTaskState(curTaskObj: object, objectNameEntries: Map): Promise { - const state = parseInt(curTaskObj['Stat-val']); - - const suspendCount = (state & OS_TASK_STATE_SUSPEND_MASK); - if (suspendCount !== 0) { - return new TaskSuspended(suspendCount); - } - - let pendTimeout = Number.MAX_SAFE_INTEGER; - let TimeoutActive = false; - - if (state & OS_TASK_STATE_TIMEOUT_ACTIVE) { - pendTimeout = parseInt(curTaskObj['Timeout-val']); - TimeoutActive = true; - } - - const maskedState = (state & OS_TASK_STATE_MASK); - - switch (maskedState as OsTaskPendingState) { - case OsTaskPendingState.READY: - if (pendTimeout) { - return new TaskDelayed(pendTimeout); - } else { - return new TaskReady(); - } - - case OsTaskPendingState.TASK_EVENT: { - const resultState = new TaskPending(); - resultState.addEventType(maskedState); - - if (curTaskObj['EventMask-val']) { - const eventMask = parseInt(curTaskObj['EventMask-val']); // Waiting bits - const event = parseInt(curTaskObj['Events-val']); // Set bits - const eventInfo: EventInfo = { address: eventMask, eventType: state, name: `mask ${eventMask} - set ${event}` }; - - if (TimeoutActive) { - eventInfo.timeOut = pendTimeout; - } - - resultState.addEvent(eventInfo); - } - - return resultState; - } - - default: { - const resultState = new TaskPending(); - resultState.addEventType(maskedState); - - if (curTaskObj['pWaitList-val']) { - const waitListEntryAddress = parseInt(curTaskObj['pWaitList-val']); - - if (waitListEntryAddress !== 0) { - const waitListEntry = await this.getVarChildrenObj(curTaskObj['pWaitList-ref'], 'pWaitList'); - const waitObject = parseInt(waitListEntry['pWaitObj-val']); - const eventInfo: EventInfo = { address: waitObject, eventType: state }; - - if (objectNameEntries.has(waitObject)) { - eventInfo.name = objectNameEntries.get(waitObject); - } - - if (TimeoutActive) { - eventInfo.timeOut = pendTimeout; - } - - resultState.addEvent(eventInfo); - } - } - - return resultState; - } - } - } - - protected async getStackInfo(thInfo: object, stackPattern: number): Promise { - const TopOfStack = thInfo['pStack-val']; - - /* only available with #if (OS_SUPPORT_STACKCHECK != 0) || (OS_SUPPORT_MPU != 0) (optional) */ - const StackSize = thInfo['StackSize-val']; - let EndOfStack: any; - - if ('pStackBase-val' in thInfo) { - EndOfStack = thInfo['pStackBase-val']; - } else { - /* older embOS versions used pStackBot instead of pStackBase */ - EndOfStack = thInfo['pStackBot-val']; - } - - let Stack = 0; - if (EndOfStack && StackSize) { - if (this.stackIncrements < 0) { - Stack = parseInt(EndOfStack) + parseInt(StackSize); - } else { - Stack = parseInt(EndOfStack) - parseInt(StackSize); - } - } else { - /* As stackStart is mandatory, we need to set it to some reasonable value */ - Stack = parseInt(TopOfStack); - } - - const stackInfo: RTOSCommon.RTOSStackInfo = { - stackStart: Stack, - stackTop: parseInt(TopOfStack) - }; - - if (EndOfStack && StackSize) { - stackInfo.stackEnd = parseInt(EndOfStack); - stackInfo.stackSize = parseInt(StackSize); - - if (this.stackIncrements < 0) { - const stackDelta = stackInfo.stackStart - stackInfo.stackTop; - stackInfo.stackFree = stackInfo.stackSize - stackDelta; - stackInfo.stackUsed = stackDelta; - } else { - const stackDelta = stackInfo.stackTop - stackInfo.stackStart; - stackInfo.stackFree = stackDelta; - stackInfo.stackUsed = stackInfo.stackSize - stackDelta; - } - - /* check stack peak */ - const memArg: DebugProtocol.ReadMemoryArguments = { - memoryReference: hexFormat(Math.min(stackInfo.stackTop, stackInfo.stackEnd)), - count: stackInfo.stackFree - }; - try { - const stackData = await this.session.customRequest('readMemory', memArg); - const buf = Buffer.from(stackData.data, 'base64'); - stackInfo.bytes = new Uint8Array(buf); - let start = this.stackIncrements < 0 ? 0 : stackInfo.bytes.length - 1; - const end = this.stackIncrements < 0 ? stackInfo.bytes.length : -1; - let peak = 0; - while (start !== end) { - if (stackInfo.bytes[start] !== stackPattern) { - break; - } - start -= this.stackIncrements; - peak++; - } - stackInfo.stackPeak = stackInfo.stackSize - peak; - } catch (e) { - console.log(e); - } - } - - return stackInfo; - } - - protected async getObjectNameEntries(frameId: number): Promise> { - const result: Map = new Map(); - - if (null !== this.OSGlobalpObjNameRoot) { - await this.OSGlobalpObjNameRoot.getValue(frameId); - - /* Follow the linked list of object identifier nodes */ - if (0 !== parseInt(this.OSGlobalpObjNameRoot.value)) { - let entry = await this.OSGlobalpObjNameRoot.getVarChildrenObj(frameId); - while (entry) { - const objectId = parseInt(entry['pOSObjID-val']); - if (!objectId || objectId === 0) { - break; - } - - const matchName = entry['sName-val'].match(/"([^*]*)"$/); - const objectName = matchName ? matchName[1] : entry['sName-val']; - - if (objectName && !result.has(objectId)) { - result.set(objectId, objectName); - } - - const nextEntryAddr = parseInt(entry['pNext-val']); - if (nextEntryAddr === 0) { - break; - } else { - entry = await this.getVarChildrenObj(entry['pNext-ref'], 'pNext'); - } - } - } - } - - return result; - } - - public lastValidHtmlContent: RTOSCommon.HtmlInfo = { html: '', css: '' }; - public getHTML(): RTOSCommon.HtmlInfo { - const htmlContent: RTOSCommon.HtmlInfo = { - html: '', css: '' - }; - // WARNING: This stuff is super fragile. Once we know how this works, then we should refactor this - let msg = ''; - if (this.status === 'none') { - htmlContent.html = '

RTOS not yet fully initialized. Will occur next time program pauses

\n'; - return htmlContent; - } else if (this.stale) { - const lastHtmlInfo = this.lastValidHtmlContent; - if (this.taskCount === Number.MAX_SAFE_INTEGER) { - msg = ' Could not read any task from "OS_Global.pTask". Perhaps program is busy or did not stop long enough'; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (this.taskCount > this.maxThreads) { - msg = ` embOS variable "OS_Global.pTask" holds ${this.taskCount} tasks which seems invalid for us`; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (lastHtmlInfo.html) { // TODO check if this check is ok - msg = ' Following info from last query may be stale.'; - } - - htmlContent.html = `

Unable to collect full RTOS information.${msg}

\n` + lastHtmlInfo.html; - htmlContent.css = lastHtmlInfo.css; - return htmlContent; - } else if ((this.taskCount !== Number.MAX_SAFE_INTEGER) && (this.finalThreads.length !== this.taskCount)) { - msg += `

Expecting ${this.taskCount} threads, found ${this.finalThreads.length}. Thread data may be unreliable

\n`; - } else if (this.finalThreads.length === 0) { - htmlContent.html = `

No ${this.name} threads detected, perhaps RTOS not yet initialized or tasks yet to be created!

\n`; - return htmlContent; - } - - const ret = this.getHTMLCommon(DisplayFieldNames, RTOSEMBOSItems, this.finalThreads, this.timeInfo); - htmlContent.html = msg + ret.html + (this.helpHtml || ''); - htmlContent.css = ret.css; - - this.lastValidHtmlContent = htmlContent; - // console.log(this.lastValidHtmlContent.html); - return this.lastValidHtmlContent; - } -} - -const OS_TASK_STATE_SUSPEND_MASK = 0x03; /* Task suspend count (bit 0 - 1) */ -const OS_TASK_STATE_TIMEOUT_ACTIVE = 0x04; /* Task timeout active (bit 2) */ -const OS_TASK_STATE_MASK = 0xF8; /* Task state mask (bit 3 - bit 7) */ - -enum OsTaskPendingState { - READY = 0x00, - TASK_EVENT = 0x08, /* flag group "assigned" to one task */ - MUTEX = 0x10, - UNKNOWN = 0x18, // Not sure when this value is set - SEMAPHORE = 0x20, - MEMPOOL = 0x28, - QUEUE_NOT_EMPTY = 0x30, - MAILBOX_NOT_FULL = 0x38, - MAILBOX_NOT_EMPTY = 0x40, - EVENT_OBJECT = 0x48, /* flag group without task "assignment" */ - QUEUE_NOT_FULL = 0x50 -} - -abstract class TaskState { - public abstract describe(): string; - public abstract fullData(): any; -} - -class TaskReady extends TaskState { - public describe(): string { - return 'READY'; - } - - public fullData(): any { - return null; - } -} - -class TaskDelayed extends TaskState { - protected delayTicks: number; - - constructor(delayTicks: number) { - super(); - this.delayTicks = delayTicks; - } - - public describe(): string { - return `DELAYED by ${this.delayTicks}`; // TODO Not sure what unit this variable holds - } - - public fullData(): any { - return null; - } -} - -class TaskSuspended extends TaskState { - private suspendCount: number; - - constructor(suspendCount: number) { - super(); - this.suspendCount = suspendCount; - } - - public describe(): string { - return `SUSPENDED (count: ${this.suspendCount})`; - } - - public fullData(): any { - return null; - } -} - -class TaskPending extends TaskState { - private pendingInfo: Map; - - constructor() { - super(); - this.pendingInfo = new Map(); - } - - public addEvent(event: EventInfo) { - this.addEventType(event.eventType); - this.pendingInfo.get(event.eventType).push(event); - } - - public addEventType(eventType: OsTaskPendingState) { - if (!this.pendingInfo.has(eventType)) { - this.pendingInfo.set(eventType, []); - } - } - - public describe(): string { - // Converting to an array here is inefficient, but JS has no builtin iterator map/reduce feature - const eventCount = [...this.pendingInfo.values()].reduce((acc, events) => acc + events.length, 0); - - if (eventCount <= 1) { - let event: EventInfo = null; - for (const events of this.pendingInfo.values()) { - if (events.length > 0) { - event = events[0]; - } - } - - if (event) { - const eventTypeStr = OsTaskPendingState[event.eventType] ? OsTaskPendingState[event.eventType] : 'Unknown'; - const eventTimeoutString = event.timeOut ? ` with timeout in ${event.timeOut}` : ''; // TODO Not sure what unit this variable holds - return `PEND ${eventTypeStr}: ${describeEvent(event)}${eventTimeoutString}`; - } else { - // This should not happen, but we still keep it as a fallback - return 'PEND Unknown'; - } - } else { - return 'PEND MULTI'; - } - } - - public fullData() { - // Build an object containing mapping event types to event descriptions - const result = {}; - const eventTypes = [...this.pendingInfo.keys()]; - eventTypes.sort(); - for (const eventType of eventTypes) { - result[OsTaskPendingState[eventType]] = []; - for (const event of this.pendingInfo.get(eventType)) { - result[OsTaskPendingState[eventType]].push(describeEvent(event)); - } - } - - return result; - } -} - -interface EventInfo { - name?: string; - timeOut?: number; - address: number; - eventType: OsTaskPendingState; -} - -function describeEvent(event: EventInfo): string { - if (event.name && event.name !== '?') { - return event.name; - } else { - return `0x${event.address.toString(16)}`; - } -} diff --git a/src/frontend/rtos/rtos-freertos.ts b/src/frontend/rtos/rtos-freertos.ts deleted file mode 100644 index 7567d007..00000000 --- a/src/frontend/rtos/rtos-freertos.ts +++ /dev/null @@ -1,417 +0,0 @@ -import * as vscode from 'vscode'; -import { DebugProtocol } from '@vscode/debugprotocol'; -import * as RTOSCommon from './rtos-common'; -import { hexFormat } from '../utils'; -import { HrTimer, toStringDecHexOctBin } from '../../common'; - -// We will have two rows of headers for FreeRTOS and the table below describes -// the columns headers for the two rows and the width of each column as a fraction -// of the overall space. -enum DisplayFields { - ID, - Address, - TaskName, - Status, - Priority, - StackStart, - StackTop, - StackEnd, - StackSize, - StackUsed, - StackFree, - StackPeak, - Runtime -} - -const numType = RTOSCommon.ColTypeEnum.colTypeNumeric; -const FreeRTOSItems: { [key: string]: RTOSCommon.DisplayColumnItem } = {}; -FreeRTOSItems[DisplayFields[DisplayFields.ID]] = { width: 1, headerRow1: '', headerRow2: 'ID', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.Address]] = { width: 3, headerRow1: 'Thread', headerRow2: 'Address', colGapBefore: 1 }; -FreeRTOSItems[DisplayFields[DisplayFields.TaskName]] = { width: 4, headerRow1: '', headerRow2: 'Task Name' }; -FreeRTOSItems[DisplayFields[DisplayFields.Status]] = { width: 3, headerRow1: '', headerRow2: 'Status' }; -FreeRTOSItems[DisplayFields[DisplayFields.Priority]] = { width: 1.5, headerRow1: 'Prio', headerRow2: 'rity', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.StackStart]] = { - width: 3, headerRow1: 'Stack', headerRow2: 'Start', - colType: RTOSCommon.ColTypeEnum.colTypeLink, colGapBefore: 1 -}; -FreeRTOSItems[DisplayFields[DisplayFields.StackTop]] = { width: 3, headerRow1: 'Stack', headerRow2: 'Top' }; -FreeRTOSItems[DisplayFields[DisplayFields.StackEnd]] = { width: 3, headerRow1: 'Stack', headerRow2: 'End' }; -FreeRTOSItems[DisplayFields[DisplayFields.StackSize]] = { width: 2, headerRow1: 'Stack', headerRow2: 'Size', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.StackUsed]] = { width: 2, headerRow1: 'Stack', headerRow2: 'Used', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.StackFree]] = { width: 2, headerRow1: 'Stack', headerRow2: 'Free', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.StackPeak]] = { width: 2, headerRow1: 'Stack', headerRow2: 'Peak', colType: numType }; -FreeRTOSItems[DisplayFields[DisplayFields.Runtime]] = { width: 2, headerRow1: '', headerRow2: 'Runtime', colType: numType }; -const DisplayFieldNames: string[] = Object.keys(FreeRTOSItems); - -function isNullOrUndefined(x) { - return (x === undefined) || (x === null); -} - -export class RTOSFreeRTOS extends RTOSCommon.RTOSBase { - // We keep a bunch of variable references (essentially pointers) that we can use to query for values - // Since all of them are global variable, we only need to create them once per session. These are - // similar to Watch/Hover variables - private uxCurrentNumberOfTasks: RTOSCommon.RTOSVarHelper; - private uxCurrentNumberOfTasksVal: number; - private pxReadyTasksLists: RTOSCommon.RTOSVarHelper; - private xDelayedTaskList1: RTOSCommon.RTOSVarHelper; - private xDelayedTaskList2: RTOSCommon.RTOSVarHelper; - private xPendingReadyList: RTOSCommon.RTOSVarHelper; - private pxCurrentTCB: RTOSCommon.RTOSVarHelper; - private xSuspendedTaskList: RTOSCommon.RTOSVarHelper; - private xTasksWaitingTermination: RTOSCommon.RTOSVarHelper; - private ulTotalRunTime: RTOSCommon.RTOSVarHelper; - private ulTotalRunTimeVal: number; - - private stale: boolean; - private curThreadAddr: number; - private foundThreads: RTOSCommon.RTOSThreadInfo[] = []; - private finalThreads: RTOSCommon.RTOSThreadInfo[] = []; - private timeInfo: string; - private readonly maxThreads = 1024; - private helpHtml: string = undefined; - - // Need to do a TON of testing for stack growing the other direction - private stackIncrements = -1; - - constructor(public session: vscode.DebugSession) { - super(session, 'FreeRTOS'); - } - - public async tryDetect(useFrameId: number): Promise { - this.progStatus = 'stopped'; - try { - if (this.status === 'none') { - // We only get references to all the interesting variables. Note that any one of the following can fail - // and the caller may try again until we know that it definitely passed or failed. Note that while we - // re-try everything, we do remember what already had succeeded and don't waste time trying again. That - // is how this.getVarIfEmpty() works - this.uxCurrentNumberOfTasks = await this.getVarIfEmpty(this.uxCurrentNumberOfTasks, useFrameId, 'uxCurrentNumberOfTasks'); - this.pxReadyTasksLists = await this.getVarIfEmpty(this.pxReadyTasksLists, useFrameId, 'pxReadyTasksLists'); - this.xDelayedTaskList1 = await this.getVarIfEmpty(this.xDelayedTaskList1, useFrameId, 'xDelayedTaskList1'); - this.xDelayedTaskList2 = await this.getVarIfEmpty(this.xDelayedTaskList2, useFrameId, 'xDelayedTaskList2'); - this.xPendingReadyList = await this.getVarIfEmpty(this.xPendingReadyList, useFrameId, 'xPendingReadyList'); - this.pxCurrentTCB = await this.getVarIfEmpty(this.pxCurrentTCB, useFrameId, 'pxCurrentTCB'); - this.xSuspendedTaskList = await this.getVarIfEmpty(this.xSuspendedTaskList, useFrameId, 'xSuspendedTaskList', true); - this.xTasksWaitingTermination = await this.getVarIfEmpty(this.xTasksWaitingTermination, useFrameId, 'xTasksWaitingTermination', true); - this.ulTotalRunTime = await this.getVarIfEmpty(this.ulTotalRunTime, useFrameId, 'ulTotalRunTime', true); - this.status = 'initialized'; - } - return this; - } catch (e) { - if (e instanceof RTOSCommon.ShouldRetry) { - console.error(e.message); - } else { - this.status = 'failed'; - this.failedWhy = e; - } - return this; - } - } - - protected createHmlHelp(th: RTOSCommon.RTOSThreadInfo, thInfo: object) { - if (this.helpHtml === undefined) { - this.helpHtml = ''; - try { - let ret: string = ''; - function strong(s) { - return `${s}`; - } - if (!thInfo['uxTCBNumber-val']) { - ret += `Thread ID missing......: Enable macro ${strong('configUSE_TRACE_FACILITY')} in FW
`; - } - if (!th.stackInfo.stackEnd) { - ret += `Stack End missing......: Enable macro ${strong('configRECORD_STACK_HIGH_ADDRESS')} in FW
`; - } - if ((thInfo['pcTaskName-val'] === '[0]') || (thInfo['pcTaskName-val'] === '[1]')) { - ret += `Thread Name missing....: Set macro ${strong('configMAX_TASK_NAME_LEN')} to something greater than 1 in FW
`; - } - - if (!this.ulTotalRunTime) { - ret += /* html */`
Missing Runtime stats..:
- /* To get runtime stats, modify the following macro in FreeRTOSConfig.h */
- #define ${strong('configGENERATE_RUN_TIME_STATS')} 1 /* 1: generate runtime statistics; 0: no runtime statistics */
- /* Also, add the following two macros to provide a high speed counter -- something at least 10x faster than
- ** your RTOS scheduler tick. One strategy could be to use a HW counter and sample its current value when needed
- */
- #define ${strong('portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()')} /* Define this to initialize your timer */
- #define ${strong('portGET_RUN_TIME_COUNTER_VALUE()')}${' '.repeat(9)}/* Define this to sample the counter */
- `; - } - if (ret) { - ret += '
Note: Make sure you consider the performance/resources impact for any changes to your FW.
\n'; - ret = '\n' - + `

\n${ret}\n

\n`; - this.helpHtml = ret; - } - } catch (e) { - console.log(e); - } - } - } - - private updateCurrentThreadAddr(frameId: number): Promise { - return new Promise((resolve, reject) => { - this.pxCurrentTCB.getValue(frameId).then((ret) => { - this.curThreadAddr = parseInt(ret); - resolve(); - }, (e) => { - reject(e); - }); - }); - } - - private updateTotalRuntime(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (!this.ulTotalRunTime) { - resolve(); - return; - } - this.ulTotalRunTime.getValue(frameId).then((ret) => { - this.ulTotalRunTimeVal = parseInt(ret); - resolve(); - }, (e) => { - reject(e); - }); - }); - } - - public refresh(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (this.progStatus !== 'stopped') { - resolve(); - return; - } - - const timer = new HrTimer(); - this.stale = true; - this.timeInfo = (new Date()).toISOString(); - // uxCurrentNumberOfTasks can go invalid anytime. Like when a reset/restart happens - this.uxCurrentNumberOfTasksVal = Number.MAX_SAFE_INTEGER; - this.foundThreads = []; - this.uxCurrentNumberOfTasks.getValue(frameId).then(async (str) => { - try { - this.uxCurrentNumberOfTasksVal = str ? parseInt(str) : Number.MAX_SAFE_INTEGER; - if ((this.uxCurrentNumberOfTasksVal > 0) && (this.uxCurrentNumberOfTasksVal <= this.maxThreads)) { - let promises = []; - const ary = await this.pxReadyTasksLists.getVarChildren(frameId); - for (const v of ary) { - promises.push(this.getThreadInfo(v.variablesReference, 'READY', frameId)); - } - promises.push(this.updateCurrentThreadAddr(frameId)); - promises.push(this.updateTotalRuntime(frameId)); - // Update in bulk, but broken up into three chunks, if the number of threads are already fulfilled, then - // not much happens - await Promise.all(promises); - promises = []; - promises.push(this.getThreadInfo(this.xDelayedTaskList1, 'BLOCKED', frameId)); - promises.push(this.getThreadInfo(this.xDelayedTaskList2, 'BLOCKED', frameId)); - promises.push(this.getThreadInfo(this.xPendingReadyList, 'PENDING', frameId)); - await Promise.all(promises); - promises = []; - promises.push(this.getThreadInfo(this.xSuspendedTaskList, 'SUSPENDED', frameId)); - promises.push(this.getThreadInfo(this.xTasksWaitingTermination, 'TERMINATED', frameId)); - await Promise.all(promises); - promises = []; - if (this.foundThreads.length > 0) { - const th = this.foundThreads[0]; - if (th.display['ID'].text !== '??') { - this.foundThreads.sort((a, b) => parseInt(a.display['ID'].text) - parseInt(b.display['ID'].text)); - } else { - this.foundThreads.sort((a, b) => parseInt(a.display['Address'].text) - parseInt(b.display['Address'].text)); - } - } - this.finalThreads = [...this.foundThreads]; - // console.table(this.finalThreads); - } else { - this.finalThreads = []; - } - this.stale = false; - this.timeInfo += ' in ' + timer.deltaMs() + ' ms'; - resolve(); - } catch (e) { - resolve(); - console.error('FreeRTOS.refresh() failed: ', e); - } - }, (reason) => { - resolve(); - console.error('FreeRTOS.refresh() failed: ', reason); - }); - }); - } - - private getThreadInfo(varRef: RTOSCommon.RTOSVarHelper | number, state: string, frameId: number): Promise { - return new Promise((resolve, reject) => { - if (!varRef || ((typeof varRef !== 'number') && !varRef.varReference) || (this.foundThreads.length >= this.uxCurrentNumberOfTasksVal)) { - resolve(); - return; - } - if (this.progStatus !== 'stopped') { - reject(new Error('Busy')); - return; - } - let promise; - if (typeof varRef !== 'number') { - promise = varRef.getVarChildrenObj(frameId); - } else { - promise = this.getVarChildrenObj(varRef, 'task-list'); - } - promise.then(async (obj) => { - const threadCount = parseInt(obj['uxNumberOfItems-val']); - const listEndRef = obj['xListEnd-ref']; - if ((threadCount <= 0) || !listEndRef) { - resolve(); - return; - } - try { - const listEndObj = await this.getVarChildrenObj(listEndRef, 'xListEnd'); - let curRef = listEndObj['pxPrevious-ref']; - for (let thIx = 0; thIx < threadCount; thIx++) { - const element = await this.getVarChildrenObj(curRef, 'pxPrevious'); - const threadId = parseInt(element['pvOwner-val']); - const thInfo = await this.getExprValChildrenObj(`((TCB_t*)${hexFormat(threadId)})`, frameId); - const threadRunning = (threadId === this.curThreadAddr); - const tmpThName = await this.getExprVal('(char *)' + thInfo['pcTaskName-exp'], frameId); - const match = tmpThName.match(/"([^*]*)"$/); - const thName = match ? match[1] : tmpThName; - const stackInfo = await this.getStackInfo(thInfo, 0xA5); - // This is the order we want stuff in - const display: { [key: string]: RTOSCommon.DisplayRowItem } = {}; - const mySetter = (x: DisplayFields, text: string, value?: any) => { - display[DisplayFieldNames[x]] = { text, value }; - }; - - mySetter(DisplayFields.ID, thInfo['uxTCBNumber-val'] || '??'); - mySetter(DisplayFields.Address, hexFormat(threadId)); - mySetter(DisplayFields.TaskName, thName); - mySetter(DisplayFields.Status, threadRunning ? 'RUNNING' : state); - mySetter(DisplayFields.StackStart, hexFormat(stackInfo.stackStart)); - mySetter(DisplayFields.StackTop, hexFormat(stackInfo.stackTop)); - mySetter(DisplayFields.StackEnd, stackInfo.stackEnd ? hexFormat(stackInfo.stackEnd) : '0x????????'); - - if (thInfo['uxBasePriority-val']) { - mySetter(DisplayFields.Priority, `${thInfo['uxPriority-val']},${thInfo['uxBasePriority-val']}`); - } else { - mySetter(DisplayFields.Priority, `${thInfo['uxPriority-val']}`); - } - - const func = (x: any) => x === undefined ? '???' : x.toString(); - mySetter(DisplayFields.StackSize, func(stackInfo.stackSize)); - mySetter(DisplayFields.StackUsed, func(stackInfo.stackUsed)); - mySetter(DisplayFields.StackFree, func(stackInfo.stackFree)); - mySetter(DisplayFields.StackPeak, func(stackInfo.stackPeak)); - if (thInfo['ulRunTimeCounter-val'] && this.ulTotalRunTimeVal) { - const tmp = ((parseInt(thInfo['ulRunTimeCounter-val']) / this.ulTotalRunTimeVal) * 100).toFixed(2); - mySetter(DisplayFields.Runtime, tmp.padStart(5, '0') + '%'); - } else { - mySetter(DisplayFields.Runtime, '??.??%'); - } - const thread: RTOSCommon.RTOSThreadInfo = { - display: display, stackInfo: stackInfo, running: threadRunning - }; - this.foundThreads.push(thread); - this.createHmlHelp(thread, thInfo); - curRef = element['pxPrevious-ref']; - } - resolve(); - } catch (e) { - console.log('FreeRTOS read thread info error', e); - } - }, (e) => { - reject(e); - }); - }); - } - - protected async getStackInfo(thInfo: any, waterMark: number) { - const pxStack = thInfo['pxStack-val']; - const pxTopOfStack = thInfo['pxTopOfStack-val']; - const pxEndOfStack = thInfo['pxEndOfStack-val']; - const stackInfo: RTOSCommon.RTOSStackInfo = { - stackStart: parseInt(pxStack), - stackTop: parseInt(pxTopOfStack) - }; - const stackDelta = Math.abs(stackInfo.stackTop - stackInfo.stackStart); - if (this.stackIncrements < 0) { - stackInfo.stackFree = stackDelta; - } else { - stackInfo.stackUsed = stackDelta; - } - - if (pxEndOfStack) { - stackInfo.stackEnd = parseInt(pxEndOfStack); - stackInfo.stackSize = Math.abs(stackInfo.stackStart - stackInfo.stackEnd); - if (this.stackIncrements < 0) { - stackInfo.stackUsed = stackInfo.stackSize - stackDelta; - } else { - stackInfo.stackFree = stackInfo.stackSize - stackDelta; - } - const memArg: DebugProtocol.ReadMemoryArguments = { - memoryReference: hexFormat(Math.min(stackInfo.stackStart, stackInfo.stackEnd)), - count: stackInfo.stackSize - }; - try { - const stackData = await this.session.customRequest('readMemory', memArg); - const buf = Buffer.from(stackData.data, 'base64'); - stackInfo.bytes = new Uint8Array(buf); - let start = this.stackIncrements < 0 ? 0 : stackInfo.bytes.length - 1; - const end = this.stackIncrements < 0 ? stackInfo.bytes.length : -1; - let peak = 0; - while (start !== end) { - if (stackInfo.bytes[start] !== waterMark) { - break; - } - start -= this.stackIncrements; - peak++; - } - stackInfo.stackPeak = stackInfo.stackSize - peak; - } catch (e) { - console.log(e); - } - } - return stackInfo; - } - - public lastValidHtmlContent: RTOSCommon.HtmlInfo = { html: '', css: '' }; - public getHTML(): RTOSCommon.HtmlInfo { - const htmlContent: RTOSCommon.HtmlInfo = { - html: '', css: '' - }; - // WARNING: This stuff is super fragile. Once we know how this works, then we should refactor this - let msg = ''; - if (this.status === 'none') { - htmlContent.html = '

RTOS not yet fully initialized. Will occur next time program pauses

\n'; - return htmlContent; - } else if (this.stale) { - const lastHtmlInfo = this.lastValidHtmlContent; - if (this.uxCurrentNumberOfTasksVal === Number.MAX_SAFE_INTEGER) { - msg = ' Could not read "uxCurrentNumberOfTasks". Perhaps program is busy or did not stop long enough'; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (this.uxCurrentNumberOfTasksVal > this.maxThreads) { - msg = ` FreeRTOS variable uxCurrentNumberOfTasks = ${this.uxCurrentNumberOfTasksVal} seems invalid`; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (lastHtmlInfo.html) { // TODO check if this check is ok - msg = ' Following info from last query may be stale.'; - } - - htmlContent.html = `

Unable to collect full RTOS information.${msg}

\n` + lastHtmlInfo.html; - htmlContent.css = lastHtmlInfo.css; - return htmlContent; - } else if ((this.uxCurrentNumberOfTasksVal !== Number.MAX_SAFE_INTEGER) && (this.finalThreads.length !== this.uxCurrentNumberOfTasksVal)) { - msg += `

Expecting ${this.uxCurrentNumberOfTasksVal} threads, found ${this.finalThreads.length}. Thread data may be unreliable

\n`; - } else if (this.finalThreads.length === 0) { - htmlContent.html = `

No ${this.name} threads detected, perhaps RTOS not yet initialized or tasks yet to be created!

\n`; - return htmlContent; - } - - const ret = this.getHTMLCommon(DisplayFieldNames, FreeRTOSItems, this.finalThreads, this.timeInfo); - htmlContent.html = msg + ret.html + (this.helpHtml || ''); - htmlContent.css = ret.css; - - this.lastValidHtmlContent = htmlContent; - // console.log(this.lastValidHtmlContent.html); - return this.lastValidHtmlContent; - } -} diff --git a/src/frontend/rtos/rtos-ucosii.ts b/src/frontend/rtos/rtos-ucosii.ts deleted file mode 100644 index add49d6b..00000000 --- a/src/frontend/rtos/rtos-ucosii.ts +++ /dev/null @@ -1,652 +0,0 @@ -import * as vscode from 'vscode'; -import { DebugProtocol } from '@vscode/debugprotocol'; -import * as RTOSCommon from './rtos-common'; -import { hexFormat } from '../utils'; -import { HrTimer } from '../../common'; - -// We will have two rows of headers for uC/OS-II and the table below describes -// the columns headers for the two rows and the width of each column as a fraction -// of the overall space. -enum DisplayFields { - ID, - Address, - TaskName, - Status, - Priority, - StackPercent, - StackPeakPercent -} - -const RTOSUCOS2Items: { [key: string]: RTOSCommon.DisplayColumnItem } = {}; -RTOSUCOS2Items[DisplayFields[DisplayFields.ID]] = { width: 1, headerRow1: '', headerRow2: 'ID', colType: RTOSCommon.ColTypeEnum.colTypeNumeric }; -RTOSUCOS2Items[DisplayFields[DisplayFields.Address]] = { width: 2, headerRow1: '', headerRow2: 'Address', colGapBefore: 1 }; -RTOSUCOS2Items[DisplayFields[DisplayFields.TaskName]] = { width: 4, headerRow1: '', headerRow2: 'Name', colGapBefore: 1 }; -RTOSUCOS2Items[DisplayFields[DisplayFields.Status]] = { - width: 4, headerRow1: 'Thread', headerRow2: 'Status', colType: RTOSCommon.ColTypeEnum.colTypeCollapse -}; -RTOSUCOS2Items[DisplayFields[DisplayFields.Priority]] = { - width: 1, headerRow1: 'Prio', headerRow2: 'rity', colType: RTOSCommon.ColTypeEnum.colTypeNumeric, colGapAfter: 1 -}; // 3 are enough but 4 aligns better with header text -RTOSUCOS2Items[DisplayFields[DisplayFields.StackPercent]] = { - width: 4, headerRow1: 'Stack Usage', headerRow2: '% (Used B / Size B)', colType: RTOSCommon.ColTypeEnum.colTypePercentage -}; -RTOSUCOS2Items[DisplayFields[DisplayFields.StackPeakPercent]] = { - width: 4, headerRow1: 'Stack Peak Usage', headerRow2: '% (Peak B / Size B)', colType: RTOSCommon.ColTypeEnum.colTypePercentage -}; - -const DisplayFieldNames: string[] = Object.keys(RTOSUCOS2Items); - -export class RTOSUCOS2 extends RTOSCommon.RTOSBase { - // We keep a bunch of variable references (essentially pointers) that we can use to query for values - // Since all of them are global variable, we only need to create them once per session. These are - // similar to Watch/Hover variables - private OSRunning: RTOSCommon.RTOSVarHelper; - private OSRunningVal: number; - - private stackEntrySize: number = 0; - - private OSTaskCtr: RTOSCommon.RTOSVarHelper; - private OSTaskCtrVal: number; - - private OSTCBList: RTOSCommon.RTOSVarHelper; - - private OSTCBCur: RTOSCommon.RTOSVarHelper; - private OSTCBCurVal: number; - - private OSFlagTbl: RTOSCommon.RTOSVarHelper; - - private stale: boolean; - private foundThreads: RTOSCommon.RTOSThreadInfo[] = []; - private finalThreads: RTOSCommon.RTOSThreadInfo[] = []; - private timeInfo: string; - private readonly maxThreads = 1024; - - private stackPattern = 0x00; - private stackIncrements = -1; // negative numbers => OS_STK_GROWTH = OS_STK_GROWTH_HI_TO_LO (1) - - private helpHtml: string = undefined; - - constructor(public session: vscode.DebugSession) { - super(session, 'uC/OS-II'); - - if (session.configuration.rtosViewConfig) { - if (session.configuration.rtosViewConfig.stackPattern) { - this.stackPattern = parseInt(session.configuration.rtosViewConfig.stackPattern); - } - - if (session.configuration.rtosViewConfig.stackGrowth) { - this.stackIncrements = parseInt(session.configuration.rtosViewConfig.stackGrowth); - } - } - } - - public async tryDetect(useFrameId: number): Promise { - this.progStatus = 'stopped'; - try { - if (this.status === 'none') { - // We only get references to all the interesting variables. Note that any one of the following can fail - // and the caller may try again until we know that it definitely passed or failed. Note that while we - // re-try everything, we do remember what already had succeeded and don't waste time trying again. That - // is how this.getVarIfEmpty() works - this.OSRunning = await this.getVarIfEmpty(this.OSRunning, useFrameId, 'OSRunning', false); - this.OSTaskCtr = await this.getVarIfEmpty(this.OSTaskCtr, useFrameId, 'OSTaskCtr', false); - this.OSTCBList = await this.getVarIfEmpty(this.OSTCBList, useFrameId, 'OSTCBList', false); - this.OSTCBCur = await this.getVarIfEmpty(this.OSTCBCur, useFrameId, 'OSTCBCur', false); - this.OSFlagTbl = await this.getVarIfEmpty(this.OSFlagTbl, useFrameId, 'OSFlagTbl', true); - this.status = 'initialized'; - } - return this; - } catch (e) { - if (e instanceof RTOSCommon.ShouldRetry) { - console.error(e.message); - } else { - this.status = 'failed'; - this.failedWhy = e; - } - return this; - } - } - - protected createHmlHelp(th: RTOSCommon.RTOSThreadInfo, thInfo: object) { - if (this.helpHtml === undefined) { - this.helpHtml = ''; - try { - let ret: string = ''; - function strong(text: string) { - return `${text}`; - } - - if (!thInfo['OSTCBTaskName-val']) { - ret += `Thread name missing: Enable macro ${strong('OS_TASK_NAME_EN')} and use ${strong('OSTaskNameSet')} in FW

`; - } - if (!thInfo['OSTCBId-val'] || !th.stackInfo.stackSize) { - ret += `Thread ID & Stack Size & Peak missing: Enable macro ${strong('OS_TASK_CREATE_EXT_EN')} and` - + `use ${strong('OSTaskCreateExt')} in FW

`; - } - - if (ret) { - ret += 'Note: Make sure you consider the performance/resources impact for any changes to your FW.
\n'; - this.helpHtml = '\n' - + `

\n${ret}\n

\n`; - } - } catch (e) { - console.log(e); - } - } - } - - public refresh(frameId: number): Promise { - return new Promise((resolve, reject) => { - if (this.progStatus !== 'stopped') { - resolve(); - return; - } - - const timer = new HrTimer(); - this.stale = true; - this.timeInfo = (new Date()).toISOString(); - - // OSRunning & OSTaskCtr can go invalid anytime. Like when a reset/restart happens - this.OSTaskCtrVal = Number.MAX_SAFE_INTEGER; - this.OSRunningVal = Number.MAX_SAFE_INTEGER; - this.foundThreads = []; - - this.OSRunning.getValue(frameId).then(async (str) => { - try { - this.OSRunningVal = str ? parseInt(str) : 0; - - if (0 !== this.OSRunningVal) { - const count = await this.OSTaskCtr.getValue(frameId); - this.OSTaskCtrVal = count ? parseInt(count) : Number.MAX_SAFE_INTEGER; - - if ((this.OSTaskCtrVal > 0) && (this.OSTaskCtrVal <= this.maxThreads)) { - const OSTCBListVal = await this.OSTCBList.getValue(frameId); - if (OSTCBListVal && (0 !== parseInt(OSTCBListVal))) { - if (this.stackEntrySize === 0) { - /* Only get stack entry size once per session */ - const stackEntrySizeRef = await this.getExprVal('sizeof(OS_STK)', frameId); - this.stackEntrySize = parseInt(stackEntrySizeRef); - } - - const osFlagTblVal = this.OSFlagTbl ? await this.OSFlagTbl.getVarChildren(frameId) : []; - const flagPendMap = await this.getPendingFlagGroupsForTasks(osFlagTblVal, frameId); - - const tmpOSTCBCurVal = await this.OSTCBCur.getValue(frameId); - this.OSTCBCurVal = tmpOSTCBCurVal ? parseInt(tmpOSTCBCurVal) : Number.MAX_SAFE_INTEGER; - - await this.getThreadInfo(this.OSTCBList, flagPendMap, frameId); - - if (this.foundThreads[0].display['ID'].text !== '???') { - this.foundThreads.sort((a, b) => parseInt(a.display['ID'].text) - parseInt(b.display['ID'].text)); - } else { - this.foundThreads.sort((a, b) => parseInt(a.display['Address'].text) - parseInt(b.display['Address'].text)); - } - } - this.finalThreads = [...this.foundThreads]; - } else { - this.finalThreads = []; - } - } else { - this.finalThreads = []; - } - - this.stale = false; - this.timeInfo += ' in ' + timer.deltaMs() + ' ms'; - resolve(); - } catch (e) { - resolve(); - console.error('RTOSUCOS2.refresh() failed: ', e); - } - }, (reason) => { - resolve(); - console.error('RTOSUCOS2.refresh() failed: ', reason); - }); - }); - } - - private getThreadInfo(tcbListEntry: RTOSCommon.RTOSVarHelper, flagPendMap: Map, frameId: number): Promise { - return new Promise((resolve, reject) => { - if (!tcbListEntry || !tcbListEntry.varReference || (this.foundThreads.length >= this.OSTaskCtrVal)) { - resolve(); - return; - } - - if (this.progStatus !== 'stopped') { - reject(new Error('Busy')); - return; - } - - tcbListEntry.getVarChildrenObj(frameId).then(async (obj) => { - try { - let curTaskObj = obj; - let thAddress = parseInt(tcbListEntry.value); - - let threadCount = 1; - - do { - const threadId = curTaskObj['OSTCBId-val']; - - let thName = '???'; - if (curTaskObj['OSTCBTaskName-exp']) { - const tmpThName = await this.getExprVal('(char *)' + curTaskObj['OSTCBTaskName-exp'], frameId); - const matchName = tmpThName.match(/"([^*]*)"$/); - thName = matchName ? matchName[1] : tmpThName; - } - - const threadRunning = (thAddress === this.OSTCBCurVal); - const thStateObject = (await this.analyzeTaskState(thAddress, curTaskObj, flagPendMap, frameId)); - const thState = thStateObject.describe(); - - const stackInfo = await this.getStackInfo(curTaskObj, this.stackPattern, frameId); - - const display: { [key: string]: RTOSCommon.DisplayRowItem } = {}; - const mySetter = (x: DisplayFields, text: string, value?: any) => { - display[DisplayFieldNames[x]] = { text, value }; - }; - - mySetter(DisplayFields.ID, (threadId ? parseInt(threadId).toString() : '???')); - mySetter(DisplayFields.Address, hexFormat(thAddress)); - mySetter(DisplayFields.TaskName, thName); - mySetter(DisplayFields.Status, threadRunning ? 'RUNNING' : thState, thStateObject.fullData()); - mySetter(DisplayFields.Priority, parseInt(curTaskObj['OSTCBPrio-val']).toString()); - - if ((stackInfo.stackUsed !== undefined) && (stackInfo.stackSize !== undefined)) { - const stackPercentVal = Math.round((stackInfo.stackUsed / stackInfo.stackSize) * 100); - const stackPercentText = `${stackPercentVal} % (${stackInfo.stackUsed} / ${stackInfo.stackSize})`; - mySetter(DisplayFields.StackPercent, stackPercentText, stackPercentVal); - } else { - mySetter(DisplayFields.StackPercent, '?? %'); - } - - if ((stackInfo.stackPeak !== undefined) && (stackInfo.stackSize !== undefined)) { - const stackPeakPercentVal = Math.round((stackInfo.stackPeak / stackInfo.stackSize) * 100); - const stackPeakPercentText = `${stackPeakPercentVal.toString().padStart(3)} % (${stackInfo.stackPeak} / ${stackInfo.stackSize})`; - mySetter(DisplayFields.StackPeakPercent, stackPeakPercentText, stackPeakPercentVal); - } else { - mySetter(DisplayFields.StackPeakPercent, '?? %'); - } - - const thread: RTOSCommon.RTOSThreadInfo = { - display: display, stackInfo: stackInfo, running: threadRunning - }; - this.foundThreads.push(thread); - this.createHmlHelp(thread, curTaskObj); - - thAddress = parseInt(curTaskObj['OSTCBNext-val']); - if (0 !== thAddress) { - const nextThreadObj = await this.getVarChildrenObj(curTaskObj['OSTCBNext-ref'], 'OSTCBNext'); - curTaskObj = nextThreadObj; - threadCount++; - } - - if (threadCount > this.OSTaskCtrVal) { - console.log('RTOSUCOS2.getThreadInfo() detected more threads in OSTCBCur linked list that OSTaskCtr states'); - break; - } - } while (0 !== thAddress); - - resolve(); - } catch (e) { - console.log('RTOSUCOS2.getThreadInfo() error', e); - } - }, (e) => { - reject(e); - }); - }); - } - - protected async getEventInfo(address: number, eventObject: object, frameId: number): Promise { - const eventInfo: EventInfo = { address, eventType: parseInt(eventObject['OSEventType-val']) }; - - if (eventObject['OSEventName-val']) { - const value = eventObject['OSEventName-val']; - const matchName = value.match(/"(.*)"$/); - eventInfo.name = matchName ? matchName[1] : value; - } - - return eventInfo; - } - - protected async readEventArray(baseAddress: number, frameId: number): Promise { - const result = []; - for (let eventIndex = 0; ; eventIndex++) { - const eventAddress = parseInt(await this.getExprVal(`((OS_EVENT**)(${baseAddress}))[${eventIndex}]`, frameId)); - if (eventAddress === 0) { - break; - } else { - const eventObject = await this.getExprValChildrenObj(`(OS_EVENT*)(${eventAddress})`, frameId); - result.push(await this.getEventInfo(eventAddress, eventObject, frameId)); - } - } - return result; - } - - protected async analyzeTaskState(threadAddr: number, curTaskObj: object, flagPendMap: Map, frameId: number): Promise { - const state = parseInt(curTaskObj['OSTCBStat-val']) as OsTaskState; - switch (state) { - case OsTaskState.READY: return new TaskReady(); - case OsTaskState.SUSPENDED: return new TaskSuspended(); - default: { - const resultState = new TaskPending(); - PendingTaskStates.forEach((candidateState) => { - if ((state & candidateState)) { - resultState.addEventType(getEventTypeForTaskState(candidateState)); - } - }); - if (curTaskObj['OSTCBEventPtr-val']) { - const eventAddress = parseInt(curTaskObj['OSTCBEventPtr-val']); - if (eventAddress !== 0) { - const event = await this.getVarChildrenObj(curTaskObj['OSTCBEventPtr-ref'], 'OSTCBEventPtr'); - const eventInfo = await this.getEventInfo(eventAddress, event, frameId); - resultState.addEvent(eventInfo); - } - } - if (curTaskObj['OSTCBEventMultiPtr-val']) { - const eventMultiBaseAddress = parseInt(curTaskObj['OSTCBEventMultiPtr-val']); - if (eventMultiBaseAddress !== 0) { - (await this.readEventArray(eventMultiBaseAddress, frameId)).forEach( - (eventInfo) => resultState.addEvent(eventInfo) - ); - } - } - if (flagPendMap.has(threadAddr)) { - flagPendMap.get(threadAddr).forEach((flagGroup) => - resultState.addEvent({ name: flagGroup.name, eventType: OsEventType.Flag, address: flagGroup.address }) - ); - } - return resultState; - } - } - } - - protected async getPendingFlagGroupsForTasks(osFlagTable: DebugProtocol.Variable[], frameId: number): Promise> { - // Builds a map from task IDs to flag groups that the tasks are pending on - const result: Map = new Map(); - for (const flagGroupPtr of osFlagTable) { - if (flagGroupPtr.variablesReference > 0 && flagGroupPtr.evaluateName) { - const osFlagGrp = await this.getVarChildrenObj(flagGroupPtr.variablesReference, flagGroupPtr.name); - // Check if we are looking at an initialized flag group - if (parseInt(osFlagGrp['OSFlagType-val']) as OsEventType === OsEventType.Flag) { - const groupAddr = parseInt(await this.getExprVal(`&(${flagGroupPtr.evaluateName})`, frameId)); - const flagGroup: FlagGroup = { address: groupAddr }; - const reprValue = osFlagGrp['OSFlagName-val']; - if (reprValue) { - const matchName = reprValue.match(/"(.*)"$/); - flagGroup.name = matchName ? matchName[1] : reprValue; - } - - // Follow the linked list of flag group nodes. The cast is safe here because we checked OSFlagType before - let flagNode = await this.getExprValChildrenObj(`(OS_FLAG_NODE *)(${osFlagGrp['OSFlagWaitList-exp']})`, frameId); - while (flagNode) { - const waitingTcbAddr = parseInt(flagNode['OSFlagNodeTCB-val']); - if (!waitingTcbAddr || waitingTcbAddr === 0) { - break; - } - - if (!result.has(waitingTcbAddr)) { - result.set(waitingTcbAddr, []); - } - result.get(waitingTcbAddr).push(flagGroup); - - const nextFlagNodeAddr = parseInt(flagNode['OSFlagNodeNext-val']); - if (nextFlagNodeAddr === 0) { - break; - } else { - // Need to cast here since the next pointer is declared as void * - flagNode = await this.getExprValChildrenObj(`(OS_FLAG_NODE *) ${nextFlagNodeAddr}`, frameId); - } - } - } - } - } - return result; - } - - protected async getStackInfo(thInfo: any, stackPattern: number, frameId: number) { - const TopOfStack = thInfo['OSTCBStkPtr-val']; - - /* only available with OS_TASK_CREATE_EXT_EN (optional) */ - const EndOfStack = thInfo['OSTCBStkBottom-val']; - const StackSize = thInfo['OSTCBStkSize-val']; - - let Stack = 0; - if (EndOfStack && StackSize) { - if (this.stackIncrements < 0) { - Stack = parseInt(EndOfStack) + (parseInt(StackSize) * this.stackEntrySize); - } else { - Stack = parseInt(EndOfStack) - (parseInt(StackSize) * this.stackEntrySize); - } - } else { - /* As stackStart is mandatory, we need to set it to some reasonable value */ - Stack = parseInt(TopOfStack); - } - - const stackInfo: RTOSCommon.RTOSStackInfo = { - stackStart: Stack, - stackTop: parseInt(TopOfStack) - }; - - if (EndOfStack && StackSize) { - stackInfo.stackEnd = parseInt(EndOfStack); - stackInfo.stackSize = parseInt(StackSize) * this.stackEntrySize; - - if (this.stackIncrements < 0) { - const stackDelta = stackInfo.stackStart - stackInfo.stackTop; - stackInfo.stackFree = stackInfo.stackSize - stackDelta; - stackInfo.stackUsed = stackDelta; - } else { - const stackDelta = stackInfo.stackTop - stackInfo.stackStart; - stackInfo.stackFree = stackDelta; - stackInfo.stackUsed = stackInfo.stackSize - stackDelta; - } - - /* check stack peak */ - const memArg: DebugProtocol.ReadMemoryArguments = { - memoryReference: hexFormat(Math.min(stackInfo.stackTop, stackInfo.stackEnd)), - count: stackInfo.stackFree - }; - try { - const stackData = await this.session.customRequest('readMemory', memArg); - const buf = Buffer.from(stackData.data, 'base64'); - stackInfo.bytes = new Uint8Array(buf); - let start = this.stackIncrements < 0 ? 0 : stackInfo.bytes.length - 1; - const end = this.stackIncrements < 0 ? stackInfo.bytes.length : -1; - let peak = 0; - while (start !== end) { - if (stackInfo.bytes[start] !== stackPattern) { - break; - } - start -= this.stackIncrements; - peak++; - } - stackInfo.stackPeak = stackInfo.stackSize - peak; - } catch (e) { - console.log(e); - } - } - - return stackInfo; - } - - public lastValidHtmlContent: RTOSCommon.HtmlInfo = { html: '', css: '' }; - public getHTML(): RTOSCommon.HtmlInfo { - const htmlContent: RTOSCommon.HtmlInfo = { - html: '', css: '' - }; - // WARNING: This stuff is super fragile. Once we know how this works, then we should refactor this - let msg = ''; - if (this.status === 'none') { - htmlContent.html = '

RTOS not yet fully initialized. Will occur next time program pauses

\n'; - return htmlContent; - } else if (this.stale) { - const lastHtmlInfo = this.lastValidHtmlContent; - if (this.OSTaskCtrVal === Number.MAX_SAFE_INTEGER) { - msg = ' Could not read "OSTaskCtr". Perhaps program is busy or did not stop long enough'; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (this.OSTaskCtrVal > this.maxThreads) { - msg = ` uC/OS-II variable OSTaskCtr = ${this.OSTaskCtrVal} seems invalid`; - lastHtmlInfo.html = ''; - lastHtmlInfo.css = ''; - } else if (lastHtmlInfo.html) { // TODO check if this check is ok - msg = ' Following info from last query may be stale.'; - } - - htmlContent.html = `

Unable to collect full RTOS information.${msg}

\n` + lastHtmlInfo.html; - htmlContent.css = lastHtmlInfo.css; - return htmlContent; - } else if ((this.OSTaskCtrVal !== Number.MAX_SAFE_INTEGER) && (this.finalThreads.length !== this.OSTaskCtrVal)) { - msg += `

Expecting ${this.OSTaskCtrVal} threads, found ${this.finalThreads.length}. Thread data may be unreliable

\n`; - } else if (this.finalThreads.length === 0) { - htmlContent.html = `

No ${this.name} threads detected, perhaps RTOS not yet initialized or tasks yet to be created!

\n`; - return htmlContent; - } - - const ret = this.getHTMLCommon(DisplayFieldNames, RTOSUCOS2Items, this.finalThreads, this.timeInfo); - htmlContent.html = msg + ret.html + (this.helpHtml || ''); - htmlContent.css = ret.css; - - this.lastValidHtmlContent = htmlContent; - // console.log(this.lastValidHtmlContent.html); - return this.lastValidHtmlContent; - } -} - -enum OsTaskState { - READY = 0x00, - SUSPENDED = 0x08, - PEND_SEMAPHORE = 0x01, - PEND_MAILBOX = 0x02, - PEND_QUEUE = 0x04, - PEND_MUTEX = 0x10, - PEND_FLAGGROUP = 0x20 -} - -const PendingTaskStates = [ - OsTaskState.PEND_SEMAPHORE, - OsTaskState.PEND_MAILBOX, - OsTaskState.PEND_QUEUE, - OsTaskState.PEND_MUTEX, - OsTaskState.PEND_FLAGGROUP, -]; - -enum OsEventType { - Mailbox = 1, - Queue = 2, - Semaphore = 3, - Mutex = 4, - Flag = 5 -} - -function getEventTypeForTaskState(state: OsTaskState): OsEventType { - switch (state) { - case OsTaskState.PEND_SEMAPHORE: return OsEventType.Semaphore; - case OsTaskState.PEND_MAILBOX: return OsEventType.Mailbox; - case OsTaskState.PEND_QUEUE: return OsEventType.Queue; - case OsTaskState.PEND_MUTEX: return OsEventType.Mutex; - case OsTaskState.PEND_FLAGGROUP: return OsEventType.Flag; - } -} - -abstract class TaskState { - public abstract describe(): string; - public abstract fullData(): any; -} - -class TaskReady extends TaskState { - public describe(): string { - return 'READY'; - } - - public fullData(): any { - return null; - } -} - -class TaskSuspended extends TaskState { - public describe(): string { - return 'SUSPENDED'; - } - - public fullData(): any { - return null; - } -} - -class TaskPending extends TaskState { - private pendingInfo: Map; - - constructor() { - super(); - this.pendingInfo = new Map(); - } - - public addEvent(event: EventInfo) { - this.addEventType(event.eventType); - this.pendingInfo.get(event.eventType).push(event); - } - - public addEventType(eventType: OsEventType) { - if (!this.pendingInfo.has(eventType)) { - this.pendingInfo.set(eventType, []); - } - } - - public describe(): string { - // Converting to an array here is inefficient, but JS has no builtin iterator map/reduce feature - const eventCount = [...this.pendingInfo.values()].reduce((acc, events) => acc + events.length, 0); - - if (eventCount <= 1) { - let event: EventInfo = null; - for (const events of this.pendingInfo.values()) { - if (events.length > 0) { - event = events[0]; - } - } - - if (event) { - const eventTypeStr = OsEventType[event.eventType] ? OsEventType[event.eventType] : 'Unknown'; - return `PEND ${eventTypeStr}: ${describeEvent(event)}`; - } else { - // This should not happen, but we still keep it as a fallback - return 'PEND Unknown'; - } - } else { - return 'PEND MULTI'; - } - } - - public fullData() { - // Build an object containing mapping event types to event descriptions - const result = {}; - const eventTypes = [...this.pendingInfo.keys()]; - eventTypes.sort(); - for (const eventType of eventTypes) { - result[OsEventType[eventType]] = []; - for (const event of this.pendingInfo.get(eventType)) { - result[OsEventType[eventType]].push(describeEvent(event)); - } - } - - return result; - } -} - -interface EventInfo { - name?: string; - address: number; - eventType: OsEventType; -} - -function describeEvent(event: EventInfo): string { - if (event.name && event.name !== '?') { - return event.name; - } else { - return `0x${event.address.toString(16)}`; - } -} - -interface FlagGroup { - name?: string; - address: number; -} diff --git a/src/frontend/rtos/rtos.ts b/src/frontend/rtos/rtos.ts deleted file mode 100644 index ffc96ac8..00000000 --- a/src/frontend/rtos/rtos.ts +++ /dev/null @@ -1,494 +0,0 @@ -import { DebugProtocol } from '@vscode/debugprotocol'; -import * as vscode from 'vscode'; -import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'path'; -import * as RTOSCommon from './rtos-common'; -import { RTOSFreeRTOS } from './rtos-freertos'; -import { RTOSUCOS2 } from './rtos-ucosii'; -import { RTOSEmbOS } from './rtos-embos'; -import { RTOSChibiOS } from './rtos-chibios'; - -const RTOS_TYPES = { - 'FreeRTOS': RTOSFreeRTOS, - 'uC/OS-II': RTOSUCOS2, - 'embOS': RTOSEmbOS - // 'ChibiOS': RTOSChibiOS -}; -export class RTOSSession { - public lastFrameId: number; - public htmlContent: RTOSCommon.HtmlInfo = { html: '', css: '' }; - public rtos: RTOSCommon.RTOSBase; // The final RTOS - private allRTOSes: RTOSCommon.RTOSBase[] = []; - public triedAndFailed = false; - - constructor(public session: vscode.DebugSession) { - this.lastFrameId = undefined; - for (const rtosType of Object.keys(RTOS_TYPES)) { - this.allRTOSes.push(new RTOS_TYPES[rtosType](session)); - } - } - - // This is the work horse. Do not call it if the panel is in disabled state. - public async onStopped(frameId: number): Promise { - return new Promise(async (resolve, reject) => { - this.lastFrameId = frameId; - const doRefresh = () => { - if (this.rtos) { - this.htmlContent.html = '

Failed to get RTOS information. Please report an issue if RTOS is actually running

\n'; - this.htmlContent.css = ''; - this.rtos.onStopped(frameId).then(() => { - this.htmlContent = this.rtos.getHTML(); - resolve(); - }); - } else { - this.triedAndFailed = true; - this.htmlContent.html = ''; - this.htmlContent.css = ''; - resolve(); - } - }; - - if (this.rtos === undefined && this.allRTOSes.length > 0) { - // Let them all work in parallel. Since this will generate a ton of gdb traffic and traffic from other sources - // like variable, watch windows, things can fail. But our own backend queues things up so failures are unlikely - // With some other backend (if for instance we support cppdbg), not sure what happens. Worst case, try one OS - // at a time. - const promises = []; - for (const rtos of this.allRTOSes) { - promises.push(rtos.tryDetect(frameId)); - } - - Promise.all(promises).then((results) => { - for (const rtos of results) { - if (rtos.status === 'failed') { - const ix = this.allRTOSes.findIndex((v) => v === rtos); - this.allRTOSes.splice(ix, 1); - if (this.allRTOSes.length === 0) { - doRefresh(); - break; - } - } else if (rtos.status === 'initialized') { - this.allRTOSes = []; - this.rtos = rtos; - doRefresh(); - break; - } - } - if (this.allRTOSes.length > 0) { - // Some RTOSes have not finished detection - this.htmlContent.html = '

RTOS detection in progress...

\n'; - this.htmlContent.css = ''; - resolve(); - } - }); - } else { - doRefresh(); - } - }); - } - - public onContinued(): void { - this.lastFrameId = undefined; - if (this.rtos) { - this.rtos.onContinued(); - } - } - - public onExited(): void { - if (this.rtos) { - this.rtos.onExited(); - } - this.lastFrameId = undefined; - this.rtos = undefined; - } - - public refresh(): Promise { - if (this.lastFrameId !== undefined) { - return this.onStopped(this.lastFrameId); - } - return new Promise((r) => r()); - } -} - -interface DebugStopRunEvent { - onStopped(session: vscode.DebugSession, frameId: number); - onContinued(session: vscode.DebugSession); -} -class DebuggerTracker implements vscode.DebugAdapterTracker { - private lastFrameId: number = undefined; - constructor( - public session: vscode.DebugSession, - protected handler: DebugStopRunEvent - ) { } - - public onDidSendMessage(msg: any): void { - const message = msg as DebugProtocol.ProtocolMessage; - if (!message) { - return; - } - switch (message.type) { - case 'event': { - const ev: DebugProtocol.Event = message as DebugProtocol.Event; - if (ev) { - if (ev.event === 'stopped') { - this.lastFrameId = undefined; - } else if (ev.event === 'continued') { - // cppdbg does not issue a continued event - this.handler.onContinued(this.session); - } - } - break; - } - case 'response': { - const rsp: DebugProtocol.Response = message as DebugProtocol.Response; - if (rsp) { - const continueCommands = ['continue', 'reverseContinue', 'step', 'stepIn', 'stepOut', 'stepBack', 'next', 'goto']; - // We don't actually do anything when the session is paused. We wait until someone (VSCode) makes - // a stack trace request and we get the frameId from there. Any one will do. Either this or we - // have to make our requests for threads, scopes, stackTrace, etc. Unnecessary traffic and work - // for the adapter. Downside is if no stackTrace is requested by someone else, then we don't do anything - // but then who is the main client for the adapter? - if (rsp.command === 'stackTrace') { - if ( - rsp.body?.stackFrames - && rsp.body.stackFrames.length > 0 - && this.lastFrameId === undefined - ) { - this.lastFrameId = rsp.body.stackFrames[0].id; - this.handler.onStopped(this.session, this.lastFrameId); - } - } else if (rsp.success && continueCommands.includes(rsp.command)) { - this.handler.onContinued(this.session); - } - } - break; - } - default: { - // console.log('Unhandled Message type ' + message.type); - break; - } - } - } -} - -export class RTOSTracker implements vscode.DebugAdapterTrackerFactory, DebugStopRunEvent { - private sessionMap: Map = new Map(); - private provider: RTOSViewProvider; - public enabled: boolean; - public visible: boolean; - - constructor(private context: vscode.ExtensionContext) { - this.provider = new RTOSViewProvider(context.extensionUri, this); - const config = vscode.workspace.getConfiguration('cortex-debug', null); - - this.enabled = config.get('showRTOS', false); - this.visible = this.enabled; - vscode.commands.executeCommand('setContext', 'cortex-debug:showRTOS', this.enabled); - context.subscriptions.push( - vscode.debug.onDidStartDebugSession(this.debugSessionStarted.bind(this)), - vscode.debug.onDidTerminateDebugSession( - this.debugSessionTerminated.bind(this) - ), - vscode.debug.registerDebugAdapterTrackerFactory('cortex-debug', this), - vscode.debug.registerDebugAdapterTrackerFactory('cppdbg', this), - vscode.window.registerWebviewViewProvider(RTOSViewProvider.viewType, this.provider), - vscode.workspace.onDidChangeConfiguration(this.settingsChanged.bind(this)), - vscode.commands.registerCommand('cortex-debug.rtos.toggleRTOSPanel', this.toggleRTOSPanel.bind(this)), - vscode.commands.registerCommand('cortex-debug.rtos.refresh', this.update.bind(this)) - ); - } - - private settingsChanged(e: vscode.ConfigurationChangeEvent) { - if (e.affectsConfiguration('cortex-debug.showRTOS')) { - const config = vscode.workspace.getConfiguration('cortex-debug', null); - this.enabled = config.get('showRTOS', false); - vscode.commands.executeCommand('setContext', 'cortex-debug:showRTOS', this.enabled); - this.update(); - } - } - - public createDebugAdapterTracker( - session: vscode.DebugSession - ): vscode.ProviderResult { - return new DebuggerTracker(session, this); - } - - public async onStopped(session: vscode.DebugSession, frameId: number) { - for (const rtosSession of this.sessionMap.values()) { - if (rtosSession.session.id === session.id) { - rtosSession.lastFrameId = frameId; - if (this.enabled && this.visible) { - await rtosSession.onStopped(frameId); - this.provider.updateHtml(); - } - break; - } - } - } - - public onContinued(session: vscode.DebugSession) { - for (const rtosSession of this.sessionMap.values()) { - if (rtosSession.session.id === session.id) { - rtosSession.onContinued(); - } - } - } - - private debugSessionStarted(session: vscode.DebugSession) { - this.sessionMap.set(session.id, new RTOSSession(session)); - } - - private debugSessionTerminated(session: vscode.DebugSession) { - const s = this.sessionMap.get(session.id); - if (s) { - s.onExited(); - this.sessionMap.delete(session.id); - } - } - - // Only updates the RTOS state. Only debug sessions that are currently stopped will be updated - public async updateRTOSInfo(): Promise { - const promises = []; - if (this.enabled && this.visible) { - for (const rtosSession of this.sessionMap.values()) { - promises.push(rtosSession.refresh()); - } - } - return Promise.all(promises); - } - - public toggleRTOSPanel() { - this.enabled = !this.enabled; - this.updateRTOSPanelStatus(); - } - - private updateRTOSPanelStatus() { - const config = vscode.workspace.getConfiguration('cortex-debug', null); - config.update('showRTOS', this.enabled); - vscode.commands.executeCommand('setContext', 'cortex-debug:showRTOS', this.enabled); - /* - if (this.enabled) { - this.provider.showAndFocus(); - } - this.update(); - */ - } - - public notifyPanelDisposed() { - this.enabled = this.visible = false; - this.updateRTOSPanelStatus(); - } - - public async visibilityChanged(v: boolean) { - if (v !== this.visible) { - this.visible = v; - if (this.visible) { - const msg = 'Some sessions are busy. RTOS panel will be updated when session is paused'; - for (const rtosSession of this.sessionMap.values()) { - if (rtosSession.lastFrameId === undefined) { - if (msg) { - vscode.window.showInformationMessage(msg); - break; - } - } - } - } - try { - await this.update(); - } catch (e) { - console.error('rtos.visibilityChanged', e); - } - } - } - - // Updates RTOS state and the Panel HTML - private busyHtml: RTOSCommon.HtmlInfo; - public update(): Promise { - return new Promise((resolve) => { - if (!this.enabled || !this.visible || !this.sessionMap.size) { - resolve(); - } - this.busyHtml = { html: /* html */'

Busy updating...

\n', css: '' }; - this.provider.updateHtml(); - this.updateRTOSInfo().then(() => { - this.busyHtml = undefined; - this.provider.updateHtml(); - resolve(); - }, (e) => { - this.busyHtml = undefined; - this.provider.updateHtml(); - resolve(); - }); - }); - } - - private lastGoodHtmlContent: RTOSCommon.HtmlInfo; - public getHtml(): RTOSCommon.HtmlInfo { - const ret: RTOSCommon.HtmlInfo = { html: '', css: '' }; - - if (this.busyHtml) { - return this.busyHtml; - } else if (this.sessionMap.size === 0) { - if (this.lastGoodHtmlContent) { - return this.lastGoodHtmlContent; - } else { - ret.html = '

No active/compatible debug sessions running.

\n'; - return ret; - } - } else if (!this.visible || !this.enabled) { - ret.html = '

Contents are not visible, so no html generated

\n'; - return ret; - } - - for (const rtosSession of this.sessionMap.values()) { - const name = `Session Name: "${rtosSession.session.name}"`; - if (!rtosSession.rtos) { - const nameAndStatus = name + ' -- No RTOS detected'; - ret.html += /* html */`

${nameAndStatus}

\n`; - if (rtosSession.triedAndFailed) { - const supported = Object.keys(RTOS_TYPES).join(', '); - ret.html += `

Failed to match any supported RTOS. Supported RTOSes are (${supported}). ` - + 'Please report issues and/or contribute code/knowledge to add your RTOS

\n'; - } else { - ret.html += /* html */'

Try refreshing this panel. RTOS detection may be still in progress

\n'; - } - } else { - const nameAndStatus = name + ', ' + rtosSession.rtos.name + ' detected.' + (!rtosSession.htmlContent ? ' (No data available yet)' : ''); - ret.html += /* html */`

${nameAndStatus}

\n` + rtosSession.htmlContent.html; - ret.css = rtosSession.htmlContent.css; - } - } - this.lastGoodHtmlContent = ret; - return ret; - } -} - -class RTOSViewProvider implements vscode.WebviewViewProvider { - public static readonly viewType = 'cortex-debug.rtos'; - private webviewView: vscode.WebviewView; - - constructor(private readonly extensionUri: vscode.Uri, private parent: RTOSTracker) { } - - public resolveWebviewView( - webviewView: vscode.WebviewView, - context: vscode.WebviewViewResolveContext, - token: vscode.CancellationToken - ) { - this.webviewView = webviewView; - this.parent.visible = this.webviewView.visible; - this.parent.enabled = true; - - webviewView.webview.options = { - // Allow scripts in the webview - enableScripts: true, - localResourceRoots: [this.extensionUri] - }; - this.webviewView.description = 'View RTOS internals'; - - this.webviewView.onDidDispose((e) => { - this.webviewView = undefined; - this.parent.notifyPanelDisposed(); - }); - - this.webviewView.onDidChangeVisibility((e) => { - this.parent.visibilityChanged(this.webviewView.visible); - }); - - this.updateHtml(); - - webviewView.webview.onDidReceiveMessage((msg) => { - switch (msg?.type) { - case 'refresh': { - this.parent.update(); - break; - } - } - }); - } - - public showAndFocus() { - if (this.webviewView) { - this.webviewView.show(false); - } - } - - public updateHtml() { - if (this.webviewView) { - this.webviewView.webview.html = this.getHtmlForWebview(); - // console.log(this.webviewView.webview.html); - } - } - - private getHtmlForWebview(): string { - const webview = this.webviewView?.webview; - if (!webview) { - return ''; - } - if (!this.parent.enabled) { - return /* html */` - - - - - RTOS Threads - - -

Currently disabled. Enable setting "cortex-debug.showRTOS" or use Command "Cortex Debug: Toggle RTOS Panel" to see any RTOS info

- - `; - } - const toolkitUri = getUri(webview, this.extensionUri, [ - 'node_modules', - '@vscode', - 'webview-ui-toolkit', - 'dist', - 'toolkit.js' - ]); - // Get the local path to main script run in the webview, then convert it to a uri we can use in the webview. - const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, 'resources', 'rtos.js')); - const rtosStyle = webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, 'resources', 'rtos.css')); - - const htmlInfo = this.parent.getHtml(); - // Use a nonce to only allow a specific script to be run. - const nonce = getNonce(); - const ret = /* html */` - - - - - - - - - - RTOS Threads - - - ${htmlInfo.html} - - - - `; - return ret; - } -} - -function getNonce() { - let text = ''; - const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; - for (let i = 0; i < 32; i++) { - text += possible.charAt(Math.floor(Math.random() * possible.length)); - } - return text; -} - -export function getUri(webview: vscode.Webview, extensionUri: vscode.Uri, pathList: string[]) { - return webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, ...pathList)); -} diff --git a/src/frontend/swo/core.ts b/src/frontend/swo/core.ts index fd24c6e9..77c0a545 100644 --- a/src/frontend/swo/core.ts +++ b/src/frontend/swo/core.ts @@ -19,7 +19,7 @@ import { SymbolInformation } from '../../symbols'; import { getNonce, RTTCommonDecoderOpts } from '../../common'; import { SocketRTTSource, SocketSWOSource } from './sources/socket'; -const RingBuffer = require('ringbufferjs'); +import RingBuffer from 'ringbufferjs'; enum Status { IDLE = 1, diff --git a/src/frontend/swo/decoders/common.ts b/src/frontend/swo/decoders/common.ts index dddbc18b..2fa3b190 100644 --- a/src/frontend/swo/decoders/common.ts +++ b/src/frontend/swo/decoders/common.ts @@ -3,11 +3,11 @@ import { Packet } from '../common'; export interface SWORTTDecoder { format: string; - softwareEvent(buffer: Packet); - hardwareEvent(event: Packet); - synchronized(); - lostSynchronization(); - close(); + softwareEvent(buffer: Packet): void; + hardwareEvent(event: Packet): void; + synchronized(): void; + lostSynchronization(): void; + close(): void; - dispose(); + dispose(): void; } diff --git a/src/frontend/swo/decoders/utils.ts b/src/frontend/swo/decoders/utils.ts index 4c42c3cc..db7c6ed3 100644 --- a/src/frontend/swo/decoders/utils.ts +++ b/src/frontend/swo/decoders/utils.ts @@ -11,7 +11,7 @@ export function parseFloat(buffer: Buffer): number { buffer = tmp; } - const result = FloatParser.parse(buffer); + const result: { value: number } = FloatParser.parse(buffer); return result.value; } @@ -22,7 +22,7 @@ export function parseSigned(buffer: Buffer): number { buffer = tmp; } - const result = SignedParser.parse(buffer); + const result: { value: number } = SignedParser.parse(buffer); return result.value; } @@ -33,7 +33,7 @@ export function parseUnsigned(buffer: Buffer): number { buffer = tmp; } - const result = UnsignedParser.parse(buffer); + const result: { value: number } = UnsignedParser.parse(buffer); return result.value; } @@ -55,7 +55,7 @@ export function parseUQ(buffer: Buffer, mask: number, shift: number) { return integer + (fractional / mask); } -export const decoders = { +export const decoders: { [key: string]: (buf: Buffer) => number } = { signed: parseSigned, float: parseFloat, Q8_24: (buffer) => parseQ(buffer, 0xFFFFFF, 24), diff --git a/src/frontend/swo/sources/serial.ts b/src/frontend/swo/sources/serial.ts index 7f58a834..6d651fe5 100644 --- a/src/frontend/swo/sources/serial.ts +++ b/src/frontend/swo/sources/serial.ts @@ -10,6 +10,7 @@ export class SerialSWOSource extends EventEmitter implements SWORTTSource { constructor(private device: string, private baudRate: number) { super(); + // eslint-disable-next-line @typescript-eslint/no-require-imports const { SerialPort } = require('serialport'); this.serialPort = new SerialPort({ path: device, baudRate: baudRate, autoOpen: false }); this.serialPort.on('data', (buffer) => { diff --git a/src/frontend/utils.ts b/src/frontend/utils.ts index 28e7fcc4..a45d8b66 100644 --- a/src/frontend/utils.ts +++ b/src/frontend/utils.ts @@ -65,7 +65,7 @@ export function parseDimIndex(spec: string, count: number): string[] { return components; } - if (/^([0-9]+)\-([0-9]+)$/i.test(spec)) { + if (/^([0-9]+)-([0-9]+)$/i.test(spec)) { const parts = spec.split('-').map((p) => parseInteger(p)); const start = parts[0]; const end = parts[1]; @@ -75,7 +75,7 @@ export function parseDimIndex(spec: string, count: number): string[] { throw new Error('dimIndex Element has invalid specification.'); } - const components = []; + const components: string[] = []; for (let i = 0; i < count; i++) { components.push(`${start + i}`); } @@ -83,7 +83,7 @@ export function parseDimIndex(spec: string, count: number): string[] { return components; } - if (/^[a-zA-Z]\-[a-zA-Z]$/.test(spec)) { + if (/^[a-zA-Z]-[a-zA-Z]$/.test(spec)) { const start = spec.charCodeAt(0); const end = spec.charCodeAt(2); @@ -92,7 +92,7 @@ export function parseDimIndex(spec: string, count: number): string[] { throw new Error('dimIndex Element has invalid specification.'); } - const components = []; + const components: string[] = []; for (let i = 0; i < count; i++) { components.push(String.fromCharCode(start + i)); } diff --git a/src/gdb.ts b/src/gdb.ts index 4cd7625b..75b32ad3 100755 --- a/src/gdb.ts +++ b/src/gdb.ts @@ -16,7 +16,7 @@ import { Variable, VariableObject, MIError, OurDataBreakpoint, OurInstructionBre import { TelemetryEvent, ConfigurationArguments, StoppedEvent, GDBServerController, SymbolFile, createPortName, GenericCustomEvent, quoteShellCmdLine, toStringDecHexOctBin, ADAPTER_DEBUG_MODE, defSymbolFile, CTIAction, getPathRelative, - SWOConfigureEvent + SWOConfigureEvent, RTTCommonDecoderOpts } from './common'; import { GDBServer, ServerConsoleLog } from './backend/server'; import { MINode } from './backend/mi_parse'; @@ -482,7 +482,7 @@ export class GDBDebugSession extends LoggingDebugSession { } if (args.swoConfig && args.swoConfig.decoders) { - args.swoConfig.decoders = args.swoConfig.decoders.map((dec) => { + args.swoConfig.decoders = args.swoConfig.decoders.map((dec): unknown => { if (dec.type === 'advanced' && dec.decoder && !path.isAbsolute(dec.decoder)) { dec.decoder = path.normalize(path.join(args.cwd, dec.decoder)); } @@ -495,7 +495,7 @@ export class GDBDebugSession extends LoggingDebugSession { if (dec.type === 'advanced' && dec.decoder && !path.isAbsolute(dec.decoder)) { dec.decoder = path.normalize(path.join(args.cwd, dec.decoder)); } - return dec; + return dec as RTTCommonDecoderOpts; }); } @@ -698,7 +698,7 @@ export class GDBDebugSession extends LoggingDebugSession { // For now, we unconditionally suppress events because we will recover after we run the post start commands this.disableSendStoppedEvents = true; this.sendDummyStackTrace = !attach && (!!this.args.runToEntryPoint || !this.args.breakAfterReset); - this.miDebugger.connect(commands).then(async () => { + this.miDebugger.connect(commands).then(() => { const mode = attach ? SessionMode.ATTACH : SessionMode.LAUNCH; this.started = true; this.serverController.debuggerLaunchCompleted(); @@ -1056,7 +1056,7 @@ export class GDBDebugSession extends LoggingDebugSession { } return new Promise((resolve, reject) => { - const doResolve = async () => { + const doResolve = () => { if (this.args.noDebug || (shouldContinue && this.isMIStatusStopped())) { if (mode === SessionMode.RESET) { this.sendDummyStackTrace = true; @@ -3337,7 +3337,7 @@ export class GDBDebugSession extends LoggingDebugSession { } } -function prettyStringArray(strings) { +function prettyStringArray(strings): unknown { if (typeof strings === 'object') { if (strings.length !== undefined) { return strings.join(', '); diff --git a/src/grapher/programstatsgraph.ts b/src/grapher/programstatsgraph.ts index 83edfb2b..7c80aee6 100644 --- a/src/grapher/programstatsgraph.ts +++ b/src/grapher/programstatsgraph.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ import * as d3 from 'd3'; import { Graph } from './types'; import { GraphDataSource } from './datasource'; diff --git a/src/grapher/timeseriesgraph.ts b/src/grapher/timeseriesgraph.ts index 968b43d6..20397b22 100644 --- a/src/grapher/timeseriesgraph.ts +++ b/src/grapher/timeseriesgraph.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ import * as d3 from 'd3'; import { TimeseriesGraphConfiguration, GraphPoint, Graph } from './types'; import { GraphDataSource } from './datasource'; diff --git a/src/grapher/xygraph.ts b/src/grapher/xygraph.ts index 0df3906e..26c4da0e 100644 --- a/src/grapher/xygraph.ts +++ b/src/grapher/xygraph.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ import * as d3 from 'd3'; import { Graph, XYGraphConfiguration, GraphPoint } from './types'; import { GraphDataSource } from './datasource'; diff --git a/src/jlink.ts b/src/jlink.ts index 2aaed7d9..356ed53e 100644 --- a/src/jlink.ts +++ b/src/jlink.ts @@ -3,8 +3,8 @@ import { GDBServerController, ConfigurationArguments, calculatePortMask, createPortName, SWOConfigureEvent, parseHexOrDecInt, RTTServerHelper, genDownloadCommands } from './common'; import * as os from 'os'; import { EventEmitter } from 'events'; +import { sync as commandExistsSync } from 'command-exists'; -const commandExistsSync = require('command-exists').sync; const EXECUTABLE_NAMES = ['JLinkGDBServerCLExe', 'JLinkGDBServerCL', 'JLinkGDBServer']; export class JLinkServerController extends EventEmitter implements GDBServerController { @@ -64,7 +64,7 @@ export class JLinkServerController extends EventEmitter implements GDBServerCont } public rttCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.rttConfig.enabled && !this.args.pvtIsReset) { const cfg = this.args.rttConfig; if ((this.args.request === 'launch') && cfg.clearSearch) { @@ -84,7 +84,7 @@ export class JLinkServerController extends EventEmitter implements GDBServerCont } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled) { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); diff --git a/src/live-watch-monitor.ts b/src/live-watch-monitor.ts index 12382aef..8c8108f9 100644 --- a/src/live-watch-monitor.ts +++ b/src/live-watch-monitor.ts @@ -230,14 +230,14 @@ export class VariablesHandler { if (!this.cachedChangeList) { return undefined; } const keys = Object.keys(pVar.children); if (keys.length === 0) { return undefined; } // We don't have previous children, force a refresh - const ret = []; + const ret: VariableObject[] = []; for (const key of keys) { const gdbVaName = pVar.children[key]; const childId = this.variableHandlesReverse.get(gdbVaName); if (childId === undefined) { return undefined; } - const childObj = this.variableHandles.get(childId) as any; + const childObj = this.variableHandles.get(childId) as VariableObject; ret.push(childObj); } return ret; diff --git a/src/openocd.ts b/src/openocd.ts index fd4d81e5..0852dfd4 100644 --- a/src/openocd.ts +++ b/src/openocd.ts @@ -131,7 +131,7 @@ export class OpenOCDServerController extends EventEmitter implements GDBServerCo } public rttCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.rttConfig.enabled && !this.args.pvtIsReset) { const cfg = this.args.rttConfig; if ((this.args.request === 'launch') && cfg.clearSearch) { @@ -163,7 +163,7 @@ export class OpenOCDServerController extends EventEmitter implements GDBServerCo } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled) { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); @@ -213,7 +213,7 @@ export class OpenOCDServerController extends EventEmitter implements GDBServerCo } public serverArguments(): string[] { - let serverargs = []; + let serverargs: string[] = []; // Regardless of the target processor, we will only supply the processor '0's port# // OpenOcd will increment and assign the right port-numer to the right processor @@ -416,7 +416,7 @@ export class OpenOCDServerController extends EventEmitter implements GDBServerCo if (this.tclSocket) { return Promise.resolve(); } - return new Promise(async (resolve, reject) => { + return new Promise((resolve, reject) => { if (this.tclSocket === undefined) { const tclPortName = createPortName(0, 'tclPort'); const tclPortNum = this.ports[tclPortName]; diff --git a/src/pemicro.ts b/src/pemicro.ts index 71fa6d62..7302f448 100644 --- a/src/pemicro.ts +++ b/src/pemicro.ts @@ -3,8 +3,6 @@ import { GDBServerController, ConfigurationArguments, createPortName, SWOConfigu import * as os from 'os'; import { EventEmitter } from 'events'; -const commandExistsSync = require('command-exists').sync; - export class PEServerController extends EventEmitter implements GDBServerController { public portsNeeded: string[] = ['gdbPort', 'swoPort', 'consolePort']; public name: 'PE'; @@ -85,7 +83,7 @@ export class PEServerController extends EventEmitter implements GDBServerControl public serverArguments(): string[] { const gdbport = this.ports['gdbPort']; - let serverargs = []; + let serverargs: string[] = []; serverargs.push('-startserver'); serverargs.push('-singlesession'); diff --git a/src/pyocd.ts b/src/pyocd.ts index 849a925d..641efe75 100644 --- a/src/pyocd.ts +++ b/src/pyocd.ts @@ -60,7 +60,7 @@ export class PyOCDServerController extends EventEmitter implements GDBServerCont } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled) { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); diff --git a/src/qemu.ts b/src/qemu.ts index fff6d830..3a537889 100644 --- a/src/qemu.ts +++ b/src/qemu.ts @@ -2,8 +2,8 @@ import { DebugProtocol } from '@vscode/debugprotocol'; import { GDBServerController, ConfigurationArguments, createPortName } from './common'; import * as os from 'os'; import { EventEmitter } from 'events'; +import { sync as commandExistsSync } from 'command-exists'; -const commandExistsSync = require('command-exists').sync; const EXECUTABLE_NAMES = ['qemu-system-arm']; export class QEMUServerController extends EventEmitter implements GDBServerController { @@ -38,12 +38,12 @@ export class QEMUServerController extends EventEmitter implements GDBServerContr } public launchCommands(): string[] { - const commands = []; + const commands: string[] = []; return commands; } public attachCommands(): string[] { - const commands = []; + const commands: string[] = []; return commands; } diff --git a/src/remote/src/tcpportscanner.ts b/src/remote/src/tcpportscanner.ts index 338199e4..c388bb3e 100644 --- a/src/remote/src/tcpportscanner.ts +++ b/src/remote/src/tcpportscanner.ts @@ -1,7 +1,7 @@ -import os = require('os'); -import net = require('net'); -import child_process = require('child_process'); -import command_exists = require('command-exists'); +import * as os from 'os'; +import * as net from 'net'; +import * as child_process from 'child_process'; +import * as command_exists from 'command-exists'; let logEnable = false; function ConsoleLog(...args: any) { @@ -99,7 +99,7 @@ export class TcpPortScanner { const promises = tries.map((host) => TcpPortScanner.isPortInUse(port, host)); return new Promise(async (resolve, reject) => { - const results = await Promise.all(promises.map((p) => p.then((x) => x).catch((e) => e))); + const results = await Promise.all(promises.map((p) => p.then((x) => x).catch(() => {}))); ConsoleLog('isPortInUseEx: Results', results); for (const r of results) { if (r !== false) { diff --git a/src/reporting.ts b/src/reporting.ts index 1355d2d5..10743715 100644 --- a/src/reporting.ts +++ b/src/reporting.ts @@ -2,9 +2,9 @@ import * as vscode from 'vscode'; import * as os from 'os'; import * as path from 'path'; import * as fs from 'fs'; +import * as ua from 'universal-analytics'; import { ConfigurationArguments } from './common'; -const ua = require('universal-analytics'); import { v4 as uuidv4 } from 'uuid'; const extension = vscode.extensions.getExtension('marus25.cortex-debug'); @@ -51,7 +51,7 @@ function telemetryEnabled(): boolean { const telemetry = vscode.workspace.getConfiguration('telemetry'); const cortexDebug = vscode.workspace.getConfiguration('cortex-debug'); - return (telemetry.enableTelemetry && cortexDebug.enableTelemetry); + return telemetry.get('enableTelemetry', false) && cortexDebug.get('enableTelemetry', false); } function activate(context: vscode.ExtensionContext) { diff --git a/src/stlink.ts b/src/stlink.ts index ebbbb3df..5ba01eeb 100755 --- a/src/stlink.ts +++ b/src/stlink.ts @@ -120,7 +120,7 @@ export class STLinkServerController extends EventEmitter implements GDBServerCon } public attachCommands(): string[] { - const commands = [ + const commands: string[] = [ // 'interpreter-exec console "monitor halt"', // Not needed because of --attach ]; return commands; @@ -134,7 +134,7 @@ export class STLinkServerController extends EventEmitter implements GDBServerCon } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled) { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); diff --git a/src/stutil.ts b/src/stutil.ts index 48a2c162..dcbd64ad 100644 --- a/src/stutil.ts +++ b/src/stutil.ts @@ -59,7 +59,7 @@ export class STUtilServerController extends EventEmitter implements GDBServerCon } public swoAndRTTCommands(): string[] { - const commands = []; + const commands: string[] = []; if (this.args.swoConfig.enabled && this.args.swoConfig.source !== 'probe') { const swocommands = this.SWOConfigurationCommands(); commands.push(...swocommands); diff --git a/src/tcpportscanner.ts b/src/tcpportscanner.ts index e7fe1b9d..f8901c5c 100644 --- a/src/tcpportscanner.ts +++ b/src/tcpportscanner.ts @@ -1,7 +1,7 @@ -import os = require('os'); -import net = require('net'); -import child_process = require('child_process'); -import command_exists = require('command-exists'); +import * as os from 'os'; +import * as net from 'net'; +import * as child_process from 'child_process'; +import command_exists from 'command-exists'; import { EventEmitter } from 'events'; let logEnable = false; diff --git a/test/suite/tcpportscanner.test.ts b/test/suite/tcpportscanner.test.ts index 916ca90f..beac28dd 100644 --- a/test/suite/tcpportscanner.test.ts +++ b/test/suite/tcpportscanner.test.ts @@ -2,7 +2,6 @@ import * as assert from 'assert'; import * as http from 'http'; -import os = require('os'); import { TcpPortScanner } from '../../src/tcpportscanner'; /** @@ -69,7 +68,7 @@ suite('TcpPortScanner Tests', () => { // See if the server started on the requested port. We do it two ways in (near) parallel // Both should succeed with the same timeout. See above when LISTEN starts - TcpPortScanner.waitForPortOpen(port, hostNameOrIp, true, 50, 1000).then(async () => { + TcpPortScanner.waitForPortOpen(port, hostNameOrIp, true, 50, 1000).then(() => { if (doLog) { console.log(`1. Success server port ${port} is ready ${timeIt()}`); } }, (err) => { if (doLog) { console.log(`1. Timeout: Failed waiting on port ${port} to open ${timeIt()}`, err); }