Home | History | Annotate | Download | only in ftrace
      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 
     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.
     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 
     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.
    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).
    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 
    190 bool Contains(const std::string& haystack, const std::string& needle) {
    191   return haystack.find(needle) != std::string::npos;
    192 }
    193 
    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 
    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.
    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.
    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
    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
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    572 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 
    578 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 
    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