1 /* 2 * Copyright (C) 2015 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 "event_selection_set.h" 18 19 #include <android-base/logging.h> 20 21 #include "environment.h" 22 #include "event_attr.h" 23 #include "event_type.h" 24 #include "IOEventLoop.h" 25 #include "perf_regs.h" 26 #include "utils.h" 27 28 constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000; 29 constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1; 30 31 bool IsBranchSamplingSupported() { 32 const EventType* type = FindEventTypeByName("cpu-cycles"); 33 if (type == nullptr) { 34 return false; 35 } 36 perf_event_attr attr = CreateDefaultPerfEventAttr(*type); 37 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; 38 attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY; 39 return IsEventAttrSupported(attr); 40 } 41 42 bool IsDwarfCallChainSamplingSupported() { 43 const EventType* type = FindEventTypeByName("cpu-cycles"); 44 if (type == nullptr) { 45 return false; 46 } 47 perf_event_attr attr = CreateDefaultPerfEventAttr(*type); 48 attr.sample_type |= 49 PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; 50 attr.exclude_callchain_user = 1; 51 attr.sample_regs_user = GetSupportedRegMask(GetBuildArch()); 52 attr.sample_stack_user = 8192; 53 return IsEventAttrSupported(attr); 54 } 55 56 bool EventSelectionSet::BuildAndCheckEventSelection( 57 const std::string& event_name, EventSelection* selection) { 58 std::unique_ptr<EventTypeAndModifier> event_type = ParseEventType(event_name); 59 if (event_type == nullptr) { 60 return false; 61 } 62 if (for_stat_cmd_) { 63 if (event_type->event_type.name == "cpu-clock" || 64 event_type->event_type.name == "task-clock") { 65 if (event_type->exclude_user || event_type->exclude_kernel) { 66 LOG(ERROR) << "Modifier u and modifier k used in event type " 67 << event_type->event_type.name 68 << " are not supported by the kernel."; 69 return false; 70 } 71 } 72 } 73 selection->event_type_modifier = *event_type; 74 selection->event_attr = CreateDefaultPerfEventAttr(event_type->event_type); 75 selection->event_attr.exclude_user = event_type->exclude_user; 76 selection->event_attr.exclude_kernel = event_type->exclude_kernel; 77 selection->event_attr.exclude_hv = event_type->exclude_hv; 78 selection->event_attr.exclude_host = event_type->exclude_host; 79 selection->event_attr.exclude_guest = event_type->exclude_guest; 80 selection->event_attr.precise_ip = event_type->precise_ip; 81 if (!IsEventAttrSupported(selection->event_attr)) { 82 LOG(ERROR) << "Event type '" << event_type->name 83 << "' is not supported on the device"; 84 return false; 85 } 86 selection->event_fds.clear(); 87 88 for (const auto& group : groups_) { 89 for (const auto& sel : group) { 90 if (sel.event_type_modifier.name == selection->event_type_modifier.name) { 91 LOG(ERROR) << "Event type '" << sel.event_type_modifier.name 92 << "' appears more than once"; 93 return false; 94 } 95 } 96 } 97 return true; 98 } 99 100 bool EventSelectionSet::AddEventType(const std::string& event_name) { 101 return AddEventGroup(std::vector<std::string>(1, event_name)); 102 } 103 104 bool EventSelectionSet::AddEventGroup( 105 const std::vector<std::string>& event_names) { 106 EventSelectionGroup group; 107 for (const auto& event_name : event_names) { 108 EventSelection selection; 109 if (!BuildAndCheckEventSelection(event_name, &selection)) { 110 return false; 111 } 112 group.push_back(std::move(selection)); 113 } 114 groups_.push_back(std::move(group)); 115 UnionSampleType(); 116 return true; 117 } 118 119 std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const { 120 std::vector<const EventType*> result; 121 for (const auto& group : groups_) { 122 for (const auto& selection : group) { 123 if (selection.event_type_modifier.event_type.type == 124 PERF_TYPE_TRACEPOINT) { 125 result.push_back(&selection.event_type_modifier.event_type); 126 } 127 } 128 } 129 return result; 130 } 131 132 bool EventSelectionSet::HasInplaceSampler() const { 133 for (const auto& group : groups_) { 134 for (const auto& sel : group) { 135 if (sel.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS && 136 sel.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) { 137 return true; 138 } 139 } 140 } 141 return false; 142 } 143 144 std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const { 145 std::vector<EventAttrWithId> result; 146 for (const auto& group : groups_) { 147 for (const auto& selection : group) { 148 EventAttrWithId attr_id; 149 attr_id.attr = &selection.event_attr; 150 for (const auto& fd : selection.event_fds) { 151 attr_id.ids.push_back(fd->Id()); 152 } 153 if (!selection.inplace_samplers.empty()) { 154 attr_id.ids.push_back(selection.inplace_samplers[0]->Id()); 155 } 156 result.push_back(attr_id); 157 } 158 } 159 return result; 160 } 161 162 // Union the sample type of different event attrs can make reading sample 163 // records in perf.data easier. 164 void EventSelectionSet::UnionSampleType() { 165 uint64_t sample_type = 0; 166 for (const auto& group : groups_) { 167 for (const auto& selection : group) { 168 sample_type |= selection.event_attr.sample_type; 169 } 170 } 171 for (auto& group : groups_) { 172 for (auto& selection : group) { 173 selection.event_attr.sample_type = sample_type; 174 } 175 } 176 } 177 178 void EventSelectionSet::SetEnableOnExec(bool enable) { 179 for (auto& group : groups_) { 180 for (auto& selection : group) { 181 // If sampling is enabled on exec, then it is disabled at startup, 182 // otherwise it should be enabled at startup. Don't use 183 // ioctl(PERF_EVENT_IOC_ENABLE) to enable it after perf_event_open(). 184 // Because some android kernels can't handle ioctl() well when cpu-hotplug 185 // happens. See http://b/25193162. 186 if (enable) { 187 selection.event_attr.enable_on_exec = 1; 188 selection.event_attr.disabled = 1; 189 } else { 190 selection.event_attr.enable_on_exec = 0; 191 selection.event_attr.disabled = 0; 192 } 193 } 194 } 195 } 196 197 bool EventSelectionSet::GetEnableOnExec() { 198 for (const auto& group : groups_) { 199 for (const auto& selection : group) { 200 if (selection.event_attr.enable_on_exec == 0) { 201 return false; 202 } 203 } 204 } 205 return true; 206 } 207 208 void EventSelectionSet::SampleIdAll() { 209 for (auto& group : groups_) { 210 for (auto& selection : group) { 211 selection.event_attr.sample_id_all = 1; 212 } 213 } 214 } 215 216 void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) { 217 for (auto& group : groups_) { 218 for (auto& selection : group) { 219 selection.event_attr.freq = 1; 220 selection.event_attr.sample_freq = sample_freq; 221 } 222 } 223 } 224 225 void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) { 226 for (auto& group : groups_) { 227 for (auto& selection : group) { 228 selection.event_attr.freq = 0; 229 selection.event_attr.sample_period = sample_period; 230 } 231 } 232 } 233 234 void EventSelectionSet::UseDefaultSampleFreq() { 235 for (auto& group : groups_) { 236 for (auto& selection : group) { 237 if (selection.event_type_modifier.event_type.type == 238 PERF_TYPE_TRACEPOINT) { 239 selection.event_attr.freq = 0; 240 selection.event_attr.sample_period = 241 DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT; 242 } else { 243 selection.event_attr.freq = 1; 244 selection.event_attr.sample_freq = 245 DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT; 246 } 247 } 248 } 249 } 250 251 bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) { 252 if (branch_sample_type != 0 && 253 (branch_sample_type & 254 (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL | 255 PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) { 256 LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex 257 << branch_sample_type; 258 return false; 259 } 260 if (branch_sample_type != 0 && !IsBranchSamplingSupported()) { 261 LOG(ERROR) << "branch stack sampling is not supported on this device."; 262 return false; 263 } 264 for (auto& group : groups_) { 265 for (auto& selection : group) { 266 perf_event_attr& attr = selection.event_attr; 267 if (branch_sample_type != 0) { 268 attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; 269 } else { 270 attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK; 271 } 272 attr.branch_sample_type = branch_sample_type; 273 } 274 } 275 return true; 276 } 277 278 void EventSelectionSet::EnableFpCallChainSampling() { 279 for (auto& group : groups_) { 280 for (auto& selection : group) { 281 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN; 282 } 283 } 284 } 285 286 bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) { 287 if (!IsDwarfCallChainSamplingSupported()) { 288 LOG(ERROR) << "dwarf callchain sampling is not supported on this device."; 289 return false; 290 } 291 for (auto& group : groups_) { 292 for (auto& selection : group) { 293 selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN | 294 PERF_SAMPLE_REGS_USER | 295 PERF_SAMPLE_STACK_USER; 296 selection.event_attr.exclude_callchain_user = 1; 297 selection.event_attr.sample_regs_user = 298 GetSupportedRegMask(GetMachineArch()); 299 selection.event_attr.sample_stack_user = dump_stack_size; 300 } 301 } 302 return true; 303 } 304 305 void EventSelectionSet::SetInherit(bool enable) { 306 for (auto& group : groups_) { 307 for (auto& selection : group) { 308 selection.event_attr.inherit = (enable ? 1 : 0); 309 } 310 } 311 } 312 313 bool EventSelectionSet::NeedKernelSymbol() const { 314 for (const auto& group : groups_) { 315 for (const auto& selection : group) { 316 if (!selection.event_type_modifier.exclude_kernel) { 317 return true; 318 } 319 } 320 } 321 return false; 322 } 323 324 static bool CheckIfCpusOnline(const std::vector<int>& cpus) { 325 std::vector<int> online_cpus = GetOnlineCpus(); 326 for (const auto& cpu : cpus) { 327 if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == 328 online_cpus.end()) { 329 LOG(ERROR) << "cpu " << cpu << " is not online."; 330 return false; 331 } 332 } 333 return true; 334 } 335 336 bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group, 337 pid_t tid, int cpu, 338 std::string* failed_event_type) { 339 std::vector<std::unique_ptr<EventFd>> event_fds; 340 // Given a tid and cpu, events on the same group should be all opened 341 // successfully or all failed to open. 342 EventFd* group_fd = nullptr; 343 for (auto& selection : group) { 344 std::unique_ptr<EventFd> event_fd = 345 EventFd::OpenEventFile(selection.event_attr, tid, cpu, group_fd, false); 346 if (event_fd != nullptr) { 347 LOG(VERBOSE) << "OpenEventFile for " << event_fd->Name(); 348 event_fds.push_back(std::move(event_fd)); 349 } else { 350 if (failed_event_type != nullptr) { 351 *failed_event_type = selection.event_type_modifier.name; 352 return false; 353 } 354 } 355 if (group_fd == nullptr) { 356 group_fd = event_fd.get(); 357 } 358 } 359 for (size_t i = 0; i < group.size(); ++i) { 360 group[i].event_fds.push_back(std::move(event_fds[i])); 361 } 362 return true; 363 } 364 365 static std::map<pid_t, std::set<pid_t>> PrepareThreads(const std::set<pid_t>& processes, 366 const std::set<pid_t>& threads) { 367 std::map<pid_t, std::set<pid_t>> result; 368 for (auto& pid : processes) { 369 std::vector<pid_t> tids = GetThreadsInProcess(pid); 370 std::set<pid_t>& threads_in_process = result[pid]; 371 threads_in_process.insert(tids.begin(), tids.end()); 372 } 373 for (auto& tid : threads) { 374 // tid = -1 means monitoring all threads. 375 if (tid == -1) { 376 result[-1].insert(-1); 377 } else { 378 pid_t pid; 379 if (GetProcessForThread(tid, &pid)) { 380 result[pid].insert(tid); 381 } 382 } 383 } 384 return result; 385 } 386 387 bool EventSelectionSet::OpenEventFiles(const std::vector<int>& on_cpus) { 388 std::vector<int> cpus = on_cpus; 389 if (!cpus.empty()) { 390 // cpus = {-1} means open an event file for all cpus. 391 if (!(cpus.size() == 1 && cpus[0] == -1) && !CheckIfCpusOnline(cpus)) { 392 return false; 393 } 394 } else { 395 cpus = GetOnlineCpus(); 396 } 397 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_); 398 for (auto& group : groups_) { 399 if (IsUserSpaceSamplerGroup(group)) { 400 if (!OpenUserSpaceSamplersOnGroup(group, process_map)) { 401 return false; 402 } 403 } else { 404 for (const auto& pair : process_map) { 405 size_t success_count = 0; 406 std::string failed_event_type; 407 for (const auto& tid : pair.second) { 408 for (const auto& cpu : cpus) { 409 if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) { 410 success_count++; 411 } 412 } 413 } 414 // We can't guarantee to open perf event file successfully for each thread on each cpu. 415 // Because threads may exit between PrepareThreads() and OpenEventFilesOnGroup(), and 416 // cpus may be offlined between GetOnlineCpus() and OpenEventFilesOnGroup(). 417 // So we only check that we can at least monitor one thread for each process. 418 if (success_count == 0) { 419 PLOG(ERROR) << "failed to open perf event file for event_type " 420 << failed_event_type << " for " 421 << (pair.first == -1 ? "all threads" 422 : "threads in process " + std::to_string(pair.first)); 423 return false; 424 } 425 } 426 } 427 } 428 return true; 429 } 430 431 bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) { 432 return group.size() == 1 && group[0].event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS; 433 } 434 435 bool EventSelectionSet::OpenUserSpaceSamplersOnGroup(EventSelectionGroup& group, 436 const std::map<pid_t, std::set<pid_t>>& process_map) { 437 CHECK_EQ(group.size(), 1u); 438 for (auto& selection : group) { 439 if (selection.event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS && 440 selection.event_attr.config == SIMPLEPERF_CONFIG_INPLACE_SAMPLER) { 441 for (auto& pair : process_map) { 442 std::unique_ptr<InplaceSamplerClient> sampler = InplaceSamplerClient::Create( 443 selection.event_attr, pair.first, pair.second); 444 if (sampler == nullptr) { 445 return false; 446 } 447 selection.inplace_samplers.push_back(std::move(sampler)); 448 } 449 } 450 } 451 return true; 452 } 453 454 static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) { 455 if (!event_fd->ReadCounter(&counter->counter)) { 456 return false; 457 } 458 counter->tid = event_fd->ThreadId(); 459 counter->cpu = event_fd->Cpu(); 460 return true; 461 } 462 463 bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) { 464 counters->clear(); 465 for (size_t i = 0; i < groups_.size(); ++i) { 466 for (auto& selection : groups_[i]) { 467 CountersInfo counters_info; 468 counters_info.group_id = i; 469 counters_info.event_name = selection.event_type_modifier.event_type.name; 470 counters_info.event_modifier = selection.event_type_modifier.modifier; 471 counters_info.counters = selection.hotplugged_counters; 472 for (auto& event_fd : selection.event_fds) { 473 CounterInfo counter; 474 if (!ReadCounter(event_fd.get(), &counter)) { 475 return false; 476 } 477 counters_info.counters.push_back(counter); 478 } 479 counters->push_back(counters_info); 480 } 481 } 482 return true; 483 } 484 485 bool EventSelectionSet::MmapEventFiles(size_t min_mmap_pages, 486 size_t max_mmap_pages) { 487 for (size_t i = max_mmap_pages; i >= min_mmap_pages; i >>= 1) { 488 if (MmapEventFiles(i, i == min_mmap_pages)) { 489 LOG(VERBOSE) << "Mapped buffer size is " << i << " pages."; 490 mmap_pages_ = i; 491 return true; 492 } 493 for (auto& group : groups_) { 494 for (auto& selection : group) { 495 for (auto& event_fd : selection.event_fds) { 496 event_fd->DestroyMappedBuffer(); 497 } 498 } 499 } 500 } 501 return false; 502 } 503 504 bool EventSelectionSet::MmapEventFiles(size_t mmap_pages, bool report_error) { 505 // Allocate a mapped buffer for each cpu. 506 std::map<int, EventFd*> cpu_map; 507 for (auto& group : groups_) { 508 for (auto& selection : group) { 509 for (auto& event_fd : selection.event_fds) { 510 auto it = cpu_map.find(event_fd->Cpu()); 511 if (it != cpu_map.end()) { 512 if (!event_fd->ShareMappedBuffer(*(it->second), report_error)) { 513 return false; 514 } 515 } else { 516 if (!event_fd->CreateMappedBuffer(mmap_pages, report_error)) { 517 return false; 518 } 519 cpu_map[event_fd->Cpu()] = event_fd.get(); 520 } 521 } 522 } 523 } 524 return true; 525 } 526 527 bool EventSelectionSet::PrepareToReadMmapEventData(const std::function<bool(Record*)>& callback) { 528 // Add read Events for perf event files having mapped buffer. 529 for (auto& group : groups_) { 530 for (auto& selection : group) { 531 for (auto& event_fd : selection.event_fds) { 532 if (event_fd->HasMappedBuffer()) { 533 if (!event_fd->StartPolling(*loop_, [this]() { 534 return ReadMmapEventData(); 535 })) { 536 return false; 537 } 538 } 539 } 540 for (auto& sampler : selection.inplace_samplers) { 541 if (!sampler->StartPolling(*loop_, callback, 542 [&] { return CheckMonitoredTargets(); })) { 543 return false; 544 } 545 } 546 } 547 } 548 549 // Prepare record callback function. 550 record_callback_ = callback; 551 return true; 552 } 553 554 // When reading from mmap buffers, we prefer reading from all buffers at once 555 // rather than reading one buffer at a time. Because by reading all buffers 556 // at once, we can merge records from different buffers easily in memory. 557 // Otherwise, we have to sort records with greater effort. 558 bool EventSelectionSet::ReadMmapEventData() { 559 size_t head_size = 0; 560 std::vector<RecordBufferHead>& heads = record_buffer_heads_; 561 if (heads.empty()) { 562 heads.resize(1); 563 } 564 heads[0].current_pos = 0; 565 size_t buffer_pos = 0; 566 567 for (auto& group : groups_) { 568 for (auto& selection : group) { 569 for (auto& event_fd : selection.event_fds) { 570 if (event_fd->HasMappedBuffer()) { 571 if (event_fd->GetAvailableMmapData(record_buffer_, buffer_pos) != 0) { 572 heads[head_size].end_pos = buffer_pos; 573 heads[head_size].attr = &selection.event_attr; 574 head_size++; 575 if (heads.size() == head_size) { 576 heads.resize(head_size + 1); 577 } 578 heads[head_size].current_pos = buffer_pos; 579 } 580 } 581 } 582 } 583 } 584 585 if (head_size == 0) { 586 return true; 587 } 588 if (head_size == 1) { 589 // Only one buffer has data, process it directly. 590 std::vector<std::unique_ptr<Record>> records = 591 ReadRecordsFromBuffer(*heads[0].attr, 592 record_buffer_.data(), buffer_pos); 593 for (auto& r : records) { 594 if (!record_callback_(r.get())) { 595 return false; 596 } 597 } 598 } else { 599 // Use a priority queue to merge records from different buffers. As 600 // records from the same buffer are already ordered by time, we only 601 // need to merge the first record from all buffers. And each time a 602 // record is popped from the queue, we put the next record from its 603 // buffer into the queue. 604 auto comparator = [&](RecordBufferHead* h1, RecordBufferHead* h2) { 605 return h1->timestamp > h2->timestamp; 606 }; 607 std::priority_queue<RecordBufferHead*, std::vector<RecordBufferHead*>, decltype(comparator)> q(comparator); 608 for (size_t i = 0; i < head_size; ++i) { 609 RecordBufferHead& h = heads[i]; 610 h.r = ReadRecordFromBuffer(*h.attr, &record_buffer_[h.current_pos]); 611 h.timestamp = h.r->Timestamp(); 612 h.current_pos += h.r->size(); 613 q.push(&h); 614 } 615 while (!q.empty()) { 616 RecordBufferHead* h = q.top(); 617 q.pop(); 618 if (!record_callback_(h->r.get())) { 619 return false; 620 } 621 if (h->current_pos < h->end_pos) { 622 h->r = ReadRecordFromBuffer(*h->attr, &record_buffer_[h->current_pos]); 623 h->timestamp = h->r->Timestamp(); 624 h->current_pos += h->r->size(); 625 q.push(h); 626 } 627 } 628 } 629 return true; 630 } 631 632 bool EventSelectionSet::FinishReadMmapEventData() { 633 if (!ReadMmapEventData()) { 634 return false; 635 } 636 if (!HasInplaceSampler()) { 637 return true; 638 } 639 // Inplace sampler server uses a buffer to cache samples before sending them, so we need to 640 // explicitly ask it to send the cached samples. 641 loop_.reset(new IOEventLoop); 642 size_t inplace_sampler_count = 0; 643 auto close_callback = [&]() { 644 if (--inplace_sampler_count == 0) { 645 return loop_->ExitLoop(); 646 } 647 return true; 648 }; 649 for (auto& group : groups_) { 650 for (auto& sel : group) { 651 for (auto& sampler : sel.inplace_samplers) { 652 if (!sampler->IsClosed()) { 653 if (!sampler->StopProfiling(*loop_, close_callback)) { 654 return false; 655 } 656 inplace_sampler_count++; 657 } 658 } 659 } 660 } 661 if (inplace_sampler_count == 0) { 662 return true; 663 } 664 665 // Set a timeout to exit the loop. 666 timeval tv; 667 tv.tv_sec = 1; 668 tv.tv_usec = 0; 669 if (!loop_->AddPeriodicEvent(tv, [&]() { return loop_->ExitLoop(); })) { 670 return false; 671 } 672 return loop_->RunLoop(); 673 } 674 675 bool EventSelectionSet::HandleCpuHotplugEvents(const std::vector<int>& monitored_cpus, 676 double check_interval_in_sec) { 677 monitored_cpus_.insert(monitored_cpus.begin(), monitored_cpus.end()); 678 online_cpus_ = GetOnlineCpus(); 679 if (!loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), 680 [&]() { return DetectCpuHotplugEvents(); })) { 681 return false; 682 } 683 return true; 684 } 685 686 bool EventSelectionSet::DetectCpuHotplugEvents() { 687 std::vector<int> new_cpus = GetOnlineCpus(); 688 for (const auto& cpu : online_cpus_) { 689 if (std::find(new_cpus.begin(), new_cpus.end(), cpu) == new_cpus.end()) { 690 if (monitored_cpus_.empty() || 691 monitored_cpus_.find(cpu) != monitored_cpus_.end()) { 692 LOG(INFO) << "Cpu " << cpu << " is offlined"; 693 if (!HandleCpuOfflineEvent(cpu)) { 694 return false; 695 } 696 } 697 } 698 } 699 for (const auto& cpu : new_cpus) { 700 if (std::find(online_cpus_.begin(), online_cpus_.end(), cpu) == 701 online_cpus_.end()) { 702 if (monitored_cpus_.empty() || 703 monitored_cpus_.find(cpu) != monitored_cpus_.end()) { 704 LOG(INFO) << "Cpu " << cpu << " is onlined"; 705 if (!HandleCpuOnlineEvent(cpu)) { 706 return false; 707 } 708 } 709 } 710 } 711 online_cpus_ = new_cpus; 712 return true; 713 } 714 715 bool EventSelectionSet::HandleCpuOfflineEvent(int cpu) { 716 if (!for_stat_cmd_) { 717 // Read mmap data here, so we won't lose the existing records of the 718 // offlined cpu. 719 if (!ReadMmapEventData()) { 720 return false; 721 } 722 } 723 for (auto& group : groups_) { 724 for (auto& selection : group) { 725 for (auto it = selection.event_fds.begin(); 726 it != selection.event_fds.end();) { 727 if ((*it)->Cpu() == cpu) { 728 if (for_stat_cmd_) { 729 CounterInfo counter; 730 if (!ReadCounter(it->get(), &counter)) { 731 return false; 732 } 733 selection.hotplugged_counters.push_back(counter); 734 } else { 735 if ((*it)->HasMappedBuffer()) { 736 if (!(*it)->StopPolling()) { 737 return false; 738 } 739 } 740 } 741 it = selection.event_fds.erase(it); 742 } else { 743 ++it; 744 } 745 } 746 } 747 } 748 return true; 749 } 750 751 bool EventSelectionSet::HandleCpuOnlineEvent(int cpu) { 752 // We need to start profiling when opening new event files. 753 SetEnableOnExec(false); 754 std::map<pid_t, std::set<pid_t>> process_map = PrepareThreads(processes_, threads_); 755 for (auto& group : groups_) { 756 if (IsUserSpaceSamplerGroup(group)) { 757 continue; 758 } 759 for (const auto& pair : process_map) { 760 for (const auto& tid : pair.second) { 761 std::string failed_event_type; 762 if (!OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) { 763 // If failed to open event files, maybe the cpu has been offlined. 764 PLOG(WARNING) << "failed to open perf event file for event_type " 765 << failed_event_type << " for " 766 << (tid == -1 ? "all threads" : "thread " + std::to_string(tid)) 767 << " on cpu " << cpu; 768 } 769 } 770 } 771 } 772 if (!for_stat_cmd_) { 773 // Prepare mapped buffer. 774 if (!CreateMappedBufferForCpu(cpu)) { 775 return false; 776 } 777 // Send a EventIdRecord. 778 std::vector<uint64_t> event_id_data; 779 uint64_t attr_id = 0; 780 for (const auto& group : groups_) { 781 for (const auto& selection : group) { 782 for (const auto& event_fd : selection.event_fds) { 783 if (event_fd->Cpu() == cpu) { 784 event_id_data.push_back(attr_id); 785 event_id_data.push_back(event_fd->Id()); 786 } 787 } 788 ++attr_id; 789 } 790 } 791 EventIdRecord r(event_id_data); 792 if (!record_callback_(&r)) { 793 return false; 794 } 795 } 796 return true; 797 } 798 799 bool EventSelectionSet::CreateMappedBufferForCpu(int cpu) { 800 EventFd* fd_with_buffer = nullptr; 801 for (auto& group : groups_) { 802 for (auto& selection : group) { 803 for (auto& event_fd : selection.event_fds) { 804 if (event_fd->Cpu() != cpu) { 805 continue; 806 } 807 if (fd_with_buffer == nullptr) { 808 if (!event_fd->CreateMappedBuffer(mmap_pages_, true)) { 809 return false; 810 } 811 fd_with_buffer = event_fd.get(); 812 } else { 813 if (!event_fd->ShareMappedBuffer(*fd_with_buffer, true)) { 814 fd_with_buffer->DestroyMappedBuffer(); 815 return false; 816 } 817 } 818 } 819 } 820 } 821 if (fd_with_buffer != nullptr && 822 !fd_with_buffer->StartPolling(*loop_, [this]() { 823 return ReadMmapEventData(); 824 })) { 825 return false; 826 } 827 return true; 828 } 829 830 bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) { 831 return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), 832 [&]() { return CheckMonitoredTargets(); }); 833 } 834 835 bool EventSelectionSet::CheckMonitoredTargets() { 836 if (!HasSampler()) { 837 return loop_->ExitLoop(); 838 } 839 for (const auto& tid : threads_) { 840 if (IsThreadAlive(tid)) { 841 return true; 842 } 843 } 844 for (const auto& pid : processes_) { 845 if (IsThreadAlive(pid)) { 846 return true; 847 } 848 } 849 return loop_->ExitLoop(); 850 } 851 852 bool EventSelectionSet::HasSampler() { 853 for (auto& group : groups_) { 854 for (auto& sel : group) { 855 if (!sel.event_fds.empty()) { 856 return true; 857 } 858 for (auto& sampler : sel.inplace_samplers) { 859 if (!sampler->IsClosed()) { 860 return true; 861 } 862 } 863 } 864 } 865 return false; 866 } 867