summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYabin Cui <yabinc@google.com>2015-08-20 02:03:29 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-08-20 02:03:29 +0000
commitb84ee6cd81da6a0929035c589f1c0450e8d10902 (patch)
tree0551967c2b1a4eb3bd7f73608706a5e7ee11720a
parent5f0c337dfa35d575585ab6c1789f196e94ab0488 (diff)
parent04d08a35c5e1cabdf6eb7397536a790b0ff44459 (diff)
downloadextras-b84ee6cd81da6a0929035c589f1c0450e8d10902.tar.gz
Merge "Simpleperf: improve output of stat command."
-rw-r--r--simpleperf/cmd_stat.cpp179
-rw-r--r--simpleperf/event_fd.h14
-rw-r--r--simpleperf/event_selection_set.cpp28
-rw-r--r--simpleperf/event_selection_set.h13
-rw-r--r--simpleperf/event_type.cpp1
-rw-r--r--simpleperf/event_type.h1
6 files changed, 178 insertions, 58 deletions
diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp
index 6358f502..245a9967 100644
--- a/simpleperf/cmd_stat.cpp
+++ b/simpleperf/cmd_stat.cpp
@@ -17,6 +17,9 @@
#include <inttypes.h>
#include <signal.h>
#include <stdio.h>
+#include <string.h>
+
+#include <algorithm>
#include <chrono>
#include <set>
#include <string>
@@ -80,9 +83,7 @@ class StatCommand : public Command {
bool AddMeasuredEventType(const std::string& event_type_name);
bool AddDefaultMeasuredEventTypes();
bool SetEventSelection();
- bool ShowCounters(
- const std::map<const EventTypeAndModifier*, std::vector<PerfCounter>>& counters_map,
- std::chrono::steady_clock::duration counting_duration);
+ bool ShowCounters(const std::vector<CountersInfo>& counters, double duration_in_sec);
bool verbose_mode_;
bool system_wide_collection_;
@@ -154,11 +155,13 @@ bool StatCommand::Run(const std::vector<std::string>& args) {
auto end_time = std::chrono::steady_clock::now();
// 5. Read and print counters.
- std::map<const EventTypeAndModifier*, std::vector<PerfCounter>> counters_map;
- if (!event_selection_set_.ReadCounters(&counters_map)) {
+ std::vector<CountersInfo> counters;
+ if (!event_selection_set_.ReadCounters(&counters)) {
return false;
}
- if (!ShowCounters(counters_map, end_time - start_time)) {
+ 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;
@@ -254,46 +257,152 @@ bool StatCommand::SetEventSelection() {
return true;
}
-bool StatCommand::ShowCounters(
- const std::map<const EventTypeAndModifier*, std::vector<PerfCounter>>& counters_map,
- std::chrono::steady_clock::duration counting_duration) {
+static std::string ReadableCountValue(uint64_t count,
+ const EventTypeAndModifier& event_type_modifier) {
+ if (event_type_modifier.event_type.name == "cpu-clock" ||
+ event_type_modifier.event_type.name == "task-clock") {
+ double value = count / 1e6;
+ return android::base::StringPrintf("%lf(ms)", value);
+ } else {
+ std::string s = android::base::StringPrintf("%" PRIu64, count);
+ for (size_t i = s.size() - 1, j = 1; i > 0; --i, ++j) {
+ if (j == 3) {
+ s.insert(s.begin() + i, ',');
+ j = 0;
+ }
+ }
+ return s;
+ }
+}
+
+struct CounterSummary {
+ const EventTypeAndModifier* event_type;
+ uint64_t count;
+ double scale;
+ std::string readable_count_str;
+ std::string comment;
+};
+
+static std::string GetCommentForSummary(const CounterSummary& summary,
+ const std::vector<CounterSummary>& summaries,
+ double duration_in_sec) {
+ const std::string& type_name = summary.event_type->event_type.name;
+ const std::string& modifier = summary.event_type->modifier;
+ if (type_name == "task-clock") {
+ double run_sec = summary.count / 1e9;
+ double cpu_usage = run_sec / duration_in_sec;
+ return android::base::StringPrintf("%lf%% cpu usage", cpu_usage * 100);
+ }
+ if (type_name == "cpu-clock") {
+ return "";
+ }
+ if (type_name == "cpu-cycles") {
+ double hz = summary.count / duration_in_sec;
+ return android::base::StringPrintf("%lf GHz", hz / 1e9);
+ }
+ if (type_name == "instructions" && summary.count != 0) {
+ for (auto& t : summaries) {
+ if (t.event_type->event_type.name == "cpu-cycles" && t.event_type->modifier == modifier) {
+ double cycles_per_instruction = t.count * 1.0 / summary.count;
+ return android::base::StringPrintf("%lf cycles per instruction", cycles_per_instruction);
+ }
+ }
+ }
+ if (android::base::EndsWith(type_name, "-misses")) {
+ std::string s;
+ if (type_name == "cache-misses") {
+ s = "cache-references";
+ } else if (type_name == "branch-misses") {
+ s = "branch-instructions";
+ } else {
+ s = type_name.substr(0, type_name.size() - strlen("-misses")) + "s";
+ }
+ for (auto& t : summaries) {
+ if (t.event_type->event_type.name == s && t.event_type->modifier == modifier && t.count != 0) {
+ double miss_rate = summary.count * 1.0 / t.count;
+ return android::base::StringPrintf("%lf%% miss rate", miss_rate * 100);
+ }
+ }
+ }
+ double rate = summary.count / duration_in_sec;
+ if (rate > 1e9) {
+ return android::base::StringPrintf("%.3lf G/sec", rate / 1e9);
+ }
+ if (rate > 1e6) {
+ return android::base::StringPrintf("%.3lf M/sec", rate / 1e6);
+ }
+ if (rate > 1e3) {
+ return android::base::StringPrintf("%.3lf K/sec", rate / 1e3);
+ }
+ return android::base::StringPrintf("%.3lf /sec", rate);
+}
+
+bool StatCommand::ShowCounters(const std::vector<CountersInfo>& counters, double duration_in_sec) {
printf("Performance counter statistics:\n\n");
- for (auto& pair : counters_map) {
- auto& event_type_modifier = 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);
+ if (verbose_mode_) {
+ for (auto& counters_info : counters) {
+ const EventTypeAndModifier* event_type = counters_info.event_type;
+ for (auto& counter_info : counters_info.counters) {
+ printf("%s(tid %d, cpu %d): count %s, time_enabled %" PRIu64 ", time running %" PRIu64
+ ", id %" PRIu64 "\n",
+ event_type->name.c_str(), counter_info.tid, counter_info.cpu,
+ ReadableCountValue(counter_info.counter.value, *event_type).c_str(),
+ counter_info.counter.time_enabled, counter_info.counter.time_running,
+ counter_info.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;
+ std::vector<CounterSummary> summaries;
+ for (auto& counters_info : counters) {
+ uint64_t value_sum = 0;
+ uint64_t time_enabled_sum = 0;
+ uint64_t time_running_sum = 0;
+ for (auto& counter_info : counters_info.counters) {
+ value_sum += counter_info.counter.value;
+ time_enabled_sum += counter_info.counter.time_enabled;
+ time_running_sum += counter_info.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) {
+ double scale = 1.0;
+ uint64_t scaled_count = value_sum;
+ if (time_running_sum < time_enabled_sum) {
+ if (time_running_sum == 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);
+ scale = static_cast<double>(time_enabled_sum) / time_running_sum;
+ scaled_count = static_cast<uint64_t>(scale * value_sum);
}
}
- printf("%'30" PRId64 "%s %s\n", scaled_count, scaled ? "(scaled)" : " ",
- event_type_modifier->name.c_str());
+ CounterSummary summary;
+ summary.event_type = counters_info.event_type;
+ summary.count = scaled_count;
+ summary.scale = scale;
+ summary.readable_count_str = ReadableCountValue(summary.count, *summary.event_type);
+ summaries.push_back(summary);
+ }
+
+ for (auto& summary : summaries) {
+ summary.comment = GetCommentForSummary(summary, summaries, duration_in_sec);
+ }
+
+ size_t count_column_width = 0;
+ size_t name_column_width = 0;
+ size_t comment_column_width = 0;
+ for (auto& summary : summaries) {
+ count_column_width = std::max(count_column_width, summary.readable_count_str.size());
+ name_column_width = std::max(name_column_width, summary.event_type->name.size());
+ comment_column_width = std::max(comment_column_width, summary.comment.size());
+ }
+
+ for (auto& summary : summaries) {
+ printf(" %*s %-*s # %-*s (%.0lf%%)\n", static_cast<int>(count_column_width),
+ summary.readable_count_str.c_str(), static_cast<int>(name_column_width),
+ summary.event_type->name.c_str(), static_cast<int>(comment_column_width),
+ summary.comment.c_str(), 1.0 / summary.scale * 100);
}
- printf("\nTotal test time: %lf seconds.\n",
- std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
+
+ printf("\nTotal test time: %lf seconds.\n", duration_in_sec);
return true;
}
diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h
index 1fe5af00..12427912 100644
--- a/simpleperf/event_fd.h
+++ b/simpleperf/event_fd.h
@@ -42,11 +42,16 @@ class EventFd {
~EventFd();
- // Give information about this perf_event_file, like (event_name, tid, cpu).
- std::string Name() const;
-
uint64_t Id() const;
+ pid_t ThreadId() const {
+ return tid_;
+ }
+
+ int Cpu() const {
+ return cpu_;
+ }
+
// It tells the kernel to start counting and recording events specified by this file.
bool EnableEvent();
@@ -81,6 +86,9 @@ class EventFd {
mmap_len_(0) {
}
+ // Give information about this perf_event_file, like (event_name, tid, cpu).
+ std::string Name() const;
+
int perf_event_fd_;
mutable uint64_t id_;
const std::string event_name_;
diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp
index 555246e6..c3a538e8 100644
--- a/simpleperf/event_selection_set.cpp
+++ b/simpleperf/event_selection_set.cpp
@@ -205,18 +205,21 @@ bool EventSelectionSet::EnableEvents() {
return true;
}
-bool EventSelectionSet::ReadCounters(
- std::map<const EventTypeAndModifier*, std::vector<PerfCounter>>* counters_map) {
+bool EventSelectionSet::ReadCounters(std::vector<CountersInfo>* counters) {
+ counters->clear();
for (auto& selection : selections_) {
- std::vector<PerfCounter> counters;
+ CountersInfo counters_info;
+ counters_info.event_type = &selection.event_type_modifier;
for (auto& event_fd : selection.event_fds) {
- PerfCounter counter;
- if (!event_fd->ReadCounter(&counter)) {
+ CountersInfo::CounterInfo counter_info;
+ if (!event_fd->ReadCounter(&counter_info.counter)) {
return false;
}
- counters.push_back(counter);
+ counter_info.tid = event_fd->ThreadId();
+ counter_info.cpu = event_fd->Cpu();
+ counters_info.counters.push_back(counter_info);
}
- counters_map->insert(std::make_pair(&selection.event_type_modifier, counters));
+ counters->push_back(counters_info);
}
return true;
}
@@ -278,17 +281,6 @@ bool EventSelectionSet::ReadMmapEventData(std::function<bool(const char*, size_t
return true;
}
-std::string EventSelectionSet::FindEventFileNameById(uint64_t id) {
- for (auto& selection : selections_) {
- for (auto& event_fd : selection.event_fds) {
- if (event_fd->Id() == id) {
- return event_fd->Name();
- }
- }
- }
- return "";
-}
-
EventSelectionSet::EventSelection* EventSelectionSet::FindSelectionByType(
const EventTypeAndModifier& event_type_modifier) {
for (auto& selection : selections_) {
diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h
index afde0af0..3fdd71a8 100644
--- a/simpleperf/event_selection_set.h
+++ b/simpleperf/event_selection_set.h
@@ -28,6 +28,16 @@
#include "event_type.h"
#include "perf_event.h"
+struct CountersInfo {
+ const EventTypeAndModifier* event_type;
+ struct CounterInfo {
+ pid_t tid;
+ int cpu;
+ PerfCounter counter;
+ };
+ std::vector<CounterInfo> counters;
+};
+
// EventSelectionSet helps to monitor events.
// Firstly, the user creates an EventSelectionSet, and adds the specific event types to monitor.
// Secondly, the user defines how to monitor the events (by setting enable_on_exec flag,
@@ -62,12 +72,11 @@ class EventSelectionSet {
bool OpenEventFilesForThreads(const std::vector<pid_t>& threads);
bool OpenEventFilesForThreadsOnAllCpus(const std::vector<pid_t>& threads);
bool EnableEvents();
- bool ReadCounters(std::map<const EventTypeAndModifier*, std::vector<PerfCounter>>* counters_map);
+ bool ReadCounters(std::vector<CountersInfo>* counters);
void PreparePollForEventFiles(std::vector<pollfd>* pollfds);
bool MmapEventFiles(size_t mmap_pages);
bool ReadMmapEventData(std::function<bool(const char*, size_t)> callback);
- std::string FindEventFileNameById(uint64_t id);
const perf_event_attr& FindEventAttrByType(const EventTypeAndModifier& event_type_modifier);
const std::vector<std::unique_ptr<EventFd>>& FindEventFdsByType(
const EventTypeAndModifier& event_type_modifier);
diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp
index 508fb9b7..1d0243c7 100644
--- a/simpleperf/event_type.cpp
+++ b/simpleperf/event_type.cpp
@@ -172,5 +172,6 @@ std::unique_ptr<EventTypeAndModifier> ParseEventType(const std::string& event_ty
LOG(ERROR) << "Unknown event type modifier '" << c << "'";
}
}
+ event_type_modifier->modifier = modifier;
return event_type_modifier;
}
diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h
index 7273e523..41001254 100644
--- a/simpleperf/event_type.h
+++ b/simpleperf/event_type.h
@@ -47,6 +47,7 @@ const EventType* FindEventTypeByName(const std::string& name);
struct EventTypeAndModifier {
std::string name;
EventType event_type;
+ std::string modifier;
bool exclude_user;
bool exclude_kernel;
bool exclude_hv;