"--duration time_in_sec Monitor for time_in_sec seconds instead of running\n"
" [command]. Here time_in_sec may be any positive\n"
" floating point number.\n"
+"--interval time_in_ms Print stat for every time_in_ms milliseconds.\n"
+" Here time_in_ms may be any positive floating point\n"
+" number.\n"
"-e event1[:modifier1],event2[:modifier2],...\n"
" Select the event list to count. Use `simpleperf list` to find\n"
" all possible event names. Modifiers can be added to define\n"
system_wide_collection_(false),
child_inherit_(true),
duration_in_sec_(0),
+ interval_in_ms_(0),
event_selection_set_(true),
csv_(false) {
// Die if parent exits.
bool AddDefaultMeasuredEventTypes();
void SetEventSelectionFlags();
bool ShowCounters(const std::vector<CountersInfo>& counters,
- double duration_in_sec);
+ double duration_in_sec, FILE* fp);
bool verbose_mode_;
bool system_wide_collection_;
bool child_inherit_;
double duration_in_sec_;
+ double interval_in_ms_;
std::vector<int> cpus_;
EventSelectionSet event_selection_set_;
std::string output_filename_;
}
}
- // 3. Open perf_event_files.
+ // 3. Open perf_event_files and output file if defined.
if (!system_wide_collection_ && cpus_.empty()) {
cpus_.push_back(-1); // Monitor on all cpus.
}
if (!event_selection_set_.OpenEventFiles(cpus_)) {
return false;
}
+ std::unique_ptr<FILE, decltype(&fclose)> fp_holder(nullptr, fclose);
+ FILE* fp = stdout;
+ if (!output_filename_.empty()) {
+ fp_holder.reset(fopen(output_filename_.c_str(), "w"));
+ if (fp_holder == nullptr) {
+ PLOG(ERROR) << "failed to open " << output_filename_;
+ return false;
+ }
+ fp = fp_holder.get();
+ }
// 4. Create IOEventLoop and add signal/periodic Events.
IOEventLoop loop;
+ std::chrono::time_point<std::chrono::steady_clock> start_time;
+ std::vector<CountersInfo> counters;
if (system_wide_collection_ || (!cpus_.empty() && cpus_[0] != -1)) {
if (!event_selection_set_.HandleCpuHotplugEvents(loop, cpus_)) {
return false;
return false;
}
}
+ auto print_counters = [&]() {
+ auto end_time = std::chrono::steady_clock::now();
+ if (!event_selection_set_.ReadCounters(&counters)) {
+ return false;
+ }
+ double duration_in_sec =
+ std::chrono::duration_cast<std::chrono::duration<double>>(end_time -
+ start_time)
+ .count();
+ if (!ShowCounters(counters, duration_in_sec, fp)) {
+ return false;
+ }
+ return true;
+ };
+
+ if (interval_in_ms_ != 0) {
+ if (!loop.AddPeriodicEvent(SecondToTimeval(interval_in_ms_ / 1000.0),
+ print_counters)) {
+ return false;
+ }
+ }
// 5. Count events while workload running.
- auto start_time = std::chrono::steady_clock::now();
+ start_time = std::chrono::steady_clock::now();
if (workload != nullptr && !workload->Start()) {
return false;
}
if (!loop.RunLoop()) {
return false;
}
- auto end_time = std::chrono::steady_clock::now();
// 6. Read and print counters.
- std::vector<CountersInfo> counters;
- if (!event_selection_set_.ReadCounters(&counters)) {
- return false;
- }
- double duration_in_sec =
- std::chrono::duration_cast<std::chrono::duration<double>>(end_time -
- start_time)
- .count();
- if (!ShowCounters(counters, duration_in_sec)) {
- return false;
- }
- return true;
+
+ return print_counters();
}
bool StatCommand::ParseOptions(const std::vector<std::string>& args,
LOG(ERROR) << "Invalid duration: " << args[i].c_str();
return false;
}
+ } else if (args[i] == "--interval") {
+ if (!NextArgumentOrError(args, &i)) {
+ return false;
+ }
+ errno = 0;
+ char* endptr;
+ interval_in_ms_ = strtod(args[i].c_str(), &endptr);
+ if (interval_in_ms_ <= 0 || *endptr != '\0' || errno == ERANGE) {
+ LOG(ERROR) << "Invalid interval: " << args[i].c_str();
+ return false;
+ }
} else if (args[i] == "-e") {
if (!NextArgumentOrError(args, &i)) {
return false;
}
bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters,
- double duration_in_sec) {
- std::unique_ptr<FILE, decltype(&fclose)> fp_holder(nullptr, fclose);
- FILE* fp = stdout;
- if (!output_filename_.empty()) {
- fp_holder.reset(fopen(output_filename_.c_str(), "w"));
- if (fp_holder == nullptr) {
- PLOG(ERROR) << "failed to open " << output_filename_;
- return false;
- }
- fp = fp_holder.get();
- }
+ double duration_in_sec, FILE* fp) {
if (csv_) {
fprintf(fp, "Performance counter statistics,\n");
} else {
TEST(stat_cmd, group_option) {
ASSERT_TRUE(
- StatCmd()->Run({"--group", "cpu-cycles,cpu-clock", "sleep", "1"}));
+ StatCmd()->Run({"--group", "cpu-clock,page-faults", "sleep", "1"}));
ASSERT_TRUE(StatCmd()->Run({"--group", "cpu-cycles,instructions", "--group",
"cpu-cycles:u,instructions:u", "--group",
"cpu-cycles:k,instructions:k", "sleep", "1"}));
ASSERT_TRUE(StatCmd()->Run({"--duration", "1", "sleep", "2"}));
}
+TEST(stat_cmd, interval_option) {
+ TemporaryFile tmp_file;
+ ASSERT_TRUE(
+ StatCmd()->Run({"--interval", "500.0", "--duration", "1.2", "-o",
+ tmp_file.path, "sleep", "2"}));
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &s));
+ size_t count = 0;
+ size_t pos = 0;
+ std::string subs = "statistics:";
+ while((pos = s.find(subs, pos)) != s.npos) {
+ pos += subs.size();
+ ++count ;
+ }
+ ASSERT_EQ(count, 3UL);
+}
+
TEST(stat_cmd, no_modifier_for_clock_events) {
for (const std::string& e : {"cpu-clock", "task-clock"}) {
for (const std::string& m : {"u", "k"}) {