1 // 2 // Copyright (C) 2022 The Android Open Source Project 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 #pragma once 17 18 #include <optional> 19 #include <ostream> 20 #include <sstream> 21 #include <string> 22 #include <type_traits> 23 #include <utility> 24 #include <vector> 25 26 #include <android-base/format.h> // IWYU pragma: export 27 #include <android-base/logging.h> 28 #include <android-base/result.h> // IWYU pragma: export 29 30 #if FMT_VERSION < 80000 31 namespace fmt { 32 // fmt::runtime was added in v8.0.0 33 template<typename T> runtime(const T & param)34 const T& runtime(const T& param) { 35 return param; 36 } 37 } 38 #endif 39 40 namespace cuttlefish { 41 42 class StackTraceError; 43 44 class StackTraceEntry { 45 public: 46 enum class FormatSpecifier : char { 47 /** Prefix multi-line output with an arrow. */ 48 kArrow = 'a', 49 /** Use colors in all other output specifiers. */ 50 kColor = 'c', 51 /** The function name without namespace or arguments. */ 52 kFunction = 'f', 53 /** The CF_EXPECT(exp) expression. */ 54 kLongExpression = 'E', 55 /** The source file path relative to ANDROID_BUILD_TOP and line number. */ 56 kLongLocation = 'L', 57 /** The user-friendly string provided to CF_EXPECT. */ 58 kMessage = 'm', 59 /** Prefix output with the stack frame index. */ 60 kNumbers = 'n', 61 /** The function signature with fully-qualified types. */ 62 kPrettyFunction = 'F', 63 /** The short location and short filename. */ 64 kShort = 's', 65 /** The `exp` inside `CF_EXPECT(exp)` */ 66 kShortExpression = 'e', 67 /** The source file basename and line number. */ 68 kShortLocation = 'l', 69 }; 70 static constexpr auto kVerbose = { 71 FormatSpecifier::kArrow, 72 FormatSpecifier::kColor, 73 FormatSpecifier::kNumbers, 74 FormatSpecifier::kShort, 75 }; 76 static constexpr auto kVeryVerbose = { 77 FormatSpecifier::kArrow, FormatSpecifier::kColor, 78 FormatSpecifier::kNumbers, FormatSpecifier::kLongLocation, 79 FormatSpecifier::kPrettyFunction, FormatSpecifier::kLongExpression, 80 FormatSpecifier::kMessage, 81 }; 82 83 StackTraceEntry(std::string file, size_t line, std::string pretty_function, 84 std::string function); 85 86 StackTraceEntry(std::string file, size_t line, std::string pretty_function, 87 std::string function, std::string expression); 88 89 StackTraceEntry(const StackTraceEntry& other); 90 91 StackTraceEntry(StackTraceEntry&&) = default; 92 StackTraceEntry& operator=(const StackTraceEntry& other); 93 StackTraceEntry& operator=(StackTraceEntry&&) = default; 94 95 template <typename T> 96 StackTraceEntry& operator<<(T&& message_ext) & { 97 message_ << std::forward<T>(message_ext); 98 return *this; 99 } 100 template <typename T> 101 StackTraceEntry operator<<(T&& message_ext) && { 102 message_ << std::forward<T>(message_ext); 103 return std::move(*this); 104 } 105 106 operator StackTraceError() &&; 107 template <typename T> 108 operator android::base::expected<T, StackTraceError>() &&; 109 110 bool HasMessage() const; 111 112 /* 113 * Print a single stack trace entry out of a list of format specifiers. 114 * Some format specifiers [a,c,n] cause changes that affect all lines, while 115 * the rest amount to printing a single line in the output. This code is 116 * reused by formatting code for both rendering individual stack trace 117 * entries, and rendering an entire stack trace with multiple entries. 118 */ 119 fmt::format_context::iterator format( 120 fmt::format_context& ctx, const std::vector<FormatSpecifier>& specifiers, 121 std::optional<int> index) const; 122 123 private: 124 std::string file_; 125 size_t line_; 126 std::string pretty_function_; 127 std::string function_; 128 std::string expression_; 129 std::stringstream message_; 130 }; 131 132 std::string ResultErrorFormat(bool color); 133 134 #define CF_STACK_TRACE_ENTRY(expression) \ 135 StackTraceEntry(__FILE__, __LINE__, __PRETTY_FUNCTION__, __func__, expression) 136 137 } // namespace cuttlefish 138 139 /** 140 * Specialized formatting for StackTraceEntry based on user-provided specifiers. 141 * 142 * A StackTraceEntry can be formatted with {:specifiers} in a `fmt::format` 143 * string, where `specifiers` is an ordered list of characters deciding on the 144 * format. `v` provides "verbose" output and `V` provides "very verbose" output. 145 * See `StackTraceEntry::FormatSpecifiers` for more fine-grained specifiers. 146 */ 147 template <> 148 struct fmt::formatter<cuttlefish::StackTraceEntry> { 149 public: 150 constexpr auto parse(format_parse_context& ctx) 151 -> format_parse_context::iterator { 152 auto it = ctx.begin(); 153 while (it != ctx.end() && *it != '}') { 154 if (*it == 'v') { 155 for (const auto& specifier : cuttlefish::StackTraceEntry::kVerbose) { 156 fmt_specs_.push_back(specifier); 157 } 158 } else if (*it == 'V') { 159 for (const auto& specifier : 160 cuttlefish::StackTraceEntry::kVeryVerbose) { 161 fmt_specs_.push_back(specifier); 162 } 163 } else { 164 fmt_specs_.push_back( 165 static_cast<cuttlefish::StackTraceEntry::FormatSpecifier>(*it)); 166 } 167 it++; 168 } 169 return it; 170 } 171 172 auto format(const cuttlefish::StackTraceEntry& entry, 173 format_context& ctx) const -> format_context::iterator { 174 return entry.format(ctx, fmt_specs_, std::nullopt); 175 } 176 177 private: 178 std::vector<cuttlefish::StackTraceEntry::FormatSpecifier> fmt_specs_; 179 }; 180 181 namespace cuttlefish { 182 183 class StackTraceError { 184 public: 185 StackTraceError& PushEntry(StackTraceEntry entry) & { 186 stack_.emplace_back(std::move(entry)); 187 return *this; 188 } 189 StackTraceError PushEntry(StackTraceEntry entry) && { 190 stack_.emplace_back(std::move(entry)); 191 return std::move(*this); 192 } 193 const std::vector<StackTraceEntry>& Stack() const { return stack_; } 194 195 std::string Message() const { 196 return fmt::format(fmt::runtime("{:m}"), *this); 197 } 198 199 std::string Trace() const { return fmt::format(fmt::runtime("{:v}"), *this); } 200 201 std::string FormatForEnv(bool color = (isatty(STDERR_FILENO) == 1)) const { 202 return fmt::format(fmt::runtime(ResultErrorFormat(color)), *this); 203 } 204 205 template <typename T> 206 operator android::base::expected<T, StackTraceError>() && { 207 return android::base::unexpected(std::move(*this)); 208 } 209 210 private: 211 std::vector<StackTraceEntry> stack_; 212 }; 213 214 inline StackTraceEntry::operator StackTraceError() && { 215 return StackTraceError().PushEntry(std::move(*this)); 216 } 217 218 template <typename T> 219 inline StackTraceEntry::operator android::base::expected<T, 220 StackTraceError>() && { 221 return android::base::unexpected(std::move(*this)); 222 } 223 224 } // namespace cuttlefish 225 226 /** 227 * Specialized formatting for a collection of StackTraceEntry elements. 228 * 229 * Can be formatted by a `fmt::format` string as {:specifiers}. See 230 * `fmt::formatter<cuttlefish::StackTraceEntry>` for the format specifiers of 231 * individual entries. By default the specifier list is passed down to all 232 * indivudal entries, with the following additional rules. The `^` specifier 233 * will change the ordering from inner-to-outer instead of outer-to-inner, and 234 * using the `/` specifier like `<abc>/<xyz>` will apply <xyz> only to the 235 * innermost stack entry, and <abc> to all other stack entries. 236 */ 237 template <> 238 struct fmt::formatter<cuttlefish::StackTraceError> { 239 public: 240 constexpr auto parse(format_parse_context& ctx) 241 -> format_parse_context::iterator { 242 auto it = ctx.begin(); 243 while (it != ctx.end() && *it != '}') { 244 if (*it == 'v') { 245 for (const auto& spec : StackTraceEntry::kVerbose) { 246 (has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_).push_back(spec); 247 } 248 } else if (*it == 'V') { 249 for (const auto& spec : StackTraceEntry::kVeryVerbose) { 250 (has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_).push_back(spec); 251 } 252 } else if (*it == '/') { 253 has_inner_fmt_spec_ = true; 254 } else if (*it == '^') { 255 inner_to_outer_ = true; 256 } else { 257 (has_inner_fmt_spec_ ? inner_fmt_specs_ : fmt_specs_) 258 .push_back(static_cast<StackTraceEntry::FormatSpecifier>(*it)); 259 } 260 it++; 261 } 262 return it; 263 } 264 265 format_context::iterator format(const cuttlefish::StackTraceError& error, 266 format_context& ctx) const; 267 268 private: 269 using StackTraceEntry = cuttlefish::StackTraceEntry; 270 using StackTraceError = cuttlefish::StackTraceError; 271 272 bool inner_to_outer_ = false; 273 bool has_inner_fmt_spec_ = false; 274 std::vector<StackTraceEntry::FormatSpecifier> fmt_specs_; 275 std::vector<StackTraceEntry::FormatSpecifier> inner_fmt_specs_; 276 }; 277 278 namespace cuttlefish { 279 280 template <typename T> 281 using Result = android::base::expected<T, StackTraceError>; 282 283 /** 284 * Error return macro that includes the location in the file in the error 285 * message. Use CF_ERRNO when including information from errno, otherwise use 286 * the base CF_ERR macro. 287 * 288 * Example usage: 289 * 290 * if (mkdir(path.c_str()) != 0) { 291 * return CF_ERRNO("mkdir(\"" << path << "\") failed: " 292 * << strerror(errno)); 293 * } 294 * 295 * This will return an error with the text 296 * 297 * mkdir(...) failed: ... 298 * at path/to/file.cpp:50 299 * in Result<std::string> MyFunction() 300 */ 301 #define CF_ERR(MSG) (CF_STACK_TRACE_ENTRY("") << MSG) 302 #define CF_ERRNO(MSG) (CF_STACK_TRACE_ENTRY("") << MSG) 303 #define CF_ERRF(MSG, ...) \ 304 (CF_STACK_TRACE_ENTRY("") << fmt::format(FMT_STRING(MSG), __VA_ARGS__)) 305 306 template <typename T> 307 T OutcomeDereference(std::optional<T>&& value) { 308 return std::move(*value); 309 } 310 311 template <typename T> 312 typename std::conditional_t<std::is_void_v<T>, bool, T> OutcomeDereference( 313 Result<T>&& result) { 314 if constexpr (std::is_void<T>::value) { 315 return result.ok(); 316 } else { 317 return std::move(*result); 318 } 319 } 320 321 template <typename T> 322 typename std::enable_if<std::is_convertible_v<T, bool>, T>::type 323 OutcomeDereference(T&& value) { 324 return std::forward<T>(value); 325 } 326 327 inline bool TypeIsSuccess(bool value) { return value; } 328 329 template <typename T> 330 bool TypeIsSuccess(std::optional<T>& value) { 331 return value.has_value(); 332 } 333 334 template <typename T> 335 bool TypeIsSuccess(Result<T>& value) { 336 return value.ok(); 337 } 338 339 template <typename T> 340 bool TypeIsSuccess(Result<T>&& value) { 341 return value.ok(); 342 } 343 344 inline auto ErrorFromType(bool) { return StackTraceError(); } 345 346 template <typename T> 347 inline auto ErrorFromType(std::optional<T>) { 348 return StackTraceError(); 349 } 350 351 template <typename T> 352 auto ErrorFromType(Result<T>& value) { 353 return value.error(); 354 } 355 356 template <typename T> 357 auto ErrorFromType(Result<T>&& value) { 358 return value.error(); 359 } 360 361 #define CF_EXPECT_OVERLOAD(_1, _2, NAME, ...) NAME 362 363 #define CF_EXPECT2(RESULT, MSG) \ 364 ({ \ 365 decltype(RESULT)&& macro_intermediate_result = RESULT; \ 366 if (!TypeIsSuccess(macro_intermediate_result)) { \ 367 auto current_entry = CF_STACK_TRACE_ENTRY(#RESULT); \ 368 current_entry << MSG; \ 369 auto error = ErrorFromType(macro_intermediate_result); \ 370 error.PushEntry(std::move(current_entry)); \ 371 return std::move(error); \ 372 }; \ 373 OutcomeDereference(std::move(macro_intermediate_result)); \ 374 }) 375 376 #define CF_EXPECT1(RESULT) CF_EXPECT2(RESULT, "") 377 378 /** 379 * Error propagation macro that can be used as an expression. 380 * 381 * The first argument can be either a Result or a type that is convertible to 382 * a boolean. A successful result will return the value inside the result, or 383 * a conversion to a `true` value will return the unconverted value. This is 384 * useful for e.g. pointers which can be tested through boolean conversion. 385 * 386 * In the failure case, this macro will return from the containing function 387 * with a failing Result. The failing result will include information about the 388 * call site, details from the optional second argument if given, and details 389 * from the failing inner expression if it is a Result. 390 * 391 * This macro must be invoked only in functions that return a Result. 392 * 393 * Example usage: 394 * 395 * Result<std::string> CreateTempDir(); 396 * 397 * Result<std::string> CreatePopulatedTempDir() { 398 * std::string dir = CF_EXPECT(CreateTempDir(), "Failed to create dir"); 399 * // Do something with dir 400 * return dir; 401 * } 402 * 403 * If CreateTempDir fails, the function will returna Result with an error 404 * message that looks like 405 * 406 * Internal error 407 * at /path/to/otherfile.cpp:50 408 * in Result<std::string> CreateTempDir() 409 * Failed to create dir 410 * at /path/to/file.cpp:81: 411 * in Result<std::string> CreatePopulatedTempDir() 412 * for CF_EXPECT(CreateTempDir()) 413 */ 414 #define CF_EXPECT(...) \ 415 CF_EXPECT_OVERLOAD(__VA_ARGS__, CF_EXPECT2, CF_EXPECT1)(__VA_ARGS__) 416 417 #define CF_EXPECTF(RESULT, MSG, ...) \ 418 CF_EXPECT(RESULT, fmt::format(FMT_STRING(MSG), __VA_ARGS__)) 419 420 #define CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, MSG) \ 421 ({ \ 422 auto&& lhs_macro_intermediate_result = LHS_RESULT; \ 423 auto&& rhs_macro_intermediate_result = RHS_RESULT; \ 424 bool comparison_result = lhs_macro_intermediate_result COMPARE_OP \ 425 rhs_macro_intermediate_result; \ 426 if (!comparison_result) { \ 427 auto current_entry = CF_STACK_TRACE_ENTRY(""); \ 428 current_entry << "Expected \"" << #LHS_RESULT << "\" " << #COMPARE_OP \ 429 << " \"" << #RHS_RESULT << "\" but was " \ 430 << lhs_macro_intermediate_result << " vs " \ 431 << rhs_macro_intermediate_result << ". "; \ 432 current_entry << MSG; \ 433 auto error = ErrorFromType(false); \ 434 error.PushEntry(std::move(current_entry)); \ 435 return std::move(error); \ 436 }; \ 437 comparison_result; \ 438 }) 439 440 #define CF_COMPARE_EXPECT3(COMPARE_OP, LHS_RESULT, RHS_RESULT) \ 441 CF_COMPARE_EXPECT4(COMPARE_OP, LHS_RESULT, RHS_RESULT, "") 442 443 #define CF_COMPARE_EXPECT_OVERLOAD(_1, _2, _3, _4, NAME, ...) NAME 444 445 #define CF_COMPARE_EXPECT(...) \ 446 CF_COMPARE_EXPECT_OVERLOAD(__VA_ARGS__, CF_COMPARE_EXPECT4, \ 447 CF_COMPARE_EXPECT3) \ 448 (__VA_ARGS__) 449 450 #define CF_EXPECT_EQ(LHS_RESULT, RHS_RESULT, ...) \ 451 CF_COMPARE_EXPECT(==, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 452 #define CF_EXPECT_NE(LHS_RESULT, RHS_RESULT, ...) \ 453 CF_COMPARE_EXPECT(!=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 454 #define CF_EXPECT_LE(LHS_RESULT, RHS_RESULT, ...) \ 455 CF_COMPARE_EXPECT(<=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 456 #define CF_EXPECT_LT(LHS_RESULT, RHS_RESULT, ...) \ 457 CF_COMPARE_EXPECT(<, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 458 #define CF_EXPECT_GE(LHS_RESULT, RHS_RESULT, ...) \ 459 CF_COMPARE_EXPECT(>=, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 460 #define CF_EXPECT_GT(LHS_RESULT, RHS_RESULT, ...) \ 461 CF_COMPARE_EXPECT(>, LHS_RESULT, RHS_RESULT, ##__VA_ARGS__) 462 463 } // namespace cuttlefish 464