blob: f03286bccff3aa4faa0a2a08de03423b17905a95 [file] [log] [blame]
/*
* 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 <base/logging.h>
#include "environment.h"
#include "event_attr.h"
#include "event_type.h"
bool IsBranchSamplingSupported() {
const EventType* event_type = EventTypeFactory::FindEventTypeByName("cpu-cycles", false);
if (event_type == nullptr) {
return false;
}
perf_event_attr attr = CreateDefaultPerfEventAttr(*event_type);
attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
attr.branch_sample_type = PERF_SAMPLE_BRANCH_ANY;
auto event_fd = EventFd::OpenEventFile(attr, getpid(), -1, false);
return event_fd != nullptr;
}
void EventSelectionSet::AddEventType(const EventType& event_type) {
EventSelection selection;
selection.event_type = &event_type;
selection.event_attr = CreateDefaultPerfEventAttr(event_type);
selections_.push_back(std::move(selection));
}
void EventSelectionSet::SetEnableOnExec(bool enable) {
for (auto& selection : selections_) {
selection.event_attr.enable_on_exec = (enable ? 1 : 0);
}
}
bool EventSelectionSet::GetEnableOnExec() {
for (auto& selection : selections_) {
if (selection.event_attr.enable_on_exec == 0) {
return false;
}
}
return true;
}
void EventSelectionSet::SampleIdAll() {
for (auto& selection : selections_) {
selection.event_attr.sample_id_all = 1;
}
}
void EventSelectionSet::SetSampleFreq(uint64_t sample_freq) {
for (auto& selection : selections_) {
perf_event_attr& attr = selection.event_attr;
attr.freq = 1;
attr.sample_freq = sample_freq;
}
}
void EventSelectionSet::SetSamplePeriod(uint64_t sample_period) {
for (auto& selection : selections_) {
perf_event_attr& attr = selection.event_attr;
attr.freq = 0;
attr.sample_period = 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& selection : selections_) {
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::EnableCallChainSampling() {
for (auto& selection : selections_) {
selection.event_attr.sample_type |= PERF_SAMPLE_CALLCHAIN;
}
}
bool EventSelectionSet::OpenEventFilesForAllCpus() {
std::vector<int> cpus = GetOnlineCpus();
if (cpus.empty()) {
return false;
}
for (auto& selection : selections_) {
for (auto& cpu : cpus) {
auto event_fd = EventFd::OpenEventFile(selection.event_attr, -1, cpu);
if (event_fd != nullptr) {
selection.event_fds.push_back(std::move(event_fd));
}
}
// As the online cpus can be enabled or disabled at runtime, we may not open event file for
// all cpus successfully. But we should open at least one cpu successfully.
if (selection.event_fds.empty()) {
PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
<< " on all cpus";
return false;
}
}
return true;
}
bool EventSelectionSet::OpenEventFilesForThreads(const std::vector<pid_t>& threads) {
for (auto& selection : selections_) {
for (auto& tid : threads) {
auto event_fd = EventFd::OpenEventFile(selection.event_attr, tid, -1);
if (event_fd == nullptr) {
return false;
}
selection.event_fds.push_back(std::move(event_fd));
}
}
return true;
}
bool EventSelectionSet::EnableEvents() {
for (auto& selection : selections_) {
for (auto& event_fd : selection.event_fds) {
if (!event_fd->EnableEvent()) {
return false;
}
}
}
return true;
}
bool EventSelectionSet::ReadCounters(
std::map<const EventType*, std::vector<PerfCounter>>* counters_map) {
for (auto& selection : selections_) {
std::vector<PerfCounter> counters;
for (auto& event_fd : selection.event_fds) {
PerfCounter counter;
if (!event_fd->ReadCounter(&counter)) {
return false;
}
counters.push_back(counter);
}
counters_map->insert(std::make_pair(selection.event_type, counters));
}
return true;
}
void EventSelectionSet::PreparePollForEventFiles(std::vector<pollfd>* pollfds) {
for (auto& selection : selections_) {
for (auto& event_fd : selection.event_fds) {
pollfd poll_fd;
event_fd->PreparePollForMmapData(&poll_fd);
pollfds->push_back(poll_fd);
}
}
}
bool EventSelectionSet::MmapEventFiles(size_t mmap_pages) {
for (auto& selection : selections_) {
for (auto& event_fd : selection.event_fds) {
if (!event_fd->MmapContent(mmap_pages)) {
return false;
}
}
}
return true;
}
static bool ReadMmapEventDataForFd(std::unique_ptr<EventFd>& event_fd,
std::function<bool(const char*, size_t)> callback,
bool* have_data) {
*have_data = false;
while (true) {
char* data;
size_t size = event_fd->GetAvailableMmapData(&data);
if (size == 0) {
break;
}
if (!callback(data, size)) {
return false;
}
*have_data = true;
event_fd->DiscardMmapData(size);
}
return true;
}
bool EventSelectionSet::ReadMmapEventData(std::function<bool(const char*, size_t)> callback) {
for (auto& selection : selections_) {
for (auto& event_fd : selection.event_fds) {
while (true) {
bool have_data;
if (!ReadMmapEventDataForFd(event_fd, callback, &have_data)) {
return false;
}
if (!have_data) {
break;
}
}
}
}
return true;
}
std::string EventSelectionSet::FindEventFileNameById(uint64_t id) {
for (auto& selection : selections_) {
for (auto& event_fd : selection.event_fds) {
if (event_fd->Id() == id) {
return event_fd->Name();
}
}
}
return "";
}
EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
const EventType& event_type) {
for (auto& selection : selections_) {
if (selection.event_type->name == event_type.name) {
return &selection;
}
}
return nullptr;
}
const perf_event_attr& EventSelectionSet::FindEventAttrByType(const EventType& event_type) {
return FindSelectionByType(event_type)->event_attr;
}
const std::vector<std::unique_ptr<EventFd>>& EventSelectionSet::FindEventFdsByType(
const EventType& event_type) {
return FindSelectionByType(event_type)->event_fds;
}