1 //
2 //
3 // Copyright 2021 the gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include <grpc/support/port_platform.h>
20
21 #include "src/core/lib/gprpp/status_helper.h"
22
23 #include <string.h>
24
25 #include <utility>
26
27 #include "absl/strings/cord.h"
28 #include "absl/strings/escaping.h"
29 #include "absl/strings/match.h"
30 #include "absl/strings/numbers.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/str_join.h"
33 #include "absl/time/clock.h"
34 #include "google/protobuf/any.upb.h"
35 #include "google/rpc/status.upb.h"
36 #include "upb/base/string_view.h"
37 #include "upb/mem/arena.hpp"
38
39 #include <grpc/support/log.h>
40
41 #include "src/core/lib/slice/percent_encoding.h"
42 #include "src/core/lib/slice/slice.h"
43
44 namespace grpc_core {
45
46 namespace {
47
48 #define TYPE_URL_PREFIX "type.googleapis.com/grpc.status."
49 #define TYPE_INT_TAG "int."
50 #define TYPE_STR_TAG "str."
51 #define TYPE_TIME_TAG "time."
52 #define TYPE_CHILDREN_TAG "children"
53 #define TYPE_URL(name) (TYPE_URL_PREFIX name)
54 const absl::string_view kTypeUrlPrefix = TYPE_URL_PREFIX;
55 const absl::string_view kTypeIntTag = TYPE_INT_TAG;
56 const absl::string_view kTypeStrTag = TYPE_STR_TAG;
57 const absl::string_view kTypeTimeTag = TYPE_TIME_TAG;
58 const absl::string_view kTypeChildrenTag = TYPE_CHILDREN_TAG;
59 const absl::string_view kChildrenPropertyUrl = TYPE_URL(TYPE_CHILDREN_TAG);
60
GetStatusIntPropertyUrl(StatusIntProperty key)61 const char* GetStatusIntPropertyUrl(StatusIntProperty key) {
62 switch (key) {
63 case StatusIntProperty::kErrorNo:
64 return TYPE_URL(TYPE_INT_TAG "errno");
65 case StatusIntProperty::kFileLine:
66 return TYPE_URL(TYPE_INT_TAG "file_line");
67 case StatusIntProperty::kStreamId:
68 return TYPE_URL(TYPE_INT_TAG "stream_id");
69 case StatusIntProperty::kRpcStatus:
70 return TYPE_URL(TYPE_INT_TAG "grpc_status");
71 case StatusIntProperty::kOffset:
72 return TYPE_URL(TYPE_INT_TAG "offset");
73 case StatusIntProperty::kIndex:
74 return TYPE_URL(TYPE_INT_TAG "index");
75 case StatusIntProperty::kSize:
76 return TYPE_URL(TYPE_INT_TAG "size");
77 case StatusIntProperty::kHttp2Error:
78 return TYPE_URL(TYPE_INT_TAG "http2_error");
79 case StatusIntProperty::kTsiCode:
80 return TYPE_URL(TYPE_INT_TAG "tsi_code");
81 case StatusIntProperty::kWsaError:
82 return TYPE_URL(TYPE_INT_TAG "wsa_error");
83 case StatusIntProperty::kFd:
84 return TYPE_URL(TYPE_INT_TAG "fd");
85 case StatusIntProperty::kHttpStatus:
86 return TYPE_URL(TYPE_INT_TAG "http_status");
87 case StatusIntProperty::kOccurredDuringWrite:
88 return TYPE_URL(TYPE_INT_TAG "occurred_during_write");
89 case StatusIntProperty::ChannelConnectivityState:
90 return TYPE_URL(TYPE_INT_TAG "channel_connectivity_state");
91 case StatusIntProperty::kLbPolicyDrop:
92 return TYPE_URL(TYPE_INT_TAG "lb_policy_drop");
93 }
94 GPR_UNREACHABLE_CODE(return "unknown");
95 }
96
GetStatusStrPropertyUrl(StatusStrProperty key)97 const char* GetStatusStrPropertyUrl(StatusStrProperty key) {
98 switch (key) {
99 case StatusStrProperty::kDescription:
100 return TYPE_URL(TYPE_STR_TAG "description");
101 case StatusStrProperty::kFile:
102 return TYPE_URL(TYPE_STR_TAG "file");
103 case StatusStrProperty::kOsError:
104 return TYPE_URL(TYPE_STR_TAG "os_error");
105 case StatusStrProperty::kSyscall:
106 return TYPE_URL(TYPE_STR_TAG "syscall");
107 case StatusStrProperty::kTargetAddress:
108 return TYPE_URL(TYPE_STR_TAG "target_address");
109 case StatusStrProperty::kGrpcMessage:
110 return TYPE_URL(TYPE_STR_TAG "grpc_message");
111 case StatusStrProperty::kRawBytes:
112 return TYPE_URL(TYPE_STR_TAG "raw_bytes");
113 case StatusStrProperty::kTsiError:
114 return TYPE_URL(TYPE_STR_TAG "tsi_error");
115 case StatusStrProperty::kFilename:
116 return TYPE_URL(TYPE_STR_TAG "filename");
117 case StatusStrProperty::kKey:
118 return TYPE_URL(TYPE_STR_TAG "key");
119 case StatusStrProperty::kValue:
120 return TYPE_URL(TYPE_STR_TAG "value");
121 }
122 GPR_UNREACHABLE_CODE(return "unknown");
123 }
124
GetStatusTimePropertyUrl(StatusTimeProperty key)125 const char* GetStatusTimePropertyUrl(StatusTimeProperty key) {
126 switch (key) {
127 case StatusTimeProperty::kCreated:
128 return TYPE_URL(TYPE_TIME_TAG "created_time");
129 }
130 GPR_UNREACHABLE_CODE(return "unknown");
131 }
132
EncodeUInt32ToBytes(uint32_t v,char * buf)133 void EncodeUInt32ToBytes(uint32_t v, char* buf) {
134 buf[0] = v & 0xFF;
135 buf[1] = (v >> 8) & 0xFF;
136 buf[2] = (v >> 16) & 0xFF;
137 buf[3] = (v >> 24) & 0xFF;
138 }
139
DecodeUInt32FromBytes(const char * buf)140 uint32_t DecodeUInt32FromBytes(const char* buf) {
141 const unsigned char* ubuf = reinterpret_cast<const unsigned char*>(buf);
142 return ubuf[0] | (static_cast<uint32_t>(ubuf[1]) << 8) |
143 (static_cast<uint32_t>(ubuf[2]) << 16) |
144 (static_cast<uint32_t>(ubuf[3]) << 24);
145 }
146
ParseChildren(absl::Cord children)147 std::vector<absl::Status> ParseChildren(absl::Cord children) {
148 std::vector<absl::Status> result;
149 upb::Arena arena;
150 // Cord is flattened to iterate the buffer easily at the cost of memory copy.
151 // TODO(veblush): Optimize this once CordReader is introduced.
152 absl::string_view buf = children.Flatten();
153 size_t cur = 0;
154 while (buf.size() - cur >= sizeof(uint32_t)) {
155 size_t msg_size = DecodeUInt32FromBytes(buf.data() + cur);
156 cur += sizeof(uint32_t);
157 GPR_ASSERT(buf.size() - cur >= msg_size);
158 google_rpc_Status* msg =
159 google_rpc_Status_parse(buf.data() + cur, msg_size, arena.ptr());
160 cur += msg_size;
161 result.push_back(internal::StatusFromProto(msg));
162 }
163 return result;
164 }
165
166 } // namespace
167
StatusCreate(absl::StatusCode code,absl::string_view msg,const DebugLocation & location,std::vector<absl::Status> children)168 absl::Status StatusCreate(absl::StatusCode code, absl::string_view msg,
169 const DebugLocation& location,
170 std::vector<absl::Status> children) {
171 absl::Status s(code, msg);
172 if (location.file() != nullptr) {
173 StatusSetStr(&s, StatusStrProperty::kFile, location.file());
174 }
175 if (location.line() != -1) {
176 StatusSetInt(&s, StatusIntProperty::kFileLine, location.line());
177 }
178 StatusSetTime(&s, StatusTimeProperty::kCreated, absl::Now());
179 for (const absl::Status& child : children) {
180 if (!child.ok()) {
181 StatusAddChild(&s, child);
182 }
183 }
184 return s;
185 }
186
StatusSetInt(absl::Status * status,StatusIntProperty key,intptr_t value)187 void StatusSetInt(absl::Status* status, StatusIntProperty key, intptr_t value) {
188 status->SetPayload(GetStatusIntPropertyUrl(key),
189 absl::Cord(std::to_string(value)));
190 }
191
StatusGetInt(const absl::Status & status,StatusIntProperty key)192 absl::optional<intptr_t> StatusGetInt(const absl::Status& status,
193 StatusIntProperty key) {
194 absl::optional<absl::Cord> p =
195 status.GetPayload(GetStatusIntPropertyUrl(key));
196 if (p.has_value()) {
197 absl::optional<absl::string_view> sv = p->TryFlat();
198 intptr_t value;
199 if (sv.has_value()) {
200 if (absl::SimpleAtoi(*sv, &value)) {
201 return value;
202 }
203 } else {
204 if (absl::SimpleAtoi(std::string(*p), &value)) {
205 return value;
206 }
207 }
208 }
209 return {};
210 }
211
StatusSetStr(absl::Status * status,StatusStrProperty key,absl::string_view value)212 void StatusSetStr(absl::Status* status, StatusStrProperty key,
213 absl::string_view value) {
214 status->SetPayload(GetStatusStrPropertyUrl(key), absl::Cord(value));
215 }
216
StatusGetStr(const absl::Status & status,StatusStrProperty key)217 absl::optional<std::string> StatusGetStr(const absl::Status& status,
218 StatusStrProperty key) {
219 absl::optional<absl::Cord> p =
220 status.GetPayload(GetStatusStrPropertyUrl(key));
221 if (p.has_value()) {
222 return std::string(*p);
223 }
224 return {};
225 }
226
StatusSetTime(absl::Status * status,StatusTimeProperty key,absl::Time time)227 void StatusSetTime(absl::Status* status, StatusTimeProperty key,
228 absl::Time time) {
229 std::string time_str =
230 absl::FormatTime(absl::RFC3339_full, time, absl::UTCTimeZone());
231 status->SetPayload(GetStatusTimePropertyUrl(key),
232 absl::Cord(std::move(time_str)));
233 }
234
StatusGetTime(const absl::Status & status,StatusTimeProperty key)235 absl::optional<absl::Time> StatusGetTime(const absl::Status& status,
236 StatusTimeProperty key) {
237 absl::optional<absl::Cord> p =
238 status.GetPayload(GetStatusTimePropertyUrl(key));
239 if (p.has_value()) {
240 absl::optional<absl::string_view> sv = p->TryFlat();
241 absl::Time time;
242 if (sv.has_value()) {
243 if (absl::ParseTime(absl::RFC3339_full, sv.value(), &time, nullptr)) {
244 return time;
245 }
246 } else {
247 std::string s = std::string(*p);
248 if (absl::ParseTime(absl::RFC3339_full, s, &time, nullptr)) {
249 return time;
250 }
251 }
252 }
253 return {};
254 }
255
StatusAddChild(absl::Status * status,absl::Status child)256 void StatusAddChild(absl::Status* status, absl::Status child) {
257 upb::Arena arena;
258 // Serialize msg to buf
259 google_rpc_Status* msg = internal::StatusToProto(child, arena.ptr());
260 size_t buf_len = 0;
261 char* buf = google_rpc_Status_serialize(msg, arena.ptr(), &buf_len);
262 // Append (msg-length and msg) to children payload
263 absl::optional<absl::Cord> old_children =
264 status->GetPayload(kChildrenPropertyUrl);
265 absl::Cord children;
266 if (old_children.has_value()) {
267 children = *old_children;
268 }
269 char head_buf[sizeof(uint32_t)];
270 EncodeUInt32ToBytes(buf_len, head_buf);
271 children.Append(absl::string_view(head_buf, sizeof(uint32_t)));
272 children.Append(absl::string_view(buf, buf_len));
273 status->SetPayload(kChildrenPropertyUrl, std::move(children));
274 }
275
StatusGetChildren(absl::Status status)276 std::vector<absl::Status> StatusGetChildren(absl::Status status) {
277 absl::optional<absl::Cord> children = status.GetPayload(kChildrenPropertyUrl);
278 return children.has_value() ? ParseChildren(*children)
279 : std::vector<absl::Status>();
280 }
281
StatusToString(const absl::Status & status)282 std::string StatusToString(const absl::Status& status) {
283 if (status.ok()) {
284 return "OK";
285 }
286 std::string head;
287 absl::StrAppend(&head, absl::StatusCodeToString(status.code()));
288 if (!status.message().empty()) {
289 absl::StrAppend(&head, ":", status.message());
290 }
291 std::vector<std::string> kvs;
292 absl::optional<absl::Cord> children;
293 status.ForEachPayload([&](absl::string_view type_url,
294 const absl::Cord& payload) {
295 if (absl::StartsWith(type_url, kTypeUrlPrefix)) {
296 type_url.remove_prefix(kTypeUrlPrefix.size());
297 if (type_url == kTypeChildrenTag) {
298 children = payload;
299 return;
300 }
301 absl::string_view payload_view;
302 std::string payload_storage;
303 if (payload.TryFlat().has_value()) {
304 payload_view = payload.TryFlat().value();
305 } else {
306 payload_storage = std::string(payload);
307 payload_view = payload_storage;
308 }
309 if (absl::StartsWith(type_url, kTypeIntTag)) {
310 type_url.remove_prefix(kTypeIntTag.size());
311 kvs.push_back(absl::StrCat(type_url, ":", payload_view));
312 } else if (absl::StartsWith(type_url, kTypeStrTag)) {
313 type_url.remove_prefix(kTypeStrTag.size());
314 kvs.push_back(absl::StrCat(type_url, ":\"",
315 absl::CHexEscape(payload_view), "\""));
316 } else if (absl::StartsWith(type_url, kTypeTimeTag)) {
317 type_url.remove_prefix(kTypeTimeTag.size());
318 absl::Time t;
319 if (absl::ParseTime(absl::RFC3339_full, payload_view, &t, nullptr)) {
320 kvs.push_back(
321 absl::StrCat(type_url, ":\"", absl::FormatTime(t), "\""));
322 } else {
323 kvs.push_back(absl::StrCat(type_url, ":\"",
324 absl::CHexEscape(payload_view), "\""));
325 }
326 } else {
327 kvs.push_back(absl::StrCat(type_url, ":\"",
328 absl::CHexEscape(payload_view), "\""));
329 }
330 } else {
331 absl::optional<absl::string_view> payload_view = payload.TryFlat();
332 std::string payload_str = absl::CHexEscape(
333 payload_view.has_value() ? *payload_view : std::string(payload));
334 kvs.push_back(absl::StrCat(type_url, ":\"", payload_str, "\""));
335 }
336 });
337 if (children.has_value()) {
338 std::vector<absl::Status> children_status = ParseChildren(*children);
339 std::vector<std::string> children_text;
340 children_text.reserve(children_status.size());
341 for (const absl::Status& child_status : children_status) {
342 children_text.push_back(StatusToString(child_status));
343 }
344 kvs.push_back(
345 absl::StrCat("children:[", absl::StrJoin(children_text, ", "), "]"));
346 }
347 return kvs.empty() ? head
348 : absl::StrCat(head, " {", absl::StrJoin(kvs, ", "), "}");
349 }
350
351 namespace internal {
352
StatusToProto(const absl::Status & status,upb_Arena * arena)353 google_rpc_Status* StatusToProto(const absl::Status& status, upb_Arena* arena) {
354 google_rpc_Status* msg = google_rpc_Status_new(arena);
355 google_rpc_Status_set_code(msg, static_cast<int32_t>(status.code()));
356 // Protobuf string field requires to be utf-8 encoding but C++ string doesn't
357 // this requirement so it can be a non utf-8 string. So it should be converted
358 // to a percent-encoded string to keep it as a utf-8 string.
359 Slice message_percent_slice =
360 PercentEncodeSlice(Slice::FromExternalString(status.message()),
361 PercentEncodingType::Compatible);
362 char* message_percent = reinterpret_cast<char*>(
363 upb_Arena_Malloc(arena, message_percent_slice.length()));
364 if (message_percent_slice.length() > 0) {
365 memcpy(message_percent, message_percent_slice.data(),
366 message_percent_slice.length());
367 }
368 google_rpc_Status_set_message(
369 msg, upb_StringView_FromDataAndSize(message_percent,
370 message_percent_slice.length()));
371 status.ForEachPayload([&](absl::string_view type_url,
372 const absl::Cord& payload) {
373 google_protobuf_Any* any = google_rpc_Status_add_details(msg, arena);
374 char* type_url_buf =
375 reinterpret_cast<char*>(upb_Arena_Malloc(arena, type_url.size()));
376 memcpy(type_url_buf, type_url.data(), type_url.size());
377 google_protobuf_Any_set_type_url(
378 any, upb_StringView_FromDataAndSize(type_url_buf, type_url.size()));
379 absl::optional<absl::string_view> v_view = payload.TryFlat();
380 if (v_view.has_value()) {
381 google_protobuf_Any_set_value(
382 any, upb_StringView_FromDataAndSize(v_view->data(), v_view->size()));
383 } else {
384 char* buf =
385 reinterpret_cast<char*>(upb_Arena_Malloc(arena, payload.size()));
386 char* cur = buf;
387 for (absl::string_view chunk : payload.Chunks()) {
388 memcpy(cur, chunk.data(), chunk.size());
389 cur += chunk.size();
390 }
391 google_protobuf_Any_set_value(
392 any, upb_StringView_FromDataAndSize(buf, payload.size()));
393 }
394 });
395 return msg;
396 }
397
StatusFromProto(google_rpc_Status * msg)398 absl::Status StatusFromProto(google_rpc_Status* msg) {
399 int32_t code = google_rpc_Status_code(msg);
400 upb_StringView message_percent_upb = google_rpc_Status_message(msg);
401 Slice message_percent_slice = Slice::FromExternalString(
402 absl::string_view(message_percent_upb.data, message_percent_upb.size));
403 Slice message_slice =
404 PermissivePercentDecodeSlice(std::move(message_percent_slice));
405 absl::Status status(
406 static_cast<absl::StatusCode>(code),
407 absl::string_view(reinterpret_cast<const char*>(message_slice.data()),
408 message_slice.size()));
409 size_t detail_len;
410 const google_protobuf_Any* const* details =
411 google_rpc_Status_details(msg, &detail_len);
412 for (size_t i = 0; i < detail_len; i++) {
413 upb_StringView type_url = google_protobuf_Any_type_url(details[i]);
414 upb_StringView value = google_protobuf_Any_value(details[i]);
415 status.SetPayload(absl::string_view(type_url.data, type_url.size),
416 absl::Cord(absl::string_view(value.data, value.size)));
417 }
418 return status;
419 }
420
StatusAllocHeapPtr(absl::Status s)421 uintptr_t StatusAllocHeapPtr(absl::Status s) {
422 if (s.ok()) return 0;
423 absl::Status* ptr = new absl::Status(s);
424 return reinterpret_cast<uintptr_t>(ptr);
425 }
426
StatusFreeHeapPtr(uintptr_t ptr)427 void StatusFreeHeapPtr(uintptr_t ptr) {
428 absl::Status* s = reinterpret_cast<absl::Status*>(ptr);
429 delete s;
430 }
431
StatusGetFromHeapPtr(uintptr_t ptr)432 absl::Status StatusGetFromHeapPtr(uintptr_t ptr) {
433 if (ptr == 0) {
434 return absl::OkStatus();
435 } else {
436 return *reinterpret_cast<absl::Status*>(ptr);
437 }
438 }
439
StatusMoveFromHeapPtr(uintptr_t ptr)440 absl::Status StatusMoveFromHeapPtr(uintptr_t ptr) {
441 if (ptr == 0) {
442 return absl::OkStatus();
443 } else {
444 absl::Status* s = reinterpret_cast<absl::Status*>(ptr);
445 absl::Status ret = std::move(*s);
446 delete s;
447 return ret;
448 }
449 }
450
451 } // namespace internal
452
453 } // namespace grpc_core
454