Skip to content

Commit ba5ce9b

Browse files
authored
New test for multiple variables in multiple threads at multiple depths (#452)
1 parent 444f8fc commit ba5ce9b

File tree

3 files changed

+144
-0
lines changed

3 files changed

+144
-0
lines changed

.vscode/launch.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,23 @@
5858
"console": "integratedTerminal",
5959
"internalConsoleOptions": "neverOpen"
6060
},
61+
{
62+
"type": "node",
63+
"request": "launch",
64+
"name": "Mocha Current File (non-stop)",
65+
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
66+
"args": [
67+
"--timeout",
68+
"999999",
69+
"--colors",
70+
"-r",
71+
"ts-node/register",
72+
"${file}",
73+
"--test-gdb-non-stop"
74+
],
75+
"console": "integratedTerminal",
76+
"internalConsoleOptions": "neverOpen"
77+
},
6178
{
6279
"type": "node",
6380
"request": "launch",

src/integration-tests/multithread.spec.ts

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe('multithread', async function () {
3838
const lineTags = {
3939
LINE_MAIN_ALL_THREADS_STARTED: 0,
4040
LINE_THREAD_IN_HELLO: 0,
41+
LINE_THREAD_INNER: 0,
4142
};
4243

4344
before(function () {
@@ -282,4 +283,113 @@ describe('multithread', async function () {
282283
});
283284
}
284285
});
286+
287+
it('gets varible values at different frames for different threads', async function () {
288+
if (!gdbNonStop) {
289+
// This test is covering only gdb-non-stop on
290+
this.skip();
291+
}
292+
293+
await dc.launchRequest(
294+
fillDefaults(this.test, {
295+
program,
296+
})
297+
);
298+
await dc.setBreakpointsRequest({
299+
source: {
300+
path: source,
301+
},
302+
breakpoints: [
303+
{
304+
line: lineTags['LINE_MAIN_ALL_THREADS_STARTED'],
305+
},
306+
{
307+
line: lineTags['LINE_THREAD_INNER'],
308+
},
309+
],
310+
});
311+
312+
const waitForStop = dc.waitForEvent('stopped');
313+
await dc.configurationDoneRequest();
314+
await waitForStop;
315+
316+
// make sure that all the threads have stopped
317+
// TODO instead of a sleep, wait until all threads have stopped
318+
await new Promise((f) => setTimeout(f, 1000));
319+
const threads = await dc.threadsRequest();
320+
const runningThreads = threads.body.threads.filter(
321+
(t) => (t as unknown as { running?: boolean }).running
322+
);
323+
expect(runningThreads).to.be.an('array').that.is.empty;
324+
const nameToId = new Map(
325+
threads.body.threads.map((thread) => [thread.name, thread.id])
326+
);
327+
// Make sure all 5 threads are there
328+
expect(nameToId).to.include.keys(Object.keys(threadNames));
329+
// and make sure that there is at least 6 threads.
330+
// We don't care about the name of the "main" thread
331+
expect(threads.body.threads).length.greaterThanOrEqual(6);
332+
333+
// check that each thread can be communicated with individually
334+
for (const [name, idInProgram] of Object.entries(threadNames)) {
335+
// There are multiple ids/indexes.
336+
// idInProgram cooresponds to the variable thread_id in the C++ source
337+
// threadId is the id of the thread in DAP
338+
const threadId = nameToId.get(name);
339+
if (threadId === undefined) {
340+
// unreachable because of expect above
341+
fail('unreachable');
342+
}
343+
344+
const stack = await dc.stackTraceRequest({ threadId });
345+
346+
// Iterate through stack frames:
347+
// Stack frame 0 is the inner method
348+
// Stack frames 1 to id + 1 are the recursive method
349+
// Stack frame id + 2 is PrintHello
350+
for (let i = 0; i < idInProgram + 3; i++) {
351+
if (i == 0) {
352+
expect(stack.body.stackFrames[i].name).to.eq(
353+
'inner_method'
354+
);
355+
} else if (i == idInProgram + 2) {
356+
expect(stack.body.stackFrames[i].name).to.eq('PrintHello');
357+
} else {
358+
expect(stack.body.stackFrames[i].name).to.eq('recursive');
359+
}
360+
361+
const scopes = await dc.scopesRequest({
362+
frameId: stack.body.stackFrames[i].id,
363+
});
364+
const vr = scopes.body.scopes[0].variablesReference;
365+
const vars = await dc.variablesRequest({
366+
variablesReference: vr,
367+
});
368+
const varnameToValue = new Map(
369+
vars.body.variables.map((variable) => [
370+
variable.name,
371+
variable.value,
372+
])
373+
);
374+
expect(varnameToValue.get('thread_id')).to.equal(
375+
idInProgram.toString()
376+
);
377+
378+
if (i == 0) {
379+
expect(varnameToValue.get('thread_id_plus_1')).to.equal(
380+
(idInProgram + 1).toString()
381+
);
382+
expect(varnameToValue.get('thread_id_plus_2')).to.equal(
383+
(idInProgram + 2).toString()
384+
);
385+
}
386+
387+
if (i > 0 && i < idInProgram + 2) {
388+
expect(varnameToValue.get('depth')).to.equal(
389+
(i - 1).toString()
390+
);
391+
}
392+
}
393+
}
394+
});
285395
});

src/integration-tests/test-programs/MultiThread.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ struct PrintHelloArgs {
1414
const char *name;
1515
};
1616

17+
static void inner_method(int thread_id)
18+
{
19+
int thread_id_plus_1 = thread_id + 1;
20+
int thread_id_plus_2 = thread_id + 2;
21+
printf("Thread %d in inner_method %d, %d\n", thread_id, thread_id_plus_1, thread_id_plus_2); /* LINE_THREAD_INNER */
22+
}
23+
static void recursive(int thread_id, int depth)
24+
{
25+
if (depth == 0) {
26+
inner_method(thread_id);
27+
} else {
28+
printf("Recursing thread %d\n", thread_id);
29+
recursive(thread_id, depth - 1);
30+
}
31+
}
32+
1733
static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg)
1834
{
1935
struct PrintHelloArgs *args = (struct PrintHelloArgs *) void_arg;
@@ -34,6 +50,7 @@ static ThreadRet THREAD_CALL_CONV PrintHello(void *void_arg)
3450
ThreadBarrierWait(barrier_start);
3551

3652
printf("Thread %d in the middle\n", thread_id); /* LINE_THREAD_IN_HELLO */
53+
recursive(thread_id, thread_id);
3754

3855
/* Make sure that the thread does not finish before the breakpoint in main hits. */
3956
ThreadBarrierWait(barrier_finish);

0 commit comments

Comments
 (0)