blob: 135e69dac34d6c69ed4e617f4c5514265eee721f [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_type.h"
#include <inttypes.h>
#include <unistd.h>
#include <algorithm>
#include <string>
#include <vector>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include "ETMRecorder.h"
#include "environment.h"
#include "event_attr.h"
#include "utils.h"
namespace simpleperf {
struct EventFormat {
EventFormat(const std::string& name, const std::string& attr, int shift)
: name(name), attr(attr), shift(shift) {}
std::string name;
std::string attr;
int shift;
};
#define EVENT_TYPE_TABLE_ENTRY(name, type, config, description, limited_arch) \
{name, type, config, description, limited_arch},
static const std::set<EventType> builtin_event_types = {
#include "event_type_table.h"
};
enum class EventFinderType {
BUILTIN,
TRACEPOINT_STRING,
TRACEPOINT_SYSTEM,
PMU,
ETM,
RAW,
SCOPED,
};
class EventTypeFinder {
public:
EventTypeFinder(EventFinderType type) : finder_type_(type) {}
virtual ~EventTypeFinder() {}
EventFinderType GetFinderType() const { return finder_type_; }
const std::set<EventType>& GetTypes() {
if (!loaded_) {
loaded_ = true;
LoadTypes();
}
return types_;
}
virtual const EventType* FindType(const std::string& name) {
const auto& types = GetTypes();
auto it = types.find(EventType(name, 0, 0, "", ""));
if (it != types.end()) {
return &*it;
}
return nullptr;
}
protected:
virtual void LoadTypes() = 0;
const EventFinderType finder_type_;
std::set<EventType> types_;
bool loaded_ = false;
};
class BuiltinTypeFinder : public EventTypeFinder {
public:
BuiltinTypeFinder() : EventTypeFinder(EventFinderType::BUILTIN) {}
protected:
void LoadTypes() override { types_ = std::move(builtin_event_types); }
};
class TracepointStringFinder : public EventTypeFinder {
public:
TracepointStringFinder(std::string&& s)
: EventTypeFinder(EventFinderType::TRACEPOINT_STRING), s_(std::move(s)) {}
protected:
void LoadTypes() override {
for (const auto& line : android::base::Split(s_, "\n")) {
std::vector<std::string> items = android::base::Split(line, " ");
CHECK_EQ(items.size(), 2u);
std::string event_name = items[0];
uint64_t id;
CHECK(android::base::ParseUint(items[1].c_str(), &id));
types_.emplace(event_name, PERF_TYPE_TRACEPOINT, id, "", "");
}
}
private:
const std::string s_;
};
class TracepointSystemFinder : public EventTypeFinder {
public:
TracepointSystemFinder() : EventTypeFinder(EventFinderType::TRACEPOINT_SYSTEM) {}
const EventType* FindType(const std::string& name) override {
if (auto it = types_.find(EventType(name, 0, 0, "", "")); it != types_.end()) {
return &*it;
}
std::vector<std::string> strs = android::base::Split(name, ":");
if (strs.size() != 2) {
return nullptr;
}
const char* tracefs_dir = GetTraceFsDir();
if (tracefs_dir == nullptr) {
return nullptr;
}
std::string path = tracefs_dir + std::string("/events/") + strs[0] + "/" + strs[1] + "/id";
uint64_t id;
if (!ReadEventId(path, &id)) {
return nullptr;
}
auto res = types_.emplace(name, PERF_TYPE_TRACEPOINT, id, "", "");
return &*res.first;
}
void RemoveType(const std::string& name) { types_.erase(EventType(name, 0, 0, "", "")); }
std::string ToString() {
std::string result;
for (auto& type : GetTypes()) {
if (!result.empty()) {
result.push_back('\n');
}
result += android::base::StringPrintf("%s %" PRIu64, type.name.c_str(), type.config);
}
return result;
}
protected:
void LoadTypes() override {
const char* tracefs_dir = GetTraceFsDir();
if (tracefs_dir == nullptr) {
return;
}
const std::string tracepoint_dirname = tracefs_dir + std::string("/events");
for (const auto& system_name : GetSubDirs(tracepoint_dirname)) {
std::string system_path = tracepoint_dirname + "/" + system_name;
for (const auto& event_name : GetSubDirs(system_path)) {
std::string id_path = system_path + "/" + event_name + "/id";
uint64_t id;
if (ReadEventId(id_path, &id)) {
types_.emplace(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", "");
}
}
}
}
private:
bool ReadEventId(const std::string& id_path, uint64_t* id) {
std::string id_content;
if (!android::base::ReadFileToString(id_path, &id_content)) {
return false;
}
if (!android::base::ParseUint(android::base::Trim(id_content), id)) {
LOG(DEBUG) << "unexpected id '" << id_content << "' in " << id_path;
return false;
}
return true;
}
};
class PMUTypeFinder : public EventTypeFinder {
public:
PMUTypeFinder() : EventTypeFinder(EventFinderType::PMU) {}
const EventType* FindType(const std::string& name) override {
if (name.find('/') == std::string::npos) {
return nullptr;
}
return EventTypeFinder::FindType(name);
}
protected:
void LoadTypes() override {
const std::string evtsrc_dirname = "/sys/bus/event_source/devices/";
for (const auto& device_name : GetSubDirs(evtsrc_dirname)) {
std::string evtdev_path = evtsrc_dirname + device_name;
std::string type_path = evtdev_path + "/type";
std::string type_content;
if (!android::base::ReadFileToString(type_path, &type_content)) {
LOG(DEBUG) << "cannot read event type: " << device_name;
continue;
}
uint64_t type_id = strtoull(type_content.c_str(), NULL, 10);
std::vector<EventFormat> formats = ParseEventFormats(evtdev_path);
std::string events_dirname = evtdev_path + "/events/";
for (const auto& event_name : GetEntriesInDir(events_dirname)) {
std::string event_path = events_dirname + event_name;
std::string event_content;
if (!android::base::ReadFileToString(event_path, &event_content)) {
LOG(DEBUG) << "cannot read event content in " << event_name;
continue;
}
uint64_t config = MakeEventConfig(event_content, formats);
if (config == ~0ULL) {
LOG(DEBUG) << "cannot handle config format in " << event_name;
continue;
}
types_.emplace(device_name + "/" + event_name + "/", type_id, config, "", "");
}
}
}
private:
std::vector<EventFormat> ParseEventFormats(const std::string& evtdev_path) {
std::vector<EventFormat> v;
std::string formats_dirname = evtdev_path + "/format/";
for (const auto& format_name : GetEntriesInDir(formats_dirname)) {
std::string format_path = formats_dirname + format_name;
std::string format_content;
if (!android::base::ReadFileToString(format_path, &format_content)) {
continue;
}
// format files look like below (currently only 'config' is supported) :
// # cat armv8_pmuv3/format/event
// config:0-15
int shift;
if (sscanf(format_content.c_str(), "config:%d", &shift) != 1) {
LOG(DEBUG) << "Invalid or unsupported event format: " << format_content;
continue;
}
v.emplace_back(EventFormat(format_name, "config", shift));
}
return v;
}
uint64_t MakeEventConfig(const std::string& event_str, std::vector<EventFormat>& formats) {
uint64_t config = 0;
// event files might have multiple terms, but usually have a term like:
// # cat armv8_pmuv3/events/cpu_cycles
// event=0x011
for (auto& s : android::base::Split(event_str, ",")) {
auto pos = s.find('=');
if (pos == std::string::npos) continue;
auto format = s.substr(0, pos);
long val;
if (!android::base::ParseInt(android::base::Trim(s.substr(pos + 1)), &val)) {
LOG(DEBUG) << "Invalid event format '" << s << "'";
continue;
}
for (auto& f : formats) {
if (f.name == format) {
if (f.attr != "config") {
LOG(DEBUG) << "cannot support other attribute: " << s;
return ~0ULL;
}
config |= val << f.shift;
break;
}
}
}
return config;
}
};
class ETMTypeFinder : public EventTypeFinder {
public:
ETMTypeFinder() : EventTypeFinder(EventFinderType::ETM) {}
const EventType* FindType(const std::string& name) override {
if (name != kETMEventName) {
return nullptr;
}
return EventTypeFinder::FindType(name);
}
protected:
void LoadTypes() override {
#if defined(__linux__)
std::unique_ptr<EventType> etm_type = ETMRecorder::GetInstance().BuildEventType();
if (etm_type) {
types_.emplace(std::move(*etm_type));
}
#endif
}
};
class RawTypeFinder : public EventTypeFinder {
public:
RawTypeFinder() : EventTypeFinder(EventFinderType::RAW) {}
const EventType* AddType(EventType&& type) {
auto result = types_.emplace(std::move(type));
return &*(result.first);
}
protected:
void LoadTypes() override {}
};
class ScopedTypeFinder : public EventTypeFinder {
public:
ScopedTypeFinder(std::set<EventType>&& types) : EventTypeFinder(EventFinderType::SCOPED) {
types_ = std::move(types);
}
protected:
void LoadTypes() override {}
};
EventTypeManager EventTypeManager::instance_;
EventTypeManager::EventTypeManager() {
type_finders_.emplace_back(new BuiltinTypeFinder());
type_finders_.emplace_back(new TracepointSystemFinder());
type_finders_.emplace_back(new PMUTypeFinder());
type_finders_.emplace_back(new ETMTypeFinder());
type_finders_.emplace_back(new RawTypeFinder());
}
EventTypeManager::~EventTypeManager() {}
std::unique_ptr<EventTypeFinder>& EventTypeManager::GetFinder(EventFinderType type) {
for (auto& finder : type_finders_) {
if (finder->GetFinderType() == type) {
return finder;
}
}
LOG(FATAL) << "Failed to get EventTypeFinder";
__builtin_unreachable();
}
RawTypeFinder& EventTypeManager::GetRawTypeFinder() {
return *static_cast<RawTypeFinder*>(GetFinder(EventFinderType::RAW).get());
}
TracepointSystemFinder& EventTypeManager::GetTracepointSystemFinder() {
return *static_cast<TracepointSystemFinder*>(GetFinder(EventFinderType::TRACEPOINT_SYSTEM).get());
}
bool EventTypeManager::ReadTracepointsFromFile(const std::string& filepath) {
std::string data;
if (!android::base::ReadFileToString(filepath, &data)) {
PLOG(ERROR) << "Failed to read " << filepath;
return false;
}
// Replace TracepointSystemFinder with TracepointStringFinder.
auto& finder = GetFinder(EventFinderType::TRACEPOINT_SYSTEM);
finder.reset(new TracepointStringFinder(std::move(data)));
return true;
}
bool EventTypeManager::WriteTracepointsToFile(const std::string& filepath) {
auto& tp_finder = GetTracepointSystemFinder();
std::string s = tp_finder.ToString();
if (!android::base::WriteStringToFile(s, filepath)) {
PLOG(ERROR) << "Failed to store tracepoint events";
return false;
}
return true;
}
bool EventTypeManager::ForEachType(const std::function<bool(const EventType&)>& callback) {
if (scoped_finder_) {
for (const auto& type : scoped_finder_->GetTypes()) {
if (!callback(type)) {
return false;
}
}
} else {
for (auto& finder : type_finders_) {
for (const auto& type : finder->GetTypes()) {
if (!callback(type)) {
return false;
}
}
}
}
return true;
}
const EventType* EventTypeManager::FindType(const std::string& name) {
if (scoped_finder_) {
return scoped_finder_->FindType(name);
}
for (auto& finder : type_finders_) {
if (auto type = finder->FindType(name)) {
return type;
}
}
return nullptr;
}
const EventType* EventTypeManager::AddRawType(const std::string& name) {
if (name.empty() || name[0] != 'r') {
return nullptr;
}
errno = 0;
char* end;
uint64_t config = strtoull(&name[1], &end, 16);
if (errno != 0 || *end != '\0') {
return nullptr;
}
auto& raw_finder = GetRawTypeFinder();
return raw_finder.AddType(EventType(name, PERF_TYPE_RAW, config, "", ""));
}
void EventTypeManager::RemoveProbeType(const std::string& name) {
GetTracepointSystemFinder().RemoveType(name);
}
void EventTypeManager::SetScopedFinder(std::unique_ptr<EventTypeFinder>&& finder) {
scoped_finder_ = std::move(finder);
}
std::vector<int> EventType::GetPmuCpumask() {
std::vector<int> empty_result;
if (!IsPmuEvent()) return empty_result;
std::string pmu = name.substr(0, name.find('/'));
std::string cpumask_path = "/sys/bus/event_source/devices/" + pmu + "/cpumask";
std::string cpumask_content;
if (!android::base::ReadFileToString(cpumask_path, &cpumask_content)) {
LOG(DEBUG) << "cannot read cpumask content in " << pmu;
return empty_result;
}
if (auto cpus = GetCpusFromString(cpumask_content); cpus) {
return std::vector<int>(cpus->begin(), cpus->end());
}
return empty_result;
}
std::string ScopedEventTypes::BuildString(const std::vector<const EventType*>& event_types) {
std::string result;
for (auto type : event_types) {
if (!result.empty()) {
result.push_back('\n');
}
result +=
android::base::StringPrintf("%s,%u,%" PRIu64, type->name.c_str(), type->type, type->config);
}
return result;
}
ScopedEventTypes::ScopedEventTypes(const std::string& event_type_str) {
std::set<EventType> event_types;
for (auto& s : android::base::Split(event_type_str, "\n")) {
std::string name = s.substr(0, s.find(','));
uint32_t type;
uint64_t config;
sscanf(s.c_str() + name.size(), ",%u,%" PRIu64, &type, &config);
event_types.emplace(name, type, config, "", "");
}
CHECK(EventTypeManager::Instance().GetScopedFinder() == nullptr);
EventTypeManager::Instance().SetScopedFinder(
std::make_unique<ScopedTypeFinder>(std::move(event_types)));
}
ScopedEventTypes::~ScopedEventTypes() {
CHECK(EventTypeManager::Instance().GetScopedFinder() != nullptr);
EventTypeManager::Instance().SetScopedFinder(nullptr);
}
const EventType* FindEventTypeByName(const std::string& name, bool report_error) {
const EventType* event_type = EventTypeManager::Instance().FindType(name);
if (event_type != nullptr) {
return event_type;
}
event_type = EventTypeManager::Instance().AddRawType(name);
if (event_type != nullptr) {
return event_type;
}
if (report_error) {
LOG(ERROR) << "Unknown event_type '" << name
<< "', try `simpleperf list` to list all possible event type names";
}
return nullptr;
}
std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_type_str) {
static std::string modifier_characters = "ukhGHp";
std::unique_ptr<EventTypeAndModifier> event_type_modifier(new EventTypeAndModifier);
event_type_modifier->name = event_type_str;
std::string event_type_name = event_type_str;
std::string modifier;
size_t comm_pos = event_type_str.rfind(':');
if (comm_pos != std::string::npos) {
bool match_modifier = true;
for (size_t i = comm_pos + 1; i < event_type_str.size(); ++i) {
char c = event_type_str[i];
if (c != ' ' && modifier_characters.find(c) == std::string::npos) {
match_modifier = false;
break;
}
}
if (match_modifier) {
event_type_name = event_type_str.substr(0, comm_pos);
modifier = event_type_str.substr(comm_pos + 1);
}
}
const EventType* event_type = FindEventTypeByName(event_type_name);
if (event_type == nullptr) {
// Try if the modifier belongs to the event type name, like some tracepoint events.
if (!modifier.empty()) {
event_type_name = event_type_str;
modifier.clear();
event_type = FindEventTypeByName(event_type_name);
}
if (event_type == nullptr) {
return nullptr;
}
}
event_type_modifier->event_type = *event_type;
if (modifier.find_first_of("ukh") != std::string::npos) {
event_type_modifier->exclude_user = true;
event_type_modifier->exclude_kernel = true;
event_type_modifier->exclude_hv = true;
}
if (modifier.find_first_of("GH") != std::string::npos) {
event_type_modifier->exclude_guest = true;
event_type_modifier->exclude_host = true;
}
for (auto& c : modifier) {
switch (c) {
case 'u':
event_type_modifier->exclude_user = false;
break;
case 'k':
event_type_modifier->exclude_kernel = false;
break;
case 'h':
event_type_modifier->exclude_hv = false;
break;
case 'G':
event_type_modifier->exclude_guest = false;
break;
case 'H':
event_type_modifier->exclude_host = false;
break;
case 'p':
event_type_modifier->precise_ip++;
break;
case ' ':
break;
default:
LOG(ERROR) << "Unknown event type modifier '" << c << "'";
}
}
event_type_modifier->modifier = modifier;
return event_type_modifier;
}
bool IsEtmEventType(uint32_t type) {
const EventType* event_type = EventTypeManager::Instance().FindType(kETMEventName);
return (event_type != nullptr) && (event_type->type == type);
}
} // namespace simpleperf