1 //===----------------------------------------------------------------------===//
2 //
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
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
15 
16 #include <ciso646>
17 #ifndef _LIBCPP_VERSION
18 #error "This header may only be used for libc++ tests"
19 #endif
20 
21 #include <__debug>
22 #include <cassert>
23 #include <cstddef>
24 #include <cstdio>
25 #include <cstdlib>
26 #include <string>
27 #include <string_view>
28 #include <utility>
29 
30 #include <unistd.h>
31 #include <errno.h>
32 #include <sys/wait.h>
33 #include "test_macros.h"
34 #include "test_allocator.h"
35 
36 #if TEST_STD_VER < 11
37 # error "C++11 or greater is required to use this header"
38 #endif
39 
40 struct DebugInfoMatcher {
41   static const int any_line = -1;
42   static constexpr const char* any_file = "*";
43   static constexpr const char* any_msg = "*";
44 
DebugInfoMatcherDebugInfoMatcher45   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)
is_emptyDebugInfoMatcher47     : is_empty(false), msg(msg_, __builtin_strlen(msg_)), file(file_, __builtin_strlen(file_)), line(line_) {}
48 
MatchesDebugInfoMatcher49   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::printf("Failed to match debug info!\n%s\nVS\n%s\n", ToString().data(), got.what().data());
58     return false;
59   }
60 
ToStringDebugInfoMatcher61   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));
65     return result;
66   }
67 
emptyDebugInfoMatcher68   bool empty() const { return is_empty; }
69 private:
CheckLineMatchesDebugInfoMatcher70   bool CheckLineMatches(int got_line) const {
71     if (line == any_line)
72       return true;
73     return got_line == line;
74   }
75 
CheckFileMatchesDebugInfoMatcher76   bool CheckFileMatches(std::string_view got_file) const {
77     assert(!empty() && "empty matcher");
78     if (file == any_file)
79       return true;
80     std::size_t found_at = got_file.find(file);
81     if (found_at == std::string_view::npos)
82       return false;
83     // require the match start at the beginning of the file or immediately after
84     // a directory separator.
85     if (found_at != 0) {
86       char last_char = got_file[found_at - 1];
87       if (last_char != '/' && last_char != '\\')
88         return false;
89     }
90     // require the match goes until the end of the string.
91     return got_file.substr(found_at) == file;
92   }
93 
CheckMessageMatchesDebugInfoMatcher94   bool CheckMessageMatches(std::string_view got_msg) const {
95     assert(!empty() && "empty matcher");
96     if (msg == any_msg)
97       return true;
98     std::size_t found_at = got_msg.find(msg);
99     if (found_at == std::string_view::npos)
100       return false;
101     // Allow any match
102     return true;
103   }
104 private:
105   bool is_empty;
106   std::string_view msg;
107   std::string_view file;
108   int line;
109 };
110 
111 static constexpr DebugInfoMatcher AnyMatcher(DebugInfoMatcher::any_msg);
112 
GlobalMatcher()113 inline DebugInfoMatcher& GlobalMatcher() {
114   static DebugInfoMatcher GMatch;
115   return GMatch;
116 }
117 
118 struct DeathTest {
119   enum ResultKind {
120     RK_DidNotDie, RK_MatchFound, RK_MatchFailure, RK_SetupFailure, RK_Unknown
121   };
122 
ResultKindToStringDeathTest123   static const char* ResultKindToString(ResultKind RK) {
124 #define CASE(K) case K: return #K
125     switch (RK) {
126     CASE(RK_MatchFailure);
127     CASE(RK_DidNotDie);
128     CASE(RK_SetupFailure);
129     CASE(RK_MatchFound);
130     CASE(RK_Unknown);
131     }
132     return "not a result kind";
133   }
134 
IsValidResultKindDeathTest135   static bool IsValidResultKind(int val) {
136     return val >= RK_DidNotDie && val <= RK_Unknown;
137   }
138 
DeathTestDebugHandlerDeathTest139   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);
143     }
144     std::exit(RK_MatchFailure);
145   }
146 
147 
DeathTestDeathTest148   DeathTest(DebugInfoMatcher const& Matcher) : matcher_(Matcher) {}
149 
150   template <class Func>
RunDeathTest151   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");
163     }
164     return RunForParent();
165   }
166 
getChildExitCodeDeathTest167   int getChildExitCode() const { return exit_code_; }
getChildStdOutDeathTest168   std::string const& getChildStdOut() const { return stdout_from_child_; }
getChildStdErrDeathTest169   std::string const& getChildStdErr() const { return stderr_from_child_; }
170 private:
171   template <class Func>
RunForChildDeathTest172   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);
179     };
180     DupFD(GetStdOutWriteFD(), STDOUT_FILENO);
181     DupFD(GetStdErrWriteFD(), STDERR_FILENO);
182 
183     GlobalMatcher() = matcher_;
184     std::__libcpp_set_debug_function(&DeathTestDebugHandler);
185     f();
186     std::exit(RK_DidNotDie);
187   }
188 
ReadChildIOUntilEndDeathTest189   static std::string ReadChildIOUntilEnd(int FD) {
190     std::string error_msg;
191     char buffer[256];
192     int num_read;
193     do {
194       while ((num_read = read(FD, buffer, 255)) > 0) {
195         buffer[num_read] = '\0';
196         error_msg += buffer;
197       }
198     } while (num_read == -1 && errno == EINTR);
199     return error_msg;
200   }
201 
CaptureIOFromChildDeathTest202   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());
209   }
210 
RunForParentDeathTest211   ResultKind RunForParent() {
212     CaptureIOFromChild();
213 
214     int status_value;
215     pid_t result = waitpid(child_pid_, &status_value, 0);
216     assert(result != -1 && "there is no child process to wait for");
217 
218     if (WIFEXITED(status_value)) {
219       exit_code_ = WEXITSTATUS(status_value);
220       if (!IsValidResultKind(exit_code_))
221         return RK_Unknown;
222       return static_cast<ResultKind>(exit_code_);
223     }
224     return RK_Unknown;
225   }
226 
227   DeathTest(DeathTest const&) = delete;
228   DeathTest& operator=(DeathTest const&) = delete;
229 
GetStdOutReadFDDeathTest230   int GetStdOutReadFD() const {
231     return stdout_pipe_fd_[0];
232   }
233 
GetStdOutWriteFDDeathTest234   int GetStdOutWriteFD() const {
235     return stdout_pipe_fd_[1];
236   }
237 
GetStdErrReadFDDeathTest238   int GetStdErrReadFD() const {
239     return stderr_pipe_fd_[0];
240   }
241 
GetStdErrWriteFDDeathTest242   int GetStdErrWriteFD() const {
243     return stderr_pipe_fd_[1];
244   }
245 private:
246   DebugInfoMatcher matcher_;
247   pid_t child_pid_ = -1;
248   int exit_code_ = -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_;
253 };
254 
255 template <class Func>
ExpectDeath(const char * stmt,Func && func,DebugInfoMatcher Matcher)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());
263     }
264     if (!DT.getChildStdErr().empty()) {
265       std::fprintf(stderr, "---------- standard err ----------\n%s\n", DT.getChildStdErr().c_str());
266     }
267     if (!DT.getChildStdOut().empty()) {
268       std::fprintf(stderr, "---------- standard out ----------\n%s\n", DT.getChildStdOut().c_str());
269     }
270     return false;
271   };
272   switch (RK) {
273   case DeathTest::RK_MatchFound:
274     return true;
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");
283   }
284   assert(false && "unreachable");
285 }
286 
287 template <class Func>
ExpectDeath(const char * stmt,Func && func)288 inline bool ExpectDeath(const char* stmt, Func&& func) {
289   return ExpectDeath(stmt, func, AnyMatcher);
290 }
291 
292 /// Assert that the specified expression throws a libc++ debug exception.
293 #define EXPECT_DEATH(...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; } )))
294 
295 #define EXPECT_DEATH_MATCHES(Matcher, ...) assert((ExpectDeath(#__VA_ARGS__, [&]() { __VA_ARGS__; }, Matcher)))
296 
297 #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H
298