367 lines
13 KiB
C++
367 lines
13 KiB
C++
//===-- MessageObjects.cpp --------------------------------------*- C++ -*-===//
|
|
//
|
|
// The LLVM Compiler Infrastructure
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "MessageObjects.h"
|
|
#include "lldb/Interpreter/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 signal)
|
|
: m_name(name.str()), m_reason(reason.str()),
|
|
m_registers(std::move(registers)), m_signal(signal) {}
|
|
|
|
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
|
|
},
|
|
NULL,
|
|
NULL,
|
|
NULL, // 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) {
|
|
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);
|
|
if (!PcOr)
|
|
return PcOr.takeError();
|
|
ThreadPcs[Id] = std::move(*PcOr);
|
|
}
|
|
|
|
auto RegistersOr = parseRegisters(Elements, Endian, RegInfos);
|
|
if (!RegistersOr)
|
|
return RegistersOr.takeError();
|
|
|
|
return llvm::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 llvm::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();
|
|
}
|