Android-cuttlefish cvd tool
result.h
Go to the documentation of this file.
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
28#include <android-base/result.h> // IWYU pragma: export
29
30#if FMT_VERSION < 80000
31namespace fmt {
32 // fmt::runtime was added in v8.0.0
33 template<typename T>
34 const T& runtime(const T& param) {
35 return param;
36 }
37}
38#endif
39
40namespace cuttlefish {
41
42class StackTraceError;
43
45 public:
46 enum class FormatSpecifier : char {
48 kArrow = 'a',
50 kColor = 'c',
52 kFunction = 'f',
54 kLongExpression = 'E',
56 kLongLocation = 'L',
58 kMessage = 'm',
60 kNumbers = 'n',
62 kPrettyFunction = 'F',
64 kShort = 's',
66 kShortExpression = 'e',
68 kShortLocation = 'l',
69 };
70 static constexpr auto kVerbose = {
75 };
76 static constexpr auto kVeryVerbose = {
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
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>
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
132std::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
147template <>
148struct 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 :
161 fmt_specs_.push_back(specifier);
162 }
163 } else {
164 fmt_specs_.push_back(
166 }
167 it++;
168 }
169 return it;
170 }
171
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
181namespace cuttlefish {
182
184 public:
186 stack_.emplace_back(std::move(entry));
187 return *this;
188 }
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>
207 return android::base::unexpected(std::move(*this));
208 }
209
210 private:
211 std::vector<StackTraceEntry> stack_;
212};
213
214inline StackTraceEntry::operator StackTraceError() && {
215 return StackTraceError().PushEntry(std::move(*this));
216}
217
218template <typename T>
219inline StackTraceEntry::operator android::base::expected<T,
220 StackTraceError>() && {
221 return android::base::unexpected(std::move(*this));
222}
223
224} // namespace cuttlefish
225
237template <>
238struct 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:
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
278namespace cuttlefish {
279
280template <typename T>
282
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
306template <typename T>
307T OutcomeDereference(std::optional<T>&& value) {
308 return std::move(*value);
309}
310
311template <typename T>
312typename 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
321template <typename T>
322typename std::enable_if<std::is_convertible_v<T, bool>, T>::type
324 return std::forward<T>(value);
325}
326
327inline bool TypeIsSuccess(bool value) { return value; }
328
329template <typename T>
330bool TypeIsSuccess(std::optional<T>& value) {
331 return value.has_value();
332}
333
334template <typename T>
336 return value.ok();
337}
338
339template <typename T>
341 return value.ok();
342}
343
344inline auto ErrorFromType(bool) { return StackTraceError(); }
345
346template <typename T>
347inline auto ErrorFromType(std::optional<T>) {
348 return StackTraceError();
349}
350
351template <typename T>
353 return value.error();
354}
355
356template <typename T>
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
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
Definition: expected.h:86
constexpr const E & error() const &
Definition: expected.h:341
constexpr bool ok() const noexcept
Definition: expected.h:334
Definition: result.h:44
static constexpr auto kVeryVerbose
Definition: result.h:76
std::string pretty_function_
Definition: result.h:126
static constexpr auto kVerbose
Definition: result.h:70
StackTraceEntry & operator=(const StackTraceEntry &other)
Definition: result.cpp:55
StackTraceEntry operator<<(T &&message_ext) &&
Definition: result.h:101
size_t line_
Definition: result.h:125
std::stringstream message_
Definition: result.h:129
FormatSpecifier
Definition: result.h:46
std::string file_
Definition: result.h:124
bool HasMessage() const
Definition: result.cpp:65
StackTraceEntry & operator=(StackTraceEntry &&)=default
StackTraceEntry(std::string file, size_t line, std::string pretty_function, std::string function)
Definition: result.cpp:30
std::string expression_
Definition: result.h:128
fmt::format_context::iterator format(fmt::format_context &ctx, const std::vector< FormatSpecifier > &specifiers, std::optional< int > index) const
Definition: result.cpp:74
std::string function_
Definition: result.h:127
StackTraceEntry(StackTraceEntry &&)=default
StackTraceEntry & operator<<(T &&message_ext) &
Definition: result.h:96
Definition: result.h:183
std::string Message() const
Definition: result.h:195
std::vector< StackTraceEntry > stack_
Definition: result.h:211
const std::vector< StackTraceEntry > & Stack() const
Definition: result.h:193
std::string Trace() const
Definition: result.h:199
StackTraceError PushEntry(StackTraceEntry entry) &&
Definition: result.h:189
std::string FormatForEnv(bool color=(isatty(STDERR_FILENO)==1)) const
Definition: result.h:201
StackTraceError & PushEntry(StackTraceEntry entry) &
Definition: result.h:185
#define error(format, args...)
Definition: fec_private.h:201
unexpected(E) -> unexpected< E >
EventFormat format
Definition: kernel_log_server.cc:57
Definition: alloc_utils.cpp:23
std::string ResultErrorFormat(bool color)
Definition: result.cpp:202
T OutcomeDereference(std::optional< T > &&value)
Definition: result.h:307
auto ErrorFromType(bool)
Definition: result.h:344
bool TypeIsSuccess(bool value)
Definition: result.h:327
Definition: result.h:31
const T & runtime(const T &param)
Definition: result.h:34
uint8_t type
Definition: pairing_connection.h:0
auto format(const cuttlefish::StackTraceEntry &entry, format_context &ctx) const -> format_context::iterator
Definition: result.h:172
constexpr auto parse(format_parse_context &ctx) -> format_parse_context::iterator
Definition: result.h:150
std::vector< cuttlefish::StackTraceEntry::FormatSpecifier > fmt_specs_
Definition: result.h:178
std::vector< StackTraceEntry::FormatSpecifier > inner_fmt_specs_
Definition: result.h:275
std::vector< StackTraceEntry::FormatSpecifier > fmt_specs_
Definition: result.h:274
constexpr auto parse(format_parse_context &ctx) -> format_parse_context::iterator
Definition: result.h:240
#define isatty
Definition: sysdeps.h:594