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/track_event.h"
24 #include "perfetto/tracing/track_event_category_registry.h"
25 #include "perfetto/tracing/track_event_interned_data_index.h"
26 #include "protos/perfetto/common/data_source_descriptor.gen.h"
27 #include "protos/perfetto/common/track_event_descriptor.pbzero.h"
28 #include "protos/perfetto/trace/interned_data/interned_data.pbzero.h"
29 #include "protos/perfetto/trace/trace_packet_defaults.pbzero.h"
30 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
31 #include "protos/perfetto/trace/track_event/track_descriptor.pbzero.h"
32
33 namespace perfetto {
34 namespace internal {
35
36 BaseTrackEventInternedDataIndex::~BaseTrackEventInternedDataIndex() = default;
37
38 namespace {
39
40 std::atomic<perfetto::base::PlatformThreadId> g_main_thread;
41 static constexpr const char kLegacySlowPrefix[] = "disabled-by-default-";
42 static constexpr const char kSlowTag[] = "slow";
43 static constexpr const char kDebugTag[] = "debug";
44
45 struct InternedEventCategory
46 : public TrackEventInternedDataIndex<
47 InternedEventCategory,
48 perfetto::protos::pbzero::InternedData::kEventCategoriesFieldNumber,
49 const char*,
50 SmallInternedDataTraits> {
Addperfetto::internal::__anon4e36d0d40111::InternedEventCategory51 static void Add(protos::pbzero::InternedData* interned_data,
52 size_t iid,
53 const char* value,
54 size_t length) {
55 auto category = interned_data->add_event_categories();
56 category->set_iid(iid);
57 category->set_name(value, length);
58 }
59 };
60
61 struct InternedEventName
62 : public TrackEventInternedDataIndex<
63 InternedEventName,
64 perfetto::protos::pbzero::InternedData::kEventNamesFieldNumber,
65 const char*,
66 SmallInternedDataTraits> {
Addperfetto::internal::__anon4e36d0d40111::InternedEventName67 static void Add(protos::pbzero::InternedData* interned_data,
68 size_t iid,
69 const char* value) {
70 auto name = interned_data->add_event_names();
71 name->set_iid(iid);
72 name->set_name(value);
73 }
74 };
75
76 struct InternedDebugAnnotationName
77 : public TrackEventInternedDataIndex<
78 InternedDebugAnnotationName,
79 perfetto::protos::pbzero::InternedData::
80 kDebugAnnotationNamesFieldNumber,
81 const char*,
82 SmallInternedDataTraits> {
Addperfetto::internal::__anon4e36d0d40111::InternedDebugAnnotationName83 static void Add(protos::pbzero::InternedData* interned_data,
84 size_t iid,
85 const char* value) {
86 auto name = interned_data->add_debug_annotation_names();
87 name->set_iid(iid);
88 name->set_name(value);
89 }
90 };
91
92 enum class MatchType { kExact, kPattern };
93
NameMatchesPattern(const std::string & pattern,const std::string & name,MatchType match_type)94 bool NameMatchesPattern(const std::string& pattern,
95 const std::string& name,
96 MatchType match_type) {
97 // To avoid pulling in all of std::regex, for now we only support a single "*"
98 // wildcard at the end of the pattern.
99 size_t i = pattern.find('*');
100 if (i != std::string::npos) {
101 PERFETTO_DCHECK(i == pattern.size() - 1);
102 if (match_type != MatchType::kPattern)
103 return false;
104 return name.substr(0, i) == pattern.substr(0, i);
105 }
106 return name == pattern;
107 }
108
NameMatchesPatternList(const std::vector<std::string> & patterns,const std::string & name,MatchType match_type)109 bool NameMatchesPatternList(const std::vector<std::string>& patterns,
110 const std::string& name,
111 MatchType match_type) {
112 for (const auto& pattern : patterns) {
113 if (NameMatchesPattern(pattern, name, match_type))
114 return true;
115 }
116 return false;
117 }
118
119 } // namespace
120
121 // static
Initialize(const TrackEventCategoryRegistry & registry,bool (* register_data_source)(const DataSourceDescriptor &))122 bool TrackEventInternal::Initialize(
123 const TrackEventCategoryRegistry& registry,
124 bool (*register_data_source)(const DataSourceDescriptor&)) {
125 if (!g_main_thread)
126 g_main_thread = perfetto::base::GetThreadId();
127
128 DataSourceDescriptor dsd;
129 dsd.set_name("track_event");
130
131 protozero::HeapBuffered<protos::pbzero::TrackEventDescriptor> ted;
132 for (size_t i = 0; i < registry.category_count(); i++) {
133 auto category = registry.GetCategory(i);
134 // Don't register group categories.
135 if (category->IsGroup())
136 continue;
137 auto cat = ted->add_available_categories();
138 cat->set_name(category->name);
139 if (category->description)
140 cat->set_description(category->description);
141 for (const auto& tag : category->tags) {
142 if (tag)
143 cat->add_tags(tag);
144 }
145 // Disabled-by-default categories get a "slow" tag.
146 if (!strncmp(category->name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)))
147 cat->add_tags(kSlowTag);
148 }
149 dsd.set_track_event_descriptor_raw(ted.SerializeAsString());
150
151 return register_data_source(dsd);
152 }
153
154 // static
EnableTracing(const TrackEventCategoryRegistry & registry,const protos::gen::TrackEventConfig & config,uint32_t instance_index)155 void TrackEventInternal::EnableTracing(
156 const TrackEventCategoryRegistry& registry,
157 const protos::gen::TrackEventConfig& config,
158 uint32_t instance_index) {
159 for (size_t i = 0; i < registry.category_count(); i++) {
160 if (IsCategoryEnabled(registry, config, *registry.GetCategory(i)))
161 registry.EnableCategoryForInstance(i, instance_index);
162 }
163 }
164
165 // static
DisableTracing(const TrackEventCategoryRegistry & registry,uint32_t instance_index)166 void TrackEventInternal::DisableTracing(
167 const TrackEventCategoryRegistry& registry,
168 uint32_t instance_index) {
169 for (size_t i = 0; i < registry.category_count(); i++)
170 registry.DisableCategoryForInstance(i, instance_index);
171 }
172
173 // static
IsCategoryEnabled(const TrackEventCategoryRegistry & registry,const protos::gen::TrackEventConfig & config,const Category & category)174 bool TrackEventInternal::IsCategoryEnabled(
175 const TrackEventCategoryRegistry& registry,
176 const protos::gen::TrackEventConfig& config,
177 const Category& category) {
178 // If this is a group category, check if any of its constituent categories are
179 // enabled. If so, then this one is enabled too.
180 if (category.IsGroup()) {
181 bool result = false;
182 category.ForEachGroupMember([&](const char* member_name, size_t name_size) {
183 for (size_t i = 0; i < registry.category_count(); i++) {
184 const auto ref_category = registry.GetCategory(i);
185 // Groups can't refer to other groups.
186 if (ref_category->IsGroup())
187 continue;
188 // Require an exact match.
189 if (ref_category->name_size() != name_size ||
190 strncmp(ref_category->name, member_name, name_size)) {
191 continue;
192 }
193 if (IsCategoryEnabled(registry, config, *ref_category)) {
194 result = true;
195 // Break ForEachGroupMember() loop.
196 return false;
197 }
198 break;
199 }
200 // No match found => keep iterating.
201 return true;
202 });
203 return result;
204 }
205
206 auto has_matching_tag = [&](std::function<bool(const char*)> matcher) {
207 for (const auto& tag : category.tags) {
208 if (!tag)
209 break;
210 if (matcher(tag))
211 return true;
212 }
213 // Legacy "disabled-by-default" categories automatically get the "slow" tag.
214 if (!strncmp(category.name, kLegacySlowPrefix, strlen(kLegacySlowPrefix)) &&
215 matcher(kSlowTag)) {
216 return true;
217 }
218 return false;
219 };
220
221 // First try exact matches, then pattern matches.
222 const std::array<MatchType, 2> match_types = {
223 {MatchType::kExact, MatchType::kPattern}};
224 for (auto match_type : match_types) {
225 // 1. Enabled categories.
226 if (NameMatchesPatternList(config.enabled_categories(), category.name,
227 match_type)) {
228 return true;
229 }
230
231 // 2. Enabled tags.
232 if (has_matching_tag([&](const char* tag) {
233 return NameMatchesPatternList(config.enabled_tags(), tag, match_type);
234 })) {
235 return true;
236 }
237
238 // 3. Disabled categories.
239 if (NameMatchesPatternList(config.disabled_categories(), category.name,
240 match_type)) {
241 return false;
242 }
243
244 // 4. Disabled tags.
245 if (has_matching_tag([&](const char* tag) {
246 if (config.disabled_tags_size()) {
247 return NameMatchesPatternList(config.disabled_tags(), tag,
248 match_type);
249 } else {
250 // The "slow" and "debug" tags are disabled by default.
251 return NameMatchesPattern(kSlowTag, tag, match_type) ||
252 NameMatchesPattern(kDebugTag, tag, match_type);
253 }
254 })) {
255 return false;
256 }
257 }
258
259 // If nothing matched, enable the category by default.
260 return true;
261 }
262
263 // static
GetTimeNs()264 uint64_t TrackEventInternal::GetTimeNs() {
265 if (GetClockId() == protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
266 return static_cast<uint64_t>(perfetto::base::GetBootTimeNs().count());
267 PERFETTO_DCHECK(GetClockId() == protos::pbzero::BUILTIN_CLOCK_MONOTONIC);
268 return static_cast<uint64_t>(perfetto::base::GetWallTimeNs().count());
269 }
270
271 // static
ResetIncrementalState(TraceWriterBase * trace_writer,uint64_t timestamp)272 void TrackEventInternal::ResetIncrementalState(TraceWriterBase* trace_writer,
273 uint64_t timestamp) {
274 auto default_track = ThreadTrack::Current();
275 {
276 // Mark any incremental state before this point invalid. Also set up
277 // defaults so that we don't need to repeat constant data for each packet.
278 auto packet = NewTracePacket(
279 trace_writer, timestamp,
280 protos::pbzero::TracePacket::SEQ_INCREMENTAL_STATE_CLEARED);
281 auto defaults = packet->set_trace_packet_defaults();
282 defaults->set_timestamp_clock_id(GetClockId());
283
284 // Establish the default track for this event sequence.
285 auto track_defaults = defaults->set_track_event_defaults();
286 track_defaults->set_track_uuid(default_track.uuid);
287 }
288
289 // Every thread should write a descriptor for its default track, because most
290 // trace points won't explicitly reference it.
291 WriteTrackDescriptor(default_track, trace_writer);
292
293 // Additionally the main thread should dump the process descriptor.
294 if (perfetto::base::GetThreadId() == g_main_thread)
295 WriteTrackDescriptor(ProcessTrack::Current(), trace_writer);
296 }
297
298 // static
299 protozero::MessageHandle<protos::pbzero::TracePacket>
NewTracePacket(TraceWriterBase * trace_writer,uint64_t timestamp,uint32_t seq_flags)300 TrackEventInternal::NewTracePacket(TraceWriterBase* trace_writer,
301 uint64_t timestamp,
302 uint32_t seq_flags) {
303 auto packet = trace_writer->NewTracePacket();
304 packet->set_timestamp(timestamp);
305 // TODO(skyostil): Stop emitting this for every event once the trace
306 // processor understands trace packet defaults.
307 if (GetClockId() != protos::pbzero::BUILTIN_CLOCK_BOOTTIME)
308 packet->set_timestamp_clock_id(GetClockId());
309 packet->set_sequence_flags(seq_flags);
310 return packet;
311 }
312
313 // static
WriteEvent(TraceWriterBase * trace_writer,TrackEventIncrementalState * incr_state,const Category * category,const char * name,perfetto::protos::pbzero::TrackEvent::Type type,uint64_t timestamp)314 EventContext TrackEventInternal::WriteEvent(
315 TraceWriterBase* trace_writer,
316 TrackEventIncrementalState* incr_state,
317 const Category* category,
318 const char* name,
319 perfetto::protos::pbzero::TrackEvent::Type type,
320 uint64_t timestamp) {
321 PERFETTO_DCHECK(g_main_thread);
322
323 if (incr_state->was_cleared) {
324 incr_state->was_cleared = false;
325 ResetIncrementalState(trace_writer, timestamp);
326 }
327 auto packet = NewTracePacket(trace_writer, timestamp);
328 EventContext ctx(std::move(packet), incr_state);
329
330 auto track_event = ctx.event();
331 if (type != protos::pbzero::TrackEvent::TYPE_UNSPECIFIED)
332 track_event->set_type(type);
333
334 // We assume that |category| and |name| point to strings with static lifetime.
335 // This means we can use their addresses as interning keys.
336 if (category && type != protos::pbzero::TrackEvent::TYPE_SLICE_END) {
337 category->ForEachGroupMember(
338 [&](const char* member_name, size_t name_size) {
339 size_t category_iid =
340 InternedEventCategory::Get(&ctx, member_name, name_size);
341 track_event->add_category_iids(category_iid);
342 return true;
343 });
344 }
345 if (name) {
346 size_t name_iid = InternedEventName::Get(&ctx, name);
347 track_event->set_name_iid(name_iid);
348 }
349 return ctx;
350 }
351
352 // static
AddDebugAnnotation(perfetto::EventContext * event_ctx,const char * name)353 protos::pbzero::DebugAnnotation* TrackEventInternal::AddDebugAnnotation(
354 perfetto::EventContext* event_ctx,
355 const char* name) {
356 auto annotation = event_ctx->event()->add_debug_annotations();
357 annotation->set_name_iid(InternedDebugAnnotationName::Get(event_ctx, name));
358 return annotation;
359 }
360
361 } // namespace internal
362 } // namespace perfetto
363