summaryrefslogtreecommitdiff
path: root/simpleperf/cmd_list.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'simpleperf/cmd_list.cpp')
-rw-r--r--simpleperf/cmd_list.cpp272
1 files changed, 217 insertions, 55 deletions
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 4953d9f1..926b7f7e 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -14,9 +14,13 @@
* limitations under the License.
*/
+#include <sched.h>
#include <stdio.h>
+
+#include <atomic>
#include <map>
#include <string>
+#include <thread>
#include <vector>
#include <android-base/file.h>
@@ -31,86 +35,244 @@
#include "event_type.h"
namespace simpleperf {
+
+extern std::unordered_map<std::string, std::unordered_set<int>> cpu_supported_raw_events;
+
+#if defined(__aarch64__) || defined(__arm__)
+extern std::unordered_map<uint64_t, std::string> arm64_cpuid_to_name;
+#endif // defined(__aarch64__) || defined(__arm__)
+
namespace {
-enum EventTypeStatus {
- NOT_SUPPORTED,
- MAY_NOT_SUPPORTED,
- SUPPORTED,
+struct RawEventTestThreadArg {
+ int cpu;
+ std::atomic<pid_t> tid;
+ std::atomic<bool> start;
+};
+
+static void RawEventTestThread(RawEventTestThreadArg* arg) {
+ cpu_set_t mask;
+ CPU_ZERO(&mask);
+ CPU_SET(arg->cpu, &mask);
+ int tid = gettid();
+ sched_setaffinity(tid, sizeof(mask), &mask);
+ arg->tid = tid;
+ while (!arg->start) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ TemporaryFile tmpfile;
+ FILE* fp = fopen(tmpfile.path, "w");
+ if (fp == nullptr) {
+ return;
+ }
+ for (int i = 0; i < 10; ++i) {
+ fprintf(fp, "output some data\n");
+ }
+ fclose(fp);
+}
+
+struct RawEventSupportStatus {
+ std::vector<int> supported_cpus;
+ std::vector<int> may_supported_cpus;
};
-static EventTypeStatus IsEventTypeSupported(const EventType& event_type) {
- // Because PMU events are provided by kernel, we assume it's supported.
- if (event_type.IsPmuEvent()) {
- return EventTypeStatus::SUPPORTED;
+class RawEventSupportChecker {
+ public:
+ bool Init() {
+#if defined(__aarch64__) || defined(__arm__)
+ cpu_models_ = GetARMCpuModels();
+ if (cpu_models_.empty()) {
+ LOG(ERROR) << "can't get device cpu info";
+ return false;
+ }
+ for (const auto& model : cpu_models_) {
+ uint64_t cpu_id = (static_cast<uint64_t>(model.implementer) << 32) | model.partnum;
+ if (auto it = arm64_cpuid_to_name.find(cpu_id); it != arm64_cpuid_to_name.end()) {
+ cpu_model_names_.push_back(it->second);
+ } else {
+ cpu_model_names_.push_back("");
+ }
+ }
+#endif // defined(__aarch64__) || defined(__arm__)
+ return true;
+ }
+
+ RawEventSupportStatus GetCpusSupportingEvent(const EventType& event_type) {
+ RawEventSupportStatus status;
+ std::string required_cpu_model;
+ // For cpu model specific events, the limited_arch is like "arm64:Cortex-A520".
+ if (auto pos = event_type.limited_arch.find(':'); pos != std::string::npos) {
+ required_cpu_model = event_type.limited_arch.substr(pos + 1);
+ }
+
+ for (size_t i = 0; i < cpu_models_.size(); ++i) {
+ const ARMCpuModel& model = cpu_models_[i];
+ const std::string& model_name = cpu_model_names_[i];
+ bool supported = false;
+ bool may_supported = false;
+ if (!required_cpu_model.empty()) {
+ // This is a cpu model specific event, only supported on required_cpu_model.
+ supported = model_name == required_cpu_model;
+ } else if (!model_name.empty()) {
+ // We know events supported on this cpu model.
+ auto it = cpu_supported_raw_events.find(model_name);
+ CHECK(it != cpu_supported_raw_events.end()) << "no events configuration for " << model_name;
+ supported = it->second.count(event_type.config) > 0;
+ } else {
+ // We need to test the event support status.
+ TestEventSupportOnCpu(event_type, model.cpus[0], supported, may_supported);
+ }
+
+ if (supported) {
+ status.supported_cpus.insert(status.supported_cpus.end(), model.cpus.begin(),
+ model.cpus.end());
+ } else if (may_supported) {
+ status.may_supported_cpus.insert(status.may_supported_cpus.end(), model.cpus.begin(),
+ model.cpus.end());
+ }
+ }
+ return status;
}
- if (event_type.type != PERF_TYPE_RAW) {
+
+ private:
+ void TestEventSupportOnCpu(const EventType& event_type, int cpu, bool& supported,
+ bool& may_supported) {
+ // Because the kernel may not check whether the raw event is supported by the cpu pmu.
+ // We can't decide whether the raw event is supported by calling perf_event_open().
+ // Instead, we can check if it can collect some real number.
+ RawEventTestThreadArg test_thread_arg;
+ test_thread_arg.cpu = cpu;
+ test_thread_arg.tid = 0;
+ test_thread_arg.start = false;
+ std::thread test_thread(RawEventTestThread, &test_thread_arg);
+ while (test_thread_arg.tid == 0) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
perf_event_attr attr = CreateDefaultPerfEventAttr(event_type);
- // Exclude kernel to list supported events even when kernel recording isn't allowed.
- attr.exclude_kernel = 1;
- return IsEventAttrSupported(attr, event_type.name) ? EventTypeStatus::SUPPORTED
- : EventTypeStatus::NOT_SUPPORTED;
- }
- if (event_type.limited_arch == "arm" && GetTargetArch() != ARCH_ARM &&
- GetTargetArch() != ARCH_ARM64) {
- return EventTypeStatus::NOT_SUPPORTED;
- }
- // Because the kernel may not check whether the raw event is supported by the cpu pmu.
- // We can't decide whether the raw event is supported by calling perf_event_open().
- // Instead, we can check if it can collect some real number.
- perf_event_attr attr = CreateDefaultPerfEventAttr(event_type);
- std::unique_ptr<EventFd> event_fd =
- EventFd::OpenEventFile(attr, gettid(), -1, nullptr, event_type.name, false);
- if (event_fd == nullptr) {
- return EventTypeStatus::NOT_SUPPORTED;
- }
- auto work_function = []() {
- TemporaryFile tmpfile;
- FILE* fp = fopen(tmpfile.path, "w");
- if (fp == nullptr) {
+ std::unique_ptr<EventFd> event_fd = EventFd::OpenEventFile(
+ attr, test_thread_arg.tid, test_thread_arg.cpu, nullptr, event_type.name, false);
+ test_thread_arg.start = true;
+ test_thread.join();
+ if (event_fd == nullptr) {
+ supported = may_supported = false;
+ return;
+ }
+ PerfCounter counter;
+ if (!event_fd->ReadCounter(&counter)) {
+ supported = may_supported = false;
return;
}
- for (int i = 0; i < 10; ++i) {
- fprintf(fp, "output some data\n");
+ if (counter.value != 0) {
+ supported = true;
+ may_supported = false;
+ } else {
+ supported = false;
+ may_supported = true;
}
- fclose(fp);
+ }
+
+ std::vector<ARMCpuModel> cpu_models_;
+ std::vector<std::string> cpu_model_names_;
+};
+
+static std::string ToCpuString(const std::vector<int>& cpus) {
+ std::string s;
+ if (cpus.empty()) {
+ return s;
+ }
+ s += std::to_string(cpus[0]);
+ int last_cpu = cpus[0];
+ bool added = true;
+ for (size_t i = 1; i < cpus.size(); ++i) {
+ if (cpus[i] == last_cpu + 1) {
+ last_cpu = cpus[i];
+ added = false;
+ } else {
+ s += "-" + std::to_string(last_cpu) + "," + std::to_string(cpus[i]);
+ last_cpu = cpus[i];
+ added = true;
+ }
+ }
+ if (!added) {
+ s += "-" + std::to_string(last_cpu);
+ }
+ return s;
+}
+
+static void PrintRawEventTypes(const std::string& type_desc) {
+ printf("List of %s:\n", type_desc.c_str());
+#if defined(__aarch64__) || defined(__arm__)
+ printf(
+ // clang-format off
+" # Please refer to \"PMU common architectural and microarchitectural event numbers\"\n"
+" # and \"ARM recommendations for IMPLEMENTATION DEFINED event numbers\" listed in\n"
+" # ARMv9 manual for details.\n"
+" # A possible link is https://developer.arm.com/documentation/ddi0487.\n"
+ // clang-format on
+ );
+#endif // defined(__aarch64__) || defined(__arm__)
+ RawEventSupportChecker support_checker;
+ if (!support_checker.Init()) {
+ return;
+ }
+ auto callback = [&](const EventType& event_type) {
+ if (event_type.type != PERF_TYPE_RAW) {
+ return true;
+ }
+ RawEventSupportStatus status = support_checker.GetCpusSupportingEvent(event_type);
+ if (status.supported_cpus.empty() && status.may_supported_cpus.empty()) {
+ return true;
+ }
+ std::string text = " " + event_type.name + " (";
+ if (!status.supported_cpus.empty()) {
+ text += "supported on cpu " + ToCpuString(status.supported_cpus);
+ if (!status.may_supported_cpus.empty()) {
+ text += ", ";
+ }
+ }
+ if (!status.may_supported_cpus.empty()) {
+ text += "may supported on cpu " + ToCpuString(status.may_supported_cpus);
+ }
+ text += ")";
+ printf("%s", text.c_str());
+ if (!event_type.description.empty()) {
+ printf("\t\t# %s", event_type.description.c_str());
+ }
+ printf("\n");
+ return true;
};
- work_function();
- PerfCounter counter;
- if (!event_fd->ReadCounter(&counter)) {
- return EventTypeStatus::NOT_SUPPORTED;
+ EventTypeManager::Instance().ForEachType(callback);
+ printf("\n");
+}
+
+static bool IsEventTypeSupported(const EventType& event_type) {
+ // PMU and tracepoint events are provided by kernel. So we assume they're supported.
+ if (event_type.IsPmuEvent() || event_type.IsTracepointEvent()) {
+ return true;
}
- // For raw events, we may not be able to detect whether it is supported on device.
- return (counter.value != 0u) ? EventTypeStatus::SUPPORTED : EventTypeStatus::MAY_NOT_SUPPORTED;
+ perf_event_attr attr = CreateDefaultPerfEventAttr(event_type);
+ // Exclude kernel to list supported events even when kernel recording isn't allowed.
+ attr.exclude_kernel = 1;
+ return IsEventAttrSupported(attr, event_type.name);
}
static void PrintEventTypesOfType(const std::string& type_name, const std::string& type_desc,
const std::function<bool(const EventType&)>& is_type_fn) {
+ if (type_name == "raw") {
+ return PrintRawEventTypes(type_desc);
+ }
printf("List of %s:\n", type_desc.c_str());
if (GetTargetArch() == ARCH_ARM || GetTargetArch() == ARCH_ARM64) {
- if (type_name == "raw") {
- printf(
- // clang-format off
-" # Please refer to \"PMU common architectural and microarchitectural event numbers\"\n"
-" # and \"ARM recommendations for IMPLEMENTATION DEFINED event numbers\" listed in\n"
-" # ARMv8 manual for details.\n"
-" # A possible link is https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile.\n"
- // clang-format on
- );
- } else if (type_name == "cache") {
+ if (type_name == "cache") {
printf(" # More cache events are available in `simpleperf list raw`.\n");
}
}
auto callback = [&](const EventType& event_type) {
if (is_type_fn(event_type)) {
- EventTypeStatus status = IsEventTypeSupported(event_type);
- if (status == EventTypeStatus::NOT_SUPPORTED) {
+ if (!IsEventTypeSupported(event_type)) {
return true;
}
printf(" %s", event_type.name.c_str());
- if (status == EventTypeStatus::MAY_NOT_SUPPORTED) {
- printf(" (may not supported)");
- }
if (!event_type.description.empty()) {
printf("\t\t# %s", event_type.description.c_str());
}