Skip to content

Commit ee6524a

Browse files
committed
[lldb-dap] Remove timings from TestDAP_attach
This PR split TestDAP_attach into two tests and removes any reliance on timing from the simple attach tests. Instead, it uses stdin and stdout to synchronize between the test and the inferior, and to check that we're attached to the correct inferior. Fixes #163295
1 parent 3b46556 commit ee6524a

File tree

5 files changed

+211
-181
lines changed

5 files changed

+211
-181
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
C_SOURCES := main.c
2+
3+
include Makefile.rules
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
"""
2+
Test lldb-dap attach commands
3+
"""
4+
5+
from lldbsuite.test.decorators import *
6+
from lldbsuite.test.lldbtest import *
7+
from lldbsuite.test import lldbutil
8+
import lldbdap_testcase
9+
import time
10+
11+
class TestDAP_attach(lldbdap_testcase.DAPTestCaseBase):
12+
@skipIfNetBSD # Hangs on NetBSD as well
13+
def test_commands(self):
14+
"""
15+
Tests the "initCommands", "preRunCommands", "stopCommands",
16+
"exitCommands", "terminateCommands" and "attachCommands"
17+
that can be passed during attach.
18+
19+
"initCommands" are a list of LLDB commands that get executed
20+
before the target is created.
21+
"preRunCommands" are a list of LLDB commands that get executed
22+
after the target has been created and before the launch.
23+
"stopCommands" are a list of LLDB commands that get executed each
24+
time the program stops.
25+
"exitCommands" are a list of LLDB commands that get executed when
26+
the process exits
27+
"attachCommands" are a list of LLDB commands that get executed and
28+
must have a valid process in the selected target in LLDB after
29+
they are done executing. This allows custom commands to create any
30+
kind of debug session.
31+
"terminateCommands" are a list of LLDB commands that get executed when
32+
the debugger session terminates.
33+
"""
34+
program = self.build_and_create_debug_adapter_for_attach()
35+
36+
# Here we just create a target and launch the process as a way to test
37+
# if we are able to use attach commands to create any kind of a target
38+
# and use it for debugging
39+
attachCommands = [
40+
'target create -d "%s"' % (program),
41+
"process launch --stop-at-entry",
42+
]
43+
initCommands = ["target list", "platform list"]
44+
preRunCommands = ["image list a.out", "image dump sections a.out"]
45+
postRunCommands = ["help trace", "help process trace"]
46+
stopCommands = ["frame variable", "thread backtrace"]
47+
exitCommands = ["expr 2+3", "expr 3+4"]
48+
terminateCommands = ["expr 4+2"]
49+
self.attach(
50+
program=program,
51+
attachCommands=attachCommands,
52+
initCommands=initCommands,
53+
preRunCommands=preRunCommands,
54+
stopCommands=stopCommands,
55+
exitCommands=exitCommands,
56+
terminateCommands=terminateCommands,
57+
postRunCommands=postRunCommands,
58+
)
59+
# Get output from the console. This should contain both the
60+
# "initCommands" and the "preRunCommands".
61+
output = self.get_console()
62+
# Verify all "initCommands" were found in console output
63+
self.verify_commands("initCommands", output, initCommands)
64+
# Verify all "preRunCommands" were found in console output
65+
self.verify_commands("preRunCommands", output, preRunCommands)
66+
# Verify all "postRunCommands" were found in console output
67+
self.verify_commands("postRunCommands", output, postRunCommands)
68+
69+
functions = ["main"]
70+
breakpoint_ids = self.set_function_breakpoints(functions)
71+
self.assertEqual(len(breakpoint_ids), len(functions), "expect one breakpoint")
72+
self.continue_to_breakpoints(breakpoint_ids)
73+
output = self.collect_console(timeout=10, pattern=stopCommands[-1])
74+
self.verify_commands("stopCommands", output, stopCommands)
75+
76+
# Continue after launch and hit the "pause()" call and stop the target.
77+
# Get output from the console. This should contain both the
78+
# "stopCommands" that were run after we stop.
79+
self.do_continue()
80+
time.sleep(0.5)
81+
self.dap_server.request_pause()
82+
self.dap_server.wait_for_stopped()
83+
output = self.collect_console(timeout=10, pattern=stopCommands[-1])
84+
self.verify_commands("stopCommands", output, stopCommands)
85+
86+
# Continue until the program exits
87+
self.continue_to_exit()
88+
# Get output from the console. This should contain both the
89+
# "exitCommands" that were run after the second breakpoint was hit
90+
# and the "terminateCommands" due to the debugging session ending
91+
output = self.collect_console(
92+
timeout=10.0,
93+
pattern=terminateCommands[0],
94+
)
95+
self.verify_commands("exitCommands", output, exitCommands)
96+
self.verify_commands("terminateCommands", output, terminateCommands)
97+
98+
def test_attach_command_process_failures(self):
99+
"""
100+
Tests that a 'attachCommands' is expected to leave the debugger's
101+
selected target with a valid process.
102+
"""
103+
program = self.build_and_create_debug_adapter_for_attach()
104+
attachCommands = ['script print("oops, forgot to attach to a process...")']
105+
resp = self.attach(
106+
program=program,
107+
attachCommands=attachCommands,
108+
expectFailure=True,
109+
)
110+
self.assertFalse(resp["success"])
111+
self.assertIn(
112+
"attachCommands failed to attach to a process",
113+
resp["body"]["error"]["format"],
114+
)
115+
116+
@skipIfNetBSD # Hangs on NetBSD as well
117+
def test_terminate_commands(self):
118+
"""
119+
Tests that the "terminateCommands", that can be passed during
120+
attach, are run when the debugger is disconnected.
121+
"""
122+
program = self.build_and_create_debug_adapter_for_attach()
123+
124+
# Here we just create a target and launch the process as a way to test
125+
# if we are able to use attach commands to create any kind of a target
126+
# and use it for debugging
127+
attachCommands = [
128+
'target create -d "%s"' % (program),
129+
"process launch --stop-at-entry",
130+
]
131+
terminateCommands = ["expr 4+2"]
132+
self.attach(
133+
program=program,
134+
attachCommands=attachCommands,
135+
terminateCommands=terminateCommands,
136+
disconnectAutomatically=False,
137+
)
138+
self.get_console()
139+
# Once it's disconnected the console should contain the
140+
# "terminateCommands"
141+
self.dap_server.request_disconnect(terminateDebuggee=True)
142+
output = self.collect_console(
143+
timeout=1.0,
144+
pattern=terminateCommands[0],
145+
)
146+
self.verify_commands("terminateCommands", output, terminateCommands)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include "attach.h"
2+
#include <stdio.h>
3+
#ifdef _WIN32
4+
#include <process.h>
5+
#include <windows.h>
6+
#else
7+
#include <unistd.h>
8+
#endif
9+
10+
int main(int argc, char const *argv[]) {
11+
lldb_enable_attach();
12+
13+
if (argc >= 2) {
14+
// Create the synchronization token.
15+
FILE *f = fopen(argv[1], "wx");
16+
if (!f)
17+
return 1;
18+
fputs("\n", f);
19+
fflush(f);
20+
fclose(f);
21+
}
22+
23+
printf("pid = %i\n", getpid());
24+
#ifdef _WIN32
25+
Sleep(10 * 1000);
26+
#else
27+
sleep(10);
28+
#endif
29+
return 0; // breakpoint 1
30+
}

0 commit comments

Comments
 (0)