Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 1 | //===----------------------------------------------------------------------===// |
| 2 | // |
Chandler Carruth | 57b08b0 | 2019-01-19 10:56:40 +0000 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef TEST_SUPPORT_DEBUG_MODE_HELPER_H |
| 10 | #define TEST_SUPPORT_DEBUG_MODE_HELPER_H |
| 11 | |
| 12 | #ifndef _LIBCPP_DEBUG |
| 13 | #error _LIBCPP_DEBUG must be defined before including this header |
| 14 | #endif |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 15 | |
| 16 | #include <ciso646> |
| 17 | #ifndef _LIBCPP_VERSION |
Eric Fiselier | cc703de | 2019-03-18 22:12:09 +0000 | [diff] [blame] | 18 | #error "This header may only be used for libc++ tests" |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 19 | #endif |
| 20 | |
| 21 | #include <__debug> |
| 22 | #include <utility> |
| 23 | #include <cstddef> |
| 24 | #include <cstdlib> |
| 25 | #include <cassert> |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 26 | #include <string_view> |
| 27 | #include <sstream> |
| 28 | #include <iostream> |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 29 | |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 30 | #include <unistd.h> |
| 31 | #include <sys/wait.h> |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 32 | #include "test_macros.h" |
| 33 | #include "assert_checkpoint.h" |
| 34 | #include "test_allocator.h" |
| 35 | |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 36 | #if TEST_STD_VER < 11 |
| 37 | # error "C++11 or greater is required to use this header" |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 38 | #endif |
| 39 | |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 40 | struct DebugInfoMatcher { |
| 41 | static const int any_line = -1; |
| 42 | static constexpr const char* any_file = "*"; |
| 43 | static constexpr const char* any_msg = "*"; |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 44 | |
Eric Fiselier | cc703de | 2019-03-18 22:12:09 +0000 | [diff] [blame] | 45 | constexpr DebugInfoMatcher() : is_empty(true), msg(any_msg, __builtin_strlen(any_msg)), file(any_file, __builtin_strlen(any_file)), line(any_line) { } |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 46 | constexpr DebugInfoMatcher(const char* msg, const char* file = any_file, int line = any_line) |
Eric Fiselier | cc703de | 2019-03-18 22:12:09 +0000 | [diff] [blame] | 47 | : is_empty(false), msg(msg, __builtin_strlen(msg)), file(file, __builtin_strlen(file)), line(line) {} |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 48 | |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 49 | bool Matches(std::__libcpp_debug_info const& got) const { |
| 50 | assert(!empty() && "empty matcher"); |
| 51 | |
| 52 | if (CheckLineMatches(got.__line_) && CheckFileMatches(got.__file_) && |
| 53 | CheckMessageMatches(got.__msg_)) |
| 54 | return true; |
| 55 | // Write to stdout because that's the file descriptor captured by the parent |
| 56 | // process. |
| 57 | std::cout << "Failed to match debug info!\n" |
| 58 | << ToString() << "\n" |
| 59 | << "VS\n" |
| 60 | << got.what() << "\n"; |
| 61 | return false; |
| 62 | } |
| 63 | |
| 64 | std::string ToString() const { |
| 65 | std::stringstream ss; |
| 66 | ss << "msg = \"" << msg << "\"\n" |
| 67 | << "line = " << (line == any_line ? "'*'" : std::to_string(line)) << "\n" |
| 68 | << "file = " << (file == any_file ? "'*'" : any_file) << ""; |
| 69 | return ss.str(); |
| 70 | } |
| 71 | |
| 72 | bool empty() const { return is_empty; } |
| 73 | private: |
| 74 | bool CheckLineMatches(int got_line) const { |
| 75 | if (line == any_line) |
| 76 | return true; |
| 77 | return got_line == line; |
| 78 | } |
| 79 | |
| 80 | bool CheckFileMatches(std::string_view got_file) const { |
| 81 | assert(!empty() && "empty matcher"); |
| 82 | if (file == any_file) |
| 83 | return true; |
| 84 | std::size_t found_at = got_file.find(file); |
| 85 | if (found_at == std::string_view::npos) |
| 86 | return false; |
| 87 | // require the match start at the beginning of the file or immediately after |
| 88 | // a directory separator. |
| 89 | if (found_at != 0) { |
| 90 | char last_char = got_file[found_at - 1]; |
| 91 | if (last_char != '/' && last_char != '\\') |
| 92 | return false; |
| 93 | } |
| 94 | // require the match goes until the end of the string. |
| 95 | return got_file.substr(found_at) == file; |
| 96 | } |
| 97 | |
| 98 | bool CheckMessageMatches(std::string_view got_msg) const { |
| 99 | assert(!empty() && "empty matcher"); |
| 100 | if (msg == any_msg) |
| 101 | return true; |
| 102 | std::size_t found_at = got_msg.find(msg); |
| 103 | if (found_at == std::string_view::npos) |
| 104 | return false; |
| 105 | // Allow any match |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 106 | return true; |
| 107 | } |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 108 | private: |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 109 | bool is_empty; |
| 110 | std::string_view msg; |
| 111 | std::string_view file; |
| 112 | int line; |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 113 | }; |
| 114 | |
Eric Fiselier | 61b302f | 2019-03-18 21:50:12 +0000 | [diff] [blame] | 115 | static constexpr DebugInfoMatcher AnyMatcher(DebugInfoMatcher::any_msg); |
| 116 | |
| 117 | inline DebugInfoMatcher& GlobalMatcher() { |
| 118 | static DebugInfoMatcher GMatch; |
| 119 | return GMatch; |
| 120 | } |
| 121 | |
| 122 | struct DeathTest { |
| 123 | enum ResultKind { |
| 124 | RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown |
| 125 | }; |
| 126 | |
| 127 | static const char* ResultKindToString(ResultKind RK) { |
| 128 | #define CASE(K) case K: return #K |
| 129 | switch (RK) { |
| 130 | CASE(RK_MatchFailure); |
| 131 | CASE(RK_DidNotDie); |
| 132 | CASE(RK_SetupFailure); |
| 133 | CASE(RK_MatchFound); |
| 134 | CASE(RK_Unknown); |
| 135 | } |
| 136 | return "not a result kind"; |
| 137 | } |
| 138 | |
| 139 | static bool IsValidResultKind(int val) { |
| 140 | return val >= RK_DidNotDie && val <= RK_Unknown; |
| 141 | } |
| 142 | |
| 143 | TEST_NORETURN static void DeathTestDebugHandler(std::__libcpp_debug_info const& info) { |
| 144 | assert(!GlobalMatcher().empty()); |
| 145 | if (GlobalMatcher().Matches(info)) { |
| 146 | std::exit(RK_MatchFound); |
| 147 | } |
| 148 | std::exit(RK_MatchFailure); |
| 149 | } |
| 150 | |
| 151 | |
| 152 | DeathTest(DebugInfoMatcher const& Matcher) : matcher_(Matcher) {} |
| 153 | |
| 154 | template <class Func> |
| 155 | ResultKind Run(Func&& f) { |
| 156 | int pipe_res = pipe(stdout_pipe_fd_); |
| 157 | assert(pipe_res != -1 && "failed to create pipe"); |
| 158 | pipe_res = pipe(stderr_pipe_fd_); |
| 159 | assert(pipe_res != -1 && "failed to create pipe"); |
| 160 | pid_t child_pid = fork(); |
| 161 | assert(child_pid != -1 && |
| 162 | "failed to fork a process to perform a death test"); |
| 163 | child_pid_ = child_pid; |
| 164 | if (child_pid_ == 0) { |
| 165 | RunForChild(std::forward<Func>(f)); |
| 166 | assert(false && "unreachable"); |
| 167 | } |
| 168 | return RunForParent(); |
| 169 | } |
| 170 | |
| 171 | int getChildExitCode() const { return exit_code_; } |
| 172 | std::string const& getChildStdOut() const { return stdout_from_child_; } |
| 173 | std::string const& getChildStdErr() const { return stderr_from_child_; } |
| 174 | private: |
| 175 | template <class Func> |
| 176 | TEST_NORETURN void RunForChild(Func&& f) { |
| 177 | close(GetStdOutReadFD()); // don't need to read from the pipe in the child. |
| 178 | close(GetStdErrReadFD()); |
| 179 | auto DupFD = [](int DestFD, int TargetFD) { |
| 180 | int dup_result = dup2(DestFD, TargetFD); |
| 181 | if (dup_result == -1) |
| 182 | std::exit(RK_SetupFailure); |
| 183 | }; |
| 184 | DupFD(GetStdOutWriteFD(), STDOUT_FILENO); |
| 185 | DupFD(GetStdErrWriteFD(), STDERR_FILENO); |
| 186 | |
| 187 | GlobalMatcher() = matcher_; |
| 188 | std::__libcpp_set_debug_function(&DeathTestDebugHandler); |
| 189 | f(); |
| 190 | std::exit(RK_DidNotDie); |
| 191 | } |
| 192 | |
| 193 | static std::string ReadChildIOUntilEnd(int FD) { |
| 194 | std::string error_msg; |
| 195 | char buffer[256]; |
| 196 | int num_read; |
| 197 | do { |
| 198 | while ((num_read = read(FD, buffer, 255)) > 0) { |
| 199 | buffer[num_read] = '\0'; |
| 200 | error_msg += buffer; |
| 201 | } |
| 202 | } while (num_read == -1 && errno == EINTR); |
| 203 | return error_msg; |
| 204 | } |
| 205 | |
| 206 | void CaptureIOFromChild() { |
| 207 | close(GetStdOutWriteFD()); // no need to write from the parent process |
| 208 | close(GetStdErrWriteFD()); |
| 209 | stdout_from_child_ = ReadChildIOUntilEnd(GetStdOutReadFD()); |
| 210 | stderr_from_child_ = ReadChildIOUntilEnd(GetStdErrReadFD()); |
| 211 | close(GetStdOutReadFD()); |
| 212 | close(GetStdErrReadFD()); |
| 213 | } |
| 214 | |
| 215 | ResultKind RunForParent() { |
| 216 | CaptureIOFromChild(); |
| 217 | |
| 218 | int status_value; |
| 219 | pid_t result = waitpid(child_pid_, &status_value, 0); |
| 220 | assert(result != -1 && "there is no child process to wait for"); |
| 221 | |
| 222 | if (WIFEXITED(status_value)) { |
| 223 | exit_code_ = WEXITSTATUS(status_value); |
| 224 | if (!IsValidResultKind(exit_code_)) |
| 225 | return RK_Unknown; |
| 226 | return static_cast<ResultKind>(exit_code_); |
| 227 | } |
| 228 | return RK_Unknown; |
| 229 | } |
| 230 | |
| 231 | DeathTest(DeathTest const&) = delete; |
| 232 | DeathTest& operator=(DeathTest const&) = delete; |
| 233 | |
| 234 | int GetStdOutReadFD() const { |
| 235 | return stdout_pipe_fd_[0]; |
| 236 | } |
| 237 | |
| 238 | int GetStdOutWriteFD() const { |
| 239 | return stdout_pipe_fd_[1]; |
| 240 | } |
| 241 | |
| 242 | int GetStdErrReadFD() const { |
| 243 | return stderr_pipe_fd_[0]; |
| 244 | } |
| 245 | |
| 246 | int GetStdErrWriteFD() const { |
| 247 | return stderr_pipe_fd_[1]; |
| 248 | } |
| 249 | private: |
| 250 | DebugInfoMatcher matcher_; |
| 251 | pid_t child_pid_ = -1; |
| 252 | int exit_code_ = -1; |
| 253 | int stdout_pipe_fd_[2]; |
| 254 | int stderr_pipe_fd_[2]; |
| 255 | std::string stdout_from_child_; |
| 256 | std::string stderr_from_child_; |
| 257 | }; |
| 258 | |
| 259 | template <class Func> |
| 260 | inline bool ExpectDeath(const char* stmt, Func&& func, DebugInfoMatcher Matcher) { |
| 261 | DeathTest DT(Matcher); |
| 262 | DeathTest::ResultKind RK = DT.Run(func); |
| 263 | auto OnFailure = [&](const char* msg) { |
| 264 | std::cerr << "EXPECT_DEATH( " << stmt << " ) failed! (" << msg << ")\n\n"; |
| 265 | if (RK != DeathTest::RK_Unknown) { |
| 266 | std::cerr << "child exit code: " << DT.getChildExitCode() << "\n"; |
| 267 | } |
| 268 | if (!DT.getChildStdErr().empty()) { |
| 269 | std::cerr << "---------- standard err ----------\n"; |
| 270 | std::cerr << DT.getChildStdErr() << "\n"; |
| 271 | } |
| 272 | if (!DT.getChildStdOut().empty()) { |
| 273 | std::cerr << "---------- standard out ----------\n"; |
| 274 | std::cerr << DT.getChildStdOut() << "\n"; |
| 275 | } |
| 276 | return false; |
| 277 | }; |
| 278 | switch (RK) { |
| 279 | case DeathTest::RK_MatchFound: |
| 280 | return true; |
| 281 | case DeathTest::RK_SetupFailure: |
| 282 | return OnFailure("child failed to setup test environment"); |
| 283 | case DeathTest::RK_Unknown: |
| 284 | return OnFailure("reason unknown"); |
| 285 | case DeathTest::RK_DidNotDie: |
| 286 | return OnFailure("child did not die"); |
| 287 | case DeathTest::RK_MatchFailure: |
| 288 | return OnFailure("matcher failed"); |
| 289 | } |
| 290 | } |
| 291 | |
| 292 | template <class Func> |
| 293 | inline bool ExpectDeath(const char* stmt, Func&& func) { |
| 294 | return ExpectDeath(stmt, func, AnyMatcher); |
| 295 | } |
| 296 | |
| 297 | /// Assert that the specified expression throws a libc++ debug exception. |
| 298 | #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } ))) |
| 299 | |
| 300 | #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher))) |
Eric Fiselier | 780b51d | 2016-12-28 05:53:01 +0000 | [diff] [blame] | 301 | |
| 302 | #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H |