xref: /llvm-project/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp (revision 1e021a162ed10c6b832cbbdd829396d55a0a71c7)
1 //===-- GDBRemoteCommunicationClientTest.cpp --------------------*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 #include <future>
10 
11 #include "GDBRemoteTestUtils.h"
12 
13 #include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
14 #include "lldb/Core/DataBuffer.h"
15 #include "lldb/Core/ModuleSpec.h"
16 #include "lldb/Core/StructuredData.h"
17 
18 #include "llvm/ADT/ArrayRef.h"
19 
20 using namespace lldb_private::process_gdb_remote;
21 using namespace lldb_private;
22 using namespace lldb;
23 using namespace llvm;
24 
25 namespace {
26 
27 typedef GDBRemoteCommunication::PacketResult PacketResult;
28 
29 struct TestClient : public GDBRemoteCommunicationClient {
30   TestClient() { m_send_acks = false; }
31 };
32 
33 void Handle_QThreadSuffixSupported(MockServer &server, bool supported) {
34   StringExtractorGDBRemote request;
35   ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
36   ASSERT_EQ("QThreadSuffixSupported", request.GetStringRef());
37   if (supported)
38     ASSERT_EQ(PacketResult::Success, server.SendOKResponse());
39   else
40     ASSERT_EQ(PacketResult::Success, server.SendUnimplementedResponse(nullptr));
41 }
42 
43 void HandlePacket(MockServer &server, StringRef expected, StringRef response) {
44   StringExtractorGDBRemote request;
45   ASSERT_EQ(PacketResult::Success, server.GetPacket(request));
46   ASSERT_EQ(expected, request.GetStringRef());
47   ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
48 }
49 
50 uint8_t all_registers[] = {'@', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
51                            'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O'};
52 std::string all_registers_hex = "404142434445464748494a4b4c4d4e4f";
53 uint8_t one_register[] = {'A', 'B', 'C', 'D'};
54 std::string one_register_hex = "41424344";
55 
56 } // end anonymous namespace
57 
58 class GDBRemoteCommunicationClientTest : public GDBRemoteTest {};
59 
60 TEST_F(GDBRemoteCommunicationClientTest, WriteRegister) {
61   TestClient client;
62   MockServer server;
63   Connect(client, server);
64   if (HasFailure())
65     return;
66 
67   const lldb::tid_t tid = 0x47;
68   const uint32_t reg_num = 4;
69   std::future<bool> write_result = std::async(std::launch::async, [&] {
70     return client.WriteRegister(tid, reg_num, one_register);
71   });
72 
73   Handle_QThreadSuffixSupported(server, true);
74 
75   HandlePacket(server, "P4=" + one_register_hex + ";thread:0047;", "OK");
76   ASSERT_TRUE(write_result.get());
77 
78   write_result = std::async(std::launch::async, [&] {
79     return client.WriteAllRegisters(tid, all_registers);
80   });
81 
82   HandlePacket(server, "G" + all_registers_hex + ";thread:0047;", "OK");
83   ASSERT_TRUE(write_result.get());
84 }
85 
86 TEST_F(GDBRemoteCommunicationClientTest, WriteRegisterNoSuffix) {
87   TestClient client;
88   MockServer server;
89   Connect(client, server);
90   if (HasFailure())
91     return;
92 
93   const lldb::tid_t tid = 0x47;
94   const uint32_t reg_num = 4;
95   std::future<bool> write_result = std::async(std::launch::async, [&] {
96     return client.WriteRegister(tid, reg_num, one_register);
97   });
98 
99   Handle_QThreadSuffixSupported(server, false);
100   HandlePacket(server, "Hg47", "OK");
101   HandlePacket(server, "P4=" + one_register_hex, "OK");
102   ASSERT_TRUE(write_result.get());
103 
104   write_result = std::async(std::launch::async, [&] {
105     return client.WriteAllRegisters(tid, all_registers);
106   });
107 
108   HandlePacket(server, "G" + all_registers_hex, "OK");
109   ASSERT_TRUE(write_result.get());
110 }
111 
112 TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) {
113   TestClient client;
114   MockServer server;
115   Connect(client, server);
116   if (HasFailure())
117     return;
118 
119   const lldb::tid_t tid = 0x47;
120   const uint32_t reg_num = 4;
121   std::future<bool> async_result = std::async(
122       std::launch::async, [&] { return client.GetpPacketSupported(tid); });
123   Handle_QThreadSuffixSupported(server, true);
124   HandlePacket(server, "p0;thread:0047;", one_register_hex);
125   ASSERT_TRUE(async_result.get());
126 
127   std::future<DataBufferSP> read_result = std::async(
128       std::launch::async, [&] { return client.ReadRegister(tid, reg_num); });
129   HandlePacket(server, "p4;thread:0047;", "41424344");
130   auto buffer_sp = read_result.get();
131   ASSERT_TRUE(bool(buffer_sp));
132   ASSERT_EQ(0,
133             memcmp(buffer_sp->GetBytes(), one_register, sizeof one_register));
134 
135   read_result = std::async(std::launch::async,
136                            [&] { return client.ReadAllRegisters(tid); });
137   HandlePacket(server, "g;thread:0047;", all_registers_hex);
138   buffer_sp = read_result.get();
139   ASSERT_TRUE(bool(buffer_sp));
140   ASSERT_EQ(0,
141             memcmp(buffer_sp->GetBytes(), all_registers, sizeof all_registers));
142 }
143 
144 TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
145   TestClient client;
146   MockServer server;
147   Connect(client, server);
148   if (HasFailure())
149     return;
150 
151   const lldb::tid_t tid = 0x47;
152   uint32_t save_id;
153   std::future<bool> async_result = std::async(std::launch::async, [&] {
154     return client.SaveRegisterState(tid, save_id);
155   });
156   Handle_QThreadSuffixSupported(server, false);
157   HandlePacket(server, "Hg47", "OK");
158   HandlePacket(server, "QSaveRegisterState", "1");
159   ASSERT_TRUE(async_result.get());
160   EXPECT_EQ(1u, save_id);
161 
162   async_result = std::async(std::launch::async, [&] {
163     return client.RestoreRegisterState(tid, save_id);
164   });
165   HandlePacket(server, "QRestoreRegisterState:1", "OK");
166   ASSERT_TRUE(async_result.get());
167 }
168 
169 TEST_F(GDBRemoteCommunicationClientTest, SyncThreadState) {
170   TestClient client;
171   MockServer server;
172   Connect(client, server);
173   if (HasFailure())
174     return;
175 
176   const lldb::tid_t tid = 0x47;
177   std::future<bool> async_result = std::async(
178       std::launch::async, [&] { return client.SyncThreadState(tid); });
179   HandlePacket(server, "qSyncThreadStateSupported", "OK");
180   HandlePacket(server, "QSyncThreadState:0047;", "OK");
181   ASSERT_TRUE(async_result.get());
182 }
183 
184 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfo) {
185   TestClient client;
186   MockServer server;
187   Connect(client, server);
188   if (HasFailure())
189     return;
190 
191   llvm::Triple triple("i386-pc-linux");
192 
193   FileSpec file_specs[] = {
194       FileSpec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix),
195       FileSpec("/foo/baz.so", false, FileSpec::ePathSyntaxPosix),
196 
197       // This is a bit dodgy but we currently depend on GetModulesInfo not
198       // performing denormalization. It can go away once the users
199       // (DynamicLoaderPOSIXDYLD, at least) correctly set the path syntax for
200       // the FileSpecs they create.
201       FileSpec("/foo/baw.so", false, FileSpec::ePathSyntaxWindows),
202   };
203   std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
204       std::async(std::launch::async,
205                  [&] { return client.GetModulesInfo(file_specs, triple); });
206   HandlePacket(
207       server, "jModulesInfo:["
208               R"({"file":"/foo/bar.so","triple":"i386-pc-linux"},)"
209               R"({"file":"/foo/baz.so","triple":"i386-pc-linux"},)"
210               R"({"file":"/foo/baw.so","triple":"i386-pc-linux"}])",
211       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
212       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}]])");
213 
214   auto result = async_result.get();
215   ASSERT_TRUE(result.hasValue());
216   ASSERT_EQ(1u, result->size());
217   EXPECT_EQ("/foo/bar.so", result.getValue()[0].GetFileSpec().GetPath());
218   EXPECT_EQ(triple, result.getValue()[0].GetArchitecture().GetTriple());
219   EXPECT_EQ(UUID("@ABCDEFGHIJKLMNO", 16), result.getValue()[0].GetUUID());
220   EXPECT_EQ(0u, result.getValue()[0].GetObjectOffset());
221   EXPECT_EQ(1234u, result.getValue()[0].GetObjectSize());
222 }
223 
224 TEST_F(GDBRemoteCommunicationClientTest, GetModulesInfoInvalidResponse) {
225   TestClient client;
226   MockServer server;
227   Connect(client, server);
228   if (HasFailure())
229     return;
230 
231   llvm::Triple triple("i386-pc-linux");
232   FileSpec file_spec("/foo/bar.so", false, FileSpec::ePathSyntaxPosix);
233 
234   const char *invalid_responses[] = {
235       "OK", "E47", "[]",
236       // no UUID
237       R"([{"triple":"i386-pc-linux",)"
238       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])",
239       // no triple
240       R"([{"uuid":"404142434445464748494a4b4c4d4e4f",)"
241       R"("file_path":"/foo/bar.so","file_offset":0,"file_size":1234}])",
242       // no file_path
243       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
244       R"("file_offset":0,"file_size":1234}])",
245       // no file_offset
246       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
247       R"("file_path":"/foo/bar.so","file_size":1234}])",
248       // no file_size
249       R"([{"uuid":"404142434445464748494a4b4c4d4e4f","triple":"i386-pc-linux",)"
250       R"("file_path":"/foo/bar.so","file_offset":0}])",
251   };
252 
253   for (const char *response : invalid_responses) {
254     std::future<llvm::Optional<std::vector<ModuleSpec>>> async_result =
255         std::async(std::launch::async,
256                    [&] { return client.GetModulesInfo(file_spec, triple); });
257     HandlePacket(
258         server,
259         R"(jModulesInfo:[{"file":"/foo/bar.so","triple":"i386-pc-linux"}])",
260         response);
261 
262     ASSERT_FALSE(async_result.get().hasValue()) << "response was: " << response;
263   }
264 }
265 
266 TEST_F(GDBRemoteCommunicationClientTest, TestPacketSpeedJSON) {
267   TestClient client;
268   MockServer server;
269   Connect(client, server);
270   if (HasFailure())
271     return;
272 
273   std::thread server_thread([&server] {
274     for (;;) {
275       StringExtractorGDBRemote request;
276       PacketResult result = server.GetPacket(request);
277       if (result == PacketResult::ErrorDisconnected)
278         return;
279       ASSERT_EQ(PacketResult::Success, result);
280       StringRef ref = request.GetStringRef();
281       ASSERT_TRUE(ref.consume_front("qSpeedTest:response_size:"));
282       int size;
283       ASSERT_FALSE(ref.consumeInteger(10, size)) << "ref: " << ref;
284       std::string response(size, 'X');
285       ASSERT_EQ(PacketResult::Success, server.SendPacket(response));
286     }
287   });
288 
289   StreamString ss;
290   client.TestPacketSpeed(10, 32, 32, 4096, true, ss);
291   client.Disconnect();
292   server_thread.join();
293 
294   GTEST_LOG_(INFO) << "Formatted output: " << ss.GetData();
295   auto object_sp = StructuredData::ParseJSON(ss.GetString());
296   ASSERT_TRUE(bool(object_sp));
297   auto dict_sp = object_sp->GetAsDictionary();
298   ASSERT_TRUE(bool(dict_sp));
299 
300   object_sp = dict_sp->GetValueForKey("packet_speeds");
301   ASSERT_TRUE(bool(object_sp));
302   dict_sp = object_sp->GetAsDictionary();
303   ASSERT_TRUE(bool(dict_sp));
304 
305   int num_packets;
306   ASSERT_TRUE(dict_sp->GetValueForKeyAsInteger("num_packets", num_packets))
307       << ss.GetString();
308   ASSERT_EQ(10, num_packets);
309 }
310