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