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 #include "common/libs/utils/result.h"
17
18 #include <optional>
19 #include <sstream>
20 #include <string>
21 #include <utility>
22 #include <vector>
23
24 #include <android-base/format.h>
25 #include <android-base/logging.h>
26 #include <android-base/result.h>
27
28 namespace cuttlefish {
29
StackTraceEntry(std::string file,size_t line,std::string pretty_function,std::string function)30 StackTraceEntry::StackTraceEntry(std::string file, size_t line,
31 std::string pretty_function,
32 std::string function)
33 : file_(std::move(file)),
34 line_(line),
35 pretty_function_(std::move(pretty_function)),
36 function_(std::move(function)) {}
37
StackTraceEntry(std::string file,size_t line,std::string pretty_function,std::string function,std::string expression)38 StackTraceEntry::StackTraceEntry(std::string file, size_t line,
39 std::string pretty_function,
40 std::string function, std::string expression)
41 : file_(std::move(file)),
42 line_(line),
43 pretty_function_(std::move(pretty_function)),
44 function_(std::move(function)),
45 expression_(std::move(expression)) {}
46
StackTraceEntry(const StackTraceEntry & other)47 StackTraceEntry::StackTraceEntry(const StackTraceEntry& other)
48 : file_(other.file_),
49 line_(other.line_),
50 pretty_function_(other.pretty_function_),
51 function_(other.function_),
52 expression_(other.expression_),
53 message_(other.message_.str()) {}
54
operator =(const StackTraceEntry & other)55 StackTraceEntry& StackTraceEntry::operator=(const StackTraceEntry& other) {
56 file_ = other.file_;
57 line_ = other.line_;
58 pretty_function_ = other.pretty_function_;
59 function_ = other.function_;
60 expression_ = other.expression_;
61 message_.str(other.message_.str());
62 return *this;
63 }
64
HasMessage() const65 bool StackTraceEntry::HasMessage() const { return !message_.str().empty(); }
66
67 /*
68 * Print a single stack trace entry out of a list of format specifiers.
69 * Some format specifiers [a,c,n] cause changes that affect all lines, while
70 * the rest amount to printing a single line in the output. This code is
71 * reused by formatting code for both rendering individual stack trace
72 * entries, and rendering an entire stack trace with multiple entries.
73 */
format(fmt::format_context & ctx,const std::vector<FormatSpecifier> & specifiers,std::optional<int> index) const74 fmt::format_context::iterator StackTraceEntry::format(
75 fmt::format_context& ctx, const std::vector<FormatSpecifier>& specifiers,
76 std::optional<int> index) const {
77 static constexpr char kTerminalBoldRed[] = "\033[0;1;31m";
78 static constexpr char kTerminalCyan[] = "\033[0;36m";
79 static constexpr char kTerminalRed[] = "\033[0;31m";
80 static constexpr char kTerminalReset[] = "\033[0m";
81 static constexpr char kTerminalUnderline[] = "\033[0;4m";
82 static constexpr char kTerminalYellow[] = "\033[0;33m";
83 auto out = ctx.out();
84 std::vector<FormatSpecifier> filtered_specs;
85 bool arrow = false;
86 bool color = false;
87 bool numbers = false;
88 for (auto spec : specifiers) {
89 switch (spec) {
90 case FormatSpecifier::kArrow:
91 arrow = true;
92 continue;
93 case FormatSpecifier::kColor:
94 color = true;
95 continue;
96 case FormatSpecifier::kLongExpression:
97 case FormatSpecifier::kShortExpression:
98 if (expression_.empty()) {
99 continue;
100 }
101 break;
102 case FormatSpecifier::kMessage:
103 if (!HasMessage()) {
104 continue;
105 }
106 break;
107 case FormatSpecifier::kNumbers:
108 numbers = true;
109 continue;
110 default: // fall through
111 break;
112 }
113 filtered_specs.emplace_back(spec);
114 }
115 if (filtered_specs.empty()) {
116 filtered_specs.push_back(FormatSpecifier::kShort);
117 }
118 for (size_t i = 0; i < filtered_specs.size(); i++) {
119 if (index.has_value() && numbers) {
120 if (color) {
121 out = fmt::format_to(out, "{}{}{}. ", kTerminalYellow, *index,
122 kTerminalReset);
123 } else {
124 out = fmt::format_to(out, "{}. ", *index);
125 }
126 }
127 if (color) {
128 out = fmt::format_to(out, "{}", kTerminalRed);
129 }
130 if (numbers) {
131 if (arrow && (int)i < ((int)filtered_specs.size()) - 2) {
132 out = fmt::format_to(out, "| ");
133 } else if (arrow && i == filtered_specs.size() - 2) {
134 out = fmt::format_to(out, "v ");
135 }
136 } else {
137 if (arrow && (int)i < ((int)filtered_specs.size()) - 2) {
138 out = fmt::format_to(out, " | ");
139 } else if (arrow && i == filtered_specs.size() - 2) {
140 out = fmt::format_to(out, " v ");
141 }
142 }
143 if (color) {
144 out = fmt::format_to(out, "{}", kTerminalReset);
145 }
146 switch (filtered_specs[i]) {
147 case FormatSpecifier::kFunction:
148 if (color) {
149 out = fmt::format_to(out, "{}{}{}", kTerminalCyan, function_,
150 kTerminalReset);
151 } else {
152 out = fmt::format_to(out, "{}", function_);
153 }
154 break;
155 case FormatSpecifier::kLongExpression:
156 out = fmt::format_to(out, "CF_EXPECT({})", expression_);
157 break;
158 case FormatSpecifier::kLongLocation:
159 if (color) {
160 out = fmt::format_to(out, "{}{}{}:{}{}{}", kTerminalUnderline, file_,
161 kTerminalReset, kTerminalYellow, line_,
162 kTerminalYellow);
163 } else {
164 out = fmt::format_to(out, "{}:{}", file_, line_);
165 }
166 break;
167 case FormatSpecifier::kMessage:
168 if (color) {
169 out = fmt::format_to(out, "{}{}{}", kTerminalBoldRed, message_.str(),
170 kTerminalReset);
171 } else {
172 out = fmt::format_to(out, "{}", message_.str());
173 }
174 break;
175 case FormatSpecifier::kPrettyFunction:
176 if (color) {
177 out = fmt::format_to(out, "{}{}{}", kTerminalCyan, pretty_function_,
178 kTerminalReset);
179 } else {
180 out = fmt::format_to(out, "{}", pretty_function_);
181 }
182 break;
183 case FormatSpecifier::kShort: {
184 auto last_slash = file_.rfind("/");
185 auto short_file =
186 file_.substr(last_slash == std::string::npos ? 0 : last_slash + 1);
187 std::string last;
188 if (HasMessage()) {
189 last = color ? kTerminalBoldRed + message_.str() + kTerminalReset
190 : message_.str();
191 }
192 if (color) {
193 out = fmt::format_to(out, "{}{}{}:{}{}{} | {}{}{} | {}",
194 kTerminalUnderline, short_file, kTerminalReset,
195 kTerminalYellow, line_, kTerminalReset,
196 kTerminalCyan, function_, kTerminalReset, last);
197 } else {
198 out = fmt::format_to(out, "{}:{} | {} | {}", short_file, line_,
199 function_, last);
200 }
201 break;
202 }
203 case FormatSpecifier::kShortExpression:
204 out = fmt::format_to(out, "{}", expression_);
205 break;
206 case FormatSpecifier::kShortLocation: {
207 auto last_slash = file_.rfind("/");
208 auto short_file =
209 file_.substr(last_slash == std::string::npos ? 0 : last_slash + 1);
210 if (color) {
211 out = fmt::format_to(out, "{}{}{}:{}{}{}", kTerminalUnderline,
212 short_file, kTerminalReset, kTerminalYellow,
213 line_, kTerminalReset);
214 } else {
215 out = fmt::format_to(out, "{}:{}", short_file, line_);
216 }
217 break;
218 }
219 default:
220 fmt::format_to(out, "unknown specifier");
221 }
222 if (i < filtered_specs.size() - 1) {
223 out = fmt::format_to(out, "\n");
224 }
225 }
226 return out;
227 }
228
ResultErrorFormat(bool color)229 std::string ResultErrorFormat(bool color) {
230 auto error_format = getenv("CF_ERROR_FORMAT");
231 std::string default_error_format = (color ? "cns/acLFEm" : "ns/aLFEm");
232 std::string fmt_str =
233 error_format == nullptr ? default_error_format : error_format;
234 if (fmt_str.find("}") != std::string::npos) {
235 fmt_str = "v";
236 }
237 return "{:" + fmt_str + "}";
238 }
239
240 } // namespace cuttlefish
241
242 fmt::format_context::iterator
format(const cuttlefish::StackTraceError & error,format_context & ctx) const243 fmt::formatter<cuttlefish::StackTraceError>::format(
244 const cuttlefish::StackTraceError& error, format_context& ctx) const {
245 auto out = ctx.out();
246 auto& stack = error.Stack();
247 int begin = inner_to_outer_ ? 0 : stack.size() - 1;
248 int end = inner_to_outer_ ? stack.size() : -1;
249 int step = inner_to_outer_ ? 1 : -1;
250 for (int i = begin; i != end; i += step) {
251 auto& specs = has_inner_fmt_spec_ && i == 0 ? inner_fmt_specs_ : fmt_specs_;
252 out = stack[i].format(ctx, specs, i);
253 if (i != end - step) {
254 out = fmt::format_to(out, "\n");
255 }
256 }
257 return out;
258 }
259