| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifdef UNSAFE_BUFFERS_BUILD |
| // TODO(crbug.com/40285824): Remove this and convert code to safer constructs. |
| #pragma allow_unsafe_buffers |
| #endif |
| |
| #include "components/exo/wayland/wayland_protocol_logger.h" |
| |
| // We need wl_object declared below. |
| #undef WL_HIDE_DEPRECATED |
| #include <wayland-server.h> |
| |
| #include <string> |
| |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/trace_event/typed_macros.h" |
| |
| namespace { |
| |
| std::string StringifyWaylandArgument(const wl_interface* type, |
| const wl_argument& arg, |
| const char** signature_ptr) { |
| while (**signature_ptr) { |
| char s = **signature_ptr; |
| |
| // Always advance the pointer before returning, so that the next |
| // call to this function will look at the next argument's signature. |
| (*signature_ptr)++; |
| |
| // The type of `arg` is indicated by this character of the signature. |
| switch (s) { |
| case 'i': |
| return base::NumberToString(arg.i); |
| case 'u': |
| return base::NumberToString(arg.u); |
| case 'f': |
| return base::NumberToString(wl_fixed_to_double(arg.f)); |
| case 's': |
| return arg.s ? arg.s : "nil"; |
| case 'o': |
| if (arg.o) { |
| const wl_interface* interface = arg.o->interface; |
| return base::StrCat({interface ? interface->name : "[unknown]", "@", |
| base::NumberToString(arg.o->id)}); |
| } |
| return "nil"; |
| case 'n': |
| // type should not normally be null, but this handles requests |
| // from clients so we need to be robust against invalid data. |
| return arg.n ? base::StrCat({"new id ", type ? type->name : "[unknown]", |
| "@", base::NumberToString(arg.n)}) |
| : "nil"; |
| case 'a': |
| // Improve on WAYLAND_DEBUG by printing array contents as hex data. |
| // If the array is "too long", truncate. |
| static const size_t max_printed_bytes = 48; |
| return base::StrCat( |
| {"array[", base::NumberToString(arg.a->size), " bytes]{", |
| base::HexEncode(arg.a->data, |
| std::min(arg.a->size, max_printed_bytes)), |
| // Append an ellipsis if the content was truncated. |
| max_printed_bytes < arg.a->size ? "...}" : "}"}); |
| case 'h': |
| return base::StrCat({"fd ", base::NumberToString(arg.h)}); |
| case '?': |
| // ? indicates the next argument is optional. |
| // Pass through to next loop iteration. |
| break; |
| case '0': |
| case '1': |
| case '2': |
| case '3': |
| case '4': |
| case '5': |
| case '6': |
| case '7': |
| case '8': |
| case '9': |
| // Numbers indicate protocol versions. |
| // Pass through to next loop iteration. |
| break; |
| } |
| } |
| // Reached a null character in the signature. This shouldn't happen. |
| return std::string("<Wayland signature parse error>"); |
| } |
| |
| void LogToPerfetto(void* user_data, |
| wl_protocol_logger_type type, |
| const wl_protocol_logger_message* message) { |
| // Early-out in the common case that tracing is not currently enabled. |
| bool tracing_enabled = false; |
| TRACE_EVENT_CATEGORY_GROUP_ENABLED("exo", &tracing_enabled); |
| if (!tracing_enabled) { |
| return; |
| } |
| |
| std::vector<std::string> msg{ |
| exo::wayland::WaylandProtocolLogger::FormatMessage(type, message)}; |
| |
| TRACE_EVENT_INSTANT( |
| "exo", perfetto::DynamicString{msg[0]}, [&](perfetto::EventContext ctx) { |
| for (size_t i = 1; i < msg.size(); i++) { |
| std::string arg_name = base::StrCat({"arg", base::NumberToString(i)}); |
| ctx.AddDebugAnnotation(perfetto::DynamicString{arg_name}, msg[i]); |
| } |
| }); |
| } |
| |
| } // namespace |
| |
| namespace exo::wayland { |
| |
| WaylandProtocolLogger::WaylandProtocolLogger(struct wl_display* display) { |
| logger_.reset(wl_display_add_protocol_logger(display, handler_, nullptr)); |
| } |
| |
| // Complex class/struct needs an explicit out-of-line destructor. |
| WaylandProtocolLogger::~WaylandProtocolLogger() {} |
| |
| // static |
| std::vector<std::string> WaylandProtocolLogger::FormatMessage( |
| wl_protocol_logger_type type, |
| const wl_protocol_logger_message* message) { |
| std::vector<std::string> return_value; |
| return_value.push_back(base::StrCat( |
| {type == WL_PROTOCOL_LOGGER_EVENT ? "Sent event: " : "Received request: ", |
| wl_resource_get_class(message->resource), "@", |
| base::NumberToString(wl_resource_get_id(message->resource)), ".", |
| message->message->name})); |
| |
| const char* signature = message->message->signature; |
| for (int i = 0; i < message->arguments_count && *signature; i++) { |
| return_value.push_back(StringifyWaylandArgument( |
| message->message->types[i], message->arguments[i], &signature)); |
| } |
| return return_value; |
| } |
| |
| // static |
| void WaylandProtocolLogger::SetHandlerFuncForTesting( |
| wl_protocol_logger_func_t handler) { |
| handler_ = handler; |
| } |
| |
| // static |
| wl_protocol_logger_func_t WaylandProtocolLogger::handler_ = LogToPerfetto; |
| |
| void WaylandProtocolLogger::Deleter::operator()(wl_protocol_logger* logger) { |
| wl_protocol_logger_destroy(logger); |
| } |
| |
| } // namespace exo::wayland |