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