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