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