Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Read stackTop for current running task with function contributed by @malsyned also in uc/OS-II.
- Add FS-RTOS support. FS-RTOS is very similar to uC/OS-II, so no need to create a own implementation for it.
- Add another tab to ThreadX to display byte pools.

## 0.0.14 - Jan 25, 2026

Expand Down
124 changes: 109 additions & 15 deletions src/rtos/rtos-threadx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,18 +112,40 @@ const MutexTableItems: { [key: string]: RTOSCommon.DisplayColumnItem } = {
},
};

const BytePoolTableItems: { [key: string]: RTOSCommon.DisplayColumnItem } = {
name: {
width: 2,
headerRow1: 'Byte Pool',
headerRow2: 'Name',
},
start: {
width: 2,
headerRow1: 'Start',
headerRow2: 'Address',
},
pool: {
width: 4,
headerRow1: '',
headerRow2: 'Usage',
colType: RTOSCommon.ColTypeEnum.colTypePercentage,
},
};

const ThreadTableItemNames: string[] = Object.keys(ThreadTableItems);
const SemaphoreTableItemNames: string[] = Object.keys(SemaphoreTableItems);
const MutexTableItemNames: string[] = Object.keys(MutexTableItems);
const BytePoolItemNames: string[] = Object.keys(BytePoolTableItems);

export class RTOSThreadX extends RTOSCommon.RTOSBase {
private threadCreatedCount: RTOSCommon.RTOSVarHelperMaybe;
private semaphoreCreatedCount: RTOSCommon.RTOSVarHelperMaybe;
private mutexCreatedCount: RTOSCommon.RTOSVarHelperMaybe;
private bytePoolCreatedCount: RTOSCommon.RTOSVarHelperMaybe;

private threads: RTOSCommon.RTOSThreadInfo[] = [];
private semaphores: RTOSCommon.RTOSDisplayInfo[] = [];
private mutexes: RTOSCommon.RTOSDisplayInfo[] = [];
private bytePools: RTOSCommon.RTOSDisplayInfo[] = [];

constructor(public session: vscode.DebugSession) {
super(session, 'ThreadX');
Expand All @@ -137,21 +159,28 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
this.threadCreatedCount,
useFrameId,
'_tx_thread_created_count',
false
false,
);

this.semaphoreCreatedCount = await this.getVarIfEmpty(
this.semaphoreCreatedCount,
useFrameId,
'_tx_semaphore_created_count',
false
false,
);

this.mutexCreatedCount = await this.getVarIfEmpty(
this.mutexCreatedCount,
useFrameId,
'_tx_mutex_created_count',
false
false,
);

this.bytePoolCreatedCount = await this.getVarIfEmpty(
this.bytePoolCreatedCount,
useFrameId,
'_tx_byte_pool_created_count',
false,
);

this.status = 'initialized';
Expand Down Expand Up @@ -189,7 +218,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
(reason) => {
resolve();
console.error('RTOSThreadX.refresh() failed: ', reason);
}
},
);

this.semaphoreCreatedCount?.getValue(frameId).then(
Expand All @@ -206,7 +235,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
(reason) => {
resolve();
console.error('RTOSThreadX.refresh() failed: ', reason);
}
},
);

this.mutexCreatedCount?.getValue(frameId).then(
Expand All @@ -223,7 +252,24 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
(reason) => {
resolve();
console.error('RTOSThreadX.refresh() failed: ', reason);
}
},
);

this.bytePoolCreatedCount?.getValue(frameId).then(
async (str) => {
try {
const numBytePools = parseInt(str ?? '') || 0;
await this.getBytePoolInfo(numBytePools, frameId);
resolve();
} catch (e) {
Comment thread
haneefdm marked this conversation as resolved.
resolve();
console.error('RTOSThreadX.refresh() failed: ', e);
}
},
(reason) => {
resolve();
console.error('RTOSThreadX.refresh() failed: ', reason);
},
);
});
}
Expand All @@ -237,18 +283,25 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
SemaphoreTableItemNames,
SemaphoreTableItems,
this.semaphores,
(_) => ''
(_) => '',
);

const htmlMutexes = this.getHTMLTable(MutexTableItemNames, MutexTableItems, this.mutexes, (_) => '');

