1 /*
2  * Copyright (c) Facebook, Inc. and its affiliates.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <iostream>
18 
19 #include <folly/init/Init.h>
20 #include <folly/logging/xlog.h>
21 #include <folly/portability/Stdlib.h>
22 
23 DEFINE_string(
24     category,
25     "",
26     "Crash with a message to this category instead of the default");
27 DEFINE_bool(crash, true, "Crash with a fatal log message.");
28 DEFINE_bool(
29     check_debug,
30     false,
31     "Print whether this binary was built in debug mode "
32     "and then exit successfully");
33 
34 DEFINE_bool(fail_fatal_xlog_if, false, "Fail an XLOG_IF(FATAL) check.");
35 DEFINE_bool(fail_dfatal_xlog_if, false, "Fail an XLOG_IF(DFATAL) check.");
36 DEFINE_bool(fail_xcheck, false, "Fail an XCHECK() test.");
37 DEFINE_bool(
38     fail_xcheck_nomsg,
39     false,
40     "Fail an XCHECK() test with no additional message.");
41 DEFINE_bool(fail_xdcheck, false, "Fail an XDCHECK() test.");
42 
43 DEFINE_int32(xcheck_eq0, 0, "Check this value using XCHECK_EQ(value, 0)");
44 DEFINE_int32(xcheck_ne0, 1, "Check this value using XCHECK_NE 0)");
45 DEFINE_int32(xcheck_lt0, -1, "Check this value using XCHECK_LT(value, 0)");
46 DEFINE_int32(xcheck_le0, 0, "Check this value using XCHECK_LE(value, 0)");
47 DEFINE_int32(xcheck_gt0, 1, "Check this value using XCHECK_GT(value, 0)");
48 DEFINE_int32(xcheck_ge0, 0, "Check this value using XCHECK_GE(value, 0)");
49 
50 DEFINE_int32(xdcheck_eq0, 0, "Check this value using XDCHECK_EQ(value, 0)");
51 DEFINE_int32(xdcheck_ne0, 1, "Check this value using XDCHECK_NE 0)");
52 DEFINE_int32(xdcheck_lt0, -1, "Check this value using XDCHECK_LT(value, 0)");
53 DEFINE_int32(xdcheck_le0, 0, "Check this value using XDCHECK_LE(value, 0)");
54 DEFINE_int32(xdcheck_gt0, 1, "Check this value using XDCHECK_GT(value, 0)");
55 DEFINE_int32(xdcheck_ge0, 0, "Check this value using XDCHECK_GE(value, 0)");
56 
57 DEFINE_bool(
58     test_xcheck_eq_evalutates_once,
59     false,
60     "Test an XCHECK_EQ() statement where the arguments have side effects");
61 DEFINE_bool(
62     xcheck_eq_custom_struct,
63     false,
64     "Test an XCHECK_EQ() statement with a custom structure, "
65     "to test log message formatting");
66 DEFINE_bool(
67     xcheck_eq_pointers,
68     false,
69     "Test an XCHECK_EQ() statement with pointer arguments");
70 
71 namespace {
72 /**
73  * Helper class to optionally log a fatal message during static initialization
74  * or destruction.
75  *
76  * Since command line arguments have not been processed during static
77  * initialization, we check an environment variable.
78  */
79 class InitChecker {
80  public:
InitChecker()81   InitChecker() : value_{getenv("CRASH_DURING_INIT")} {
82     if (value_ && strcmp(value_, "shutdown") != 0) {
83       XLOG(FATAL) << "crashing during static initialization";
84     }
85   }
~InitChecker()86   ~InitChecker() {
87     if (value_) {
88       XLOG(FATAL) << "crashing during static destruction";
89     }
90   }
91 
92   const char* value_{nullptr};
93 };
94 
95 static InitChecker initChecker;
96 } // namespace
97 
98 namespace {
runHelper()99 int runHelper() {
100   if (!FLAGS_category.empty()) {
101     folly::Logger logger{FLAGS_category};
102     FB_LOG(logger, FATAL, "crashing to category ", FLAGS_category);
103   }
104 
105   if (!FLAGS_crash) {
106     return 0;
107   }
108 
109   XLOG(FATAL) << "test program crashing!";
110   // Even though this function is defined to return an integer, the compiler
111   // should be able to detect that XLOG(FATAL) never returns.  It shouldn't
112   // complain that we don't return an integer here.
113 }
114 } // namespace
115 
fbLogFatalCheck()116 std::string fbLogFatalCheck() {
117   folly::Logger logger("some.category");
118   FB_LOG(logger, FATAL) << "we always crash";
119   // This function mostly exists to make sure the compiler does not warn
120   // about a missing return statement here.
121 }
122 
123 struct MyStruct {
MyStructMyStruct124   MyStruct(uint32_t a_, uint32_t b_) : a(a_), b(b_) {}
125   uint32_t a;
126   uint32_t b;
127 };
operator ==(const MyStruct & s1,const MyStruct & s2)128 bool operator==(const MyStruct& s1, const MyStruct& s2) {
129   return (s1.a == s2.a) && (s1.b == s2.b);
130 }
operator <=(const MyStruct & s1,const MyStruct & s2)131 bool operator<=(const MyStruct& s1, const MyStruct& s2) {
132   return !(s1 == s2);
133 }
134 
135 /*
136  * This is a simple helper program to exercise the LOG(FATAL) functionality.
137  */
main(int argc,char * argv[])138 int main(int argc, char* argv[]) {
139   folly::Init init(&argc, &argv);
140 
141   if (FLAGS_check_debug) {
142     std::cout << "DEBUG=" << static_cast<int>(folly::kIsDebug) << "\n";
143     return 0;
144   }
145 
146   XLOG_IF(FATAL, FLAGS_fail_fatal_xlog_if) << "--fail_fatal_xlog_if specified!";
147   XLOG_IF(DFATAL, FLAGS_fail_dfatal_xlog_if)
148       << "--fail_dfatal_xlog_if specified!";
149   XCHECK(!FLAGS_fail_xcheck) << ": --fail_xcheck specified!";
150   XCHECK(!FLAGS_fail_xcheck_nomsg);
151   XDCHECK(!FLAGS_fail_xdcheck) << ": --fail_xdcheck specified!";
152 
153   XCHECK_EQ(FLAGS_xcheck_eq0, 0) << " extra user args";
154   XCHECK_NE(FLAGS_xcheck_ne0, 0, " extra user args");
155   XCHECK_LT(FLAGS_xcheck_lt0, 0, " extra ", "user", " args");
156   XCHECK_LE(FLAGS_xcheck_le0, 0, " extra ", "user") << " args";
157   XCHECK_GT(FLAGS_xcheck_gt0, 0) << " extra user args";
158   XCHECK_GE(FLAGS_xcheck_ge0, 0) << " extra user args";
159   XDCHECK_EQ(FLAGS_xdcheck_eq0, 0) << " extra user args";
160   XDCHECK_NE(FLAGS_xdcheck_ne0, 0, " extra user args");
161   XDCHECK_LT(FLAGS_xdcheck_lt0, 0) << " extra user args";
162   XDCHECK_LE(FLAGS_xdcheck_le0, 0) << " extra user args";
163   XDCHECK_GT(FLAGS_xdcheck_gt0, 0) << " extra user args";
164   XDCHECK_GE(FLAGS_xdcheck_ge0, 0) << " extra user args";
165 
166   if (FLAGS_test_xcheck_eq_evalutates_once) {
167     // Make sure XCHECK_EQ() only evaluates "++x" once,
168     // and logs that it equals 6 and not 7.
169     int x = 5;
170     XCHECK_EQ(++x, 7);
171   }
172   if (FLAGS_xcheck_eq_custom_struct) {
173     auto m = MyStruct(1, 0x12abcdef);
174     XCHECK_EQ(MyStruct(1, 2), m);
175   }
176   if (FLAGS_xcheck_eq_pointers) {
177     int localInt = 5;
178     XCHECK_EQ(&argc, &localInt);
179   }
180 
181   // Do the remainder of the work in a separate helper function.
182   //
183   // The main reason for putting this in a helper function is to ensure that
184   // the compiler does not warn about missing return statements on XLOG(FATAL)
185   // code paths.  Unfortunately it appears like some compilers always suppress
186   // this warning for main().
187   return runHelper();
188 }
189