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
28constexpr uint64_t DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT = 4000;
29constexpr uint64_t DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT = 1;
30
31bool 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
42bool 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
56bool 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
100bool EventSelectionSet::AddEventType(const std::string& event_name) {
101  return AddEventGroup(std::vector<std::string>(1, event_name));
102}
103
104bool 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
119std::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
132bool 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
144std::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.
164void 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
178void 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
197bool 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
208void EventSelectionSet::SampleIdAll() {
209  for (auto& group : groups_) {
210    for (auto& selection : group) {
211      selection.event_attr.sample_id_all = 1;
212    }
213  }
214}
215
216void 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
225void 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
234void 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
251bool 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
278void 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
286bool 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
305void 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
313bool 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
324static 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
336bool 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
365static 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
387bool 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
431bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
432  return group.size() == 1 && group[0].event_attr.type == SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS;
433}
434
435bool 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
454static 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
463bool 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
485bool 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
504bool 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
527bool 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.
558bool 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
632bool 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
675bool 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
686bool 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
715bool 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
751bool 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
799bool 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
830bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) {
831  return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec),
832                                 [&]() { return CheckMonitoredTargets(); });
833}
834
835bool 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
852bool 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