• 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/ext/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 "protos/perfetto/trace/ftrace/ftrace_event.pbzero.h"
30 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
31 #include "protos/perfetto/trace/ftrace/generic.pbzero.h"
32 
33 namespace perfetto {
34 
35 namespace {
36 
37 using protos::pbzero::GenericFtraceEvent;
38 using protozero::proto_utils::ProtoSchemaType;
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) && defined(__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 =
78         strstr(sysinfo.machine, "64") || strstr(sysinfo.machine, "armv8") ? 8
79                                                                           : 4;
80   }
81 #endif
82 
83   // header_page typically looks as follows on a 64-bit kernel:
84   // field: u64 timestamp; offset:0; size:8; signed:0;
85   // field: local_t commit; offset:8; size:8; signed:1;
86   // field: int overwrite; offset:8; size:1; signed:1;
87   // field: char data; offset:16; size:4080; signed:0;
88   //
89   // On a 32-bit kernel local_t is 32-bit wide and data starts @ offset 12.
90 
91   spec.timestamp = FtraceEvent::Field{"u64 timestamp", 0, 8, 0};
92   spec.size = FtraceEvent::Field{"local_t commit", 8, commit_size, 1};
93   spec.overwrite = FtraceEvent::Field{"int overwrite", 8, 1, 1};
94   return spec;
95 }
96 
BuildEventsDeque(const std::vector<Event> & events)97 const std::deque<Event> BuildEventsDeque(const std::vector<Event>& events) {
98   size_t largest_id = 0;
99   for (const Event& event : events) {
100     if (event.ftrace_event_id > largest_id)
101       largest_id = event.ftrace_event_id;
102   }
103   std::deque<Event> events_by_id;
104   events_by_id.resize(largest_id + 1);
105   for (const Event& event : events) {
106     events_by_id[event.ftrace_event_id] = event;
107   }
108   events_by_id.shrink_to_fit();
109   return events_by_id;
110 }
111 
112 // Merge the information from |ftrace_field| into |field| (mutating it).
113 // We should set the following fields: offset, size, ftrace field type and
114 // translation strategy.
MergeFieldInfo(const FtraceEvent::Field & ftrace_field,Field * field,const char * event_name_for_debug)115 bool MergeFieldInfo(const FtraceEvent::Field& ftrace_field,
116                     Field* field,
117                     const char* event_name_for_debug) {
118   PERFETTO_DCHECK(field->ftrace_name);
119   PERFETTO_DCHECK(field->proto_field_id);
120   PERFETTO_DCHECK(static_cast<int>(field->proto_field_type));
121   PERFETTO_DCHECK(!field->ftrace_offset);
122   PERFETTO_DCHECK(!field->ftrace_size);
123   PERFETTO_DCHECK(!field->ftrace_type);
124 
125   if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
126                        ftrace_field.is_signed, &field->ftrace_type)) {
127     PERFETTO_DFATAL(
128         "Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
129         "size:%d "
130         "signed:%d)",
131         event_name_for_debug, field->ftrace_name,
132         ftrace_field.type_and_name.c_str(), ftrace_field.size,
133         ftrace_field.is_signed);
134     return false;
135   }
136 
137   field->ftrace_offset = ftrace_field.offset;
138   field->ftrace_size = ftrace_field.size;
139 
140   if (!SetTranslationStrategy(field->ftrace_type, field->proto_field_type,
141                               &field->strategy)) {
142     PERFETTO_DLOG(
143         "Failed to find translation strategy for ftrace field \"%s.%s\" (%s -> "
144         "%s)",
145         event_name_for_debug, field->ftrace_name, ToString(field->ftrace_type),
146         protozero::proto_utils::ProtoSchemaToString(field->proto_field_type));
147     // TODO(hjd): Uncomment DCHECK when proto generation is fixed.
148     // PERFETTO_DFATAL("Failed to find translation strategy");
149     return false;
150   }
151 
152   return true;
153 }
154 
155 // For each field in |fields| find the matching field from |ftrace_fields| (by
156 // comparing ftrace_name) and copy the information from the FtraceEvent::Field
157 // into the Field (mutating it). If there is no matching field in
158 // |ftrace_fields| remove the Field from |fields|. Return the maximum observed
159 // 'field end' (offset + size).
MergeFields(const std::vector<FtraceEvent::Field> & ftrace_fields,std::vector<Field> * fields,const char * event_name_for_debug)160 uint16_t MergeFields(const std::vector<FtraceEvent::Field>& ftrace_fields,
161                      std::vector<Field>* fields,
162                      const char* event_name_for_debug) {
163   uint16_t fields_end = 0;
164 
165   // Loop over each Field in |fields| modifying it with information from the
166   // matching |ftrace_fields| field or removing it.
167   auto field = fields->begin();
168   while (field != fields->end()) {
169     bool success = false;
170     for (const FtraceEvent::Field& ftrace_field : ftrace_fields) {
171       if (GetNameFromTypeAndName(ftrace_field.type_and_name) !=
172           field->ftrace_name)
173         continue;
174 
175       success = MergeFieldInfo(ftrace_field, &*field, event_name_for_debug);
176 
177       uint16_t field_end = field->ftrace_offset + field->ftrace_size;
178       fields_end = std::max<uint16_t>(fields_end, field_end);
179 
180       break;
181     }
182     if (success) {
183       ++field;
184     } else {
185       field = fields->erase(field);
186     }
187   }
188   return fields_end;
189 }
190 
Contains(const std::string & haystack,const std::string & needle)191 bool Contains(const std::string& haystack, const std::string& needle) {
192   return haystack.find(needle) != std::string::npos;
193 }
194 
RegexError(int errcode,const regex_t * preg)195 std::string RegexError(int errcode, const regex_t* preg) {
196   char buf[64];
197   regerror(errcode, preg, buf, sizeof(buf));
198   return {buf, sizeof(buf)};
199 }
200 
Match(const char * string,const char * pattern)201 bool Match(const char* string, const char* pattern) {
202   regex_t re;
203   int ret = regcomp(&re, pattern, REG_EXTENDED | REG_NOSUB);
204   if (ret != 0) {
205     PERFETTO_FATAL("regcomp: %s", RegexError(ret, &re).c_str());
206   }
207   ret = regexec(&re, string, 0, nullptr, 0);
208   regfree(&re);
209   return ret != REG_NOMATCH;
210 }
211 
212 // Set proto field type and id based on the ftrace type.
SetProtoType(FtraceFieldType ftrace_type,ProtoSchemaType * proto_type,uint32_t * proto_field_id)213 void SetProtoType(FtraceFieldType ftrace_type,
214                   ProtoSchemaType* proto_type,
215                   uint32_t* proto_field_id) {
216   switch (ftrace_type) {
217     case kFtraceCString:
218     case kFtraceFixedCString:
219     case kFtraceStringPtr:
220     case kFtraceDataLoc:
221       *proto_type = ProtoSchemaType::kString;
222       *proto_field_id = GenericFtraceEvent::Field::kStrValueFieldNumber;
223       break;
224     case kFtraceInt8:
225     case kFtraceInt16:
226     case kFtraceInt32:
227     case kFtracePid32:
228     case kFtraceCommonPid32:
229     case kFtraceInt64:
230       *proto_type = ProtoSchemaType::kInt64;
231       *proto_field_id = GenericFtraceEvent::Field::kIntValueFieldNumber;
232       break;
233     case kFtraceUint8:
234     case kFtraceUint16:
235     case kFtraceUint32:
236     case kFtraceBool:
237     case kFtraceDevId32:
238     case kFtraceDevId64:
239     case kFtraceUint64:
240     case kFtraceInode32:
241     case kFtraceInode64:
242     case kFtraceSymAddr64:
243       *proto_type = ProtoSchemaType::kUint64;
244       *proto_field_id = GenericFtraceEvent::Field::kUintValueFieldNumber;
245       break;
246     case kInvalidFtraceFieldType:
247       PERFETTO_FATAL("Unexpected ftrace field type");
248   }
249 }
250 
251 }  // namespace
252 
253 // This is similar but different from InferProtoType (see format_parser.cc).
254 // TODO(hjd): Fold FtraceEvent(::Field) into Event.
InferFtraceType(const std::string & type_and_name,size_t size,bool is_signed,FtraceFieldType * out)255 bool InferFtraceType(const std::string& type_and_name,
256                      size_t size,
257                      bool is_signed,
258                      FtraceFieldType* out) {
259   // Fixed length strings: e.g. "char foo[16]".
260   //
261   // We don't care about the number, since we get the size as it's own field and
262   // since it can be a string defined elsewhere in a kernel header file.
263   //
264   // Somewhat awkwardly these fields are both fixed size and null terminated
265   // meaning that we can't just drop them directly into the protobuf (since if
266   // the string is shorter than 15 characters we want only the bit up to the
267   // null terminator).
268   //
269   // In some rare cases (e.g. old kernel bugs) these strings might not be null
270   // terminated (b/205763418).
271   if (Match(type_and_name.c_str(),
272             R"(char [a-zA-Z_][a-zA-Z_0-9]*\[[a-zA-Z_0-9]+\])")) {
273     *out = kFtraceFixedCString;
274     return true;
275   }
276 
277   // String pointers: "__data_loc char[] foo" (as in
278   // 'cpufreq_interactive_boost').
279   // TODO(fmayer): Handle u32[], u8[], __u8[] as well.
280   if (Contains(type_and_name, "__data_loc char[] ")) {
281     if (size != 4) {
282       PERFETTO_ELOG("__data_loc with incorrect size: %s (%zd)",
283                     type_and_name.c_str(), size);
284       return false;
285     }
286     *out = kFtraceDataLoc;
287     return true;
288   }
289 
290   // Parsing of sys_enter argument field declared as
291   //    field:unsigned long args[6];
292   if (type_and_name == "unsigned long args[6]") {
293     if (size == 24) {
294       // 24 / 6 = 4 -> 32bit system
295       *out = kFtraceUint32;
296       return true;
297     } else if (size == 48) {
298       // 48 / 6 = 8 -> 64bit system
299       *out = kFtraceUint64;
300       return true;
301     }
302   }
303 
304   if (Contains(type_and_name, "char[] ")) {
305     *out = kFtraceStringPtr;
306     return true;
307   }
308   if (Contains(type_and_name, "char * ")) {
309     *out = kFtraceStringPtr;
310     return true;
311   }
312 
313   // Kernel addresses that need symbolization via kallsyms. Only 64-bit kernels
314   // are supported for now. 32-bit kernels seems to be going away.
315   if ((base::StartsWith(type_and_name, "void*") ||
316        base::StartsWith(type_and_name, "void *")) &&
317       size == 8) {
318     *out = kFtraceSymAddr64;
319     return true;
320   }
321 
322   // Variable length strings: "char foo" + size: 0 (as in 'print').
323   if (base::StartsWith(type_and_name, "char ") && size == 0) {
324     *out = kFtraceCString;
325     return true;
326   }
327 
328   if (base::StartsWith(type_and_name, "bool ")) {
329     *out = kFtraceBool;
330     return true;
331   }
332 
333   if (base::StartsWith(type_and_name, "ino_t ") ||
334       base::StartsWith(type_and_name, "i_ino ")) {
335     if (size == 4) {
336       *out = kFtraceInode32;
337       return true;
338     } else if (size == 8) {
339       *out = kFtraceInode64;
340       return true;
341     }
342   }
343 
344   if (base::StartsWith(type_and_name, "dev_t ")) {
345     if (size == 4) {
346       *out = kFtraceDevId32;
347       return true;
348     } else if (size == 8) {
349       *out = kFtraceDevId64;
350       return true;
351     }
352   }
353 
354   // Pids (as in 'sched_switch').
355   if (base::StartsWith(type_and_name, "pid_t ") && size == 4) {
356     *out = kFtracePid32;
357     return true;
358   }
359 
360   if (Contains(type_and_name, "common_pid") && size == 4) {
361     *out = kFtraceCommonPid32;
362     return true;
363   }
364 
365   // Ints of various sizes:
366   if (size == 1 && is_signed) {
367     *out = kFtraceInt8;
368     return true;
369   } else if (size == 1 && !is_signed) {
370     *out = kFtraceUint8;
371     return true;
372   } else if (size == 2 && is_signed) {
373     *out = kFtraceInt16;
374     return true;
375   } else if (size == 2 && !is_signed) {
376     *out = kFtraceUint16;
377     return true;
378   } else if (size == 4 && is_signed) {
379     *out = kFtraceInt32;
380     return true;
381   } else if (size == 4 && !is_signed) {
382     *out = kFtraceUint32;
383     return true;
384   } else if (size == 8 && is_signed) {
385     *out = kFtraceInt64;
386     return true;
387   } else if (size == 8 && !is_signed) {
388     *out = kFtraceUint64;
389     return true;
390   }
391 
392   PERFETTO_DLOG("Could not infer ftrace type for '%s'", type_and_name.c_str());
393   return false;
394 }
395 
396 // static
397 ProtoTranslationTable::FtracePageHeaderSpec
DefaultPageHeaderSpecForTesting()398 ProtoTranslationTable::DefaultPageHeaderSpecForTesting() {
399   std::string page_header =
400       R"(	field: u64 timestamp;	offset:0;	size:8;	signed:0;
401 	field: local_t commit;	offset:8;	size:8;	signed:1;
402 	field: int overwrite;	offset:8;	size:1;	signed:1;
403 	field: char data;	offset:16;	size:4080;	signed:0;)";
404   std::vector<FtraceEvent::Field> page_header_fields;
405   PERFETTO_CHECK(ParseFtraceEventBody(std::move(page_header), nullptr,
406                                       &page_header_fields));
407   return MakeFtracePageHeaderSpec(page_header_fields);
408 }
409 
410 // static
Create(const FtraceProcfs * ftrace_procfs,std::vector<Event> events,std::vector<Field> common_fields)411 std::unique_ptr<ProtoTranslationTable> ProtoTranslationTable::Create(
412     const FtraceProcfs* ftrace_procfs,
413     std::vector<Event> events,
414     std::vector<Field> common_fields) {
415   bool common_fields_processed = false;
416   uint16_t common_fields_end = 0;
417 
418   std::string page_header = ftrace_procfs->ReadPageHeaderFormat();
419   bool ftrace_header_parsed = false;
420   FtracePageHeaderSpec header_spec{};
421   if (!page_header.empty()) {
422     std::vector<FtraceEvent::Field> page_header_fields;
423     ftrace_header_parsed = ParseFtraceEventBody(std::move(page_header), nullptr,
424                                                 &page_header_fields);
425     header_spec = MakeFtracePageHeaderSpec(page_header_fields);
426   }
427 
428   if (!ftrace_header_parsed) {
429     PERFETTO_LOG("Failed to parse ftrace page header, using fallback layout");
430     header_spec = GuessFtracePageHeaderSpec();
431   }
432 
433   for (Event& event : events) {
434     if (event.proto_field_id ==
435         protos::pbzero::FtraceEvent::kGenericFieldNumber) {
436       continue;
437     }
438     PERFETTO_DCHECK(event.name);
439     PERFETTO_DCHECK(event.group);
440     PERFETTO_DCHECK(event.proto_field_id);
441     PERFETTO_DCHECK(!event.ftrace_event_id);
442 
443     std::string contents =
444         ftrace_procfs->ReadEventFormat(event.group, event.name);
445     FtraceEvent ftrace_event;
446     if (contents.empty() || !ParseFtraceEvent(contents, &ftrace_event)) {
447       if (!strcmp(event.group, "ftrace") && !strcmp(event.name, "print")) {
448         // On some "user" builds of Android <P the ftrace/print event is not
449         // selinux-allowed. Thankfully this event is an always-on built-in
450         // so we don't need to write to its 'enable' file. However we need to
451         // know its binary layout to decode it, so we hardcode it.
452         ftrace_event.id = 5;  // Seems quite stable across kernels.
453         ftrace_event.name = "print";
454         // The only field we care about is:
455         // field:char buf; offset:16; size:0; signed:0;
456         ftrace_event.fields.emplace_back(
457             FtraceEvent::Field{"char buf", 16, 0, 0});
458       } else {
459         continue;
460       }
461     }
462 
463     // Special case function_graph events as they use a u64 field for kernel
464     // function pointers. Fudge the type so that |MergeFields| correctly tags
465     // the fields for kernel address symbolization (kFtraceSymAddr64).
466     if (!strcmp(event.group, "ftrace") &&
467         (!strcmp(event.name, "funcgraph_entry") ||
468          !strcmp(event.name, "funcgraph_exit"))) {
469       for (auto& field : ftrace_event.fields) {
470         if (GetNameFromTypeAndName(field.type_and_name) == "func") {
471           field.type_and_name = "void * func";
472           break;
473         }
474       }
475     }
476 
477     event.ftrace_event_id = ftrace_event.id;
478 
479     if (!common_fields_processed) {
480       common_fields_end =
481           MergeFields(ftrace_event.common_fields, &common_fields, event.name);
482       common_fields_processed = true;
483     }
484 
485     uint16_t fields_end =
486         MergeFields(ftrace_event.fields, &event.fields, event.name);
487 
488     event.size = std::max<uint16_t>(fields_end, common_fields_end);
489   }
490 
491   events.erase(std::remove_if(events.begin(), events.end(),
492                               [](const Event& event) {
493                                 return event.proto_field_id == 0 ||
494                                        event.ftrace_event_id == 0;
495                               }),
496                events.end());
497 
498   // Pre-parse certain scheduler events, and see if the compile-time assumptions
499   // about their format hold for this kernel.
500   CompactSchedEventFormat compact_sched =
501       ValidateFormatForCompactSched(events, common_fields);
502 
503   std::string text = ftrace_procfs->ReadPrintkFormats();
504   PrintkMap printk_formats = ParsePrintkFormats(text);
505 
506   auto table = std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
507       ftrace_procfs, events, std::move(common_fields), header_spec,
508       compact_sched, std::move(printk_formats)));
509   return table;
510 }
511 
ProtoTranslationTable(const FtraceProcfs * ftrace_procfs,const std::vector<Event> & events,std::vector<Field> common_fields,FtracePageHeaderSpec ftrace_page_header_spec,CompactSchedEventFormat compact_sched_format,PrintkMap printk_formats)512 ProtoTranslationTable::ProtoTranslationTable(
513     const FtraceProcfs* ftrace_procfs,
514     const std::vector<Event>& events,
515     std::vector<Field> common_fields,
516     FtracePageHeaderSpec ftrace_page_header_spec,
517     CompactSchedEventFormat compact_sched_format,
518     PrintkMap printk_formats)
519     : ftrace_procfs_(ftrace_procfs),
520       events_(BuildEventsDeque(events)),
521       largest_id_(events_.size() - 1),
522       common_fields_(std::move(common_fields)),
523       ftrace_page_header_spec_(ftrace_page_header_spec),
524       compact_sched_format_(compact_sched_format),
525       printk_formats_(printk_formats) {
526   for (const Event& event : events) {
527     group_and_name_to_event_[GroupAndName(event.group, event.name)] =
528         &events_.at(event.ftrace_event_id);
529     name_to_events_[event.name].push_back(&events_.at(event.ftrace_event_id));
530     group_to_events_[event.group].push_back(&events_.at(event.ftrace_event_id));
531   }
532   for (const Field& field : common_fields_) {
533     if (field.proto_field_id == protos::pbzero::FtraceEvent::kPidFieldNumber) {
534       common_pid_ = field;
535     }
536   }
537 }
538 
GetOrCreateEvent(const GroupAndName & group_and_name)539 const Event* ProtoTranslationTable::GetOrCreateEvent(
540     const GroupAndName& group_and_name) {
541   const Event* event = GetEvent(group_and_name);
542   if (event)
543     return event;
544   // The ftrace event does not already exist so a new one will be created
545   // by parsing the format file.
546   std::string contents = ftrace_procfs_->ReadEventFormat(group_and_name.group(),
547                                                          group_and_name.name());
548   if (contents.empty())
549     return nullptr;
550   FtraceEvent ftrace_event = {};
551   ParseFtraceEvent(contents, &ftrace_event);
552 
553   // Ensure events vector is large enough
554   if (ftrace_event.id > largest_id_) {
555     events_.resize(ftrace_event.id + 1);
556     largest_id_ = ftrace_event.id;
557   }
558 
559   // Set known event variables
560   Event* e = &events_.at(ftrace_event.id);
561   e->ftrace_event_id = ftrace_event.id;
562   e->proto_field_id = protos::pbzero::FtraceEvent::kGenericFieldNumber;
563   e->name = InternString(group_and_name.name());
564   e->group = InternString(group_and_name.group());
565 
566   // Calculate size of common fields.
567   for (const FtraceEvent::Field& ftrace_field : ftrace_event.common_fields) {
568     uint16_t field_end = ftrace_field.offset + ftrace_field.size;
569     e->size = std::max(field_end, e->size);
570   }
571 
572   // For every field in the ftrace event, make a field in the generic event.
573   for (const FtraceEvent::Field& ftrace_field : ftrace_event.fields)
574     e->size = std::max(CreateGenericEventField(ftrace_field, *e), e->size);
575 
576   group_and_name_to_event_[group_and_name] = &events_.at(e->ftrace_event_id);
577   name_to_events_[e->name].push_back(&events_.at(e->ftrace_event_id));
578   group_to_events_[e->group].push_back(&events_.at(e->ftrace_event_id));
579 
580   return e;
581 }
582 
InternString(const std::string & str)583 const char* ProtoTranslationTable::InternString(const std::string& str) {
584   auto it_and_inserted = interned_strings_.insert(str);
585   return it_and_inserted.first->c_str();
586 }
587 
CreateGenericEventField(const FtraceEvent::Field & ftrace_field,Event & event)588 uint16_t ProtoTranslationTable::CreateGenericEventField(
589     const FtraceEvent::Field& ftrace_field,
590     Event& event) {
591   uint16_t field_end = ftrace_field.offset + ftrace_field.size;
592   std::string field_name = GetNameFromTypeAndName(ftrace_field.type_and_name);
593   if (field_name.empty()) {
594     PERFETTO_DLOG("Field: %s could not be added to the generic event.",
595                   ftrace_field.type_and_name.c_str());
596     return field_end;
597   }
598   event.fields.emplace_back();
599   Field* field = &event.fields.back();
600   field->ftrace_name = InternString(field_name);
601   if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
602                        ftrace_field.is_signed, &field->ftrace_type)) {
603     PERFETTO_DLOG(
604         "Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
605         "size:%d "
606         "signed:%d)",
607         event.name, field->ftrace_name, ftrace_field.type_and_name.c_str(),
608         ftrace_field.size, ftrace_field.is_signed);
609     event.fields.pop_back();
610     return field_end;
611   }
612   SetProtoType(field->ftrace_type, &field->proto_field_type,
613                &field->proto_field_id);
614   field->ftrace_offset = ftrace_field.offset;
615   field->ftrace_size = ftrace_field.size;
616   // Proto type is set based on ftrace type so all fields should have a
617   // translation strategy.
618   bool success = SetTranslationStrategy(
619       field->ftrace_type, field->proto_field_type, &field->strategy);
620   PERFETTO_DCHECK(success);
621   return field_end;
622 }
623 
624 EventFilter::EventFilter() = default;
625 EventFilter::~EventFilter() = default;
626 
AddEnabledEvent(size_t ftrace_event_id)627 void EventFilter::AddEnabledEvent(size_t ftrace_event_id) {
628   if (ftrace_event_id >= enabled_ids_.size())
629     enabled_ids_.resize(ftrace_event_id + 1);
630   enabled_ids_[ftrace_event_id] = true;
631 }
632 
DisableEvent(size_t ftrace_event_id)633 void EventFilter::DisableEvent(size_t ftrace_event_id) {
634   if (ftrace_event_id >= enabled_ids_.size())
635     return;
636   enabled_ids_[ftrace_event_id] = false;
637 }
638 
IsEventEnabled(size_t ftrace_event_id) const639 bool EventFilter::IsEventEnabled(size_t ftrace_event_id) const {
640   if (ftrace_event_id == 0 || ftrace_event_id >= enabled_ids_.size())
641     return false;
642   return enabled_ids_[ftrace_event_id];
643 }
644 
GetEnabledEvents() const645 std::set<size_t> EventFilter::GetEnabledEvents() const {
646   std::set<size_t> enabled;
647   for (size_t i = 0; i < enabled_ids_.size(); i++) {
648     if (enabled_ids_[i]) {
649       enabled.insert(i);
650     }
651   }
652   return enabled;
653 }
654 
EnableEventsFrom(const EventFilter & other)655 void EventFilter::EnableEventsFrom(const EventFilter& other) {
656   size_t max_length = std::max(enabled_ids_.size(), other.enabled_ids_.size());
657   enabled_ids_.resize(max_length);
658   for (size_t i = 0; i < other.enabled_ids_.size(); i++) {
659     if (other.enabled_ids_[i])
660       enabled_ids_[i] = true;
661   }
662 }
663 
664 ProtoTranslationTable::~ProtoTranslationTable() = default;
665 
666 }  // namespace perfetto
667