#include <stdlib.h>
#include <stdio.h>
#include <inttypes.h>
#include <linux/string.h>
#include <linux/time64.h>
#include <math.h>
#include <perf/cpumap.h>
#include "color.h"
#include "counts.h"
#include "evlist.h"
#include "evsel.h"
#include "stat.h"
#include "top.h"
#include "thread_map.h"
#include "cpumap.h"
#include "string2.h"
#include <linux/ctype.h>
#include "cgroup.h"
#include <api/fs/fs.h>
#include "util.h"
#include "iostat.h"
#include "pmu.h"
#define CNTR_NOT_SUPPORTED "<not supported>"
#define CNTR_NOT_COUNTED "<not counted>"
#define METRIC_LEN 38
#define EVNAME_LEN 32
#define COUNTS_LEN 18
#define INTERVAL_LEN 16
#define CGROUP_LEN 16
#define COMM_LEN 16
#define PID_LEN 7
#define CPUS_LEN 4
static int aggr_header_lens[] = {
[AGGR_CORE] = 18,
[AGGR_CACHE] = 22,
[AGGR_DIE] = 12,
[AGGR_SOCKET] = 6,
[AGGR_NODE] = 6,
[AGGR_NONE] = 6,
[AGGR_THREAD] = 16,
[AGGR_GLOBAL] = 0,
};
static const char *aggr_header_csv[] = {
[AGGR_CORE] = "core,cpus,",
[AGGR_CACHE] = "cache,cpus,",
[AGGR_DIE] = "die,cpus,",
[AGGR_SOCKET] = "socket,cpus,",
[AGGR_NONE] = "cpu,",
[AGGR_THREAD] = "comm-pid,",
[AGGR_NODE] = "node,",
[AGGR_GLOBAL] = ""
};
static const char *aggr_header_std[] = {
[AGGR_CORE] = "core",
[AGGR_CACHE] = "cache",
[AGGR_DIE] = "die",
[AGGR_SOCKET] = "socket",
[AGGR_NONE] = "cpu",
[AGGR_THREAD] = "comm-pid",
[AGGR_NODE] = "node",
[AGGR_GLOBAL] = ""
};
static void print_running_std(struct perf_stat_config *config, u64 run, u64 ena)
{
if (run != ena)
fprintf(config->output, " (%.2f%%)", 100.0 * run / ena);
}
static void print_running_csv(struct perf_stat_config *config, u64 run, u64 ena)
{
double enabled_percent = 100;
if (run != ena)
enabled_percent = 100 * run / ena;
fprintf(config->output, "%s%" PRIu64 "%s%.2f",
config->csv_sep, run, config->csv_sep, enabled_percent);
}
static void print_running_json(struct perf_stat_config *config, u64 run, u64 ena)
{
double enabled_percent = 100;
if (run != ena)
enabled_percent = 100 * run / ena;
fprintf(config->output, "\"event-runtime\" : %" PRIu64 ", \"pcnt-running\" : %.2f, ",
run, enabled_percent);
}
static void print_running(struct perf_stat_config *config,
u64 run, u64 ena, bool before_metric)
{
if (config->json_output) {
if (before_metric)
print_running_json(config, run, ena);
} else if (config->csv_output) {
if (before_metric)
print_running_csv(config, run, ena);
} else {
if (!before_metric)
print_running_std(config, run, ena);
}
}
static void print_noise_pct_std(struct perf_stat_config *config,
double pct)
{
if (pct)
fprintf(config->output, " ( +-%6.2f%% )", pct);
}
static void print_noise_pct_csv(struct perf_stat_config *config,
double pct)
{
fprintf(config->output, "%s%.2f%%", config->csv_sep, pct);
}
static void print_noise_pct_json(struct perf_stat_config *config,
double pct)
{
fprintf(config->output, "\"variance\" : %.2f, ", pct);
}
static void print_noise_pct(struct perf_stat_config *config,
double total, double avg, bool before_metric)
{
double pct = rel_stddev_stats(total, avg);
if (config->json_output) {
if (before_metric)
print_noise_pct_json(config, pct);
} else if (config->csv_output) {
if (before_metric)
print_noise_pct_csv(config, pct);
} else {
if (!before_metric)
print_noise_pct_std(config, pct);
}
}
static void print_noise(struct perf_stat_config *config,
struct evsel *evsel, double avg, bool before_metric)
{
struct perf_stat_evsel *ps;
if (config->run_count == 1)
return;
ps = evsel->stats;
print_noise_pct(config, stddev_stats(&ps->res_stats), avg, before_metric);
}
static void print_cgroup_std(struct perf_stat_config *config, const char *cgrp_name)
{
fprintf(config->output, " %-*s", CGROUP_LEN, cgrp_name);
}
static void print_cgroup_csv(struct perf_stat_config *config, const char *cgrp_name)
{
fprintf(config->output, "%s%s", config->csv_sep, cgrp_name);
}
static void print_cgroup_json(struct perf_stat_config *config, const char *cgrp_name)
{
fprintf(config->output, "\"cgroup\" : \"%s\", ", cgrp_name);
}
static void print_cgroup(struct perf_stat_config *config, struct cgroup *cgrp)
{
if (nr_cgroups || config->cgroup_list) {
const char *cgrp_name = cgrp ? cgrp->name : "";
if (config->json_output)
print_cgroup_json(config, cgrp_name);
else if (config->csv_output)
print_cgroup_csv(config, cgrp_name);
else
print_cgroup_std(config, cgrp_name);
}
}
static void print_aggr_id_std(struct perf_stat_config *config,
struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
int idx = config->aggr_mode;
char buf[128];
switch (config->aggr_mode) {
case AGGR_CORE:
snprintf(buf, sizeof(buf), "S%d-D%d-C%d", id.socket, id.die, id.core);
break;
case AGGR_CACHE:
snprintf(buf, sizeof(buf), "S%d-D%d-L%d-ID%d",
id.socket, id.die, id.cache_lvl, id.cache);
break;
case AGGR_DIE:
snprintf(buf, sizeof(buf), "S%d-D%d", id.socket, id.die);
break;
case AGGR_SOCKET:
snprintf(buf, sizeof(buf), "S%d", id.socket);
break;
case AGGR_NODE:
snprintf(buf, sizeof(buf), "N%d", id.node);
break;
case AGGR_NONE:
if (evsel->percore && !config->percore_show_thread) {
snprintf(buf, sizeof(buf), "S%d-D%d-C%d ",
id.socket, id.die, id.core);
fprintf(output, "%-*s ",
aggr_header_lens[AGGR_CORE], buf);
} else if (id.cpu.cpu > -1) {
fprintf(output, "CPU%-*d ",
aggr_header_lens[AGGR_NONE] - 3, id.cpu.cpu);
}
return;
case AGGR_THREAD:
fprintf(output, "%*s-%-*d ",
COMM_LEN, perf_thread_map__comm(evsel->core.threads, id.thread_idx),
PID_LEN, perf_thread_map__pid(evsel->core.threads, id.thread_idx));
return;
case AGGR_GLOBAL:
case AGGR_UNSET:
case AGGR_MAX:
default:
return;
}
fprintf(output, "%-*s %*d ", aggr_header_lens[idx], buf, 4, aggr_nr);
}
static void print_aggr_id_csv(struct perf_stat_config *config,
struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
const char *sep = config->csv_sep;
switch (config->aggr_mode) {
case AGGR_CORE:
fprintf(output, "S%d-D%d-C%d%s%d%s",
id.socket, id.die, id.core, sep, aggr_nr, sep);
break;
case AGGR_CACHE:
fprintf(config->output, "S%d-D%d-L%d-ID%d%s%d%s",
id.socket, id.die, id.cache_lvl, id.cache, sep, aggr_nr, sep);
break;
case AGGR_DIE:
fprintf(output, "S%d-D%d%s%d%s",
id.socket, id.die, sep, aggr_nr, sep);
break;
case AGGR_SOCKET:
fprintf(output, "S%d%s%d%s",
id.socket, sep, aggr_nr, sep);
break;
case AGGR_NODE:
fprintf(output, "N%d%s%d%s",
id.node, sep, aggr_nr, sep);
break;
case AGGR_NONE:
if (evsel->percore && !config->percore_show_thread) {
fprintf(output, "S%d-D%d-C%d%s",
id.socket, id.die, id.core, sep);
} else if (id.cpu.cpu > -1) {
fprintf(output, "CPU%d%s",
id.cpu.cpu, sep);
}
break;
case AGGR_THREAD:
fprintf(output, "%s-%d%s",
perf_thread_map__comm(evsel->core.threads, id.thread_idx),
perf_thread_map__pid(evsel->core.threads, id.thread_idx),
sep);
break;
case AGGR_GLOBAL:
case AGGR_UNSET:
case AGGR_MAX:
default:
break;
}
}
static void print_aggr_id_json(struct perf_stat_config *config,
struct evsel *evsel, struct aggr_cpu_id id, int aggr_nr)
{
FILE *output = config->output;
switch (config->aggr_mode) {
case AGGR_CORE:
fprintf(output, "\"core\" : \"S%d-D%d-C%d\", \"aggregate-number\" : %d, ",
id.socket, id.die, id.core, aggr_nr);
break;
case AGGR_CACHE:
fprintf(output, "\"cache\" : \"S%d-D%d-L%d-ID%d\", \"aggregate-number\" : %d, ",
id.socket, id.die, id.cache_lvl, id.cache, aggr_nr);
break;
case AGGR_DIE:
fprintf(output, "\"die\" : \"S%d-D%d\", \"aggregate-number\" : %d, ",
id.socket, id.die, aggr_nr);
break;
case AGGR_SOCKET:
fprintf(output, "\"socket\" : \"S%d\", \"aggregate-number\" : %d, ",
id.socket, aggr_nr);
break;
case AGGR_NODE:
fprintf(output, "\"node\" : \"N%d\", \"aggregate-number\" : %d, ",
id.node, aggr_nr);
break;
case AGGR_NONE:
if (evsel->percore && !config->percore_show_thread) {
fprintf(output, "\"core\" : \"S%d-D%d-C%d\"",
id.socket, id.die, id.core);
} el
|