1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "perfetto/tracing/internal/track_event_internal.h"
18 
19 #include "perfetto/base/proc_utils.h"
20 #include "perfetto/base/thread_utils.h"
21 #include "perfetto/base/time.h"
22 #include "perfetto/tracing/core/data_source_config.h"
23 #include "perfetto/tracing/internal/track_event_interned_fields.h"
24 #include "perfetto/tracing/track_event.h"
25 #include "perfetto/tracing/track_event_category_registry.h"
26 #include "perfetto/tracing/track_event_interned_data_index.h"
27 #include "protos/perfetto/common/data_source_descriptor.gen.h"
28 #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
29 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
30 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
31 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
32 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
33 
34 namespace perfetto {
35 
36 TrackEventSessionObserver::~TrackEventSessionObserver() = default;
OnSetup(const DataSourceBase::SetupArgs &)37 void TrackEventSessionObserver::OnSetup(const DataSourceBase::SetupArgs&) {}
OnStart(const DataSourceBase::StartArgs &)38 void TrackEventSessionObserver::OnStart(const DataSourceBase::StartArgs&) {}
OnStop(const DataSourceBase::StopArgs &)39 void TrackEventSessionObserver::OnStop(const DataSourceBase::StopArgs&) {}
40 
41 namespace internal {
42 
43 BaseTrackEventInternedDataIndex::~BaseTrackEventInternedDataIndex() = default;
44 
45 namespace {
46 
47 std::atomic<perfetto::base::PlatformThreadId> g_main_thread;
48 static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
49 static constexpr const char kSlowTag[] = "slow";
50 static constexpr const char kDebugTag[] = "debug";
51 
ForEachObserver(std::function<bool (TrackEventSessionObserver * &)> callback)52 void ForEachObserver(
53     std::function<bool(TrackEventSessionObserver*&)> callback) {
54   // Session observers, shared by all track event data source instances.
55   static constexpr int kMaxObservers = 8;
56   static std::recursive_mutex* mutex = new std::recursive_mutex{};  // Leaked.
57   static std::array<TrackEventSessionObserver*, kMaxObservers> observers{};
58   std::unique_lock<std::recursive_mutex> lock(*mutex);
59   for (auto& o : observers) {
60     if (!callback(o))
61       break;
62   }
63 }
64 
65 enum class MatchType { kExact, kPattern };
66 
NameMatchesPattern(const std::string & pattern,const std::string & name,MatchType match_type)67 bool NameMatchesPattern(const std::string& pattern,
68                         const std::string& name,
69                         MatchType match_type) {
70   // To avoid pulling in all of std::regex, for now we only support a single "*"
71   // wildcard at the end of the pattern.
72   size_t i = pattern.find('*');
73   if (i != std::string::npos) {
74     PERFETTO_DCHECK(i == pattern.size() - 1);
75     if (match_type != MatchType::kPattern)
76       return false;
77     return name.substr(0, i) == pattern.substr(0, i);
78   }
79   return name == pattern;
80 }
81 
NameMatchesPatternList(const std::vector<std::string> & patterns,const std::string & name,MatchType match_type)82 bool NameMatchesPatternList(const std::vector<std::string>& patterns,
83                             const std::string& name,
84                             MatchType match_type) {
85   for (const auto& pattern : patterns) {
86     if (NameMatchesPattern(pattern, name, match_type))
87       return true;
88   }
89   return false;
90 }
91 
92 }  // namespace
93 
94 // static
95 const Track TrackEventInternal::kDefaultTrack{};
96 
97 // static
98 std::atomic<int> TrackEventInternal::session_count_{};
99 
100 // static
Initialize(const TrackEventCategoryRegistry & registry,bool (* register_data_source)(const DataSourceDescriptor &))101 bool TrackEventInternal::Initialize(
102     const TrackEventCategoryRegistry& registry,
103     bool (*register_data_source)(const DataSourceDescriptor&)) {
104   if (!g_main_thread)
105     g_main_thread = perfetto::base::GetThreadId();
106 
107   DataSourceDescriptor dsd;
108   dsd.set_name("track_event");
109 
110   protozero::HeapBuffered<protos::pbzero::TrackEventDescriptor> ted;
111   for (size_t i = 0; i < registry.category_count(); i++) {
112     auto category = registry.GetCategory(i);
113     // Don't register group categories.
114     if (category->IsGroup())
115       continue;
116     auto cat = ted->add_available_categories();
117     cat->set_name(category->name);
118     if (category->description)
119       cat->set_description(category->description);
120     for (const auto& tag : category->tags) {
121       if (tag)
122         cat->add_tags(tag);
123     }
124     // Disabled-by-default categories get a "slow" tag.
125     if (!strncmp(category->name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)))
126       cat->add_tags(kSlowTag);
127   }
128   dsd.set_track_event_descriptor_raw(ted.SerializeAsString());
129 
130   return register_data_source(dsd);
131 }
132 
133 // static
AddSessionObserver(TrackEventSessionObserver * observer)134 bool TrackEventInternal::AddSessionObserver(
135     TrackEventSessionObserver* observer) {
136   bool result = false;
137   ForEachObserver([&](TrackEventSessionObserver*& o) {
138     if (!o) {
139       o = observer;
140       result = true;
141       return false;
142     }
143     return true;
144   });
145   return result;
146 }
147 
148 // static
RemoveSessionObserver(TrackEventSessionObserver * observer)149 void TrackEventInternal::RemoveSessionObserver(
150     TrackEventSessionObserver* observer) {
151   ForEachObserver([&](TrackEventSessionObserver*& o) {
152     if (o == observer) {
153       o = nullptr;
154       return false;
155     }
156     return true;
157   });
158 }
159 
160 // static
EnableTracing(const TrackEventCategoryRegistry & registry,const protos::gen::TrackEventConfig & config,const DataSourceBase::SetupArgs & args)161 void TrackEventInternal::EnableTracing(
162     const TrackEventCategoryRegistry& registry,
163     const protos::gen::TrackEventConfig& config,
164     const DataSourceBase::SetupArgs& args) {
165   for (size_t i = 0; i < registry.category_count(); i++) {
166     if (IsCategoryEnabled(registry, config, *registry.GetCategory(i)))
167       registry.EnableCategoryForInstance(i, args.internal_instance_index);
168   }
169   ForEachObserver([&](TrackEventSessionObserver*& o) {
170     if (o)
171       o->OnSetup(args);
172     return true;
173   });
174 }
175 
176 // static
OnStart(const DataSourceBase::StartArgs & args)177 void TrackEventInternal::OnStart(const DataSourceBase::StartArgs& args) {
178   session_count_.fetch_add(1);
179   ForEachObserver([&](TrackEventSessionObserver*& o) {
180     if (o)
181       o->OnStart(args);
182     return true;
183   });
184 }
185 
186 // static
DisableTracing(const TrackEventCategoryRegistry & registry,const DataSourceBase::StopArgs & args)187 void TrackEventInternal::DisableTracing(
188     const TrackEventCategoryRegistry& registry,
189     const DataSourceBase::StopArgs& args) {
190   ForEachObserver([&](TrackEventSessionObserver*& o) {
191     if (o)
192       o->OnStop(args);
193     return true;
194   });
195   for (size_t i = 0; i < registry.category_count(); i++)
196     registry.DisableCategoryForInstance(i, args.internal_instance_index);
197 }
198 
199 // static
IsCategoryEnabled(const TrackEventCategoryRegistry & registry,const protos::gen::TrackEventConfig & config,const Category & category)200 bool TrackEventInternal::IsCategoryEnabled(
201     const TrackEventCategoryRegistry& registry,
202     const protos::gen::TrackEventConfig& config,
203     const Category& category) {
204   // If this is a group category, check if any of its constituent categories are
205   // enabled. If so, then this one is enabled too.
206   if (category.IsGroup()) {
207     bool result = false;
208     category.ForEachGroupMember([&](const char* member_name, size_t name_size) {
209       for (size_t i = 0; i < registry.category_count(); i++) {
210         const auto ref_category = registry.GetCategory(i);
211         // Groups can't refer to other groups.
212         if (ref_category->IsGroup())
213           continue;
214         // Require an exact match.
215         if (ref_category->name_size() != name_size ||
216             strncmp(ref_category->name, member_name, name_size)) {
217           continue;
218         }
219         if (IsCategoryEnabled(registry, config, *ref_category)) {
220           result = true;
221           // Break ForEachGroupMember() loop.
222           return false;
223         }
224         break;
225       }
226       // No match? Must be a dynamic category.
227       DynamicCategory dyn_category(std::string(member_name, name_size));
228       Category ref_category{Category::FromDynamicCategory(dyn_category)};
229       if (IsCategoryEnabled(registry, config, ref_category)) {
230         result = true;
231         // Break ForEachGroupMember() loop.
232         return false;
233       }
234       // No match found => keep iterating.
235       return true;
236     });
237     return result;
238   }
239 
240   auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
241     for (const auto& tag : category.tags) {
242       if (!tag)
243         break;
244       if (matcher(tag))
245         return true;
246     }
247     // Legacy "disabled-by-default" categories automatically get the "slow" tag.
248     if (!strncmp(category.name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)) &&
249         matcher(kSlowTag)) {
250       return true;
251     }
252     return false;
253   };
254 
255   // First try exact matches, then pattern matches.
256   const std::array<MatchType, 2> match_types = {
257       {MatchType::kExact, MatchType::kPattern}};
258   for (auto match_type : match_types) {
259     // 1. Enabled categories.
260     if (NameMatchesPatternList(config.enabled_categories(), category.name,
261                                match_type)) {
262       return true;
263     }
264 
265     // 2. Enabled tags.
266     if (has_matching_tag([&](const char* tag) {
267           return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
268         })) {
269       return true;
270     }
271 
272     // 3. Disabled categories.
273     if (NameMatchesPatternList(config.disabled_categories(), category.name,
274                                match_type)) {
275       return false;
276     }
277 
278     // 4. Disabled tags.
279     if (has_matching_tag([&](const char* tag) {
280           if (config.disabled_tags_size()) {
281             return NameMatchesPatternList(config.disabled_tags(), tag,
282                                           match_type);
283           } else {
284             // The "slow" and "debug" tags are disabled by default.
285             return NameMatchesPattern(kSlowTag, tag, match_type) ||
286                    NameMatchesPattern(kDebugTag, tag, match_type);
287           }
288         })) {
289       return false;
290     }
291   }
292 
293   // If nothing matched, enable the category by default.
294   return true;
295 }
296 
297 // static
GetTimeNs()298 uint64_t TrackEventInternal::GetTimeNs() {
299   if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
300     return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
301   PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
302   return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
303 }
304 
305 // static
GetSessionCount()306 int TrackEventInternal::GetSessionCount() {
307   return session_count_.load();
308 }
309 
310 // static
ResetIncrementalState(TraceWriterBase * trace_writer,uint64_t timestamp)311 void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
312                                                uint64_t timestamp) {
313   auto default_track = ThreadTrack::Current();
314   {
315     // Mark any incremental state before this point invalid. Also set up
316     // defaults so that we don't need to repeat constant data for each packet.
317     auto packet = NewTracePacket(
318         trace_writer, timestamp,
319         protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
320     auto defaults = packet->set_trace_packet_defaults();
321     defaults->set_timestamp_clock_id(GetClockId());
322 
323     // Establish the default track for this event sequence.
324     auto track_defaults = defaults->set_track_event_defaults();
325     track_defaults->set_track_uuid(default_track.uuid);
326   }
327 
328   // Every thread should write a descriptor for its default track, because most
329   // trace points won't explicitly reference it.
330   WriteTrackDescriptor(default_track, trace_writer);
331 
332   // Additionally the main thread should dump the process descriptor.
333   if (perfetto::base::GetThreadId() == g_main_thread)
334     WriteTrackDescriptor(ProcessTrack::Current(), trace_writer);
335 }
336 
337 // static
338 protozero::MessageHandle<protos::pbzero::TracePacket>
NewTracePacket(TraceWriterBase * trace_writer,uint64_t timestamp,uint32_t seq_flags)339 TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
340                                    uint64_t timestamp,
341                                    uint32_t seq_flags) {
342   auto packet = trace_writer->NewTracePacket();
343   packet->set_timestamp(timestamp);
344   // TODO(skyostil): Stop emitting this for every event once the trace
345   // processor understands trace packet defaults.
346   if (GetClockId() != protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
347     packet->set_timestamp_clock_id(GetClockId());
348   packet->set_sequence_flags(seq_flags);
349   return packet;
350 }
351 
352 // static
WriteEvent(TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const Category * category,const char * name,perfetto::protos::pbzero::TrackEvent::Type type,uint64_t timestamp)353 EventContext TrackEventInternal::WriteEvent(
354     TraceWriterBase* trace_writer,
355     TrackEventIncrementalState* incr_state,
356     const Category* category,
357     const char* name,
358     perfetto::protos::pbzero::TrackEvent::Type type,
359     uint64_t timestamp) {
360   PERFETTO_DCHECK(g_main_thread);
361   PERFETTO_DCHECK(!incr_state->was_cleared);
362 
363   auto packet = NewTracePacket(trace_writer, timestamp);
364   EventContext ctx(std::move(packet), incr_state);
365 
366   auto track_event = ctx.event();
367   if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
368     track_event->set_type(type);
369 
370   // We assume that |category| and |name| point to strings with static lifetime.
371   // This means we can use their addresses as interning keys.
372   // TODO(skyostil): Intern categories at compile time.
373   if (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END &&
374       type != protos::pbzero::TrackEvent::TYPE_COUNTER) {
375     category->ForEachGroupMember(
376         [&](const char* member_name, size_t name_size) {
377           size_t category_iid =
378               InternedEventCategory::Get(&ctx, member_name, name_size);
379           track_event->add_category_iids(category_iid);
380           return true;
381         });
382   }
383   if (name && type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
384     size_t name_iid = InternedEventName::Get(&ctx, name);
385     track_event->set_name_iid(name_iid);
386   }
387   return ctx;
388 }
389 
390 // static
AddDebugAnnotation(perfetto::EventContext * event_ctx,const char * name)391 protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
392     perfetto::EventContext* event_ctx,
393     const char* name) {
394   auto annotation = event_ctx->event()->add_debug_annotations();
395   annotation->set_name_iid(InternedDebugAnnotationName::Get(event_ctx, name));
396   return annotation;
397 }
398 
399 }  // namespace internal
400 }  // namespace perfetto
401