diff options
Diffstat (limited to 'simpleperf/event_type.cpp')
-rw-r--r-- | simpleperf/event_type.cpp | 566 |
1 files changed, 384 insertions, 182 deletions
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp index 71c3d9f1..135e69da 100644 --- a/simpleperf/event_type.cpp +++ b/simpleperf/event_type.cpp @@ -28,17 +28,16 @@ #include <android-base/stringprintf.h> #include <android-base/strings.h> -#include "environment.h" #include "ETMRecorder.h" +#include "environment.h" #include "event_attr.h" #include "utils.h" -using namespace simpleperf; +namespace simpleperf { struct EventFormat { EventFormat(const std::string& name, const std::string& attr, int shift) - : name(name), attr(attr), shift(shift) { - } + : name(name), attr(attr), shift(shift) {} std::string name; std::string attr; @@ -46,188 +45,411 @@ struct EventFormat { }; #define EVENT_TYPE_TABLE_ENTRY(name, type, config, description, limited_arch) \ - {name, type, config, description, limited_arch}, + {name, type, config, description, limited_arch}, -static const std::vector<EventType> static_event_type_array = { +static const std::set<EventType> builtin_event_types = { #include "event_type_table.h" }; -static std::string tracepoint_events; -static std::set<EventType> g_event_types; -static uint32_t g_etm_event_type; +enum class EventFinderType { + BUILTIN, + TRACEPOINT_STRING, + TRACEPOINT_SYSTEM, + PMU, + ETM, + RAW, + SCOPED, +}; -bool SetTracepointEventsFilePath(const std::string& filepath) { - if (!android::base::ReadFileToString(filepath, &tracepoint_events)) { - PLOG(ERROR) << "Failed to read " << filepath; - return false; +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_; } - return true; -} -std::string GetTracepointEvents() { - std::string result; - for (auto& event : GetAllEventTypes()) { - if (event.type != PERF_TYPE_TRACEPOINT) { - continue; + 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; } - if (!result.empty()) { - result.push_back('\n'); + 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, "", ""); } - result += android::base::StringPrintf("%s %" PRIu64, event.name.c_str(), event.config); } - return result; -} -static std::vector<EventType> GetTracepointEventTypesFromString(const std::string& s) { - std::vector<EventType> result; - for (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]; + 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; - CHECK(android::base::ParseUint(items[1].c_str(), &id)); - result.push_back(EventType(event_name, PERF_TYPE_TRACEPOINT, id, "", "")); + if (!ReadEventId(path, &id)) { + return nullptr; + } + auto res = types_.emplace(name, PERF_TYPE_TRACEPOINT, id, "", ""); + return &*res.first; } - return result; -} -static std::vector<EventType> GetTracepointEventTypesFromTraceFs() { - std::vector<EventType> result; - const char* tracefs_dir = GetTraceFsDir(); - if (tracefs_dir == nullptr) { + 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; } - 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"; - std::string id_content; - if (!android::base::ReadFileToString(id_path, &id_content)) { + + 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; } - char* endptr; - uint64_t id = strtoull(id_content.c_str(), &endptr, 10); - if (endptr == id_content.c_str()) { - LOG(DEBUG) << "unexpected id '" << id_content << "' in " << id_path; + 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; } - result.push_back(EventType(system_name + ":" + event_name, PERF_TYPE_TRACEPOINT, id, "", "")); + + v.emplace_back(EventFormat(format_name, "config", shift)); } + return v; } - return result; -} -static std::vector<EventType> GetTracepointEventTypes() { - std::vector<EventType> result; - if (!tracepoint_events.empty()) { - result = GetTracepointEventTypesFromString(tracepoint_events); - } else { - result = GetTracepointEventTypesFromTraceFs(); + 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; } - std::sort(result.begin(), result.end(), - [](const EventType& type1, const EventType& type2) { return type1.name < type2.name; }); - return result; -} +}; + +class ETMTypeFinder : public EventTypeFinder { + public: + ETMTypeFinder() : EventTypeFinder(EventFinderType::ETM) {} -static 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; + const EventType* FindType(const std::string& name) override { + if (name != kETMEventName) { + return nullptr; } + return EventTypeFinder::FindType(name); + } - // 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; + 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) {} - v.emplace_back(EventFormat(format_name, "config", shift)); + const EventType* AddType(EventType&& type) { + auto result = types_.emplace(std::move(type)); + return &*(result.first); } - return v; + + 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()); } -static 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; +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(); +} - for (auto& f : formats) { - if (f.name == format) { - if (f.attr != "config") { - LOG(DEBUG) << "cannot support other attribute: " << s; - return ~0ULL; - } +RawTypeFinder& EventTypeManager::GetRawTypeFinder() { + return *static_cast<RawTypeFinder*>(GetFinder(EventFinderType::RAW).get()); +} - config |= val << f.shift; - break; +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 config; + return true; } -static std::vector<EventType> GetPmuEventTypes() { - std::vector<EventType> result; - 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; +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; } - uint64_t type_id = strtoull(type_content.c_str(), NULL, 10); + } + return nullptr; +} - std::vector<EventFormat> formats = ParseEventFormats(evtdev_path); +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, "", "")); +} - 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; - } +void EventTypeManager::RemoveProbeType(const std::string& name) { + GetTracepointSystemFinder().RemoveType(name); +} - uint64_t config = MakeEventConfig(event_content, formats); - if (config == ~0ULL) { - LOG(DEBUG) << "cannot handle config format in " << event_name; - continue; - } - result.emplace_back(EventType(device_name + "/" + event_name + "/", - type_id, config, "", "")); - } - } - return result; +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; + if (!IsPmuEvent()) return empty_result; std::string pmu = name.substr(0, name.find('/')); std::string cpumask_path = "/sys/bus/event_source/devices/" + pmu + "/cpumask"; @@ -236,7 +458,10 @@ std::vector<int> EventType::GetPmuCpumask() { LOG(DEBUG) << "cannot read cpumask content in " << pmu; return empty_result; } - return GetCpusFromString(cpumask_content); + 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) { @@ -245,65 +470,39 @@ std::string ScopedEventTypes::BuildString(const std::vector<const EventType*>& e if (!result.empty()) { result.push_back('\n'); } - result += android::base::StringPrintf("%s,%u,%" PRIu64, type->name.c_str(), type->type, - type->config); + 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) { - saved_event_types_ = std::move(g_event_types); - saved_etm_event_type_ = g_etm_event_type; - g_event_types.clear(); + 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); - if (name == "cs-etm") { - g_etm_event_type = type; - } - g_event_types.emplace(name, 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() { - g_event_types = std::move(saved_event_types_); - g_etm_event_type = saved_etm_event_type_; -} - -const std::set<EventType>& GetAllEventTypes() { - if (g_event_types.empty()) { - g_event_types.insert(static_event_type_array.begin(), static_event_type_array.end()); - std::vector<EventType> tracepoint_array = GetTracepointEventTypes(); - g_event_types.insert(tracepoint_array.begin(), tracepoint_array.end()); - std::vector<EventType> pmu_array = GetPmuEventTypes(); - g_event_types.insert(pmu_array.begin(), pmu_array.end()); -#if defined(__linux__) - std::unique_ptr<EventType> etm_type = ETMRecorder::GetInstance().BuildEventType(); - if (etm_type) { - g_etm_event_type = etm_type->type; - g_event_types.emplace(std::move(*etm_type)); - } -#endif - } - return g_event_types; + CHECK(EventTypeManager::Instance().GetScopedFinder() != nullptr); + EventTypeManager::Instance().SetScopedFinder(nullptr); } const EventType* FindEventTypeByName(const std::string& name, bool report_error) { - const auto& event_types = GetAllEventTypes(); - auto it = event_types.find(EventType(name, 0, 0, "", "")); - if (it != event_types.end()) { - return &*it; - } - if (!name.empty() && name[0] == 'r') { - char* end; - uint64_t config = strtoull(&name[1], &end, 16); - if (end != &name[1] && *end == '\0') { - auto result = g_event_types.emplace(name, PERF_TYPE_RAW, config, "", ""); - CHECK(result.second); - return &*(result.first); - } + 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 @@ -387,5 +586,8 @@ std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_ty } bool IsEtmEventType(uint32_t type) { - return g_etm_event_type != 0 && type == g_etm_event_type; + const EventType* event_type = EventTypeManager::Instance().FindType(kETMEventName); + return (event_type != nullptr) && (event_type->type == type); } + +} // namespace simpleperf |