diff options
author | Yabin Cui <yabinc@google.com> | 2015-06-01 11:21:37 -0700 |
---|---|---|
committer | Yabin Cui <yabinc@google.com> | 2015-06-04 15:26:32 -0700 |
commit | f79f07e13c56f7ca3be1435cea7f8861daf7efaa (patch) | |
tree | 8c76bac6fe4d9b52b69e57393f5deecb18febd97 /simpleperf | |
parent | d4637d6e7d17f48d9325fa133be82b06a408f523 (diff) | |
download | extras-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.cpp | 45 | ||||
-rw-r--r-- | simpleperf/cmd_dumprecord_test.cpp | 16 | ||||
-rw-r--r-- | simpleperf/cmd_help.cpp | 13 | ||||
-rw-r--r-- | simpleperf/cmd_list.cpp | 14 | ||||
-rw-r--r-- | simpleperf/cmd_list_test.cpp | 10 | ||||
-rw-r--r-- | simpleperf/cmd_record.cpp | 99 | ||||
-rw-r--r-- | simpleperf/cmd_record_test.cpp | 62 | ||||
-rw-r--r-- | simpleperf/cmd_stat.cpp | 60 | ||||
-rw-r--r-- | simpleperf/cmd_stat_test.cpp | 14 | ||||
-rw-r--r-- | simpleperf/command.cpp | 61 | ||||
-rw-r--r-- | simpleperf/command.h | 18 | ||||
-rw-r--r-- | simpleperf/command_test.cpp | 26 | ||||
-rw-r--r-- | simpleperf/main.cpp | 3 | ||||
-rw-r--r-- | simpleperf/utils.cpp | 10 | ||||
-rw-r--r-- | simpleperf/utils.h | 2 | ||||
-rw-r--r-- | simpleperf/workload.cpp | 2 |
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; |