@@ -20,6 +20,15 @@ import { expect } from 'chai';
2020import * as path from 'path' ;
2121import { fail } from 'assert' ;
2222import * as os from 'os' ;
23+ import { ThreadContext , base64ToHex } from '../GDBDebugSession' ;
24+ import { DebugProtocol } from '@vscode/debugprotocol' ;
25+
26+ interface VariableContext {
27+ name : string ;
28+ threadId : number ;
29+ varAddress : number ;
30+ stackFramePosition : number ;
31+ }
2332
2433describe ( 'multithread' , async function ( ) {
2534 let dc : CdtDebugClient ;
@@ -50,6 +59,29 @@ describe('multithread', async function () {
5059 await dc . stop ( ) ;
5160 } ) ;
5261
62+ /**
63+ * Verify that `resp` contains the bytes `expectedBytes` and the
64+ * `expectedAddress` start address matches. In this case we know
65+ * we're searching for a string so truncate after 0 byte.
66+ *
67+ */
68+ function verifyReadMemoryResponse (
69+ resp : DebugProtocol . ReadMemoryResponse ,
70+ expectedBytes : string ,
71+ expectedAddress : number
72+ ) {
73+ const memData = base64ToHex ( resp . body ?. data ?? '' ) . toString ( ) ;
74+ const memString = Buffer . from ( memData . toString ( ) , 'hex' ) . toString ( ) ;
75+ // Only use the data before the 0 byte (truncate after)
76+ const simpleString = memString . substring ( 0 , memString . search ( / \0 / ) ) ;
77+ expect ( simpleString ) . eq ( expectedBytes ) ;
78+ expect ( resp . body ?. address ) . match ( / ^ 0 x [ 0 - 9 a - f A - F ] + $ / ) ;
79+ if ( resp . body ?. address ) {
80+ const actualAddress = parseInt ( resp . body ?. address ) ;
81+ expect ( actualAddress ) . eq ( expectedAddress ) ;
82+ }
83+ }
84+
5385 it ( 'sees all threads' , async function ( ) {
5486 if ( ! gdbNonStop && os . platform ( ) === 'win32' && isRemoteTest ) {
5587 // The way thread names are set in remote tests on windows is unsupported
@@ -182,4 +214,85 @@ describe('multithread', async function () {
182214 }
183215 }
184216 } ) ;
217+
218+ it ( 'verify threadId,frameID for multiple threads' , async function ( ) {
219+ if ( ! gdbNonStop && os . platform ( ) === 'win32' && isRemoteTest ) {
220+ // The way thread names are set in remote tests on windows is unsupported
221+ this . skip ( ) ;
222+ }
223+
224+ await dc . hitBreakpoint (
225+ fillDefaults ( this . test , {
226+ program : program ,
227+ } ) ,
228+ {
229+ path : source ,
230+ line : lineTags [ 'LINE_MAIN_ALL_THREADS_STARTED' ] ,
231+ }
232+ ) ;
233+
234+ const variableContextArray : VariableContext [ ] = [ ] ;
235+ const threads = await dc . threadsRequest ( ) ;
236+ // cycle through the threads and create an index for later
237+ for ( const threadInfo of threads . body . threads ) {
238+ // threadId is the id of the thread in DAP
239+ const threadId = threadInfo . id ;
240+ if ( threadId === undefined ) {
241+ // Shouldn't have undefined thread.
242+ fail ( 'unreachable' ) ;
243+ }
244+ if ( ! ( threadInfo . name in threadNames ) ) {
245+ continue ;
246+ }
247+
248+ if ( gdbNonStop ) {
249+ const waitForStopped = dc . waitForEvent ( 'stopped' ) ;
250+ const pr = dc . pauseRequest ( { threadId } ) ;
251+ await Promise . all ( [ pr , waitForStopped ] ) ;
252+ }
253+
254+ const stack = await dc . stackTraceRequest ( { threadId } ) ;
255+ let nameAddress : number | undefined = undefined ;
256+ let stackFramePosition = 0 ;
257+ // Frame Reference ID starts at 1000 but actual stack frame # is index.
258+ for ( const frame of stack . body . stackFrames ) {
259+ if ( frame . name === 'PrintHello' ) {
260+ // Grab the address for "name" in this thread now because
261+ // gdb-non-stop doesn't have different frame.id's for threads.
262+ const addrOfVariableResp = await dc . evaluateRequest ( {
263+ expression : 'name' ,
264+ frameId : frame . id ,
265+ } ) ;
266+ nameAddress = parseInt ( addrOfVariableResp . body . result , 16 ) ;
267+ break ;
268+ }
269+ stackFramePosition ++ ;
270+ }
271+ if ( nameAddress === undefined ) {
272+ fail ( "Failed to find address of name in 'PrintHello'" ) ;
273+ }
274+
275+ variableContextArray . push ( {
276+ name : threadInfo . name . toString ( ) ,
277+ threadId : threadInfo . id ,
278+ varAddress : nameAddress ,
279+ stackFramePosition,
280+ } ) ;
281+ }
282+ // cycle through the threads and confirm each thread name (different for each thread)
283+ for ( const context of variableContextArray ) {
284+ // Get the address of the variable.
285+ const mem = await dc . readMemoryWithContextRequest ( [
286+ {
287+ memoryReference : '0x' + context . varAddress . toString ( 16 ) ,
288+ count : 10 ,
289+ } ,
290+ {
291+ threadId : context . threadId ,
292+ frameId : context . stackFramePosition ,
293+ } as ThreadContext ,
294+ ] ) ;
295+ verifyReadMemoryResponse ( mem , context . name , context . varAddress ) ;
296+ }
297+ } ) ;
185298} ) ;
0 commit comments