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 <utility>
23 #include <cstddef>
24 #include <cstdlib>
25 #include <cassert>
26 #include <string_view>
27 #include <sstream>
28 #include <iostream>
29 
30 #include <unistd.h>
31 #include <sys/wait.h>
32 #include "test_macros.h"
33 #include "assert_checkpoint.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::cout << "Failed to match debug info!\n"
58               << ToString() << "\n"
59               << "VS\n"
60               << got.what() << "\n";
61       return false;
62     }
63 
ToStringDebugInfoMatcher64   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 
emptyDebugInfoMatcher72   bool empty() const { return is_empty; }
73 private:
CheckLineMatchesDebugInfoMatcher74   bool CheckLineMatches(int got_line) const {
75     if (line == any_line)
76       return true;
77     return got_line == line;
78   }
79 
CheckFileMatchesDebugInfoMatcher80   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 
CheckMessageMatchesDebugInfoMatcher98   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
106     return true;
107   }
108 private:
109   bool is_empty;
110   std::string_view msg;
111   std::string_view file;
112   int line;
113 };
114 
115 static constexpr DebugInfoMatcher AnyMatcher(DebugInfoMatcher::any_msg);
116 
GlobalMatcher()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 
ResultKindToStringDeathTest127   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 
IsValidResultKindDeathTest139   static bool IsValidResultKind(int val) {
140     return val >= RK_DidNotDie && val <= RK_Unknown;
141   }
142 
DeathTestDebugHandlerDeathTest143   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 
DeathTestDeathTest152   DeathTest(DebugInfoMatcher const& Matcher) : matcher_(Matcher) {}
153 
154   template <class Func>
RunDeathTest155   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 
getChildExitCodeDeathTest171   int getChildExitCode() const { return exit_code_; }
getChildStdOutDeathTest172   std::string const& getChildStdOut() const { return stdout_from_child_; }
getChildStdErrDeathTest173   std::string const& getChildStdErr() const { return stderr_from_child_; }
174 private:
175   template <class Func>
RunForChildDeathTest176   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 
ReadChildIOUntilEndDeathTest193   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 
CaptureIOFromChildDeathTest206   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 
RunForParentDeathTest215   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 
GetStdOutReadFDDeathTest234   int GetStdOutReadFD() const {
235     return stdout_pipe_fd_[0];
236   }
237 
GetStdOutWriteFDDeathTest238   int GetStdOutWriteFD() const {
239     return stdout_pipe_fd_[1];
240   }
241 
GetStdErrReadFDDeathTest242   int GetStdErrReadFD() const {
243     return stderr_pipe_fd_[0];
244   }
245 
GetStdErrWriteFDDeathTest246   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>
ExpectDeath(const char * stmt,Func && func,DebugInfoMatcher Matcher)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>
ExpectDeath(const char * stmt,Func && 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)))
301 
302 #endif // TEST_SUPPORT_DEBUG_MODE_HELPER_H
303