1 //===-- MessageObjects.cpp ------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "MessageObjects.h"
10 #include "lldb/Utility/Args.h"
11 #include "lldb/Utility/StringExtractor.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "gtest/gtest.h"
14
15 using namespace lldb_private;
16 using namespace lldb;
17 using namespace llvm;
18 namespace llgs_tests {
19
create(StringRef response)20 Expected<ProcessInfo> ProcessInfo::create(StringRef response) {
21 ProcessInfo process_info;
22 auto elements_or_error = SplitUniquePairList("ProcessInfo", response);
23 if (!elements_or_error)
24 return elements_or_error.takeError();
25
26 auto &elements = *elements_or_error;
27 if (elements["pid"].getAsInteger(16, process_info.m_pid))
28 return make_parsing_error("ProcessInfo: pid");
29 if (elements["parent-pid"].getAsInteger(16, process_info.m_parent_pid))
30 return make_parsing_error("ProcessInfo: parent-pid");
31 if (elements["real-uid"].getAsInteger(16, process_info.m_real_uid))
32 return make_parsing_error("ProcessInfo: real-uid");
33 if (elements["real-gid"].getAsInteger(16, process_info.m_real_gid))
34 return make_parsing_error("ProcessInfo: real-uid");
35 if (elements["effective-uid"].getAsInteger(16, process_info.m_effective_uid))
36 return make_parsing_error("ProcessInfo: effective-uid");
37 if (elements["effective-gid"].getAsInteger(16, process_info.m_effective_gid))
38 return make_parsing_error("ProcessInfo: effective-gid");
39 if (elements["ptrsize"].getAsInteger(10, process_info.m_ptrsize))
40 return make_parsing_error("ProcessInfo: ptrsize");
41
42 process_info.m_triple = fromHex(elements["triple"]);
43 StringRef endian_str = elements["endian"];
44 if (endian_str == "little")
45 process_info.m_endian = support::little;
46 else if (endian_str == "big")
47 process_info.m_endian = support::big;
48 else
49 return make_parsing_error("ProcessInfo: endian");
50
51 return process_info;
52 }
53
GetPid() const54 lldb::pid_t ProcessInfo::GetPid() const { return m_pid; }
55
GetEndian() const56 support::endianness ProcessInfo::GetEndian() const { return m_endian; }
57
58 //====== ThreadInfo ============================================================
ThreadInfo(StringRef name,StringRef reason,RegisterMap registers,unsigned int)59 ThreadInfo::ThreadInfo(StringRef name, StringRef reason, RegisterMap registers,
60 unsigned int)
61 : m_name(name.str()), m_reason(reason.str()),
62 m_registers(std::move(registers)) {}
63
ReadRegister(unsigned int Id) const64 const RegisterValue *ThreadInfo::ReadRegister(unsigned int Id) const {
65 auto Iter = m_registers.find(Id);
66 return Iter == m_registers.end() ? nullptr : &Iter->getSecond();
67 }
68
69 //====== JThreadsInfo ==========================================================
70
71 Expected<RegisterMap>
parseRegisters(const StructuredData::Dictionary & Dict,ArrayRef<RegisterInfo> RegInfos)72 JThreadsInfo::parseRegisters(const StructuredData::Dictionary &Dict,
73 ArrayRef<RegisterInfo> RegInfos) {
74 RegisterMap Result;
75
76 auto KeysObj = Dict.GetKeys();
77 auto Keys = KeysObj->GetAsArray();
78 for (size_t i = 0; i < Keys->GetSize(); i++) {
79 StringRef KeyStr, ValueStr;
80 Keys->GetItemAtIndexAsString(i, KeyStr);
81 Dict.GetValueForKeyAsString(KeyStr, ValueStr);
82 unsigned int Register;
83 if (!llvm::to_integer(KeyStr, Register, 10))
84 return make_parsing_error("JThreadsInfo: register key[{0}]", i);
85
86 auto RegValOr =
87 parseRegisterValue(RegInfos[Register], ValueStr, support::big);
88 if (!RegValOr)
89 return RegValOr.takeError();
90 Result[Register] = std::move(*RegValOr);
91 }
92 return std::move(Result);
93 }
94
create(StringRef Response,ArrayRef<RegisterInfo> RegInfos)95 Expected<JThreadsInfo> JThreadsInfo::create(StringRef Response,
96 ArrayRef<RegisterInfo> RegInfos) {
97 JThreadsInfo jthreads_info;
98
99 StructuredData::ObjectSP json =
100 StructuredData::ParseJSON(std::string(Response));
101 StructuredData::Array *array = json->GetAsArray();
102 if (!array)
103 return make_parsing_error("JThreadsInfo: JSON array");
104
105 for (size_t i = 0; i < array->GetSize(); i++) {
106 StructuredData::Dictionary *thread_info;
107 array->GetItemAtIndexAsDictionary(i, thread_info);
108 if (!thread_info)
109 return make_parsing_error("JThreadsInfo: JSON obj at {0}", i);
110
111 StringRef name, reason;
112 thread_info->GetValueForKeyAsString("name", name);
113 thread_info->GetValueForKeyAsString("reason", reason);
114 uint64_t signal;
115 thread_info->GetValueForKeyAsInteger("signal", signal);
116 uint64_t tid;
117 thread_info->GetValueForKeyAsInteger("tid", tid);
118
119 StructuredData::Dictionary *register_dict;
120 thread_info->GetValueForKeyAsDictionary("registers", register_dict);
121 if (!register_dict)
122 return make_parsing_error("JThreadsInfo: registers JSON obj");
123
124 auto RegsOr = parseRegisters(*register_dict, RegInfos);
125 if (!RegsOr)
126 return RegsOr.takeError();
127 jthreads_info.m_thread_infos[tid] =
128 ThreadInfo(name, reason, std::move(*RegsOr), signal);
129 }
130
131 return jthreads_info;
132 }
133
GetThreadInfos() const134 const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const {
135 return m_thread_infos;
136 }
137
create(StringRef Response)138 Expected<RegisterInfo> RegisterInfoParser::create(StringRef Response) {
139 auto ElementsOr = SplitUniquePairList("RegisterInfoParser", Response);
140 if (!ElementsOr)
141 return ElementsOr.takeError();
142 auto &Elements = *ElementsOr;
143
144 RegisterInfo Info = {
145 nullptr, // Name
146 nullptr, // Alt name
147 0, // byte size
148 0, // offset
149 eEncodingUint, // encoding
150 eFormatHex, // format
151 {
152 LLDB_INVALID_REGNUM, // eh_frame reg num
153 LLDB_INVALID_REGNUM, // DWARF reg num
154 LLDB_INVALID_REGNUM, // generic reg num
155 LLDB_INVALID_REGNUM, // process plugin reg num
156 LLDB_INVALID_REGNUM // native register number
157 },
158 nullptr,
159 nullptr,
160 nullptr, // Dwarf expression opcode bytes pointer
161 0 // Dwarf expression opcode bytes length
162 };
163 Info.name = ConstString(Elements["name"]).GetCString();
164 if (!Info.name)
165 return make_parsing_error("qRegisterInfo: name");
166
167 Info.alt_name = ConstString(Elements["alt-name"]).GetCString();
168
169 if (!to_integer(Elements["bitsize"], Info.byte_size, 10))
170 return make_parsing_error("qRegisterInfo: bit-size");
171 Info.byte_size /= CHAR_BIT;
172
173 if (!to_integer(Elements["offset"], Info.byte_offset, 10))
174 Info.byte_offset = LLDB_INVALID_INDEX32;
175
176 Info.encoding = Args::StringToEncoding(Elements["encoding"]);
177 if (Info.encoding == eEncodingInvalid)
178 return make_parsing_error("qRegisterInfo: encoding");
179
180 Info.format = StringSwitch<Format>(Elements["format"])
181 .Case("binary", eFormatBinary)
182 .Case("decimal", eFormatDecimal)
183 .Case("hex", eFormatHex)
184 .Case("float", eFormatFloat)
185 .Case("vector-sint8", eFormatVectorOfSInt8)
186 .Case("vector-uint8", eFormatVectorOfUInt8)
187 .Case("vector-sint16", eFormatVectorOfSInt16)
188 .Case("vector-uint16", eFormatVectorOfUInt16)
189 .Case("vector-sint32", eFormatVectorOfSInt32)
190 .Case("vector-uint32", eFormatVectorOfUInt32)
191 .Case("vector-float32", eFormatVectorOfFloat32)
192 .Case("vector-uint64", eFormatVectorOfUInt64)
193 .Case("vector-uint128", eFormatVectorOfUInt128)
194 .Default(eFormatInvalid);
195 if (Info.format == eFormatInvalid)
196 return make_parsing_error("qRegisterInfo: format");
197
198 Info.kinds[eRegisterKindGeneric] =
199 Args::StringToGenericRegister(Elements["generic"]);
200
201 return std::move(Info);
202 }
203
parseRegisterValue(const RegisterInfo & Info,StringRef HexValue,llvm::support::endianness Endian,bool ZeroPad)204 Expected<RegisterValue> parseRegisterValue(const RegisterInfo &Info,
205 StringRef HexValue,
206 llvm::support::endianness Endian,
207 bool ZeroPad) {
208 SmallString<128> Storage;
209 if (ZeroPad && HexValue.size() < Info.byte_size * 2) {
210 Storage.insert(Storage.begin(), Info.byte_size * 2 - HexValue.size(), '0');
211 Storage += HexValue;
212 HexValue = Storage;
213 }
214
215 SmallVector<uint8_t, 64> Bytes(HexValue.size() / 2);
216 StringExtractor(HexValue).GetHexBytes(Bytes, '\xcc');
217 RegisterValue Value;
218 Status ST;
219 Value.SetFromMemoryData(
220 &Info, Bytes.data(), Bytes.size(),
221 Endian == support::little ? eByteOrderLittle : eByteOrderBig, ST);
222 if (ST.Fail())
223 return ST.ToError();
224 return Value;
225 }
226
227 //====== StopReply =============================================================
228 Expected<std::unique_ptr<StopReply>>
create(StringRef Response,llvm::support::endianness Endian,ArrayRef<RegisterInfo> RegInfos)229 StopReply::create(StringRef Response, llvm::support::endianness Endian,
230 ArrayRef<RegisterInfo> RegInfos) {
231 if (Response.size() < 3)
232 return make_parsing_error("StopReply: Invalid packet");
233 if (Response.consume_front("T"))
234 return StopReplyStop::create(Response, Endian, RegInfos);
235 if (Response.consume_front("W"))
236 return StopReplyExit::create(Response);
237 return make_parsing_error("StopReply: Invalid packet");
238 }
239
parseRegisters(const StringMap<SmallVector<StringRef,2>> & Elements,support::endianness Endian,ArrayRef<lldb_private::RegisterInfo> RegInfos)240 Expected<RegisterMap> StopReplyStop::parseRegisters(
241 const StringMap<SmallVector<StringRef, 2>> &Elements,
242 support::endianness Endian, ArrayRef<lldb_private::RegisterInfo> RegInfos) {
243
244 RegisterMap Result;
245 for (const auto &E : Elements) {
246 StringRef Key = E.getKey();
247 const auto &Val = E.getValue();
248 if (Key.size() != 2)
249 continue;
250
251 unsigned int Reg;
252 if (!to_integer(Key, Reg, 16))
253 continue;
254
255 if (Val.size() != 1)
256 return make_parsing_error(
257 "StopReplyStop: multiple entries for register field [{0:x}]", Reg);
258
259 auto RegValOr = parseRegisterValue(RegInfos[Reg], Val[0], Endian);
260 if (!RegValOr)
261 return RegValOr.takeError();
262 Result[Reg] = std::move(*RegValOr);
263 }
264 return std::move(Result);
265 }
266
267 Expected<std::unique_ptr<StopReplyStop>>
create(StringRef Response,support::endianness Endian,ArrayRef<RegisterInfo> RegInfos)268 StopReplyStop::create(StringRef Response, support::endianness Endian,
269 ArrayRef<RegisterInfo> RegInfos) {
270 unsigned int Signal;
271 StringRef SignalStr = Response.take_front(2);
272 Response = Response.drop_front(2);
273 if (!to_integer(SignalStr, Signal, 16))
274 return make_parsing_error("StopReply: stop signal");
275
276 auto Elements = SplitPairList(Response);
277 for (StringRef Field :
278 {"name", "reason", "thread", "threads", "thread-pcs"}) {
279 // This will insert an empty field if there is none. In the future, we
280 // should probably differentiate between these fields not being present and
281 // them being empty, but right now no tests depends on this.
282 if (Elements.insert({Field, {""}}).first->second.size() != 1)
283 return make_parsing_error(
284 "StopReply: got multiple responses for the {0} field", Field);
285 }
286 StringRef Name = Elements["name"][0];
287 StringRef Reason = Elements["reason"][0];
288
289 lldb::tid_t Thread;
290 if (!to_integer(Elements["thread"][0], Thread, 16))
291 return make_parsing_error("StopReply: thread");
292
293 SmallVector<StringRef, 20> Threads;
294 SmallVector<StringRef, 20> Pcs;
295 Elements["threads"][0].split(Threads, ',');
296 Elements["thread-pcs"][0].split(Pcs, ',');
297 if (Threads.size() != Pcs.size())
298 return make_parsing_error("StopReply: thread/PC count mismatch");
299
300 RegisterMap ThreadPcs;
301 const RegisterInfo *PcInfo = find_if(RegInfos, [](const RegisterInfo &Info) {
302 return Info.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC;
303 });
304 assert(PcInfo);
305
306 for (auto ThreadPc : zip(Threads, Pcs)) {
307 lldb::tid_t Id;
308 if (!to_integer(std::get<0>(ThreadPc), Id, 16))
309 return make_parsing_error("StopReply: Thread id '{0}'",
310 std::get<0>(ThreadPc));
311
312 auto PcOr = parseRegisterValue(*PcInfo, std::get<1>(ThreadPc), Endian,
313 /*ZeroPad*/ true);
314 if (!PcOr)
315 return PcOr.takeError();
316 ThreadPcs[Id] = std::move(*PcOr);
317 }
318
319 auto RegistersOr = parseRegisters(Elements, Endian, RegInfos);
320 if (!RegistersOr)
321 return RegistersOr.takeError();
322
323 return std::make_unique<StopReplyStop>(Signal, Thread, Name,
324 std::move(ThreadPcs),
325 std::move(*RegistersOr), Reason);
326 }
327
328 Expected<std::unique_ptr<StopReplyExit>>
create(StringRef Response)329 StopReplyExit::create(StringRef Response) {
330 uint8_t Status;
331 if (!to_integer(Response, Status, 16))
332 return make_parsing_error("StopReply: exit status");
333 return std::make_unique<StopReplyExit>(Status);
334 }
335
336 //====== Globals ===============================================================
SplitUniquePairList(StringRef caller,StringRef str)337 Expected<StringMap<StringRef>> SplitUniquePairList(StringRef caller,
338 StringRef str) {
339 SmallVector<StringRef, 20> elements;
340 str.split(elements, ';');
341
342 StringMap<StringRef> pairs;
343 for (StringRef s : elements) {
344 std::pair<StringRef, StringRef> pair = s.split(':');
345 if (pairs.count(pair.first))
346 return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first);
347
348 pairs.insert(pair);
349 }
350
351 return pairs;
352 }
353
SplitPairList(StringRef str)354 StringMap<SmallVector<StringRef, 2>> SplitPairList(StringRef str) {
355 SmallVector<StringRef, 20> elements;
356 str.split(elements, ';');
357
358 StringMap<SmallVector<StringRef, 2>> pairs;
359 for (StringRef s : elements) {
360 std::pair<StringRef, StringRef> pair = s.split(':');
361 pairs[pair.first].push_back(pair.second);
362 }
363
364 return pairs;
365 }
366 } // namespace llgs_tests
367
operator <<(std::ostream & OS,const RegisterValue & RegVal)368 std::ostream &lldb_private::operator<<(std::ostream &OS,
369 const RegisterValue &RegVal) {
370 ArrayRef<uint8_t> Bytes(static_cast<const uint8_t *>(RegVal.GetBytes()),
371 RegVal.GetByteSize());
372 return OS << formatv("RegisterValue[{0}]: {1:@[x-2]}", RegVal.GetByteSize(),
373 make_range(Bytes.begin(), Bytes.end()))
374 .str();
375 }
376