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