/* * Copyright (C) 2015 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 <inttypes.h> #include <stdio.h> #include <chrono> #include <string> #include <vector> #include <base/logging.h> #include <base/strings.h> #include "command.h" #include "environment.h" #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{ "cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", "instructions", "branch-instructions", "branch-misses", "task-clock", "context-switches", "page-faults", }; class StatCommandImpl { public: StatCommandImpl() : verbose_mode_(false), system_wide_collection_(false) { } bool Run(const std::vector<std::string>& args); private: bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args); bool AddMeasuredEventType(const std::string& event_type_name, bool report_unsupported_type = true); bool AddDefaultMeasuredEventTypes(); bool ShowCounters(const std::map<const EventType*, std::vector<PerfCounter>>& counters_map, std::chrono::steady_clock::duration counting_duration); EventSelectionSet event_selection_set_; bool verbose_mode_; bool system_wide_collection_; }; bool StatCommandImpl::Run(const std::vector<std::string>& args) { // 1. Parse options. std::vector<std::string> workload_args; if (!ParseOptions(args, &workload_args)) { return false; } // 2. Add default measured event types. if (event_selection_set_.Empty()) { if (!AddDefaultMeasuredEventTypes()) { return false; } } // 3. Create workload. if (workload_args.empty()) { // TODO: change default workload to sleep 99999, and run stat until Ctrl-C. workload_args = std::vector<std::string>({"sleep", "1"}); } std::unique_ptr<Workload> workload = Workload::CreateWorkload(workload_args); if (workload == nullptr) { return false; } // 4. Open perf_event_files. if (system_wide_collection_) { if (!event_selection_set_.OpenEventFilesForAllCpus()) { return false; } } else { event_selection_set_.EnableOnExec(); if (!event_selection_set_.OpenEventFilesForProcess(workload->GetPid())) { return false; } } // 5. Count events while workload running. auto start_time = std::chrono::steady_clock::now(); // If monitoring only one process, we use the enable_on_exec flag, and don't need to start // counting manually. if (system_wide_collection_) { if (!event_selection_set_.EnableEvents()) { return false; } } if (!workload->Start()) { return false; } workload->WaitFinish(); auto end_time = std::chrono::steady_clock::now(); // 6. Read and print counters. std::map<const EventType*, std::vector<PerfCounter>> counters_map; if (!event_selection_set_.ReadCounters(&counters_map)) { return false; } if (!ShowCounters(counters_map, end_time - start_time)) { return false; } return true; } bool StatCommandImpl::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) { if (args[i] == "-a") { system_wide_collection_ = true; } else if (args[i] == "-e") { if (!NextArgumentOrError(args, &i)) { return false; } std::vector<std::string> event_types = android::base::Split(args[i], ","); for (auto& event_type : event_types) { if (!AddMeasuredEventType(event_type)) { return false; } } } else if (args[i] == "--verbose") { verbose_mode_ = true; } else { LOG(ERROR) << "Unknown option for stat command: " << args[i]; LOG(ERROR) << "Try `simpleperf help stat`"; return false; } } if (non_option_args != nullptr) { non_option_args->clear(); for (; i < args.size(); ++i) { non_option_args->push_back(args[i]); } } return true; } bool StatCommandImpl::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) { return false; } event_selection_set_.AddEventType(*event_type); return true; } bool StatCommandImpl::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); } if (event_selection_set_.Empty()) { LOG(ERROR) << "Failed to add any supported default measured types"; return false; } return true; } bool StatCommandImpl::ShowCounters( const std::map<const EventType*, std::vector<PerfCounter>>& counters_map, std::chrono::steady_clock::duration counting_duration) { printf("Performance counter statistics:\n\n"); for (auto& pair : counters_map) { auto& event_type = pair.first; auto& counters = pair.second; if (verbose_mode_) { for (auto& counter : counters) { printf("%s: value %'" PRId64 ", time_enabled %" PRId64 ", time_running %" PRId64 ", id %" PRId64 "\n", event_selection_set_.FindEventFileNameById(counter.id).c_str(), counter.value, counter.time_enabled, counter.time_running, counter.id); } } PerfCounter sum_counter; memset(&sum_counter, 0, sizeof(sum_counter)); for (auto& counter : counters) { sum_counter.value += counter.value; sum_counter.time_enabled += counter.time_enabled; sum_counter.time_running += counter.time_running; } bool scaled = false; int64_t scaled_count = sum_counter.value; if (sum_counter.time_running < sum_counter.time_enabled) { if (sum_counter.time_running == 0) { scaled_count = 0; } else { scaled = true; scaled_count = static_cast<int64_t>(static_cast<double>(sum_counter.value) * sum_counter.time_enabled / sum_counter.time_running); } } printf("%'30" PRId64 "%s %s\n", scaled_count, scaled ? "(scaled)" : " ", event_type->name); } printf("\nTotal test time: %lf seconds.\n", std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count()); 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;