PSA: Log LLCPP errors in style

45 views
Skip to first unread message

Yifei Teng

unread,
May 17, 2021, 7:35:10 PM5/17/21
to fidl-dev

TL;DR: fidl::UnbindInfo and result types in LLCPP have better logging support.


Action required: do not use .reason(), .status(), or .error_message() when logging LLCPP errors. See the better alternatives below.


When using the LLCPP FIDL bindings, you may have came across error logging code looking like the following:

fidl::UnbindInfo info = /* … */;

LOGF(ERROR, "Server error: %s, %d, %s",

     info.status_string(), info.reason(), info.error_message());


or alternatively when making method calls:

fidl::WireResult result = fidl::WireCall(client_end).Foo();

if (!result.ok()) {

  zxlogf(ERROR, "FIDL call failed: %d, %s\n",

         result.status(), result.error_message());

}


It can be cumbersome manually logging all the relevant information, and different call-sites tend to miss various bits and pieces. fxrev.dev/525500 improves the situation by introducing one uniform logging format that is also more detailed. The logging format is supported on these types:


  • fidl::UnbindInfo: the description of why a server/client endpoint was unbound

  • fidl::Result: the return value of one-way and asynchronous two-way calls

  • fidl::WireResult<Method>: the return value of synchronous two-way calls

  • fidl::OwnedEncodedMessage<T>: the encoding result in persistence use cases


Logging an error with FX_LOGS

fidl::UnbindInfo can be piped to the stream-based logger directly:

void Unbound(fidl::UnbindInfo info) {

  if (!info.ok())

    FX_LOGS(ERROR) << info;

}


To log the error portion of a result type, use the .error() accessor:

fidl::WireResult bar = fidl::WireCall(foo_client_end).GetBar();

if (!bar.ok()) {

  FX_LOGS(ERROR) << "GetBar failed: " << bar.error();

}

Other stream-based API (e.g. FAIL() from gtest, std::cerr) are also supported.


Logging an error with zxlogf / printf

Use the .FormatDescription() method to get a std::string:

fidl::WireResult bar = fidl::WireCall(foo_client_end).GetBar();

if (!bar.ok()) {

  zxlogf(ERROR, "GetBar failed: %s\n", bar.FormatDescription().c_str());

}


When an error happens, the rendered log would look along the following lines:

GetBar failed: FIDL operation failed due to decode error, status: ZX_ERR_INVALID_ARGS (-10), detail: non-nullable string was absent


Another example for when a fidl::Client unbinds:

FIDL endpoint was unbound due to peer closed, epitaph: ZX_ERR_INTERNAL (-1)


I don't want any heap allocations in the error path

The FormatDescription function allocates a string on the heap. There may still be scenarios where a const char* error message is desired, such as in latency-sensitive applications or when the error needs to be passed through a C API without worrying about ownership of the error string. In those cases, you may use the .lossy_description() method on a result, which would return a const char* with static lifetime (i.e. stays alive throughout the duration of the program). The lossy description as the name suggests would find the closest error string on a best-effort basis, and may lose information.


Cheers,

Yifei


Reply all
Reply to author
Forward
0 new messages