Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New framework for lldb client-server communication tests.
Summary: This is a new C++ test framework based on Google Test, and one working example test. The intention is to replace the existing tests in packages/Python/lldbsuite/test/tools/lldb-server/ with this suite and use this framework for all new client server tests. Reviewers: labath, beanz Reviewed By: labath, beanz Subscribers: beanz, emaste, zturner, tberghammer, takuto.ikuta, krytarowski, mgorny, lldb-commits Differential Revision: https://reviews.llvm.org/D32930 Patch by Jason Majors <jmajors@google.com> llvm-svn: 304793
- Loading branch information
Showing
10 changed files
with
789 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
if(CMAKE_SYSTEM_NAME MATCHES "Android|Linux|NetBSD") | ||
add_subdirectory(lldb-server) | ||
endif() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
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}) | ||
endfunction() | ||
|
||
add_lldb_test_executable(thread_inferior inferior/thread_inferior.cpp) | ||
|
||
add_definitions(-DLLDB_SERVER="$<TARGET_FILE:lldb-server>") | ||
add_definitions(-DTHREAD_INFERIOR="${CMAKE_CURRENT_BINARY_DIR}/thread_inferior") | ||
add_subdirectory(tests) | ||
add_dependencies(LLDBServerTests thread_inferior) |
41 changes: 41 additions & 0 deletions
41
lldb/unittests/tools/lldb-server/inferior/thread_inferior.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
add_lldb_unittest(LLDBServerTests | ||
TestClient.cpp | ||
MessageObjects.cpp | ||
ThreadIdsInJstopinfoTest.cpp | ||
|
||
LINK_LIBS | ||
lldbHost | ||
lldbCore | ||
lldbInterpreter | ||
lldbTarget | ||
lldbPluginPlatformLinux | ||
lldbPluginProcessGDBRemote | ||
LINK_COMPONENTS | ||
Support | ||
) |
207 changes: 207 additions & 0 deletions
207
lldb/unittests/tools/lldb-server/tests/MessageObjects.cpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
//===-- 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/Core/StructuredData.h" | ||
#include "llvm/ADT/StringExtras.h" | ||
#include "gtest/gtest.h" | ||
|
||
using namespace lldb_private; | ||
using namespace llvm; | ||
using namespace llvm::support; | ||
namespace llgs_tests { | ||
|
||
Expected<ProcessInfo> ProcessInfo::Create(StringRef response) { | ||
ProcessInfo process_info; | ||
auto elements_or_error = SplitPairList("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; } | ||
|
||
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); | ||
} | ||
|
||
bool ThreadInfo::ReadRegisterAsUint64(unsigned int register_id, | ||
uint64_t &value) const { | ||
StringRef value_str(m_registers.lookup(register_id)); | ||
if (value_str.getAsInteger(16, value)) { | ||
GTEST_LOG_(ERROR) | ||
<< formatv("ThreadInfo: Unable to parse register value at {0}.", | ||
register_id) | ||
.str(); | ||
return false; | ||
} | ||
|
||
sys::swapByteOrder(value); | ||
return true; | ||
} | ||
|
||
//====== JThreadsInfo ========================================================== | ||
Expected<JThreadsInfo> JThreadsInfo::Create(StringRef response, | ||
endianness endian) { | ||
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"); | ||
|
||
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(); | ||
} | ||
|
||
jthreads_info.m_thread_infos[tid] = | ||
ThreadInfo(name, reason, registers, signal); | ||
} | ||
|
||
return jthreads_info; | ||
} | ||
|
||
const ThreadInfoMap &JThreadsInfo::GetThreadInfos() const { | ||
return m_thread_infos; | ||
} | ||
|
||
//====== StopReply ============================================================= | ||
const U64Map &StopReply::GetThreadPcs() const { return m_thread_pcs; } | ||
|
||
Expected<StopReply> StopReply::Create(StringRef response, | ||
llvm::support::endianness endian) { | ||
StopReply stop_reply; | ||
|
||
auto elements_or_error = SplitPairList("StopReply", response); | ||
if (auto split_error = elements_or_error.takeError()) { | ||
return std::move(split_error); | ||
} | ||
|
||
auto elements = *elements_or_error; | ||
stop_reply.m_name = elements["name"]; | ||
stop_reply.m_reason = elements["reason"]; | ||
|
||
SmallVector<StringRef, 20> threads; | ||
SmallVector<StringRef, 20> pcs; | ||
elements["threads"].split(threads, ','); | ||
elements["thread-pcs"].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); | ||
|
||
stop_reply.m_thread_pcs[thread_id] = pc; | ||
} | ||
|
||
for (auto i = elements.begin(); i != elements.end(); i++) { | ||
StringRef key = i->getKey(); | ||
StringRef val = i->getValue(); | ||
if (key.size() >= 9 && key[0] == 'T' && key.substr(3, 6) == "thread") { | ||
if (val.getAsInteger(16, stop_reply.m_thread)) | ||
return make_parsing_error("StopReply: thread id"); | ||
if (key.substr(1, 2).getAsInteger(16, stop_reply.m_signal)) | ||
return make_parsing_error("StopReply: stop signal"); | ||
} else if (key.size() == 2) { | ||
unsigned int reg; | ||
if (!key.getAsInteger(16, reg)) { | ||
stop_reply.m_registers[reg] = val.str(); | ||
} | ||
} | ||
} | ||
|
||
return stop_reply; | ||
} | ||
|
||
//====== Globals =============================================================== | ||
Expected<StringMap<StringRef>> SplitPairList(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(s.split(':')); | ||
} | ||
|
||
return pairs; | ||
} | ||
} // namespace llgs_tests |
102 changes: 102 additions & 0 deletions
102
lldb/unittests/tools/lldb-server/tests/MessageObjects.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
//===-- MessageObjects.h ----------------------------------------*- C++ -*-===// | ||
// | ||
// The LLVM Compiler Infrastructure | ||
// | ||
// This file is distributed under the University of Illinois Open Source | ||
// License. See LICENSE.TXT for details. | ||
// | ||
//===----------------------------------------------------------------------===// | ||
|
||
#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<uint64_t, uint64_t> U64Map; | ||
typedef llvm::DenseMap<unsigned int, std::string> RegisterMap; | ||
|
||
class 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, | ||
const RegisterMap ®isters, unsigned int signal); | ||
|
||
llvm::StringRef ReadRegister(unsigned int register_id) const; | ||
bool ReadRegisterAsUint64(unsigned int register_id, uint64_t &value) const; | ||
|
||
private: | ||
std::string m_name; | ||
std::string m_reason; | ||
RegisterMap m_registers; | ||
unsigned int m_signal; | ||
}; | ||
|
||
class JThreadsInfo { | ||
public: | ||
static llvm::Expected<JThreadsInfo> Create(llvm::StringRef response, | ||
llvm::support::endianness endian); | ||
|
||
const ThreadInfoMap &GetThreadInfos() const; | ||
|
||
private: | ||
JThreadsInfo() = default; | ||
ThreadInfoMap m_thread_infos; | ||
}; | ||
|
||
class StopReply { | ||
public: | ||
static llvm::Expected<StopReply> Create(llvm::StringRef response, | ||
llvm::support::endianness endian); | ||
const U64Map &GetThreadPcs() const; | ||
|
||
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; | ||
}; | ||
|
||
// Common functions for parsing packet data. | ||
llvm::Expected<llvm::StringMap<llvm::StringRef>> | ||
SplitPairList(llvm::StringRef caller, 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 |
Oops, something went wrong.