Skip to content

Commit 56bbced

Browse files
authored
Add basic testing of libunwind.a runtimes build (#6417)
This builds the archive and checks relevant symbols are defined. While here, this refactors the runtimes test to share much more code between the different runtimes. Last but not least, this adds a convenience type-def for the libunwind runtimes builder. There is an inconsistency between how we spell things as `Libunwind` or `LibUnwind`. We should canonicalize on the former as it matches the underscores and other things we will spell in this space. I'm not fixing existing spellings in this PR but will send follow-ups for those.
1 parent 13fbe3c commit 56bbced

File tree

2 files changed

+83
-32
lines changed

2 files changed

+83
-32
lines changed

toolchain/driver/clang_runtimes.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,8 @@ class ClangArchiveRuntimesBuilder : public ClangRuntimesBuilderBase {
257257

258258
extern template class ClangArchiveRuntimesBuilder<Runtimes::LibUnwind>;
259259

260+
using LibunwindBuilder = ClangArchiveRuntimesBuilder<Runtimes::LibUnwind>;
261+
260262
// Builds the target-specific resource directory for Clang.
261263
//
262264
// There is a resource directory installed along side the Clang binary that

toolchain/driver/clang_runtimes_test.cpp

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <string>
1212
#include <utility>
1313

14+
#include "common/check.h"
1415
#include "common/ostream.h"
1516
#include "llvm/ADT/IntrusiveRefCntPtr.h"
1617
#include "llvm/ADT/SmallVector.h"
@@ -62,43 +63,79 @@ MATCHER_P(TextSymbolNamed, name_matcher, "") {
6263

6364
class ClangRuntimesTest : public ::testing::Test {
6465
public:
66+
// Helper to get the `llvm-nm` listing of defined symbols for an archive.
67+
//
68+
// TODO: It would be nice to use a library API and matchers instead of
69+
// `llvm-nm` and matching text on the output.
70+
auto NmListDefinedSymbols(const std::filesystem::path& archive)
71+
-> std::string {
72+
LLVMRunner llvm_runner(&install_paths_, &llvm::errs());
73+
std::string out;
74+
std::string err;
75+
bool result = Testing::CallWithCapturedOutput(out, err, [&] {
76+
return llvm_runner.Run(
77+
LLVMTool::Nm, {"--format=just-symbols", "--defined-only", "--quiet",
78+
archive.native()});
79+
});
80+
CARBON_CHECK(result, "Unable to run `llvm-nm`:\n{1}", err);
81+
82+
return out;
83+
}
84+
85+
// Helper to expect a specific symbol in the `llvm-nm` list.
86+
//
87+
// This handles platform-specific formatting of symbols.
88+
auto ExpectSymbol(llvm::StringRef nm_list, llvm::StringRef symbol) -> void {
89+
std::string symbol_substr = llvm::formatv(
90+
target_triple_.isMacOSX() ? "\n_{0}\n" : "\n{0}\n", symbol);
91+
92+
// Do the actual match with `HasSubstr` so it can explain failures.
93+
EXPECT_THAT(nm_list, HasSubstr(symbol_substr));
94+
}
95+
6596
InstallPaths install_paths_ =
6697
InstallPaths::MakeForBazelRunfiles(Testing::GetExePath());
67-
Runtimes::Cache runtimes_cache_ =
68-
*Runtimes::Cache::MakeSystem(install_paths_);
6998
llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> vfs_ =
7099
llvm::vfs::getRealFileSystem();
71-
};
72-
73-
TEST_F(ClangRuntimesTest, ResourceDir) {
74-
ClangRunner runner(&install_paths_, vfs_, &llvm::errs());
100+
// Note that for debugging, you can pass `llvm::errs()` as the vlog stream,
101+
// but this makes the output both very verbose and hard to use with multiple
102+
// threads.
103+
ClangRunner runner_{&install_paths_, vfs_};
75104

76105
// Note that we can't test arbitrary targets here as we need to be able to
77106
// compile the builtin functions for the target. We use the default target as
78107
// the most likely to pass.
79-
std::string target = llvm::sys::getDefaultTargetTriple();
80-
llvm::Triple target_triple(target);
81-
Runtimes::Cache::Features features = {.target = target};
82-
auto runtimes = *runtimes_cache_.Lookup(features);
83-
llvm::DefaultThreadPool threads(llvm::optimal_concurrency());
84-
85-
ClangResourceDirBuilder resource_dir_builder(&runner, &threads, target_triple,
86-
&runtimes);
108+
std::string target_ = llvm::sys::getDefaultTargetTriple();
109+
llvm::Triple target_triple_{target_};
110+
111+
Runtimes::Cache runtimes_cache_ =
112+
*Runtimes::Cache::MakeSystem(install_paths_);
113+
Runtimes::Cache::Features features = {.target = target_};
114+
Runtimes runtimes_ = *runtimes_cache_.Lookup(features);
115+
116+
// Note that for debugging it may be useful to replace this with a
117+
// single-threaded thread pool. However the test will be _much_ slower.
118+
llvm::DefaultThreadPool threads_{llvm::optimal_concurrency()};
119+
};
120+
121+
TEST_F(ClangRuntimesTest, ResourceDir) {
122+
ClangResourceDirBuilder resource_dir_builder(&runner_, &threads_,
123+
target_triple_, &runtimes_);
87124
auto build_result = std::move(resource_dir_builder).Wait();
88125
ASSERT_TRUE(build_result.ok()) << build_result.error();
89126
std::filesystem::path resource_dir_path = std::move(*build_result);
90127

91128
// For Linux we can directly check the CRT begin/end object files.
92-
if (target_triple.isOSLinux()) {
129+
if (target_triple_.isOSLinux()) {
93130
std::filesystem::path crt_begin_path =
94-
resource_dir_path / "lib" / target / "clang_rt.crtbegin.o";
131+
resource_dir_path / "lib" / target_ / "clang_rt.crtbegin.o";
95132
ASSERT_TRUE(std::filesystem::is_regular_file(crt_begin_path));
96133
auto begin_result =
97134
llvm::object::ObjectFile::createObjectFile(crt_begin_path.native());
98135
llvm::object::ObjectFile& crtbegin = *begin_result->getBinary();
99136
EXPECT_TRUE(crtbegin.isELF());
100137
EXPECT_TRUE(crtbegin.isObject());
101-
EXPECT_THAT(crtbegin.getArch(), Eq(target_triple.getArch()));
138+
EXPECT_THAT(crtbegin.getArch(), Eq(target_triple_.getArch()));
102139

103140
llvm::SmallVector<llvm::object::SymbolRef> symbols(crtbegin.symbols());
104141
// The first symbol should come from the source file.
@@ -110,14 +147,14 @@ TEST_F(ClangRuntimesTest, ResourceDir) {
110147
TextSymbolNamed("__do_fini")}));
111148

112149
std::filesystem::path crt_end_path =
113-
resource_dir_path / "lib" / target / "clang_rt.crtend.o";
150+
resource_dir_path / "lib" / target_ / "clang_rt.crtend.o";
114151
ASSERT_TRUE(std::filesystem::is_regular_file(crt_end_path));
115152
auto end_result =
116153
llvm::object::ObjectFile::createObjectFile(crt_end_path.native());
117154
llvm::object::ObjectFile& crtend = *end_result->getBinary();
118155
EXPECT_TRUE(crtend.isELF());
119156
EXPECT_TRUE(crtend.isObject());
120-
EXPECT_THAT(crtend.getArch(), Eq(target_triple.getArch()));
157+
EXPECT_THAT(crtend.getArch(), Eq(target_triple_.getArch()));
121158

122159
// Just check the source file symbol, not much of interest in the end.
123160
llvm::object::SymbolRef crtend_front_symbol = *crtend.symbol_begin();
@@ -129,25 +166,37 @@ TEST_F(ClangRuntimesTest, ResourceDir) {
129166
// than directly inspecting the objects is a bit awkward, but lets us easily
130167
// ignore the wrapping in an archive file.
131168
std::filesystem::path builtins_path =
132-
resource_dir_path / "lib" / target / "libclang_rt.builtins.a";
133-
LLVMRunner llvm_runner(&install_paths_, &llvm::errs());
134-
std::string out;
135-
std::string err;
136-
EXPECT_TRUE(Testing::CallWithCapturedOutput(out, err, [&] {
137-
return llvm_runner.Run(LLVMTool::Nm, {builtins_path.native()});
138-
}));
169+
resource_dir_path / "lib" / target_ / "libclang_rt.builtins.a";
170+
std::string builtins_symbols = NmListDefinedSymbols(builtins_path);
139171

140172
// Check that we found a definition of `__mulodi4`, a builtin function
141-
// provided by Compiler-RT, but not `libgcc` historically. Note that on macOS
142-
// there is a leading `_` due to mangling.
143-
EXPECT_THAT(out, HasSubstr(target_triple.isMacOSX() ? "T ___mulodi4\n"
144-
: "T __mulodi4\n"));
173+
// provided by Compiler-RT.
174+
ExpectSymbol(builtins_symbols, "__mulodi4");
145175

146176
// Check that we don't include the `chkstk` builtins outside of Windows.
147-
if (!target_triple.isOSWindows()) {
148-
EXPECT_THAT(out, Not(HasSubstr("chkstk")));
177+
if (!target_triple_.isOSWindows()) {
178+
EXPECT_THAT(builtins_symbols, Not(HasSubstr("chkstk")));
149179
}
150180
}
151181

182+
TEST_F(ClangRuntimesTest, Libunwind) {
183+
LibunwindBuilder libunwind_builder(&runner_, &threads_, target_triple_,
184+
&runtimes_);
185+
auto build_result = std::move(libunwind_builder).Wait();
186+
ASSERT_TRUE(build_result.ok()) << build_result.error();
187+
std::filesystem::path runtimes_path = std::move(*build_result);
188+
189+
std::filesystem::path libunwind_path = runtimes_path / "lib/libunwind.a";
190+
std::string libunwind_symbols = NmListDefinedSymbols(libunwind_path);
191+
192+
// Check a few of the main exported symbols here. The set here is somewhat
193+
// arbitrary, but chosen to be among the more stable names and have at least
194+
// one from most of the object files that should be linked into the archive.
195+
ExpectSymbol(libunwind_symbols, "_Unwind_Resume");
196+
ExpectSymbol(libunwind_symbols, "_Unwind_Backtrace");
197+
ExpectSymbol(libunwind_symbols, "__unw_getcontext");
198+
ExpectSymbol(libunwind_symbols, "__unw_get_proc_info");
199+
}
200+
152201
} // namespace
153202
} // namespace Carbon

0 commit comments

Comments
 (0)