diff options
author | Yabin Cui <yabinc@google.com> | 2019-07-12 13:09:15 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2019-07-17 11:23:48 -0700 |
commit | d5bccd10425f771b04bab0311826416a9bc2ee54 (patch) | |
tree | d19d50dc27f47dbad799daec66f9574cd8f68063 | |
parent | ecddd834da99bf957972bed4e8d79628c13fe921 (diff) | |
download | extras-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.bp | 1 | ||||
-rw-r--r-- | simpleperf/ETMRecorder.cpp | 164 | ||||
-rw-r--r-- | simpleperf/ETMRecorder.h | 68 | ||||
-rw-r--r-- | simpleperf/cmd_list.cpp | 10 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 8 | ||||
-rw-r--r-- | simpleperf/event_attr.cpp | 6 | ||||
-rw-r--r-- | simpleperf/event_selection_set.cpp | 10 | ||||
-rw-r--r-- | simpleperf/event_type.cpp | 9 |
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; } |