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 bool ThreadInfo::ReadRegisterAsUint64(unsigned int register_id,
69                                       uint64_t &value) const {
70   std::string value_str(m_registers.lookup(register_id));
71   if (!llvm::to_integer(value_str, value, 16)) {
72     GTEST_LOG_(ERROR)
73         << formatv("ThreadInfo: Unable to parse register value at {0}.",
74                    register_id)
75                .str();
76     return false;
77   }
78 
79   sys::swapByteOrder(value);
80   return true;
81 }
82 
83 //====== JThreadsInfo ==========================================================
84 Expected<JThreadsInfo> JThreadsInfo::Create(StringRef response,
85                                             endianness endian) {
86   JThreadsInfo jthreads_info;
87 
88   StructuredData::ObjectSP json = StructuredData::ParseJSON(response);
89   StructuredData::Array *array = json->GetAsArray();
90   if (!array)
91     return make_parsing_error("JThreadsInfo: JSON array");
92 
93   for (size_t i = 0; i < array->GetSize(); i++) {
94     StructuredData::Dictionary *thread_info;
95     array->GetItemAtIndexAsDictionary(i, thread_info);
96     if (!thread_info)
97       return make_parsing_error("JThreadsInfo: JSON obj at {0}", i);
98 
99     StringRef name, reason;
100     thread_info->GetValueForKeyAsString("name", name);
101     thread_info->GetValueForKeyAsString("reason", reason);
102     uint64_t signal;
103     thread_info->GetValueForKeyAsInteger("signal", signal);
104     uint64_t tid;
105     thread_info->GetValueForKeyAsInteger("tid", tid);
106 
107     StructuredData::Dictionary *register_dict;
108     thread_info->GetValueForKeyAsDictionary("registers", register_dict);
109     if (!register_dict)
110       return make_parsing_error("JThreadsInfo: registers JSON obj");
111 
112     RegisterMap registers;
113 
114     auto keys_obj = register_dict->GetKeys();
115     auto keys = keys_obj->GetAsArray();
116     for (size_t i = 0; i < keys->GetSize(); i++) {
117       StringRef key_str, value_str;
118       keys->GetItemAtIndexAsString(i, key_str);
119       register_dict->GetValueForKeyAsString(key_str, value_str);
120       unsigned int register_id;
121       if (key_str.getAsInteger(10, register_id))
122         return make_parsing_error("JThreadsInfo: register key[{0}]", i);
123 
124       registers[register_id] = value_str.str();
125     }
126 
127     jthreads_info.m_thread_infos[tid] =
128         ThreadInfo(name, reason, registers, signal);
129   }
130 
131   return jthreads_info;
132 }
133 
134 const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const {
135   return m_thread_infos;
136 }
137 
138 //====== StopReply =============================================================
139 const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; }
140 
141 Expected<StopReply> StopReply::Create(StringRef response,
142                                       llvm::support::endianness endian) {
143   if (response.size() < 3 || !response.consume_front("T"))
144     return make_parsing_error("StopReply: Invalid packet");
145 
146   StopReply stop_reply;
147 
148   StringRef signal = response.take_front(2);
149   response = response.drop_front(2);
150   if (!llvm::to_integer(signal, stop_reply.m_signal, 16))
151     return make_parsing_error("StopReply: stop signal");
152 
153   auto elements = SplitPairList(response);
154   for (StringRef field :
155        {"name", "reason", "thread", "threads", "thread-pcs"}) {
156     // This will insert an empty field if there is none. In the future, we
157     // should probably differentiate between these fields not being present and
158     // them being empty, but right now no tests depends on this.
159     if (elements.insert({field, {""}}).first->second.size() != 1)
160       return make_parsing_error(
161           "StopReply: got multiple responses for the {0} field", field);
162   }
163   stop_reply.m_name = elements["name"][0];
164   stop_reply.m_reason = elements["reason"][0];
165 
166   if (!llvm::to_integer(elements["thread"][0], stop_reply.m_thread, 16))
167     return make_parsing_error("StopReply: thread");
168 
169   SmallVector<StringRef, 20> threads;
170   SmallVector<StringRef, 20> pcs;
171   elements["threads"][0].split(threads, ',');
172   elements["thread-pcs"][0].split(pcs, ',');
173   if (threads.size() != pcs.size())
174     return make_parsing_error("StopReply: thread/PC count mismatch");
175 
176   for (size_t i = 0; i < threads.size(); i++) {
177     lldb::tid_t thread_id;
178     uint64_t pc;
179     if (threads[i].getAsInteger(16, thread_id))
180       return make_parsing_error("StopReply: thread ID at [{0}].", i);
181     if (pcs[i].getAsInteger(16, pc))
182       return make_parsing_error("StopReply: thread PC at [{0}].", i);
183 
184     stop_reply.m_thread_pcs[thread_id] = pc;
185   }
186 
187   for (auto i = elements.begin(); i != elements.end(); i++) {
188     StringRef key = i->getKey();
189     const auto &val = i->getValue();
190     if (key.size() == 2) {
191       unsigned int reg;
192       if (key.getAsInteger(16, reg))
193         continue;
194       if (val.size() != 1)
195         return make_parsing_error(
196             "StopReply: multiple entries for register field [{0:x}]", reg);
197 
198       stop_reply.m_registers[reg] = val[0].str();
199     }
200   }
201 
202   return stop_reply;
203 }
204 
205 //====== Globals ===============================================================
206 Expected<StringMap<StringRef>> SplitUniquePairList(StringRef caller,
207                                                    StringRef str) {
208   SmallVector<StringRef, 20> elements;
209   str.split(elements, ';');
210 
211   StringMap<StringRef> pairs;
212   for (StringRef s : elements) {
213     std::pair<StringRef, StringRef> pair = s.split(':');
214     if (pairs.count(pair.first))
215       return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first);
216 
217     pairs.insert(pair);
218   }
219 
220   return pairs;
221 }
222 
223 StringMap<SmallVector<StringRef, 2>> SplitPairList(StringRef str) {
224   SmallVector<StringRef, 20> elements;
225   str.split(elements, ';');
226 
227   StringMap<SmallVector<StringRef, 2>> pairs;
228   for (StringRef s : elements) {
229     std::pair<StringRef, StringRef> pair = s.split(':');
230     pairs[pair.first].push_back(pair.second);
231   }
232 
233   return pairs;
234 }
235 } // namespace llgs_tests
236