ReadValueInEtmDir(name + "/trcidr/trcidr0", &cpu_info.trcidr0) &&
ReadValueInEtmDir(name + "/trcidr/trcidr1", &cpu_info.trcidr1) &&
ReadValueInEtmDir(name + "/trcidr/trcidr2", &cpu_info.trcidr2) &&
+ ReadValueInEtmDir(name + "/trcidr/trcidr4", &cpu_info.trcidr4) &&
ReadValueInEtmDir(name + "/trcidr/trcidr8", &cpu_info.trcidr8) &&
ReadValueInEtmDir(name + "/mgmt/trcauthstatus", &cpu_info.trcauthstatus);
if (!success) {
return AuxTraceInfoRecord(data, etm4_v);
}
+size_t ETMRecorder::GetAddrFilterPairs() {
+ CHECK(etm_supported_);
+ size_t min_pairs = std::numeric_limits<size_t>::max();
+ for (auto& p : etm_info_) {
+ min_pairs = std::min<size_t>(min_pairs, GetBits(p.second.trcidr4, 0, 3));
+ }
+ if (min_pairs > 0) {
+ --min_pairs; // One pair is used by the kernel to set default addr filter.
+ }
+ return min_pairs;
+}
+
} // namespace simpleperf
\ No newline at end of file
uint32_t trcidr0;
uint32_t trcidr1;
uint32_t trcidr2;
+ uint32_t trcidr4;
uint32_t trcidr8;
uint32_t trcauthstatus;
bool CheckEtmSupport();
void SetEtmPerfEventAttr(perf_event_attr* attr);
AuxTraceInfoRecord CreateAuxTraceInfoRecord();
+ size_t GetAddrFilterPairs();
private:
bool ReadEtmInfo();
"--no-inherit Don't record created child threads/processes.\n"
"--cpu-percent <percent> Set the max percent of cpu time used for recording.\n"
" percent is in range [1-100], default is 25.\n"
+"--include-filter binary1,binary2,...\n"
+" Trace only selected binaries in cs-etm instruction tracing.\n"
+" Each entry is a binary path.\n"
"\n"
"Dwarf unwinding options:\n"
"--post-unwind=(yes|no) If `--call-graph dwarf` option is used, then the user's\n"
}
} else if (args[i] == "--in-app") {
in_app_context_ = true;
+ } else if (args[i] == "--include-filter") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ event_selection_set_.SetIncludeFilters(android::base::Split(args[i], ","));
} else if (args[i] == "-j") {
if (!NextArgumentOrError(args, &i)) {
return false;
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
+#include <android-base/strings.h>
#include <map>
#include <memory>
ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "1024"}));
// not power of two
ASSERT_FALSE(RunRecordCmd({"-e", "cs-etm", "--aux-buffer-size", "12k"}));
+}
+
+TEST(record_cmd, include_filter_option) {
+ TEST_REQUIRE_HW_COUNTER();
+ if (!ETMRecorder::GetInstance().CheckEtmSupport()) {
+ GTEST_LOG_(INFO) << "Omit this test since etm isn't supported on this device";
+ return;
+ }
+ FILE* fp = popen("which sleep", "r");
+ ASSERT_TRUE(fp != nullptr);
+ std::string path;
+ ASSERT_TRUE(android::base::ReadFdToString(fileno(fp), &path));
+ pclose(fp);
+ path = android::base::Trim(path);
+ std::string sleep_exec_path;
+ ASSERT_TRUE(android::base::Realpath(path, &sleep_exec_path));
+ // --include-filter doesn't apply to cpu-cycles.
+ ASSERT_FALSE(RunRecordCmd({"--include-filter", sleep_exec_path}));
+ TemporaryFile record_file;
+ ASSERT_TRUE(
+ RunRecordCmd({"-e", "cs-etm", "--include-filter", sleep_exec_path}, record_file.path));
+ TemporaryFile inject_file;
+ ASSERT_TRUE(
+ CreateCommandInstance("inject")->Run({"-i", record_file.path, "-o", inject_file.path}));
+ std::string data;
+ ASSERT_TRUE(android::base::ReadFileToString(inject_file.path, &data));
+ // Only instructions in sleep_exec_path are traced.
+ for (auto& line : android::base::Split(data, "\n")) {
+ if (android::base::StartsWith(line, "dso ")) {
+ std::string dso = line.substr(strlen("dso "), sleep_exec_path.size());
+ ASSERT_EQ(dso, sleep_exec_path);
+ }
+ }
}
\ No newline at end of file
return true;
}
+bool EventFd::SetFilter(const std::string& filter) {
+ bool success = ioctl(perf_event_fd_, PERF_EVENT_IOC_SET_FILTER, filter.c_str()) >= 0;
+ if (!success) {
+ PLOG(ERROR) << "failed to set filter";
+ }
+ return success;
+}
+
bool EventFd::InnerReadCounter(PerfCounter* counter) const {
CHECK(counter != nullptr);
if (!android::base::ReadFully(perf_event_fd_, counter, sizeof(*counter))) {
// It tells the kernel to start counting and recording events specified by
// this file.
bool SetEnableEvent(bool enable);
+ bool SetFilter(const std::string& filter);
bool ReadCounter(PerfCounter* counter);
}
}
}
- return true;
+ return ApplyFilters();
}
bool EventSelectionSet::IsUserSpaceSamplerGroup(EventSelectionGroup& group) {
return true;
}
+bool EventSelectionSet::ApplyFilters() {
+ if (include_filters_.empty()) {
+ return true;
+ }
+ if (!has_aux_trace_) {
+ LOG(ERROR) << "include filters only take effect in cs-etm instruction tracing";
+ return false;
+ }
+ size_t supported_pairs = ETMRecorder::GetInstance().GetAddrFilterPairs();
+ if (supported_pairs < include_filters_.size()) {
+ LOG(ERROR) << "filter binary count is " << include_filters_.size()
+ << ", bigger than maximum supported filters on device, which is " << supported_pairs;
+ return false;
+ }
+ std::string filter_str;
+ for (auto& binary : include_filters_) {
+ std::string path;
+ if (!android::base::Realpath(binary, &path)) {
+ PLOG(ERROR) << "failed to find include filter binary: " << binary;
+ return false;
+ }
+ uint64_t file_size = GetFileSize(path);
+ if (!filter_str.empty()) {
+ filter_str += ',';
+ }
+ android::base::StringAppendF(&filter_str, "filter 0/%" PRIu64 "@%s", file_size, path.c_str());
+ }
+ for (auto& group : groups_) {
+ for (auto& selection : group) {
+ if (IsEtmEventType(selection.event_type_modifier.event_type.type)) {
+ for (auto& event_fd : selection.event_fds) {
+ if (!event_fd->SetFilter(filter_str)) {
+ return false;
+ }
+ }
+ }
+ }
+ }
+ return true;
+}
+
static bool ReadCounter(EventFd* event_fd, CounterInfo* counter) {
if (!event_fd->ReadCounter(&counter->counter)) {
return false;
bool NeedKernelSymbol() const;
void SetRecordNotExecutableMaps(bool record);
bool RecordNotExecutableMaps() const;
+ void SetIncludeFilters(std::vector<std::string>&& filters) {
+ include_filters_ = std::move(filters);
+ }
void AddMonitoredProcesses(const std::set<pid_t>& processes) {
processes_.insert(processes.begin(), processes.end());
const std::map<pid_t, std::set<pid_t>>& process_map);
bool OpenEventFilesOnGroup(EventSelectionGroup& group, pid_t tid, int cpu,
std::string* failed_event_type);
+ bool ApplyFilters();
bool ReadMmapEventData(bool with_time_limit);
bool DetectCpuHotplugEvents();
std::unique_ptr<simpleperf::RecordReadThread> record_read_thread_;
bool has_aux_trace_ = false;
+ std::vector<std::string> include_filters_;
DISALLOW_COPY_AND_ASSIGN(EventSelectionSet);
};