• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 "src/traced/probes/ftrace/proto_translation_table.h"
18 
19 #include <regex.h>
20 #include <sys/utsname.h>
21 
22 #include <algorithm>
23 
24 #include "perfetto/base/string_utils.h"
25 #include "perfetto/protozero/proto_utils.h"
26 #include "src/traced/probes/ftrace/event_info.h"
27 #include "src/traced/probes/ftrace/ftrace_procfs.h"
28 
29 #include "perfetto/trace/ftrace/ftrace_event.pbzero.h"
30 #include "perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
31 #include "perfetto/trace/ftrace/generic.pbzero.h"
32 
33 namespace perfetto {
34 
35 namespace {
36 
37 using protozero::proto_utils::ProtoSchemaType;
38 using protos::pbzero::GenericFtraceEvent;
39 
MakeFtracePageHeaderSpec(const std::vector<FtraceEvent::Field> & fields)40 ProtoTranslationTable::FtracePageHeaderSpec MakeFtracePageHeaderSpec(
41     const std::vector<FtraceEvent::Field>& fields) {
42   ProtoTranslationTable::FtracePageHeaderSpec spec;
43   for (const FtraceEvent::Field& field : fields) {
44     std::string name = GetNameFromTypeAndName(field.type_and_name);
45     if (name == "timestamp")
46       spec.timestamp = field;
47     else if (name == "commit")
48       spec.size = field;
49     else if (name == "overwrite")
50       spec.overwrite = field;
51     else if (name != "data")
52       PERFETTO_DFATAL("Invalid field in header spec: %s", name.c_str());
53   }
54   return spec;
55 }
56 
57 // Fallback used when the "header_page" is not readable.
58 // It uses a hard-coded header_page. The only caveat is that the size of the
59 // |commit| field depends on the kernel bit-ness. This function tries to infer
60 // that from the uname() and if that fails assumes that the kernel bitness
61 // matches the userspace bitness.
GuessFtracePageHeaderSpec()62 ProtoTranslationTable::FtracePageHeaderSpec GuessFtracePageHeaderSpec() {
63   ProtoTranslationTable::FtracePageHeaderSpec spec{};
64 #if PERFETTO_BUILDFLAG(PERFETTO_OS_ANDROID) && __i386__
65   // local_t is arch-specific and models the largest size of an integer that is
66   // still atomic across bus transactions, exceptions and IRQ. On android x86
67   // this is always size 8
68   uint16_t commit_size = 8;
69 #else
70   uint16_t commit_size = sizeof(long);
71 
72   struct utsname sysinfo;
73   // If user space is 32-bit check the kernel to verify.
74   if (commit_size < 8 && uname(&sysinfo) == 0) {
75     // Arm returns armv# for its machine type. The first (and only currently)
76     // arm processor that supports 64bit is the armv8 series.
77     commit_size = strstr(sysinfo.machine, "64") ||
78                   strstr(sysinfo.machine, "armv8") ? 8 : 4;
79   }
80 #endif
81 
82   // header_page typically looks as follows on a 64-bit kernel:
83   // field: u64 timestamp; offset:0; size:8; signed:0;
84   // field: local_t commit; offset:8; size:8; signed:1;
85   // field: int overwrite; offset:8; size:1; signed:1;
86   // field: char data; offset:16; size:4080; signed:0;
87   //
88   // On a 32-bit kernel local_t is 32-bit wide and data starts @ offset 12.
89 
90   spec.timestamp = FtraceEvent::Field{"u64 timestamp", 0, 8, 0};
91   spec.size = FtraceEvent::Field{"local_t commit", 8, commit_size, 1};
92   spec.overwrite = FtraceEvent::Field{"int overwrite", 8, 1, 1};
93   return spec;
94 }
95 
BuildEventsVector(const std::vector<Event> & events)96 const std::vector<Event> BuildEventsVector(const std::vector<Event>& events) {
97   size_t largest_id = 0;
98   for (const Event& event : events) {
99     if (event.ftrace_event_id > largest_id)
100       largest_id = event.ftrace_event_id;
101   }
102   std::vector<Event> events_by_id;
103   events_by_id.resize(largest_id + 1);
104   for (const Event& event : events) {
105     events_by_id[event.ftrace_event_id] = event;
106   }
107   events_by_id.shrink_to_fit();
108   return events_by_id;
109 }
110 
111 // Merge the information from |ftrace_field| into |field| (mutating it).
112 // We should set the following fields: offset, size, ftrace field type and
113 // translation strategy.
MergeFieldInfo(const FtraceEvent::Field & ftrace_field,Field * field,const char * event_name_for_debug)114 bool MergeFieldInfo(const FtraceEvent::Field& ftrace_field,
115                     Field* field,
116                     const char* event_name_for_debug) {
117   PERFETTO_DCHECK(field->ftrace_name);
118   PERFETTO_DCHECK(field->proto_field_id);
119   PERFETTO_DCHECK(static_cast<int>(field->proto_field_type));
120   PERFETTO_DCHECK(!field->ftrace_offset);
121   PERFETTO_DCHECK(!field->ftrace_size);
122   PERFETTO_DCHECK(!field->ftrace_type);
123 
124   if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
125                        ftrace_field.is_signed, &field->ftrace_type)) {
126     PERFETTO_FATAL(
127         "Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
128         "size:%d "
129         "signed:%d)",
130         event_name_for_debug, field->ftrace_name,
131         ftrace_field.type_and_name.c_str(), ftrace_field.size,
132         ftrace_field.is_signed);
133     return false;
134   }
135 
136   field->ftrace_offset = ftrace_field.offset;
137   field->ftrace_size = ftrace_field.size;
138 
139   if (!SetTranslationStrategy(field->ftrace_type, field->proto_field_type,
140                               &field->strategy)) {
141     PERFETTO_DLOG(
142         "Failed to find translation strategy for ftrace field \"%s.%s\" (%s -> "
143         "%s)",
144         event_name_for_debug, field->ftrace_name, ToString(field->ftrace_type),
145         protozero::proto_utils::ProtoSchemaToString(field->proto_field_type));
146     // TODO(hjd): Uncomment DCHECK when proto generation is fixed.
147     // PERFETTO_DFATAL("Failed to find translation strategy");
148     return false;
149   }
150 
151   return true;
152 }
153 
154 // For each field in |fields| find the matching field from |ftrace_fields| (by
155 // comparing ftrace_name) and copy the information from the FtraceEvent::Field
156 // into the Field (mutating it). If there is no matching field in
157 // |ftrace_fields| remove the Field from |fields|. Return the maximum observed
158 // 'field end' (offset + size).
MergeFields(const std::vector<FtraceEvent::Field> & ftrace_fields,std::vector<Field> * fields,const char * event_name_for_debug)159 uint16_t MergeFields(const std::vector<FtraceEvent::Field>& ftrace_fields,
160                      std::vector<Field>* fields,
161                      const char* event_name_for_debug) {
162   uint16_t fields_end = 0;
163 
164   // Loop over each Field in |fields| modifiying it with information from the
165   // matching |ftrace_fields| field or removing it.
166   auto field = fields->begin();
167   while (field != fields->end()) {
168     bool success = false;
169     for (const FtraceEvent::Field& ftrace_field : ftrace_fields) {
170       if (GetNameFromTypeAndName(ftrace_field.type_and_name) !=
171           field->ftrace_name)
172         continue;
173 
174       success = MergeFieldInfo(ftrace_field, &*field, event_name_for_debug);
175 
176       uint16_t field_end = field->ftrace_offset + field->ftrace_size;
177       fields_end = std::max<uint16_t>(fields_end, field_end);
178 
179       break;
180     }
181     if (success) {
182       ++field;
183     } else {
184       field = fields->erase(field);
185     }
186   }
187   return fields_end;
188 }
189 
Contains(const std::string & haystack,const std::string & needle)190 bool Contains(const std::string& haystack, const std::string& needle) {
191   return haystack.find(needle) != std::string::npos;
192 }
193 
RegexError(int errcode,const regex_t * preg)194 std::string RegexError(int errcode, const regex_t* preg) {
195   char buf[64];
196   regerror(errcode, preg, buf, sizeof(buf));
197   return {buf, sizeof(buf)};
198 }
199 
Match(const char * string,const char * pattern)200 bool Match(const char* string, const char* pattern) {
201   regex_t re;
202   int ret = regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB);
203   if (ret != 0) {
204     PERFETTO_FATAL("regcomp: %s", RegexError(ret, &re).c_str());
205   }
206   ret = regexec(&re, string, 0, nullptr, 0);
207   regfree(&re);
208   return ret != REG_NOMATCH;
209 }
210 
211 // Set proto field type and id based on the ftrace type.
SetProtoType(FtraceFieldType ftrace_type,ProtoSchemaType * proto_type,uint32_t * proto_field_id)212 void SetProtoType(FtraceFieldType ftrace_type,
213                   ProtoSchemaType* proto_type,
214                   uint32_t* proto_field_id) {
215   switch (ftrace_type) {
216     case kFtraceCString:
217     case kFtraceFixedCString:
218     case kFtraceStringPtr:
219     case kFtraceDataLoc:
220       *proto_type = ProtoSchemaType::kString;
221       *proto_field_id = GenericFtraceEvent::Field::kStrValueFieldNumber;
222       break;
223     case kFtraceInt8:
224     case kFtraceInt16:
225     case kFtraceInt32:
226     case kFtracePid32:
227     case kFtraceCommonPid32:
228     case kFtraceInt64:
229       *proto_type = ProtoSchemaType::kInt64;
230       *proto_field_id = GenericFtraceEvent::Field::kIntValueFieldNumber;
231       break;
232     case kFtraceUint8:
233     case kFtraceUint16:
234     case kFtraceUint32:
235     case kFtraceBool:
236     case kFtraceDevId32:
237     case kFtraceDevId64:
238     case kFtraceUint64:
239     case kFtraceInode32:
240     case kFtraceInode64:
241       *proto_type = ProtoSchemaType::kUint64;
242       *proto_field_id = GenericFtraceEvent::Field::kUintValueFieldNumber;
243       break;
244   }
245 }
246 
247 }  // namespace
248 
249 // This is similar but different from InferProtoType (see format_parser.cc).
250 // TODO(hjd): Fold FtraceEvent(::Field) into Event.
InferFtraceType(const std::string & type_and_name,size_t size,bool is_signed,FtraceFieldType * out)251 bool InferFtraceType(const std::string& type_and_name,
252                      size_t size,
253                      bool is_signed,
254                      FtraceFieldType* out) {
255   // Fixed length strings: e.g. "char foo[16]" we don't care about the number
256   // since we get the size as it's own field. Somewhat awkwardly these fields
257   // are both fixed size and null terminated meaning that we can't just drop
258   // them directly into the protobuf (since if the string is shorter than 15
259   // characters we want only the bit up to the null terminator).
260   if (Match(type_and_name.c_str(), R"(char [a-zA-Z_]+\[[0-9]+\])")) {
261     *out = kFtraceFixedCString;
262     return true;
263   }
264 
265   // String pointers: "__data_loc char[] foo" (as in
266   // 'cpufreq_interactive_boost').
267   // TODO(fmayer): Handle u32[], u8[], __u8[] as well.
268   if (Contains(type_and_name, "__data_loc char[] ")) {
269     if (size != 4) {
270       PERFETTO_ELOG("__data_loc with incorrect size: %s (%zd)",
271                     type_and_name.c_str(), size);
272       return false;
273     }
274     *out = kFtraceDataLoc;
275     return true;
276   }
277 
278   if (Contains(type_and_name, "char[] ")) {
279     *out = kFtraceStringPtr;
280     return true;
281   }
282   if (Contains(type_and_name, "char * ")) {
283     *out = kFtraceStringPtr;
284     return true;
285   }
286 
287   // Variable length strings: "char foo" + size: 0 (as in 'print').
288   if (base::StartsWith(type_and_name, "char ") && size == 0) {
289     *out = kFtraceCString;
290     return true;
291   }
292 
293   if (base::StartsWith(type_and_name, "bool ")) {
294     *out = kFtraceBool;
295     return true;
296   }
297 
298   if (base::StartsWith(type_and_name, "ino_t ") ||
299       base::StartsWith(type_and_name, "i_ino ")) {
300     if (size == 4) {
301       *out = kFtraceInode32;
302       return true;
303     } else if (size == 8) {
304       *out = kFtraceInode64;
305       return true;
306     }
307   }
308 
309   if (base::StartsWith(type_and_name, "dev_t ")) {
310     if (size == 4) {
311       *out = kFtraceDevId32;
312       return true;
313     } else if (size == 8) {
314       *out = kFtraceDevId64;
315       return true;
316     }
317   }
318 
319   // Pids (as in 'sched_switch').
320   if (base::StartsWith(type_and_name, "pid_t ") && size == 4) {
321     *out = kFtracePid32;
322     return true;
323   }
324 
325   if (Contains(type_and_name, "common_pid") && size == 4) {
326     *out = kFtraceCommonPid32;
327     return true;
328   }
329 
330   // Ints of various sizes:
331   if (size == 1 && is_signed) {
332     *out = kFtraceInt8;
333     return true;
334   } else if (size == 1 && !is_signed) {
335     *out = kFtraceUint8;
336     return true;
337   } else if (size == 2 && is_signed) {
338     *out = kFtraceInt16;
339     return true;
340   } else if (size == 2 && !is_signed) {
341     *out = kFtraceUint16;
342     return true;
343   } else if (size == 4 && is_signed) {
344     *out = kFtraceInt32;
345     return true;
346   } else if (size == 4 && !is_signed) {
347     *out = kFtraceUint32;
348     return true;
349   } else if (size == 8 && is_signed) {
350     *out = kFtraceInt64;
351     return true;
352   } else if (size == 8 && !is_signed) {
353     *out = kFtraceUint64;
354     return true;
355   }
356 
357   PERFETTO_DLOG("Could not infer ftrace type for '%s'", type_and_name.c_str());
358   return false;
359 }
360 
361 // static
362 ProtoTranslationTable::FtracePageHeaderSpec
DefaultPageHeaderSpecForTesting()363 ProtoTranslationTable::DefaultPageHeaderSpecForTesting() {
364   std::string page_header =
365       R"(	field: u64 timestamp;	offset:0;	size:8;	signed:0;
366 	field: local_t commit;	offset:8;	size:8;	signed:1;
367 	field: int overwrite;	offset:8;	size:1;	signed:1;
368 	field: char data;	offset:16;	size:4080;	signed:0;)";
369   std::vector<FtraceEvent::Field> page_header_fields;
370   PERFETTO_CHECK(ParseFtraceEventBody(std::move(page_header), nullptr,
371                                       &page_header_fields));
372   return MakeFtracePageHeaderSpec(page_header_fields);
373 }
374 
375 // static
Create(const FtraceProcfs * ftrace_procfs,std::vector<Event> events,std::vector<Field> common_fields)376 std::unique_ptr<ProtoTranslationTable> ProtoTranslationTable::Create(
377     const FtraceProcfs* ftrace_procfs,
378     std::vector<Event> events,
379     std::vector<Field> common_fields) {
380   bool common_fields_processed = false;
381   uint16_t common_fields_end = 0;
382 
383   std::string page_header = ftrace_procfs->ReadPageHeaderFormat();
384   bool ftrace_header_parsed = false;
385   FtracePageHeaderSpec header_spec{};
386   if (!page_header.empty()) {
387     std::vector<FtraceEvent::Field> page_header_fields;
388     ftrace_header_parsed = ParseFtraceEventBody(std::move(page_header), nullptr,
389                                                 &page_header_fields);
390     header_spec = MakeFtracePageHeaderSpec(page_header_fields);
391   }
392 
393   if (!ftrace_header_parsed) {
394     PERFETTO_LOG("Failed to parse ftrace page header, using fallback layout");
395     header_spec = GuessFtracePageHeaderSpec();
396   }
397 
398   for (Event& event : events) {
399     if (event.proto_field_id ==
400         protos::pbzero::FtraceEvent::kGenericFieldNumber) {
401       continue;
402     }
403     PERFETTO_DCHECK(event.name);
404     PERFETTO_DCHECK(event.group);
405     PERFETTO_DCHECK(event.proto_field_id);
406     PERFETTO_DCHECK(!event.ftrace_event_id);
407 
408     std::string contents =
409         ftrace_procfs->ReadEventFormat(event.group, event.name);
410     FtraceEvent ftrace_event;
411     if (contents.empty() || !ParseFtraceEvent(contents, &ftrace_event)) {
412       if (!strcmp(event.group, "ftrace") && !strcmp(event.name, "print")) {
413         // On some "user" builds of Android <P the ftrace/print event is not
414         // selinux-whitelisted. Thankfully this event is an always-on built-in
415         // so we don't need to write to its 'enable' file. However we need to
416         // know its binary layout to decode it, so we hardcode it.
417         ftrace_event.id = 5;  // Seems quite stable across kernels.
418         ftrace_event.name = "print";
419         // The only field we care about is:
420         // field:char buf; offset:16; size:0; signed:0;
421         ftrace_event.fields.emplace_back(
422             FtraceEvent::Field{"char buf", 16, 0, 0});
423       } else {
424         continue;
425       }
426     }
427 
428     event.ftrace_event_id = ftrace_event.id;
429 
430     if (!common_fields_processed) {
431       common_fields_end =
432           MergeFields(ftrace_event.common_fields, &common_fields, event.name);
433       common_fields_processed = true;
434     }
435 
436     uint16_t fields_end =
437         MergeFields(ftrace_event.fields, &event.fields, event.name);
438 
439     event.size = std::max<uint16_t>(fields_end, common_fields_end);
440   }
441 
442   events.erase(std::remove_if(events.begin(), events.end(),
443                               [](const Event& event) {
444                                 return event.proto_field_id == 0 ||
445                                        event.ftrace_event_id == 0;
446                               }),
447                events.end());
448 
449   auto table = std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
450       ftrace_procfs, events, std::move(common_fields), header_spec));
451   return table;
452 }
453 
ProtoTranslationTable(const FtraceProcfs * ftrace_procfs,const std::vector<Event> & events,std::vector<Field> common_fields,FtracePageHeaderSpec ftrace_page_header_spec)454 ProtoTranslationTable::ProtoTranslationTable(
455     const FtraceProcfs* ftrace_procfs,
456     const std::vector<Event>& events,
457     std::vector<Field> common_fields,
458     FtracePageHeaderSpec ftrace_page_header_spec)
459     : ftrace_procfs_(ftrace_procfs),
460       events_(BuildEventsVector(events)),
461       largest_id_(events_.size() - 1),
462       common_fields_(std::move(common_fields)),
463       ftrace_page_header_spec_(ftrace_page_header_spec) {
464   for (const Event& event : events) {
465     group_and_name_to_event_[GroupAndName(event.group, event.name)] =
466         &events_.at(event.ftrace_event_id);
467     name_to_events_[event.name].push_back(&events_.at(event.ftrace_event_id));
468     group_to_events_[event.group].push_back(&events_.at(event.ftrace_event_id));
469   }
470 }
471 
GetOrCreateEvent(const GroupAndName & group_and_name)472 const Event* ProtoTranslationTable::GetOrCreateEvent(
473     const GroupAndName& group_and_name) {
474   const Event* event = GetEvent(group_and_name);
475   if (event)
476     return event;
477   // The ftrace event does not already exist so a new one will be created
478   // by parsing the format file.
479   std::string contents = ftrace_procfs_->ReadEventFormat(group_and_name.group(),
480                                                          group_and_name.name());
481   if (contents.empty())
482     return nullptr;
483   FtraceEvent ftrace_event = {};
484   ParseFtraceEvent(contents, &ftrace_event);
485 
486   // Ensure events vector is large enough
487   if (ftrace_event.id > largest_id_) {
488     events_.resize(ftrace_event.id + 1);
489     largest_id_ = ftrace_event.id;
490   }
491 
492   // Set known event variables
493   Event* e = &events_.at(ftrace_event.id);
494   e->ftrace_event_id = ftrace_event.id;
495   e->proto_field_id = protos::pbzero::FtraceEvent::kGenericFieldNumber;
496   e->name = InternString(group_and_name.name());
497   e->group = InternString(group_and_name.group());
498 
499   // Calculate size of common fields.
500   for (const FtraceEvent::Field& ftrace_field : ftrace_event.common_fields) {
501     uint16_t field_end = ftrace_field.offset + ftrace_field.size;
502     e->size = std::max(field_end, e->size);
503   }
504 
505   // For every field in the ftrace event, make a field in the generic event.
506   for (const FtraceEvent::Field& ftrace_field : ftrace_event.fields)
507     e->size = std::max(CreateGenericEventField(ftrace_field, *e), e->size);
508 
509   group_and_name_to_event_[group_and_name] = &events_.at(e->ftrace_event_id);
510   name_to_events_[e->name].push_back(&events_.at(e->ftrace_event_id));
511   group_to_events_[e->group].push_back(&events_.at(e->ftrace_event_id));
512 
513   return e;
514 };
515 
InternString(const std::string & str)516 const char* ProtoTranslationTable::InternString(const std::string& str) {
517   auto it_and_inserted = interned_strings_.insert(str);
518   return it_and_inserted.first->c_str();
519 };
520 
CreateGenericEventField(const FtraceEvent::Field & ftrace_field,Event & event)521 uint16_t ProtoTranslationTable::CreateGenericEventField(
522     const FtraceEvent::Field& ftrace_field,
523     Event& event) {
524   uint16_t field_end = ftrace_field.offset + ftrace_field.size;
525   std::string field_name = GetNameFromTypeAndName(ftrace_field.type_and_name);
526   if (field_name.empty()) {
527     PERFETTO_DLOG("Field: %s could not be added to the generic event.",
528                   ftrace_field.type_and_name.c_str());
529     return field_end;
530   }
531   event.fields.emplace_back();
532   Field* field = &event.fields.back();
533   field->ftrace_name = InternString(field_name);
534   if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
535                        ftrace_field.is_signed, &field->ftrace_type)) {
536     PERFETTO_DLOG(
537         "Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
538         "size:%d "
539         "signed:%d)",
540         event.name, field->ftrace_name, ftrace_field.type_and_name.c_str(),
541         ftrace_field.size, ftrace_field.is_signed);
542     event.fields.pop_back();
543     return field_end;
544   }
545   SetProtoType(field->ftrace_type, &field->proto_field_type,
546                &field->proto_field_id);
547   field->ftrace_offset = ftrace_field.offset;
548   field->ftrace_size = ftrace_field.size;
549   // Proto type is set based on ftrace type so all fields should have a
550   // translation strategy.
551   bool success = SetTranslationStrategy(
552       field->ftrace_type, field->proto_field_type, &field->strategy);
553   PERFETTO_DCHECK(success);
554   return field_end;
555 }
556 
557 EventFilter::EventFilter() = default;
558 EventFilter::~EventFilter() = default;
559 
AddEnabledEvent(size_t ftrace_event_id)560 void EventFilter::AddEnabledEvent(size_t ftrace_event_id) {
561   if (ftrace_event_id >= enabled_ids_.size())
562     enabled_ids_.resize(ftrace_event_id + 1);
563   enabled_ids_[ftrace_event_id] = true;
564 }
565 
DisableEvent(size_t ftrace_event_id)566 void EventFilter::DisableEvent(size_t ftrace_event_id) {
567   if (ftrace_event_id >= enabled_ids_.size())
568     return;
569   enabled_ids_[ftrace_event_id] = false;
570 }
571 
IsEventEnabled(size_t ftrace_event_id) const572 bool EventFilter::IsEventEnabled(size_t ftrace_event_id) const {
573   if (ftrace_event_id == 0 || ftrace_event_id >= enabled_ids_.size())
574     return false;
575   return enabled_ids_[ftrace_event_id];
576 }
577 
GetEnabledEvents() const578 std::set<size_t> EventFilter::GetEnabledEvents() const {
579   std::set<size_t> enabled;
580   for (size_t i = 0; i < enabled_ids_.size(); i++) {
581     if (enabled_ids_[i]) {
582       enabled.insert(i);
583     }
584   }
585   return enabled;
586 }
587 
EnableEventsFrom(const EventFilter & other)588 void EventFilter::EnableEventsFrom(const EventFilter& other) {
589   size_t max_length = std::max(enabled_ids_.size(), other.enabled_ids_.size());
590   enabled_ids_.resize(max_length);
591   for (size_t i = 0; i < other.enabled_ids_.size(); i++) {
592     if (other.enabled_ids_[i])
593       enabled_ids_[i] = true;
594   }
595 }
596 
597 ProtoTranslationTable::~ProtoTranslationTable() = default;
598 
599 }  // namespace perfetto
600