cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

block-info.c (12608B)


      1// SPDX-License-Identifier: GPL-2.0
      2#include <stdlib.h>
      3#include <string.h>
      4#include <linux/zalloc.h>
      5#include "block-info.h"
      6#include "sort.h"
      7#include "annotate.h"
      8#include "symbol.h"
      9#include "dso.h"
     10#include "map.h"
     11#include "srcline.h"
     12#include "evlist.h"
     13#include "hist.h"
     14#include "ui/browsers/hists.h"
     15
     16static struct block_header_column {
     17	const char *name;
     18	int width;
     19} block_columns[PERF_HPP_REPORT__BLOCK_MAX_INDEX] = {
     20	[PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT] = {
     21		.name = "Sampled Cycles%",
     22		.width = 15,
     23	},
     24	[PERF_HPP_REPORT__BLOCK_LBR_CYCLES] = {
     25		.name = "Sampled Cycles",
     26		.width = 14,
     27	},
     28	[PERF_HPP_REPORT__BLOCK_CYCLES_PCT] = {
     29		.name = "Avg Cycles%",
     30		.width = 11,
     31	},
     32	[PERF_HPP_REPORT__BLOCK_AVG_CYCLES] = {
     33		.name = "Avg Cycles",
     34		.width = 10,
     35	},
     36	[PERF_HPP_REPORT__BLOCK_RANGE] = {
     37		.name = "[Program Block Range]",
     38		.width = 70,
     39	},
     40	[PERF_HPP_REPORT__BLOCK_DSO] = {
     41		.name = "Shared Object",
     42		.width = 20,
     43	}
     44};
     45
     46struct block_info *block_info__get(struct block_info *bi)
     47{
     48	if (bi)
     49		refcount_inc(&bi->refcnt);
     50	return bi;
     51}
     52
     53void block_info__put(struct block_info *bi)
     54{
     55	if (bi && refcount_dec_and_test(&bi->refcnt))
     56		free(bi);
     57}
     58
     59struct block_info *block_info__new(void)
     60{
     61	struct block_info *bi = zalloc(sizeof(*bi));
     62
     63	if (bi)
     64		refcount_set(&bi->refcnt, 1);
     65	return bi;
     66}
     67
     68int64_t __block_info__cmp(struct hist_entry *left, struct hist_entry *right)
     69{
     70	struct block_info *bi_l = left->block_info;
     71	struct block_info *bi_r = right->block_info;
     72	int cmp;
     73
     74	if (!bi_l->sym || !bi_r->sym) {
     75		if (!bi_l->sym && !bi_r->sym)
     76			return -1;
     77		else if (!bi_l->sym)
     78			return -1;
     79		else
     80			return 1;
     81	}
     82
     83	cmp = strcmp(bi_l->sym->name, bi_r->sym->name);
     84	if (cmp)
     85		return cmp;
     86
     87	if (bi_l->start != bi_r->start)
     88		return (int64_t)(bi_r->start - bi_l->start);
     89
     90	return (int64_t)(bi_r->end - bi_l->end);
     91}
     92
     93int64_t block_info__cmp(struct perf_hpp_fmt *fmt __maybe_unused,
     94			struct hist_entry *left, struct hist_entry *right)
     95{
     96	return __block_info__cmp(left, right);
     97}
     98
     99static void init_block_info(struct block_info *bi, struct symbol *sym,
    100			    struct cyc_hist *ch, int offset,
    101			    u64 total_cycles)
    102{
    103	bi->sym = sym;
    104	bi->start = ch->start;
    105	bi->end = offset;
    106	bi->cycles = ch->cycles;
    107	bi->cycles_aggr = ch->cycles_aggr;
    108	bi->num = ch->num;
    109	bi->num_aggr = ch->num_aggr;
    110	bi->total_cycles = total_cycles;
    111
    112	memcpy(bi->cycles_spark, ch->cycles_spark,
    113	       NUM_SPARKS * sizeof(u64));
    114}
    115
    116int block_info__process_sym(struct hist_entry *he, struct block_hist *bh,
    117			    u64 *block_cycles_aggr, u64 total_cycles)
    118{
    119	struct annotation *notes;
    120	struct cyc_hist *ch;
    121	static struct addr_location al;
    122	u64 cycles = 0;
    123
    124	if (!he->ms.map || !he->ms.sym)
    125		return 0;
    126
    127	memset(&al, 0, sizeof(al));
    128	al.map = he->ms.map;
    129	al.sym = he->ms.sym;
    130
    131	notes = symbol__annotation(he->ms.sym);
    132	if (!notes || !notes->src || !notes->src->cycles_hist)
    133		return 0;
    134	ch = notes->src->cycles_hist;
    135	for (unsigned int i = 0; i < symbol__size(he->ms.sym); i++) {
    136		if (ch[i].num_aggr) {
    137			struct block_info *bi;
    138			struct hist_entry *he_block;
    139
    140			bi = block_info__new();
    141			if (!bi)
    142				return -1;
    143
    144			init_block_info(bi, he->ms.sym, &ch[i], i,
    145					total_cycles);
    146			cycles += bi->cycles_aggr / bi->num_aggr;
    147
    148			he_block = hists__add_entry_block(&bh->block_hists,
    149							  &al, bi);
    150			if (!he_block) {
    151				block_info__put(bi);
    152				return -1;
    153			}
    154		}
    155	}
    156
    157	if (block_cycles_aggr)
    158		*block_cycles_aggr += cycles;
    159
    160	return 0;
    161}
    162
    163static int block_column_header(struct perf_hpp_fmt *fmt,
    164			       struct perf_hpp *hpp,
    165			       struct hists *hists __maybe_unused,
    166			       int line __maybe_unused,
    167			       int *span __maybe_unused)
    168{
    169	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    170
    171	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
    172			 block_fmt->header);
    173}
    174
    175static int block_column_width(struct perf_hpp_fmt *fmt,
    176			      struct perf_hpp *hpp __maybe_unused,
    177			      struct hists *hists __maybe_unused)
    178{
    179	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    180
    181	return block_fmt->width;
    182}
    183
    184static int color_pct(struct perf_hpp *hpp, int width, double pct)
    185{
    186#ifdef HAVE_SLANG_SUPPORT
    187	if (use_browser) {
    188		return __hpp__slsmg_color_printf(hpp, "%*.2f%%",
    189						 width - 1, pct);
    190	}
    191#endif
    192	return hpp_color_scnprintf(hpp, "%*.2f%%", width - 1, pct);
    193}
    194
    195static int block_total_cycles_pct_entry(struct perf_hpp_fmt *fmt,
    196					struct perf_hpp *hpp,
    197					struct hist_entry *he)
    198{
    199	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    200	struct block_info *bi = he->block_info;
    201	double ratio = 0.0;
    202
    203	if (block_fmt->total_cycles)
    204		ratio = (double)bi->cycles_aggr / (double)block_fmt->total_cycles;
    205
    206	return color_pct(hpp, block_fmt->width, 100.0 * ratio);
    207}
    208
    209static int64_t block_total_cycles_pct_sort(struct perf_hpp_fmt *fmt,
    210					   struct hist_entry *left,
    211					   struct hist_entry *right)
    212{
    213	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    214	struct block_info *bi_l = left->block_info;
    215	struct block_info *bi_r = right->block_info;
    216	double l, r;
    217
    218	if (block_fmt->total_cycles) {
    219		l = ((double)bi_l->cycles_aggr /
    220			(double)block_fmt->total_cycles) * 100000.0;
    221		r = ((double)bi_r->cycles_aggr /
    222			(double)block_fmt->total_cycles) * 100000.0;
    223		return (int64_t)l - (int64_t)r;
    224	}
    225
    226	return 0;
    227}
    228
    229static void cycles_string(u64 cycles, char *buf, int size)
    230{
    231	if (cycles >= 1000000)
    232		scnprintf(buf, size, "%.1fM", (double)cycles / 1000000.0);
    233	else if (cycles >= 1000)
    234		scnprintf(buf, size, "%.1fK", (double)cycles / 1000.0);
    235	else
    236		scnprintf(buf, size, "%1d", cycles);
    237}
    238
    239static int block_cycles_lbr_entry(struct perf_hpp_fmt *fmt,
    240				  struct perf_hpp *hpp, struct hist_entry *he)
    241{
    242	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    243	struct block_info *bi = he->block_info;
    244	char cycles_buf[16];
    245
    246	cycles_string(bi->cycles_aggr, cycles_buf, sizeof(cycles_buf));
    247
    248	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
    249			 cycles_buf);
    250}
    251
    252static int block_cycles_pct_entry(struct perf_hpp_fmt *fmt,
    253				  struct perf_hpp *hpp, struct hist_entry *he)
    254{
    255	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    256	struct block_info *bi = he->block_info;
    257	double ratio = 0.0;
    258	u64 avg;
    259
    260	if (block_fmt->block_cycles && bi->num_aggr) {
    261		avg = bi->cycles_aggr / bi->num_aggr;
    262		ratio = (double)avg / (double)block_fmt->block_cycles;
    263	}
    264
    265	return color_pct(hpp, block_fmt->width, 100.0 * ratio);
    266}
    267
    268static int block_avg_cycles_entry(struct perf_hpp_fmt *fmt,
    269				  struct perf_hpp *hpp,
    270				  struct hist_entry *he)
    271{
    272	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    273	struct block_info *bi = he->block_info;
    274	char cycles_buf[16];
    275
    276	cycles_string(bi->cycles_aggr / bi->num_aggr, cycles_buf,
    277		      sizeof(cycles_buf));
    278
    279	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
    280			 cycles_buf);
    281}
    282
    283static int block_range_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    284			     struct hist_entry *he)
    285{
    286	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    287	struct block_info *bi = he->block_info;
    288	char buf[128];
    289	char *start_line, *end_line;
    290
    291	symbol_conf.disable_add2line_warn = true;
    292
    293	start_line = map__srcline(he->ms.map, bi->sym->start + bi->start,
    294				  he->ms.sym);
    295
    296	end_line = map__srcline(he->ms.map, bi->sym->start + bi->end,
    297				he->ms.sym);
    298
    299	if ((strncmp(start_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0) &&
    300	    (strncmp(end_line, SRCLINE_UNKNOWN, strlen(SRCLINE_UNKNOWN)) != 0)) {
    301		scnprintf(buf, sizeof(buf), "[%s -> %s]",
    302			  start_line, end_line);
    303	} else {
    304		scnprintf(buf, sizeof(buf), "[%7lx -> %7lx]",
    305			  bi->start, bi->end);
    306	}
    307
    308	free_srcline(start_line);
    309	free_srcline(end_line);
    310
    311	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width, buf);
    312}
    313
    314static int block_dso_entry(struct perf_hpp_fmt *fmt, struct perf_hpp *hpp,
    315			   struct hist_entry *he)
    316{
    317	struct block_fmt *block_fmt = container_of(fmt, struct block_fmt, fmt);
    318	struct map *map = he->ms.map;
    319
    320	if (map && map->dso) {
    321		return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
    322				 map->dso->short_name);
    323	}
    324
    325	return scnprintf(hpp->buf, hpp->size, "%*s", block_fmt->width,
    326			 "[unknown]");
    327}
    328
    329static void init_block_header(struct block_fmt *block_fmt)
    330{
    331	struct perf_hpp_fmt *fmt = &block_fmt->fmt;
    332
    333	BUG_ON(block_fmt->idx >= PERF_HPP_REPORT__BLOCK_MAX_INDEX);
    334
    335	block_fmt->header = block_columns[block_fmt->idx].name;
    336	block_fmt->width = block_columns[block_fmt->idx].width;
    337
    338	fmt->header = block_column_header;
    339	fmt->width = block_column_width;
    340}
    341
    342static void hpp_register(struct block_fmt *block_fmt, int idx,
    343			 struct perf_hpp_list *hpp_list)
    344{
    345	struct perf_hpp_fmt *fmt = &block_fmt->fmt;
    346
    347	block_fmt->idx = idx;
    348	INIT_LIST_HEAD(&fmt->list);
    349	INIT_LIST_HEAD(&fmt->sort_list);
    350
    351	switch (idx) {
    352	case PERF_HPP_REPORT__BLOCK_TOTAL_CYCLES_PCT:
    353		fmt->color = block_total_cycles_pct_entry;
    354		fmt->cmp = block_info__cmp;
    355		fmt->sort = block_total_cycles_pct_sort;
    356		break;
    357	case PERF_HPP_REPORT__BLOCK_LBR_CYCLES:
    358		fmt->entry = block_cycles_lbr_entry;
    359		break;
    360	case PERF_HPP_REPORT__BLOCK_CYCLES_PCT:
    361		fmt->color = block_cycles_pct_entry;
    362		break;
    363	case PERF_HPP_REPORT__BLOCK_AVG_CYCLES:
    364		fmt->entry = block_avg_cycles_entry;
    365		break;
    366	case PERF_HPP_REPORT__BLOCK_RANGE:
    367		fmt->entry = block_range_entry;
    368		break;
    369	case PERF_HPP_REPORT__BLOCK_DSO:
    370		fmt->entry = block_dso_entry;
    371		break;
    372	default:
    373		return;
    374	}
    375
    376	init_block_header(block_fmt);
    377	perf_hpp_list__column_register(hpp_list, fmt);
    378}
    379
    380static void register_block_columns(struct perf_hpp_list *hpp_list,
    381				   struct block_fmt *block_fmts,
    382				   int *block_hpps, int nr_hpps)
    383{
    384	for (int i = 0; i < nr_hpps; i++)
    385		hpp_register(&block_fmts[i], block_hpps[i], hpp_list);
    386}
    387
    388static void init_block_hist(struct block_hist *bh, struct block_fmt *block_fmts,
    389			    int *block_hpps, int nr_hpps)
    390{
    391	__hists__init(&bh->block_hists, &bh->block_list);
    392	perf_hpp_list__init(&bh->block_list);
    393	bh->block_list.nr_header_lines = 1;
    394
    395	register_block_columns(&bh->block_list, block_fmts,
    396			       block_hpps, nr_hpps);
    397
    398	/* Sort by the first fmt */
    399	perf_hpp_list__register_sort_field(&bh->block_list, &block_fmts[0].fmt);
    400}
    401
    402static int process_block_report(struct hists *hists,
    403				struct block_report *block_report,
    404				u64 total_cycles, int *block_hpps,
    405				int nr_hpps)
    406{
    407	struct rb_node *next = rb_first_cached(&hists->entries);
    408	struct block_hist *bh = &block_report->hist;
    409	struct hist_entry *he;
    410
    411	if (nr_hpps > PERF_HPP_REPORT__BLOCK_MAX_INDEX)
    412		return -1;
    413
    414	block_report->nr_fmts = nr_hpps;
    415	init_block_hist(bh, block_report->fmts, block_hpps, nr_hpps);
    416
    417	while (next) {
    418		he = rb_entry(next, struct hist_entry, rb_node);
    419		block_info__process_sym(he, bh, &block_report->cycles,
    420					total_cycles);
    421		next = rb_next(&he->rb_node);
    422	}
    423
    424	for (int i = 0; i < nr_hpps; i++) {
    425		block_report->fmts[i].total_cycles = total_cycles;
    426		block_report->fmts[i].block_cycles = block_report->cycles;
    427	}
    428
    429	hists__output_resort(&bh->block_hists, NULL);
    430	return 0;
    431}
    432
    433struct block_report *block_info__create_report(struct evlist *evlist,
    434					       u64 total_cycles,
    435					       int *block_hpps, int nr_hpps,
    436					       int *nr_reps)
    437{
    438	struct block_report *block_reports;
    439	int nr_hists = evlist->core.nr_entries, i = 0;
    440	struct evsel *pos;
    441
    442	block_reports = calloc(nr_hists, sizeof(struct block_report));
    443	if (!block_reports)
    444		return NULL;
    445
    446	evlist__for_each_entry(evlist, pos) {
    447		struct hists *hists = evsel__hists(pos);
    448
    449		process_block_report(hists, &block_reports[i], total_cycles,
    450				     block_hpps, nr_hpps);
    451		i++;
    452	}
    453
    454	*nr_reps = nr_hists;
    455	return block_reports;
    456}
    457
    458void block_info__free_report(struct block_report *reps, int nr_reps)
    459{
    460	for (int i = 0; i < nr_reps; i++)
    461		hists__delete_entries(&reps[i].hist.block_hists);
    462
    463	free(reps);
    464}
    465
    466int report__browse_block_hists(struct block_hist *bh, float min_percent,
    467			       struct evsel *evsel, struct perf_env *env,
    468			       struct annotation_options *annotation_opts)
    469{
    470	int ret;
    471
    472	switch (use_browser) {
    473	case 0:
    474		symbol_conf.report_individual_block = true;
    475		hists__fprintf(&bh->block_hists, true, 0, 0, min_percent,
    476			       stdout, true);
    477		return 0;
    478	case 1:
    479		symbol_conf.report_individual_block = true;
    480		ret = block_hists_tui_browse(bh, evsel, min_percent,
    481					     env, annotation_opts);
    482		return ret;
    483	default:
    484		return -1;
    485	}
    486
    487	return 0;
    488}
    489
    490float block_info__total_cycles_percent(struct hist_entry *he)
    491{
    492	struct block_info *bi = he->block_info;
    493
    494	if (bi->total_cycles)
    495		return bi->cycles * 100.0 / bi->total_cycles;
    496
    497	return 0.0;
    498}