Skip to content

Commit c89d721

Browse files
authored
[lldb-dap] Remove timings from TestDAP_attach (#163452)
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 22cd380 commit c89d721

File tree

5 files changed

+209
-187
lines changed

5 files changed

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