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 kFtraceSymAddr32:
243 case kFtraceSymAddr64:
244 *proto_type = ProtoSchemaType::kUint64;
245 *proto_field_id = GenericFtraceEvent::Field::kUintValueFieldNumber;
246 break;
247 case kInvalidFtraceFieldType:
248 PERFETTO_FATAL("Unexpected ftrace field type");
249 }
250 }
251
252 } // namespace
253
254 // This is similar but different from InferProtoType (see format_parser.cc).
255 // TODO(hjd): Fold FtraceEvent(::Field) into Event.
InferFtraceType(const std::string & type_and_name,size_t size,bool is_signed,FtraceFieldType * out)256 bool InferFtraceType(const std::string& type_and_name,
257 size_t size,
258 bool is_signed,
259 FtraceFieldType* out) {
260 // Fixed length strings: e.g. "char foo[16]".
261 //
262 // We don't care about the number, since we get the size as it's own field and
263 // since it can be a string defined elsewhere in a kernel header file.
264 //
265 // Somewhat awkwardly these fields are both fixed size and null terminated
266 // meaning that we can't just drop them directly into the protobuf (since if
267 // the string is shorter than 15 characters we want only the bit up to the
268 // null terminator).
269 //
270 // In some rare cases (e.g. old kernel bugs) these strings might not be null
271 // terminated (b/205763418).
272 if (Match(type_and_name.c_str(),
273 R"(char [a-zA-Z_][a-zA-Z_0-9]*\[[a-zA-Z_0-9]+\])")) {
274 *out = kFtraceFixedCString;
275 return true;
276 }
277
278 // String pointers: "__data_loc char[] foo" (as in
279 // 'cpufreq_interactive_boost').
280 // TODO(fmayer): Handle u32[], u8[], __u8[] as well.
281 if (Contains(type_and_name, "__data_loc char[] ")) {
282 if (size != 4) {
283 PERFETTO_ELOG("__data_loc with incorrect size: %s (%zd)",
284 type_and_name.c_str(), size);
285 return false;
286 }
287 *out = kFtraceDataLoc;
288 return true;
289 }
290
291 // Parsing of sys_enter argument field declared as
292 // field:unsigned long args[6];
293 if (type_and_name == "unsigned long args[6]") {
294 if (size == 24) {
295 // 24 / 6 = 4 -> 32bit system
296 *out = kFtraceUint32;
297 return true;
298 } else if (size == 48) {
299 // 48 / 6 = 8 -> 64bit system
300 *out = kFtraceUint64;
301 return true;
302 }
303 }
304
305 if (Contains(type_and_name, "char[] ")) {
306 *out = kFtraceStringPtr;
307 return true;
308 }
309 if (Contains(type_and_name, "char * ")) {
310 *out = kFtraceStringPtr;
311 return true;
312 }
313
314 // Kernel addresses that need symbolization via kallsyms.
315 if (base::StartsWith(type_and_name, "void*") ||
316 base::StartsWith(type_and_name, "void *")) {
317 if (size == 4) {
318 *out = kFtraceSymAddr32;
319 return true;
320 } else if (size == 8) {
321 *out = kFtraceSymAddr64;
322 return true;
323 }
324 }
325
326 // Variable length strings: "char foo" + size: 0 (as in 'print').
327 if (base::StartsWith(type_and_name, "char ") && size == 0) {
328 *out = kFtraceCString;
329 return true;
330 }
331
332 if (base::StartsWith(type_and_name, "bool ")) {
333 *out = kFtraceBool;
334 return true;
335 }
336
337 if (base::StartsWith(type_and_name, "ino_t ") ||
338 base::StartsWith(type_and_name, "i_ino ")) {
339 if (size == 4) {
340 *out = kFtraceInode32;
341 return true;
342 } else if (size == 8) {
343 *out = kFtraceInode64;
344 return true;
345 }
346 }
347
348 if (base::StartsWith(type_and_name, "dev_t ")) {
349 if (size == 4) {
350 *out = kFtraceDevId32;
351 return true;
352 } else if (size == 8) {
353 *out = kFtraceDevId64;
354 return true;
355 }
356 }
357
358 // Pids (as in 'sched_switch').
359 if (base::StartsWith(type_and_name, "pid_t ") && size == 4) {
360 *out = kFtracePid32;
361 return true;
362 }
363
364 if (Contains(type_and_name, "common_pid") && size == 4) {
365 *out = kFtraceCommonPid32;
366 return true;
367 }
368
369 // Ints of various sizes:
370 if (size == 1 && is_signed) {
371 *out = kFtraceInt8;
372 return true;
373 } else if (size == 1 && !is_signed) {
374 *out = kFtraceUint8;
375 return true;
376 } else if (size == 2 && is_signed) {
377 *out = kFtraceInt16;
378 return true;
379 } else if (size == 2 && !is_signed) {
380 *out = kFtraceUint16;
381 return true;
382 } else if (size == 4 && is_signed) {
383 *out = kFtraceInt32;
384 return true;
385 } else if (size == 4 && !is_signed) {
386 *out = kFtraceUint32;
387 return true;
388 } else if (size == 8 && is_signed) {
389 *out = kFtraceInt64;
390 return true;
391 } else if (size == 8 && !is_signed) {
392 *out = kFtraceUint64;
393 return true;
394 }
395
396 PERFETTO_DLOG("Could not infer ftrace type for '%s'", type_and_name.c_str());
397 return false;
398 }
399
400 // static
401 ProtoTranslationTable::FtracePageHeaderSpec
DefaultPageHeaderSpecForTesting()402 ProtoTranslationTable::DefaultPageHeaderSpecForTesting() {
403 std::string page_header =
404 R"( field: u64 timestamp; offset:0; size:8; signed:0;
405 field: local_t commit; offset:8; size:8; signed:1;
406 field: int overwrite; offset:8; size:1; signed:1;
407 field: char data; offset:16; size:4080; signed:0;)";
408 std::vector<FtraceEvent::Field> page_header_fields;
409 PERFETTO_CHECK(ParseFtraceEventBody(std::move(page_header), nullptr,
410 &page_header_fields));
411 return MakeFtracePageHeaderSpec(page_header_fields);
412 }
413
414 // static
Create(const FtraceProcfs * ftrace_procfs,std::vector<Event> events,std::vector<Field> common_fields)415 std::unique_ptr<ProtoTranslationTable> ProtoTranslationTable::Create(
416 const FtraceProcfs* ftrace_procfs,
417 std::vector<Event> events,
418 std::vector<Field> common_fields) {
419 bool common_fields_processed = false;
420 uint16_t common_fields_end = 0;
421
422 std::string page_header = ftrace_procfs->ReadPageHeaderFormat();
423 bool ftrace_header_parsed = false;
424 FtracePageHeaderSpec header_spec{};
425 if (!page_header.empty()) {
426 std::vector<FtraceEvent::Field> page_header_fields;
427 ftrace_header_parsed = ParseFtraceEventBody(std::move(page_header), nullptr,
428 &page_header_fields);
429 header_spec = MakeFtracePageHeaderSpec(page_header_fields);
430 }
431
432 if (!ftrace_header_parsed) {
433 PERFETTO_LOG("Failed to parse ftrace page header, using fallback layout");
434 header_spec = GuessFtracePageHeaderSpec();
435 }
436
437 for (Event& event : events) {
438 if (event.proto_field_id ==
439 protos::pbzero::FtraceEvent::kGenericFieldNumber) {
440 continue;
441 }
442 PERFETTO_DCHECK(event.name);
443 PERFETTO_DCHECK(event.group);
444 PERFETTO_DCHECK(event.proto_field_id);
445 PERFETTO_DCHECK(!event.ftrace_event_id);
446
447 std::string contents =
448 ftrace_procfs->ReadEventFormat(event.group, event.name);
449 FtraceEvent ftrace_event;
450 if (contents.empty() || !ParseFtraceEvent(contents, &ftrace_event)) {
451 if (!strcmp(event.group, "ftrace") && !strcmp(event.name, "print")) {
452 // On some "user" builds of Android <P the ftrace/print event is not
453 // selinux-allowed. Thankfully this event is an always-on built-in
454 // so we don't need to write to its 'enable' file. However we need to
455 // know its binary layout to decode it, so we hardcode it.
456 ftrace_event.id = 5; // Seems quite stable across kernels.
457 ftrace_event.name = "print";
458 // The only field we care about is:
459 // field:char buf; offset:16; size:0; signed:0;
460 ftrace_event.fields.emplace_back(
461 FtraceEvent::Field{"char buf", 16, 0, 0});
462 } else {
463 continue;
464 }
465 }
466
467 // Special case function_graph events as they use a u64 field for kernel
468 // function pointers. Fudge the type so that |MergeFields| correctly tags
469 // the fields for kernel address symbolization (kFtraceSymAddr64).
470 if (!strcmp(event.group, "ftrace") &&
471 (!strcmp(event.name, "funcgraph_entry") ||
472 !strcmp(event.name, "funcgraph_exit"))) {
473 for (auto& field : ftrace_event.fields) {
474 if (GetNameFromTypeAndName(field.type_and_name) == "func") {
475 field.type_and_name = "void * func";
476 break;
477 }
478 }
479 }
480
481 event.ftrace_event_id = ftrace_event.id;
482
483 if (!common_fields_processed) {
484 common_fields_end =
485 MergeFields(ftrace_event.common_fields, &common_fields, event.name);
486 common_fields_processed = true;
487 }
488
489 uint16_t fields_end =
490 MergeFields(ftrace_event.fields, &event.fields, event.name);
491
492 event.size = std::max<uint16_t>(fields_end, common_fields_end);
493 }
494
495 events.erase(std::remove_if(events.begin(), events.end(),
496 [](const Event& event) {
497 return event.proto_field_id == 0 ||
498 event.ftrace_event_id == 0;
499 }),
500 events.end());
501
502 // Pre-parse certain scheduler events, and see if the compile-time assumptions
503 // about their format hold for this kernel.
504 CompactSchedEventFormat compact_sched =
505 ValidateFormatForCompactSched(events, common_fields);
506
507 std::string text = ftrace_procfs->ReadPrintkFormats();
508 PrintkMap printk_formats = ParsePrintkFormats(text);
509
510 auto table = std::unique_ptr<ProtoTranslationTable>(new ProtoTranslationTable(
511 ftrace_procfs, events, std::move(common_fields), header_spec,
512 compact_sched, std::move(printk_formats)));
513 return table;
514 }
515
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)516 ProtoTranslationTable::ProtoTranslationTable(
517 const FtraceProcfs* ftrace_procfs,
518 const std::vector<Event>& events,
519 std::vector<Field> common_fields,
520 FtracePageHeaderSpec ftrace_page_header_spec,
521 CompactSchedEventFormat compact_sched_format,
522 PrintkMap printk_formats)
523 : ftrace_procfs_(ftrace_procfs),
524 events_(BuildEventsDeque(events)),
525 largest_id_(events_.size() - 1),
526 common_fields_(std::move(common_fields)),
527 ftrace_page_header_spec_(ftrace_page_header_spec),
528 compact_sched_format_(compact_sched_format),
529 printk_formats_(printk_formats) {
530 for (const Event& event : events) {
531 group_and_name_to_event_[GroupAndName(event.group, event.name)] =
532 &events_.at(event.ftrace_event_id);
533 name_to_events_[event.name].push_back(&events_.at(event.ftrace_event_id));
534 group_to_events_[event.group].push_back(&events_.at(event.ftrace_event_id));
535 }
536 for (const Field& field : common_fields_) {
537 if (field.proto_field_id == protos::pbzero::FtraceEvent::kPidFieldNumber) {
538 common_pid_ = field;
539 }
540 }
541 }
542
CreateEventWithProtoId(const GroupAndName & group_and_name,uint32_t proto_field_id)543 const Event* ProtoTranslationTable::CreateEventWithProtoId(
544 const GroupAndName& group_and_name,
545 uint32_t proto_field_id) {
546 // The ftrace event does not already exist so a new one will be created
547 // by parsing the format file.
548 std::string contents = ftrace_procfs_->ReadEventFormat(group_and_name.group(),
549 group_and_name.name());
550 if (contents.empty())
551 return nullptr;
552 FtraceEvent ftrace_event = {};
553 ParseFtraceEvent(contents, &ftrace_event);
554
555 // Ensure events vector is large enough
556 if (ftrace_event.id > largest_id_) {
557 events_.resize(ftrace_event.id + 1);
558 largest_id_ = ftrace_event.id;
559 }
560
561 // Set known event variables
562 Event* e = &events_.at(ftrace_event.id);
563 e->ftrace_event_id = ftrace_event.id;
564 e->proto_field_id = proto_field_id;
565 e->name = InternString(group_and_name.name());
566 e->group = InternString(group_and_name.group());
567
568 // Calculate size of common fields.
569 for (const FtraceEvent::Field& ftrace_field : ftrace_event.common_fields) {
570 uint16_t field_end = ftrace_field.offset + ftrace_field.size;
571 e->size = std::max(field_end, e->size);
572 }
573
574 // For every field in the ftrace event, make a field in the generic event.
575 for (const FtraceEvent::Field& ftrace_field : ftrace_event.fields)
576 e->size = std::max(CreateGenericEventField(ftrace_field, *e), e->size);
577
578 group_and_name_to_event_[group_and_name] = &events_.at(e->ftrace_event_id);
579 name_to_events_[e->name].push_back(&events_.at(e->ftrace_event_id));
580 group_to_events_[e->group].push_back(&events_.at(e->ftrace_event_id));
581
582 return e;
583 }
584
GetOrCreateEvent(const GroupAndName & group_and_name)585 const Event* ProtoTranslationTable::GetOrCreateEvent(
586 const GroupAndName& group_and_name) {
587 const Event* event = GetEvent(group_and_name);
588 if (event)
589 return event;
590 return CreateEventWithProtoId(
591 group_and_name, protos::pbzero::FtraceEvent::kGenericFieldNumber);
592 }
593
GetOrCreateKprobeEvent(const GroupAndName & group_and_name)594 const Event* ProtoTranslationTable::GetOrCreateKprobeEvent(
595 const GroupAndName& group_and_name) {
596 const Event* event = GetEvent(group_and_name);
597 const uint32_t proto_field_id =
598 protos::pbzero::FtraceEvent::kKprobeEventFieldNumber;
599 if (event) {
600 if (event->proto_field_id != proto_field_id) {
601 return nullptr;
602 }
603 return event;
604 }
605 return CreateEventWithProtoId(group_and_name, proto_field_id);
606 }
607
RemoveEvent(const GroupAndName & group_and_name)608 void ProtoTranslationTable::RemoveEvent(const GroupAndName& group_and_name) {
609 const std::string& group = group_and_name.group();
610 const std::string& name = group_and_name.name();
611 auto it = group_and_name_to_event_.find(group_and_name);
612 if (it == group_and_name_to_event_.end()) {
613 return;
614 }
615 Event* event = &events_[it->second->ftrace_event_id];
616 event->ftrace_event_id = 0;
617 if (auto it2 = name_to_events_.find(name); it2 != name_to_events_.end()) {
618 std::vector<const Event*>& events = it2->second;
619 events.erase(std::remove(events.begin(), events.end(), event),
620 events.end());
621 if (events.empty()) {
622 name_to_events_.erase(it2);
623 }
624 }
625 if (auto it2 = group_to_events_.find(group); it2 != group_to_events_.end()) {
626 std::vector<const Event*>& events = it2->second;
627 events.erase(std::remove(events.begin(), events.end(), event),
628 events.end());
629 if (events.empty()) {
630 group_to_events_.erase(it2);
631 }
632 }
633 group_and_name_to_event_.erase(it);
634 }
635
InternString(const std::string & str)636 const char* ProtoTranslationTable::InternString(const std::string& str) {
637 auto it_and_inserted = interned_strings_.insert(str);
638 return it_and_inserted.first->c_str();
639 }
640
CreateGenericEventField(const FtraceEvent::Field & ftrace_field,Event & event)641 uint16_t ProtoTranslationTable::CreateGenericEventField(
642 const FtraceEvent::Field& ftrace_field,
643 Event& event) {
644 uint16_t field_end = ftrace_field.offset + ftrace_field.size;
645 std::string field_name = GetNameFromTypeAndName(ftrace_field.type_and_name);
646 if (field_name.empty()) {
647 PERFETTO_DLOG("Field: %s could not be added to the generic event.",
648 ftrace_field.type_and_name.c_str());
649 return field_end;
650 }
651 event.fields.emplace_back();
652 Field* field = &event.fields.back();
653 field->ftrace_name = InternString(field_name);
654 if (!InferFtraceType(ftrace_field.type_and_name, ftrace_field.size,
655 ftrace_field.is_signed, &field->ftrace_type)) {
656 PERFETTO_DLOG(
657 "Failed to infer ftrace field type for \"%s.%s\" (type:\"%s\" "
658 "size:%d "
659 "signed:%d)",
660 event.name, field->ftrace_name, ftrace_field.type_and_name.c_str(),
661 ftrace_field.size, ftrace_field.is_signed);
662 event.fields.pop_back();
663 return field_end;
664 }
665 SetProtoType(field->ftrace_type, &field->proto_field_type,
666 &field->proto_field_id);
667 field->ftrace_offset = ftrace_field.offset;
668 field->ftrace_size = ftrace_field.size;
669 // Proto type is set based on ftrace type so all fields should have a
670 // translation strategy.
671 bool success = SetTranslationStrategy(
672 field->ftrace_type, field->proto_field_type, &field->strategy);
673 PERFETTO_DCHECK(success);
674 return field_end;
675 }
676
677 EventFilter::EventFilter() = default;
678 EventFilter::~EventFilter() = default;
679
AddEnabledEvent(size_t ftrace_event_id)680 void EventFilter::AddEnabledEvent(size_t ftrace_event_id) {
681 if (ftrace_event_id >= enabled_ids_.size())
682 enabled_ids_.resize(ftrace_event_id + 1);
683 enabled_ids_[ftrace_event_id] = true;
684 }
685
DisableEvent(size_t ftrace_event_id)686 void EventFilter::DisableEvent(size_t ftrace_event_id) {
687 if (ftrace_event_id >= enabled_ids_.size())
688 return;
689 enabled_ids_[ftrace_event_id] = false;
690 }
691
IsEventEnabled(size_t ftrace_event_id) const692 bool EventFilter::IsEventEnabled(size_t ftrace_event_id) const {
693 if (ftrace_event_id == 0 || ftrace_event_id >= enabled_ids_.size())
694 return false;
695 return enabled_ids_[ftrace_event_id];
696 }
697
GetEnabledEvents() const698 std::set<size_t> EventFilter::GetEnabledEvents() const {
699 std::set<size_t> enabled;
700 for (size_t i = 0; i < enabled_ids_.size(); i++) {
701 if (enabled_ids_[i]) {
702 enabled.insert(i);
703 }
704 }
705 return enabled;
706 }
707
EnableEventsFrom(const EventFilter & other)708 void EventFilter::EnableEventsFrom(const EventFilter& other) {
709 size_t max_length = std::max(enabled_ids_.size(), other.enabled_ids_.size());
710 enabled_ids_.resize(max_length);
711 for (size_t i = 0; i < other.enabled_ids_.size(); i++) {
712 if (other.enabled_ids_[i])
713 enabled_ids_[i] = true;
714 }
715 }
716
717 ProtoTranslationTable::~ProtoTranslationTable() = default;
718
719 } // namespace perfetto
720