const tabs = [{ title: 'Threads' }, { title: 'Semaphores' }, { title: 'Mutexes' }];
const views = [{ content: htmlThreads.html }, { content: htmlSemaphores.html }, { content: htmlMutexes.html }];
const htmlBytePools = this.getHTMLTable(BytePoolItemNames, BytePoolTableItems, this.bytePools, (_) => '');

const tabs = [{ title: 'Threads' }, { title: 'Semaphores' }, { title: 'Mutexes' }, { title: 'Byte Pools' }];
const views = [
{ content: htmlThreads.html },
{ content: htmlSemaphores.html },
{ content: htmlMutexes.html },
{ content: htmlBytePools.html },
];

const htmlPanels = this.getHTMLPanels(tabs, views, [], true);

htmlContent.html = htmlPanels;
htmlContent.css = htmlThreads.css;
htmlContent.css = [htmlThreads.css, htmlBytePools.css].join('\n');

return htmlContent;
}
Expand All @@ -261,7 +314,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {

let thread: RTOSCommon.RTOSStrToValueMap | undefined = await this.getExprValChildrenObj(
'_tx_thread_created_ptr',
frameId
frameId,
);

for (let i = 0; i < numThreads && thread !== undefined; i++) {
Expand Down Expand Up @@ -358,7 +411,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
let address = (await this.getExprVal('_tx_semaphore_created_ptr', frameId)) ?? '';
let semaphore: RTOSCommon.RTOSStrToValueMap | undefined = await this.getExprValChildrenObj(
'_tx_semaphore_created_ptr',
frameId
frameId,
);

for (let i = 0; i < numSemaphores && semaphore !== undefined; i++) {
Expand All @@ -369,7 +422,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {

const suspended = await this.getSuspendedThreads(
semaphore['tx_semaphore_suspension_list'],
parseInt(suspensions) || 0
parseInt(suspensions) || 0,
);

semaphores.push({
Expand Down Expand Up @@ -413,7 +466,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
let address = (await this.getExprVal('_tx_mutex_created_ptr', frameId)) ?? '';
let mutex: RTOSCommon.RTOSStrToValueMap | undefined = await this.getExprValChildrenObj(
'_tx_mutex_created_ptr',
frameId
frameId,
);

for (let i = 0; i < numMutexes && mutex !== undefined; i++) {
Expand All @@ -426,7 +479,7 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {

const suspended = await this.getSuspendedThreads(
mutex['tx_mutex_suspension_list'],
parseInt(suspensions) || 0
parseInt(suspensions) || 0,
);

mutexes.push({
Expand All @@ -447,6 +500,47 @@ export class RTOSThreadX extends RTOSCommon.RTOSBase {
this.mutexes = mutexes;
}

private async getBytePoolInfo(numBytePools: number, frameId: number): Promise<void> {
const bytePools: RTOSCommon.RTOSDisplayInfo[] = [];

let bytePool: RTOSCommon.RTOSStrToValueMap | undefined = await this.getExprValChildrenObj(
'_tx_byte_pool_created_ptr',
frameId,
);

for (let i = 0; i < numBytePools && bytePool !== undefined; i++) {
const name = this.stringFromCharPointer(bytePool['tx_byte_pool_name']?.val);
const available = parseInt(bytePool['tx_byte_pool_available']?.val ?? '') || 0;
const size = parseInt(bytePool['tx_byte_pool_size']?.val ?? '') || 0;
const poolStart = parseInt(bytePool['tx_byte_pool_start']?.val ?? '') || 0;

const startText = RTOSCommon.hexFormat(poolStart);

const used = size - available;
const usedText = `${used} / ${size}`;
let usedPercent = 0;
if (size > 0) {
const ratio = used / size;
if (Number.isFinite(ratio)) {
usedPercent = Math.max(0, Math.min(100, Math.round(ratio * 100)));
}
}

bytePools.push({
display: {
name: { text: name },
start: { text: startText },
pool: { text: usedText, value: usedPercent },
},
});

const next = bytePool['tx_byte_pool_created_next'];
bytePool = (await this.getVarChildrenObj(next?.ref ?? NaN, 'next byte pool')) ?? undefined;
}
Comment thread
haneefdm marked this conversation as resolved.

this.bytePools = bytePools;
}

private stringFromCharPointer(name: string | undefined): string {
if (name === undefined) {
return '?';
Expand Down