aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--include/pdb/address.hpp4
-rw-r--r--include/pdb/call_stack.hpp5
-rw-r--r--test/test_lib.cpp14
-rw-r--r--test/test_lib.hpp1
-rw-r--r--test/unit_tests/CMakeLists.txt2
-rw-r--r--test/unit_tests/call_stack.cpp79
-rw-r--r--test/unit_tests/dbghelp.cpp22
-rw-r--r--test/unit_tests/fixtures.hpp82
8 files changed, 165 insertions, 44 deletions
diff --git a/include/pdb/address.hpp b/include/pdb/address.hpp
index fa692ad..f7a7239 100644
--- a/include/pdb/address.hpp
+++ b/include/pdb/address.hpp
@@ -20,6 +20,10 @@ inline std::string format_address(Address address) {
return oss.str();
}
+inline std::string format_address(void* address) {
+ return format_address(reinterpret_cast<Address>(address));
+}
+
inline bool parse_address(Address& dest, const std::string& src) {
std::istringstream iss{src};
iss >> std::hex;
diff --git a/include/pdb/call_stack.hpp b/include/pdb/call_stack.hpp
index f0e13af..ef4c119 100644
--- a/include/pdb/call_stack.hpp
+++ b/include/pdb/call_stack.hpp
@@ -43,6 +43,11 @@ public:
const std::array<Address, max_length> frames;
const std::size_t length;
+ const Address* begin() const { return frames.data(); }
+ const Address* cbegin() const { return begin(); }
+ const Address* end() const { return begin() + length; }
+ const Address* cend() const { return end(); }
+
private:
CallStack() = default;
};
diff --git a/test/test_lib.cpp b/test/test_lib.cpp
index 63e51ad..711d4fd 100644
--- a/test/test_lib.cpp
+++ b/test/test_lib.cpp
@@ -18,22 +18,26 @@ void do_print_call_stack() {
call_stack.dump(boost::nowide::cout, dbghelp);
}
+void do_throw_call_stack() {
+ throw pdb::CallStack::capture();
+}
+
} // namespace
volatile int var = 42;
void baz(F f) {
- boost::nowide::cout << "baz\n";
+ boost::nowide::cout << "baz " << pdb::format_address(&baz) << '\n';
f();
}
void bar(F f) {
- boost::nowide::cout << "bar\n";
+ boost::nowide::cout << "bar " << pdb::format_address(&bar) << '\n';
baz(f);
}
void foo(F f) {
- boost::nowide::cout << "foo\n";
+ boost::nowide::cout << "foo " << pdb::format_address(&foo) << '\n';
bar(f);
}
@@ -41,4 +45,8 @@ void print_call_stack() {
foo(&do_print_call_stack);
}
+void throw_call_stack() {
+ foo(&do_throw_call_stack);
+}
+
} // namespace test_ns
diff --git a/test/test_lib.hpp b/test/test_lib.hpp
index cf7b841..bc488dc 100644
--- a/test/test_lib.hpp
+++ b/test/test_lib.hpp
@@ -15,5 +15,6 @@ TEST_LIB_API void bar(F);
TEST_LIB_API void baz(F);
TEST_LIB_API void print_call_stack();
+TEST_LIB_API void throw_call_stack();
} // namespace test_ns
diff --git a/test/unit_tests/CMakeLists.txt b/test/unit_tests/CMakeLists.txt
index 6b5eca6..1241061 100644
--- a/test/unit_tests/CMakeLists.txt
+++ b/test/unit_tests/CMakeLists.txt
@@ -1,6 +1,6 @@
find_package(Boost REQUIRED COMPONENTS filesystem unit_test_framework)
-add_executable(unit_tests main.cpp dbghelp.cpp error.cpp)
+add_executable(unit_tests main.cpp call_stack.cpp dbghelp.cpp error.cpp)
target_link_libraries(unit_tests PRIVATE pdb_repo test_lib)
target_link_libraries(unit_tests PRIVATE Boost::disable_autolinking Boost::filesystem Boost::nowide Boost::unit_test_framework)
diff --git a/test/unit_tests/call_stack.cpp b/test/unit_tests/call_stack.cpp
new file mode 100644
index 0000000..481ab6e
--- /dev/null
+++ b/test/unit_tests/call_stack.cpp
@@ -0,0 +1,79 @@
+#include "fixtures.hpp"
+
+#include <pdb/all.hpp>
+#include <test_lib.hpp>
+
+#include <boost/test/unit_test.hpp>
+
+#include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
+
+BOOST_FIXTURE_TEST_SUITE(call_stack_tests, CurrentProcess)
+
+BOOST_AUTO_TEST_CASE(call_stack) {
+ try {
+ test_ns::throw_call_stack();
+ } catch (const pdb::CallStack& call_stack) {
+ // First, check that the call stack have been caught.
+ BOOST_TEST(true, "Caught the call stack");
+
+ // Debug output:
+ std::vector<std::string> pretty;
+ pretty.reserve(call_stack.length);
+
+ BOOST_TEST_MESSAGE("Call stack:");
+ for (const auto& addr : call_stack) {
+ pretty.emplace_back(call_stack.pretty_print_address(dbghelp, addr));
+ BOOST_TEST_MESSAGE('\t' << pdb::format_address(addr) << ' ' << pretty.back());
+ }
+
+ // Second, resolve the symbols:
+ std::vector<pdb::SymbolInfo> symbols;
+ symbols.reserve(call_stack.length);
+
+ BOOST_TEST_MESSAGE("Resolved symbols:");
+ for (const auto& addr : call_stack) {
+ symbols.emplace_back(dbghelp.resolve_symbol(addr));
+ const auto& symbol = symbols.back();
+ BOOST_TEST_MESSAGE('\t' << pdb::format_address(symbol.get_offline_address()) << ' '
+ << symbol.get_name());
+ }
+
+ {
+ // Third, check that the expected function frames are in the call stack.
+ const auto expected = expected_function_addresses();
+
+ const auto check = [&](pdb::Address addr) {
+ for (const auto& symbol : symbols)
+ if (symbol.get_offline_address() == addr)
+ return true;
+ return false;
+ };
+ for (const auto& addr : expected) {
+ BOOST_TEST(check(addr), "Function frame captured: " << pdb::format_address(addr));
+ }
+ }
+
+ {
+ // Fourth, check that the expected function names are in the call stack.
+ const auto expected = expected_functions_full();
+
+ const auto check = [&](const std::string& name) {
+ for (const auto& actual : pretty)
+ if (actual.find(name) != std::string::npos)
+ return true;
+ return false;
+ };
+ for (const auto& name : expected) {
+ BOOST_TEST(check(name), "Function frame captured: " << name);
+ }
+ }
+ return;
+ }
+ // We must catch the call stack.
+ BOOST_TEST(false, "Didn't catch the call stack");
+}
+
+BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/unit_tests/dbghelp.cpp b/test/unit_tests/dbghelp.cpp
index 4346bf7..bd2d300 100644
--- a/test/unit_tests/dbghelp.cpp
+++ b/test/unit_tests/dbghelp.cpp
@@ -1,8 +1,6 @@
#include "fixtures.hpp"
-#include "paths.hpp"
#include <pdb/all.hpp>
-#include <test_lib.hpp>
#include <boost/test/unit_test.hpp>
@@ -10,15 +8,7 @@
#include <string>
#include <vector>
-namespace {
-
-void throw_call_stack() {
- throw pdb::CallStack::capture();
-}
-
-} // namespace
-
-BOOST_FIXTURE_TEST_SUITE(dbghelp_tests, DbgHelpWithSymbols)
+BOOST_FIXTURE_TEST_SUITE(enum_symbols_tests, PostMortem)
BOOST_AUTO_TEST_CASE(enum_symbols) {
// Symbols can be enumerated, and all the expected symbols are there.
@@ -44,14 +34,4 @@ BOOST_AUTO_TEST_CASE(enum_symbols) {
}
}
-BOOST_AUTO_TEST_CASE(call_stack) {
- try {
- test_ns::foo(&throw_call_stack);
- } catch (const pdb::CallStack& call_stack) {
- BOOST_TEST(true, "Caught the call stack");
- return;
- }
- BOOST_TEST(false, "Didn't catch the call stack");
-}
-
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/unit_tests/fixtures.hpp b/test/unit_tests/fixtures.hpp
index eb7dcc0..fadd393 100644
--- a/test/unit_tests/fixtures.hpp
+++ b/test/unit_tests/fixtures.hpp
@@ -3,6 +3,7 @@
#include "paths.hpp"
#include <pdb/all.hpp>
+#include <test_lib.hpp>
#include <boost/filesystem.hpp>
#include <boost/test/unit_test.hpp>
@@ -12,19 +13,6 @@
#include <unordered_set>
#include <utility>
-class DbgHelp {
-public:
- DbgHelp() : dbghelp{pdb::DbgHelp::post_mortem()} { BOOST_TEST_MESSAGE("Initializing DbgHelp"); }
-
- ~DbgHelp() { BOOST_TEST_MESSAGE("Cleaning up DbgHelp"); }
-
- const pdb::DbgHelp dbghelp;
-
-private:
- DbgHelp(const DbgHelp&) = delete;
- DbgHelp& operator=(const DbgHelp&) = delete;
-};
-
template <typename T>
using Set = std::unordered_set<T>;
@@ -34,9 +22,18 @@ Set<T> join(Set<T>&& xs, Set<T>&& ys) {
return std::move(xs);
}
-class DbgHelpWithSymbols : public DbgHelp {
+class DbgHelp {
public:
- DbgHelpWithSymbols() { load_test_lib_pdb(); }
+ DbgHelp(pdb::DbgHelp&& dbghelp) : dbghelp{std::move(dbghelp)} {}
+
+ ~DbgHelp() { BOOST_TEST_MESSAGE("Cleaning up DbgHelp"); }
+
+ const pdb::DbgHelp dbghelp;
+
+ static const std::string& get_module_name() {
+ static const std::string name{"test_lib"};
+ return name;
+ }
static const std::string& get_namespace() {
static const std::string name{"test_ns"};
@@ -44,16 +41,41 @@ public:
}
typedef Set<std::string> SymbolList;
+ typedef Set<pdb::Address> AddressList;
+
+ static AddressList expected_function_addresses() {
+ return cast({&test_ns::foo, &test_ns::bar, &test_ns::baz});
+ }
static SymbolList expected_functions() { return make_qualified({"foo", "bar", "baz"}); }
+ static SymbolList expected_functions_full() { return add_module(expected_functions()); }
+
static SymbolList expected_variables() { return make_qualified({"var"}); }
static SymbolList expected_symbols() {
return join(expected_functions(), expected_variables());
}
+protected:
+ static pdb::DbgHelp init_dbghelp(bool current_process) {
+ BOOST_TEST_MESSAGE("Initializing DbgHelp");
+ if (current_process) {
+ return pdb::DbgHelp::current_process();
+ } else {
+ return pdb::DbgHelp::post_mortem();
+ }
+ }
+
private:
+ static AddressList cast(Set<void*>&& fs) {
+ AddressList addresses;
+ for (auto&& f : fs) {
+ addresses.emplace(reinterpret_cast<pdb::Address>(f));
+ }
+ return addresses;
+ }
+
static SymbolList make_qualified(SymbolList&& plain) {
SymbolList qualified;
for (auto&& name : plain) {
@@ -62,13 +84,35 @@ private:
return qualified;
}
- void load_test_lib_pdb() {
- const auto pdb_path = get_test_lib_pdb_path().string();
+ static SymbolList add_module(SymbolList&& plain) {
+ SymbolList full;
+ for (auto&& name : plain) {
+ full.emplace(get_module_name() + "!" + std::move(name));
+ }
+ return full;
+ }
+
+ DbgHelp(const DbgHelp&) = delete;
+ DbgHelp& operator=(const DbgHelp&) = delete;
+};
+
+class PostMortem : public DbgHelp {
+public:
+ PostMortem() : DbgHelp{init_dbghelp(false)} { load_module_pdb(); }
+
+private:
+ void load_module_pdb() {
+ const auto pdb_path = get_module_pdb_path().string();
BOOST_TEST_MESSAGE("Loading PDB: " << pdb_path);
dbghelp.load_pdb(pdb_path);
}
- static boost::filesystem::path get_test_lib_pdb_path() {
- return Paths::get().exe_dir / "test_lib.pdb";
+ static boost::filesystem::path get_module_pdb_path() {
+ return Paths::get().exe_dir / (get_module_name() + ".pdb");
}
};
+
+class CurrentProcess : public DbgHelp {
+public:
+ CurrentProcess() : DbgHelp{init_dbghelp(true)} {}
+};