|
@@ -739,6 +739,9 @@ struct outstate {
|
|
|
FILE *fh;
|
|
|
bool newline;
|
|
|
const char *prefix;
|
|
|
+ int nfields;
|
|
|
+ int id, nr;
|
|
|
+ struct perf_evsel *evsel;
|
|
|
};
|
|
|
|
|
|
#define METRIC_LEN 35
|
|
@@ -754,12 +757,9 @@ static void do_new_line_std(struct outstate *os)
|
|
|
{
|
|
|
fputc('\n', os->fh);
|
|
|
fputs(os->prefix, os->fh);
|
|
|
+ aggr_printout(os->evsel, os->id, os->nr);
|
|
|
if (stat_config.aggr_mode == AGGR_NONE)
|
|
|
fprintf(os->fh, " ");
|
|
|
- if (stat_config.aggr_mode == AGGR_CORE)
|
|
|
- fprintf(os->fh, " ");
|
|
|
- if (stat_config.aggr_mode == AGGR_SOCKET)
|
|
|
- fprintf(os->fh, " ");
|
|
|
fprintf(os->fh, " ");
|
|
|
}
|
|
|
|
|
@@ -789,6 +789,44 @@ static void print_metric_std(void *ctx, const char *color, const char *fmt,
|
|
|
fprintf(out, " %-*s", METRIC_LEN - n - 1, unit);
|
|
|
}
|
|
|
|
|
|
+static void new_line_csv(void *ctx)
|
|
|
+{
|
|
|
+ struct outstate *os = ctx;
|
|
|
+ int i;
|
|
|
+
|
|
|
+ fputc('\n', os->fh);
|
|
|
+ if (os->prefix)
|
|
|
+ fprintf(os->fh, "%s%s", os->prefix, csv_sep);
|
|
|
+ aggr_printout(os->evsel, os->id, os->nr);
|
|
|
+ for (i = 0; i < os->nfields; i++)
|
|
|
+ fputs(csv_sep, os->fh);
|
|
|
+}
|
|
|
+
|
|
|
+static void print_metric_csv(void *ctx,
|
|
|
+ const char *color __maybe_unused,
|
|
|
+ const char *fmt, const char *unit, double val)
|
|
|
+{
|
|
|
+ struct outstate *os = ctx;
|
|
|
+ FILE *out = os->fh;
|
|
|
+ char buf[64], *vals, *ends;
|
|
|
+
|
|
|
+ if (unit == NULL || fmt == NULL) {
|
|
|
+ fprintf(out, "%s%s%s%s", csv_sep, csv_sep, csv_sep, csv_sep);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ snprintf(buf, sizeof(buf), fmt, val);
|
|
|
+ vals = buf;
|
|
|
+ while (isspace(*vals))
|
|
|
+ vals++;
|
|
|
+ ends = vals;
|
|
|
+ while (isdigit(*ends) || *ends == '.')
|
|
|
+ ends++;
|
|
|
+ *ends = 0;
|
|
|
+ while (isspace(*unit))
|
|
|
+ unit++;
|
|
|
+ fprintf(out, "%s%s%s%s", csv_sep, vals, csv_sep, unit);
|
|
|
+}
|
|
|
+
|
|
|
static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
|
|
|
{
|
|
|
FILE *output = stat_config.output;
|
|
@@ -817,6 +855,28 @@ static void nsec_printout(int id, int nr, struct perf_evsel *evsel, double avg)
|
|
|
fprintf(output, "%s%s", csv_sep, evsel->cgrp->name);
|
|
|
}
|
|
|
|
|
|
+static int first_shadow_cpu(struct perf_evsel *evsel, int id)
|
|
|
+{
|
|
|
+ int i;
|
|
|
+
|
|
|
+ if (!aggr_get_id)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ if (stat_config.aggr_mode == AGGR_NONE)
|
|
|
+ return id;
|
|
|
+
|
|
|
+ if (stat_config.aggr_mode == AGGR_GLOBAL)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ for (i = 0; i < perf_evsel__nr_cpus(evsel); i++) {
|
|
|
+ int cpu2 = perf_evsel__cpus(evsel)->map[i];
|
|
|
+
|
|
|
+ if (aggr_get_id(evsel_list->cpus, cpu2) == id)
|
|
|
+ return cpu2;
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static void abs_printout(int id, int nr, struct perf_evsel *evsel, double avg)
|
|
|
{
|
|
|
FILE *output = stat_config.output;
|
|
@@ -853,13 +913,32 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
|
|
struct perf_stat_output_ctx out;
|
|
|
struct outstate os = {
|
|
|
.fh = stat_config.output,
|
|
|
- .prefix = prefix ? prefix : ""
|
|
|
+ .prefix = prefix ? prefix : "",
|
|
|
+ .id = id,
|
|
|
+ .nr = nr,
|
|
|
+ .evsel = counter,
|
|
|
};
|
|
|
print_metric_t pm = print_metric_std;
|
|
|
void (*nl)(void *);
|
|
|
|
|
|
nl = new_line_std;
|
|
|
|
|
|
+ if (csv_output) {
|
|
|
+ static int aggr_fields[] = {
|
|
|
+ [AGGR_GLOBAL] = 0,
|
|
|
+ [AGGR_THREAD] = 1,
|
|
|
+ [AGGR_NONE] = 1,
|
|
|
+ [AGGR_SOCKET] = 2,
|
|
|
+ [AGGR_CORE] = 2,
|
|
|
+ };
|
|
|
+
|
|
|
+ pm = print_metric_csv;
|
|
|
+ nl = new_line_csv;
|
|
|
+ os.nfields = 3;
|
|
|
+ os.nfields += aggr_fields[stat_config.aggr_mode];
|
|
|
+ if (counter->cgrp)
|
|
|
+ os.nfields++;
|
|
|
+ }
|
|
|
if (run == 0 || ena == 0 || counter->counts->scaled == -1) {
|
|
|
aggr_printout(counter, id, nr);
|
|
|
|
|
@@ -880,7 +959,12 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
|
|
fprintf(stat_config.output, "%s%s",
|
|
|
csv_sep, counter->cgrp->name);
|
|
|
|
|
|
+ if (!csv_output)
|
|
|
+ pm(&os, NULL, NULL, "", 0);
|
|
|
+ print_noise(counter, noise);
|
|
|
print_running(run, ena);
|
|
|
+ if (csv_output)
|
|
|
+ pm(&os, NULL, NULL, "", 0);
|
|
|
return;
|
|
|
}
|
|
|
|
|
@@ -893,14 +977,41 @@ static void printout(int id, int nr, struct perf_evsel *counter, double uval,
|
|
|
out.new_line = nl;
|
|
|
out.ctx = &os;
|
|
|
|
|
|
- if (!csv_output)
|
|
|
- perf_stat__print_shadow_stats(counter, uval,
|
|
|
- stat_config.aggr_mode == AGGR_GLOBAL ? 0 :
|
|
|
- cpu_map__id_to_cpu(id),
|
|
|
+ if (csv_output) {
|
|
|
+ print_noise(counter, noise);
|
|
|
+ print_running(run, ena);
|
|
|
+ }
|
|
|
+
|
|
|
+ perf_stat__print_shadow_stats(counter, uval,
|
|
|
+ first_shadow_cpu(counter, id),
|
|
|
&out);
|
|
|
+ if (!csv_output) {
|
|
|
+ print_noise(counter, noise);
|
|
|
+ print_running(run, ena);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+static void aggr_update_shadow(void)
|
|
|
+{
|
|
|
+ int cpu, s2, id, s;
|
|
|
+ u64 val;
|
|
|
+ struct perf_evsel *counter;
|
|
|
|
|
|
- print_noise(counter, noise);
|
|
|
- print_running(run, ena);
|
|
|
+ for (s = 0; s < aggr_map->nr; s++) {
|
|
|
+ id = aggr_map->map[s];
|
|
|
+ evlist__for_each(evsel_list, counter) {
|
|
|
+ val = 0;
|
|
|
+ for (cpu = 0; cpu < perf_evsel__nr_cpus(counter); cpu++) {
|
|
|
+ s2 = aggr_get_id(evsel_list->cpus, cpu);
|
|
|
+ if (s2 != id)
|
|
|
+ continue;
|
|
|
+ val += perf_counts(counter->counts, cpu, 0)->val;
|
|
|
+ }
|
|
|
+ val = val * counter->scale;
|
|
|
+ perf_stat__update_shadow_stats(counter, &val,
|
|
|
+ first_shadow_cpu(counter, id));
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static void print_aggr(char *prefix)
|
|
@@ -914,6 +1025,8 @@ static void print_aggr(char *prefix)
|
|
|
if (!(aggr_map || aggr_get_id))
|
|
|
return;
|
|
|
|
|
|
+ aggr_update_shadow();
|
|
|
+
|
|
|
for (s = 0; s < aggr_map->nr; s++) {
|
|
|
id = aggr_map->map[s];
|
|
|
evlist__for_each(evsel_list, counter) {
|
|
@@ -1441,7 +1554,7 @@ static int perf_stat_init_aggr_mode_file(struct perf_stat *st)
|
|
|
*/
|
|
|
static int add_default_attributes(void)
|
|
|
{
|
|
|
- struct perf_event_attr default_attrs[] = {
|
|
|
+ struct perf_event_attr default_attrs0[] = {
|
|
|
|
|
|
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_TASK_CLOCK },
|
|
|
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_CONTEXT_SWITCHES },
|
|
@@ -1449,8 +1562,14 @@ static int add_default_attributes(void)
|
|
|
{ .type = PERF_TYPE_SOFTWARE, .config = PERF_COUNT_SW_PAGE_FAULTS },
|
|
|
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_CPU_CYCLES },
|
|
|
+};
|
|
|
+ struct perf_event_attr frontend_attrs[] = {
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_FRONTEND },
|
|
|
+};
|
|
|
+ struct perf_event_attr backend_attrs[] = {
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_STALLED_CYCLES_BACKEND },
|
|
|
+};
|
|
|
+ struct perf_event_attr default_attrs1[] = {
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_INSTRUCTIONS },
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS },
|
|
|
{ .type = PERF_TYPE_HARDWARE, .config = PERF_COUNT_HW_BRANCH_MISSES },
|
|
@@ -1567,7 +1686,19 @@ static int add_default_attributes(void)
|
|
|
}
|
|
|
|
|
|
if (!evsel_list->nr_entries) {
|
|
|
- if (perf_evlist__add_default_attrs(evsel_list, default_attrs) < 0)
|
|
|
+ if (perf_evlist__add_default_attrs(evsel_list, default_attrs0) < 0)
|
|
|
+ return -1;
|
|
|
+ if (pmu_have_event("cpu", "stalled-cycles-frontend")) {
|
|
|
+ if (perf_evlist__add_default_attrs(evsel_list,
|
|
|
+ frontend_attrs) < 0)
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (pmu_have_event("cpu", "stalled-cycles-backend")) {
|
|
|
+ if (perf_evlist__add_default_attrs(evsel_list,
|
|
|
+ backend_attrs) < 0)
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+ if (perf_evlist__add_default_attrs(evsel_list, default_attrs1) < 0)
|
|
|
return -1;
|
|
|
}
|
|
|
|
|
@@ -1835,6 +1966,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __maybe_unused)
|
|
|
argc = parse_options_subcommand(argc, argv, stat_options, stat_subcommands,
|
|
|
(const char **) stat_usage,
|
|
|
PARSE_OPT_STOP_AT_NON_OPTION);
|
|
|
+ perf_stat__init_shadow_stats();
|
|
|
|
|
|
if (csv_sep) {
|
|
|
csv_output = true;
|