LOCAL_PATH := $(call my-dir)
simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
+simpleperf_use_bionic_perf_event_h_flag := -DUSE_BIONIC_PERF_EVENT_H -I bionic
+simpleperf_host_common_cppflags := $(simpleperf_common_cppflags) $(simpleperf_use_bionic_perf_event_h_flag)
simpleperf_common_shared_libraries := \
libbase \
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
LOCAL_SRC_FILES := $(libsimpleperf_src_files)
LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
LOCAL_LDLIBS := -lrt
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
-LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
+LOCAL_CPPFLAGS := $(simpleperf_host_common_cppflags)
LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_SHARED_LIBRARIES := $(simpleperf_common_shared_libraries)
#include <poll.h>
#include <signal.h>
#include <string>
+#include <unordered_map>
#include <vector>
#include <base/logging.h>
static std::string default_measured_event_type = "cpu-cycles";
+static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
+ {"u", PERF_SAMPLE_BRANCH_USER},
+ {"k", PERF_SAMPLE_BRANCH_KERNEL},
+ {"any", PERF_SAMPLE_BRANCH_ANY},
+ {"any_call", PERF_SAMPLE_BRANCH_ANY_CALL},
+ {"any_ret", PERF_SAMPLE_BRANCH_ANY_RETURN},
+ {"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
+};
+
class RecordCommandImpl {
public:
RecordCommandImpl()
: use_sample_freq_(true),
sample_freq_(1000),
system_wide_collection_(false),
+ branch_sampling_(0),
measured_event_type_(nullptr),
perf_mmap_pages_(256),
record_filename_("perf.data") {
private:
bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
bool SetMeasuredEventType(const std::string& event_type_name);
- void SetEventSelection();
+ bool SetEventSelection();
bool WriteData(const char* data, size_t size);
bool DumpKernelAndModuleMmaps();
bool DumpThreadCommAndMmaps();
uint64_t sample_period_; // Sample once when 'sample_period_' events occur.
bool system_wide_collection_;
+ uint64_t branch_sampling_;
const EventType* measured_event_type_;
EventSelectionSet event_selection_set_;
return false;
}
}
- SetEventSelection();
+ if (!SetEventSelection()) {
+ return false;
+ }
// 2. Create workload.
if (workload_args.empty()) {
for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
if (args[i] == "-a") {
system_wide_collection_ = true;
+ } else if (args[i] == "-b") {
+ branch_sampling_ = branch_sampling_type_map["any"];
} else if (args[i] == "-c") {
if (!NextArgumentOrError(args, &i)) {
return false;
return false;
}
use_sample_freq_ = true;
+ } else if (args[i] == "-j") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ std::vector<std::string> branch_sampling_types = android::base::Split(args[i], ",");
+ for (auto& type : branch_sampling_types) {
+ auto it = branch_sampling_type_map.find(type);
+ if (it == branch_sampling_type_map.end()) {
+ LOG(ERROR) << "unrecognized branch sampling filter: " << type;
+ return false;
+ }
+ branch_sampling_ |= it->second;
+ }
} else if (args[i] == "-o") {
if (!NextArgumentOrError(args, &i)) {
return false;
return true;
}
-void RecordCommandImpl::SetEventSelection() {
+bool RecordCommandImpl::SetEventSelection() {
event_selection_set_.AddEventType(*measured_event_type_);
if (use_sample_freq_) {
event_selection_set_.SetSampleFreq(sample_freq_);
event_selection_set_.SetSamplePeriod(sample_period_);
}
event_selection_set_.SampleIdAll();
+ if (!event_selection_set_.SetBranchSampling(branch_sampling_)) {
+ return false;
+ }
+ return true;
}
bool RecordCommandImpl::WriteData(const char* data, size_t size) {
" Gather sampling information when running [command]. If [command]\n"
" is not specified, sleep 1 is used instead.\n"
" -a System-wide collection.\n"
+ " -b Enable take branch stack sampling. Same as '-j any'\n"
" -c count Set event sample period.\n"
" -e event Select the event to sample (Use `simpleperf list`)\n"
" to find all possible event names.\n"
" -f freq Set event sample frequency.\n"
" -F freq Same as '-f freq'.\n"
+ " -j branch_filter1,branch_filter2,...\n"
+ " Enable taken branch stack sampling. Each sample\n"
+ " captures a series of consecutive taken branches.\n"
+ " The following filters are defined:\n"
+ " any: any type of branch\n"
+ " any_call: any function call or system call\n"
+ " any_ret: any function return or system call return\n"
+ " ind_call: any indirect branch\n"
+ " u: only when the branch target is at the user level\n"
+ " k: only when the branch target is in the kernel\n"
+ " This option requires at least one branch type among any,\n"
+ " any_call, any_ret, ind_call.\n"
" -o record_file_name Set record file name, default is perf.data.\n") {
}
TEST_F(RecordCommandTest, tracepoint_event) {
ASSERT_TRUE(record_cmd->Run({"record", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
}
+
+TEST_F(RecordCommandTest, branch_sampling) {
+ ASSERT_TRUE(record_cmd->Run({"record", "-a", "-b", "sleep", "1"}));
+ ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,any_call,any_ret,ind_call", "sleep", "1"}));
+ ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,k", "sleep", "1"}));
+ ASSERT_TRUE(record_cmd->Run({"record", "-j", "any,u", "sleep", "1"}));
+ ASSERT_FALSE(record_cmd->Run({"record", "-j", "u", "sleep", "1"}));
+}
}
}
+bool EventSelectionSet::SetBranchSampling(uint64_t branch_sample_type) {
+ if (branch_sample_type != 0 &&
+ (branch_sample_type & (PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
+ PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_CALL)) == 0) {
+ LOG(ERROR) << "Invalid branch_sample_type: 0x" << std::hex << branch_sample_type;
+ return false;
+ }
+ for (auto& selection : selections_) {
+ perf_event_attr& attr = selection.event_attr;
+ if (branch_sample_type != 0) {
+ attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
+ } else {
+ attr.sample_type &= ~PERF_SAMPLE_BRANCH_STACK;
+ }
+ attr.branch_sample_type = branch_sample_type;
+ }
+ return true;
+}
+
bool EventSelectionSet::OpenEventFilesForAllCpus() {
std::vector<int> cpus = GetOnlineCpus();
if (cpus.empty()) {
// As the online cpus can be enabled or disabled at runtime, we may not open event file for
// all cpus successfully. But we should open at least one cpu successfully.
if (selection.event_fds.empty()) {
- LOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
- << " on all cpus";
+ PLOG(ERROR) << "failed to open perf event file for event_type " << selection.event_type->name
+ << " on all cpus";
return false;
}
}
void SampleIdAll();
void SetSampleFreq(uint64_t sample_freq);
void SetSamplePeriod(uint64_t sample_period);
+ bool SetBranchSampling(uint64_t branch_sample_type);
bool OpenEventFilesForAllCpus();
bool OpenEventFilesForProcess(pid_t pid);
#ifndef SIMPLE_PERF_PERF_EVENT_H_
#define SIMPLE_PERF_PERF_EVENT_H_
+#if defined(USE_BIONIC_PERF_EVENT_H)
+
+#include <libc/kernel/uapi/linux/perf_event.h>
+
+#else
+
#include <linux/perf_event.h>
+#endif
+
#endif // SIMPLE_PERF_PERF_EVENT_H_
}
template <class T>
+void MoveFromBinaryFormat(T* data_p, size_t n, const char*& p) {
+ size_t size = n * sizeof(T);
+ memcpy(data_p, p, size);
+ p += size;
+}
+
+template <class T>
void MoveToBinaryFormat(const T& data, char*& p) {
*reinterpret_cast<T*>(p) = data;
p += sizeof(T);
if (sample_type & PERF_SAMPLE_PERIOD) {
MoveFromBinaryFormat(period_data, p);
}
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+ uint64_t nr;
+ MoveFromBinaryFormat(nr, p);
+ branch_stack_data.stack.resize(nr);
+ MoveFromBinaryFormat(branch_stack_data.stack.data(), nr, p);
+ }
// TODO: Add parsing of other PERF_SAMPLE_*.
CHECK_LE(p, end);
if (p < end) {
if (sample_type & PERF_SAMPLE_PERIOD) {
PrintIndented(indent, "period %" PRId64 "\n", period_data.period);
}
+ if (sample_type & PERF_SAMPLE_BRANCH_STACK) {
+ PrintIndented(indent, "branch_stack nr=%" PRIu64 "\n", branch_stack_data.stack.size());
+ for (auto& item : branch_stack_data.stack) {
+ PrintIndented(indent + 1, "from 0x%" PRIx64 ", to 0x%" PRIx64 ", flags 0x%" PRIx64 "\n",
+ item.from, item.to, item.flags);
+ }
+ }
}
BuildIdRecord::BuildIdRecord(const perf_event_header* pheader) : Record(pheader) {
uint64_t period;
};
+struct PerfSampleBranchStackType {
+ struct BranchStackItemType {
+ uint64_t from;
+ uint64_t to;
+ uint64_t flags;
+ };
+ std::vector<BranchStackItemType> stack;
+};
+
// SampleId is optional at the end of a record in binary format. Its content is determined by
// sample_id_all and sample_type in perf_event_attr. To avoid the complexity of referring to
// perf_event_attr each time, we copy sample_id_all and sample_type inside the SampleId structure.
PerfSampleCpuType cpu_data; // Valid if PERF_SAMPLE_CPU.
PerfSamplePeriodType period_data; // Valid if PERF_SAMPLE_PERIOD.
+ PerfSampleBranchStackType branch_stack_data; // Valid if PERF_SAMPLE_BRANCH_STACK.
+
SampleRecord(const perf_event_attr& attr, const perf_event_header* pheader);
protected: