forked from OSchip/llvm-project
llgs-test: Parse and store register info recieved from lldb-server
Summary: Right now the test client is not parsing register values correctly, which is manifesting itself in one test failing on 32-bit architectures (pr36013). This parses the information from the qRegisterInfo packets and stores it in the client, which will enable fixing the parsing in a follow up commit. I am also adding a new templated SendMessage overload, which enables one to send a message get a parsed response in a single call. Reviewers: eugene, davide Subscribers: lldb-commits Differential Revision: https://reviews.llvm.org/D43076 llvm-svn: 324722
This commit is contained in:
parent
425f7b4894
commit
a1181312ea
|
@ -8,16 +8,18 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "MessageObjects.h"
|
||||
#include "lldb/Interpreter/Args.h"
|
||||
#include "lldb/Utility/StructuredData.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> ProcessInfo::Create(StringRef response) {
|
||||
Expected<ProcessInfo> ProcessInfo::create(StringRef response) {
|
||||
ProcessInfo process_info;
|
||||
auto elements_or_error = SplitUniquePairList("ProcessInfo", response);
|
||||
if (!elements_or_error)
|
||||
|
@ -132,6 +134,72 @@ 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);
|
||||
}
|
||||
|
||||
//====== StopReply =============================================================
|
||||
Expected<std::unique_ptr<StopReply>>
|
||||
StopReply::create(StringRef Response, llvm::support::endianness Endian) {
|
||||
|
|
|
@ -25,9 +25,11 @@ typedef llvm::DenseMap<uint64_t, ThreadInfo> ThreadInfoMap;
|
|||
typedef llvm::DenseMap<uint64_t, uint64_t> U64Map;
|
||||
typedef llvm::DenseMap<unsigned int, std::string> RegisterMap;
|
||||
|
||||
class ProcessInfo {
|
||||
template <typename T> struct Parser { using result_type = T; };
|
||||
|
||||
class ProcessInfo : public Parser<ProcessInfo> {
|
||||
public:
|
||||
static llvm::Expected<ProcessInfo> Create(llvm::StringRef response);
|
||||
static llvm::Expected<ProcessInfo> create(llvm::StringRef response);
|
||||
lldb::pid_t GetPid() const;
|
||||
llvm::support::endianness GetEndian() const;
|
||||
|
||||
|
@ -73,6 +75,11 @@ private:
|
|||
ThreadInfoMap m_thread_infos;
|
||||
};
|
||||
|
||||
struct RegisterInfoParser : public Parser<lldb_private::RegisterInfo> {
|
||||
static llvm::Expected<lldb_private::RegisterInfo>
|
||||
create(llvm::StringRef Response);
|
||||
};
|
||||
|
||||
class StopReply {
|
||||
public:
|
||||
StopReply() = default;
|
||||
|
|
|
@ -25,8 +25,7 @@
|
|||
using namespace lldb;
|
||||
using namespace lldb_private;
|
||||
using namespace llvm;
|
||||
|
||||
namespace llgs_tests {
|
||||
using namespace llgs_tests;
|
||||
|
||||
TestClient::TestClient(std::unique_ptr<Connection> Conn) {
|
||||
SetConnection(Conn.release());
|
||||
|
@ -103,7 +102,7 @@ Expected<std::unique_ptr<TestClient>> TestClient::launchCustom(StringRef Log, Ar
|
|||
auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn)));
|
||||
|
||||
if (!InferiorArgs.empty()) {
|
||||
if (Error E = Client->QueryProcessInfo())
|
||||
if (Error E = Client->queryProcess())
|
||||
return std::move(E);
|
||||
}
|
||||
|
||||
|
@ -128,7 +127,7 @@ Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
|
|||
return E;
|
||||
if (Error E = SendMessage("qLaunchSuccess"))
|
||||
return E;
|
||||
if (Error E = QueryProcessInfo())
|
||||
if (Error E = queryProcess())
|
||||
return E;
|
||||
return Error::success();
|
||||
}
|
||||
|
@ -147,7 +146,9 @@ Error TestClient::ContinueThread(unsigned long thread_id) {
|
|||
return Continue(formatv("vCont;c:{0:x-}", thread_id).str());
|
||||
}
|
||||
|
||||
const ProcessInfo &TestClient::GetProcessInfo() { return *m_process_info; }
|
||||
const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() {
|
||||
return *m_process_info;
|
||||
}
|
||||
|
||||
Optional<JThreadsInfo> TestClient::GetJThreadsInfo() {
|
||||
std::string response;
|
||||
|
@ -201,42 +202,42 @@ Error TestClient::SendMessage(StringRef message, std::string &response_string,
|
|||
}
|
||||
|
||||
unsigned int TestClient::GetPcRegisterId() {
|
||||
if (m_pc_register != UINT_MAX)
|
||||
return m_pc_register;
|
||||
|
||||
for (unsigned int register_id = 0;; register_id++) {
|
||||
std::string message = formatv("qRegisterInfo{0:x-}", register_id).str();
|
||||
std::string response;
|
||||
if (SendMessage(message, response)) {
|
||||
GTEST_LOG_(ERROR) << "Unable to query register ID for PC register.";
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
auto elements_or_error = SplitUniquePairList("GetPcRegisterId", response);
|
||||
if (auto split_error = elements_or_error.takeError()) {
|
||||
GTEST_LOG_(ERROR) << "GetPcRegisterId: Error splitting response: "
|
||||
<< response;
|
||||
return UINT_MAX;
|
||||
}
|
||||
|
||||
auto elements = *elements_or_error;
|
||||
if (elements["alt-name"] == "pc" || elements["generic"] == "pc") {
|
||||
m_pc_register = register_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
assert(m_pc_register != LLDB_INVALID_REGNUM);
|
||||
return m_pc_register;
|
||||
}
|
||||
|
||||
llvm::Error TestClient::QueryProcessInfo() {
|
||||
std::string response;
|
||||
if (Error E = SendMessage("qProcessInfo", response))
|
||||
Error TestClient::qProcessInfo() {
|
||||
m_process_info = None;
|
||||
auto InfoOr = SendMessage<ProcessInfo>("qProcessInfo");
|
||||
if (!InfoOr)
|
||||
return InfoOr.takeError();
|
||||
m_process_info = std::move(*InfoOr);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TestClient::qRegisterInfos() {
|
||||
for (unsigned int Reg = 0;; ++Reg) {
|
||||
std::string Message = formatv("qRegisterInfo{0:x-}", Reg).str();
|
||||
Expected<RegisterInfo> InfoOr = SendMessage<RegisterInfoParser>(Message);
|
||||
if (!InfoOr) {
|
||||
consumeError(InfoOr.takeError());
|
||||
break;
|
||||
}
|
||||
m_register_infos.emplace_back(std::move(*InfoOr));
|
||||
if (m_register_infos[Reg].kinds[eRegisterKindGeneric] ==
|
||||
LLDB_REGNUM_GENERIC_PC)
|
||||
m_pc_register = Reg;
|
||||
}
|
||||
if (m_pc_register == LLDB_INVALID_REGNUM)
|
||||
return make_parsing_error("qRegisterInfo: generic");
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error TestClient::queryProcess() {
|
||||
if (Error E = qProcessInfo())
|
||||
return E;
|
||||
if (Error E = qRegisterInfos())
|
||||
return E;
|
||||
auto create_or_error = ProcessInfo::Create(response);
|
||||
if (!create_or_error)
|
||||
return create_or_error.takeError();
|
||||
m_process_info = *create_or_error;
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
|
@ -265,5 +266,3 @@ Error TestClient::Continue(StringRef message) {
|
|||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
} // namespace llgs_tests
|
||||
|
|
|
@ -74,12 +74,17 @@ public:
|
|||
std::string &response_string);
|
||||
llvm::Error SendMessage(llvm::StringRef message, std::string &response_string,
|
||||
PacketResult expected_result);
|
||||
|
||||
template <typename P>
|
||||
llvm::Expected<typename P::result_type> SendMessage(llvm::StringRef Message);
|
||||
unsigned int GetPcRegisterId();
|
||||
|
||||
private:
|
||||
TestClient(std::unique_ptr<lldb_private::Connection> Conn);
|
||||
|
||||
llvm::Error QueryProcessInfo();
|
||||
llvm::Error qProcessInfo();
|
||||
llvm::Error qRegisterInfos();
|
||||
llvm::Error queryProcess();
|
||||
llvm::Error Continue(llvm::StringRef message);
|
||||
std::string FormatFailedResult(
|
||||
const std::string &message,
|
||||
|
@ -88,9 +93,19 @@ private:
|
|||
|
||||
llvm::Optional<ProcessInfo> m_process_info;
|
||||
std::unique_ptr<StopReply> m_stop_reply;
|
||||
unsigned int m_pc_register = UINT_MAX;
|
||||
std::vector<lldb_private::RegisterInfo> m_register_infos;
|
||||
unsigned int m_pc_register = LLDB_INVALID_REGNUM;
|
||||
};
|
||||
|
||||
template <typename P>
|
||||
llvm::Expected<typename P::result_type>
|
||||
TestClient::SendMessage(llvm::StringRef Message) {
|
||||
std::string ResponseText;
|
||||
if (llvm::Error E = SendMessage(Message, ResponseText))
|
||||
return std::move(E);
|
||||
return P::create(ResponseText);
|
||||
}
|
||||
|
||||
} // namespace llgs_tests
|
||||
|
||||
#endif // LLDB_SERVER_TESTS_TESTCLIENT_H
|
||||
|
|
|
@ -44,7 +44,7 @@ TEST_F(StandardStartupTest, TestStopReplyContainsThreadPcs) {
|
|||
<< "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_pcs[tid], *pc_value)
|
||||
ASSERT_EQ(stop_reply_pc.second, *pc_value)
|
||||
<< "Mismatched PC for thread: " << tid;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue