Index: lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h +++ lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.h @@ -10,7 +10,9 @@ #ifndef LLDB_SERVER_TESTS_MESSAGEOBJECTS_H #define LLDB_SERVER_TESTS_MESSAGEOBJECTS_H +#include "lldb/Core/RegisterValue.h" #include "lldb/Host/Host.h" +#include "lldb/Utility/StructuredData.h" #include "lldb/lldb-types.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" @@ -22,8 +24,7 @@ namespace llgs_tests { class ThreadInfo; typedef llvm::DenseMap ThreadInfoMap; -typedef llvm::DenseMap U64Map; -typedef llvm::DenseMap RegisterMap; +typedef llvm::DenseMap RegisterMap; template struct Parser { using result_type = T; }; @@ -51,10 +52,9 @@ public: ThreadInfo() = default; ThreadInfo(llvm::StringRef name, llvm::StringRef reason, - const RegisterMap ®isters, unsigned int signal); + RegisterMap registers, unsigned int signal); - llvm::StringRef ReadRegister(unsigned int register_id) const; - llvm::Expected ReadRegisterAsUint64(unsigned int register_id) const; + const lldb_private::RegisterValue *ReadRegister(unsigned int Id) const; private: std::string m_name; @@ -63,14 +63,19 @@ unsigned int m_signal; }; -class JThreadsInfo { +class JThreadsInfo : public Parser { public: - static llvm::Expected Create(llvm::StringRef response, - llvm::support::endianness endian); + static llvm::Expected + create(llvm::StringRef Response, + llvm::ArrayRef RegInfos); const ThreadInfoMap &GetThreadInfos() const; private: + static llvm::Expected + parseRegisters(const lldb_private::StructuredData::Dictionary &Dict, + llvm::ArrayRef RegInfos); + JThreadsInfo() = default; ThreadInfoMap m_thread_infos; }; @@ -80,13 +85,18 @@ create(llvm::StringRef Response); }; +llvm::Expected +parseRegisterValue(const lldb_private::RegisterInfo &Info, + llvm::StringRef HexValue, llvm::support::endianness Endian); + class StopReply { public: StopReply() = default; virtual ~StopReply() = default; static llvm::Expected> - create(llvm::StringRef response, llvm::support::endianness endian); + create(llvm::StringRef Response, llvm::support::endianness Endian, + llvm::ArrayRef RegInfos); // for llvm::cast<> virtual lldb_private::WaitStatus getKind() const = 0; @@ -98,15 +108,17 @@ class StopReplyStop : public StopReply { public: StopReplyStop(uint8_t Signal, lldb::tid_t ThreadId, llvm::StringRef Name, - U64Map ThreadPcs, RegisterMap Registers, llvm::StringRef Reason) + RegisterMap ThreadPcs, RegisterMap Registers, + llvm::StringRef Reason) : Signal(Signal), ThreadId(ThreadId), Name(Name), ThreadPcs(std::move(ThreadPcs)), Registers(std::move(Registers)), Reason(Reason) {} static llvm::Expected> - create(llvm::StringRef response, llvm::support::endianness endian); + create(llvm::StringRef Response, llvm::support::endianness Endian, + llvm::ArrayRef RegInfos); - const U64Map &getThreadPcs() const { return ThreadPcs; } + const RegisterMap &getThreadPcs() const { return ThreadPcs; } lldb::tid_t getThreadId() const { return ThreadId; } // for llvm::cast<> @@ -118,10 +130,15 @@ } private: + static llvm::Expected parseRegisters( + const llvm::StringMap> &Elements, + llvm::support::endianness Endian, + llvm::ArrayRef RegInfos); + uint8_t Signal; lldb::tid_t ThreadId; std::string Name; - U64Map ThreadPcs; + RegisterMap ThreadPcs; RegisterMap Registers; std::string Reason; }; @@ -163,4 +180,8 @@ } // namespace llgs_tests +namespace lldb_private { +std::ostream &operator<<(std::ostream &OS, const RegisterValue &RegVal); +} + #endif // LLDB_SERVER_TESTS_MESSAGEOBJECTS_H Index: lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/MessageObjects.cpp @@ -9,14 +9,13 @@ #include "MessageObjects.h" #include "lldb/Interpreter/Args.h" -#include "lldb/Utility/StructuredData.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; -using namespace llvm::support; namespace llgs_tests { Expected ProcessInfo::create(StringRef response) { @@ -55,36 +54,50 @@ lldb::pid_t ProcessInfo::GetPid() const { return m_pid; } -endianness ProcessInfo::GetEndian() const { return m_endian; } +support::endianness ProcessInfo::GetEndian() const { return m_endian; } //====== ThreadInfo ============================================================ -ThreadInfo::ThreadInfo(StringRef name, StringRef reason, - const RegisterMap ®isters, unsigned int signal) - : m_name(name.str()), m_reason(reason.str()), m_registers(registers), - m_signal(signal) {} - -StringRef ThreadInfo::ReadRegister(unsigned int register_id) const { - return m_registers.lookup(register_id); -} - -Expected -ThreadInfo::ReadRegisterAsUint64(unsigned int register_id) const { - uint64_t value; - std::string value_str(m_registers.lookup(register_id)); - if (!llvm::to_integer(value_str, value, 16)) - return make_parsing_error("ThreadInfo value for register {0}: {1}", - register_id, value_str); - - sys::swapByteOrder(value); - return value; +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 JThreadsInfo::Create(StringRef response, - endianness endian) { + +Expected +JThreadsInfo::parseRegisters(const StructuredData::Dictionary &Dict, + ArrayRef 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::create(StringRef Response, + ArrayRef RegInfos) { JThreadsInfo jthreads_info; - StructuredData::ObjectSP json = StructuredData::ParseJSON(response); + StructuredData::ObjectSP json = StructuredData::ParseJSON(Response); StructuredData::Array *array = json->GetAsArray(); if (!array) return make_parsing_error("JThreadsInfo: JSON array"); @@ -108,23 +121,11 @@ if (!register_dict) return make_parsing_error("JThreadsInfo: registers JSON obj"); - RegisterMap registers; - - auto keys_obj = register_dict->GetKeys(); - auto keys = keys_obj->GetAsArray(); - for (size_t i = 0; i < keys->GetSize(); i++) { - StringRef key_str, value_str; - keys->GetItemAtIndexAsString(i, key_str); - register_dict->GetValueForKeyAsString(key_str, value_str); - unsigned int register_id; - if (key_str.getAsInteger(10, register_id)) - return make_parsing_error("JThreadsInfo: register key[{0}]", i); - - registers[register_id] = value_str.str(); - } - + auto RegsOr = parseRegisters(*register_dict, RegInfos); + if (!RegsOr) + return RegsOr.takeError(); jthreads_info.m_thread_infos[tid] = - ThreadInfo(name, reason, registers, signal); + ThreadInfo(name, reason, std::move(*RegsOr), signal); } return jthreads_info; @@ -200,20 +201,64 @@ return std::move(Info); } +Expected parseRegisterValue(const RegisterInfo &Info, + StringRef HexValue, + llvm::support::endianness Endian) { + SmallVector 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> -StopReply::create(StringRef Response, llvm::support::endianness Endian) { +StopReply::create(StringRef Response, llvm::support::endianness Endian, + ArrayRef RegInfos) { if (Response.size() < 3) return make_parsing_error("StopReply: Invalid packet"); if (Response.consume_front("T")) - return StopReplyStop::create(Response, Endian); + return StopReplyStop::create(Response, Endian, RegInfos); if (Response.consume_front("W")) return StopReplyExit::create(Response); return make_parsing_error("StopReply: Invalid packet"); } +Expected StopReplyStop::parseRegisters( + const StringMap> &Elements, + support::endianness Endian, ArrayRef 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> -StopReplyStop::create(StringRef Response, llvm::support::endianness Endian) { +StopReplyStop::create(StringRef Response, support::endianness Endian, + ArrayRef RegInfos) { unsigned int Signal; StringRef SignalStr = Response.take_front(2); Response = Response.drop_front(2); @@ -244,40 +289,31 @@ if (Threads.size() != Pcs.size()) return make_parsing_error("StopReply: thread/PC count mismatch"); - U64Map ThreadPcs; + 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; - uint64_t Pc; if (!to_integer(std::get<0>(ThreadPc), Id, 16)) return make_parsing_error("StopReply: Thread id '{0}'", std::get<0>(ThreadPc)); - if (!to_integer(std::get<1>(ThreadPc), Pc, 16)) - return make_parsing_error("StopReply Thread Pc '{0}'", - std::get<1>(ThreadPc)); - - ThreadPcs[Id] = Pc; - } - - RegisterMap Registers; - 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( - "StopReply: multiple entries for register field [{0:x}]", Reg); - - Registers[Reg] = Val[0].str(); + auto PcOr = parseRegisterValue(*PcInfo, std::get<1>(ThreadPc), Endian); + if (!PcOr) + return PcOr.takeError(); + ThreadPcs[Id] = std::move(*PcOr); } - return llvm::make_unique(Signal, Thread, Name, ThreadPcs, - Registers, Reason); + auto RegistersOr = parseRegisters(Elements, Endian, RegInfos); + if (!RegistersOr) + return RegistersOr.takeError(); + + return llvm::make_unique(Signal, Thread, Name, + std::move(ThreadPcs), + std::move(*RegistersOr), Reason); } Expected> @@ -319,3 +355,12 @@ return pairs; } } // namespace llgs_tests + +std::ostream &lldb_private::operator<<(std::ostream &OS, + const RegisterValue &RegVal) { + ArrayRef Bytes(static_cast(RegVal.GetBytes()), + RegVal.GetByteSize()); + return OS << formatv("RegisterValue[{0}]: {1:@[x-2]}", RegVal.GetByteSize(), + make_range(Bytes.begin(), Bytes.end())) + .str(); +} Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h +++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.h @@ -59,7 +59,7 @@ llvm::Error ContinueAll(); llvm::Error ContinueThread(unsigned long thread_id); const ProcessInfo &GetProcessInfo(); - llvm::Optional GetJThreadsInfo(); + llvm::Expected GetJThreadsInfo(); const StopReply &GetLatestStopReply(); template llvm::Expected GetLatestStopReplyAs() { assert(m_stop_reply); @@ -75,8 +75,9 @@ llvm::Error SendMessage(llvm::StringRef message, std::string &response_string, PacketResult expected_result); - template - llvm::Expected SendMessage(llvm::StringRef Message); + template + llvm::Expected SendMessage(llvm::StringRef Message, + CreateArgs &&... Args); unsigned int GetPcRegisterId(); private: @@ -97,13 +98,13 @@ unsigned int m_pc_register = LLDB_INVALID_REGNUM; }; -template +template llvm::Expected -TestClient::SendMessage(llvm::StringRef Message) { +TestClient::SendMessage(llvm::StringRef Message, CreateArgs &&... Args) { std::string ResponseText; if (llvm::Error E = SendMessage(Message, ResponseText)) return std::move(E); - return P::create(ResponseText); + return P::create(ResponseText, std::forward(Args)...); } } // namespace llgs_tests Index: lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/TestClient.cpp @@ -150,17 +150,8 @@ return *m_process_info; } -Optional TestClient::GetJThreadsInfo() { - std::string response; - if (SendMessage("jThreadsInfo", response)) - return llvm::None; - auto creation = JThreadsInfo::Create(response, m_process_info->GetEndian()); - if (auto create_error = creation.takeError()) { - GTEST_LOG_(ERROR) << toString(std::move(create_error)); - return llvm::None; - } - - return std::move(*creation); +Expected TestClient::GetJThreadsInfo() { + return SendMessage("jThreadsInfo", m_register_infos); } const StopReply &TestClient::GetLatestStopReply() { @@ -247,7 +238,8 @@ std::string response; if (Error E = SendMessage(message, response)) return E; - auto creation = StopReply::create(response, m_process_info->GetEndian()); + auto creation = StopReply::create(response, m_process_info->GetEndian(), + m_register_infos); if (Error E = creation.takeError()) return E; Index: lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp =================================================================== --- lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp +++ lldb/trunk/unittests/tools/lldb-server/tests/ThreadIdsInJstopinfoTest.cpp @@ -9,13 +9,19 @@ #include "TestBase.h" #include "TestClient.h" +#include "lldb/Utility/DataExtractor.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/Path.h" #include "llvm/Testing/Support/Error.h" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include using namespace llgs_tests; +using namespace lldb_private; using namespace llvm; +using namespace lldb; +using namespace testing; TEST_F(StandardStartupTest, TestStopReplyContainsThreadPcs) { // This inferior spawns 4 threads, then forces a break. @@ -29,7 +35,7 @@ ASSERT_NE(pc_reg, UINT_MAX); auto jthreads_info = Client->GetJThreadsInfo(); - ASSERT_TRUE(jthreads_info); + ASSERT_THAT_EXPECTED(jthreads_info, Succeeded()); auto stop_reply = Client->GetLatestStopReplyAs(); ASSERT_THAT_EXPECTED(stop_reply, Succeeded()); @@ -42,9 +48,7 @@ unsigned long tid = stop_reply_pc.first; ASSERT_TRUE(thread_infos.find(tid) != thread_infos.end()) << "Thread ID: " << tid << " not in JThreadsInfo."; - auto pc_value = thread_infos[tid].ReadRegisterAsUint64(pc_reg); - ASSERT_THAT_EXPECTED(pc_value, Succeeded()); - ASSERT_EQ(stop_reply_pc.second, *pc_value) - << "Mismatched PC for thread: " << tid; + EXPECT_THAT(thread_infos[tid].ReadRegister(pc_reg), + Pointee(Eq(stop_reply_pc.second))); } }