xref: /aosp_15_r20/external/perfetto/src/tracing/console_interceptor.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
1*6dbdd20aSAndroid Build Coastguard Worker /*
2*6dbdd20aSAndroid Build Coastguard Worker  * Copyright (C) 2020 The Android Open Source Project
3*6dbdd20aSAndroid Build Coastguard Worker  *
4*6dbdd20aSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*6dbdd20aSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*6dbdd20aSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*6dbdd20aSAndroid Build Coastguard Worker  *
8*6dbdd20aSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*6dbdd20aSAndroid Build Coastguard Worker  *
10*6dbdd20aSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*6dbdd20aSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*6dbdd20aSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6dbdd20aSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*6dbdd20aSAndroid Build Coastguard Worker  * limitations under the License.
15*6dbdd20aSAndroid Build Coastguard Worker  */
16*6dbdd20aSAndroid Build Coastguard Worker 
17*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/tracing/console_interceptor.h"
18*6dbdd20aSAndroid Build Coastguard Worker 
19*6dbdd20aSAndroid Build Coastguard Worker #include <stdarg.h>
20*6dbdd20aSAndroid Build Coastguard Worker 
21*6dbdd20aSAndroid Build Coastguard Worker #include <algorithm>
22*6dbdd20aSAndroid Build Coastguard Worker #include <cmath>
23*6dbdd20aSAndroid Build Coastguard Worker #include <optional>
24*6dbdd20aSAndroid Build Coastguard Worker #include <tuple>
25*6dbdd20aSAndroid Build Coastguard Worker 
26*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/file_utils.h"
27*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/hash.h"
28*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/scoped_file.h"
29*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/string_utils.h"
30*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/ext/base/utils.h"
31*6dbdd20aSAndroid Build Coastguard Worker #include "perfetto/tracing/internal/track_event_internal.h"
32*6dbdd20aSAndroid Build Coastguard Worker 
33*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/common/interceptor_descriptor.gen.h"
34*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/config/data_source_config.gen.h"
35*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/config/interceptor_config.gen.h"
36*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/config/interceptors/console_config.gen.h"
37*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
38*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/trace_packet.pbzero.h"
39*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
40*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/track_event/process_descriptor.pbzero.h"
41*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/track_event/thread_descriptor.pbzero.h"
42*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
43*6dbdd20aSAndroid Build Coastguard Worker #include "protos/perfetto/trace/track_event/track_event.pbzero.h"
44*6dbdd20aSAndroid Build Coastguard Worker 
45*6dbdd20aSAndroid Build Coastguard Worker namespace perfetto {
46*6dbdd20aSAndroid Build Coastguard Worker 
47*6dbdd20aSAndroid Build Coastguard Worker // sRGB color.
48*6dbdd20aSAndroid Build Coastguard Worker struct ConsoleColor {
49*6dbdd20aSAndroid Build Coastguard Worker   uint8_t r;
50*6dbdd20aSAndroid Build Coastguard Worker   uint8_t g;
51*6dbdd20aSAndroid Build Coastguard Worker   uint8_t b;
52*6dbdd20aSAndroid Build Coastguard Worker };
53*6dbdd20aSAndroid Build Coastguard Worker 
54*6dbdd20aSAndroid Build Coastguard Worker namespace {
55*6dbdd20aSAndroid Build Coastguard Worker 
56*6dbdd20aSAndroid Build Coastguard Worker int g_output_fd_for_testing;
57*6dbdd20aSAndroid Build Coastguard Worker 
58*6dbdd20aSAndroid Build Coastguard Worker // Google Turbo colormap.
59*6dbdd20aSAndroid Build Coastguard Worker constexpr std::array<ConsoleColor, 16> kTurboColors = {{
60*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x30, 0x12, 0x3b},
61*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x40, 0x40, 0xa1},
62*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x46, 0x6b, 0xe3},
63*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x41, 0x93, 0xfe},
64*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x28, 0xbb, 0xeb},
65*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x17, 0xdc, 0xc2},
66*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x32, 0xf1, 0x97},
67*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0x6d, 0xfd, 0x62},
68*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xa4, 0xfc, 0x3b},
69*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xcd, 0xeb, 0x34},
70*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xed, 0xcf, 0x39},
71*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xfd, 0xab, 0x33},
72*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xfa, 0x7d, 0x20},
73*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xea, 0x50, 0x0d},
74*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xd0, 0x2f, 0x04},
75*6dbdd20aSAndroid Build Coastguard Worker     ConsoleColor{0xa9, 0x15, 0x01},
76*6dbdd20aSAndroid Build Coastguard Worker }};
77*6dbdd20aSAndroid Build Coastguard Worker 
78*6dbdd20aSAndroid Build Coastguard Worker constexpr size_t kHueBits = 4;
79*6dbdd20aSAndroid Build Coastguard Worker constexpr uint32_t kMaxHue = kTurboColors.size() << kHueBits;
80*6dbdd20aSAndroid Build Coastguard Worker constexpr uint8_t kLightness = 128u;
81*6dbdd20aSAndroid Build Coastguard Worker constexpr ConsoleColor kWhiteColor{0xff, 0xff, 0xff};
82*6dbdd20aSAndroid Build Coastguard Worker 
83*6dbdd20aSAndroid Build Coastguard Worker const char kDim[] = "\x1b[90m";
84*6dbdd20aSAndroid Build Coastguard Worker const char kDefault[] = "\x1b[39m";
85*6dbdd20aSAndroid Build Coastguard Worker const char kReset[] = "\x1b[0m";
86*6dbdd20aSAndroid Build Coastguard Worker 
87*6dbdd20aSAndroid Build Coastguard Worker #define FMT_RGB_SET "\x1b[38;2;%d;%d;%dm"
88*6dbdd20aSAndroid Build Coastguard Worker #define FMT_RGB_SET_BG "\x1b[48;2;%d;%d;%dm"
89*6dbdd20aSAndroid Build Coastguard Worker 
Mix(ConsoleColor a,ConsoleColor b,uint8_t ratio)90*6dbdd20aSAndroid Build Coastguard Worker ConsoleColor Mix(ConsoleColor a, ConsoleColor b, uint8_t ratio) {
91*6dbdd20aSAndroid Build Coastguard Worker   return {
92*6dbdd20aSAndroid Build Coastguard Worker       static_cast<uint8_t>(a.r + (((b.r - a.r) * ratio) >> 8)),
93*6dbdd20aSAndroid Build Coastguard Worker       static_cast<uint8_t>(a.g + (((b.g - a.g) * ratio) >> 8)),
94*6dbdd20aSAndroid Build Coastguard Worker       static_cast<uint8_t>(a.b + (((b.b - a.b) * ratio) >> 8)),
95*6dbdd20aSAndroid Build Coastguard Worker   };
96*6dbdd20aSAndroid Build Coastguard Worker }
97*6dbdd20aSAndroid Build Coastguard Worker 
HueToRGB(uint32_t hue)98*6dbdd20aSAndroid Build Coastguard Worker ConsoleColor HueToRGB(uint32_t hue) {
99*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(hue < kMaxHue);
100*6dbdd20aSAndroid Build Coastguard Worker   uint32_t c1 = hue >> kHueBits;
101*6dbdd20aSAndroid Build Coastguard Worker   uint32_t c2 =
102*6dbdd20aSAndroid Build Coastguard Worker       std::min(static_cast<uint32_t>(kTurboColors.size() - 1), c1 + 1u);
103*6dbdd20aSAndroid Build Coastguard Worker   uint32_t ratio = hue & ((1 << kHueBits) - 1);
104*6dbdd20aSAndroid Build Coastguard Worker   return Mix(kTurboColors[c1], kTurboColors[c2],
105*6dbdd20aSAndroid Build Coastguard Worker              static_cast<uint8_t>(ratio | (ratio << kHueBits)));
106*6dbdd20aSAndroid Build Coastguard Worker }
107*6dbdd20aSAndroid Build Coastguard Worker 
CounterToHue(uint32_t counter)108*6dbdd20aSAndroid Build Coastguard Worker uint32_t CounterToHue(uint32_t counter) {
109*6dbdd20aSAndroid Build Coastguard Worker   // We split the hue space into 8 segments, reversing the order of bits so
110*6dbdd20aSAndroid Build Coastguard Worker   // successive counter values will be far from each other.
111*6dbdd20aSAndroid Build Coastguard Worker   uint32_t reversed =
112*6dbdd20aSAndroid Build Coastguard Worker       ((counter & 0x7) >> 2) | ((counter & 0x3)) | ((counter & 0x1) << 2);
113*6dbdd20aSAndroid Build Coastguard Worker   return reversed * kMaxHue / 8;
114*6dbdd20aSAndroid Build Coastguard Worker }
115*6dbdd20aSAndroid Build Coastguard Worker 
116*6dbdd20aSAndroid Build Coastguard Worker }  // namespace
117*6dbdd20aSAndroid Build Coastguard Worker 
118*6dbdd20aSAndroid Build Coastguard Worker class ConsoleInterceptor::Delegate : public TrackEventStateTracker::Delegate {
119*6dbdd20aSAndroid Build Coastguard Worker  public:
120*6dbdd20aSAndroid Build Coastguard Worker   explicit Delegate(InterceptorContext&);
121*6dbdd20aSAndroid Build Coastguard Worker   ~Delegate() override;
122*6dbdd20aSAndroid Build Coastguard Worker 
123*6dbdd20aSAndroid Build Coastguard Worker   TrackEventStateTracker::SessionState* GetSessionState() override;
124*6dbdd20aSAndroid Build Coastguard Worker   void OnTrackUpdated(TrackEventStateTracker::Track&) override;
125*6dbdd20aSAndroid Build Coastguard Worker   void OnTrackEvent(const TrackEventStateTracker::Track&,
126*6dbdd20aSAndroid Build Coastguard Worker                     const TrackEventStateTracker::ParsedTrackEvent&) override;
127*6dbdd20aSAndroid Build Coastguard Worker 
128*6dbdd20aSAndroid Build Coastguard Worker  private:
129*6dbdd20aSAndroid Build Coastguard Worker   using SelfHandle = LockedHandle<ConsoleInterceptor>;
130*6dbdd20aSAndroid Build Coastguard Worker 
131*6dbdd20aSAndroid Build Coastguard Worker   InterceptorContext& context_;
132*6dbdd20aSAndroid Build Coastguard Worker   std::optional<SelfHandle> locked_self_;
133*6dbdd20aSAndroid Build Coastguard Worker };
134*6dbdd20aSAndroid Build Coastguard Worker 
135*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::~ConsoleInterceptor() = default;
136*6dbdd20aSAndroid Build Coastguard Worker 
ThreadLocalState(ThreadLocalStateArgs & args)137*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::ThreadLocalState::ThreadLocalState(
138*6dbdd20aSAndroid Build Coastguard Worker     ThreadLocalStateArgs& args) {
139*6dbdd20aSAndroid Build Coastguard Worker   if (auto self = args.GetInterceptorLocked()) {
140*6dbdd20aSAndroid Build Coastguard Worker     start_time_ns = self->start_time_ns_;
141*6dbdd20aSAndroid Build Coastguard Worker     use_colors = self->use_colors_;
142*6dbdd20aSAndroid Build Coastguard Worker     fd = self->fd_;
143*6dbdd20aSAndroid Build Coastguard Worker   }
144*6dbdd20aSAndroid Build Coastguard Worker }
145*6dbdd20aSAndroid Build Coastguard Worker 
146*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::ThreadLocalState::~ThreadLocalState() = default;
147*6dbdd20aSAndroid Build Coastguard Worker 
Delegate(InterceptorContext & context)148*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::Delegate::Delegate(InterceptorContext& context)
149*6dbdd20aSAndroid Build Coastguard Worker     : context_(context) {}
150*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::Delegate::~Delegate() = default;
151*6dbdd20aSAndroid Build Coastguard Worker 
152*6dbdd20aSAndroid Build Coastguard Worker TrackEventStateTracker::SessionState*
GetSessionState()153*6dbdd20aSAndroid Build Coastguard Worker ConsoleInterceptor::Delegate::GetSessionState() {
154*6dbdd20aSAndroid Build Coastguard Worker   // When the session state is retrieved for the first time, it is cached (and
155*6dbdd20aSAndroid Build Coastguard Worker   // kept locked) until we return from OnTracePacket. This avoids having to lock
156*6dbdd20aSAndroid Build Coastguard Worker   // and unlock the instance multiple times per invocation.
157*6dbdd20aSAndroid Build Coastguard Worker   if (locked_self_.has_value())
158*6dbdd20aSAndroid Build Coastguard Worker     return &locked_self_.value()->session_state_;
159*6dbdd20aSAndroid Build Coastguard Worker   locked_self_ =
160*6dbdd20aSAndroid Build Coastguard Worker       std::make_optional<SelfHandle>(context_.GetInterceptorLocked());
161*6dbdd20aSAndroid Build Coastguard Worker   return &locked_self_.value()->session_state_;
162*6dbdd20aSAndroid Build Coastguard Worker }
163*6dbdd20aSAndroid Build Coastguard Worker 
OnTrackUpdated(TrackEventStateTracker::Track & track)164*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::Delegate::OnTrackUpdated(
165*6dbdd20aSAndroid Build Coastguard Worker     TrackEventStateTracker::Track& track) {
166*6dbdd20aSAndroid Build Coastguard Worker   auto track_color = HueToRGB(CounterToHue(track.index));
167*6dbdd20aSAndroid Build Coastguard Worker   std::array<char, 16> title;
168*6dbdd20aSAndroid Build Coastguard Worker   if (!track.name.empty()) {
169*6dbdd20aSAndroid Build Coastguard Worker     snprintf(title.data(), title.size(), "%s", track.name.c_str());
170*6dbdd20aSAndroid Build Coastguard Worker   } else if (track.pid && track.tid) {
171*6dbdd20aSAndroid Build Coastguard Worker     snprintf(title.data(), title.size(), "%u:%u",
172*6dbdd20aSAndroid Build Coastguard Worker              static_cast<uint32_t>(track.pid),
173*6dbdd20aSAndroid Build Coastguard Worker              static_cast<uint32_t>(track.tid));
174*6dbdd20aSAndroid Build Coastguard Worker   } else if (track.pid) {
175*6dbdd20aSAndroid Build Coastguard Worker     snprintf(title.data(), title.size(), "%" PRId64, track.pid);
176*6dbdd20aSAndroid Build Coastguard Worker   } else {
177*6dbdd20aSAndroid Build Coastguard Worker     snprintf(title.data(), title.size(), "%" PRIu64, track.uuid);
178*6dbdd20aSAndroid Build Coastguard Worker   }
179*6dbdd20aSAndroid Build Coastguard Worker   int title_width = static_cast<int>(title.size());
180*6dbdd20aSAndroid Build Coastguard Worker 
181*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context_.GetThreadLocalState();
182*6dbdd20aSAndroid Build Coastguard Worker   std::array<char, 128> message_prefix{};
183*6dbdd20aSAndroid Build Coastguard Worker   size_t written = 0;
184*6dbdd20aSAndroid Build Coastguard Worker   if (tls.use_colors) {
185*6dbdd20aSAndroid Build Coastguard Worker     written = base::SprintfTrunc(message_prefix.data(), message_prefix.size(),
186*6dbdd20aSAndroid Build Coastguard Worker                                  FMT_RGB_SET_BG " %s%s %-*.*s", track_color.r,
187*6dbdd20aSAndroid Build Coastguard Worker                                  track_color.g, track_color.b, kReset, kDim,
188*6dbdd20aSAndroid Build Coastguard Worker                                  title_width, title_width, title.data());
189*6dbdd20aSAndroid Build Coastguard Worker   } else {
190*6dbdd20aSAndroid Build Coastguard Worker     written = base::SprintfTrunc(message_prefix.data(), message_prefix.size(),
191*6dbdd20aSAndroid Build Coastguard Worker                                  "%-*.*s", title_width + 2, title_width,
192*6dbdd20aSAndroid Build Coastguard Worker                                  title.data());
193*6dbdd20aSAndroid Build Coastguard Worker   }
194*6dbdd20aSAndroid Build Coastguard Worker   track.user_data.assign(
195*6dbdd20aSAndroid Build Coastguard Worker       message_prefix.begin(),
196*6dbdd20aSAndroid Build Coastguard Worker       message_prefix.begin() + static_cast<ssize_t>(written));
197*6dbdd20aSAndroid Build Coastguard Worker }
198*6dbdd20aSAndroid Build Coastguard Worker 
OnTrackEvent(const TrackEventStateTracker::Track & track,const TrackEventStateTracker::ParsedTrackEvent & event)199*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::Delegate::OnTrackEvent(
200*6dbdd20aSAndroid Build Coastguard Worker     const TrackEventStateTracker::Track& track,
201*6dbdd20aSAndroid Build Coastguard Worker     const TrackEventStateTracker::ParsedTrackEvent& event) {
202*6dbdd20aSAndroid Build Coastguard Worker   // Start printing.
203*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context_.GetThreadLocalState();
204*6dbdd20aSAndroid Build Coastguard Worker   tls.buffer_pos = 0;
205*6dbdd20aSAndroid Build Coastguard Worker 
206*6dbdd20aSAndroid Build Coastguard Worker   // Print timestamp and track identifier.
207*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context_, kDim);
208*6dbdd20aSAndroid Build Coastguard Worker   Printf(context_, "[%7.3lf] %.*s",
209*6dbdd20aSAndroid Build Coastguard Worker          static_cast<double>(event.timestamp_ns - tls.start_time_ns) / 1e9,
210*6dbdd20aSAndroid Build Coastguard Worker          static_cast<int>(track.user_data.size()), track.user_data.data());
211*6dbdd20aSAndroid Build Coastguard Worker 
212*6dbdd20aSAndroid Build Coastguard Worker   // Print category.
213*6dbdd20aSAndroid Build Coastguard Worker   Printf(context_, "%-5.*s ",
214*6dbdd20aSAndroid Build Coastguard Worker          std::min(5, static_cast<int>(event.category.size)),
215*6dbdd20aSAndroid Build Coastguard Worker          event.category.data);
216*6dbdd20aSAndroid Build Coastguard Worker 
217*6dbdd20aSAndroid Build Coastguard Worker   // Print stack depth.
218*6dbdd20aSAndroid Build Coastguard Worker   for (size_t i = 0; i < event.stack_depth; i++) {
219*6dbdd20aSAndroid Build Coastguard Worker     Printf(context_, "-  ");
220*6dbdd20aSAndroid Build Coastguard Worker   }
221*6dbdd20aSAndroid Build Coastguard Worker 
222*6dbdd20aSAndroid Build Coastguard Worker   // Print slice name.
223*6dbdd20aSAndroid Build Coastguard Worker   auto slice_color = HueToRGB(event.name_hash % kMaxHue);
224*6dbdd20aSAndroid Build Coastguard Worker   auto highlight_color = Mix(slice_color, kWhiteColor, kLightness);
225*6dbdd20aSAndroid Build Coastguard Worker   if (event.track_event.type() == protos::pbzero::TrackEvent::TYPE_SLICE_END) {
226*6dbdd20aSAndroid Build Coastguard Worker     SetColor(context_, kDefault);
227*6dbdd20aSAndroid Build Coastguard Worker     Printf(context_, "} ");
228*6dbdd20aSAndroid Build Coastguard Worker   }
229*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context_, highlight_color);
230*6dbdd20aSAndroid Build Coastguard Worker   Printf(context_, "%.*s", static_cast<int>(event.name.size), event.name.data);
231*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context_, kReset);
232*6dbdd20aSAndroid Build Coastguard Worker   if (event.track_event.type() ==
233*6dbdd20aSAndroid Build Coastguard Worker       protos::pbzero::TrackEvent::TYPE_SLICE_BEGIN) {
234*6dbdd20aSAndroid Build Coastguard Worker     SetColor(context_, kDefault);
235*6dbdd20aSAndroid Build Coastguard Worker     Printf(context_, " {");
236*6dbdd20aSAndroid Build Coastguard Worker   }
237*6dbdd20aSAndroid Build Coastguard Worker 
238*6dbdd20aSAndroid Build Coastguard Worker   // Print annotations.
239*6dbdd20aSAndroid Build Coastguard Worker   if (event.track_event.has_debug_annotations()) {
240*6dbdd20aSAndroid Build Coastguard Worker     PrintDebugAnnotations(context_, event.track_event, slice_color,
241*6dbdd20aSAndroid Build Coastguard Worker                           highlight_color);
242*6dbdd20aSAndroid Build Coastguard Worker   }
243*6dbdd20aSAndroid Build Coastguard Worker 
244*6dbdd20aSAndroid Build Coastguard Worker   // TODO(skyostil): Print typed arguments.
245*6dbdd20aSAndroid Build Coastguard Worker 
246*6dbdd20aSAndroid Build Coastguard Worker   // Print duration for longer events.
247*6dbdd20aSAndroid Build Coastguard Worker   constexpr uint64_t kNsPerMillisecond = 1000000u;
248*6dbdd20aSAndroid Build Coastguard Worker   if (event.duration_ns >= 10 * kNsPerMillisecond) {
249*6dbdd20aSAndroid Build Coastguard Worker     SetColor(context_, kDim);
250*6dbdd20aSAndroid Build Coastguard Worker     Printf(context_, " +%" PRIu64 "ms", event.duration_ns / kNsPerMillisecond);
251*6dbdd20aSAndroid Build Coastguard Worker   }
252*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context_, kReset);
253*6dbdd20aSAndroid Build Coastguard Worker   Printf(context_, "\n");
254*6dbdd20aSAndroid Build Coastguard Worker }
255*6dbdd20aSAndroid Build Coastguard Worker 
256*6dbdd20aSAndroid Build Coastguard Worker // static
Register()257*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::Register() {
258*6dbdd20aSAndroid Build Coastguard Worker   perfetto::protos::gen::InterceptorDescriptor desc;
259*6dbdd20aSAndroid Build Coastguard Worker   desc.set_name("console");
260*6dbdd20aSAndroid Build Coastguard Worker   Interceptor<ConsoleInterceptor>::Register(desc);
261*6dbdd20aSAndroid Build Coastguard Worker }
262*6dbdd20aSAndroid Build Coastguard Worker 
263*6dbdd20aSAndroid Build Coastguard Worker // static
SetOutputFdForTesting(int fd)264*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::SetOutputFdForTesting(int fd) {
265*6dbdd20aSAndroid Build Coastguard Worker   g_output_fd_for_testing = fd;
266*6dbdd20aSAndroid Build Coastguard Worker }
267*6dbdd20aSAndroid Build Coastguard Worker 
OnSetup(const SetupArgs & args)268*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::OnSetup(const SetupArgs& args) {
269*6dbdd20aSAndroid Build Coastguard Worker   int fd = STDOUT_FILENO;
270*6dbdd20aSAndroid Build Coastguard Worker   if (g_output_fd_for_testing)
271*6dbdd20aSAndroid Build Coastguard Worker     fd = g_output_fd_for_testing;
272*6dbdd20aSAndroid Build Coastguard Worker #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) && \
273*6dbdd20aSAndroid Build Coastguard Worker     !PERFETTO_BUILDFLAG(PERFETTO_OS_WASM)
274*6dbdd20aSAndroid Build Coastguard Worker   bool use_colors = isatty(fd);
275*6dbdd20aSAndroid Build Coastguard Worker #else
276*6dbdd20aSAndroid Build Coastguard Worker   bool use_colors = false;
277*6dbdd20aSAndroid Build Coastguard Worker #endif
278*6dbdd20aSAndroid Build Coastguard Worker   const protos::gen::ConsoleConfig& config =
279*6dbdd20aSAndroid Build Coastguard Worker       args.config.interceptor_config().console_config();
280*6dbdd20aSAndroid Build Coastguard Worker   if (config.has_enable_colors())
281*6dbdd20aSAndroid Build Coastguard Worker     use_colors = config.enable_colors();
282*6dbdd20aSAndroid Build Coastguard Worker   if (config.output() == protos::gen::ConsoleConfig::OUTPUT_STDOUT) {
283*6dbdd20aSAndroid Build Coastguard Worker     fd = STDOUT_FILENO;
284*6dbdd20aSAndroid Build Coastguard Worker   } else if (config.output() == protos::gen::ConsoleConfig::OUTPUT_STDERR) {
285*6dbdd20aSAndroid Build Coastguard Worker     fd = STDERR_FILENO;
286*6dbdd20aSAndroid Build Coastguard Worker   }
287*6dbdd20aSAndroid Build Coastguard Worker   fd_ = fd;
288*6dbdd20aSAndroid Build Coastguard Worker   use_colors_ = use_colors;
289*6dbdd20aSAndroid Build Coastguard Worker }
290*6dbdd20aSAndroid Build Coastguard Worker 
OnStart(const StartArgs &)291*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::OnStart(const StartArgs&) {
292*6dbdd20aSAndroid Build Coastguard Worker   start_time_ns_ = internal::TrackEventInternal::GetTimeNs();
293*6dbdd20aSAndroid Build Coastguard Worker }
294*6dbdd20aSAndroid Build Coastguard Worker 
OnStop(const StopArgs &)295*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::OnStop(const StopArgs&) {}
296*6dbdd20aSAndroid Build Coastguard Worker 
297*6dbdd20aSAndroid Build Coastguard Worker // static
OnTracePacket(InterceptorContext context)298*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::OnTracePacket(InterceptorContext context) {
299*6dbdd20aSAndroid Build Coastguard Worker   {
300*6dbdd20aSAndroid Build Coastguard Worker     auto& tls = context.GetThreadLocalState();
301*6dbdd20aSAndroid Build Coastguard Worker     Delegate delegate(context);
302*6dbdd20aSAndroid Build Coastguard Worker     perfetto::protos::pbzero::TracePacket::Decoder packet(
303*6dbdd20aSAndroid Build Coastguard Worker         context.packet_data.data, context.packet_data.size);
304*6dbdd20aSAndroid Build Coastguard Worker     TrackEventStateTracker::ProcessTracePacket(delegate, tls.sequence_state,
305*6dbdd20aSAndroid Build Coastguard Worker                                                packet);
306*6dbdd20aSAndroid Build Coastguard Worker   }  // (Potential) lock scope for session state.
307*6dbdd20aSAndroid Build Coastguard Worker   Flush(context);
308*6dbdd20aSAndroid Build Coastguard Worker }
309*6dbdd20aSAndroid Build Coastguard Worker 
310*6dbdd20aSAndroid Build Coastguard Worker // static
Printf(InterceptorContext & context,const char * format,...)311*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::Printf(InterceptorContext& context,
312*6dbdd20aSAndroid Build Coastguard Worker                                 const char* format,
313*6dbdd20aSAndroid Build Coastguard Worker                                 ...) {
314*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context.GetThreadLocalState();
315*6dbdd20aSAndroid Build Coastguard Worker   ssize_t remaining = static_cast<ssize_t>(tls.message_buffer.size()) -
316*6dbdd20aSAndroid Build Coastguard Worker                       static_cast<ssize_t>(tls.buffer_pos);
317*6dbdd20aSAndroid Build Coastguard Worker   int written = 0;
318*6dbdd20aSAndroid Build Coastguard Worker   if (remaining > 0) {
319*6dbdd20aSAndroid Build Coastguard Worker     va_list args;
320*6dbdd20aSAndroid Build Coastguard Worker     va_start(args, format);
321*6dbdd20aSAndroid Build Coastguard Worker     written = vsnprintf(&tls.message_buffer[tls.buffer_pos],
322*6dbdd20aSAndroid Build Coastguard Worker                         static_cast<size_t>(remaining), format, args);
323*6dbdd20aSAndroid Build Coastguard Worker     PERFETTO_DCHECK(written >= 0);
324*6dbdd20aSAndroid Build Coastguard Worker     va_end(args);
325*6dbdd20aSAndroid Build Coastguard Worker   }
326*6dbdd20aSAndroid Build Coastguard Worker 
327*6dbdd20aSAndroid Build Coastguard Worker   // In case of buffer overflow, flush to the fd and write the latest message to
328*6dbdd20aSAndroid Build Coastguard Worker   // it directly instead.
329*6dbdd20aSAndroid Build Coastguard Worker   if (remaining <= 0 || written > remaining) {
330*6dbdd20aSAndroid Build Coastguard Worker     FILE* output = (tls.fd == STDOUT_FILENO) ? stdout : stderr;
331*6dbdd20aSAndroid Build Coastguard Worker     if (g_output_fd_for_testing) {
332*6dbdd20aSAndroid Build Coastguard Worker       output = fdopen(dup(g_output_fd_for_testing), "w");
333*6dbdd20aSAndroid Build Coastguard Worker     }
334*6dbdd20aSAndroid Build Coastguard Worker     Flush(context);
335*6dbdd20aSAndroid Build Coastguard Worker     va_list args;
336*6dbdd20aSAndroid Build Coastguard Worker     va_start(args, format);
337*6dbdd20aSAndroid Build Coastguard Worker     vfprintf(output, format, args);
338*6dbdd20aSAndroid Build Coastguard Worker     va_end(args);
339*6dbdd20aSAndroid Build Coastguard Worker     if (g_output_fd_for_testing) {
340*6dbdd20aSAndroid Build Coastguard Worker       fclose(output);
341*6dbdd20aSAndroid Build Coastguard Worker     }
342*6dbdd20aSAndroid Build Coastguard Worker   } else if (written > 0) {
343*6dbdd20aSAndroid Build Coastguard Worker     tls.buffer_pos += static_cast<size_t>(written);
344*6dbdd20aSAndroid Build Coastguard Worker   }
345*6dbdd20aSAndroid Build Coastguard Worker }
346*6dbdd20aSAndroid Build Coastguard Worker 
347*6dbdd20aSAndroid Build Coastguard Worker // static
Flush(InterceptorContext & context)348*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::Flush(InterceptorContext& context) {
349*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context.GetThreadLocalState();
350*6dbdd20aSAndroid Build Coastguard Worker   ssize_t res = base::WriteAll(tls.fd, &tls.message_buffer[0], tls.buffer_pos);
351*6dbdd20aSAndroid Build Coastguard Worker   PERFETTO_DCHECK(res == static_cast<ssize_t>(tls.buffer_pos));
352*6dbdd20aSAndroid Build Coastguard Worker   tls.buffer_pos = 0;
353*6dbdd20aSAndroid Build Coastguard Worker }
354*6dbdd20aSAndroid Build Coastguard Worker 
355*6dbdd20aSAndroid Build Coastguard Worker // static
SetColor(InterceptorContext & context,const ConsoleColor & color)356*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::SetColor(InterceptorContext& context,
357*6dbdd20aSAndroid Build Coastguard Worker                                   const ConsoleColor& color) {
358*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context.GetThreadLocalState();
359*6dbdd20aSAndroid Build Coastguard Worker   if (!tls.use_colors)
360*6dbdd20aSAndroid Build Coastguard Worker     return;
361*6dbdd20aSAndroid Build Coastguard Worker   Printf(context, FMT_RGB_SET, color.r, color.g, color.b);
362*6dbdd20aSAndroid Build Coastguard Worker }
363*6dbdd20aSAndroid Build Coastguard Worker 
364*6dbdd20aSAndroid Build Coastguard Worker // static
SetColor(InterceptorContext & context,const char * color)365*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::SetColor(InterceptorContext& context,
366*6dbdd20aSAndroid Build Coastguard Worker                                   const char* color) {
367*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context.GetThreadLocalState();
368*6dbdd20aSAndroid Build Coastguard Worker   if (!tls.use_colors)
369*6dbdd20aSAndroid Build Coastguard Worker     return;
370*6dbdd20aSAndroid Build Coastguard Worker   Printf(context, "%s", color);
371*6dbdd20aSAndroid Build Coastguard Worker }
372*6dbdd20aSAndroid Build Coastguard Worker 
373*6dbdd20aSAndroid Build Coastguard Worker // static
PrintDebugAnnotations(InterceptorContext & context,const protos::pbzero::TrackEvent_Decoder & track_event,const ConsoleColor & slice_color,const ConsoleColor & highlight_color)374*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::PrintDebugAnnotations(
375*6dbdd20aSAndroid Build Coastguard Worker     InterceptorContext& context,
376*6dbdd20aSAndroid Build Coastguard Worker     const protos::pbzero::TrackEvent_Decoder& track_event,
377*6dbdd20aSAndroid Build Coastguard Worker     const ConsoleColor& slice_color,
378*6dbdd20aSAndroid Build Coastguard Worker     const ConsoleColor& highlight_color) {
379*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context, slice_color);
380*6dbdd20aSAndroid Build Coastguard Worker   Printf(context, "(");
381*6dbdd20aSAndroid Build Coastguard Worker 
382*6dbdd20aSAndroid Build Coastguard Worker   bool is_first = true;
383*6dbdd20aSAndroid Build Coastguard Worker   for (auto it = track_event.debug_annotations(); it; it++) {
384*6dbdd20aSAndroid Build Coastguard Worker     perfetto::protos::pbzero::DebugAnnotation::Decoder annotation(*it);
385*6dbdd20aSAndroid Build Coastguard Worker     SetColor(context, slice_color);
386*6dbdd20aSAndroid Build Coastguard Worker     if (!is_first)
387*6dbdd20aSAndroid Build Coastguard Worker       Printf(context, ", ");
388*6dbdd20aSAndroid Build Coastguard Worker 
389*6dbdd20aSAndroid Build Coastguard Worker     PrintDebugAnnotationName(context, annotation);
390*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, ":");
391*6dbdd20aSAndroid Build Coastguard Worker 
392*6dbdd20aSAndroid Build Coastguard Worker     SetColor(context, highlight_color);
393*6dbdd20aSAndroid Build Coastguard Worker     PrintDebugAnnotationValue(context, annotation);
394*6dbdd20aSAndroid Build Coastguard Worker 
395*6dbdd20aSAndroid Build Coastguard Worker     is_first = false;
396*6dbdd20aSAndroid Build Coastguard Worker   }
397*6dbdd20aSAndroid Build Coastguard Worker   SetColor(context, slice_color);
398*6dbdd20aSAndroid Build Coastguard Worker   Printf(context, ")");
399*6dbdd20aSAndroid Build Coastguard Worker }
400*6dbdd20aSAndroid Build Coastguard Worker 
401*6dbdd20aSAndroid Build Coastguard Worker // static
PrintDebugAnnotationName(InterceptorContext & context,const perfetto::protos::pbzero::DebugAnnotation::Decoder & annotation)402*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::PrintDebugAnnotationName(
403*6dbdd20aSAndroid Build Coastguard Worker     InterceptorContext& context,
404*6dbdd20aSAndroid Build Coastguard Worker     const perfetto::protos::pbzero::DebugAnnotation::Decoder& annotation) {
405*6dbdd20aSAndroid Build Coastguard Worker   auto& tls = context.GetThreadLocalState();
406*6dbdd20aSAndroid Build Coastguard Worker   protozero::ConstChars name{};
407*6dbdd20aSAndroid Build Coastguard Worker   if (annotation.name_iid()) {
408*6dbdd20aSAndroid Build Coastguard Worker     name.data =
409*6dbdd20aSAndroid Build Coastguard Worker         tls.sequence_state.debug_annotation_names[annotation.name_iid()].data();
410*6dbdd20aSAndroid Build Coastguard Worker     name.size =
411*6dbdd20aSAndroid Build Coastguard Worker         tls.sequence_state.debug_annotation_names[annotation.name_iid()].size();
412*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_name()) {
413*6dbdd20aSAndroid Build Coastguard Worker     name.data = annotation.name().data;
414*6dbdd20aSAndroid Build Coastguard Worker     name.size = annotation.name().size;
415*6dbdd20aSAndroid Build Coastguard Worker   }
416*6dbdd20aSAndroid Build Coastguard Worker   Printf(context, "%.*s", static_cast<int>(name.size), name.data);
417*6dbdd20aSAndroid Build Coastguard Worker }
418*6dbdd20aSAndroid Build Coastguard Worker 
419*6dbdd20aSAndroid Build Coastguard Worker // static
PrintDebugAnnotationValue(InterceptorContext & context,const perfetto::protos::pbzero::DebugAnnotation::Decoder & annotation)420*6dbdd20aSAndroid Build Coastguard Worker void ConsoleInterceptor::PrintDebugAnnotationValue(
421*6dbdd20aSAndroid Build Coastguard Worker     InterceptorContext& context,
422*6dbdd20aSAndroid Build Coastguard Worker     const perfetto::protos::pbzero::DebugAnnotation::Decoder& annotation) {
423*6dbdd20aSAndroid Build Coastguard Worker   if (annotation.has_bool_value()) {
424*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%s", annotation.bool_value() ? "true" : "false");
425*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_uint_value()) {
426*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%" PRIu64, annotation.uint_value());
427*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_int_value()) {
428*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%" PRId64, annotation.int_value());
429*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_double_value()) {
430*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%f", annotation.double_value());
431*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_string_value()) {
432*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%.*s", static_cast<int>(annotation.string_value().size),
433*6dbdd20aSAndroid Build Coastguard Worker            annotation.string_value().data);
434*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_pointer_value()) {
435*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%p", reinterpret_cast<void*>(annotation.pointer_value()));
436*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_legacy_json_value()) {
437*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "%.*s",
438*6dbdd20aSAndroid Build Coastguard Worker            static_cast<int>(annotation.legacy_json_value().size),
439*6dbdd20aSAndroid Build Coastguard Worker            annotation.legacy_json_value().data);
440*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_dict_entries()) {
441*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "{");
442*6dbdd20aSAndroid Build Coastguard Worker     bool is_first = true;
443*6dbdd20aSAndroid Build Coastguard Worker     for (auto it = annotation.dict_entries(); it; ++it) {
444*6dbdd20aSAndroid Build Coastguard Worker       if (!is_first)
445*6dbdd20aSAndroid Build Coastguard Worker         Printf(context, ", ");
446*6dbdd20aSAndroid Build Coastguard Worker       perfetto::protos::pbzero::DebugAnnotation::Decoder key_value(*it);
447*6dbdd20aSAndroid Build Coastguard Worker       PrintDebugAnnotationName(context, key_value);
448*6dbdd20aSAndroid Build Coastguard Worker       Printf(context, ":");
449*6dbdd20aSAndroid Build Coastguard Worker       PrintDebugAnnotationValue(context, key_value);
450*6dbdd20aSAndroid Build Coastguard Worker       is_first = false;
451*6dbdd20aSAndroid Build Coastguard Worker     }
452*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "}");
453*6dbdd20aSAndroid Build Coastguard Worker   } else if (annotation.has_array_values()) {
454*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "[");
455*6dbdd20aSAndroid Build Coastguard Worker     bool is_first = true;
456*6dbdd20aSAndroid Build Coastguard Worker     for (auto it = annotation.array_values(); it; ++it) {
457*6dbdd20aSAndroid Build Coastguard Worker       if (!is_first)
458*6dbdd20aSAndroid Build Coastguard Worker         Printf(context, ", ");
459*6dbdd20aSAndroid Build Coastguard Worker       perfetto::protos::pbzero::DebugAnnotation::Decoder key_value(*it);
460*6dbdd20aSAndroid Build Coastguard Worker       PrintDebugAnnotationValue(context, key_value);
461*6dbdd20aSAndroid Build Coastguard Worker       is_first = false;
462*6dbdd20aSAndroid Build Coastguard Worker     }
463*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "]");
464*6dbdd20aSAndroid Build Coastguard Worker   } else {
465*6dbdd20aSAndroid Build Coastguard Worker     Printf(context, "{}");
466*6dbdd20aSAndroid Build Coastguard Worker   }
467*6dbdd20aSAndroid Build Coastguard Worker }
468*6dbdd20aSAndroid Build Coastguard Worker 
469*6dbdd20aSAndroid Build Coastguard Worker }  // namespace perfetto
470