| //===-- MessageObjects.cpp --------------------------------------*- C++ -*-===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "MessageObjects.h" | |
| #include "lldb/Utility/Args.h" | |
| #include "lldb/Utility/StringExtractor.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "gtest/gtest.h" | |
| using namespace lldb_private; | |
| using namespace lldb; | |
| using namespace llvm; | |
| namespace llgs_tests { | |
| Expected<ProcessInfo> ProcessInfo::create(StringRef response) { | |
| ProcessInfo process_info; | |
| auto elements_or_error = SplitUniquePairList("ProcessInfo", response); | |
| if (!elements_or_error) | |
| return elements_or_error.takeError(); | |
| auto &elements = *elements_or_error; | |
| if (elements["pid"].getAsInteger(16, process_info.m_pid)) | |
| return make_parsing_error("ProcessInfo: pid"); | |
| if (elements["parent-pid"].getAsInteger(16, process_info.m_parent_pid)) | |
| return make_parsing_error("ProcessInfo: parent-pid"); | |
| if (elements["real-uid"].getAsInteger(16, process_info.m_real_uid)) | |
| return make_parsing_error("ProcessInfo: real-uid"); | |
| if (elements["real-gid"].getAsInteger(16, process_info.m_real_gid)) | |
| return make_parsing_error("ProcessInfo: real-uid"); | |
| if (elements["effective-uid"].getAsInteger(16, process_info.m_effective_uid)) | |
| return make_parsing_error("ProcessInfo: effective-uid"); | |
| if (elements["effective-gid"].getAsInteger(16, process_info.m_effective_gid)) | |
| return make_parsing_error("ProcessInfo: effective-gid"); | |
| if (elements["ptrsize"].getAsInteger(10, process_info.m_ptrsize)) | |
| return make_parsing_error("ProcessInfo: ptrsize"); | |
| process_info.m_triple = fromHex(elements["triple"]); | |
| StringRef endian_str = elements["endian"]; | |
| if (endian_str == "little") | |
| process_info.m_endian = support::little; | |
| else if (endian_str == "big") | |
| process_info.m_endian = support::big; | |
| else | |
| return make_parsing_error("ProcessInfo: endian"); | |
| return process_info; | |
| } | |
| lldb::pid_t ProcessInfo::GetPid() const { return m_pid; } | |
| support::endianness ProcessInfo::GetEndian() const { return m_endian; } | |
| //====== ThreadInfo ============================================================ | |
| ThreadInfo::ThreadInfo(StringRef name, StringRef reason, RegisterMap registers, | |
| unsigned int) | |
| : m_name(name.str()), m_reason(reason.str()), | |
| m_registers(std::move(registers)) {} | |
| const RegisterValue *ThreadInfo::ReadRegister(unsigned int Id) const { | |
| auto Iter = m_registers.find(Id); | |
| return Iter == m_registers.end() ? nullptr : &Iter->getSecond(); | |
| } | |
| //====== JThreadsInfo ========================================================== | |
| Expected<RegisterMap> | |
| JThreadsInfo::parseRegisters(const StructuredData::Dictionary &Dict, | |
| ArrayRef<RegisterInfo> RegInfos) { | |
| RegisterMap Result; | |
| auto KeysObj = Dict.GetKeys(); | |
| auto Keys = KeysObj->GetAsArray(); | |
| for (size_t i = 0; i < Keys->GetSize(); i++) { | |
| StringRef KeyStr, ValueStr; | |
| Keys->GetItemAtIndexAsString(i, KeyStr); | |
| Dict.GetValueForKeyAsString(KeyStr, ValueStr); | |
| unsigned int Register; | |
| if (!llvm::to_integer(KeyStr, Register, 10)) | |
| return make_parsing_error("JThreadsInfo: register key[{0}]", i); | |
| auto RegValOr = | |
| parseRegisterValue(RegInfos[Register], ValueStr, support::big); | |
| if (!RegValOr) | |
| return RegValOr.takeError(); | |
| Result[Register] = std::move(*RegValOr); | |
| } | |
| return std::move(Result); | |
| } | |
| Expected<JThreadsInfo> JThreadsInfo::create(StringRef Response, | |
| ArrayRef<RegisterInfo> RegInfos) { | |
| JThreadsInfo jthreads_info; | |
| StructuredData::ObjectSP json = StructuredData::ParseJSON(Response); | |
| StructuredData::Array *array = json->GetAsArray(); | |
| if (!array) | |
| return make_parsing_error("JThreadsInfo: JSON array"); | |
| for (size_t i = 0; i < array->GetSize(); i++) { | |
| StructuredData::Dictionary *thread_info; | |
| array->GetItemAtIndexAsDictionary(i, thread_info); | |
| if (!thread_info) | |
| return make_parsing_error("JThreadsInfo: JSON obj at {0}", i); | |
| StringRef name, reason; | |
| thread_info->GetValueForKeyAsString("name", name); | |
| thread_info->GetValueForKeyAsString("reason", reason); | |
| uint64_t signal; | |
| thread_info->GetValueForKeyAsInteger("signal", signal); | |
| uint64_t tid; | |
| thread_info->GetValueForKeyAsInteger("tid", tid); | |
| StructuredData::Dictionary *register_dict; | |
| thread_info->GetValueForKeyAsDictionary("registers", register_dict); | |
| if (!register_dict) | |
| return make_parsing_error("JThreadsInfo: registers JSON obj"); | |
| auto RegsOr = parseRegisters(*register_dict, RegInfos); | |
| if (!RegsOr) | |
| return RegsOr.takeError(); | |
| jthreads_info.m_thread_infos[tid] = | |
| ThreadInfo(name, reason, std::move(*RegsOr), signal); | |
| } | |
| return jthreads_info; | |
| } | |
| const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const { | |
| return m_thread_infos; | |
| } | |
| Expected<RegisterInfo> RegisterInfoParser::create(StringRef Response) { | |
| auto ElementsOr = SplitUniquePairList("RegisterInfoParser", Response); | |
| if (!ElementsOr) | |
| return ElementsOr.takeError(); | |
| auto &Elements = *ElementsOr; | |
| RegisterInfo Info = { | |
| nullptr, // Name | |
| nullptr, // Alt name | |
| 0, // byte size | |
| 0, // offset | |
| eEncodingUint, // encoding | |
| eFormatHex, // format | |
| { | |
| LLDB_INVALID_REGNUM, // eh_frame reg num | |
| LLDB_INVALID_REGNUM, // DWARF reg num | |
| LLDB_INVALID_REGNUM, // generic reg num | |
| LLDB_INVALID_REGNUM, // process plugin reg num | |
| LLDB_INVALID_REGNUM // native register number | |
| }, | |
| nullptr, | |
| nullptr, | |
| nullptr, // Dwarf expression opcode bytes pointer | |
| 0 // Dwarf expression opcode bytes length | |
| }; | |
| Info.name = ConstString(Elements["name"]).GetCString(); | |
| if (!Info.name) | |
| return make_parsing_error("qRegisterInfo: name"); | |
| Info.alt_name = ConstString(Elements["alt-name"]).GetCString(); | |
| if (!to_integer(Elements["bitsize"], Info.byte_size, 10)) | |
| return make_parsing_error("qRegisterInfo: bit-size"); | |
| Info.byte_size /= CHAR_BIT; | |
| if (!to_integer(Elements["offset"], Info.byte_offset, 10)) | |
| return make_parsing_error("qRegisterInfo: offset"); | |
| Info.encoding = Args::StringToEncoding(Elements["encoding"]); | |
| if (Info.encoding == eEncodingInvalid) | |
| return make_parsing_error("qRegisterInfo: encoding"); | |
| Info.format = StringSwitch<Format>(Elements["format"]) | |
| .Case("binary", eFormatBinary) | |
| .Case("decimal", eFormatDecimal) | |
| .Case("hex", eFormatHex) | |
| .Case("float", eFormatFloat) | |
| .Case("vector-sint8", eFormatVectorOfSInt8) | |
| .Case("vector-uint8", eFormatVectorOfUInt8) | |
| .Case("vector-sint16", eFormatVectorOfSInt16) | |
| .Case("vector-uint16", eFormatVectorOfUInt16) | |
| .Case("vector-sint32", eFormatVectorOfSInt32) | |
| .Case("vector-uint32", eFormatVectorOfUInt32) | |
| .Case("vector-float32", eFormatVectorOfFloat32) | |
| .Case("vector-uint64", eFormatVectorOfUInt64) | |
| .Case("vector-uint128", eFormatVectorOfUInt128) | |
| .Default(eFormatInvalid); | |
| if (Info.format == eFormatInvalid) | |
| return make_parsing_error("qRegisterInfo: format"); | |
| Info.kinds[eRegisterKindGeneric] = | |
| Args::StringToGenericRegister(Elements["generic"]); | |
| return std::move(Info); | |
| } | |
| Expected<RegisterValue> parseRegisterValue(const RegisterInfo &Info, | |
| StringRef HexValue, | |
| llvm::support::endianness Endian, | |
| bool ZeroPad) { | |
| SmallString<128> Storage; | |
| if (ZeroPad && HexValue.size() < Info.byte_size * 2) { | |
| Storage.insert(Storage.begin(), Info.byte_size * 2 - HexValue.size(), '0'); | |
| Storage += HexValue; | |
| HexValue = Storage; | |
| } | |
| SmallVector<uint8_t, 64> Bytes(HexValue.size() / 2); | |
| StringExtractor(HexValue).GetHexBytes(Bytes, '\xcc'); | |
| RegisterValue Value; | |
| Status ST; | |
| Value.SetFromMemoryData( | |
| &Info, Bytes.data(), Bytes.size(), | |
| Endian == support::little ? eByteOrderLittle : eByteOrderBig, ST); | |
| if (ST.Fail()) | |
| return ST.ToError(); | |
| return Value; | |
| } | |
| //====== StopReply ============================================================= | |
| Expected<std::unique_ptr<StopReply>> | |
| StopReply::create(StringRef Response, llvm::support::endianness Endian, | |
| ArrayRef<RegisterInfo> RegInfos) { | |
| if (Response.size() < 3) | |
| return make_parsing_error("StopReply: Invalid packet"); | |
| if (Response.consume_front("T")) | |
| return StopReplyStop::create(Response, Endian, RegInfos); | |
| if (Response.consume_front("W")) | |
| return StopReplyExit::create(Response); | |
| return make_parsing_error("StopReply: Invalid packet"); | |
| } | |
| Expected<RegisterMap> StopReplyStop::parseRegisters( | |
| const StringMap<SmallVector<StringRef, 2>> &Elements, | |
| support::endianness Endian, ArrayRef<lldb_private::RegisterInfo> RegInfos) { | |
| RegisterMap Result; | |
| for (const auto &E : Elements) { | |
| StringRef Key = E.getKey(); | |
| const auto &Val = E.getValue(); | |
| if (Key.size() != 2) | |
| continue; | |
| unsigned int Reg; | |
| if (!to_integer(Key, Reg, 16)) | |
| continue; | |
| if (Val.size() != 1) | |
| return make_parsing_error( | |
| "StopReplyStop: multiple entries for register field [{0:x}]", Reg); | |
| auto RegValOr = parseRegisterValue(RegInfos[Reg], Val[0], Endian); | |
| if (!RegValOr) | |
| return RegValOr.takeError(); | |
| Result[Reg] = std::move(*RegValOr); | |
| } | |
| return std::move(Result); | |
| } | |
| Expected<std::unique_ptr<StopReplyStop>> | |
| StopReplyStop::create(StringRef Response, support::endianness Endian, | |
| ArrayRef<RegisterInfo> RegInfos) { | |
| unsigned int Signal; | |
| StringRef SignalStr = Response.take_front(2); | |
| Response = Response.drop_front(2); | |
| if (!to_integer(SignalStr, Signal, 16)) | |
| return make_parsing_error("StopReply: stop signal"); | |
| auto Elements = SplitPairList(Response); | |
| for (StringRef Field : | |
| {"name", "reason", "thread", "threads", "thread-pcs"}) { | |
| // This will insert an empty field if there is none. In the future, we | |
| // should probably differentiate between these fields not being present and | |
| // them being empty, but right now no tests depends on this. | |
| if (Elements.insert({Field, {""}}).first->second.size() != 1) | |
| return make_parsing_error( | |
| "StopReply: got multiple responses for the {0} field", Field); | |
| } | |
| StringRef Name = Elements["name"][0]; | |
| StringRef Reason = Elements["reason"][0]; | |
| lldb::tid_t Thread; | |
| if (!to_integer(Elements["thread"][0], Thread, 16)) | |
| return make_parsing_error("StopReply: thread"); | |
| SmallVector<StringRef, 20> Threads; | |
| SmallVector<StringRef, 20> Pcs; | |
| Elements["threads"][0].split(Threads, ','); | |
| Elements["thread-pcs"][0].split(Pcs, ','); | |
| if (Threads.size() != Pcs.size()) | |
| return make_parsing_error("StopReply: thread/PC count mismatch"); | |
| RegisterMap ThreadPcs; | |
| const RegisterInfo *PcInfo = find_if(RegInfos, [](const RegisterInfo &Info) { | |
| return Info.kinds[eRegisterKindGeneric] == LLDB_REGNUM_GENERIC_PC; | |
| }); | |
| assert(PcInfo); | |
| for (auto ThreadPc : zip(Threads, Pcs)) { | |
| lldb::tid_t Id; | |
| if (!to_integer(std::get<0>(ThreadPc), Id, 16)) | |
| return make_parsing_error("StopReply: Thread id '{0}'", | |
| std::get<0>(ThreadPc)); | |
| auto PcOr = parseRegisterValue(*PcInfo, std::get<1>(ThreadPc), Endian, | |
| /*ZeroPad*/ true); | |
| if (!PcOr) | |
| return PcOr.takeError(); | |
| ThreadPcs[Id] = std::move(*PcOr); | |
| } | |
| auto RegistersOr = parseRegisters(Elements, Endian, RegInfos); | |
| if (!RegistersOr) | |
| return RegistersOr.takeError(); | |
| return std::make_unique<StopReplyStop>(Signal, Thread, Name, | |
| std::move(ThreadPcs), | |
| std::move(*RegistersOr), Reason); | |
| } | |
| Expected<std::unique_ptr<StopReplyExit>> | |
| StopReplyExit::create(StringRef Response) { | |
| uint8_t Status; | |
| if (!to_integer(Response, Status, 16)) | |
| return make_parsing_error("StopReply: exit status"); | |
| return std::make_unique<StopReplyExit>(Status); | |
| } | |
| //====== Globals =============================================================== | |
| Expected<StringMap<StringRef>> SplitUniquePairList(StringRef caller, | |
| StringRef str) { | |
| SmallVector<StringRef, 20> elements; | |
| str.split(elements, ';'); | |
| StringMap<StringRef> pairs; | |
| for (StringRef s : elements) { | |
| std::pair<StringRef, StringRef> pair = s.split(':'); | |
| if (pairs.count(pair.first)) | |
| return make_parsing_error("{0}: Duplicate Key: {1}", caller, pair.first); | |
| pairs.insert(pair); | |
| } | |
| return pairs; | |
| } | |
| StringMap<SmallVector<StringRef, 2>> SplitPairList(StringRef str) { | |
| SmallVector<StringRef, 20> elements; | |
| str.split(elements, ';'); | |
| StringMap<SmallVector<StringRef, 2>> pairs; | |
| for (StringRef s : elements) { | |
| std::pair<StringRef, StringRef> pair = s.split(':'); | |
| pairs[pair.first].push_back(pair.second); | |
| } | |
| return pairs; | |
| } | |
| } // namespace llgs_tests | |
| std::ostream &lldb_private::operator<<(std::ostream &OS, | |
| const RegisterValue &RegVal) { | |
| ArrayRef<uint8_t> Bytes(static_cast<const uint8_t *>(RegVal.GetBytes()), | |
| RegVal.GetByteSize()); | |
| return OS << formatv("RegisterValue[{0}]: {1:@[x-2]}", RegVal.GetByteSize(), | |
| make_range(Bytes.begin(), Bytes.end())) | |
| .str(); | |
| } |