1 //===-- MessageObjects.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 #include "MessageObjects.h"
11 #include "lldb/Utility/StructuredData.h"
12 #include "llvm/ADT/StringExtras.h"
13 #include "gtest/gtest.h"
14 
15 using namespace lldb_private;
16 using namespace llvm;
17 using namespace llvm::support;
18 namespace llgs_tests {
19 
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 
54 lldb::pid_t ProcessInfo::GetPid() const { return m_pid; }
55 
56 endianness ProcessInfo::GetEndian() const { return m_endian; }
57 
58 //====== ThreadInfo ============================================================
59 ThreadInfo::ThreadInfo(StringRef name, StringRef reason,
60                        const RegisterMap &registers, unsigned int signal)
61     : m_name(name.str()), m_reason(reason.str()), m_registers(registers),
62       m_signal(signal) {}
63 
64 StringRef ThreadInfo::ReadRegister(unsigned int register_id) const {
65   return m_registers.lookup(register_id);
66 }
67 
68 Expected<uint64_t>
69 ThreadInfo::ReadRegisterAsUint64(unsigned int register_id) const {
70   uint64_t value;
71   std::string value_str(m_registers.lookup(register_id));
72   if (!llvm::to_integer(value_str, value, 16))
73     return make_parsing_error("ThreadInfo value for register {0}: {1}",
74                               register_id, value_str);
75 
76   sys::swapByteOrder(value);
77   return value;
78 }
79 
80 //====== JThreadsInfo ==========================================================
81 Expected<JThreadsInfo> JThreadsInfo::Create(StringRef response,
82                                             endianness endian) {
83   JThreadsInfo jthreads_info;
84 
85   StructuredData::ObjectSP json = StructuredData::ParseJSON(response);
86   StructuredData::Array *array = json->GetAsArray();
87   if (!array)
88     return make_parsing_error("JThreadsInfo: JSON array");
89 
90   for (size_t i = 0; i < array->GetSize(); i++) {
91     StructuredData::Dictionary *thread_info;
92     array->GetItemAtIndexAsDictionary(i, thread_info);
93     if (!thread_info)
94       return make_parsing_error("JThreadsInfo: JSON obj at {0}", i);
95 
96     StringRef name, reason;
97     thread_info->GetValueForKeyAsString("name", name);
98     thread_info->GetValueForKeyAsString("reason", reason);
99     uint64_t signal;
100     thread_info->GetValueForKeyAsInteger("signal", signal);
101     uint64_t tid;
102     thread_info->GetValueForKeyAsInteger("tid", tid);
103 
104     StructuredData::Dictionary *register_dict;
105     thread_info->GetValueForKeyAsDictionary("registers", register_dict);
106     if (!register_dict)
107       return make_parsing_error("JThreadsInfo: registers JSON obj");
108 
109     RegisterMap registers;
110 
111     auto keys_obj = register_dict->GetKeys();
112     auto keys = keys_obj->GetAsArray();
113     for (size_t i = 0; i < keys->GetSize(); i++) {
114       StringRef key_str, value_str;
115       keys->GetItemAtIndexAsString(i, key_str);
116       register_dict->GetValueForKeyAsString(key_str, value_str);
117       unsigned int register_id;
118       if (key_str.getAsInteger(10, register_id))
119         return make_parsing_error("JThreadsInfo: register key[{0}]", i);
120 
121       registers[register_id] = value_str.str();
122     }
123 
124     jthreads_info.m_thread_infos[tid] =
125         ThreadInfo(name, reason, registers, signal);
126   }
127 
128   return jthreads_info;
129 }
130 
131 const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const {
132   return m_thread_infos;
133 }
134 
135 //====== StopReply =============================================================
136 Expected<std::unique_ptr<StopReply>>
137 StopReply::create(StringRef Response, llvm::support::endianness Endian) {
138   if (Response.size() < 3)
139     return make_parsing_error("StopReply: Invalid packet");
140   if (Response.consume_front("T"))
141     return StopReplyStop::create(Response, Endian);
142   if (Response.consume_front("W"))
143     return StopReplyExit::create(Response);
144   return make_parsing_error("StopReply: Invalid packet");
145 }
146 
147 Expected<std::unique_ptr<StopReplyStop>>
148 StopReplyStop::create(StringRef Response, llvm::support::endianness Endian) {
149   unsigned int Signal;
150   StringRef SignalStr = Response.take_front(2);
151   Response = Response.drop_front(2);
152   if (!to_integer(SignalStr, Signal, 16))
153     return make_parsing_error("StopReply: stop signal");
154 
155   auto Elements = SplitPairList(Response);
156   for (StringRef Field :
157        {"name", "reason", "thread", "threads", "thread-pcs"}) {
158     // This will insert an empty field if there is none. In the future, we
159     // should probably differentiate between these fields not being present and
160     // them being empty, but right now no tests depends on this.
161     if (Elements.insert({Field, {""}}).first->second.size() != 1)
162       return make_parsing_error(
163           "StopReply: got multiple responses for the {0} field", Field);
164   }
165   StringRef Name = Elements["name"][0];
166   StringRef Reason = Elements["reason"][0];
167 
168   lldb::tid_t Thread;
169   if (!to_integer(Elements["thread"][0], Thread, 16))
170     return make_parsing_error("StopReply: thread");
171 
172   SmallVector<StringRef, 20> Threads;
173   SmallVector<StringRef, 20> Pcs;
174   Elements["threads"][0].split(Threads, ',');
175   Elements["thread-pcs"][0].split(Pcs, ',');
176   if (Threads.size() != Pcs.size())
177     return make_parsing_error("StopReply: thread/PC count mismatch");
178 
179   U64Map ThreadPcs;
180   for (auto ThreadPc : zip(Threads, Pcs)) {
181     lldb::tid_t Id;
182     uint64_t Pc;
183     if (!to_integer(std::get<0>(ThreadPc), Id, 16))
184       return make_parsing_error("StopReply: Thread id '{0}'",
185                                 std::get<0>(ThreadPc));
186     if (!to_integer(std::get<1>(ThreadPc), Pc, 16))
187       return make_parsing_error("StopReply Thread Pc '{0}'",
188                                 std::get<1>(ThreadPc));
189 
190     ThreadPcs[Id] = Pc;
191   }
192 
193   RegisterMap Registers;
194   for (const auto &E : Elements) {
195     StringRef Key = E.getKey();
196     const auto &Val = E.getValue();
197     if (Key.size() != 2)
198       continue;
199 
200     unsigned int Reg;
201     if (!to_integer(Key, Reg, 16))
202       continue;
203 
204     if (Val.size() != 1)
205       return make_parsing_error(
206           "StopReply: multiple entries for register field [{0:x}]", Reg);
207 
208     Registers[Reg] = Val[0].str();
209   }
210 
211   return llvm::make_unique<StopReplyStop>(Signal, Thread, Name, ThreadPcs,
212                                           Registers, Reason);
213 }
214 
215 Expected<std::unique_ptr<StopReplyExit>>
216 StopReplyExit::create(StringRef Response) {
217   uint8_t Status;
218   if (!to_integer(Response, Status, 16))
219     return make_parsing_error("StopReply: exit status");
220   return llvm::make_unique<StopReplyExit>(Status);
221 }
222 
223 //====== Globals ===============================================================
224 Expected<StringMap<StringRef>> SplitUniquePairList(StringRef caller,
225                                                    StringRef str) {
226   SmallVector<StringRef, 20> elements;
227   str.split(elements, ';');
228 
229   StringMap<StringRef> pairs;
230   for (StringRef s : elements) {
231     std::pair<StringRef, StringRef> pair = s.split(':');
232     if (pairs.count(pair.first))
233       return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first);
234 
235     pairs.insert(pair);
236   }
237 
238   return pairs;
239 }
240 
241 StringMap<SmallVector<StringRef, 2>> SplitPairList(StringRef str) {
242   SmallVector<StringRef, 20> elements;
243   str.split(elements, ';');
244 
245   StringMap<SmallVector<StringRef, 2>> pairs;
246   for (StringRef s : elements) {
247     std::pair<StringRef, StringRef> pair = s.split(':');
248     pairs[pair.first].push_back(pair.second);
249   }
250 
251   return pairs;
252 }
253 } // namespace llgs_tests
254