1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
// 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() = default;
// 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
|