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:
Pavel Labath 2017-12-15 15:19:45 +00:00
parent 5de20e039e
commit 93a582c00a
6 changed files with 147 additions and 60 deletions

View File

@ -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.

View File

@ -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 ===============================================================

View File

@ -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.

View File

@ -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;

View File

@ -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;
};

View File

@ -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.";