forked from OSchip/llvm-project
llgs-tests: Add support for "exit" stop-reply packets
Summary: This makes StopReply class abstract, so that we can represent different types of stop replies such as StopReplyStop and StopReplyExit (there should also be a StopReplySignal, but I don't need that right now so I haven't implemented it yet). This prepares the ground for a new test I'm writing. Reviewers: eugene, zturner Subscribers: lldb-commits Differential Revision: https://reviews.llvm.org/D41067 llvm-svn: 320820
This commit is contained in:
parent
5de20e039e
commit
93a582c00a
|
@ -48,6 +48,12 @@ struct WaitStatus {
|
|||
static WaitStatus Decode(int wstatus);
|
||||
};
|
||||
|
||||
inline bool operator==(WaitStatus a, WaitStatus b) {
|
||||
return a.type == b.type && a.status == b.status;
|
||||
}
|
||||
|
||||
inline bool operator!=(WaitStatus a, WaitStatus b) { return !(a == b); }
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
/// @class Host Host.h "lldb/Host/Host.h"
|
||||
/// @brief A class that provides host computer information.
|
||||
|
|
|
@ -133,70 +133,91 @@ const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const {
|
|||
}
|
||||
|
||||
//====== StopReply =============================================================
|
||||
const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; }
|
||||
|
||||
Expected<StopReply> StopReply::Create(StringRef response,
|
||||
llvm::support::endianness endian) {
|
||||
if (response.size() < 3 || !response.consume_front("T"))
|
||||
Expected<std::unique_ptr<StopReply>>
|
||||
StopReply::create(StringRef Response, llvm::support::endianness Endian) {
|
||||
if (Response.size() < 3)
|
||||
return make_parsing_error("StopReply: Invalid packet");
|
||||
if (Response.consume_front("T"))
|
||||
return StopReplyStop::create(Response, Endian);
|
||||
if (Response.consume_front("W"))
|
||||
return StopReplyExit::create(Response);
|
||||
return make_parsing_error("StopReply: Invalid packet");
|
||||
}
|
||||
|
||||
StopReply stop_reply;
|
||||
|
||||
StringRef signal = response.take_front(2);
|
||||
response = response.drop_front(2);
|
||||
if (!llvm::to_integer(signal, stop_reply.m_signal, 16))
|
||||
Expected<std::unique_ptr<StopReplyStop>>
|
||||
StopReplyStop::create(StringRef Response, llvm::support::endianness Endian) {
|
||||
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 :
|
||||
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)
|
||||
if (Elements.insert({Field, {""}}).first->second.size() != 1)
|
||||
return make_parsing_error(
|
||||
"StopReply: got multiple responses for the {0} field", field);
|
||||
"StopReply: got multiple responses for the {0} field", Field);
|
||||
}
|
||||
stop_reply.m_name = elements["name"][0];
|
||||
stop_reply.m_reason = elements["reason"][0];
|
||||
StringRef Name = Elements["name"][0];
|
||||
StringRef Reason = Elements["reason"][0];
|
||||
|
||||
if (!llvm::to_integer(elements["thread"][0], stop_reply.m_thread, 16))
|
||||
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())
|
||||
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");
|
||||
|
||||
for (size_t i = 0; i < threads.size(); i++) {
|
||||
lldb::tid_t thread_id;
|
||||
uint64_t pc;
|
||||
if (threads[i].getAsInteger(16, thread_id))
|
||||
return make_parsing_error("StopReply: thread ID at [{0}].", i);
|
||||
if (pcs[i].getAsInteger(16, pc))
|
||||
return make_parsing_error("StopReply: thread PC at [{0}].", i);
|
||||
U64Map ThreadPcs;
|
||||
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));
|
||||
|
||||
stop_reply.m_thread_pcs[thread_id] = pc;
|
||||
ThreadPcs[Id] = Pc;
|
||||
}
|
||||
|
||||
for (auto i = elements.begin(); i != elements.end(); i++) {
|
||||
StringRef key = i->getKey();
|
||||
const auto &val = i->getValue();
|
||||
if (key.size() == 2) {
|
||||
unsigned int reg;
|
||||
if (key.getAsInteger(16, reg))
|
||||
continue;
|
||||
if (val.size() != 1)
|
||||
return make_parsing_error(
|
||||
"StopReply: multiple entries for register field [{0:x}]", reg);
|
||||
RegisterMap Registers;
|
||||
for (const auto &E : Elements) {
|
||||
StringRef Key = E.getKey();
|
||||
const auto &Val = E.getValue();
|
||||
if (Key.size() != 2)
|
||||
continue;
|
||||
|
||||
stop_reply.m_registers[reg] = val[0].str();
|
||||
}
|
||||
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();
|
||||
}
|
||||
|
||||
return stop_reply;
|
||||
return llvm::make_unique<StopReplyStop>(Signal, Thread, Name, ThreadPcs,
|
||||
Registers, 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 ===============================================================
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#ifndef LLDB_SERVER_TESTS_MESSAGEOBJECTS_H
|
||||
#define LLDB_SERVER_TESTS_MESSAGEOBJECTS_H
|
||||
|
||||
#include "lldb/Host/Host.h"
|
||||
#include "lldb/lldb-types.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include "llvm/ADT/SmallString.h"
|
||||
|
@ -74,20 +75,67 @@ private:
|
|||
|
||||
class StopReply {
|
||||
public:
|
||||
static llvm::Expected<StopReply> Create(llvm::StringRef response,
|
||||
llvm::support::endianness endian);
|
||||
const U64Map &GetThreadPcs() const;
|
||||
StopReply() = default;
|
||||
virtual ~StopReply() = default;
|
||||
|
||||
static llvm::Expected<std::unique_ptr<StopReply>>
|
||||
create(llvm::StringRef response, llvm::support::endianness endian);
|
||||
|
||||
// for llvm::cast<>
|
||||
virtual lldb_private::WaitStatus getKind() const = 0;
|
||||
|
||||
StopReply(const StopReply &) = delete;
|
||||
void operator=(const StopReply &) = delete;
|
||||
};
|
||||
|
||||
class StopReplyStop : public StopReply {
|
||||
public:
|
||||
StopReplyStop(uint8_t Signal, lldb::tid_t ThreadId, llvm::StringRef Name,
|
||||
U64Map 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<std::unique_ptr<StopReplyStop>>
|
||||
create(llvm::StringRef response, llvm::support::endianness endian);
|
||||
|
||||
const U64Map &getThreadPcs() const { return ThreadPcs; }
|
||||
lldb::tid_t getThreadId() const { return ThreadId; }
|
||||
|
||||
// for llvm::cast<>
|
||||
lldb_private::WaitStatus getKind() const override {
|
||||
return lldb_private::WaitStatus{lldb_private::WaitStatus::Stop, Signal};
|
||||
}
|
||||
static bool classof(const StopReply *R) {
|
||||
return R->getKind().type == lldb_private::WaitStatus::Stop;
|
||||
}
|
||||
|
||||
private:
|
||||
StopReply() = default;
|
||||
void ParseResponse(llvm::StringRef response,
|
||||
llvm::support::endianness endian);
|
||||
unsigned int m_signal;
|
||||
lldb::tid_t m_thread;
|
||||
std::string m_name;
|
||||
U64Map m_thread_pcs;
|
||||
RegisterMap m_registers;
|
||||
std::string m_reason;
|
||||
uint8_t Signal;
|
||||
lldb::tid_t ThreadId;
|
||||
std::string Name;
|
||||
U64Map ThreadPcs;
|
||||
RegisterMap Registers;
|
||||
std::string Reason;
|
||||
};
|
||||
|
||||
class StopReplyExit : public StopReply {
|
||||
public:
|
||||
explicit StopReplyExit(uint8_t Status) : Status(Status) {}
|
||||
|
||||
static llvm::Expected<std::unique_ptr<StopReplyExit>>
|
||||
create(llvm::StringRef response);
|
||||
|
||||
// for llvm::cast<>
|
||||
lldb_private::WaitStatus getKind() const override {
|
||||
return lldb_private::WaitStatus{lldb_private::WaitStatus::Exit, Status};
|
||||
}
|
||||
static bool classof(const StopReply *R) {
|
||||
return R->getKind().type == lldb_private::WaitStatus::Exit;
|
||||
}
|
||||
|
||||
private:
|
||||
uint8_t Status;
|
||||
};
|
||||
|
||||
// Common functions for parsing packet data.
|
||||
|
|
|
@ -154,7 +154,8 @@ Optional<JThreadsInfo> TestClient::GetJThreadsInfo() {
|
|||
}
|
||||
|
||||
const StopReply &TestClient::GetLatestStopReply() {
|
||||
return m_stop_reply.getValue();
|
||||
assert(m_stop_reply);
|
||||
return *m_stop_reply;
|
||||
}
|
||||
|
||||
Error TestClient::SendMessage(StringRef message) {
|
||||
|
@ -236,7 +237,7 @@ Error TestClient::Continue(StringRef message) {
|
|||
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());
|
||||
if (Error E = creation.takeError())
|
||||
return E;
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include "lldb/Utility/ArchSpec.h"
|
||||
#include "lldb/Utility/Connection.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/Support/Casting.h"
|
||||
#include "llvm/Support/FormatVariadic.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
@ -44,6 +46,14 @@ public:
|
|||
const ProcessInfo &GetProcessInfo();
|
||||
llvm::Optional<JThreadsInfo> GetJThreadsInfo();
|
||||
const StopReply &GetLatestStopReply();
|
||||
template <typename T> llvm::Expected<const T &> GetLatestStopReplyAs() {
|
||||
assert(m_stop_reply);
|
||||
if (const auto *Reply = llvm::dyn_cast<T>(m_stop_reply.get()))
|
||||
return *Reply;
|
||||
return llvm::make_error<llvm::StringError>(
|
||||
llvm::formatv("Unexpected Stop Reply {0}", m_stop_reply->getKind()),
|
||||
llvm::inconvertibleErrorCode());
|
||||
}
|
||||
llvm::Error SendMessage(llvm::StringRef message);
|
||||
llvm::Error SendMessage(llvm::StringRef message,
|
||||
std::string &response_string);
|
||||
|
@ -62,7 +72,7 @@ private:
|
|||
result);
|
||||
|
||||
llvm::Optional<ProcessInfo> m_process_info;
|
||||
llvm::Optional<StopReply> m_stop_reply;
|
||||
std::unique_ptr<StopReply> m_stop_reply;
|
||||
unsigned int m_pc_register = UINT_MAX;
|
||||
};
|
||||
|
||||
|
|
|
@ -31,8 +31,9 @@ TEST_F(StandardStartupTest, TestStopReplyContainsThreadPcs) {
|
|||
auto jthreads_info = Client->GetJThreadsInfo();
|
||||
ASSERT_TRUE(jthreads_info);
|
||||
|
||||
auto stop_reply = Client->GetLatestStopReply();
|
||||
auto stop_reply_pcs = stop_reply.GetThreadPcs();
|
||||
auto stop_reply = Client->GetLatestStopReplyAs<StopReplyStop>();
|
||||
ASSERT_THAT_EXPECTED(stop_reply, Succeeded());
|
||||
auto stop_reply_pcs = stop_reply->getThreadPcs();
|
||||
auto thread_infos = jthreads_info->GetThreadInfos();
|
||||
ASSERT_EQ(stop_reply_pcs.size(), thread_infos.size())
|
||||
<< "Thread count mismatch.";
|
||||
|
|
Loading…
Reference in New Issue