blob: a5bdeb68e1fc3b0ffdb639989db5eea8d1d13a43 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/fidl/llcpp/status.h>
#include <lib/fit/nullable.h>
#include <zircon/compiler.h>
#include <array>
#include <iostream>
#include <string>
#include <string_view>
namespace fidl {
namespace internal {
const char* const kErrorInvalidHeader = "invalid header";
const char* const kErrorUnknownTxId = "unknown txid";
const char* const kErrorUnknownOrdinal = "unknown ordinal";
const char* const kErrorTransport = "underlying transport I/O error";
const char* const kErrorChannelUnbound = "failed outgoing operation on unbound channel";
const char* const kErrorWaitOneFailed = "zx_channel_wait_one failed";
const char* const kErrorSyncEventBufferTooSmall =
"received a larger message than allowed by the events";
const char* const kErrorSyncEventUnhandledTransitionalEvent = "unhandled transitional event";
const char* const kCallerAllocatedBufferTooSmall =
"buffer provided to caller-allocating flavor is too small";
const char* const kUnknownInteraction = "peer did not recognize this method";
const char* const kUnsupportedTransportError =
"server sent a transport_err value that is not supported";
} // namespace internal
namespace {
// A buffer of 256 bytes is sufficient for all tested results.
// If the description exceeds this length at runtime,
// the output will be truncated.
// We can increase the size if necessary.
using StatusFormattingBuffer = std::array<char, 256>;
} // namespace
[[nodiscard]] std::string Status::FormatDescription() const {
StatusFormattingBuffer buf;
size_t length = FormatImpl(buf.begin(), sizeof(buf), /* from_unbind_info */ false);
return std::string(buf.begin(), length);
}
[[nodiscard]] const char* Status::lossy_description() const {
// If an error string was explicitly specified, use that.
if (error_) {
return error_;
}
// Otherwise, derive an error from |reason_|.
return reason_description();
}
[[nodiscard]] const char* Status::reason_description() const {
// The error descriptions are quite terse to save binary size.
switch (reason_) {
case internal::kUninitializedReason:
return nullptr;
case Reason::kUnbind:
return "user initiated unbind";
case Reason::kClose:
return "(server) user initiated close with epitaph";
case Reason::kPeerClosed:
return "peer closed";
case Reason::kDispatcherError:
return "dispatcher error";
case Reason::kTransportError:
return internal::kErrorTransport;
case Reason::kEncodeError:
return "encode error";
case Reason::kDecodeError:
return "decode error";
case Reason::kUnexpectedMessage:
return "unexpected message";
case Reason::kUnknownInteraction:
return "unknown interaction";
}
}
size_t Status::FormatImpl(char* destination, size_t length, bool from_unbind_info) const {
ZX_ASSERT(length > 0);
int num_would_write = 0;
// We use |snprintf| since it minimizes allocation and is faster
// than output streams.
if (!from_unbind_info && status_ == ZX_OK && reason_ == internal::kUninitializedReason) {
num_would_write = snprintf(destination, length, "FIDL success");
} else {
const char* prelude = from_unbind_info ? "FIDL endpoint was unbound" : "FIDL operation failed";
const char* status_meaning = [&] {
switch (reason_) {
// This reason may only appear in an |UnbindInfo|.
case Reason::kClose:
ZX_DEBUG_ASSERT(from_unbind_info);
return "status of sending epitaph";
case Reason::kPeerClosed:
if (status_ != ZX_ERR_PEER_CLOSED) {
return "epitaph";
}
break;
default:
break;
}
return "status";
}();
const char* detail_prefix = error_ ? ", detail: " : "";
const char* detail = error_ ? error_ : "";
#ifdef __Fuchsia__
num_would_write = snprintf(destination, length, "%s due to %s, %s: %s (%d)%s%s", prelude,
reason_description(), status_meaning, status_string(), status_,
detail_prefix, detail);
#else
num_would_write =
snprintf(destination, length, "%s due to %s, %s: %d%s%s", prelude, reason_description(),
status_meaning, status_, detail_prefix, detail);
#endif // __Fuchsia__
}
ZX_ASSERT(num_would_write > 0);
return static_cast<size_t>(num_would_write) >= length ? length - 1 : num_would_write;
}
std::ostream& operator<<(std::ostream& ostream, const Status& result) {
StatusFormattingBuffer buf;
size_t length = result.FormatImpl(buf.begin(), sizeof(buf), /* from_unbind_info */ false);
ostream << std::string_view(buf.begin(), length);
return ostream;
}
[[nodiscard]] std::string UnbindInfo::FormatDescription() const {
StatusFormattingBuffer buf;
size_t length = FormatImpl(buf.begin(), sizeof(buf), /* from_unbind_info */ true);
return std::string(buf.begin(), length);
}
std::ostream& operator<<(std::ostream& ostream, const UnbindInfo& info) {
StatusFormattingBuffer buf;
size_t length = info.Status::FormatImpl(buf.begin(), sizeof(buf), /* from_unbind_info */ true);
ostream << std::string_view(buf.begin(), length);
return ostream;
}
size_t fidl::internal::DisplayError<fidl::Status>::Format(const fidl::Status& value,
char* destination, size_t capacity) {
return value.FormatImpl(destination, capacity, false);
}
} // namespace fidl