123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302 |
- //===----------------------------------------------------------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #ifndef TEST_SUPPORT_DEBUG_MODE_HELPER_H
- #define TEST_SUPPORT_DEBUG_MODE_HELPER_H
- #ifndef _LIBCPP_DEBUG
- #error _LIBCPP_DEBUG must be defined before including this header
- #endif
- #include <ciso646>
- #ifndef _LIBCPP_VERSION
- #error "This header may only be used for libc++ tests"
- #endif
- #include <__debug>
- #include <utility>
- #include <cstddef>
- #include <cstdlib>
- #include <cassert>
- #include <string_view>
- #include <sstream>
- #include <iostream>
- #include <unistd.h>
- #include <sys/wait.h>
- #include "test_macros.h"
- #include "assert_checkpoint.h"
- #include "test_allocator.h"
- #if TEST_STD_VER < 11
- # error "C++11 or greater is required to use this header"
- #endif
- struct DebugInfoMatcher {
- static const int any_line = -1;
- static constexpr const char* any_file = "*";
- static constexpr const char* any_msg = "*";
- constexpr DebugInfoMatcher() : is_empty(true), msg(any_msg, __builtin_strlen(any_msg)), file(any_file, __builtin_strlen(any_file)), line(any_line) { }
- constexpr DebugInfoMatcher(const char* msg, const char* file = any_file, int line = any_line)
- : is_empty(false), msg(msg, __builtin_strlen(msg)), file(file, __builtin_strlen(file)), line(line) {}
- bool Matches(std::__libcpp_debug_info const& got) const {
- assert(!empty() && "empty matcher");
- if (CheckLineMatches(got.__line_) && CheckFileMatches(got.__file_) &&
- CheckMessageMatches(got.__msg_))
- return true;
- // Write to stdout because that's the file descriptor captured by the parent
- // process.
- std::cout << "Failed to match debug info!\n"
- << ToString() << "\n"
- << "VS\n"
- << got.what() << "\n";
- return false;
- }
- std::string ToString() const {
- std::stringstream ss;
- ss << "msg = \"" << msg << "\"\n"
- << "line = " << (line == any_line ? "'*'" : std::to_string(line)) << "\n"
- << "file = " << (file == any_file ? "'*'" : any_file) << "";
- return ss.str();
- }
- bool empty() const { return is_empty; }
- private:
- bool CheckLineMatches(int got_line) const {
- if (line == any_line)
- return true;
- return got_line == line;
- }
- bool CheckFileMatches(std::string_view got_file) const {
- assert(!empty() && "empty matcher");
- if (file == any_file)
- return true;
- std::size_t found_at = got_file.find(file);
- if (found_at == std::string_view::npos)
- return false;
- // require the match start at the beginning of the file or immediately after
- // a directory separator.
- if (found_at != 0) {
- char last_char = got_file[found_at - 1];
- if (last_char != '/' && last_char != '\\')
- return false;
- }
- // require the match goes until the end of the string.
- return got_file.substr(found_at) == file;
- }
- bool CheckMessageMatches(std::string_view got_msg) const {
- assert(!empty() && "empty matcher");
- if (msg == any_msg)
- return true;
- std::size_t found_at = got_msg.find(msg);
- if (found_at == std::string_view::npos)
- return false;
- // Allow any match
- return true;
- }
- private:
- bool is_empty;
- std::string_view msg;
- std::string_view file;
- int line;
- };
- static constexpr DebugInfoMatcher AnyMatcher(DebugInfoMatcher::any_msg);
- inline DebugInfoMatcher& GlobalMatcher() {
- static DebugInfoMatcher GMatch;
- return GMatch;
- }
- struct DeathTest {
- enum ResultKind {
- RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown
- };
- static const char* ResultKindToString(ResultKind RK) {
- #define CASE(K) case K: return #K
- switch (RK) {
- CASE(RK_MatchFailure);
- CASE(RK_DidNotDie);
- CASE(RK_SetupFailure);
- CASE(RK_MatchFound);
- CASE(RK_Unknown);
- }
- return "not a result kind";
- }
- static bool IsValidResultKind(int val) {
- return val >= RK_DidNotDie && val <= RK_Unknown;
- }
- TEST_NORETURN static void DeathTestDebugHandler(std::__libcpp_debug_info const& info) {
- assert(!GlobalMatcher().empty());
- if (GlobalMatcher().Matches(info)) {
- std::exit(RK_MatchFound);
- }
- std::exit(RK_MatchFailure);
- }
- DeathTest(DebugInfoMatcher const& Matcher) : matcher_(Matcher) {}
- template <class Func>
- ResultKind Run(Func&& f) {
- int pipe_res = pipe(stdout_pipe_fd_);
- assert(pipe_res != -1 && "failed to create pipe");
- pipe_res = pipe(stderr_pipe_fd_);
- assert(pipe_res != -1 && "failed to create pipe");
- pid_t child_pid = fork();
- assert(child_pid != -1 &&
- "failed to fork a process to perform a death test");
- child_pid_ = child_pid;
- if (child_pid_ == 0) {
- RunForChild(std::forward<Func>(f));
- assert(false && "unreachable");
- }
- return RunForParent();
- }
- int getChildExitCode() const { return exit_code_; }
- std::string const& getChildStdOut() const { return stdout_from_child_; }
- std::string const& getChildStdErr() const { return stderr_from_child_; }
- private:
- template <class Func>
- TEST_NORETURN void RunForChild(Func&& f) {
- close(GetStdOutReadFD()); // don't need to read from the pipe in the child.
- close(GetStdErrReadFD());
- auto DupFD = [](int DestFD, int TargetFD) {
- int dup_result = dup2(DestFD, TargetFD);
- if (dup_result == -1)
- std::exit(RK_SetupFailure);
- };
- DupFD(GetStdOutWriteFD(), STDOUT_FILENO);
- DupFD(GetStdErrWriteFD(), STDERR_FILENO);
- GlobalMatcher() = matcher_;
- std::__libcpp_set_debug_function(&DeathTestDebugHandler);
- f();
- std::exit(RK_DidNotDie);
- }
- static std::string ReadChildIOUntilEnd(int FD) {
- std::string error_msg;
- char buffer[256];
- int num_read;
- do {
- while ((num_read = read(FD, buffer, 255)) > 0) {
- buffer[num_read] = '\0';
- error_msg += buffer;
- }
- } while (num_read == -1 && errno == EINTR);
- return error_msg;
- }
- void CaptureIOFromChild() {
- close(GetStdOutWriteFD()); // no need to write from the parent process
- close(GetStdErrWriteFD());
- stdout_from_child_ = ReadChildIOUntilEnd(GetStdOutReadFD());
- stderr_from_child_ = ReadChildIOUntilEnd(GetStdErrReadFD());
- close(GetStdOutReadFD());
- close(GetStdErrReadFD());
- }
- ResultKind RunForParent() {
- CaptureIOFromChild();
- int status_value;
- pid_t result = waitpid(child_pid_, &status_value, 0);
- assert(result != -1 && "there is no child process to wait for");
- if (WIFEXITED(status_value)) {
- exit_code_ = WEXITSTATUS(status_value);
- if (!IsValidResultKind(exit_code_))
- return RK_Unknown;
- return static_cast<ResultKind>(exit_code_);
- }
- return RK_Unknown;
- }
- DeathTest(DeathTest const&) = delete;
- DeathTest& operator=(DeathTest const&) = delete;
- int GetStdOutReadFD() const {
- return stdout_pipe_fd_[0];
- }
- int GetStdOutWriteFD() const {
- return stdout_pipe_fd_[1];
- }
- int GetStdErrReadFD() const {
- return stderr_pipe_fd_[0];
- }
- int GetStdErrWriteFD() const {
- return stderr_pipe_fd_[1];
- }
- private:
- DebugInfoMatcher matcher_;
- pid_t child_pid_ = -1;
- int exit_code_ = -1;
- int stdout_pipe_fd_[2];
- int stderr_pipe_fd_[2];
- std::string stdout_from_child_;
- std::string stderr_from_child_;
- };
- template <class Func>
- inline bool ExpectDeath(const char* stmt, Func&& func, DebugInfoMatcher Matcher) {
- DeathTest DT(Matcher);
- DeathTest::ResultKind RK = DT.Run(func);
- auto OnFailure = [&](const char* msg) {
- std::cerr << "EXPECT_DEATH( " << stmt << " ) failed! (" << msg << ")\n\n";
- if (RK != DeathTest::RK_Unknown) {
- std::cerr << "child exit code: " << DT.getChildExitCode() << "\n";
- }
- if (!DT.getChildStdErr().empty()) {
- std::cerr << "---------- standard err ----------\n";
- std::cerr << DT.getChildStdErr() << "\n";
- }
- if (!DT.getChildStdOut().empty()) {
- std::cerr << "---------- standard out ----------\n";
- std::cerr << DT.getChildStdOut() << "\n";
- }
- return false;
- };
- switch (RK) {
- case DeathTest::RK_MatchFound:
- return true;
- case DeathTest::RK_SetupFailure:
- return OnFailure("child failed to setup test environment");
- case DeathTest::RK_Unknown:
- return OnFailure("reason unknown");
- case DeathTest::RK_DidNotDie:
- return OnFailure("child did not die");
- case DeathTest::RK_MatchFailure:
- return OnFailure("matcher failed");
- }
- }
- template <class Func>
- inline bool ExpectDeath(const char* stmt, Func&& func) {
- return ExpectDeath(stmt, func, AnyMatcher);
- }
- /// Assert that the specified expression throws a libc++ debug exception.
- #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } )))
- #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher)))
- #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H
|