/*
* 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