mirror of
https://github.com/torvalds/linux.git
synced 2024-12-30 14:52:05 +00:00
perf/core improvements and fixes:
User visible: - Introduce 'srcfile' sort key: (Andi Kleen) # perf record -F 10000 usleep 1 # perf report --stdio --dsos '[kernel.vmlinux]' -s srcfile <SNIP> # Overhead Source File 26.49% copy_page_64.S 5.49% signal.c 0.51% msr.h # It can be combined with other fields, for instance, experiment with '-s srcfile,symbol'. There are some oddities in some distros and with some specific DSOs, being investigated, so your mileage may vary. - Update the column width for the "srcline" sort key (Arnaldo Carvalho de Melo) - Support per-event 'freq' term: (Namhyung Kim) $ perf record -e 'cpu/instructions,freq=1234/',cycles -c 1000 sleep 1 $ perf evlist -F cpu/instructions,freq=1234/: sample_freq=1234 cycles: sample_period=1000 $ Infrastructure: - Move perf_counts struct and functions into separate object (Jiri Olsa) - Unset perf_event_attr::freq when period term is set (Jiri Olsa) - Move callchain option parsing code to util.c (Kan Liang) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJVyQ9wAAoJENZQFvNTUqpAItAP/A7pmI5VtOjute3uHQ8BgbiG IRlmNBobYG9ds7Z0llgFtN0IpjSBpsyvoAt6O7X68j+h53/w2dxuiiKeiOQbt0K6 w2nM3stXv9k67CLLS70qCJ9tHYE0zY7YDp7iw3hAMo4Vs/wqwezh85ksDkxv6TDR q8bKOK7jXnBDU6I6XiWb1V2pHVfFgdE7sPt0Oo0EXXT6wzm6EeM7PwcdI2ag5PLf zg++5DCrjwdrDD/0+F2gzQFpmGx0UrNMg1MP+bwh9woJ9d974Y5rKspPtWGnLqDE HKoKTeyroD4FmnFrRXL75CMJm1Ln6Pa+EB/Cgsm5/4ZeUovd31RKjVwikgziS2fv 80KU8bt+MK0qxiVYSicKORq/3HerW9Y/Cx/9lP8vTbyBvya8MqfthFjwoqCtUl1M tacjCGG4dbBq7hgOs3WwKbrC0dEa1uTL3XpXCq8dFGhzY7OKs88DF5pKxC6epAxy Q9IhvDPPAW5tSPrUWJVs5dXbxx6vDuytVH0aZJc7sthvAWoxLaZZeR/O84uVZt27 5mV5QWLbVsIEA9FJk16bAwtu+i3bk/GcQVGtEpxwehyP7gEW1mQyTYuXgySY+qBm hEmMmekCaNOIXPZXjULu7m3YNU23tTkgU/eGXKzF+8Zdr0DJsMyP6IXF/TT37CM/ JSCs5/4xe0R9govqlTGj =JSPY -----END PGP SIGNATURE----- Merge tag 'perf-core-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/core Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo: User visible changes: - Introduce 'srcfile' sort key: (Andi Kleen) # perf record -F 10000 usleep 1 # perf report --stdio --dsos '[kernel.vmlinux]' -s srcfile <SNIP> # Overhead Source File 26.49% copy_page_64.S 5.49% signal.c 0.51% msr.h # It can be combined with other fields, for instance, experiment with '-s srcfile,symbol'. There are some oddities in some distros and with some specific DSOs, being investigated, so your mileage may vary. - Update the column width for the "srcline" sort key (Arnaldo Carvalho de Melo) - Support per-event 'freq' term: (Namhyung Kim) $ perf record -e 'cpu/instructions,freq=1234/',cycles -c 1000 sleep 1 $ perf evlist -F cpu/instructions,freq=1234/: sample_freq=1234 cycles: sample_period=1000 $ Infrastructure changes: - Move perf_counts struct and functions into separate object (Jiri Olsa) - Unset perf_event_attr::freq when period term is set (Jiri Olsa) - Move callchain option parsing code to util.c (Kan Liang) Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
commit
5f1230c9b8
@ -49,6 +49,7 @@ OPTIONS
|
||||
These params can be used to overload default config values per event.
|
||||
Here is a list of the params.
|
||||
- 'period': Set event sampling period
|
||||
- 'freq': Set event sampling frequency
|
||||
- 'time': Disable/enable time stamping. Acceptable values are 1 for
|
||||
enabling time stamping. 0 for disabling time stamping.
|
||||
The default is 1.
|
||||
|
@ -81,6 +81,8 @@ OPTIONS
|
||||
- cpu: cpu number the task ran at the time of sample
|
||||
- srcline: filename and line number executed at the time of sample. The
|
||||
DWARF debugging info must be provided.
|
||||
- srcfile: file name of the source file of the same. Requires dwarf
|
||||
information.
|
||||
- weight: Event specific weight, e.g. memory latency or transaction
|
||||
abort cost. This is the global weight.
|
||||
- local_weight: Local weight version of the weight above.
|
||||
@ -354,6 +356,8 @@ OPTIONS
|
||||
|
||||
To disable decoding entirely, use --no-itrace.
|
||||
|
||||
--full-source-path::
|
||||
Show the full path for source files for srcline output.
|
||||
|
||||
include::callchain-overhead-calculation.txt[]
|
||||
|
||||
|
@ -260,6 +260,9 @@ OPTIONS
|
||||
|
||||
To disable decoding entirely, use --no-itrace.
|
||||
|
||||
--full-source-path::
|
||||
Show the full path for source files for srcline output.
|
||||
|
||||
SEE ALSO
|
||||
--------
|
||||
linkperf:perf-record[1], linkperf:perf-script-perl[1],
|
||||
|
@ -738,6 +738,8 @@ int cmd_report(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
|
||||
"Instruction Tracing options",
|
||||
itrace_parse_synth_opts),
|
||||
OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename,
|
||||
"Show full source file name path for source lines"),
|
||||
OPT_END()
|
||||
};
|
||||
struct perf_data_file file = {
|
||||
|
@ -1653,6 +1653,8 @@ int cmd_script(int argc, const char **argv, const char *prefix __maybe_unused)
|
||||
OPT_CALLBACK_OPTARG(0, "itrace", &itrace_synth_opts, NULL, "opts",
|
||||
"Instruction Tracing options",
|
||||
itrace_parse_synth_opts),
|
||||
OPT_BOOLEAN(0, "full-source-path", &srcline_full_filename,
|
||||
"Show full source file name path for source lines"),
|
||||
OPT_END()
|
||||
};
|
||||
const char * const script_subcommands[] = { "record", "report", NULL };
|
||||
|
@ -58,6 +58,7 @@
|
||||
#include "util/cpumap.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/thread_map.h"
|
||||
#include "util/counts.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/prctl.h>
|
||||
|
@ -68,6 +68,7 @@ libperf-y += target.o
|
||||
libperf-y += rblist.o
|
||||
libperf-y += intlist.o
|
||||
libperf-y += vdso.o
|
||||
libperf-y += counts.o
|
||||
libperf-y += stat.o
|
||||
libperf-y += stat-shadow.o
|
||||
libperf-y += record.o
|
||||
|
@ -25,96 +25,9 @@
|
||||
|
||||
__thread struct callchain_cursor callchain_cursor;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
static int get_stack_size(const char *str, unsigned long *_size)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long size;
|
||||
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
|
||||
|
||||
size = strtoul(str, &endptr, 0);
|
||||
|
||||
do {
|
||||
if (*endptr)
|
||||
break;
|
||||
|
||||
size = round_up(size, sizeof(u64));
|
||||
if (!size || size > max_size)
|
||||
break;
|
||||
|
||||
*_size = size;
|
||||
return 0;
|
||||
|
||||
} while (0);
|
||||
|
||||
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
|
||||
max_size, str);
|
||||
return -1;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
|
||||
int parse_callchain_record_opt(const char *arg, struct callchain_param *param)
|
||||
{
|
||||
char *tok, *name, *saveptr = NULL;
|
||||
char *buf;
|
||||
int ret = -1;
|
||||
|
||||
/* We need buffer that we know we can write to. */
|
||||
buf = malloc(strlen(arg) + 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(buf, arg);
|
||||
|
||||
tok = strtok_r((char *)buf, ",", &saveptr);
|
||||
name = tok ? : (char *)buf;
|
||||
|
||||
do {
|
||||
/* Framepointer style */
|
||||
if (!strncmp(name, "fp", sizeof("fp"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
param->record_mode = CALLCHAIN_FP;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for --call-graph fp\n");
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
/* Dwarf style */
|
||||
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
param->record_mode = CALLCHAIN_DWARF;
|
||||
param->dump_size = default_stack_dump_size;
|
||||
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
if (tok) {
|
||||
unsigned long size = 0;
|
||||
|
||||
ret = get_stack_size(tok, &size);
|
||||
param->dump_size = size;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
} else if (!strncmp(name, "lbr", sizeof("lbr"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
param->record_mode = CALLCHAIN_LBR;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for --call-graph lbr\n");
|
||||
break;
|
||||
} else {
|
||||
pr_err("callchain: Unknown --call-graph option "
|
||||
"value: %s\n", arg);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
return parse_callchain_record(arg, param);
|
||||
}
|
||||
|
||||
static int parse_callchain_mode(const char *value)
|
||||
|
@ -177,6 +177,7 @@ int fill_callchain_info(struct addr_location *al, struct callchain_cursor_node *
|
||||
bool hide_unresolved);
|
||||
|
||||
extern const char record_callchain_help[];
|
||||
extern int parse_callchain_record(const char *arg, struct callchain_param *param);
|
||||
int parse_callchain_record_opt(const char *arg, struct callchain_param *param);
|
||||
int parse_callchain_report_opt(const char *arg);
|
||||
int perf_callchain_config(const char *var, const char *value);
|
||||
|
52
tools/perf/util/counts.c
Normal file
52
tools/perf/util/counts.c
Normal file
@ -0,0 +1,52 @@
|
||||
#include <stdlib.h>
|
||||
#include "evsel.h"
|
||||
#include "counts.h"
|
||||
|
||||
struct perf_counts *perf_counts__new(int ncpus, int nthreads)
|
||||
{
|
||||
struct perf_counts *counts = zalloc(sizeof(*counts));
|
||||
|
||||
if (counts) {
|
||||
struct xyarray *values;
|
||||
|
||||
values = xyarray__new(ncpus, nthreads, sizeof(struct perf_counts_values));
|
||||
if (!values) {
|
||||
free(counts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
counts->values = values;
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
void perf_counts__delete(struct perf_counts *counts)
|
||||
{
|
||||
if (counts) {
|
||||
xyarray__delete(counts->values);
|
||||
free(counts);
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_counts__reset(struct perf_counts *counts)
|
||||
{
|
||||
xyarray__reset(counts->values);
|
||||
}
|
||||
|
||||
void perf_evsel__reset_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_counts__reset(evsel->counts);
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
evsel->counts = perf_counts__new(ncpus, nthreads);
|
||||
return evsel->counts != NULL ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_counts__delete(evsel->counts);
|
||||
evsel->counts = NULL;
|
||||
}
|
37
tools/perf/util/counts.h
Normal file
37
tools/perf/util/counts.h
Normal file
@ -0,0 +1,37 @@
|
||||
#ifndef __PERF_COUNTS_H
|
||||
#define __PERF_COUNTS_H
|
||||
|
||||
#include "xyarray.h"
|
||||
|
||||
struct perf_counts_values {
|
||||
union {
|
||||
struct {
|
||||
u64 val;
|
||||
u64 ena;
|
||||
u64 run;
|
||||
};
|
||||
u64 values[3];
|
||||
};
|
||||
};
|
||||
|
||||
struct perf_counts {
|
||||
s8 scaled;
|
||||
struct perf_counts_values aggr;
|
||||
struct xyarray *values;
|
||||
};
|
||||
|
||||
|
||||
static inline struct perf_counts_values*
|
||||
perf_counts(struct perf_counts *counts, int cpu, int thread)
|
||||
{
|
||||
return xyarray__entry(counts->values, cpu, thread);
|
||||
}
|
||||
|
||||
struct perf_counts *perf_counts__new(int ncpus, int nthreads);
|
||||
void perf_counts__delete(struct perf_counts *counts);
|
||||
|
||||
void perf_evsel__reset_counts(struct perf_evsel *evsel);
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel);
|
||||
|
||||
#endif /* __PERF_COUNTS_H */
|
@ -598,6 +598,11 @@ static void apply_config_terms(struct perf_evsel *evsel)
|
||||
switch (term->type) {
|
||||
case PERF_EVSEL__CONFIG_TERM_PERIOD:
|
||||
attr->sample_period = term->val.period;
|
||||
attr->freq = 0;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_FREQ:
|
||||
attr->sample_freq = term->val.freq;
|
||||
attr->freq = 1;
|
||||
break;
|
||||
case PERF_EVSEL__CONFIG_TERM_TIME:
|
||||
if (term->val.time)
|
||||
@ -2153,8 +2158,13 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
printed += perf_event_attr__fprintf(fp, &evsel->attr,
|
||||
__print_attr__fprintf, &first);
|
||||
} else if (details->freq) {
|
||||
printed += comma_fprintf(fp, &first, " sample_freq=%" PRIu64,
|
||||
(u64)evsel->attr.sample_freq);
|
||||
const char *term = "sample_freq";
|
||||
|
||||
if (!evsel->attr.freq)
|
||||
term = "sample_period";
|
||||
|
||||
printed += comma_fprintf(fp, &first, " %s=%" PRIu64,
|
||||
term, (u64)evsel->attr.sample_freq);
|
||||
}
|
||||
out:
|
||||
fputc('\n', fp);
|
||||
|
@ -9,7 +9,7 @@
|
||||
#include "xyarray.h"
|
||||
#include "symbol.h"
|
||||
#include "cpumap.h"
|
||||
#include "stat.h"
|
||||
#include "counts.h"
|
||||
|
||||
struct perf_evsel;
|
||||
|
||||
@ -39,6 +39,7 @@ struct cgroup_sel;
|
||||
*/
|
||||
enum {
|
||||
PERF_EVSEL__CONFIG_TERM_PERIOD,
|
||||
PERF_EVSEL__CONFIG_TERM_FREQ,
|
||||
PERF_EVSEL__CONFIG_TERM_TIME,
|
||||
PERF_EVSEL__CONFIG_TERM_MAX,
|
||||
};
|
||||
@ -48,6 +49,7 @@ struct perf_evsel_config_term {
|
||||
int type;
|
||||
union {
|
||||
u64 period;
|
||||
u64 freq;
|
||||
bool time;
|
||||
} val;
|
||||
};
|
||||
|
@ -151,6 +151,12 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)
|
||||
hists__new_col_len(hists, HISTC_LOCAL_WEIGHT, 12);
|
||||
hists__new_col_len(hists, HISTC_GLOBAL_WEIGHT, 12);
|
||||
|
||||
if (h->srcline)
|
||||
hists__new_col_len(hists, HISTC_SRCLINE, strlen(h->srcline));
|
||||
|
||||
if (h->srcfile)
|
||||
hists__new_col_len(hists, HISTC_SRCFILE, strlen(h->srcfile));
|
||||
|
||||
if (h->transaction)
|
||||
hists__new_col_len(hists, HISTC_TRANSACTION,
|
||||
hist_entry__transaction_len());
|
||||
@ -761,6 +767,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
||||
struct hist_entry **he_cache = iter->priv;
|
||||
struct hist_entry *he;
|
||||
struct hist_entry he_tmp = {
|
||||
.hists = evsel__hists(evsel),
|
||||
.cpu = al->cpu,
|
||||
.thread = al->thread,
|
||||
.comm = thread__comm(al->thread),
|
||||
@ -945,6 +952,8 @@ void hist_entry__delete(struct hist_entry *he)
|
||||
|
||||
zfree(&he->stat_acc);
|
||||
free_srcline(he->srcline);
|
||||
if (he->srcfile && he->srcfile[0])
|
||||
free(he->srcfile);
|
||||
free_callchain(he->callchain);
|
||||
free(he);
|
||||
}
|
||||
|
@ -30,6 +30,7 @@ enum hist_column {
|
||||
HISTC_PARENT,
|
||||
HISTC_CPU,
|
||||
HISTC_SRCLINE,
|
||||
HISTC_SRCFILE,
|
||||
HISTC_MISPREDICT,
|
||||
HISTC_IN_TX,
|
||||
HISTC_ABORT,
|
||||
|
@ -597,6 +597,9 @@ do { \
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
CHECK_TYPE_VAL(NUM);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
|
||||
CHECK_TYPE_VAL(NUM);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE:
|
||||
/*
|
||||
* TODO uncomment when the field is available
|
||||
@ -659,6 +662,9 @@ do { \
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD:
|
||||
ADD_CONFIG_TERM(PERIOD, period, term->val.num);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ:
|
||||
ADD_CONFIG_TERM(FREQ, freq, term->val.num);
|
||||
break;
|
||||
case PARSE_EVENTS__TERM_TYPE_TIME:
|
||||
ADD_CONFIG_TERM(TIME, time, term->val.num);
|
||||
break;
|
||||
|
@ -62,6 +62,7 @@ enum {
|
||||
PARSE_EVENTS__TERM_TYPE_CONFIG2,
|
||||
PARSE_EVENTS__TERM_TYPE_NAME,
|
||||
PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD,
|
||||
PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ,
|
||||
PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE,
|
||||
PARSE_EVENTS__TERM_TYPE_TIME,
|
||||
};
|
||||
|
@ -182,6 +182,7 @@ config1 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG1); }
|
||||
config2 { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_CONFIG2); }
|
||||
name { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_NAME); }
|
||||
period { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD); }
|
||||
freq { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_SAMPLE_FREQ); }
|
||||
branch_type { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_BRANCH_SAMPLE_TYPE); }
|
||||
time { return term(yyscanner, PARSE_EVENTS__TERM_TYPE_TIME); }
|
||||
, { return ','; }
|
||||
|
@ -634,7 +634,7 @@ static char *formats_error_string(struct list_head *formats)
|
||||
{
|
||||
struct perf_pmu_format *format;
|
||||
char *err, *str;
|
||||
static const char *static_terms = "config,config1,config2,name,period,branch_type,time\n";
|
||||
static const char *static_terms = "config,config1,config2,name,period,freq,branch_type,time\n";
|
||||
unsigned i = 0;
|
||||
|
||||
if (!asprintf(&str, "valid terms:"))
|
||||
|
@ -16,7 +16,7 @@ util/util.c
|
||||
util/xyarray.c
|
||||
util/cgroup.c
|
||||
util/rblist.c
|
||||
util/stat.c
|
||||
util/counts.c
|
||||
util/strlist.c
|
||||
util/trace-event.c
|
||||
../lib/rbtree.c
|
||||
|
@ -319,6 +319,57 @@ struct sort_entry sort_srcline = {
|
||||
.se_width_idx = HISTC_SRCLINE,
|
||||
};
|
||||
|
||||
/* --sort srcfile */
|
||||
|
||||
static char no_srcfile[1];
|
||||
|
||||
static char *get_srcfile(struct hist_entry *e)
|
||||
{
|
||||
char *sf, *p;
|
||||
struct map *map = e->ms.map;
|
||||
|
||||
sf = get_srcline(map->dso, map__rip_2objdump(map, e->ip),
|
||||
e->ms.sym, true);
|
||||
p = strchr(sf, ':');
|
||||
if (p && *sf) {
|
||||
*p = 0;
|
||||
return sf;
|
||||
}
|
||||
free(sf);
|
||||
return no_srcfile;
|
||||
}
|
||||
|
||||
static int64_t
|
||||
sort__srcfile_cmp(struct hist_entry *left, struct hist_entry *right)
|
||||
{
|
||||
if (!left->srcfile) {
|
||||
if (!left->ms.map)
|
||||
left->srcfile = no_srcfile;
|
||||
else
|
||||
left->srcfile = get_srcfile(left);
|
||||
}
|
||||
if (!right->srcfile) {
|
||||
if (!right->ms.map)
|
||||
right->srcfile = no_srcfile;
|
||||
else
|
||||
right->srcfile = get_srcfile(right);
|
||||
}
|
||||
return strcmp(right->srcfile, left->srcfile);
|
||||
}
|
||||
|
||||
static int hist_entry__srcfile_snprintf(struct hist_entry *he, char *bf,
|
||||
size_t size, unsigned int width)
|
||||
{
|
||||
return repsep_snprintf(bf, size, "%-*.*s", width, width, he->srcfile);
|
||||
}
|
||||
|
||||
struct sort_entry sort_srcfile = {
|
||||
.se_header = "Source File",
|
||||
.se_cmp = sort__srcfile_cmp,
|
||||
.se_snprintf = hist_entry__srcfile_snprintf,
|
||||
.se_width_idx = HISTC_SRCFILE,
|
||||
};
|
||||
|
||||
/* --sort parent */
|
||||
|
||||
static int64_t
|
||||
@ -1196,6 +1247,7 @@ static struct sort_dimension common_sort_dimensions[] = {
|
||||
DIM(SORT_PARENT, "parent", sort_parent),
|
||||
DIM(SORT_CPU, "cpu", sort_cpu),
|
||||
DIM(SORT_SRCLINE, "srcline", sort_srcline),
|
||||
DIM(SORT_SRCFILE, "srcfile", sort_srcfile),
|
||||
DIM(SORT_LOCAL_WEIGHT, "local_weight", sort_local_weight),
|
||||
DIM(SORT_GLOBAL_WEIGHT, "weight", sort_global_weight),
|
||||
DIM(SORT_TRANSACTION, "transaction", sort_transaction),
|
||||
|
@ -114,6 +114,7 @@ struct hist_entry {
|
||||
};
|
||||
};
|
||||
char *srcline;
|
||||
char *srcfile;
|
||||
struct symbol *parent;
|
||||
struct rb_root sorted_chain;
|
||||
struct branch_info *branch_info;
|
||||
@ -172,6 +173,7 @@ enum sort_type {
|
||||
SORT_PARENT,
|
||||
SORT_CPU,
|
||||
SORT_SRCLINE,
|
||||
SORT_SRCFILE,
|
||||
SORT_LOCAL_WEIGHT,
|
||||
SORT_GLOBAL_WEIGHT,
|
||||
SORT_TRANSACTION,
|
||||
|
@ -10,6 +10,8 @@
|
||||
|
||||
#include "symbol.h"
|
||||
|
||||
bool srcline_full_filename;
|
||||
|
||||
#ifdef HAVE_LIBBFD_SUPPORT
|
||||
|
||||
/*
|
||||
@ -277,7 +279,9 @@ char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
if (!addr2line(dso_name, addr, &file, &line, dso))
|
||||
goto out;
|
||||
|
||||
if (asprintf(&srcline, "%s:%u", basename(file), line) < 0) {
|
||||
if (asprintf(&srcline, "%s:%u",
|
||||
srcline_full_filename ? file : basename(file),
|
||||
line) < 0) {
|
||||
free(file);
|
||||
goto out;
|
||||
}
|
||||
|
@ -97,55 +97,6 @@ void perf_stat_evsel_id_init(struct perf_evsel *evsel)
|
||||
}
|
||||
}
|
||||
|
||||
struct perf_counts *perf_counts__new(int ncpus, int nthreads)
|
||||
{
|
||||
struct perf_counts *counts = zalloc(sizeof(*counts));
|
||||
|
||||
if (counts) {
|
||||
struct xyarray *values;
|
||||
|
||||
values = xyarray__new(ncpus, nthreads, sizeof(struct perf_counts_values));
|
||||
if (!values) {
|
||||
free(counts);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
counts->values = values;
|
||||
}
|
||||
|
||||
return counts;
|
||||
}
|
||||
|
||||
void perf_counts__delete(struct perf_counts *counts)
|
||||
{
|
||||
if (counts) {
|
||||
xyarray__delete(counts->values);
|
||||
free(counts);
|
||||
}
|
||||
}
|
||||
|
||||
static void perf_counts__reset(struct perf_counts *counts)
|
||||
{
|
||||
xyarray__reset(counts->values);
|
||||
}
|
||||
|
||||
void perf_evsel__reset_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_counts__reset(evsel->counts);
|
||||
}
|
||||
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads)
|
||||
{
|
||||
evsel->counts = perf_counts__new(ncpus, nthreads);
|
||||
return evsel->counts != NULL ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel)
|
||||
{
|
||||
perf_counts__delete(evsel->counts);
|
||||
evsel->counts = NULL;
|
||||
}
|
||||
|
||||
void perf_evsel__reset_stat_priv(struct perf_evsel *evsel)
|
||||
{
|
||||
int i;
|
||||
|
@ -33,23 +33,6 @@ enum aggr_mode {
|
||||
AGGR_THREAD,
|
||||
};
|
||||
|
||||
struct perf_counts_values {
|
||||
union {
|
||||
struct {
|
||||
u64 val;
|
||||
u64 ena;
|
||||
u64 run;
|
||||
};
|
||||
u64 values[3];
|
||||
};
|
||||
};
|
||||
|
||||
struct perf_counts {
|
||||
s8 scaled;
|
||||
struct perf_counts_values aggr;
|
||||
struct xyarray *values;
|
||||
};
|
||||
|
||||
struct perf_stat_config {
|
||||
enum aggr_mode aggr_mode;
|
||||
bool scale;
|
||||
@ -57,12 +40,6 @@ struct perf_stat_config {
|
||||
unsigned int interval;
|
||||
};
|
||||
|
||||
static inline struct perf_counts_values*
|
||||
perf_counts(struct perf_counts *counts, int cpu, int thread)
|
||||
{
|
||||
return xyarray__entry(counts->values, cpu, thread);
|
||||
}
|
||||
|
||||
void update_stats(struct stats *stats, u64 val);
|
||||
double avg_stats(struct stats *stats);
|
||||
double stddev_stats(struct stats *stats);
|
||||
@ -96,13 +73,6 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count,
|
||||
void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel,
|
||||
double avg, int cpu, enum aggr_mode aggr);
|
||||
|
||||
struct perf_counts *perf_counts__new(int ncpus, int nthreads);
|
||||
void perf_counts__delete(struct perf_counts *counts);
|
||||
|
||||
void perf_evsel__reset_counts(struct perf_evsel *evsel);
|
||||
int perf_evsel__alloc_counts(struct perf_evsel *evsel, int ncpus, int nthreads);
|
||||
void perf_evsel__free_counts(struct perf_evsel *evsel);
|
||||
|
||||
void perf_evsel__reset_stat_priv(struct perf_evsel *evsel);
|
||||
int perf_evsel__alloc_stat_priv(struct perf_evsel *evsel);
|
||||
void perf_evsel__free_stat_priv(struct perf_evsel *evsel);
|
||||
|
@ -566,6 +566,96 @@ unsigned long parse_tag_value(const char *str, struct parse_tag *tags)
|
||||
return (unsigned long) -1;
|
||||
}
|
||||
|
||||
int get_stack_size(const char *str, unsigned long *_size)
|
||||
{
|
||||
char *endptr;
|
||||
unsigned long size;
|
||||
unsigned long max_size = round_down(USHRT_MAX, sizeof(u64));
|
||||
|
||||
size = strtoul(str, &endptr, 0);
|
||||
|
||||
do {
|
||||
if (*endptr)
|
||||
break;
|
||||
|
||||
size = round_up(size, sizeof(u64));
|
||||
if (!size || size > max_size)
|
||||
break;
|
||||
|
||||
*_size = size;
|
||||
return 0;
|
||||
|
||||
} while (0);
|
||||
|
||||
pr_err("callchain: Incorrect stack dump size (max %ld): %s\n",
|
||||
max_size, str);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int parse_callchain_record(const char *arg, struct callchain_param *param)
|
||||
{
|
||||
char *tok, *name, *saveptr = NULL;
|
||||
char *buf;
|
||||
int ret = -1;
|
||||
|
||||
/* We need buffer that we know we can write to. */
|
||||
buf = malloc(strlen(arg) + 1);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
strcpy(buf, arg);
|
||||
|
||||
tok = strtok_r((char *)buf, ",", &saveptr);
|
||||
name = tok ? : (char *)buf;
|
||||
|
||||
do {
|
||||
/* Framepointer style */
|
||||
if (!strncmp(name, "fp", sizeof("fp"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
param->record_mode = CALLCHAIN_FP;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for --call-graph fp\n");
|
||||
break;
|
||||
|
||||
#ifdef HAVE_DWARF_UNWIND_SUPPORT
|
||||
/* Dwarf style */
|
||||
} else if (!strncmp(name, "dwarf", sizeof("dwarf"))) {
|
||||
const unsigned long default_stack_dump_size = 8192;
|
||||
|
||||
ret = 0;
|
||||
param->record_mode = CALLCHAIN_DWARF;
|
||||
param->dump_size = default_stack_dump_size;
|
||||
|
||||
tok = strtok_r(NULL, ",", &saveptr);
|
||||
if (tok) {
|
||||
unsigned long size = 0;
|
||||
|
||||
ret = get_stack_size(tok, &size);
|
||||
param->dump_size = size;
|
||||
}
|
||||
#endif /* HAVE_DWARF_UNWIND_SUPPORT */
|
||||
} else if (!strncmp(name, "lbr", sizeof("lbr"))) {
|
||||
if (!strtok_r(NULL, ",", &saveptr)) {
|
||||
param->record_mode = CALLCHAIN_LBR;
|
||||
ret = 0;
|
||||
} else
|
||||
pr_err("callchain: No more arguments "
|
||||
"needed for --call-graph lbr\n");
|
||||
break;
|
||||
} else {
|
||||
pr_err("callchain: Unknown --call-graph option "
|
||||
"value: %s\n", arg);
|
||||
break;
|
||||
}
|
||||
|
||||
} while (0);
|
||||
|
||||
free(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int filename__read_str(const char *filename, char **buf, size_t *sizep)
|
||||
{
|
||||
size_t size = 0, alloc_size = 0;
|
||||
|
@ -318,6 +318,7 @@ static inline int path__join3(char *bf, size_t size,
|
||||
struct dso;
|
||||
struct symbol;
|
||||
|
||||
extern bool srcline_full_filename;
|
||||
char *get_srcline(struct dso *dso, u64 addr, struct symbol *sym,
|
||||
bool show_sym);
|
||||
void free_srcline(char *srcline);
|
||||
@ -351,4 +352,6 @@ static inline char *asprintf_expr_not_in_ints(const char *var, size_t nints, int
|
||||
return asprintf_expr_inout_ints(var, false, nints, ints);
|
||||
}
|
||||
|
||||
int get_stack_size(const char *str, unsigned long *_size);
|
||||
|
||||
#endif /* GIT_COMPAT_UTIL_H */
|
||||
|
Loading…
Reference in New Issue
Block a user