File: wayland_protocol_logger.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (157 lines) | stat: -rw-r--r-- 5,387 bytes parent folder | download | duplicates (6)
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