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