diff options
Diffstat (limited to 'simpleperf/event_selection_set.cpp')
-rw-r--r-- | simpleperf/event_selection_set.cpp | 254 |
1 files changed, 68 insertions, 186 deletions
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index 82d41fd4..6583bd2d 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -19,25 +19,19 @@ #include <algorithm> #include <atomic> #include <thread> -#include <unordered_map> #include <android-base/logging.h> -#include <android-base/stringprintf.h> -#include <android-base/strings.h> -#include "ETMRecorder.h" -#include "IOEventLoop.h" -#include "RecordReadThread.h" #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" -namespace simpleperf { - -using android::base::StringPrintf; +using namespace simpleperf; bool IsBranchSamplingSupported() { const EventType* type = FindEventTypeByName("cpu-cycles"); @@ -56,7 +50,8 @@ bool IsDwarfCallChainSamplingSupported() { return false; } perf_event_attr attr = CreateDefaultPerfEventAttr(*type); - attr.sample_type |= PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_REGS_USER | PERF_SAMPLE_STACK_USER; + 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; @@ -64,11 +59,6 @@ bool IsDwarfCallChainSamplingSupported() { } bool IsDumpingRegsForTracepointEventsSupported() { - if (auto version = GetKernelVersion(); version && version.value() >= std::make_pair(4, 2)) { - // Kernel >= 4.2 has patch "5b09a094f2 arm64: perf: Fix callchain parse error with kernel - // tracepoint events". So no need to test. - return true; - } const EventType* event_type = FindEventTypeByName("sched:sched_switch", false); if (event_type == nullptr) { return false; @@ -98,20 +88,16 @@ bool IsDumpingRegsForTracepointEventsSupported() { done = true; thread.join(); - // There are small chances that we don't see samples immediately after joining the thread on - // cuttlefish, probably due to data synchronization between cpus. To avoid flaky tests, use a - // loop to wait for samples. - for (int timeout = 0; timeout < 1000; timeout++) { - std::vector<char> buffer = event_fd->GetAvailableMmapData(); - std::vector<std::unique_ptr<Record>> records = - ReadRecordsFromBuffer(attr, buffer.data(), buffer.size()); - for (auto& r : records) { - if (r->type() == PERF_RECORD_SAMPLE) { - auto& record = *static_cast<SampleRecord*>(r.get()); - return record.ip_data.ip != 0; + std::vector<char> buffer = event_fd->GetAvailableMmapData(); + std::vector<std::unique_ptr<Record>> records = + ReadRecordsFromBuffer(attr, buffer.data(), buffer.size()); + for (auto& r : records) { + if (r->type() == PERF_RECORD_SAMPLE) { + auto& record = *static_cast<SampleRecord*>(r.get()); + if (record.ip_data.ip != 0) { + return true; } } - usleep(1); } return false; } @@ -145,23 +131,6 @@ bool IsMmap2Supported() { 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) {} @@ -174,9 +143,11 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam return false; } if (for_stat_cmd_) { - if (event_type->event_type.name == "cpu-clock" || event_type->event_type.name == "task-clock") { + 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 + LOG(ERROR) << "Modifier u and modifier k used in event type " + << event_type->event_type.name << " are not supported by the kernel."; return false; } @@ -228,7 +199,8 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam // 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"; + LOG(ERROR) << "Event type '" << event_type->name + << "' is not supported on the device"; return false; } if (set_default_sample_freq) { @@ -240,7 +212,8 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam 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"; + LOG(ERROR) << "Event type '" << sel.event_type_modifier.name + << "' appears more than once"; return false; } } @@ -252,8 +225,8 @@ bool EventSelectionSet::AddEventType(const std::string& event_name, size_t* grou return AddEventGroup(std::vector<std::string>(1, event_name), group_id); } -bool EventSelectionSet::AddEventGroup(const std::vector<std::string>& event_names, - size_t* group_id) { +bool EventSelectionSet::AddEventGroup( + const std::vector<std::string>& event_names, size_t* group_id) { EventSelectionGroup group; bool first_event = groups_.empty(); bool first_in_group = true; @@ -297,7 +270,8 @@ std::vector<const EventType*> EventSelectionSet::GetTracepointEvents() const { std::vector<const EventType*> result; for (const auto& group : groups_) { for (const auto& selection : group) { - if (selection.event_type_modifier.event_type.type == PERF_TYPE_TRACEPOINT) { + if (selection.event_type_modifier.event_type.type == + PERF_TYPE_TRACEPOINT) { result.push_back(&selection.event_type_modifier.event_type); } } @@ -331,18 +305,6 @@ std::vector<EventAttrWithId> EventSelectionSet::GetEventAttrWithId() const { return result; } -std::unordered_map<uint64_t, std::string> EventSelectionSet::GetEventNamesById() const { - std::unordered_map<uint64_t, std::string> result; - for (const auto& group : groups_) { - for (const auto& selection : group) { - for (const auto& fd : selection.event_fds) { - result[fd->Id()] = selection.event_type_modifier.name; - } - } - } - return result; -} - // Union the sample type of different event attrs can make reading sample // records in perf.data easier. void EventSelectionSet::UnionSampleType() { @@ -412,9 +374,11 @@ void EventSelectionSet::SetSampleSpeed(size_t group_id, const SampleSpeed& speed 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; + (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()) { @@ -450,10 +414,12 @@ bool EventSelectionSet::EnableDwarfCallChainSampling(uint32_t dump_stack_size) { } 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.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_regs_user = + GetSupportedRegMask(GetMachineArch()); selection.event_attr.sample_stack_user = dump_stack_size; } } @@ -478,7 +444,14 @@ void EventSelectionSet::SetClockId(int clock_id) { } bool EventSelectionSet::NeedKernelSymbol() const { - return !ExcludeKernel(); + 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) { @@ -490,68 +463,11 @@ bool EventSelectionSet::RecordNotExecutableMaps() const { return groups_[0][0].event_attr.mmap_data == 1; } -void EventSelectionSet::WakeupPerSample() { - for (auto& group : groups_) { - for (auto& selection : group) { - selection.event_attr.watermark = 0; - selection.event_attr.wakeup_events = 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. - 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 (auto version = GetKernelVersion(); version && version.value() >= std::make_pair(4, 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<int>& cpus) { std::vector<int> online_cpus = GetOnlineCpus(); for (const auto& cpu : cpus) { - if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == online_cpus.end()) { + if (std::find(online_cpus.begin(), online_cpus.end(), cpu) == + online_cpus.end()) { LOG(ERROR) << "cpu " << cpu << " is not online."; return false; } @@ -559,7 +475,8 @@ static bool CheckIfCpusOnline(const std::vector<int>& cpus) { return true; } -bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu, +bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group, + pid_t tid, int cpu, std::string* failed_event_type) { std::vector<std::unique_ptr<EventFd>> event_fds; // Given a tid and cpu, events on the same group should be all opened @@ -569,8 +486,8 @@ bool EventSelectionSet::OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t std::unique_ptr<EventFd> 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; + *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)); @@ -640,40 +557,32 @@ bool EventSelectionSet::OpenEventFiles(const std::vector<int>& cpus) { } bool EventSelectionSet::ApplyFilters() { - return ApplyAddrFilters() && ApplyTracepointFilters(); -} - -bool EventSelectionSet::ApplyAddrFilters() { - if (addr_filters_.empty()) { + if (include_filters_.empty()) { return true; } if (!has_aux_trace_) { - LOG(ERROR) << "addr filters only take effect in cs-etm instruction tracing"; + LOG(ERROR) << "include 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."; + size_t supported_pairs = ETMRecorder::GetInstance().GetAddrFilterPairs(); + if (supported_pairs < include_filters_.size()) { + LOG(ERROR) << "filter binary count is " << include_filters_.size() + << ", bigger than maximum supported filters on device, which is " << supported_pairs; return false; } - std::string filter_str; - for (auto& filter : addr_filters_) { + for (auto& binary : include_filters_) { + std::string path; + if (!android::base::Realpath(binary, &path)) { + PLOG(ERROR) << "failed to find include filter binary: " << binary; + return false; + } + uint64_t file_size = GetFileSize(path); if (!filter_str.empty()) { filter_str += ','; } - filter_str += filter.ToString(); + android::base::StringAppendF(&filter_str, "filter 0/%" PRIu64 "@%s", file_size, path.c_str()); } - for (auto& group : groups_) { for (auto& selection : group) { if (IsEtmEventType(selection.event_type_modifier.event_type.type)) { @@ -688,21 +597,6 @@ bool EventSelectionSet::ApplyAddrFilters() { 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; @@ -737,9 +631,10 @@ bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) { 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)); + 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; } @@ -795,17 +690,6 @@ bool EventSelectionSet::FinishReadMmapEventData() { return true; } -void EventSelectionSet::CloseEventFiles() { - if (record_read_thread_) { - record_read_thread_->StopReadThread(); - } - for (auto& group : groups_) { - for (auto& event : group) { - event.event_fds.clear(); - } - } -} - bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) { return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), [&]() { return CheckMonitoredTargets(); }); @@ -851,5 +735,3 @@ bool EventSelectionSet::SetEnableEvents(bool enable) { } return true; } - -} // namespace simpleperf |