diff options
Diffstat (limited to 'gnu/llvm/lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp')
-rw-r--r-- | gnu/llvm/lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/gnu/llvm/lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp b/gnu/llvm/lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp new file mode 100644 index 00000000000..e4f1a92f46a --- /dev/null +++ b/gnu/llvm/lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp @@ -0,0 +1,374 @@ +//===-- 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(); +} |