summaryrefslogtreecommitdiff
path: root/simpleperf
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-06-01 11:21:37 -0700
committerYabin Cui <yabinc@google.com>2015-06-04 15:26:32 -0700
commitf79f07e13c56f7ca3be1435cea7f8861daf7efaa (patch)
tree8c76bac6fe4d9b52b69e57393f5deecb18febd97 /simpleperf
parentd4637d6e7d17f48d9325fa133be82b06a408f523 (diff)
downloadextras-f79f07e13c56f7ca3be1435cea7f8861daf7efaa.tar.gz
Simpleperf: refactor command system.
Register a callback function to create a new command instance instead of registering a command instance. Then we can release resources in the command destructors, and don't need xxxCommandImpl classes any more. Bug: 19483574 Change-Id: Ibb54892ec0655fd43909347afd72bb08bc8a716c
Diffstat (limited to 'simpleperf')
-rw-r--r--simpleperf/cmd_dumprecord.cpp45
-rw-r--r--simpleperf/cmd_dumprecord_test.cpp16
-rw-r--r--simpleperf/cmd_help.cpp13
-rw-r--r--simpleperf/cmd_list.cpp14
-rw-r--r--simpleperf/cmd_list_test.cpp10
-rw-r--r--simpleperf/cmd_record.cpp99
-rw-r--r--simpleperf/cmd_record_test.cpp62
-rw-r--r--simpleperf/cmd_stat.cpp60
-rw-r--r--simpleperf/cmd_stat_test.cpp14
-rw-r--r--simpleperf/command.cpp61
-rw-r--r--simpleperf/command.h18
-rw-r--r--simpleperf/command_test.cpp26
-rw-r--r--simpleperf/main.cpp3
-rw-r--r--simpleperf/utils.cpp10
-rw-r--r--simpleperf/utils.h2
-rw-r--r--simpleperf/workload.cpp2
16 files changed, 214 insertions, 241 deletions
diff --git a/simpleperf/cmd_dumprecord.cpp b/simpleperf/cmd_dumprecord.cpp
index 57eec1f6..e4594b6a 100644
--- a/simpleperf/cmd_dumprecord.cpp
+++ b/simpleperf/cmd_dumprecord.cpp
@@ -30,9 +30,13 @@
using namespace PerfFileFormat;
-class DumpRecordCommandImpl {
+class DumpRecordCommand : public Command {
public:
- DumpRecordCommandImpl() : record_filename_("perf.data") {
+ DumpRecordCommand()
+ : Command("dump", "dump perf record file",
+ "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
+ " Dump different parts of a perf record file. Default file is perf.data.\n"),
+ record_filename_("perf.data") {
}
bool Run(const std::vector<std::string>& args);
@@ -50,7 +54,7 @@ class DumpRecordCommandImpl {
std::vector<int> features_;
};
-bool DumpRecordCommandImpl::Run(const std::vector<std::string>& args) {
+bool DumpRecordCommand::Run(const std::vector<std::string>& args) {
if (!ParseOptions(args)) {
return false;
}
@@ -66,16 +70,19 @@ bool DumpRecordCommandImpl::Run(const std::vector<std::string>& args) {
return true;
}
-bool DumpRecordCommandImpl::ParseOptions(const std::vector<std::string>& args) {
- if (args.size() == 2) {
- record_filename_ = args[1];
+bool DumpRecordCommand::ParseOptions(const std::vector<std::string>& args) {
+ if (args.size() == 1) {
+ record_filename_ = args[0];
+ } else if (args.size() > 1) {
+ ReportUnknownOption(args, 1);
+ return false;
}
return true;
}
static const std::string GetFeatureName(int feature);
-void DumpRecordCommandImpl::DumpFileHeader() {
+void DumpRecordCommand::DumpFileHeader() {
const FileHeader* header = record_file_reader_->FileHeader();
printf("magic: ");
for (size_t i = 0; i < 8; ++i) {
@@ -138,7 +145,7 @@ static const std::string GetFeatureName(int feature) {
return android::base::StringPrintf("unknown_feature(%d)", feature);
}
-void DumpRecordCommandImpl::DumpAttrSection() {
+void DumpRecordCommand::DumpAttrSection() {
std::vector<const FileAttr*> attrs = record_file_reader_->AttrSection();
for (size_t i = 0; i < attrs.size(); ++i) {
auto& attr = attrs[i];
@@ -157,14 +164,14 @@ void DumpRecordCommandImpl::DumpAttrSection() {
}
}
-void DumpRecordCommandImpl::DumpDataSection() {
+void DumpRecordCommand::DumpDataSection() {
std::vector<std::unique_ptr<const Record>> records = record_file_reader_->DataSection();
for (auto& record : records) {
record->Dump();
}
}
-void DumpRecordCommandImpl::DumpFeatureSection() {
+void DumpRecordCommand::DumpFeatureSection() {
std::vector<SectionDesc> sections = record_file_reader_->FeatureSectionDescriptors();
CHECK_EQ(sections.size(), features_.size());
for (size_t i = 0; i < features_.size(); ++i) {
@@ -187,18 +194,6 @@ void DumpRecordCommandImpl::DumpFeatureSection() {
}
}
-class DumpRecordCommand : public Command {
- public:
- DumpRecordCommand()
- : Command("dump", "dump perf record file",
- "Usage: simpleperf dumprecord [options] [perf_record_file]\n"
- " Dump different parts of a perf record file. Default file is perf.data.\n") {
- }
-
- bool Run(const std::vector<std::string>& args) override {
- DumpRecordCommandImpl impl;
- return impl.Run(args);
- }
-};
-
-DumpRecordCommand dumprecord_cmd;
+__attribute__((constructor)) static void RegisterDumpRecordCommand() {
+ RegisterCommand("dump", [] { return std::unique_ptr<Command>(new DumpRecordCommand); });
+}
diff --git a/simpleperf/cmd_dumprecord_test.cpp b/simpleperf/cmd_dumprecord_test.cpp
index c4708330..f23ae166 100644
--- a/simpleperf/cmd_dumprecord_test.cpp
+++ b/simpleperf/cmd_dumprecord_test.cpp
@@ -21,22 +21,22 @@
class DumpRecordCommandTest : public ::testing::Test {
protected:
virtual void SetUp() {
- record_cmd = Command::FindCommandByName("record");
+ record_cmd = CreateCommandInstance("record");
ASSERT_TRUE(record_cmd != nullptr);
- dumprecord_cmd = Command::FindCommandByName("dump");
+ dumprecord_cmd = CreateCommandInstance("dump");
ASSERT_TRUE(dumprecord_cmd != nullptr);
}
- Command* record_cmd;
- Command* dumprecord_cmd;
+ std::unique_ptr<Command> record_cmd;
+ std::unique_ptr<Command> dumprecord_cmd;
};
TEST_F(DumpRecordCommandTest, no_options) {
- ASSERT_TRUE(record_cmd->Run({"record", "-a", "sleep", "1"}));
- ASSERT_TRUE(dumprecord_cmd->Run({"dump"}));
+ ASSERT_TRUE(record_cmd->Run({"-a", "sleep", "1"}));
+ ASSERT_TRUE(dumprecord_cmd->Run({}));
}
TEST_F(DumpRecordCommandTest, record_file_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-a", "-o", "perf2.data", "sleep", "1"}));
- ASSERT_TRUE(dumprecord_cmd->Run({"dump", "perf2.data"}));
+ ASSERT_TRUE(record_cmd->Run({"-a", "-o", "perf2.data", "sleep", "1"}));
+ ASSERT_TRUE(dumprecord_cmd->Run({"perf2.data"}));
}
diff --git a/simpleperf/cmd_help.cpp b/simpleperf/cmd_help.cpp
index 0f3839b3..75df732a 100644
--- a/simpleperf/cmd_help.cpp
+++ b/simpleperf/cmd_help.cpp
@@ -39,10 +39,10 @@ class HelpCommand : public Command {
};
bool HelpCommand::Run(const std::vector<std::string>& args) {
- if (args.size() == 1) {
+ if (args.empty()) {
PrintShortHelp();
} else {
- Command* cmd = Command::FindCommandByName(args[1]);
+ std::unique_ptr<Command> cmd = CreateCommandInstance(args[0]);
if (cmd == nullptr) {
LOG(ERROR) << "malformed command line: can't find help string for unknown command " << args[0];
LOG(ERROR) << "try using \"--help\"";
@@ -56,8 +56,9 @@ bool HelpCommand::Run(const std::vector<std::string>& args) {
void HelpCommand::PrintShortHelp() {
printf("Usage: simpleperf [--help] subcommand [args_for_subcommand]\n\n");
- for (auto& command : Command::GetAllCommands()) {
- printf("%-20s%s\n", command->Name().c_str(), command->ShortHelpString().c_str());
+ for (auto& cmd_name : GetAllCommandNames()) {
+ std::unique_ptr<Command> cmd = CreateCommandInstance(cmd_name);
+ printf("%-20s%s\n", cmd_name.c_str(), cmd->ShortHelpString().c_str());
}
}
@@ -65,4 +66,6 @@ void HelpCommand::PrintLongHelpForOneCommand(const Command& command) {
printf("%s\n", command.LongHelpString().c_str());
}
-HelpCommand help_command;
+__attribute__((constructor)) static void RegisterHelpCommand() {
+ RegisterCommand("help", [] { return std::unique_ptr<Command>(new HelpCommand); });
+}
diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp
index 18c3972b..338ae266 100644
--- a/simpleperf/cmd_list.cpp
+++ b/simpleperf/cmd_list.cpp
@@ -56,16 +56,16 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
};
std::vector<std::string> names;
- if (args.size() == 1) {
+ if (args.empty()) {
for (auto& item : type_map) {
names.push_back(item.first);
}
} else {
- for (size_t i = 1; i < args.size(); ++i) {
- if (type_map.find(args[i]) != type_map.end()) {
- names.push_back(args[i]);
+ for (auto& arg : args) {
+ if (type_map.find(arg) != type_map.end()) {
+ names.push_back(arg);
} else {
- LOG(ERROR) << "unknown event type category: " << args[i] << ", try using \"help list\"";
+ LOG(ERROR) << "unknown event type category: " << arg << ", try using \"help list\"";
return false;
}
}
@@ -80,4 +80,6 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
return true;
}
-ListCommand list_command;
+__attribute__((constructor)) static void RegisterListCommand() {
+ RegisterCommand("list", [] { return std::unique_ptr<Command>(new ListCommand); });
+}
diff --git a/simpleperf/cmd_list_test.cpp b/simpleperf/cmd_list_test.cpp
index ddc82ca7..2bc64214 100644
--- a/simpleperf/cmd_list_test.cpp
+++ b/simpleperf/cmd_list_test.cpp
@@ -21,21 +21,21 @@
class ListCommandTest : public ::testing::Test {
protected:
virtual void SetUp() {
- list_cmd = Command::FindCommandByName("list");
+ list_cmd = CreateCommandInstance("list");
ASSERT_TRUE(list_cmd != nullptr);
}
- Command* list_cmd;
+ std::unique_ptr<Command> list_cmd;
};
TEST_F(ListCommandTest, no_options) {
- ASSERT_TRUE(list_cmd->Run({"list"}));
+ ASSERT_TRUE(list_cmd->Run({}));
}
TEST_F(ListCommandTest, one_option) {
- ASSERT_TRUE(list_cmd->Run({"list", "sw"}));
+ ASSERT_TRUE(list_cmd->Run({"sw"}));
}
TEST_F(ListCommandTest, multiple_options) {
- ASSERT_TRUE(list_cmd->Run({"list", "hw", "tracepoint"}));
+ ASSERT_TRUE(list_cmd->Run({"hw", "tracepoint"}));
}
diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp
index 94cf052a..83fa593a 100644
--- a/simpleperf/cmd_record.cpp
+++ b/simpleperf/cmd_record.cpp
@@ -31,7 +31,6 @@
#include "read_elf.h"
#include "record.h"
#include "record_file.h"
-#include "utils.h"
#include "workload.h"
static std::string default_measured_event_type = "cpu-cycles";
@@ -45,10 +44,34 @@ static std::unordered_map<std::string, uint64_t> branch_sampling_type_map = {
{"ind_call", PERF_SAMPLE_BRANCH_IND_CALL},
};
-class RecordCommandImpl {
+class RecordCommand : public Command {
public:
- RecordCommandImpl()
- : use_sample_freq_(true),
+ RecordCommand()
+ : Command("record", "record sampling info in perf.data",
+ "Usage: simpleperf record [options] [command [command-args]]\n"
+ " 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"),
+ use_sample_freq_(true),
sample_freq_(1000),
system_wide_collection_(false),
branch_sampling_(0),
@@ -59,7 +82,7 @@ class RecordCommandImpl {
saved_sigchild_handler_ = signal(SIGCHLD, [](int) {});
}
- ~RecordCommandImpl() {
+ ~RecordCommand() {
signal(SIGCHLD, saved_sigchild_handler_);
}
@@ -95,7 +118,7 @@ class RecordCommandImpl {
sighandler_t saved_sigchild_handler_;
};
-bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
+bool RecordCommand::Run(const std::vector<std::string>& args) {
// 1. Parse options, and use default measured event type if not given.
std::vector<std::string> workload_args;
if (!ParseOptions(args, &workload_args)) {
@@ -165,7 +188,7 @@ bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
return false;
}
auto callback =
- std::bind(&RecordCommandImpl::WriteData, this, std::placeholders::_1, std::placeholders::_2);
+ std::bind(&RecordCommand::WriteData, this, std::placeholders::_1, std::placeholders::_2);
while (true) {
if (!event_selection_set_.ReadMmapEventData(callback)) {
return false;
@@ -186,10 +209,10 @@ bool RecordCommandImpl::Run(const std::vector<std::string>& args) {
return true;
}
-bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
- std::vector<std::string>* non_option_args) {
+bool RecordCommand::ParseOptions(const std::vector<std::string>& args,
+ std::vector<std::string>* non_option_args) {
size_t i;
- for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+ for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
if (args[i] == "-a") {
system_wide_collection_ = true;
} else if (args[i] == "-b") {
@@ -242,8 +265,7 @@ bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
}
record_filename_ = args[i];
} else {
- LOG(ERROR) << "Unknown option for record command: '" << args[i] << "'\n";
- LOG(ERROR) << "Try `simpleperf help record`";
+ ReportUnknownOption(args, i);
return false;
}
}
@@ -257,7 +279,7 @@ bool RecordCommandImpl::ParseOptions(const std::vector<std::string>& args,
return true;
}
-bool RecordCommandImpl::SetMeasuredEventType(const std::string& event_type_name) {
+bool RecordCommand::SetMeasuredEventType(const std::string& event_type_name) {
const EventType* event_type = EventTypeFactory::FindEventTypeByName(event_type_name);
if (event_type == nullptr) {
return false;
@@ -266,7 +288,7 @@ bool RecordCommandImpl::SetMeasuredEventType(const std::string& event_type_name)
return true;
}
-bool RecordCommandImpl::SetEventSelection() {
+bool RecordCommand::SetEventSelection() {
event_selection_set_.AddEventType(*measured_event_type_);
if (use_sample_freq_) {
event_selection_set_.SetSampleFreq(sample_freq_);
@@ -280,11 +302,11 @@ bool RecordCommandImpl::SetEventSelection() {
return true;
}
-bool RecordCommandImpl::WriteData(const char* data, size_t size) {
+bool RecordCommand::WriteData(const char* data, size_t size) {
return record_file_writer_->WriteData(data, size);
}
-bool RecordCommandImpl::DumpKernelAndModuleMmaps() {
+bool RecordCommand::DumpKernelAndModuleMmaps() {
KernelMmap kernel_mmap;
std::vector<ModuleMmap> module_mmaps;
if (!GetKernelAndModuleMmaps(&kernel_mmap, &module_mmaps)) {
@@ -310,7 +332,7 @@ bool RecordCommandImpl::DumpKernelAndModuleMmaps() {
return true;
}
-bool RecordCommandImpl::DumpThreadCommAndMmaps() {
+bool RecordCommand::DumpThreadCommAndMmaps() {
std::vector<ThreadComm> thread_comms;
if (!GetThreadComms(&thread_comms)) {
return false;
@@ -343,14 +365,14 @@ bool RecordCommandImpl::DumpThreadCommAndMmaps() {
return true;
}
-bool RecordCommandImpl::DumpAdditionalFeatures() {
+bool RecordCommand::DumpAdditionalFeatures() {
if (!record_file_writer_->WriteFeatureHeader(1)) {
return false;
}
return DumpBuildIdFeature();
}
-bool RecordCommandImpl::DumpBuildIdFeature() {
+bool RecordCommand::DumpBuildIdFeature() {
std::vector<std::string> hit_kernel_modules;
std::vector<std::string> hit_user_files;
if (!record_file_writer_->GetHitModules(&hit_kernel_modules, &hit_user_files)) {
@@ -396,39 +418,6 @@ bool RecordCommandImpl::DumpBuildIdFeature() {
return true;
}
-class RecordCommand : public Command {
- public:
- RecordCommand()
- : Command("record", "record sampling info in perf.data",
- "Usage: simpleperf record [options] [command [command-args]]\n"
- " 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") {
- }
-
- bool Run(const std::vector<std::string>& args) override {
- RecordCommandImpl impl;
- return impl.Run(args);
- }
-};
-
-RecordCommand record_command;
+__attribute__((constructor)) static void RegisterRecordCommand() {
+ RegisterCommand("record", [] { return std::unique_ptr<Command>(new RecordCommand()); });
+}
diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp
index f92f027c..eddccfcc 100644
--- a/simpleperf/cmd_record_test.cpp
+++ b/simpleperf/cmd_record_test.cpp
@@ -23,43 +23,37 @@
using namespace PerfFileFormat;
-class RecordCommandTest : public ::testing::Test {
- protected:
- virtual void SetUp() {
- record_cmd = Command::FindCommandByName("record");
- ASSERT_TRUE(record_cmd != nullptr);
- }
-
- Command* record_cmd;
-};
+static std::unique_ptr<Command> RecordCmd() {
+ return CreateCommandInstance("record");
+}
-TEST_F(RecordCommandTest, no_options) {
- ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+TEST(record_cmd, no_options) {
+ ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
}
-TEST_F(RecordCommandTest, system_wide_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-a", "sleep", "1"}));
+TEST(record_cmd, system_wide_option) {
+ ASSERT_TRUE(RecordCmd()->Run({"-a", "sleep", "1"}));
}
-TEST_F(RecordCommandTest, sample_period_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-c", "100000", "sleep", "1"}));
+TEST(record_cmd, sample_period_option) {
+ ASSERT_TRUE(RecordCmd()->Run({"-c", "100000", "sleep", "1"}));
}
-TEST_F(RecordCommandTest, event_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-e", "cpu-clock", "sleep", "1"}));
+TEST(record_cmd, event_option) {
+ ASSERT_TRUE(RecordCmd()->Run({"-e", "cpu-clock", "sleep", "1"}));
}
-TEST_F(RecordCommandTest, freq_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-f", "99", "sleep", "1"}));
- ASSERT_TRUE(record_cmd->Run({"record", "-F", "99", "sleep", "1"}));
+TEST(record_cmd, freq_option) {
+ ASSERT_TRUE(RecordCmd()->Run({"-f", "99", "sleep", "1"}));
+ ASSERT_TRUE(RecordCmd()->Run({"-F", "99", "sleep", "1"}));
}
-TEST_F(RecordCommandTest, output_file_option) {
- ASSERT_TRUE(record_cmd->Run({"record", "-o", "perf2.data", "sleep", "1"}));
+TEST(record_cmd, output_file_option) {
+ ASSERT_TRUE(RecordCmd()->Run({"-o", "perf2.data", "sleep", "1"}));
}
-TEST_F(RecordCommandTest, dump_kernel_mmap) {
- ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+TEST(record_cmd, dump_kernel_mmap) {
+ ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
ASSERT_TRUE(reader != nullptr);
std::vector<std::unique_ptr<const Record>> records = reader->DataSection();
@@ -77,8 +71,8 @@ TEST_F(RecordCommandTest, dump_kernel_mmap) {
ASSERT_TRUE(have_kernel_mmap);
}
-TEST_F(RecordCommandTest, dump_build_id_feature) {
- ASSERT_TRUE(record_cmd->Run({"record", "sleep", "1"}));
+TEST(record_cmd, dump_build_id_feature) {
+ ASSERT_TRUE(RecordCmd()->Run({"sleep", "1"}));
std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance("perf.data");
ASSERT_TRUE(reader != nullptr);
const FileHeader* file_header = reader->FileHeader();
@@ -87,14 +81,14 @@ TEST_F(RecordCommandTest, dump_build_id_feature) {
ASSERT_GT(reader->FeatureSectionDescriptors().size(), 0u);
}
-TEST_F(RecordCommandTest, tracepoint_event) {
- ASSERT_TRUE(record_cmd->Run({"record", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
+TEST(record_cmd, tracepoint_event) {
+ ASSERT_TRUE(RecordCmd()->Run({"-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"}));
+TEST(record_cmd, branch_sampling) {
+ ASSERT_TRUE(RecordCmd()->Run({"-a", "-b", "sleep", "1"}));
+ ASSERT_TRUE(RecordCmd()->Run({"-j", "any,any_call,any_ret,ind_call", "sleep", "1"}));
+ ASSERT_TRUE(RecordCmd()->Run({"-j", "any,k", "sleep", "1"}));
+ ASSERT_TRUE(RecordCmd()->Run({"-j", "any,u", "sleep", "1"}));
+ ASSERT_FALSE(RecordCmd()->Run({"-j", "u", "sleep", "1"}));
}
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 16af81e6..de3b032a 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -28,7 +28,6 @@
#include "event_selection_set.h"
#include "event_type.h"
#include "perf_event.h"
-#include "utils.h"
#include "workload.h"
static std::vector<std::string> default_measured_event_types{
@@ -37,9 +36,19 @@ static std::vector<std::string> default_measured_event_types{
"task-clock", "context-switches", "page-faults",
};
-class StatCommandImpl {
+class StatCommand : public Command {
public:
- StatCommandImpl() : verbose_mode_(false), system_wide_collection_(false) {
+ StatCommand()
+ : Command("stat", "gather performance counter information",
+ "Usage: simpleperf stat [options] [command [command-args]]\n"
+ " Gather performance counter information of running [command]. If [command]\n"
+ " is not specified, sleep 1 is used instead.\n\n"
+ " -a Collect system-wide information.\n"
+ " -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
+ " to find all possible event names.\n"
+ " --verbose Show result in verbose mode.\n"),
+ verbose_mode_(false),
+ system_wide_collection_(false) {
}
bool Run(const std::vector<std::string>& args);
@@ -56,7 +65,7 @@ class StatCommandImpl {
bool system_wide_collection_;
};
-bool StatCommandImpl::Run(const std::vector<std::string>& args) {
+bool StatCommand::Run(const std::vector<std::string>& args) {
// 1. Parse options.
std::vector<std::string> workload_args;
if (!ParseOptions(args, &workload_args)) {
@@ -118,10 +127,10 @@ bool StatCommandImpl::Run(const std::vector<std::string>& args) {
return true;
}
-bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
- std::vector<std::string>* non_option_args) {
+bool StatCommand::ParseOptions(const std::vector<std::string>& args,
+ std::vector<std::string>* non_option_args) {
size_t i;
- for (i = 1; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
+ for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
if (args[i] == "-a") {
system_wide_collection_ = true;
} else if (args[i] == "-e") {
@@ -137,8 +146,7 @@ bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
} else if (args[i] == "--verbose") {
verbose_mode_ = true;
} else {
- LOG(ERROR) << "Unknown option for stat command: " << args[i];
- LOG(ERROR) << "Try `simpleperf help stat`";
+ ReportUnknownOption(args, i);
return false;
}
}
@@ -152,8 +160,8 @@ bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
return true;
}
-bool StatCommandImpl::AddMeasuredEventType(const std::string& event_type_name,
- bool report_unsupported_type) {
+bool StatCommand::AddMeasuredEventType(const std::string& event_type_name,
+ bool report_unsupported_type) {
const EventType* event_type =
EventTypeFactory::FindEventTypeByName(event_type_name, report_unsupported_type);
if (event_type == nullptr) {
@@ -163,7 +171,7 @@ bool StatCommandImpl::AddMeasuredEventType(const std::string& event_type_name,
return true;
}
-bool StatCommandImpl::AddDefaultMeasuredEventTypes() {
+bool StatCommand::AddDefaultMeasuredEventTypes() {
for (auto& name : default_measured_event_types) {
// It is not an error when some event types in the default list are not supported by the kernel.
AddMeasuredEventType(name, false);
@@ -175,7 +183,7 @@ bool StatCommandImpl::AddDefaultMeasuredEventTypes() {
return true;
}
-bool StatCommandImpl::ShowCounters(
+bool StatCommand::ShowCounters(
const std::map<const EventType*, std::vector<PerfCounter>>& counters_map,
std::chrono::steady_clock::duration counting_duration) {
printf("Performance counter statistics:\n\n");
@@ -218,26 +226,6 @@ bool StatCommandImpl::ShowCounters(
return true;
}
-class StatCommand : public Command {
- public:
- StatCommand()
- : Command("stat", "gather performance counter information",
- "Usage: simpleperf stat [options] [command [command-args]]\n"
- " Gather performance counter information of running [command]. If [command]\n"
- " is not specified, sleep 1 is used instead.\n\n"
- " -a Collect system-wide information.\n"
- " -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
- " to find all possible event names.\n"
- " --verbose Show result in verbose mode.\n") {
- }
-
- bool Run(const std::vector<std::string>& args) override {
- // Keep the implementation in StatCommandImpl, so the resources used are cleaned up when the
- // command finishes. This is useful when we need to call some commands multiple times, like
- // in unit tests.
- StatCommandImpl impl;
- return impl.Run(args);
- }
-};
-
-StatCommand stat_command;
+__attribute__((constructor)) static void RegisterStatCommand() {
+ RegisterCommand("stat", [] { return std::unique_ptr<Command>(new StatCommand); });
+}
diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp
index 0ed49bcb..f2639797 100644
--- a/simpleperf/cmd_stat_test.cpp
+++ b/simpleperf/cmd_stat_test.cpp
@@ -21,30 +21,30 @@
class StatCommandTest : public ::testing::Test {
protected:
virtual void SetUp() {
- stat_cmd = Command::FindCommandByName("stat");
+ stat_cmd = CreateCommandInstance("stat");
ASSERT_TRUE(stat_cmd != nullptr);
}
protected:
- Command* stat_cmd;
+ std::unique_ptr<Command> stat_cmd;
};
TEST_F(StatCommandTest, no_options) {
- ASSERT_TRUE(stat_cmd->Run({"stat", "sleep", "1"}));
+ ASSERT_TRUE(stat_cmd->Run({"sleep", "1"}));
}
TEST_F(StatCommandTest, event_option) {
- ASSERT_TRUE(stat_cmd->Run({"stat", "-e", "cpu-clock,task-clock", "sleep", "1"}));
+ ASSERT_TRUE(stat_cmd->Run({"-e", "cpu-clock,task-clock", "sleep", "1"}));
}
TEST_F(StatCommandTest, system_wide_option) {
- ASSERT_TRUE(stat_cmd->Run({"stat", "-a", "sleep", "1"}));
+ ASSERT_TRUE(stat_cmd->Run({"-a", "sleep", "1"}));
}
TEST_F(StatCommandTest, verbose_option) {
- ASSERT_TRUE(stat_cmd->Run({"stat", "--verbose", "sleep", "1"}));
+ ASSERT_TRUE(stat_cmd->Run({"--verbose", "sleep", "1"}));
}
TEST_F(StatCommandTest, tracepoint_event) {
- ASSERT_TRUE(stat_cmd->Run({"stat", "-a", "-e", "sched:sched_switch", "sleep", "1"}));
+ ASSERT_TRUE(stat_cmd->Run({"-a", "-e", "sched:sched_switch", "sleep", "1"}));
}
diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp
index 79cbc446..8889d7f4 100644
--- a/simpleperf/command.cpp
+++ b/simpleperf/command.cpp
@@ -17,43 +17,54 @@
#include "command.h"
#include <algorithm>
+#include <map>
#include <string>
#include <vector>
-static std::vector<Command*>& Commands() {
- // commands is used in the constructor of Command. Defining it as a static
- // variable in a function makes sure it is initialized before use.
- static std::vector<Command*> commands;
- return commands;
-}
+#include <base/logging.h>
-Command* Command::FindCommandByName(const std::string& cmd_name) {
- for (auto& command : Commands()) {
- if (command->Name() == cmd_name) {
- return command;
- }
+bool Command::NextArgumentOrError(const std::vector<std::string>& args, size_t* pi) {
+ if (*pi + 1 == args.size()) {
+ LOG(ERROR) << "No argument following " << args[*pi] << " option. Try `simpleperf help " << name_
+ << "`";
+ return false;
}
- return nullptr;
+ ++*pi;
+ return true;
+}
+
+void Command::ReportUnknownOption(const std::vector<std::string>& args, size_t i) {
+ LOG(ERROR) << "Unknown option for " << name_ << " command: '" << args[i]
+ << "'. Try `simpleperf help " << name_ << "`";
+}
+
+typedef std::function<std::unique_ptr<Command>(void)> callback_t;
+
+static std::map<std::string, callback_t>& CommandMap() {
+ // commands is used in the constructor of Command. Defining it as a static
+ // variable in a function makes sure it is initialized before use.
+ static std::map<std::string, callback_t> command_map;
+ return command_map;
}
-static bool CompareCommandByName(Command* cmd1, Command* cmd2) {
- return cmd1->Name() < cmd2->Name();
+void RegisterCommand(const std::string& cmd_name,
+ std::function<std::unique_ptr<Command>(void)> callback) {
+ CommandMap().insert(std::make_pair(cmd_name, callback));
}
-const std::vector<Command*>& Command::GetAllCommands() {
- std::sort(Commands().begin(), Commands().end(), CompareCommandByName);
- return Commands();
+void UnRegisterCommand(const std::string& cmd_name) {
+ CommandMap().erase(cmd_name);
}
-void Command::RegisterCommand(Command* cmd) {
- Commands().push_back(cmd);
+std::unique_ptr<Command> CreateCommandInstance(const std::string& cmd_name) {
+ auto it = CommandMap().find(cmd_name);
+ return (it == CommandMap().end()) ? nullptr : (it->second)();
}
-void Command::UnRegisterCommand(Command* cmd) {
- for (auto it = Commands().begin(); it != Commands().end(); ++it) {
- if (*it == cmd) {
- Commands().erase(it);
- break;
- }
+const std::vector<std::string> GetAllCommandNames() {
+ std::vector<std::string> names;
+ for (auto pair : CommandMap()) {
+ names.push_back(pair.first);
}
+ return names;
}
diff --git a/simpleperf/command.h b/simpleperf/command.h
index 46b49cbe..e2c84536 100644
--- a/simpleperf/command.h
+++ b/simpleperf/command.h
@@ -17,6 +17,8 @@
#ifndef SIMPLE_PERF_COMMAND_H_
#define SIMPLE_PERF_COMMAND_H_
+#include <functional>
+#include <memory>
#include <string>
#include <vector>
@@ -27,11 +29,9 @@ class Command {
Command(const std::string& name, const std::string& short_help_string,
const std::string& long_help_string)
: name_(name), short_help_string_(short_help_string), long_help_string_(long_help_string) {
- RegisterCommand(this);
}
virtual ~Command() {
- UnRegisterCommand(this);
}
const std::string& Name() const {
@@ -48,18 +48,22 @@ class Command {
virtual bool Run(const std::vector<std::string>& args) = 0;
- static Command* FindCommandByName(const std::string& cmd_name);
- static const std::vector<Command*>& GetAllCommands();
+ protected:
+ bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi);
+ void ReportUnknownOption(const std::vector<std::string>& args, size_t i);
private:
const std::string name_;
const std::string short_help_string_;
const std::string long_help_string_;
- static void RegisterCommand(Command* cmd);
- static void UnRegisterCommand(Command* cmd);
-
DISALLOW_COPY_AND_ASSIGN(Command);
};
+void RegisterCommand(const std::string& cmd_name,
+ std::function<std::unique_ptr<Command>(void)> callback);
+void UnRegisterCommand(const std::string& cmd_name);
+std::unique_ptr<Command> CreateCommandInstance(const std::string& cmd_name);
+const std::vector<std::string> GetAllCommandNames();
+
#endif // SIMPLE_PERF_COMMAND_H_
diff --git a/simpleperf/command_test.cpp b/simpleperf/command_test.cpp
index 4a0baa6b..18cb5693 100644
--- a/simpleperf/command_test.cpp
+++ b/simpleperf/command_test.cpp
@@ -20,7 +20,7 @@
class MockCommand : public Command {
public:
- MockCommand(const std::string& name) : Command(name, name + "_short_help", name + "_long_help") {
+ MockCommand() : Command("mock", "mock_short_help", "mock_long_help") {
}
bool Run(const std::vector<std::string>&) override {
@@ -28,20 +28,18 @@ class MockCommand : public Command {
}
};
-TEST(command, FindCommandByName) {
- ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
- {
- MockCommand mock1("mock1");
- ASSERT_EQ(Command::FindCommandByName("mock1"), &mock1);
- }
- ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
+TEST(command, CreateCommandInstance) {
+ ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr);
+ RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); });
+ ASSERT_TRUE(CreateCommandInstance("mock1") != nullptr);
+ UnRegisterCommand("mock1");
+ ASSERT_TRUE(CreateCommandInstance("mock1") == nullptr);
}
TEST(command, GetAllCommands) {
- size_t command_count = Command::GetAllCommands().size();
- {
- MockCommand mock1("mock1");
- ASSERT_EQ(command_count + 1, Command::GetAllCommands().size());
- }
- ASSERT_EQ(command_count, Command::GetAllCommands().size());
+ size_t command_count = GetAllCommandNames().size();
+ RegisterCommand("mock1", [] { return std::unique_ptr<Command>(new MockCommand); });
+ ASSERT_EQ(command_count + 1, GetAllCommandNames().size());
+ UnRegisterCommand("mock1");
+ ASSERT_EQ(command_count, GetAllCommandNames().size());
}
diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp
index 173026eb..93c52e50 100644
--- a/simpleperf/main.cpp
+++ b/simpleperf/main.cpp
@@ -38,12 +38,13 @@ int main(int argc, char** argv) {
}
}
- Command* command = Command::FindCommandByName(args[0]);
+ std::unique_ptr<Command> command = CreateCommandInstance(args[0]);
if (command == nullptr) {
LOG(ERROR) << "malformed command line: unknown command " << args[0];
return 1;
}
std::string command_name = args[0];
+ args.erase(args.begin());
LOG(DEBUG) << "command '" << command_name << "' starts running";
bool result = command->Run(args);
diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp
index 349cf5d1..5062504c 100644
--- a/simpleperf/utils.cpp
+++ b/simpleperf/utils.cpp
@@ -36,16 +36,6 @@ bool IsPowerOfTwo(uint64_t value) {
return (value != 0 && ((value & (value - 1)) == 0));
}
-bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi) {
- if (*pi + 1 == args.size()) {
- LOG(ERROR) << "No argument following " << args[*pi] << " option. Try `simpleperf help "
- << args[0] << "`";
- return false;
- }
- ++*pi;
- return true;
-}
-
void GetEntriesInDir(const std::string& dirpath, std::vector<std::string>* files,
std::vector<std::string>* subdirs) {
if (files != nullptr) {
diff --git a/simpleperf/utils.h b/simpleperf/utils.h
index fba3558b..268e5167 100644
--- a/simpleperf/utils.h
+++ b/simpleperf/utils.h
@@ -56,8 +56,6 @@ void PrintIndented(size_t indent, const char* fmt, ...);
bool IsPowerOfTwo(uint64_t value);
-bool NextArgumentOrError(const std::vector<std::string>& args, size_t* pi);
-
void GetEntriesInDir(const std::string& dirpath, std::vector<std::string>* files,
std::vector<std::string>* subdirs);
diff --git a/simpleperf/workload.cpp b/simpleperf/workload.cpp
index f8e4edde..9d395cb1 100644
--- a/simpleperf/workload.cpp
+++ b/simpleperf/workload.cpp
@@ -110,7 +110,7 @@ bool Workload::Start() {
char exec_child_failed;
ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
if (nread != 0) {
- LOG(ERROR) << "exec child failed";
+ ((nread == -1) ? PLOG(ERROR) : LOG(ERROR)) << "exec child failed, nread = " << nread;
return false;
}
work_state_ = Started;