/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "event_selection_set.h" #include #include #include #include #include #include #include "environment.h" #include "ETMRecorder.h" #include "event_attr.h" #include "event_type.h" #include "IOEventLoop.h" #include "perf_regs.h" #include "tracing.h" #include "utils.h" #include "RecordReadThread.h" using android::base::StringPrintf; using namespace simpleperf; bool IsBranchSamplingSupported() { const EventType* type = FindEventTypeByName("cpu-cycles"); if (type == nullptr) { return false; } perf_event_attr attr = CreateDefaultPerfEventAttr(*type); attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY; return IsEventAttrSupported(attr, type->name); } bool IsDwarfCallChainSamplingSupported() { const EventType* type = FindEventTypeByName("cpu-clock"); if (type == nullptr) { return false; } perf_event_attr attr = CreateDefaultPerfEventAttr(*type); attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; attr.exclude_callchain_user = 1; attr.sample_regs_user = GetSupportedRegMask(GetBuildArch()); attr.sample_stack_user = 8192; return IsEventAttrSupported(attr, type->name); } bool IsDumpingRegsForTracepointEventsSupported() { const EventType* event_type = FindEventTypeByName("sched:sched_switch", false); if (event_type == nullptr) { return false; } std::atomic done(false); std::atomic thread_id(0); std::thread thread([&]() { thread_id = gettid(); while (!done) { usleep(1); } usleep(1); // Make a sched out to generate one sample. }); while (thread_id == 0) { usleep(1); } perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type); attr.freq = 0; attr.sample_period = 1; std::unique_ptr event_fd = EventFd::OpenEventFile(attr, thread_id, -1, nullptr, event_type->name); if (event_fd == nullptr || !event_fd->CreateMappedBuffer(4, true)) { done = true; thread.join(); return false; } done = true; thread.join(); std::vector buffer = event_fd->GetAvailableMmapData(); std::vector> records = ReadRecordsFromBuffer(attr, buffer.data(), buffer.size()); for (auto& r : records) { if (r->type() == PERF_RECORD_SAMPLE) { auto& record = *static_cast(r.get()); if (record.ip_data.ip != 0) { return true; } } } return false; } bool IsSettingClockIdSupported() { // Do the real check only once and keep the result in a static variable. static int is_supported = -1; if (is_supported == -1) { const EventType* type = FindEventTypeByName("cpu-clock"); if (type == nullptr) { is_supported = 0; } else { // Check if the kernel supports setting clockid, which was added in kernel 4.0. Just check // with one clockid is enough. Because all needed clockids were supported before kernel 4.0. perf_event_attr attr = CreateDefaultPerfEventAttr(*type); attr.use_clockid = 1; attr.clockid = CLOCK_MONOTONIC; is_supported = IsEventAttrSupported(attr, type->name) ? 1 : 0; } } return is_supported; } bool IsMmap2Supported() { const EventType* type = FindEventTypeByName("cpu-clock"); if (type == nullptr) { return false; } perf_event_attr attr = CreateDefaultPerfEventAttr(*type); attr.mmap2 = 1; return IsEventAttrSupported(attr, type->name); } std::string AddrFilter::ToString() const { switch (type) { case FILE_RANGE: return StringPrintf("filter 0x%" PRIx64 "/0x%" PRIx64 "@%s", addr, size, file_path.c_str()); case AddrFilter::FILE_START: return StringPrintf("start 0x%" PRIx64 "@%s", addr, file_path.c_str()); case AddrFilter::FILE_STOP: return StringPrintf("stop 0x%" PRIx64 "@%s", addr, file_path.c_str()); case AddrFilter::KERNEL_RANGE: return StringPrintf("filter 0x%" PRIx64 "/0x%" PRIx64, addr, size); case AddrFilter::KERNEL_START: return StringPrintf("start 0x%" PRIx64, addr); case AddrFilter::KERNEL_STOP: return StringPrintf("stop 0x%" PRIx64, addr); } } EventSelectionSet::EventSelectionSet(bool for_stat_cmd) : for_stat_cmd_(for_stat_cmd), loop_(new IOEventLoop) {} EventSelectionSet::~EventSelectionSet() {} bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_name, bool first_event, EventSelection* selection) { std::unique_ptr event_type = ParseEventType(event_name); if (event_type == nullptr) { return false; } if (for_stat_cmd_) { if (event_type->event_type.name == "cpu-clock" || event_type->event_type.name == "task-clock") { if (event_type->exclude_user || event_type->exclude_kernel) { LOG(ERROR) << "Modifier u and modifier k used in event type " << event_type->event_type.name << " are not supported by the kernel."; return false; } } } selection->event_type_modifier = *event_type; selection->event_attr = CreateDefaultPerfEventAttr(event_type->event_type); selection->event_attr.exclude_user = event_type->exclude_user; selection->event_attr.exclude_kernel = event_type->exclude_kernel; selection->event_attr.exclude_hv = event_type->exclude_hv; selection->event_attr.exclude_host = event_type->exclude_host; selection->event_attr.exclude_guest = event_type->exclude_guest; selection->event_attr.precise_ip = event_type->precise_ip; if (IsEtmEventType(event_type->event_type.type)) { auto& etm_recorder = ETMRecorder::GetInstance(); if (!etm_recorder.CheckEtmSupport()) { return false; } ETMRecorder::GetInstance().SetEtmPerfEventAttr(&selection->event_attr); } bool set_default_sample_freq = false; if (!for_stat_cmd_) { if (event_type->event_type.type == PERF_TYPE_TRACEPOINT) { selection->event_attr.freq = 0; selection->event_attr.sample_period = DEFAULT_SAMPLE_PERIOD_FOR_TRACEPOINT_EVENT; } else if (IsEtmEventType(event_type->event_type.type)) { // ETM recording has no sample frequency to adjust. Using sample frequency only wastes time // enabling/disabling etm devices. So don't adjust frequency by default. selection->event_attr.freq = 0; selection->event_attr.sample_period = 1; } else { selection->event_attr.freq = 1; // Set default sample freq here may print msg "Adjust sample freq to max allowed sample // freq". But this is misleading. Because default sample freq may not be the final sample // freq we use. So use minimum sample freq (1) here. selection->event_attr.sample_freq = 1; set_default_sample_freq = true; } // We only need to dump mmap and comm records for the first event type. Because all event types // are monitoring the same processes. if (first_event) { selection->event_attr.mmap = 1; selection->event_attr.comm = 1; if (IsMmap2Supported()) { selection->event_attr.mmap2 = 1; } } } // PMU events are provided by kernel, so they should be supported if (!event_type->event_type.IsPmuEvent() && !IsEventAttrSupported(selection->event_attr, selection->event_type_modifier.name)) { LOG(ERROR) << "Event type '" << event_type->name << "' is not supported on the device"; return false; } if (set_default_sample_freq) { selection->event_attr.sample_freq = DEFAULT_SAMPLE_FREQ_FOR_NONTRACEPOINT_EVENT; } selection->event_fds.clear(); for (const auto& group : groups_) { for (const auto& sel : group) { if (sel.event_type_modifier.name == selection->event_type_modifier.name) { LOG(ERROR) << "Event type '" << sel.event_type_modifier.name << "' appears more than once"; return false; } } } return true; } bool EventSelectionSet::AddEventType(const std::string& event_name, size_t* group_id) { return AddEventGroup(std::vector(1, event_name), group_id); } bool EventSelectionSet::AddEventGroup( const std::vector& event_names, size_t* group_id) { EventSelectionGroup group; bool first_event = groups_.empty(); bool first_in_group = true; for (const auto& event_name : event_names) { EventSelection selection; if (!BuildAndCheckEventSelection(event_name, first_event, &selection)) { return false; } if (IsEtmEventType(selection.event_attr.type)) { has_aux_trace_ = true; } if (first_in_group) { auto& event_type = selection.event_type_modifier.event_type; if (event_type.IsPmuEvent()) { selection.allowed_cpus = event_type.GetPmuCpumask(); } } first_event = false; first_in_group = false; group.push_back(std::move(selection)); } groups_.push_back(std::move(group)); UnionSampleType(); if (group_id != nullptr) { *group_id = groups_.size() - 1; } return true; } std::vector EventSelectionSet::GetEvents() const { std::vector result; for (const auto& group : groups_) { for (const auto& selection : group) { result.push_back(&selection.event_type_modifier.event_type); } } return result; } std::vector EventSelectionSet::GetTracepointEvents() const { std::vector result; for (const auto& group : groups_) { for (const auto& selection : group) { if (selection.event_type_modifier.event_type.type == PERF_TYPE_TRACEPOINT) { result.push_back(&selection.event_type_modifier.event_type); } } } return result; } bool EventSelectionSet::ExcludeKernel() const { for (const auto& group : groups_) { for (const auto& selection : group) { if (!selection.event_type_modifier.exclude_kernel) { return false; } } } return true; } std::vector EventSelectionSet::GetEventAttrWithId() const { std::vector result; for (const auto& group : groups_) { for (const auto& selection : group) { EventAttrWithId attr_id; attr_id.attr = &selection.event_attr; for (const auto& fd : selection.event_fds) { attr_id.ids.push_back(fd->Id()); } result.push_back(attr_id); } } return result; } // Union the sample type of different event attrs can make reading sample // records in perf.data easier. void EventSelectionSet::UnionSampleType() { uint64_t sample_type = 0; for (const auto& group : groups_) { for (const auto& selection : group) { sample_type |= selection.event_attr.sample_type; } } for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.sample_type = sample_type; } } } void EventSelectionSet::SetEnableOnExec(bool enable) { for (auto& group : groups_) { for (auto& selection : group) { // If sampling is enabled on exec, then it is disabled at startup, // otherwise it should be enabled at startup. Don't use // ioctl(PERF_EVENT_IOC_ENABLE) to enable it after perf_event_open(). // Because some android kernels can't handle ioctl() well when cpu-hotplug // happens. See http://b/25193162. if (enable) { selection.event_attr.enable_on_exec = 1; selection.event_attr.disabled = 1; } else { selection.event_attr.enable_on_exec = 0; selection.event_attr.disabled = 0; } } } } bool EventSelectionSet::GetEnableOnExec() { for (const auto& group : groups_) { for (const auto& selection : group) { if (selection.event_attr.enable_on_exec == 0) { return false; } } } return true; } void EventSelectionSet::SampleIdAll() { for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.sample_id_all = 1; } } } void EventSelectionSet::SetSampleSpeed(size_t group_id, const SampleSpeed& speed) { CHECK_LT(group_id, groups_.size()); for (auto& selection : groups_[group_id]) { if (speed.UseFreq()) { selection.event_attr.freq = 1; selection.event_attr.sample_freq = speed.sample_freq; } else { selection.event_attr.freq = 0; selection.event_attr.sample_period = speed.sample_period; } } } bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) { if (branch_sample_type != 0 && (branch_sample_type & (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL | PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) { LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex << branch_sample_type; return false; } if (branch_sample_type != 0 && !IsBranchSamplingSupported()) { LOG(ERROR) << "branch stack sampling is not supported on this device."; return false; } for (auto& group : groups_) { for (auto& selection : group) { perf_event_attr& attr = selection.event_attr; if (branch_sample_type != 0) { attr.sample_type |= PERF_SAMPLE_BRANCH_STACK; } else { attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK; } attr.branch_sample_type = branch_sample_type; } } return true; } void EventSelectionSet::EnableFpCallChainSampling() { for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN; } } } bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) { if (!IsDwarfCallChainSamplingSupported()) { LOG(ERROR) << "dwarf callchain sampling is not supported on this device."; return false; } for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; selection.event_attr.exclude_callchain_user = 1; selection.event_attr.sample_regs_user = GetSupportedRegMask(GetMachineArch()); selection.event_attr.sample_stack_user = dump_stack_size; } } return true; } void EventSelectionSet::SetInherit(bool enable) { for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.inherit = (enable ? 1 : 0); } } } void EventSelectionSet::SetClockId(int clock_id) { for (auto& group : groups_) { for (auto& selection : group) { selection.event_attr.use_clockid = 1; selection.event_attr.clockid = clock_id; } } } bool EventSelectionSet::NeedKernelSymbol() const { for (const auto& group : groups_) { for (const auto& selection : group) { if (!selection.event_type_modifier.exclude_kernel) { return true; } } } return false; } void EventSelectionSet::SetRecordNotExecutableMaps(bool record) { // We only need to dump non-executable mmap records for the first event type. groups_[0][0].event_attr.mmap_data = record ? 1 : 0; } bool EventSelectionSet::RecordNotExecutableMaps() const { return groups_[0][0].event_attr.mmap_data == 1; } bool EventSelectionSet::SetTracepointFilter(const std::string& filter) { // 1. Find the tracepoint event to set filter. EventSelection* selection = nullptr; if (!groups_.empty()) { auto& group = groups_.back(); if (group.size() == 1) { if (group[0].event_attr.type == PERF_TYPE_TRACEPOINT) { selection = &group[0]; } } } if (selection == nullptr) { LOG(ERROR) << "No tracepoint event before filter: " << filter; return false; } // 2. Check the format of the filter. int kernel_major; int kernel_minor; bool use_quote = false; // Quotes are needed for string operands in kernel >= 4.19, probably after patch "tracing: Rewrite // filter logic to be simpler and faster". if (GetKernelVersion(&kernel_major, &kernel_minor)) { if (kernel_major >= 5 || (kernel_major == 4 && kernel_minor >= 19)) { use_quote = true; } } FieldNameSet used_fields; auto adjusted_filter = AdjustTracepointFilter(filter, use_quote, &used_fields); if (!adjusted_filter) { return false; } // 3. Check if used fields are available in the tracepoint event. auto& event_type = selection->event_type_modifier.event_type; if (auto opt_fields = GetFieldNamesForTracepointEvent(event_type); opt_fields) { FieldNameSet& fields = opt_fields.value(); for (const auto& field : used_fields) { if (fields.find(field) == fields.end()) { LOG(ERROR) << "field name " << field << " used in \"" << filter << "\" doesn't exist in " << event_type.name << ". Available fields are " << android::base::Join(fields, ","); return false; } } } // 4. Connect the filter to the event. selection->tracepoint_filter = adjusted_filter.value(); return true; } static bool CheckIfCpusOnline(const std::vector& cpus) { std::vector online_cpus = GetOnlineCpus(); for (const auto& cpu : cpus) { if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == online_cpus.end()) { LOG(ERROR) << "cpu " << cpu << " is not online."; return false; } } return true; } bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu, std::string* failed_event_type) { std::vector> event_fds; // Given a tid and cpu, events on the same group should be all opened // successfully or all failed to open. EventFd* group_fd = nullptr; for (auto& selection : group) { std::unique_ptr event_fd = EventFd::OpenEventFile( selection.event_attr, tid, cpu, group_fd, selection.event_type_modifier.name, false); if (!event_fd) { *failed_event_type = selection.event_type_modifier.name; return false; } LOG(VERBOSE) << "OpenEventFile for " << event_fd->Name(); event_fds.push_back(std::move(event_fd)); if (group_fd == nullptr) { group_fd = event_fds.back().get(); } } for (size_t i = 0; i < group.size(); ++i) { group[i].event_fds.push_back(std::move(event_fds[i])); } return true; } static std::set PrepareThreads(const std::set& processes, const std::set& threads) { std::set result = threads; for (auto& pid : processes) { std::vector tids = GetThreadsInProcess(pid); result.insert(tids.begin(), tids.end()); } return result; } bool EventSelectionSet::OpenEventFiles(const std::vector& cpus) { std::vector monitored_cpus; if (cpus.empty()) { monitored_cpus = GetOnlineCpus(); } else if (cpus.size() == 1 && cpus[0] == -1) { monitored_cpus = {-1}; } else { if (!CheckIfCpusOnline(cpus)) { return false; } monitored_cpus = cpus; } std::set threads = PrepareThreads(processes_, threads_); for (auto& group : groups_) { size_t success_count = 0; std::string failed_event_type; for (const auto tid : threads) { const std::vector* pcpus = &monitored_cpus; if (!group[0].allowed_cpus.empty()) { // override cpu list if event's PMU has a cpumask as those PMUs are // agnostic to cpu and it's meaningless to specify cpus for them. pcpus = &group[0].allowed_cpus; } for (const auto& cpu : *pcpus) { if (OpenEventFilesOnGroup(group, tid, cpu, &failed_event_type)) { success_count++; } } } // We can't guarantee to open perf event file successfully for each thread on each cpu. // Because threads may exit between PrepareThreads() and OpenEventFilesOnGroup(), and // cpus may be offlined between GetOnlineCpus() and OpenEventFilesOnGroup(). // So we only check that we can at least monitor one thread for each event group. if (success_count == 0) { int error_number = errno; PLOG(ERROR) << "failed to open perf event file for event_type " << failed_event_type; if (error_number == EMFILE) { LOG(ERROR) << "Please increase hard limit of open file numbers."; } return false; } } return ApplyFilters(); } bool EventSelectionSet::ApplyFilters() { return ApplyAddrFilters() && ApplyTracepointFilters(); } bool EventSelectionSet::ApplyAddrFilters() { if (addr_filters_.empty()) { return true; } if (!has_aux_trace_) { LOG(ERROR) << "addr filters only take effect in cs-etm instruction tracing"; return false; } // Check filter count limit. size_t required_etm_filter_count = 0; for (auto& filter : addr_filters_) { // A range filter needs two etm filters. required_etm_filter_count += (filter.type == AddrFilter::FILE_RANGE || filter.type == AddrFilter::KERNEL_RANGE) ? 2 : 1; } size_t etm_filter_count = ETMRecorder::GetInstance().GetAddrFilterPairs() * 2; if (etm_filter_count < required_etm_filter_count) { LOG(ERROR) << "needed " << required_etm_filter_count << " etm filters, but only " << etm_filter_count << " filters are available."; return false; } std::string filter_str; for (auto& filter : addr_filters_) { if (!filter_str.empty()) { filter_str += ','; } filter_str += filter.ToString(); } for (auto& group : groups_) { for (auto& selection : group) { if (IsEtmEventType(selection.event_type_modifier.event_type.type)) { for (auto& event_fd : selection.event_fds) { if (!event_fd->SetFilter(filter_str)) { return false; } } } } } return true; } bool EventSelectionSet::ApplyTracepointFilters() { for (auto& group : groups_) { for (auto& selection : group) { if (!selection.tracepoint_filter.empty()) { for (auto& event_fd : selection.event_fds) { if (!event_fd->SetFilter(selection.tracepoint_filter)) { return false; } } } } } return true; } static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) { if (!event_fd->ReadCounter(&counter->counter)) { return false; } counter->tid = event_fd->ThreadId(); counter->cpu = event_fd->Cpu(); return true; } bool EventSelectionSet::ReadCounters(std::vector* counters) { counters->clear(); for (size_t i = 0; i < groups_.size(); ++i) { for (auto& selection : groups_[i]) { CountersInfo counters_info; counters_info.group_id = i; counters_info.event_name = selection.event_type_modifier.event_type.name; counters_info.event_modifier = selection.event_type_modifier.modifier; counters_info.counters = selection.hotplugged_counters; for (auto& event_fd : selection.event_fds) { CounterInfo counter; if (!ReadCounter(event_fd.get(), &counter)) { return false; } counters_info.counters.push_back(counter); } counters->push_back(counters_info); } } return true; } bool EventSelectionSet::MmapEventFiles(size_t min_mmap_pages, size_t max_mmap_pages, size_t aux_buffer_size, size_t record_buffer_size, bool allow_cutting_samples, bool exclude_perf) { record_read_thread_.reset( new simpleperf::RecordReadThread(record_buffer_size, groups_[0][0].event_attr, min_mmap_pages, max_mmap_pages, aux_buffer_size, allow_cutting_samples, exclude_perf)); return true; } bool EventSelectionSet::PrepareToReadMmapEventData(const std::function& callback) { // Prepare record callback function. record_callback_ = callback; if (!record_read_thread_->RegisterDataCallback(*loop_, [this]() { return ReadMmapEventData(true); })) { return false; } std::vector event_fds; for (auto& group : groups_) { for (auto& selection : group) { for (auto& event_fd : selection.event_fds) { event_fds.push_back(event_fd.get()); } } } return record_read_thread_->AddEventFds(event_fds); } bool EventSelectionSet::SyncKernelBuffer() { return record_read_thread_->SyncKernelBuffer(); } // Read records from the RecordBuffer. If with_time_limit is false, read until the RecordBuffer is // empty, otherwise stop after 100 ms or when the record buffer is empty. bool EventSelectionSet::ReadMmapEventData(bool with_time_limit) { uint64_t start_time_in_ns; if (with_time_limit) { start_time_in_ns = GetSystemClock(); } std::unique_ptr r; while ((r = record_read_thread_->GetRecord()) != nullptr) { if (!record_callback_(r.get())) { return false; } if (with_time_limit && (GetSystemClock() - start_time_in_ns) >= 1e8) { break; } } return true; } bool EventSelectionSet::FinishReadMmapEventData() { // Stop the read thread, so we don't get more records beyond current time. if (!SyncKernelBuffer() || !record_read_thread_->StopReadThread()) { return false; } if (!ReadMmapEventData(false)) { return false; } return true; } bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) { return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), [&]() { return CheckMonitoredTargets(); }); } bool EventSelectionSet::CheckMonitoredTargets() { if (!HasSampler()) { return loop_->ExitLoop(); } for (const auto& tid : threads_) { if (IsThreadAlive(tid)) { return true; } } for (const auto& pid : processes_) { if (IsThreadAlive(pid)) { return true; } } return loop_->ExitLoop(); } bool EventSelectionSet::HasSampler() { for (auto& group : groups_) { for (auto& sel : group) { if (!sel.event_fds.empty()) { return true; } } } return false; } bool EventSelectionSet::SetEnableEvents(bool enable) { for (auto& group : groups_) { for (auto& sel : group) { for (auto& fd : sel.event_fds) { if (!fd->SetEnableEvent(enable)) { return false; } } } } return true; }