• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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