/* * Copyright (C) 2017 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. */ #define SIMPLEPERF_EXPORT __attribute__((visibility("default"))) #include "include/simpleperf.h" #include <memory> #include <set> #include <string> #include <vector> #include <android-base/logging.h> #include "environment.h" #include "event_attr.h" #include "event_fd.h" #include "event_selection_set.h" #include "event_type.h" namespace simpleperf { std::vector<std::string> GetAllEvents() { std::vector<std::string> result; if (!CheckPerfEventLimit()) { return result; } for (auto& type : GetAllEventTypes()) { perf_event_attr attr = CreateDefaultPerfEventAttr(type); if (IsEventAttrSupported(attr)) { result.push_back(type.name); } } return result; } bool IsEventSupported(const std::string& name) { if (!CheckPerfEventLimit()) { return false; } std::unique_ptr<EventTypeAndModifier> type = ParseEventType(name); if (type == nullptr) { return false; } perf_event_attr attr = CreateDefaultPerfEventAttr(type->event_type); return IsEventAttrSupported(attr); } class PerfEventSetImpl : public PerfEventSet { public: virtual ~PerfEventSetImpl() {} bool AddEvent(const std::string& name) override { if (!IsEventSupported(name)) { return false; } event_names_.push_back(name); return true; } bool MonitorCurrentProcess() override { whole_process_ = true; return true; } bool MonitorCurrentThread() override { whole_process_ = false; threads_.insert(gettid()); return true; } bool MonitorThreadsInCurrentProcess(const std::vector<pid_t>& threads) override { whole_process_ = false; std::vector<pid_t> tids = GetThreadsInProcess(getpid()); for (auto& tid : threads) { if (std::find(tids.begin(), tids.end(), tid) == tids.end()) { LOG(ERROR) << "Thread " << tid << " doesn't exist in current process."; return false; } } threads_.insert(threads.begin(), threads.end()); return true; } protected: PerfEventSetImpl() : whole_process_(false) {} std::vector<std::string> event_names_; bool whole_process_; std::set<pid_t> threads_; }; class PerfEventSetForCounting : public PerfEventSetImpl { public: PerfEventSetForCounting() : in_counting_state_(false) {} virtual ~PerfEventSetForCounting() {} bool StartCounters() override; bool StopCounters() override; bool ReadCounters(std::vector<Counter>* counters) override; private: bool CreateEventSelectionSet(); void InitAccumulatedCounters(); bool ReadRawCounters(std::vector<Counter>* counters); // Add counter b to a. void AddCounter(Counter& a, const Counter& b); // Sub counter b from a. void SubCounter(Counter& a, const Counter& b); bool in_counting_state_; std::unique_ptr<EventSelectionSet> event_selection_set_; // The counters at the last time calling StartCounting(). std::vector<Counter> last_start_counters_; // The accumulated counters of counting periods, excluding // the last one. std::vector<Counter> accumulated_counters_; }; bool PerfEventSetForCounting::CreateEventSelectionSet() { std::unique_ptr<EventSelectionSet> set(new EventSelectionSet(true)); if (event_names_.empty()) { LOG(ERROR) << "No events."; return false; } for (const auto& name : event_names_) { if (!set->AddEventType(name)) { return false; } } if (whole_process_) { set->AddMonitoredProcesses({getpid()}); } else { if (threads_.empty()) { LOG(ERROR) << "No monitored threads."; return false; } set->AddMonitoredThreads(threads_); } if (!set->OpenEventFiles({-1})) { return false; } event_selection_set_ = std::move(set); return true; } void PerfEventSetForCounting::InitAccumulatedCounters() { for (const auto& name : event_names_) { Counter counter; counter.event = name; counter.value = 0; counter.time_enabled_in_ns = 0; counter.time_running_in_ns = 0; accumulated_counters_.push_back(counter); } } bool PerfEventSetForCounting::ReadRawCounters(std::vector<Counter>* counters) { CHECK(event_selection_set_); std::vector<CountersInfo> s; if (!event_selection_set_->ReadCounters(&s)) { return false; } CHECK_EQ(s.size(), event_names_.size()); counters->resize(s.size()); for (size_t i = 0; i < s.size(); ++i) { CountersInfo& info = s[i]; std::string name = info.event_modifier.empty() ? info.event_name : info.event_name + ":" + info.event_modifier; CHECK_EQ(name, event_names_[i]); Counter& sum = (*counters)[i]; sum.event = name; sum.value = 0; sum.time_enabled_in_ns = 0; sum.time_running_in_ns = 0; for (CounterInfo& c : info.counters) { sum.value += c.counter.value; sum.time_enabled_in_ns += c.counter.time_enabled; sum.time_running_in_ns += c.counter.time_running; } } return true; } void PerfEventSetForCounting::AddCounter(Counter& a, const Counter& b) { a.value += b.value; a.time_enabled_in_ns += b.time_enabled_in_ns; a.time_running_in_ns += b.time_enabled_in_ns; } void PerfEventSetForCounting::SubCounter(Counter& a, const Counter& b) { a.value -= b.value; a.time_enabled_in_ns -= b.time_enabled_in_ns; a.time_running_in_ns -= b.time_running_in_ns; } bool PerfEventSetForCounting::StartCounters() { if (in_counting_state_) { return true; } if (event_selection_set_ == nullptr) { if (!CreateEventSelectionSet()) { return false; } InitAccumulatedCounters(); } if (!ReadRawCounters(&last_start_counters_)) { return false; } in_counting_state_ = true; return true; } bool PerfEventSetForCounting::StopCounters() { if (!in_counting_state_) { return true; } std::vector<Counter> cur; if (!ReadRawCounters(&cur)) { return false; } for (size_t i = 0; i < event_names_.size(); ++i) { SubCounter(cur[i], last_start_counters_[i]); AddCounter(accumulated_counters_[i], cur[i]); } in_counting_state_ = false; return true; } bool PerfEventSetForCounting::ReadCounters(std::vector<Counter>* counters) { if (!in_counting_state_) { *counters = accumulated_counters_; return true; } if (!ReadRawCounters(counters)) { return false; } for (size_t i = 0; i < event_names_.size(); ++i) { SubCounter((*counters)[i], last_start_counters_[i]); AddCounter((*counters)[i], accumulated_counters_[i]); } return true; } PerfEventSet* PerfEventSet::CreateInstance(PerfEventSet::Type type) { if (!CheckPerfEventLimit()) { return nullptr; } if (type == Type::kPerfForCounting) { return new PerfEventSetForCounting; } return nullptr; } bool PerfEventSet::AddEvent(const std::string&) { return false; } bool PerfEventSet::MonitorCurrentProcess() { return false; } bool PerfEventSet::MonitorCurrentThread() { return false; } bool PerfEventSet::MonitorThreadsInCurrentProcess(const std::vector<pid_t>&) { return false; } bool PerfEventSet::StartCounters() { return false; } bool PerfEventSet::StopCounters() { return false; } bool PerfEventSet::ReadCounters(std::vector<Counter>*) { return false; } } // namespace simpleperf