• 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 #include "src/core/lib/gprpp/time_util.h"
23 
24 #include <grpc/support/log.h>
25 
26 #include "absl/strings/cord.h"
27 #include "absl/strings/escaping.h"
28 #include "absl/strings/match.h"
29 #include "absl/strings/str_format.h"
30 #include "absl/strings/str_join.h"
31 #include "absl/time/clock.h"
32 
33 #include "google/protobuf/any.upb.h"
34 #include "google/rpc/status.upb.h"
35 #include "upb/upb.hpp"
36 
37 namespace grpc_core {
38 
39 namespace {
40 
41 #define TYPE_URL_PREFIX "type.googleapis.com/grpc.status."
42 #define TYPE_INT_TAG "int."
43 #define TYPE_STR_TAG "str."
44 #define TYPE_TIME_TAG "time."
45 #define TYPE_CHILDREN_TAG "children"
46 #define TYPE_URL(name) (TYPE_URL_PREFIX name)
47 const absl::string_view kTypeUrlPrefix = TYPE_URL_PREFIX;
48 const absl::string_view kTypeIntTag = TYPE_INT_TAG;
49 const absl::string_view kTypeStrTag = TYPE_STR_TAG;
50 const absl::string_view kTypeTimeTag = TYPE_TIME_TAG;
51 const absl::string_view kTypeChildrenTag = TYPE_CHILDREN_TAG;
52 const absl::string_view kChildrenPropertyUrl = TYPE_URL(TYPE_CHILDREN_TAG);
53 
GetStatusIntPropertyUrl(StatusIntProperty key)54 const char* GetStatusIntPropertyUrl(StatusIntProperty key) {
55   switch (key) {
56     case StatusIntProperty::kErrorNo:
57       return TYPE_URL(TYPE_INT_TAG "errno");
58     case StatusIntProperty::kFileLine:
59       return TYPE_URL(TYPE_INT_TAG "file_line");
60     case StatusIntProperty::kStreamId:
61       return TYPE_URL(TYPE_INT_TAG "stream_id");
62     case StatusIntProperty::kRpcStatus:
63       return TYPE_URL(TYPE_INT_TAG "grpc_status");
64     case StatusIntProperty::kOffset:
65       return TYPE_URL(TYPE_INT_TAG "offset");
66     case StatusIntProperty::kIndex:
67       return TYPE_URL(TYPE_INT_TAG "index");
68     case StatusIntProperty::kSize:
69       return TYPE_URL(TYPE_INT_TAG "size");
70     case StatusIntProperty::kHttp2Error:
71       return TYPE_URL(TYPE_INT_TAG "http2_error");
72     case StatusIntProperty::kTsiCode:
73       return TYPE_URL(TYPE_INT_TAG "tsi_code");
74     case StatusIntProperty::kWsaError:
75       return TYPE_URL(TYPE_INT_TAG "wsa_error");
76     case StatusIntProperty::kFd:
77       return TYPE_URL(TYPE_INT_TAG "fd");
78     case StatusIntProperty::kHttpStatus:
79       return TYPE_URL(TYPE_INT_TAG "http_status");
80     case StatusIntProperty::kOccurredDuringWrite:
81       return TYPE_URL(TYPE_INT_TAG "occurred_during_write");
82     case StatusIntProperty::ChannelConnectivityState:
83       return TYPE_URL(TYPE_INT_TAG "channel_connectivity_state");
84     case StatusIntProperty::kLbPolicyDrop:
85       return TYPE_URL(TYPE_INT_TAG "lb_policy_drop");
86   }
87   GPR_UNREACHABLE_CODE(return "unknown");
88 }
89 
GetStatusStrPropertyUrl(StatusStrProperty key)90 const char* GetStatusStrPropertyUrl(StatusStrProperty key) {
91   switch (key) {
92     case StatusStrProperty::kDescription:
93       return TYPE_URL(TYPE_STR_TAG "description");
94     case StatusStrProperty::kFile:
95       return TYPE_URL(TYPE_STR_TAG "file");
96     case StatusStrProperty::kOsError:
97       return TYPE_URL(TYPE_STR_TAG "os_error");
98     case StatusStrProperty::kSyscall:
99       return TYPE_URL(TYPE_STR_TAG "syscall");
100     case StatusStrProperty::kTargetAddress:
101       return TYPE_URL(TYPE_STR_TAG "target_address");
102     case StatusStrProperty::kGrpcMessage:
103       return TYPE_URL(TYPE_STR_TAG "grpc_message");
104     case StatusStrProperty::kRawBytes:
105       return TYPE_URL(TYPE_STR_TAG "raw_bytes");
106     case StatusStrProperty::kTsiError:
107       return TYPE_URL(TYPE_STR_TAG "tsi_error");
108     case StatusStrProperty::kFilename:
109       return TYPE_URL(TYPE_STR_TAG "filename");
110     case StatusStrProperty::kKey:
111       return TYPE_URL(TYPE_STR_TAG "key");
112     case StatusStrProperty::kValue:
113       return TYPE_URL(TYPE_STR_TAG "value");
114   }
115   GPR_UNREACHABLE_CODE(return "unknown");
116 }
117 
GetStatusTimePropertyUrl(StatusTimeProperty key)118 const char* GetStatusTimePropertyUrl(StatusTimeProperty key) {
119   switch (key) {
120     case StatusTimeProperty::kCreated:
121       return TYPE_URL(TYPE_TIME_TAG "created_time");
122   }
123   GPR_UNREACHABLE_CODE(return "unknown");
124 }
125 
EncodeUInt32ToBytes(uint32_t v,char * buf)126 void EncodeUInt32ToBytes(uint32_t v, char* buf) {
127   buf[0] = v & 0xFF;
128   buf[1] = (v >> 8) & 0xFF;
129   buf[2] = (v >> 16) & 0xFF;
130   buf[3] = (v >> 24) & 0xFF;
131 }
132 
DecodeUInt32FromBytes(const char * buf)133 uint32_t DecodeUInt32FromBytes(const char* buf) {
134   return buf[0] | (uint32_t(buf[1]) << 8) | (uint32_t(buf[2]) << 16) |
135          (uint32_t(buf[3]) << 24);
136 }
137 
ParseChildren(absl::Cord children)138 std::vector<absl::Status> ParseChildren(absl::Cord children) {
139   std::vector<absl::Status> result;
140   upb::Arena arena;
141   // Cord is flattened to iterate the buffer easily at the cost of memory copy.
142   // TODO(veblush): Optimize this once CordReader is introduced.
143   absl::string_view buf = children.Flatten();
144   size_t cur = 0;
145   while (buf.size() - cur >= sizeof(uint32_t)) {
146     size_t msg_size = DecodeUInt32FromBytes(buf.data() + cur);
147     cur += sizeof(uint32_t);
148     GPR_ASSERT(buf.size() - cur >= msg_size);
149     google_rpc_Status* msg =
150         google_rpc_Status_parse(buf.data() + cur, msg_size, arena.ptr());
151     cur += msg_size;
152     result.push_back(internal::StatusFromProto(msg));
153   }
154   return result;
155 }
156 
157 }  // namespace
158 
StatusCreate(absl::StatusCode code,absl::string_view msg,const DebugLocation & location,std::initializer_list<absl::Status> children)159 absl::Status StatusCreate(absl::StatusCode code, absl::string_view msg,
160                           const DebugLocation& location,
161                           std::initializer_list<absl::Status> children) {
162   absl::Status s(code, msg);
163   if (location.file() != nullptr) {
164     StatusSetStr(&s, StatusStrProperty::kFile, location.file());
165   }
166   if (location.line() != -1) {
167     StatusSetInt(&s, StatusIntProperty::kFileLine, location.line());
168   }
169   StatusSetTime(&s, StatusTimeProperty::kCreated, absl::Now());
170   for (const absl::Status& child : children) {
171     if (!child.ok()) {
172       StatusAddChild(&s, child);
173     }
174   }
175   return s;
176 }
177 
StatusSetInt(absl::Status * status,StatusIntProperty key,intptr_t value)178 void StatusSetInt(absl::Status* status, StatusIntProperty key, intptr_t value) {
179   status->SetPayload(GetStatusIntPropertyUrl(key),
180                      absl::Cord(std::to_string(value)));
181 }
182 
StatusGetInt(const absl::Status & status,StatusIntProperty key)183 absl::optional<intptr_t> StatusGetInt(const absl::Status& status,
184                                       StatusIntProperty key) {
185   absl::optional<absl::Cord> p =
186       status.GetPayload(GetStatusIntPropertyUrl(key));
187   if (p.has_value()) {
188     absl::optional<absl::string_view> sv = p->TryFlat();
189     intptr_t value;
190     if (sv.has_value()) {
191       if (absl::SimpleAtoi(*sv, &value)) {
192         return value;
193       }
194     } else {
195       if (absl::SimpleAtoi(std::string(*p), &value)) {
196         return value;
197       }
198     }
199   }
200   return {};
201 }
202 
StatusSetStr(absl::Status * status,StatusStrProperty key,absl::string_view value)203 void StatusSetStr(absl::Status* status, StatusStrProperty key,
204                   absl::string_view value) {
205   status->SetPayload(GetStatusStrPropertyUrl(key), absl::Cord(value));
206 }
207 
StatusGetStr(const absl::Status & status,StatusStrProperty key)208 absl::optional<std::string> StatusGetStr(const absl::Status& status,
209                                          StatusStrProperty key) {
210   absl::optional<absl::Cord> p =
211       status.GetPayload(GetStatusStrPropertyUrl(key));
212   if (p.has_value()) {
213     return std::string(*p);
214   }
215   return {};
216 }
217 
StatusSetTime(absl::Status * status,StatusTimeProperty key,absl::Time time)218 void StatusSetTime(absl::Status* status, StatusTimeProperty key,
219                    absl::Time time) {
220   status->SetPayload(GetStatusTimePropertyUrl(key),
221                      absl::Cord(absl::string_view(
222                          reinterpret_cast<const char*>(&time), sizeof(time))));
223 }
224 
StatusGetTime(const absl::Status & status,StatusTimeProperty key)225 absl::optional<absl::Time> StatusGetTime(const absl::Status& status,
226                                          StatusTimeProperty key) {
227   absl::optional<absl::Cord> p =
228       status.GetPayload(GetStatusTimePropertyUrl(key));
229   if (p.has_value()) {
230     absl::optional<absl::string_view> sv = p->TryFlat();
231     if (sv.has_value()) {
232       return *reinterpret_cast<const absl::Time*>(sv->data());
233     } else {
234       std::string s = std::string(*p);
235       return *reinterpret_cast<const absl::Time*>(s.c_str());
236     }
237   }
238   return {};
239 }
240 
StatusAddChild(absl::Status * status,absl::Status child)241 void StatusAddChild(absl::Status* status, absl::Status child) {
242   upb::Arena arena;
243   // Serialize msg to buf
244   google_rpc_Status* msg = internal::StatusToProto(child, arena.ptr());
245   size_t buf_len = 0;
246   char* buf = google_rpc_Status_serialize(msg, arena.ptr(), &buf_len);
247   // Append (msg-length and msg) to children payload
248   absl::optional<absl::Cord> old_children =
249       status->GetPayload(kChildrenPropertyUrl);
250   absl::Cord children;
251   if (old_children.has_value()) {
252     children = *old_children;
253   }
254   char head_buf[sizeof(uint32_t)];
255   EncodeUInt32ToBytes(buf_len, head_buf);
256   children.Append(absl::string_view(head_buf, sizeof(uint32_t)));
257   children.Append(absl::string_view(buf, buf_len));
258   status->SetPayload(kChildrenPropertyUrl, std::move(children));
259 }
260 
StatusGetChildren(absl::Status status)261 std::vector<absl::Status> StatusGetChildren(absl::Status status) {
262   absl::optional<absl::Cord> children = status.GetPayload(kChildrenPropertyUrl);
263   return children.has_value() ? ParseChildren(*children)
264                               : std::vector<absl::Status>();
265 }
266 
StatusToString(const absl::Status & status)267 std::string StatusToString(const absl::Status& status) {
268   if (status.ok()) {
269     return "OK";
270   }
271   std::string head;
272   absl::StrAppend(&head, absl::StatusCodeToString(status.code()));
273   if (!status.message().empty()) {
274     absl::StrAppend(&head, ":", status.message());
275   }
276   std::vector<std::string> kvs;
277   absl::optional<absl::Cord> children;
278   status.ForEachPayload([&](absl::string_view type_url,
279                             const absl::Cord& payload) {
280     if (absl::StartsWith(type_url, kTypeUrlPrefix)) {
281       type_url.remove_prefix(kTypeUrlPrefix.size());
282       if (type_url == kTypeChildrenTag) {
283         children = payload;
284         return;
285       }
286       absl::string_view payload_view;
287       std::string payload_storage;
288       if (payload.TryFlat().has_value()) {
289         payload_view = payload.TryFlat().value();
290       } else {
291         payload_storage = std::string(payload);
292         payload_view = payload_storage;
293       }
294       if (absl::StartsWith(type_url, kTypeIntTag)) {
295         type_url.remove_prefix(kTypeIntTag.size());
296         kvs.push_back(absl::StrCat(type_url, ":", payload_view));
297       } else if (absl::StartsWith(type_url, kTypeStrTag)) {
298         type_url.remove_prefix(kTypeStrTag.size());
299         kvs.push_back(absl::StrCat(type_url, ":\"",
300                                    absl::CHexEscape(payload_view), "\""));
301       } else if (absl::StartsWith(type_url, kTypeTimeTag)) {
302         type_url.remove_prefix(kTypeTimeTag.size());
303         absl::Time t =
304             *reinterpret_cast<const absl::Time*>(payload_view.data());
305         kvs.push_back(absl::StrCat(type_url, ":\"", absl::FormatTime(t), "\""));
306       } else {
307         kvs.push_back(absl::StrCat(type_url, ":\"",
308                                    absl::CHexEscape(payload_view), "\""));
309       }
310     } else {
311       absl::optional<absl::string_view> payload_view = payload.TryFlat();
312       std::string payload_str = absl::CHexEscape(
313           payload_view.has_value() ? *payload_view : std::string(payload));
314       kvs.push_back(absl::StrCat(type_url, ":\"", payload_str, "\""));
315     }
316   });
317   if (children.has_value()) {
318     std::vector<absl::Status> children_status = ParseChildren(*children);
319     std::vector<std::string> children_text;
320     children_text.reserve(children_status.size());
321     for (const absl::Status& child_status : children_status) {
322       children_text.push_back(StatusToString(child_status));
323     }
324     kvs.push_back(
325         absl::StrCat("children:[", absl::StrJoin(children_text, ", "), "]"));
326   }
327   return kvs.empty() ? head
328                      : absl::StrCat(head, " {", absl::StrJoin(kvs, ", "), "}");
329 }
330 
331 namespace internal {
332 
StatusToProto(absl::Status status,upb_arena * arena)333 google_rpc_Status* StatusToProto(absl::Status status, upb_arena* arena) {
334   google_rpc_Status* msg = google_rpc_Status_new(arena);
335   google_rpc_Status_set_code(msg, int32_t(status.code()));
336   google_rpc_Status_set_message(
337       msg, upb_strview_make(status.message().data(), status.message().size()));
338   status.ForEachPayload([&](absl::string_view type_url,
339                             const absl::Cord& payload) {
340     google_protobuf_Any* any = google_rpc_Status_add_details(msg, arena);
341     char* type_url_buf =
342         reinterpret_cast<char*>(upb_arena_malloc(arena, type_url.size()));
343     memcpy(type_url_buf, type_url.data(), type_url.size());
344     google_protobuf_Any_set_type_url(
345         any, upb_strview_make(type_url_buf, type_url.size()));
346     absl::optional<absl::string_view> v_view = payload.TryFlat();
347     if (v_view.has_value()) {
348       google_protobuf_Any_set_value(
349           any, upb_strview_make(v_view->data(), v_view->size()));
350     } else {
351       char* buf =
352           reinterpret_cast<char*>(upb_arena_malloc(arena, payload.size()));
353       char* cur = buf;
354       for (absl::string_view chunk : payload.Chunks()) {
355         memcpy(cur, chunk.data(), chunk.size());
356         cur += chunk.size();
357       }
358       google_protobuf_Any_set_value(any, upb_strview_make(buf, payload.size()));
359     }
360   });
361   return msg;
362 }
363 
StatusFromProto(google_rpc_Status * msg)364 absl::Status StatusFromProto(google_rpc_Status* msg) {
365   int32_t code = google_rpc_Status_code(msg);
366   upb_strview message = google_rpc_Status_message(msg);
367   absl::Status status(static_cast<absl::StatusCode>(code),
368                       absl::string_view(message.data, message.size));
369   size_t detail_len;
370   const google_protobuf_Any* const* details =
371       google_rpc_Status_details(msg, &detail_len);
372   for (size_t i = 0; i < detail_len; i++) {
373     upb_strview type_url = google_protobuf_Any_type_url(details[i]);
374     upb_strview value = google_protobuf_Any_value(details[i]);
375     status.SetPayload(absl::string_view(type_url.data, type_url.size),
376                       absl::Cord(absl::string_view(value.data, value.size)));
377   }
378   return status;
379 }
380 
StatusAllocPtr(absl::Status s)381 uintptr_t StatusAllocPtr(absl::Status s) {
382   // This relies the fact that absl::Status has only one member, StatusRep*
383   // so the sizeof(absl::Status) has the same size of intptr_t and StatusRep*
384   // can be stolen using placement allocation.
385   static_assert(sizeof(intptr_t) == sizeof(absl::Status),
386                 "absl::Status should be as big as intptr_t");
387   // This does two things;
388   // 1. Copies StatusRep* of absl::Status to ptr
389   // 2. Increases the counter of StatusRep if it's not inlined
390   uintptr_t ptr;
391   new (&ptr) absl::Status(s);
392   return ptr;
393 }
394 
StatusFreePtr(uintptr_t ptr)395 void StatusFreePtr(uintptr_t ptr) {
396   // Decreases the counter of StatusRep if it's not inlined.
397   reinterpret_cast<absl::Status*>(&ptr)->~Status();
398 }
399 
StatusGetFromPtr(uintptr_t ptr)400 absl::Status StatusGetFromPtr(uintptr_t ptr) {
401   // Constructs Status from ptr having the address of StatusRep.
402   return *reinterpret_cast<absl::Status*>(&ptr);
403 }
404 
405 }  // namespace internal
406 
407 }  // namespace grpc_core
408