xref: /aosp_15_r20/external/webrtc/rtc_base/event_tracer.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 #include "rtc_base/event_tracer.h"
11 
12 #include <inttypes.h>
13 #include <stdint.h>
14 #include <stdio.h>
15 #include <string.h>
16 
17 #include <atomic>
18 #include <string>
19 #include <vector>
20 
21 #include "absl/strings/string_view.h"
22 #include "api/sequence_checker.h"
23 #include "rtc_base/checks.h"
24 #include "rtc_base/event.h"
25 #include "rtc_base/logging.h"
26 #include "rtc_base/platform_thread.h"
27 #include "rtc_base/platform_thread_types.h"
28 #include "rtc_base/synchronization/mutex.h"
29 #include "rtc_base/thread_annotations.h"
30 #include "rtc_base/time_utils.h"
31 #include "rtc_base/trace_event.h"
32 
33 // This is a guesstimate that should be enough in most cases.
34 static const size_t kEventLoggerArgsStrBufferInitialSize = 256;
35 static const size_t kTraceArgBufferLength = 32;
36 
37 namespace webrtc {
38 
39 namespace {
40 
41 GetCategoryEnabledPtr g_get_category_enabled_ptr = nullptr;
42 AddTraceEventPtr g_add_trace_event_ptr = nullptr;
43 
44 }  // namespace
45 
SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,AddTraceEventPtr add_trace_event_ptr)46 void SetupEventTracer(GetCategoryEnabledPtr get_category_enabled_ptr,
47                       AddTraceEventPtr add_trace_event_ptr) {
48   g_get_category_enabled_ptr = get_category_enabled_ptr;
49   g_add_trace_event_ptr = add_trace_event_ptr;
50 }
51 
GetCategoryEnabled(const char * name)52 const unsigned char* EventTracer::GetCategoryEnabled(const char* name) {
53   if (g_get_category_enabled_ptr)
54     return g_get_category_enabled_ptr(name);
55 
56   // A string with null terminator means category is disabled.
57   return reinterpret_cast<const unsigned char*>("\0");
58 }
59 
60 // Arguments to this function (phase, etc.) are as defined in
61 // webrtc/rtc_base/trace_event.h.
AddTraceEvent(char phase,const unsigned char * category_enabled,const char * name,unsigned long long id,int num_args,const char ** arg_names,const unsigned char * arg_types,const unsigned long long * arg_values,unsigned char flags)62 void EventTracer::AddTraceEvent(char phase,
63                                 const unsigned char* category_enabled,
64                                 const char* name,
65                                 unsigned long long id,
66                                 int num_args,
67                                 const char** arg_names,
68                                 const unsigned char* arg_types,
69                                 const unsigned long long* arg_values,
70                                 unsigned char flags) {
71   if (g_add_trace_event_ptr) {
72     g_add_trace_event_ptr(phase, category_enabled, name, id, num_args,
73                           arg_names, arg_types, arg_values, flags);
74   }
75 }
76 
77 }  // namespace webrtc
78 
79 namespace rtc {
80 namespace tracing {
81 namespace {
82 
83 // Atomic-int fast path for avoiding logging when disabled.
84 static std::atomic<int> g_event_logging_active(0);
85 
86 // TODO(pbos): Log metadata for all threads, etc.
87 class EventLogger final {
88  public:
~EventLogger()89   ~EventLogger() { RTC_DCHECK(thread_checker_.IsCurrent()); }
90 
AddTraceEvent(const char * name,const unsigned char * category_enabled,char phase,int num_args,const char ** arg_names,const unsigned char * arg_types,const unsigned long long * arg_values,uint64_t timestamp,int pid,rtc::PlatformThreadId thread_id)91   void AddTraceEvent(const char* name,
92                      const unsigned char* category_enabled,
93                      char phase,
94                      int num_args,
95                      const char** arg_names,
96                      const unsigned char* arg_types,
97                      const unsigned long long* arg_values,
98                      uint64_t timestamp,
99                      int pid,
100                      rtc::PlatformThreadId thread_id) {
101     std::vector<TraceArg> args(num_args);
102     for (int i = 0; i < num_args; ++i) {
103       TraceArg& arg = args[i];
104       arg.name = arg_names[i];
105       arg.type = arg_types[i];
106       arg.value.as_uint = arg_values[i];
107 
108       // Value is a pointer to a temporary string, so we have to make a copy.
109       if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
110         // Space for the string and for the terminating null character.
111         size_t str_length = strlen(arg.value.as_string) + 1;
112         char* str_copy = new char[str_length];
113         memcpy(str_copy, arg.value.as_string, str_length);
114         arg.value.as_string = str_copy;
115       }
116     }
117     webrtc::MutexLock lock(&mutex_);
118     trace_events_.push_back(
119         {name, category_enabled, phase, args, timestamp, 1, thread_id});
120   }
121 
122   // The TraceEvent format is documented here:
123   // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview
Log()124   void Log() {
125     RTC_DCHECK(output_file_);
126     static constexpr webrtc::TimeDelta kLoggingInterval =
127         webrtc::TimeDelta::Millis(100);
128     fprintf(output_file_, "{ \"traceEvents\": [\n");
129     bool has_logged_event = false;
130     while (true) {
131       bool shutting_down = shutdown_event_.Wait(kLoggingInterval);
132       std::vector<TraceEvent> events;
133       {
134         webrtc::MutexLock lock(&mutex_);
135         trace_events_.swap(events);
136       }
137       std::string args_str;
138       args_str.reserve(kEventLoggerArgsStrBufferInitialSize);
139       for (TraceEvent& e : events) {
140         args_str.clear();
141         if (!e.args.empty()) {
142           args_str += ", \"args\": {";
143           bool is_first_argument = true;
144           for (TraceArg& arg : e.args) {
145             if (!is_first_argument)
146               args_str += ",";
147             is_first_argument = false;
148             args_str += " \"";
149             args_str += arg.name;
150             args_str += "\": ";
151             args_str += TraceArgValueAsString(arg);
152 
153             // Delete our copy of the string.
154             if (arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
155               delete[] arg.value.as_string;
156               arg.value.as_string = nullptr;
157             }
158           }
159           args_str += " }";
160         }
161         fprintf(output_file_,
162                 "%s{ \"name\": \"%s\""
163                 ", \"cat\": \"%s\""
164                 ", \"ph\": \"%c\""
165                 ", \"ts\": %" PRIu64
166                 ", \"pid\": %d"
167 #if defined(WEBRTC_WIN)
168                 ", \"tid\": %lu"
169 #else
170                 ", \"tid\": %d"
171 #endif  // defined(WEBRTC_WIN)
172                 "%s"
173                 "}\n",
174                 has_logged_event ? "," : " ", e.name, e.category_enabled,
175                 e.phase, e.timestamp, e.pid, e.tid, args_str.c_str());
176         has_logged_event = true;
177       }
178       if (shutting_down)
179         break;
180     }
181     fprintf(output_file_, "]}\n");
182     if (output_file_owned_)
183       fclose(output_file_);
184     output_file_ = nullptr;
185   }
186 
Start(FILE * file,bool owned)187   void Start(FILE* file, bool owned) {
188     RTC_DCHECK(thread_checker_.IsCurrent());
189     RTC_DCHECK(file);
190     RTC_DCHECK(!output_file_);
191     output_file_ = file;
192     output_file_owned_ = owned;
193     {
194       webrtc::MutexLock lock(&mutex_);
195       // Since the atomic fast-path for adding events to the queue can be
196       // bypassed while the logging thread is shutting down there may be some
197       // stale events in the queue, hence the vector needs to be cleared to not
198       // log events from a previous logging session (which may be days old).
199       trace_events_.clear();
200     }
201     // Enable event logging (fast-path). This should be disabled since starting
202     // shouldn't be done twice.
203     int zero = 0;
204     RTC_CHECK(g_event_logging_active.compare_exchange_strong(zero, 1));
205 
206     // Finally start, everything should be set up now.
207     logging_thread_ =
208         PlatformThread::SpawnJoinable([this] { Log(); }, "EventTracingThread");
209     TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Start");
210   }
211 
Stop()212   void Stop() {
213     RTC_DCHECK(thread_checker_.IsCurrent());
214     TRACE_EVENT_INSTANT0("webrtc", "EventLogger::Stop");
215     // Try to stop. Abort if we're not currently logging.
216     int one = 1;
217     if (g_event_logging_active.compare_exchange_strong(one, 0))
218       return;
219 
220     // Wake up logging thread to finish writing.
221     shutdown_event_.Set();
222     // Join the logging thread.
223     logging_thread_.Finalize();
224   }
225 
226  private:
227   struct TraceArg {
228     const char* name;
229     unsigned char type;
230     // Copied from webrtc/rtc_base/trace_event.h TraceValueUnion.
231     union TraceArgValue {
232       bool as_bool;
233       unsigned long long as_uint;
234       long long as_int;
235       double as_double;
236       const void* as_pointer;
237       const char* as_string;
238     } value;
239 
240     // Assert that the size of the union is equal to the size of the as_uint
241     // field since we are assigning to arbitrary types using it.
242     static_assert(sizeof(TraceArgValue) == sizeof(unsigned long long),
243                   "Size of TraceArg value union is not equal to the size of "
244                   "the uint field of that union.");
245   };
246 
247   struct TraceEvent {
248     const char* name;
249     const unsigned char* category_enabled;
250     char phase;
251     std::vector<TraceArg> args;
252     uint64_t timestamp;
253     int pid;
254     rtc::PlatformThreadId tid;
255   };
256 
TraceArgValueAsString(TraceArg arg)257   static std::string TraceArgValueAsString(TraceArg arg) {
258     std::string output;
259 
260     if (arg.type == TRACE_VALUE_TYPE_STRING ||
261         arg.type == TRACE_VALUE_TYPE_COPY_STRING) {
262       // Space for every character to be an espaced character + two for
263       // quatation marks.
264       output.reserve(strlen(arg.value.as_string) * 2 + 2);
265       output += '\"';
266       const char* c = arg.value.as_string;
267       do {
268         if (*c == '"' || *c == '\\') {
269           output += '\\';
270           output += *c;
271         } else {
272           output += *c;
273         }
274       } while (*++c);
275       output += '\"';
276     } else {
277       output.resize(kTraceArgBufferLength);
278       size_t print_length = 0;
279       switch (arg.type) {
280         case TRACE_VALUE_TYPE_BOOL:
281           if (arg.value.as_bool) {
282             strcpy(&output[0], "true");
283             print_length = 4;
284           } else {
285             strcpy(&output[0], "false");
286             print_length = 5;
287           }
288           break;
289         case TRACE_VALUE_TYPE_UINT:
290           print_length = snprintf(&output[0], kTraceArgBufferLength, "%llu",
291                                   arg.value.as_uint);
292           break;
293         case TRACE_VALUE_TYPE_INT:
294           print_length = snprintf(&output[0], kTraceArgBufferLength, "%lld",
295                                   arg.value.as_int);
296           break;
297         case TRACE_VALUE_TYPE_DOUBLE:
298           print_length = snprintf(&output[0], kTraceArgBufferLength, "%f",
299                                   arg.value.as_double);
300           break;
301         case TRACE_VALUE_TYPE_POINTER:
302           print_length = snprintf(&output[0], kTraceArgBufferLength, "\"%p\"",
303                                   arg.value.as_pointer);
304           break;
305       }
306       size_t output_length = print_length < kTraceArgBufferLength
307                                  ? print_length
308                                  : kTraceArgBufferLength - 1;
309       // This will hopefully be very close to nop. On most implementations, it
310       // just writes null byte and sets the length field of the string.
311       output.resize(output_length);
312     }
313 
314     return output;
315   }
316 
317   webrtc::Mutex mutex_;
318   std::vector<TraceEvent> trace_events_ RTC_GUARDED_BY(mutex_);
319   rtc::PlatformThread logging_thread_;
320   rtc::Event shutdown_event_;
321   webrtc::SequenceChecker thread_checker_;
322   FILE* output_file_ = nullptr;
323   bool output_file_owned_ = false;
324 };
325 
326 static std::atomic<EventLogger*> g_event_logger(nullptr);
327 static const char* const kDisabledTracePrefix = TRACE_DISABLED_BY_DEFAULT("");
InternalGetCategoryEnabled(const char * name)328 const unsigned char* InternalGetCategoryEnabled(const char* name) {
329   const char* prefix_ptr = &kDisabledTracePrefix[0];
330   const char* name_ptr = name;
331   // Check whether name contains the default-disabled prefix.
332   while (*prefix_ptr == *name_ptr && *prefix_ptr != '\0') {
333     ++prefix_ptr;
334     ++name_ptr;
335   }
336   return reinterpret_cast<const unsigned char*>(*prefix_ptr == '\0' ? ""
337                                                                     : name);
338 }
339 
InternalEnableAllCategories(const char * name)340 const unsigned char* InternalEnableAllCategories(const char* name) {
341   return reinterpret_cast<const unsigned char*>(name);
342 }
343 
InternalAddTraceEvent(char phase,const unsigned char * category_enabled,const char * name,unsigned long long id,int num_args,const char ** arg_names,const unsigned char * arg_types,const unsigned long long * arg_values,unsigned char flags)344 void InternalAddTraceEvent(char phase,
345                            const unsigned char* category_enabled,
346                            const char* name,
347                            unsigned long long id,
348                            int num_args,
349                            const char** arg_names,
350                            const unsigned char* arg_types,
351                            const unsigned long long* arg_values,
352                            unsigned char flags) {
353   // Fast path for when event tracing is inactive.
354   if (g_event_logging_active.load() == 0)
355     return;
356 
357   g_event_logger.load()->AddTraceEvent(
358       name, category_enabled, phase, num_args, arg_names, arg_types, arg_values,
359       rtc::TimeMicros(), 1, rtc::CurrentThreadId());
360 }
361 
362 }  // namespace
363 
SetupInternalTracer(bool enable_all_categories)364 void SetupInternalTracer(bool enable_all_categories) {
365   EventLogger* null_logger = nullptr;
366   RTC_CHECK(
367       g_event_logger.compare_exchange_strong(null_logger, new EventLogger()));
368   webrtc::SetupEventTracer(enable_all_categories ? InternalEnableAllCategories
369                                                  : InternalGetCategoryEnabled,
370                            InternalAddTraceEvent);
371 }
372 
StartInternalCaptureToFile(FILE * file)373 void StartInternalCaptureToFile(FILE* file) {
374   EventLogger* event_logger = g_event_logger.load();
375   if (event_logger) {
376     event_logger->Start(file, false);
377   }
378 }
379 
StartInternalCapture(absl::string_view filename)380 bool StartInternalCapture(absl::string_view filename) {
381   EventLogger* event_logger = g_event_logger.load();
382   if (!event_logger)
383     return false;
384 
385   FILE* file = fopen(std::string(filename).c_str(), "w");
386   if (!file) {
387     RTC_LOG(LS_ERROR) << "Failed to open trace file '" << filename
388                       << "' for writing.";
389     return false;
390   }
391   event_logger->Start(file, true);
392   return true;
393 }
394 
StopInternalCapture()395 void StopInternalCapture() {
396   EventLogger* event_logger = g_event_logger.load();
397   if (event_logger) {
398     event_logger->Stop();
399   }
400 }
401 
ShutdownInternalTracer()402 void ShutdownInternalTracer() {
403   StopInternalCapture();
404   EventLogger* old_logger = g_event_logger.load(std::memory_order_acquire);
405   RTC_DCHECK(old_logger);
406   RTC_CHECK(g_event_logger.compare_exchange_strong(old_logger, nullptr));
407   delete old_logger;
408   webrtc::SetupEventTracer(nullptr, nullptr);
409 }
410 
411 }  // namespace tracing
412 }  // namespace rtc
413