Imported Upstream version 6.10.0.49

Former-commit-id: 1d6753294b2993e1fbf92de9366bb9544db4189b
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2020-01-16 16:38:04 +00:00
parent d94e79959b
commit 468663ddbb
48518 changed files with 2789335 additions and 61176 deletions

View File

@@ -0,0 +1,3 @@
if(CMAKE_SYSTEM_NAME MATCHES "Android|Darwin|Linux|NetBSD")
add_subdirectory(lldb-server)
endif()

View File

@@ -0,0 +1,25 @@
set(ALL_LLDB_TEST_EXECUTABLES)
function(add_lldb_test_executable test_name)
set(EXCLUDE_FROM_ALL ON)
add_llvm_executable(${test_name} NO_INSTALL_RPATH ${ARGN})
set(outdir ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
set_output_directory(${test_name} BINARY_DIR ${outdir} LIBRARY_DIR ${outdir})
list(APPEND ALL_LLDB_TEST_EXECUTABLES ${test_name})
set(ALL_LLDB_TEST_EXECUTABLES ${ALL_LLDB_TEST_EXECUTABLES} PARENT_SCOPE)
endfunction()
add_lldb_test_executable(thread_inferior inferior/thread_inferior.cpp)
add_lldb_test_executable(environment_check inferior/environment_check.cpp)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_definitions(-DLLDB_SERVER="$<TARGET_FILE:debugserver>" -DLLDB_SERVER_IS_DEBUGSERVER=1)
else()
add_definitions(-DLLDB_SERVER="$<TARGET_FILE:lldb-server>" -DLLDB_SERVER_IS_DEBUGSERVER=0)
endif()
add_definitions(
-DLLDB_TEST_INFERIOR_PATH="${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_CFG_INTDIR}"
-DLLDB_TEST_INFERIOR_SUFFIX="${CMAKE_EXECUTABLE_SUFFIX}"
)
add_subdirectory(tests)

View File

@@ -0,0 +1,20 @@
//===-- thread_inferior.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <string>
#include <cstdlib>
int main() {
const char *value = std::getenv("LLDB_TEST_MAGIC_VARIABLE");
if (!value)
return 1;
if (std::string(value) != "LLDB_TEST_MAGIC_VALUE")
return 2;
return 0;
}

View File

@@ -0,0 +1,41 @@
//===-- thread_inferior.cpp -------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <atomic>
#include <chrono>
#include <string>
#include <thread>
#include <vector>
int main(int argc, char* argv[]) {
int thread_count = 2;
if (argc > 1) {
thread_count = std::stoi(argv[1], nullptr, 10);
}
std::atomic<bool> delay(true);
std::vector<std::thread> threads;
for (int i = 0; i < thread_count; i++) {
threads.push_back(std::thread([&delay] {
while (delay.load())
std::this_thread::sleep_for(std::chrono::seconds(1));
}));
}
// Cause a break.
volatile char *p = NULL;
*p = 'a';
delay.store(false);
for (std::thread& t : threads) {
t.join();
}
return 0;
}

View File

@@ -0,0 +1,21 @@
add_lldb_unittest(LLDBServerTests
LLGSTest.cpp
MessageObjects.cpp
TestBase.cpp
TestClient.cpp
ThreadIdsInJstopinfoTest.cpp
LINK_LIBS
lldbHost
lldbCore
lldbInterpreter
lldbTarget
lldbPluginPlatformLinux
lldbPluginProcessGDBRemote
LLVMTestingSupport
LINK_COMPONENTS
Support
)
add_dependencies(LLDBServerTests lldb-server ${ALL_LLDB_TEST_EXECUTABLES})

View File

@@ -0,0 +1,48 @@
//===-- LLGSTest.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestBase.h"
#include "lldb/Host/Host.h"
#include "llvm/Testing/Support/Error.h"
using namespace llgs_tests;
using namespace lldb_private;
using namespace llvm;
TEST_F(TestBase, LaunchModePreservesEnvironment) {
putenv(const_cast<char *>("LLDB_TEST_MAGIC_VARIABLE=LLDB_TEST_MAGIC_VALUE"));
auto ClientOr = TestClient::launch(getLogFileName(),
{getInferiorPath("environment_check")});
ASSERT_THAT_EXPECTED(ClientOr, Succeeded());
auto &Client = **ClientOr;
ASSERT_THAT_ERROR(Client.ContinueAll(), Succeeded());
ASSERT_THAT_EXPECTED(
Client.GetLatestStopReplyAs<StopReplyExit>(),
HasValue(testing::Property(&StopReply::getKind,
WaitStatus{WaitStatus::Exit, 0})));
}
TEST_F(TestBase, DS_TEST(DebugserverEnv)) {
// Test that --env takes precedence over inherited environment variables.
putenv(const_cast<char *>("LLDB_TEST_MAGIC_VARIABLE=foobar"));
auto ClientOr = TestClient::launchCustom(getLogFileName(),
{ "--env", "LLDB_TEST_MAGIC_VARIABLE=LLDB_TEST_MAGIC_VALUE" },
{getInferiorPath("environment_check")});
ASSERT_THAT_EXPECTED(ClientOr, Succeeded());
auto &Client = **ClientOr;
ASSERT_THAT_ERROR(Client.ContinueAll(), Succeeded());
ASSERT_THAT_EXPECTED(
Client.GetLatestStopReplyAs<StopReplyExit>(),
HasValue(testing::Property(&StopReply::getKind,
WaitStatus{WaitStatus::Exit, 0})));
}

View File

@@ -0,0 +1,366 @@
//===-- 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();
}

