summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2019-07-12 13:09:15 -0700
committerYabin Cui <yabinc@google.com>2019-07-17 11:23:48 -0700
commitd5bccd10425f771b04bab0311826416a9bc2ee54 (patch)
treed19d50dc27f47dbad799daec66f9574cd8f68063
parentecddd834da99bf957972bed4e8d79628c13fe921 (diff)
downloadextras-d5bccd10425f771b04bab0311826416a9bc2ee54.tar.gz
simpleperf: add cs-etm event type.
cs-etm event type is used to select etm instruction tracing. It can be show in simpleperf list cmd, and used by `simpleperf record -e cs-etm`. Changes receiving etm data from the kernel will be added in later CLs. Bug: 135204414 Test: run simpleperf_unit_test. Change-Id: I431b5521dc23519ff8e154d50458ec0aa9ac65cd
-rw-r--r--simpleperf/Android.bp1
-rw-r--r--simpleperf/ETMRecorder.cpp164
-rw-r--r--simpleperf/ETMRecorder.h68
-rw-r--r--simpleperf/cmd_list.cpp10
-rw-r--r--simpleperf/cmd_record_test.cpp8
-rw-r--r--simpleperf/event_attr.cpp6
-rw-r--r--simpleperf/event_selection_set.cpp10
-rw-r--r--simpleperf/event_type.cpp9
8 files changed, 274 insertions, 2 deletions
diff --git a/simpleperf/Android.bp b/simpleperf/Android.bp
index be134209..df43a59a 100644
--- a/simpleperf/Android.bp
+++ b/simpleperf/Android.bp
@@ -285,6 +285,7 @@ cc_defaults {
"cmd_stat.cpp",
"cmd_trace_sched.cpp",
"environment.cpp",
+ "ETMRecorder.cpp",
"event_fd.cpp",
"event_selection_set.cpp",
"InplaceSamplerClient.cpp",
diff --git a/simpleperf/ETMRecorder.cpp b/simpleperf/ETMRecorder.cpp
new file mode 100644
index 00000000..0da803f5
--- /dev/null
+++ b/simpleperf/ETMRecorder.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2019 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 "ETMRecorder.h"
+
+#include <stdio.h>
+#include <sys/sysinfo.h>
+
+#include <memory>
+#include <limits>
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/parseint.h>
+#include <android-base/strings.h>
+
+#include "utils.h"
+
+namespace simpleperf {
+
+static constexpr uint64_t ETM4_CFG_CTXTID = 1ULL << 6;
+static constexpr uint64_t ETM4_CFG_TS = 1ULL << 11;
+
+static const std::string ETM_DIR = "/sys/bus/event_source/devices/cs_etm/";
+
+template <typename T>
+static bool ReadValueInEtmDir(const std::string& file, T* value) {
+ std::string s;
+ uint64_t v;
+ if (!android::base::ReadFileToString(ETM_DIR + file, &s) ||
+ !android::base::ParseUint(android::base::Trim(s), &v)) {
+ LOG(ERROR) << "failed to read " << ETM_DIR << file;
+ return false;
+ }
+ *value = static_cast<T>(v);
+ return true;
+}
+
+static uint32_t GetBits(uint32_t value, int start, int end) {
+ return (value >> start) & ((1U << (end - start + 1)) - 1);
+}
+
+int ETMPerCpu::GetMajorVersion() const {
+ return GetBits(trcidr1, 8, 11);
+}
+
+bool ETMPerCpu::IsContextIDSupported() const {
+ return GetBits(trcidr2, 5, 9) >= 4;
+}
+
+bool ETMPerCpu::IsTimestampSupported() const {
+ return GetBits(trcidr0, 24, 28) > 0;
+}
+
+ETMRecorder& ETMRecorder::GetInstance() {
+ static ETMRecorder etm;
+ return etm;
+}
+
+int ETMRecorder::GetEtmEventType() {
+ if (event_type_ == 0) {
+ if (!IsDir(ETM_DIR) || !ReadValueInEtmDir("type", &event_type_)) {
+ event_type_ = -1;
+ }
+ }
+ return event_type_;
+}
+
+std::unique_ptr<EventType> ETMRecorder::BuildEventType() {
+ int etm_event_type = GetEtmEventType();
+ if (etm_event_type == -1) {
+ return nullptr;
+ }
+ return std::make_unique<EventType>(
+ "cs-etm", etm_event_type, 0, "CoreSight ETM instruction tracing", "arm");
+}
+
+bool ETMRecorder::CheckEtmSupport() {
+ if (GetEtmEventType() == -1) {
+ LOG(ERROR) << "etm event type isn't supported on device";
+ return false;
+ }
+ if (!ReadEtmInfo()) {
+ LOG(ERROR) << "etm devices are not available";
+ return false;
+ }
+ for (const auto& p : etm_info_) {
+ if (p.second.GetMajorVersion() < 4) {
+ LOG(ERROR) << "etm device version is less than 4.0";
+ return false;
+ }
+ if (!p.second.IsContextIDSupported()) {
+ LOG(ERROR) << "etm device doesn't support contextID";
+ return false;
+ }
+ }
+ if (!FindSinkConfig()) {
+ LOG(ERROR) << "can't find etr device, which moves etm data to memory";
+ return false;
+ }
+ etm_supported_ = true;
+ return true;
+}
+
+bool ETMRecorder::ReadEtmInfo() {
+ int cpu_count = get_nprocs_conf();
+ for (const auto &name : GetEntriesInDir(ETM_DIR)) {
+ int cpu;
+ if (sscanf(name.c_str(), "cpu%d", &cpu) == 1) {
+ ETMPerCpu &cpu_info = etm_info_[cpu];
+ bool success =
+ ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
+ ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
+ ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
+ ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
+ ReadValueInEtmDir(name + "/trcidr/trcidr9", &cpu_info.trcidr9) &&
+ ReadValueInEtmDir(name + "/mgmt/trctraceid", &cpu_info.trctraceid);
+ if (!success) {
+ return false;
+ }
+ }
+ }
+ return (etm_info_.size() == cpu_count);
+}
+
+bool ETMRecorder::FindSinkConfig() {
+ for (const auto &name : GetEntriesInDir(ETM_DIR + "sinks")) {
+ if (name.find("etr") != -1) {
+ if (ReadValueInEtmDir("sinks/" + name, &sink_config_)) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void ETMRecorder::SetEtmPerfEventAttr(perf_event_attr* attr) {
+ CHECK(etm_supported_);
+ attr->config |= ETM4_CFG_CTXTID;
+ bool ts_supported = true;
+ for (auto& p : etm_info_) {
+ ts_supported &= p.second.IsTimestampSupported();
+ }
+ if (ts_supported) {
+ attr->config |= ETM4_CFG_TS;
+ }
+ attr->config2 = sink_config_;
+}
+
+} // namespace simpleperf \ No newline at end of file
diff --git a/simpleperf/ETMRecorder.h b/simpleperf/ETMRecorder.h
new file mode 100644
index 00000000..f4c57378
--- /dev/null
+++ b/simpleperf/ETMRecorder.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#pragma once
+
+#include <inttypes.h>
+
+#include <map>
+#include <memory>
+
+#include "event_type.h"
+#include "perf_event.h"
+
+namespace simpleperf {
+
+struct ETMPerCpu {
+ uint32_t trcidr0;
+ uint32_t trcidr1;
+ uint32_t trcidr2;
+ uint32_t trcidr8;
+ uint32_t trcidr9;
+ uint32_t trctraceid;
+
+ int GetMajorVersion() const;
+ bool IsContextIDSupported() const;
+ bool IsTimestampSupported() const;
+};
+
+// Help recording Coresight ETM data on ARM devices.
+// 1. Get etm event type on device.
+// 2. Get sink config, which selects the ETR device moving etm data to memory.
+// 3. Get etm info on each cpu.
+// The etm event type and sink config are used to build perf_event_attr for etm data tracing.
+// The etm info is kept in perf.data to help etm decoding.
+class ETMRecorder {
+ public:
+ static ETMRecorder& GetInstance();
+
+ // If not found, return -1.
+ int GetEtmEventType();
+ std::unique_ptr<EventType> BuildEventType();
+ bool CheckEtmSupport();
+ void SetEtmPerfEventAttr(perf_event_attr* attr);
+
+ private:
+ bool ReadEtmInfo();
+ bool FindSinkConfig();
+
+ int event_type_ = 0;
+ bool etm_supported_ = false;
+ uint32_t sink_config_ = 0;
+ std::map<int, ETMPerCpu> etm_info_;
+};
+
+} // namespace simpleperf \ No newline at end of file
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index c87c05f5..97549bed 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -24,11 +24,14 @@
#include "command.h"
#include "environment.h"
+#include "ETMRecorder.h"
#include "event_attr.h"
#include "event_fd.h"
#include "event_selection_set.h"
#include "event_type.h"
+using namespace simpleperf;
+
static bool IsEventTypeSupported(const EventType& event_type) {
if (event_type.type != PERF_TYPE_RAW) {
perf_event_attr attr = CreateDefaultPerfEventAttr(event_type);
@@ -100,8 +103,9 @@ class ListCommand : public Command {
" hw hardware events\n"
" sw software events\n"
" cache hardware cache events\n"
-" raw raw pmu events\n"
+" raw raw cpu pmu events\n"
" tracepoint tracepoint events\n"
+" cs-etm coresight etm instruction tracing events\n"
"Options:\n"
"--show-features Show features supported on the device, including:\n"
" dwarf-based-call-graph\n"
@@ -128,6 +132,7 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
{"raw", {PERF_TYPE_RAW, "raw events provided by cpu pmu"}},
{"tracepoint", {PERF_TYPE_TRACEPOINT, "tracepoint events"}},
{"user-space-sampler", {SIMPLEPERF_TYPE_USER_SPACE_SAMPLERS, "user-space samplers"}},
+ {"cs-etm", {-1, "coresight etm events"}},
};
std::vector<std::string> names;
@@ -153,6 +158,9 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
for (auto& name : names) {
auto it = type_map.find(name);
+ if (name == "cs-etm") {
+ it->second.first = ETMRecorder::GetInstance().GetEtmEventType();
+ }
PrintEventTypesOfType(it->second.first, it->second.second, event_types);
}
return true;
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index 1f0f8d3c..39cf496d 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -31,6 +31,7 @@
#include "command.h"
#include "environment.h"
+#include "ETMRecorder.h"
#include "event_selection_set.h"
#include "get_test_data.h"
#include "record.h"
@@ -38,6 +39,7 @@
#include "test_util.h"
#include "thread_tree.h"
+using namespace simpleperf;
using namespace PerfFileFormat;
static std::unique_ptr<Command> RecordCmd() {
@@ -803,3 +805,9 @@ TEST(record_cmd, no_cut_samples_option) {
TEST_REQUIRE_HW_COUNTER();
ASSERT_TRUE(RunRecordCmd({"--no-cut-samples"}));
}
+
+TEST(record_cmd, cs_etm_event) {
+ if (ETMRecorder::GetInstance().CheckEtmSupport()) {
+ ASSERT_TRUE(RunRecordCmd({"-e", "cs-etm"}));
+ }
+} \ No newline at end of file
diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp
index 09efa31b..121a7efa 100644
--- a/simpleperf/event_attr.cpp
+++ b/simpleperf/event_attr.cpp
@@ -133,6 +133,7 @@ void DumpPerfEventAttr(const perf_event_attr& attr, size_t indent) {
PrintIndented(indent + 1, "sample_id_all %u, exclude_host %u, exclude_guest %u\n",
attr.sample_id_all, attr.exclude_host, attr.exclude_guest);
+ PrintIndented(indent + 1, "config2 0x%llx\n", attr.config2);
PrintIndented(indent + 1, "branch_sample_type 0x%" PRIx64 "\n", attr.branch_sample_type);
PrintIndented(indent + 1, "exclude_callchain_kernel %u, exclude_callchain_user %u\n",
attr.exclude_callchain_kernel, attr.exclude_callchain_user);
@@ -229,7 +230,10 @@ bool IsCpuSupported(const perf_event_attr& attr) {
std::string GetEventNameByAttr(const perf_event_attr& attr) {
for (const auto& event_type : GetAllEventTypes()) {
- if (event_type.type == attr.type && event_type.config == attr.config) {
+ // An event type uses both type and config value to define itself. But etm event type
+ // only uses type value (whose config value is used to set etm options).
+ if (event_type.type == attr.type &&
+ (event_type.config == attr.config || event_type.name == "cs-etm")) {
std::string name = event_type.name;
if (attr.exclude_user && !attr.exclude_kernel) {
name += ":k";
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 44085bc5..622e4bc8 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -23,6 +23,7 @@
#include <android-base/logging.h>
#include "environment.h"
+#include "ETMRecorder.h"
#include "event_attr.h"
#include "event_type.h"
#include "IOEventLoop.h"
@@ -30,6 +31,8 @@
#include "utils.h"
#include "RecordReadThread.h"
+using namespace simpleperf;
+
bool IsBranchSamplingSupported() {
const EventType* type = FindEventTypeByName("cpu-cycles");
if (type == nullptr) {
@@ -159,6 +162,13 @@ bool EventSelectionSet::BuildAndCheckEventSelection(const std::string& event_nam
selection->event_attr.exclude_host = event_type->exclude_host;
selection->event_attr.exclude_guest = event_type->exclude_guest;
selection->event_attr.precise_ip = event_type->precise_ip;
+ if (event_type->event_type.name == "cs-etm") {
+ auto& etm_recorder = ETMRecorder::GetInstance();
+ if (!etm_recorder.CheckEtmSupport()) {
+ return false;
+ }
+ ETMRecorder::GetInstance().SetEtmPerfEventAttr(&selection->event_attr);
+ }
bool set_default_sample_freq = false;
if (!for_stat_cmd_) {
if (event_type->event_type.type == PERF_TYPE_TRACEPOINT) {
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index ed323791..96f4c683 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -28,9 +28,12 @@
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
+#include "ETMRecorder.h"
#include "event_attr.h"
#include "utils.h"
+using namespace simpleperf;
+
#define EVENT_TYPE_TABLE_ENTRY(name, type, config, description, limited_arch) \
{name, type, config, description, limited_arch},
@@ -145,6 +148,12 @@ const std::set<EventType>& GetAllEventTypes() {
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());
+#if defined(__linux__)
+ std::unique_ptr<EventType> etm_type = ETMRecorder::GetInstance().BuildEventType();
+ if (etm_type) {
+ g_event_types.emplace(std::move(*etm_type));
+ }
+#endif
}
return g_event_types;
}