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