View File

@@ -0,0 +1,187 @@
//===-- MessageObjects.h ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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"
#include "llvm/Support/Endian.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <string>
namespace llgs_tests {
class ThreadInfo;
typedef llvm::DenseMap<uint64_t, ThreadInfo> ThreadInfoMap;
typedef llvm::DenseMap<unsigned int, lldb_private::RegisterValue> RegisterMap;
template <typename T> struct Parser { using result_type = T; };
class ProcessInfo : public Parser<ProcessInfo> {
public:
static llvm::Expected<ProcessInfo> create(llvm::StringRef response);
lldb::pid_t GetPid() const;
llvm::support::endianness GetEndian() const;
private:
ProcessInfo() = default;
lldb::pid_t m_pid;
lldb::pid_t m_parent_pid;
uint32_t m_real_uid;
uint32_t m_real_gid;
uint32_t m_effective_uid;
uint32_t m_effective_gid;
std::string m_triple;
llvm::SmallString<16> m_ostype;
llvm::support::endianness m_endian;
unsigned int m_ptrsize;
};
class ThreadInfo {
public:
ThreadInfo() = default;
ThreadInfo(llvm::StringRef name, llvm::StringRef reason,
RegisterMap registers, unsigned int signal);
const lldb_private::RegisterValue *ReadRegister(unsigned int Id) const;
private:
std::string m_name;
std::string m_reason;
RegisterMap m_registers;
unsigned int m_signal;
};
class JThreadsInfo : public Parser<JThreadsInfo> {
public:
static llvm::Expected<JThreadsInfo>
create(llvm::StringRef Response,
llvm::ArrayRef<lldb_private::RegisterInfo> RegInfos);
const ThreadInfoMap &GetThreadInfos() const;
private:
static llvm::Expected<RegisterMap>
parseRegisters(const lldb_private::StructuredData::Dictionary &Dict,
llvm::ArrayRef<lldb_private::RegisterInfo> RegInfos);
JThreadsInfo() = default;
ThreadInfoMap m_thread_infos;
};
struct RegisterInfoParser : public Parser<lldb_private::RegisterInfo> {
static llvm::Expected<lldb_private::RegisterInfo>
create(llvm::StringRef Response);
};
llvm::Expected<lldb_private::RegisterValue>
parseRegisterValue(const lldb_private::RegisterInfo &Info,
llvm::StringRef HexValue, llvm::support::endianness Endian);
class StopReply {
public:
StopReply() = default;
virtual ~StopReply() = default;
static llvm::Expected<std::unique_ptr<StopReply>>
create(llvm::StringRef Response, llvm::support::endianness Endian,
llvm::ArrayRef<lldb_private::RegisterInfo> RegInfos);
// 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,
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<std::unique_ptr<StopReplyStop>>
create(llvm::StringRef Response, llvm::support::endianness Endian,
llvm::ArrayRef<lldb_private::RegisterInfo> RegInfos);
const RegisterMap &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:
static llvm::Expected<RegisterMap> parseRegisters(
const llvm::StringMap<llvm::SmallVector<llvm::StringRef, 2>> &Elements,
llvm::support::endianness Endian,
llvm::ArrayRef<lldb_private::RegisterInfo> RegInfos);
uint8_t Signal;
lldb::tid_t ThreadId;
std::string Name;
RegisterMap 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.
llvm::Expected<llvm::StringMap<llvm::StringRef>>
SplitUniquePairList(llvm::StringRef caller, llvm::StringRef s);
llvm::StringMap<llvm::SmallVector<llvm::StringRef, 2>>
SplitPairList(llvm::StringRef s);
template <typename... Args>
llvm::Error make_parsing_error(llvm::StringRef format, Args &&... args) {
std::string error =
"Unable to parse " +
llvm::formatv(format.data(), std::forward<Args>(args)...).str();
return llvm::make_error<llvm::StringError>(error,
llvm::inconvertibleErrorCode());
}
} // namespace llgs_tests
namespace lldb_private {
std::ostream &operator<<(std::ostream &OS, const RegisterValue &RegVal);
}
#endif // LLDB_SERVER_TESTS_MESSAGEOBJECTS_H

View File

@@ -0,0 +1,36 @@
//===-- TestBase.cpp --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestBase.h"
#include <cstdlib>
using namespace llgs_tests;
using namespace llvm;
std::string TestBase::getLogFileName() {
const auto *test_info =
::testing::UnitTest::GetInstance()->current_test_info();
assert(test_info);
const char *Dir = getenv("LOG_FILE_DIRECTORY");
if (!Dir)
return "";
if (!llvm::sys::fs::is_directory(Dir)) {
GTEST_LOG_(WARNING) << "Cannot access log directory: " << Dir;
return "";
}
SmallString<64> DirStr(Dir);
sys::path::append(DirStr, std::string("server-") +
test_info->test_case_name() + "-" +
test_info->name() + ".log");
return DirStr.str();
}

View File

@@ -0,0 +1,48 @@
//===-- TestBase.h ----------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_SERVER_TESTS_TESTBASE_H
#define LLDB_SERVER_TESTS_TESTBASE_H
#include "TestClient.h"
#include "lldb/Host/HostInfo.h"
#include "llvm/Support/Path.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
namespace llgs_tests {
class TestBase: public ::testing::Test {
public:
static void SetUpTestCase() { lldb_private::HostInfo::Initialize(); }
static std::string getInferiorPath(llvm::StringRef Name) {
llvm::SmallString<64> Path(LLDB_TEST_INFERIOR_PATH);
llvm::sys::path::append(Path, Name + LLDB_TEST_INFERIOR_SUFFIX);
return Path.str();
}
static std::string getLogFileName();
};
class StandardStartupTest: public TestBase {
public:
void SetUp() override {
auto ClientOr = TestClient::launch(getLogFileName());
ASSERT_THAT_EXPECTED(ClientOr, llvm::Succeeded());
Client = std::move(*ClientOr);
}
protected:
std::unique_ptr<TestClient> Client;
};
} // namespace llgs_tests
#endif // LLDB_SERVER_TESTS_TESTBASE_H

View File

@@ -0,0 +1,268 @@
//===-- TestClient.cpp ------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "TestClient.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/common/TCPSocket.h"
#include "lldb/Host/posix/ConnectionFileDescriptorPosix.h"
#include "lldb/Interpreter/Args.h"
#include "lldb/Target/ProcessLaunchInfo.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Path.h"
#include "llvm/Testing/Support/Error.h"
#include "gtest/gtest.h"
#include <cstdlib>
#include <future>
#include <sstream>
#include <string>
using namespace lldb;
using namespace lldb_private;
using namespace llvm;
using namespace llgs_tests;
TestClient::TestClient(std::unique_ptr<Connection> Conn) {
SetConnection(Conn.release());
SendAck(); // Send this as a handshake.
}
TestClient::~TestClient() {
if (!IsConnected())
return;
std::string response;
// Debugserver (non-conformingly?) sends a reply to the k packet instead of
// simply closing the connection.
PacketResult result =
IsDebugServer() ? PacketResult::Success : PacketResult::ErrorDisconnected;
EXPECT_THAT_ERROR(SendMessage("k", response, result), Succeeded());
}
Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log) {
return launch(Log, {});
}
Expected<std::unique_ptr<TestClient>> TestClient::launch(StringRef Log, ArrayRef<StringRef> InferiorArgs) {
return launchCustom(Log, {}, InferiorArgs);
}
Expected<std::unique_ptr<TestClient>> TestClient::launchCustom(StringRef Log, ArrayRef<StringRef> ServerArgs, ArrayRef<StringRef> InferiorArgs) {
const ArchSpec &arch_spec = HostInfo::GetArchitecture();
Args args;
args.AppendArgument(LLDB_SERVER);
if (IsLldbServer())
args.AppendArgument("gdbserver");
args.AppendArgument("--reverse-connect");
if (!Log.empty()) {
args.AppendArgument(("--log-file=" + Log).str());
if (IsLldbServer())
args.AppendArgument("--log-channels=gdb-remote packets");
else
args.AppendArgument("--log-flags=0x800000");
}
Status status;
TCPSocket listen_socket(true, false);
status = listen_socket.Listen("127.0.0.1:0", 5);
if (status.Fail())
return status.ToError();
args.AppendArgument(
("localhost:" + Twine(listen_socket.GetLocalPortNumber())).str());
for (StringRef arg : ServerArgs)
args.AppendArgument(arg);
if (!InferiorArgs.empty()) {
args.AppendArgument("--");
for (StringRef arg : InferiorArgs)
args.AppendArgument(arg);
}
ProcessLaunchInfo Info;
Info.SetArchitecture(arch_spec);
Info.SetArguments(args, true);
StringList Env;
Host::GetEnvironment(Env);
Info.GetEnvironmentEntries() = Args(Env);
status = Host::LaunchProcess(Info);
if (status.Fail())
return status.ToError();
Socket *accept_socket;
listen_socket.Accept(accept_socket);
auto Conn = llvm::make_unique<ConnectionFileDescriptor>(accept_socket);
auto Client = std::unique_ptr<TestClient>(new TestClient(std::move(Conn)));
if (!InferiorArgs.empty()) {
if (Error E = Client->queryProcess())
return std::move(E);
}
return std::move(Client);
}
Error TestClient::SetInferior(llvm::ArrayRef<std::string> inferior_args) {
StringList env;
Host::GetEnvironment(env);
for (size_t i = 0; i < env.GetSize(); ++i) {
if (SendEnvironmentPacket(env[i].c_str()) != 0) {
return make_error<StringError>(
formatv("Failed to set environment variable: {0}", env[i]).str(),
inconvertibleErrorCode());
}
}
std::stringstream command;
command << "A";
for (size_t i = 0; i < inferior_args.size(); i++) {
if (i > 0)
command << ',';
std::string hex_encoded = toHex(inferior_args[i]);
command << hex_encoded.size() << ',' << i << ',' << hex_encoded;
}
if (Error E = SendMessage(command.str()))
return E;
if (Error E = SendMessage("qLaunchSuccess"))
return E;
if (Error E = queryProcess())
return E;
return Error::success();
}
Error TestClient::ListThreadsInStopReply() {
return SendMessage("QListThreadsInStopReply");
}
Error TestClient::SetBreakpoint(unsigned long address) {
return SendMessage(formatv("Z0,{0:x-},1", address).str());
}
Error TestClient::ContinueAll() { return Continue("vCont;c"); }
Error TestClient::ContinueThread(unsigned long thread_id) {
return Continue(formatv("vCont;c:{0:x-}", thread_id).str());
}
const llgs_tests::ProcessInfo &TestClient::GetProcessInfo() {
return *m_process_info;
}
Expected<JThreadsInfo> TestClient::GetJThreadsInfo() {
return SendMessage<JThreadsInfo>("jThreadsInfo", m_register_infos);
}
const StopReply &TestClient::GetLatestStopReply() {
assert(m_stop_reply);
return *m_stop_reply;
}
Error TestClient::SendMessage(StringRef message) {
std::string dummy_string;
return SendMessage(message, dummy_string);
}
Error TestClient::SendMessage(StringRef message, std::string &response_string) {
if (Error E = SendMessage(message, response_string, PacketResult::Success))
return E;
if (response_string[0] == 'E') {
return make_error<StringError>(
formatv("Error `{0}` while sending message: {1}", response_string,
message)
.str(),
inconvertibleErrorCode());
}
return Error::success();
}
Error TestClient::SendMessage(StringRef message, std::string &response_string,
PacketResult expected_result) {
StringExtractorGDBRemote response;
GTEST_LOG_(INFO) << "Send Packet: " << message.str();
PacketResult result = SendPacketAndWaitForResponse(message, response, false);
response.GetEscapedBinaryData(response_string);
GTEST_LOG_(INFO) << "Read Packet: " << response_string;
if (result != expected_result)
return make_error<StringError>(
formatv("Error sending message `{0}`: {1}", message, result).str(),
inconvertibleErrorCode());
return Error::success();
}
unsigned int TestClient::GetPcRegisterId() {
assert(m_pc_register != LLDB_INVALID_REGNUM);
return m_pc_register;
}
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;
return Error::success();
}
Error TestClient::Continue(StringRef message) {
assert(m_process_info.hasValue());
std::string response;
if (Error E = SendMessage(message, response))
return E;
auto creation = StopReply::create(response, m_process_info->GetEndian(),
m_register_infos);
if (Error E = creation.takeError())
return E;
m_stop_reply = std::move(*creation);
if (!isa<StopReplyStop>(m_stop_reply)) {
StringExtractorGDBRemote R;
PacketResult result = ReadPacket(R, GetPacketTimeout(), false);
if (result != PacketResult::ErrorDisconnected) {
return make_error<StringError>(
formatv("Expected connection close after receiving {0}. Got {1}/{2} "
"instead.",
response, result, R.GetStringRef())
.str(),
inconvertibleErrorCode());
}
}
return Error::success();
}

