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