// SPDX-License-Identifier: GPL-2.0
#include <math.h>
#include <stdio.h>
#include "evsel.h"
#include "stat.h"
#include "color.h"
#include "debug.h"
#include "pmu.h"
#include "rblist.h"
#include "evlist.h"
#include "expr.h"
#include "metricgroup.h"
#include "cgroup.h"
#include "units.h"
#include <linux/zalloc.h>
#include "iostat.h"
#include "util/hashmap.h"
#include "tool_pmu.h"
struct stats walltime_nsecs_stats;
struct rusage_stats ru_stats;
enum {
CTX_BIT_USER = 1 << 0 ,
CTX_BIT_KERNEL = 1 << 1 ,
CTX_BIT_HV = 1 << 2 ,
CTX_BIT_HOST = 1 << 3 ,
CTX_BIT_IDLE = 1 << 4 ,
CTX_BIT_MAX = 1 << 5 ,
};
enum stat_type {
STAT_NONE = 0 ,
STAT_NSECS,
STAT_CYCLES,
STAT_INSTRUCTIONS,
STAT_STALLED_CYCLES_FRONT,
STAT_STALLED_CYCLES_BACK,
STAT_BRANCHES,
STAT_BRANCH_MISS,
STAT_CACHE_REFS,
STAT_CACHE_MISSES,
STAT_L1_DCACHE,
STAT_L1_ICACHE,
STAT_LL_CACHE,
STAT_ITLB_CACHE,
STAT_DTLB_CACHE,
STAT_L1D_MISS,
STAT_L1I_MISS,
STAT_LL_MISS,
STAT_DTLB_MISS,
STAT_ITLB_MISS,
STAT_MAX
};
static int evsel_context(const struct evsel *evsel)
{
int ctx = 0 ;
if (evsel->core.attr.exclude_kernel)
ctx |= CTX_BIT_KERNEL;
if (evsel->core.attr.exclude_user)
ctx |= CTX_BIT_USER;
if (evsel->core.attr.exclude_hv)
ctx |= CTX_BIT_HV;
if (evsel->core.attr.exclude_host)
ctx |= CTX_BIT_HOST;
if (evsel->core.attr.exclude_idle)
ctx |= CTX_BIT_IDLE;
return ctx;
}
void perf_stat__reset_shadow_stats(void )
{
memset(&walltime_nsecs_stats, 0 , sizeof (walltime_nsecs_stats));
memset(&ru_stats, 0 , sizeof (ru_stats));
}
static enum stat_type evsel__stat_type(struct evsel *evsel)
{
/* Fake perf_hw_cache_op_id values for use with evsel__match. */
u64 PERF_COUNT_hw_cache_l1d_miss = PERF_COUNT_HW_CACHE_L1D |
((PERF_COUNT_HW_CACHE_OP_READ) << 8 ) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16 );
u64 PERF_COUNT_hw_cache_l1i_miss = PERF_COUNT_HW_CACHE_L1I |
((PERF_COUNT_HW_CACHE_OP_READ) << 8 ) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16 );
u64 PERF_COUNT_hw_cache_ll_miss = PERF_COUNT_HW_CACHE_LL |
((PERF_COUNT_HW_CACHE_OP_READ) << 8 ) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16 );
u64 PERF_COUNT_hw_cache_dtlb_miss = PERF_COUNT_HW_CACHE_DTLB |
((PERF_COUNT_HW_CACHE_OP_READ) << 8 ) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16 );
u64 PERF_COUNT_hw_cache_itlb_miss = PERF_COUNT_HW_CACHE_ITLB |
((PERF_COUNT_HW_CACHE_OP_READ) << 8 ) |
((PERF_COUNT_HW_CACHE_RESULT_MISS) << 16 );
if (evsel__is_clock(evsel))
return STAT_NSECS;
else if (evsel__match(evsel, HARDWARE, HW_CPU_CYCLES))
return STAT_CYCLES;
else if (evsel__match(evsel, HARDWARE, HW_INSTRUCTIONS))
return STAT_INSTRUCTIONS;
else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_FRONTEND))
return STAT_STALLED_CYCLES_FRONT;
else if (evsel__match(evsel, HARDWARE, HW_STALLED_CYCLES_BACKEND))
return STAT_STALLED_CYCLES_BACK;
else if (evsel__match(evsel, HARDWARE, HW_BRANCH_INSTRUCTIONS))
return STAT_BRANCHES;
else if (evsel__match(evsel, HARDWARE, HW_BRANCH_MISSES))
return STAT_BRANCH_MISS;
else if (evsel__match(evsel, HARDWARE, HW_CACHE_REFERENCES))
return STAT_CACHE_REFS;
else if (evsel__match(evsel, HARDWARE, HW_CACHE_MISSES))
return STAT_CACHE_MISSES;
else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1D))
return STAT_L1_DCACHE;
else if (evsel__match(evsel, HW_CACHE, HW_CACHE_L1I))
return STAT_L1_ICACHE;
else if (evsel__match(evsel, HW_CACHE, HW_CACHE_LL))
return STAT_LL_CACHE;
else if (evsel__match(evsel, HW_CACHE, HW_CACHE_DTLB))
return STAT_DTLB_CACHE;
else if (evsel__match(evsel, HW_CACHE, HW_CACHE_ITLB))
return STAT_ITLB_CACHE;
else if (evsel__match(evsel, HW_CACHE, hw_cache_l1d_miss))
return STAT_L1D_MISS;
else if (evsel__match(evsel, HW_CACHE, hw_cache_l1i_miss))
return STAT_L1I_MISS;
else if (evsel__match(evsel, HW_CACHE, hw_cache_ll_miss))
return STAT_LL_MISS;
else if (evsel__match(evsel, HW_CACHE, hw_cache_dtlb_miss))
return STAT_DTLB_MISS;
else if (evsel__match(evsel, HW_CACHE, hw_cache_itlb_miss))
return STAT_ITLB_MISS;
return STAT_NONE;
}
static enum metric_threshold_classify get_ratio_thresh(const double ratios[3 ], double val)
{
assert(ratios[0 ] > ratios[1 ]);
assert(ratios[1 ] > ratios[2 ]);
return val > ratios[1 ]
? (val > ratios[0 ] ? METRIC_THRESHOLD_BAD : METRIC_THRESHOLD_NEARLY_BAD)
: (val > ratios[2 ] ? METRIC_THRESHOLD_LESS_GOOD : METRIC_THRESHOLD_GOOD);
}
static double find_stat(const struct evsel *evsel, int aggr_idx, enum stat_type type)
{
struct evsel *cur;
int evsel_ctx = evsel_context(evsel);
struct perf_pmu *evsel_pmu = evsel__find_pmu(evsel);
evlist__for_each_entry(evsel->evlist, cur) {
struct perf_stat_aggr *aggr;
/* Ignore the evsel that is being searched from. */
if (evsel == cur)
continue ;
/* Ignore evsels that are part of different groups. */
if (evsel->core.leader->nr_members > 1 &&
evsel->core.leader != cur->core.leader)
continue ;
/* Ignore evsels with mismatched modifiers. */
if (evsel_ctx != evsel_context(cur))
continue ;
/* Ignore if not the cgroup we're looking for. */
if (evsel->cgrp != cur->cgrp)
continue ;
/* Ignore if not the stat we're looking for. */
if (type != evsel__stat_type(cur))
continue ;
/*
* Except the SW CLOCK events ,
* ignore if not the PMU we ' re looking for .
*/
if ((type != STAT_NSECS) && (evsel_pmu != evsel__find_pmu(cur)))
continue ;
aggr = &cur->stats->aggr[aggr_idx];
if (type == STAT_NSECS)
return aggr->counts.val;
return aggr->counts.val * cur->scale;
}
return 0 .0 ;
}
static void print_ratio(struct perf_stat_config *config,
const struct evsel *evsel, int aggr_idx,
double numerator, struct perf_stat_output_ctx *out,
enum stat_type denominator_type,
const double thresh_ratios[3 ], const char *_unit)
{
double denominator = find_stat(evsel, aggr_idx, denominator_type);
double ratio = 0 ;
enum metric_threshold_classify thresh = METRIC_THRESHOLD_UNKNOWN;
const char *fmt = NULL;
const char *unit = NULL;
if (numerator && denominator) {
ratio = numerator / denominator * 100 .0 ;
thresh = get_ratio_thresh(thresh_ratios, ratio);
fmt = "%7.2f%%" ;
unit = _unit;
}
out->print_metric(config, out->ctx, thresh, fmt, unit, ratio);
}
static void print_stalled_cycles_front(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double stalled,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {50 .0 , 30 .0 , 10 .0 };
print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, thresh_ratios,
"frontend cycles idle" );
}
static void print_stalled_cycles_back(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double stalled,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {75 .0 , 50 .0 , 20 .0 };
print_ratio(config, evsel, aggr_idx, stalled, out, STAT_CYCLES, thresh_ratios,
"backend cycles idle" );
}
static void print_branch_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_BRANCHES, thresh_ratios,
"of all branches" );
}
static void print_l1d_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_DCACHE, thresh_ratios,
"of all L1-dcache accesses" );
}
static void print_l1i_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_L1_ICACHE, thresh_ratios,
"of all L1-icache accesses" );
}
static void print_ll_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_LL_CACHE, thresh_ratios,
"of all LL-cache accesses" );
}
static void print_dtlb_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_DTLB_CACHE, thresh_ratios,
"of all dTLB cache accesses" );
}
static void print_itlb_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_ITLB_CACHE, thresh_ratios,
"of all iTLB cache accesses" );
}
static void print_cache_miss(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out)
{
const double thresh_ratios[3 ] = {20 .0 , 10 .0 , 5 .0 };
print_ratio(config, evsel, aggr_idx, misses, out, STAT_CACHE_REFS, thresh_ratios,
"of all cache refs" );
}
static void print_instructions(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double instructions,
struct perf_stat_output_ctx *out)
{
print_metric_t print_metric = out->print_metric;
void *ctxp = out->ctx;
double cycles = find_stat(evsel, aggr_idx, STAT_CYCLES);
double max_stalled = max(find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_FRONT),
find_stat(evsel, aggr_idx, STAT_STALLED_CYCLES_BACK));
if (cycles) {
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, "%7.2f " ,
"insn per cycle" , instructions / cycles);
} else {
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, /*fmt=*/NULL,
"insn per cycle" , 0 );
}
if (max_stalled && instructions) {
if (out->new_line)
out->new_line(config, ctxp);
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, "%7.2f " ,
"stalled cycles per insn" , max_stalled / instructions);
}
}
static void print_cycles(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double cycles,
struct perf_stat_output_ctx *out)
{
double nsecs = find_stat(evsel, aggr_idx, STAT_NSECS);
if (cycles && nsecs) {
double ratio = cycles / nsecs;
out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, "%8.3f" ,
"GHz" , ratio);
} else {
out->print_metric(config, out->ctx, METRIC_THRESHOLD_UNKNOWN, /*fmt=*/NULL,
"GHz" , 0 );
}
}
static void print_nsecs(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx __maybe_unused, double nsecs,
struct perf_stat_output_ctx *out)
{
print_metric_t print_metric = out->print_metric;
void *ctxp = out->ctx;
double wall_time = avg_stats(&walltime_nsecs_stats);
if (wall_time) {
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, "%8.3f" , "CPUs utilized" ,
nsecs / (wall_time * evsel->scale));
} else {
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, /*fmt=*/NULL,
"CPUs utilized" , 0 );
}
}
static int prepare_metric(const struct metric_expr *mexp,
const struct evsel *evsel,
struct expr_parse_ctx *pctx,
int aggr_idx)
{
struct evsel * const *metric_events = mexp->metric_events;
struct metric_ref *metric_refs = mexp->metric_refs;
int i;
for (i = 0 ; metric_events[i]; i++) {
char *n;
double val;
int source_count = 0 ;
if (evsel__is_tool(metric_events[i])) {
struct stats *stats;
double scale;
switch (evsel__tool_event(metric_events[i])) {
case TOOL_PMU__EVENT_DURATION_TIME:
stats = &walltime_nsecs_stats;
scale = 1 e-9 ;
break ;
case TOOL_PMU__EVENT_USER_TIME:
stats = &ru_stats.ru_utime_usec_stat;
scale = 1 e-6 ;
break ;
case TOOL_PMU__EVENT_SYSTEM_TIME:
stats = &ru_stats.ru_stime_usec_stat;
scale = 1 e-6 ;
break ;
case TOOL_PMU__EVENT_NONE:
pr_err("Invalid tool event 'none'" );
abort();
case TOOL_PMU__EVENT_MAX:
pr_err("Invalid tool event 'max'" );
abort();
case TOOL_PMU__EVENT_HAS_PMEM:
case TOOL_PMU__EVENT_NUM_CORES:
case TOOL_PMU__EVENT_NUM_CPUS:
case TOOL_PMU__EVENT_NUM_CPUS_ONLINE:
case TOOL_PMU__EVENT_NUM_DIES:
case TOOL_PMU__EVENT_NUM_PACKAGES:
case TOOL_PMU__EVENT_SLOTS:
case TOOL_PMU__EVENT_SMT_ON:
case TOOL_PMU__EVENT_SYSTEM_TSC_FREQ:
default :
pr_err("Unexpected tool event '%s'" , evsel__name(metric_events[i]));
abort();
}
val = avg_stats(stats) * scale;
source_count = 1 ;
} else {
struct perf_stat_evsel *ps = metric_events[i]->stats;
struct perf_stat_aggr *aggr;
/*
* If there are multiple uncore PMUs and we ' re not
* reading the leader ' s stats , determine the stats for
* the appropriate uncore PMU .
*/
if (evsel && evsel->metric_leader &&
evsel->pmu != evsel->metric_leader->pmu &&
mexp->metric_events[i]->pmu == evsel->metric_leader->pmu) {
struct evsel *pos;
evlist__for_each_entry(evsel->evlist, pos) {
if (pos->pmu != evsel->pmu)
continue ;
if (pos->metric_leader != mexp->metric_events[i])
continue ;
ps = pos->stats;
source_count = 1 ;
break ;
}
}
aggr = &ps->aggr[aggr_idx];
if (!aggr)
break ;
if (!metric_events[i]->supported) {
/*
* Not supported events will have a count of 0 ,
* which can be confusing in a
* metric . Explicitly set the value to NAN . Not
* counted events ( enable time of 0 ) are read as
* 0 .
*/
val = NAN;
source_count = 0 ;
} else {
val = aggr->counts.val;
if (!source_count)
source_count = evsel__source_count(metric_events[i]);
}
}
n = strdup(evsel__metric_id(metric_events[i]));
if (!n)
return -ENOMEM;
expr__add_id_val_source_count(pctx, n, val, source_count);
}
for (int j = 0 ; metric_refs && metric_refs[j].metric_name; j++) {
int ret = expr__add_ref(pctx, &metric_refs[j]);
if (ret)
return ret;
}
return i;
}
static void generic_metric(struct perf_stat_config *config,
struct metric_expr *mexp,
struct evsel *evsel,
int aggr_idx,
struct perf_stat_output_ctx *out)
{
print_metric_t print_metric = out->print_metric;
const char *metric_name = mexp->metric_name;
const char *metric_expr = mexp->metric_expr;
const char *metric_threshold = mexp->metric_threshold;
const char *metric_unit = mexp->metric_unit;
struct evsel * const *metric_events = mexp->metric_events;
int runtime = mexp->runtime;
struct expr_parse_ctx *pctx;
double ratio, scale, threshold;
int i;
void *ctxp = out->ctx;
enum metric_threshold_classify thresh = METRIC_THRESHOLD_UNKNOWN;
pctx = expr__ctx_new();
if (!pctx)
return ;
if (config->user_requested_cpu_list)
pctx->sctx.user_requested_cpu_list = strdup(config->user_requested_cpu_list);
pctx->sctx.runtime = runtime;
pctx->sctx.system_wide = config->system_wide;
i = prepare_metric(mexp, evsel, pctx, aggr_idx);
if (i < 0 ) {
expr__ctx_free(pctx);
return ;
}
if (!metric_events[i]) {
if (expr__parse(&ratio, pctx, metric_expr) == 0 ) {
char *unit;
char metric_bf[128 ];
if (metric_threshold &&
expr__parse(&threshold, pctx, metric_threshold) == 0 &&
!isnan(threshold)) {
thresh = fpclassify(threshold) == FP_ZERO
? METRIC_THRESHOLD_GOOD : METRIC_THRESHOLD_BAD;
}
if (metric_unit && metric_name) {
if (perf_pmu__convert_scale(metric_unit,
&unit, &scale) >= 0 ) {
ratio *= scale;
}
if (strstr(metric_expr, "?" ))
scnprintf(metric_bf, sizeof (metric_bf),
"%s %s_%d" , unit, metric_name, runtime);
else
scnprintf(metric_bf, sizeof (metric_bf),
"%s %s" , unit, metric_name);
print_metric(config, ctxp, thresh, "%8.1f" ,
metric_bf, ratio);
} else {
print_metric(config, ctxp, thresh, "%8.2f" ,
metric_name ?
metric_name :
out->force_header ? evsel->name : "" ,
ratio);
}
} else {
print_metric(config, ctxp, thresh, /*fmt=*/NULL,
out->force_header ?
(metric_name ?: evsel->name) : "" , 0 );
}
} else {
print_metric(config, ctxp, thresh, /*fmt=*/NULL,
out->force_header ?
(metric_name ?: evsel->name) : "" , 0 );
}
expr__ctx_free(pctx);
}
double test_generic_metric(struct metric_expr *mexp, int aggr_idx)
{
struct expr_parse_ctx *pctx;
double ratio = 0 .0 ;
pctx = expr__ctx_new();
if (!pctx)
return NAN;
if (prepare_metric(mexp, /*evsel=*/NULL, pctx, aggr_idx) < 0)
goto out;
if (expr__parse(&ratio, pctx, mexp->metric_expr))
ratio = 0 .0 ;
out:
expr__ctx_free(pctx);
return ratio;
}
static void perf_stat__print_metricgroup_header(struct perf_stat_config *config,
struct evsel *evsel,
void *ctxp,
const char *name,
struct perf_stat_output_ctx *out)
{
bool need_full_name = perf_pmus__num_core_pmus() > 1 ;
static const char *last_name;
static const struct perf_pmu *last_pmu;
char full_name[64 ];
/*
* A metricgroup may have several metric events ,
* e . g . , TopdownL1 on e - core of ADL .
* The name has been output by the first metric
* event . Only align with other metics from
* different metric events .
*/
if (last_name && !strcmp(last_name, name)) {
if (!need_full_name || last_pmu != evsel->pmu) {
out->print_metricgroup_header(config, ctxp, NULL);
return ;
}
}
if (need_full_name && evsel->pmu)
scnprintf(full_name, sizeof (full_name), "%s (%s)" , name, evsel->pmu->name);
else
scnprintf(full_name, sizeof (full_name), "%s" , name);
out->print_metricgroup_header(config, ctxp, full_name);
last_name = name;
last_pmu = evsel->pmu;
}
/**
* perf_stat__print_shadow_stats_metricgroup - Print out metrics associated with the evsel
* For the non - default , all metrics associated
* with the evsel are printed .
* For the default mode , only the metrics from
* the same metricgroup and the name of the
* metricgroup are printed . To print the metrics
* from the next metricgroup ( if available ) ,
* invoke the function with correspoinding
* metric_expr .
*/
void *perf_stat__print_shadow_stats_metricgroup(struct perf_stat_config *config,
struct evsel *evsel,
int aggr_idx,
int *num,
void *from,
struct perf_stat_output_ctx *out)
{
struct metric_event *me;
struct metric_expr *mexp = from;
void *ctxp = out->ctx;
bool header_printed = false ;
const char *name = NULL;
struct rblist *metric_events = &evsel->evlist->metric_events;
me = metricgroup__lookup(metric_events, evsel, false );
if (me == NULL)
return NULL;
if (!mexp)
mexp = list_first_entry(&me->head, typeof(*mexp), nd);
list_for_each_entry_from(mexp, &me->head, nd) {
/* Print the display name of the Default metricgroup */
if (!config->metric_only && me->is_default) {
if (!name)
name = mexp->default_metricgroup_name;
/*
* Two or more metricgroup may share the same metric
* event , e . g . , TopdownL1 and TopdownL2 on SPR .
* Return and print the prefix , e . g . , noise , running
* for the next metricgroup .
*/
if (strcmp(name, mexp->default_metricgroup_name))
return (void *)mexp;
/* Only print the name of the metricgroup once */
if (!header_printed) {
header_printed = true ;
perf_stat__print_metricgroup_header(config, evsel, ctxp,
name, out);
}
}
if ((*num)++ > 0 && out->new_line)
out->new_line(config, ctxp);
generic_metric(config, mexp, evsel, aggr_idx, out);
}
return NULL;
}
void perf_stat__print_shadow_stats(struct perf_stat_config *config,
struct evsel *evsel,
double avg, int aggr_idx,
struct perf_stat_output_ctx *out)
{
typedef void (*stat_print_function_t)(struct perf_stat_config *config,
const struct evsel *evsel,
int aggr_idx, double misses,
struct perf_stat_output_ctx *out);
static const stat_print_function_t stat_print_function[STAT_MAX] = {
[STAT_INSTRUCTIONS] = print_instructions,
[STAT_BRANCH_MISS] = print_branch_miss,
[STAT_L1D_MISS] = print_l1d_miss,
[STAT_L1I_MISS] = print_l1i_miss,
[STAT_DTLB_MISS] = print_dtlb_miss,
[STAT_ITLB_MISS] = print_itlb_miss,
[STAT_LL_MISS] = print_ll_miss,
[STAT_CACHE_MISSES] = print_cache_miss,
[STAT_STALLED_CYCLES_FRONT] = print_stalled_cycles_front,
[STAT_STALLED_CYCLES_BACK] = print_stalled_cycles_back,
[STAT_CYCLES] = print_cycles,
[STAT_NSECS] = print_nsecs,
};
print_metric_t print_metric = out->print_metric;
void *ctxp = out->ctx;
int num = 1 ;
if (config->iostat_run) {
iostat_print_metric(config, evsel, out);
} else {
stat_print_function_t fn = stat_print_function[evsel__stat_type(evsel)];
if (fn)
fn(config, evsel, aggr_idx, avg, out);
else {
double nsecs = find_stat(evsel, aggr_idx, STAT_NSECS);
if (nsecs) {
char unit = ' ' ;
char unit_buf[10 ] = "/sec" ;
double ratio = convert_unit_double(1000000000 .0 * avg / nsecs,
&unit);
if (unit != ' ' )
snprintf(unit_buf, sizeof (unit_buf), "%c/sec" , unit);
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN, "%8.3f" ,
unit_buf, ratio);
} else {
num = 0 ;
}
}
}
perf_stat__print_shadow_stats_metricgroup(config, evsel, aggr_idx,
&num, NULL, out);
if (num == 0 ) {
print_metric(config, ctxp, METRIC_THRESHOLD_UNKNOWN,
/*fmt=*/NULL, /*unit=*/NULL, 0);
}
}
/**
* perf_stat__skip_metric_event - Skip the evsel in the Default metricgroup ,
* if it ' s not running or not the metric event .
*/
bool perf_stat__skip_metric_event(struct evsel *evsel,
u64 ena, u64 run)
{
if (!evsel->default_metricgroup)
return false ;
if (!ena || !run)
return true ;
return !metricgroup__lookup(&evsel->evlist->metric_events, evsel, false );
}
Messung V0.5 in Prozent C=93 H=97 G=94
¤ Dauer der Verarbeitung: 0.9 Sekunden
¤
*© Formatika GbR, Deutschland