View File

@@ -0,0 +1,112 @@
//===-- TestClient.h --------------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#ifndef LLDB_SERVER_TESTS_TESTCLIENT_H
#define LLDB_SERVER_TESTS_TESTCLIENT_H
#include "MessageObjects.h"
#include "Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h"
#include "lldb/Target/ProcessLaunchInfo.h"
#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>
#if LLDB_SERVER_IS_DEBUGSERVER
#define LLGS_TEST(x) DISABLED_ ## x
#define DS_TEST(x) x
#else
#define LLGS_TEST(x) x
#define DS_TEST(x) DISABLED_ ## x
#endif
namespace llgs_tests {
class TestClient
: public lldb_private::process_gdb_remote::GDBRemoteCommunicationClient {
public:
static bool IsDebugServer() { return LLDB_SERVER_IS_DEBUGSERVER; }
static bool IsLldbServer() { return !IsDebugServer(); }
/// Launches the server, connects it to the client and returns the client. If
/// Log is non-empty, the server will write it's log to this file.
static llvm::Expected<std::unique_ptr<TestClient>> launch(llvm::StringRef Log);
/// Launches the server, while specifying the inferior on its command line.
/// When the client connects, it already has a process ready.
static llvm::Expected<std::unique_ptr<TestClient>>
launch(llvm::StringRef Log, llvm::ArrayRef<llvm::StringRef> InferiorArgs);
/// Allows user to pass additional arguments to the server. Be careful when
/// using this for generic tests, as the two stubs have different
/// command-line interfaces.
static llvm::Expected<std::unique_ptr<TestClient>>
launchCustom(llvm::StringRef Log, llvm::ArrayRef<llvm::StringRef> ServerArgs, llvm::ArrayRef<llvm::StringRef> InferiorArgs);
~TestClient() override;
llvm::Error SetInferior(llvm::ArrayRef<std::string> inferior_args);
llvm::Error ListThreadsInStopReply();
llvm::Error SetBreakpoint(unsigned long address);
llvm::Error ContinueAll();
llvm::Error ContinueThread(unsigned long thread_id);
const ProcessInfo &GetProcessInfo();
llvm::Expected<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);
llvm::Error SendMessage(llvm::StringRef message, std::string &response_string,
PacketResult expected_result);
template <typename P, typename... CreateArgs>
llvm::Expected<typename P::result_type> SendMessage(llvm::StringRef Message,
CreateArgs &&... Args);
unsigned int GetPcRegisterId();
private:
TestClient(std::unique_ptr<lldb_private::Connection> Conn);
llvm::Error qProcessInfo();
llvm::Error qRegisterInfos();
llvm::Error queryProcess();
llvm::Error Continue(llvm::StringRef message);
std::string FormatFailedResult(
const std::string &message,
lldb_private::process_gdb_remote::GDBRemoteCommunication::PacketResult
result);
llvm::Optional<ProcessInfo> m_process_info;
std::unique_ptr<StopReply> m_stop_reply;
std::vector<lldb_private::RegisterInfo> m_register_infos;
unsigned int m_pc_register = LLDB_INVALID_REGNUM;
};
template <typename P, typename... CreateArgs>
llvm::Expected<typename P::result_type>
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, std::forward<CreateArgs>(Args)...);
}
} // namespace llgs_tests
#endif // LLDB_SERVER_TESTS_TESTCLIENT_H

View File

@@ -0,0 +1,54 @@
//===-- ThreadsInJstopinfoTest.cpp ------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#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 <string>
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.
ASSERT_THAT_ERROR(
Client->SetInferior({getInferiorPath("thread_inferior"), "4"}),
Succeeded());
ASSERT_THAT_ERROR(Client->ListThreadsInStopReply(), Succeeded());
ASSERT_THAT_ERROR(Client->ContinueAll(), Succeeded());
unsigned int pc_reg = Client->GetPcRegisterId();
ASSERT_NE(pc_reg, UINT_MAX);
auto jthreads_info = Client->GetJThreadsInfo();
ASSERT_THAT_EXPECTED(jthreads_info, Succeeded());
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.";
for (auto stop_reply_pc : stop_reply_pcs) {
unsigned long tid = stop_reply_pc.first;
ASSERT_TRUE(thread_infos.find(tid) != thread_infos.end())
<< "Thread ID: " << tid << " not in JThreadsInfo.";
EXPECT_THAT(thread_infos[tid].ReadRegister(pc_reg),
Pointee(Eq(stop_reply_pc.second)));
}
}