1 //===----------------------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #ifndef TEST_SUPPORT_DEBUG_MODE_HELPER_H
10 #define TEST_SUPPORT_DEBUG_MODE_HELPER_H
13 #error _LIBCPP_DEBUG must be defined before including this header
17 #ifndef _LIBCPP_VERSION
18 #error "This header may only be used for libc++ tests"
27 #include <string_view>
33 #include "test_macros.h"
34 #include "test_allocator.h"
37 # error "C++11 or greater is required to use this header"
40 struct DebugInfoMatcher
{
41 static const int any_line
= -1;
42 static constexpr const char* any_file
= "*";
43 static constexpr const char* any_msg
= "*";
45 constexpr DebugInfoMatcher() : is_empty(true), msg(any_msg
, __builtin_strlen(any_msg
)), file(any_file
, __builtin_strlen(any_file
)), line(any_line
) { }
46 constexpr DebugInfoMatcher(const char* msg_
, const char* file_
= any_file
, int line_
= any_line
)
47 : is_empty(false), msg(msg_
, __builtin_strlen(msg_
)), file(file_
, __builtin_strlen(file_
)), line(line_
) {}
49 bool Matches(std::__libcpp_debug_info
const& got
) const {
50 assert(!empty() && "empty matcher");
52 if (CheckLineMatches(got
.__line_
) && CheckFileMatches(got
.__file_
) &&
53 CheckMessageMatches(got
.__msg_
))
55 // Write to stdout because that's the file descriptor captured by the parent
57 std::printf("Failed to match debug info!\n%s\nVS\n%s\n", ToString().data(), got
.what().data());
61 std::string
ToString() const {
62 std::string result
= "msg = \""; result
+= msg
; result
+= "\"\n";
63 result
+= "line = " + (line
== any_line
? "'*'" : std::to_string(line
)) + "\n";
64 result
+= "file = " + (file
== any_file
? "'*'" : std::string(any_file
));
68 bool empty() const { return is_empty
; }
70 bool CheckLineMatches(int got_line
) const {
73 return got_line
== line
;
76 bool CheckFileMatches(std::string_view got_file
) const {
77 assert(!empty() && "empty matcher");
80 std::size_t found_at
= got_file
.find(file
);
81 if (found_at
== std::string_view::npos
)
83 // require the match start at the beginning of the file or immediately after
84 // a directory separator.
86 char last_char
= got_file
[found_at
- 1];
87 if (last_char
!= '/' && last_char
!= '\\')
90 // require the match goes until the end of the string.
91 return got_file
.substr(found_at
) == file
;
94 bool CheckMessageMatches(std::string_view got_msg
) const {
95 assert(!empty() && "empty matcher");
98 std::size_t found_at
= got_msg
.find(msg
);
99 if (found_at
== std::string_view::npos
)
106 std::string_view msg
;
107 std::string_view file
;
111 static constexpr DebugInfoMatcher
AnyMatcher(DebugInfoMatcher::any_msg
);
113 inline DebugInfoMatcher
& GlobalMatcher() {
114 static DebugInfoMatcher GMatch
;
120 RK_DidNotDie
, RK_MatchFound
, RK_MatchFailure
, RK_SetupFailure
, RK_Unknown
123 static const char* ResultKindToString(ResultKind RK
) {
124 #define CASE(K) case K: return #K
126 CASE(RK_MatchFailure
);
128 CASE(RK_SetupFailure
);
132 return "not a result kind";
135 static bool IsValidResultKind(int val
) {
136 return val
>= RK_DidNotDie
&& val
<= RK_Unknown
;
139 TEST_NORETURN
static void DeathTestDebugHandler(std::__libcpp_debug_info
const& info
) {
140 assert(!GlobalMatcher().empty());
141 if (GlobalMatcher().Matches(info
)) {
142 std::exit(RK_MatchFound
);
144 std::exit(RK_MatchFailure
);
148 DeathTest(DebugInfoMatcher
const& Matcher
) : matcher_(Matcher
) {}
150 template <class Func
>
151 ResultKind
Run(Func
&& f
) {
152 int pipe_res
= pipe(stdout_pipe_fd_
);
153 assert(pipe_res
!= -1 && "failed to create pipe");
154 pipe_res
= pipe(stderr_pipe_fd_
);
155 assert(pipe_res
!= -1 && "failed to create pipe");
156 pid_t child_pid
= fork();
157 assert(child_pid
!= -1 &&
158 "failed to fork a process to perform a death test");
159 child_pid_
= child_pid
;
160 if (child_pid_
== 0) {
161 RunForChild(std::forward
<Func
>(f
));
162 assert(false && "unreachable");
164 return RunForParent();
167 int getChildExitCode() const { return exit_code_
; }
168 std::string
const& getChildStdOut() const { return stdout_from_child_
; }
169 std::string
const& getChildStdErr() const { return stderr_from_child_
; }
171 template <class Func
>
172 TEST_NORETURN
void RunForChild(Func
&& f
) {
173 close(GetStdOutReadFD()); // don't need to read from the pipe in the child.
174 close(GetStdErrReadFD());
175 auto DupFD
= [](int DestFD
, int TargetFD
) {
176 int dup_result
= dup2(DestFD
, TargetFD
);
177 if (dup_result
== -1)
178 std::exit(RK_SetupFailure
);
180 DupFD(GetStdOutWriteFD(), STDOUT_FILENO
);
181 DupFD(GetStdErrWriteFD(), STDERR_FILENO
);
183 GlobalMatcher() = matcher_
;
184 std::__libcpp_set_debug_function(&DeathTestDebugHandler
);
186 std::exit(RK_DidNotDie
);
189 static std::string
ReadChildIOUntilEnd(int FD
) {
190 std::string error_msg
;
194 while ((num_read
= read(FD
, buffer
, 255)) > 0) {
195 buffer
[num_read
] = '\0';
198 } while (num_read
== -1 && errno
== EINTR
);
202 void CaptureIOFromChild() {
203 close(GetStdOutWriteFD()); // no need to write from the parent process
204 close(GetStdErrWriteFD());
205 stdout_from_child_
= ReadChildIOUntilEnd(GetStdOutReadFD());
206 stderr_from_child_
= ReadChildIOUntilEnd(GetStdErrReadFD());
207 close(GetStdOutReadFD());
208 close(GetStdErrReadFD());
211 ResultKind
RunForParent() {
212 CaptureIOFromChild();
215 pid_t result
= waitpid(child_pid_
, &status_value
, 0);
216 assert(result
!= -1 && "there is no child process to wait for");
218 if (WIFEXITED(status_value
)) {
219 exit_code_
= WEXITSTATUS(status_value
);
220 if (!IsValidResultKind(exit_code_
))
222 return static_cast<ResultKind
>(exit_code_
);
227 DeathTest(DeathTest
const&) = delete;
228 DeathTest
& operator=(DeathTest
const&) = delete;
230 int GetStdOutReadFD() const {
231 return stdout_pipe_fd_
[0];
234 int GetStdOutWriteFD() const {
235 return stdout_pipe_fd_
[1];
238 int GetStdErrReadFD() const {
239 return stderr_pipe_fd_
[0];
242 int GetStdErrWriteFD() const {
243 return stderr_pipe_fd_
[1];
246 DebugInfoMatcher matcher_
;
247 pid_t child_pid_
= -1;
249 int stdout_pipe_fd_
[2];
250 int stderr_pipe_fd_
[2];
251 std::string stdout_from_child_
;
252 std::string stderr_from_child_
;
255 template <class Func
>
256 inline bool ExpectDeath(const char* stmt
, Func
&& func
, DebugInfoMatcher Matcher
) {
257 DeathTest
DT(Matcher
);
258 DeathTest::ResultKind RK
= DT
.Run(func
);
259 auto OnFailure
= [&](const char* msg
) {
260 std::fprintf(stderr
, "EXPECT_DEATH( %s ) failed! (%s)\n\n", stmt
, msg
);
261 if (RK
!= DeathTest::RK_Unknown
) {
262 std::fprintf(stderr
, "child exit code: %d\n", DT
.getChildExitCode());
264 if (!DT
.getChildStdErr().empty()) {
265 std::fprintf(stderr
, "---------- standard err ----------\n%s\n", DT
.getChildStdErr().c_str());
267 if (!DT
.getChildStdOut().empty()) {
268 std::fprintf(stderr
, "---------- standard out ----------\n%s\n", DT
.getChildStdOut().c_str());
273 case DeathTest::RK_MatchFound
:
275 case DeathTest::RK_SetupFailure
:
276 return OnFailure("child failed to setup test environment");
277 case DeathTest::RK_Unknown
:
278 return OnFailure("reason unknown");
279 case DeathTest::RK_DidNotDie
:
280 return OnFailure("child did not die");
281 case DeathTest::RK_MatchFailure
:
282 return OnFailure("matcher failed");
284 assert(false && "unreachable");
287 template <class Func
>
288 inline bool ExpectDeath(const char* stmt
, Func
&& func
) {
289 return ExpectDeath(stmt
, func
, AnyMatcher
);
292 /// Assert that the specified expression throws a libc++ debug exception.
293 #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } )))
295 #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher)))
297 #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H