Merge tag 'perf-core-for-mingo-5.1-20190307' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux into perf/urgent
Pull perf/core changes from Arnaldo Carvalho de Melo: perf bpf: Arnaldo Carvalho de Melo: - Automatically add BTF ELF markers to 'perf trace' BPF programs, so that tools such as 'bpftool map dump' can pretty print map keys and values. perf c2c: Jiri Olsa: - Fix report for empty NUMA node. perf diff: Jin Yao: - Support --time, --cpu, --pid and --tid filter options. perf probe: Arnaldo Carvalho de Melo: - Clarify error message about not finding kernel modules debuginfo. perf record: Jiri Olsa: - Fixup probing for max attr.precise_ip. perf trace: Arnaldo Carvalho de Melo: - Add missing %s lost in the 'msg_flags' recvmmsg arg when adding prefix suppression logic. perf annotate: Arnaldo Carvalho de Melo: - Calculate the max instruction name, align column to that, removing the hardcoded max 6 chars and cope with instructions with names longer than that, such as vpmovmskb, vpcmpeqb, etc. kernel: Song Liu: - Consider events with attr.bpf_event set as side-band. Gustavo A. R. Silva: - Mark expected switch fall-through in perf_event_parse_addr_filter(). Libraries: Jiri Olsa: - Fix leaks and double frees on error paths. libtraceevent: Tony Jones: - Fix buffer overflow in arg_eval(). python scripting: Tony Jones: - More python3 fixes. Trivial: Yang Wei: - Remove needless extra semicolon in clang C++ glue code. Intel PT/BTS: Adrian Hunter: - Improve auxtrace address filter error message when there is no DSO. - Fix divide by zero when TSC is not available. - Further improvements to the export to sqlite/posgresql python scripts and to the GUI sqlviewer, exporting 'parent_id' so that we have enable the creation of call trees. Andi Kleen: - Generalize function to copy from thread addr space from intel-bts code. Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com> Signed-off-by: Ingo Molnar <mingo@kernel.org>
Этот коммит содержится в:
@@ -198,18 +198,18 @@ static void ins__delete(struct ins_operands *ops)
|
||||
}
|
||||
|
||||
static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
return scnprintf(bf, size, "%-6s %s", ins->name, ops->raw);
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->raw);
|
||||
}
|
||||
|
||||
int ins__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
if (ins->ops->scnprintf)
|
||||
return ins->ops->scnprintf(ins, bf, size, ops);
|
||||
return ins->ops->scnprintf(ins, bf, size, ops, max_ins_name);
|
||||
|
||||
return ins__raw_scnprintf(ins, bf, size, ops);
|
||||
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
|
||||
}
|
||||
|
||||
bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2)
|
||||
@@ -273,18 +273,18 @@ indirect_call:
|
||||
}
|
||||
|
||||
static int call__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
if (ops->target.sym)
|
||||
return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
|
||||
|
||||
if (ops->target.addr == 0)
|
||||
return ins__raw_scnprintf(ins, bf, size, ops);
|
||||
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
|
||||
|
||||
if (ops->target.name)
|
||||
return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.name);
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.name);
|
||||
|
||||
return scnprintf(bf, size, "%-6s *%" PRIx64, ins->name, ops->target.addr);
|
||||
return scnprintf(bf, size, "%-*s *%" PRIx64, max_ins_name, ins->name, ops->target.addr);
|
||||
}
|
||||
|
||||
static struct ins_ops call_ops = {
|
||||
@@ -388,15 +388,15 @@ static int jump__parse(struct arch *arch, struct ins_operands *ops, struct map_s
|
||||
}
|
||||
|
||||
static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
const char *c;
|
||||
|
||||
if (!ops->target.addr || ops->target.offset < 0)
|
||||
return ins__raw_scnprintf(ins, bf, size, ops);
|
||||
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
|
||||
|
||||
if (ops->target.outside && ops->target.sym != NULL)
|
||||
return scnprintf(bf, size, "%-6s %s", ins->name, ops->target.sym->name);
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name, ops->target.sym->name);
|
||||
|
||||
c = strchr(ops->raw, ',');
|
||||
c = validate_comma(c, ops);
|
||||
@@ -415,7 +415,7 @@ static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
c++;
|
||||
}
|
||||
|
||||
return scnprintf(bf, size, "%-6s %.*s%" PRIx64,
|
||||
return scnprintf(bf, size, "%-*s %.*s%" PRIx64, max_ins_name,
|
||||
ins->name, c ? c - ops->raw : 0, ops->raw,
|
||||
ops->target.offset);
|
||||
}
|
||||
@@ -483,16 +483,16 @@ out_free_ops:
|
||||
}
|
||||
|
||||
static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
int printed;
|
||||
|
||||
if (ops->locked.ins.ops == NULL)
|
||||
return ins__raw_scnprintf(ins, bf, size, ops);
|
||||
return ins__raw_scnprintf(ins, bf, size, ops, max_ins_name);
|
||||
|
||||
printed = scnprintf(bf, size, "%-6s ", ins->name);
|
||||
printed = scnprintf(bf, size, "%-*s ", max_ins_name, ins->name);
|
||||
return printed + ins__scnprintf(&ops->locked.ins, bf + printed,
|
||||
size - printed, ops->locked.ops);
|
||||
size - printed, ops->locked.ops, max_ins_name);
|
||||
}
|
||||
|
||||
static void lock__delete(struct ins_operands *ops)
|
||||
@@ -564,9 +564,9 @@ out_free_source:
|
||||
}
|
||||
|
||||
static int mov__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
return scnprintf(bf, size, "%-6s %s,%s", ins->name,
|
||||
return scnprintf(bf, size, "%-*s %s,%s", max_ins_name, ins->name,
|
||||
ops->source.name ?: ops->source.raw,
|
||||
ops->target.name ?: ops->target.raw);
|
||||
}
|
||||
@@ -604,9 +604,9 @@ static int dec__parse(struct arch *arch __maybe_unused, struct ins_operands *ops
|
||||
}
|
||||
|
||||
static int dec__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops)
|
||||
struct ins_operands *ops, int max_ins_name)
|
||||
{
|
||||
return scnprintf(bf, size, "%-6s %s", ins->name,
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, ins->name,
|
||||
ops->target.name ?: ops->target.raw);
|
||||
}
|
||||
|
||||
@@ -616,9 +616,9 @@ static struct ins_ops dec_ops = {
|
||||
};
|
||||
|
||||
static int nop__scnprintf(struct ins *ins __maybe_unused, char *bf, size_t size,
|
||||
struct ins_operands *ops __maybe_unused)
|
||||
struct ins_operands *ops __maybe_unused, int max_ins_name)
|
||||
{
|
||||
return scnprintf(bf, size, "%-6s", "nop");
|
||||
return scnprintf(bf, size, "%-*s", max_ins_name, "nop");
|
||||
}
|
||||
|
||||
static struct ins_ops nop_ops = {
|
||||
@@ -1232,12 +1232,12 @@ void disasm_line__free(struct disasm_line *dl)
|
||||
annotation_line__delete(&dl->al);
|
||||
}
|
||||
|
||||
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw)
|
||||
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name)
|
||||
{
|
||||
if (raw || !dl->ins.ops)
|
||||
return scnprintf(bf, size, "%-6s %s", dl->ins.name, dl->ops.raw);
|
||||
return scnprintf(bf, size, "%-*s %s", max_ins_name, dl->ins.name, dl->ops.raw);
|
||||
|
||||
return ins__scnprintf(&dl->ins, bf, size, &dl->ops);
|
||||
return ins__scnprintf(&dl->ins, bf, size, &dl->ops, max_ins_name);
|
||||
}
|
||||
|
||||
static void annotation_line__add(struct annotation_line *al, struct list_head *head)
|
||||
@@ -2414,12 +2414,30 @@ static inline int width_jumps(int n)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int annotation__max_ins_name(struct annotation *notes)
|
||||
{
|
||||
int max_name = 0, len;
|
||||
struct annotation_line *al;
|
||||
|
||||
list_for_each_entry(al, ¬es->src->source, node) {
|
||||
if (al->offset == -1)
|
||||
continue;
|
||||
|
||||
len = strlen(disasm_line(al)->ins.name);
|
||||
if (max_name < len)
|
||||
max_name = len;
|
||||
}
|
||||
|
||||
return max_name;
|
||||
}
|
||||
|
||||
void annotation__init_column_widths(struct annotation *notes, struct symbol *sym)
|
||||
{
|
||||
notes->widths.addr = notes->widths.target =
|
||||
notes->widths.min_addr = hex_width(symbol__size(sym));
|
||||
notes->widths.max_addr = hex_width(sym->end);
|
||||
notes->widths.jumps = width_jumps(notes->max_jump_sources);
|
||||
notes->widths.max_ins_name = annotation__max_ins_name(notes);
|
||||
}
|
||||
|
||||
void annotation__update_column_widths(struct annotation *notes)
|
||||
@@ -2583,7 +2601,7 @@ call_like:
|
||||
obj__printf(obj, " ");
|
||||
}
|
||||
|
||||
disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
|
||||
disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset, notes->widths.max_ins_name);
|
||||
}
|
||||
|
||||
static void ipc_coverage_string(char *bf, int size, struct annotation *notes)
|
||||
|
@@ -59,14 +59,14 @@ struct ins_ops {
|
||||
void (*free)(struct ins_operands *ops);
|
||||
int (*parse)(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms);
|
||||
int (*scnprintf)(struct ins *ins, char *bf, size_t size,
|
||||
struct ins_operands *ops);
|
||||
struct ins_operands *ops, int max_ins_name);
|
||||
};
|
||||
|
||||
bool ins__is_jump(const struct ins *ins);
|
||||
bool ins__is_call(const struct ins *ins);
|
||||
bool ins__is_ret(const struct ins *ins);
|
||||
bool ins__is_lock(const struct ins *ins);
|
||||
int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops);
|
||||
int ins__scnprintf(struct ins *ins, char *bf, size_t size, struct ins_operands *ops, int max_ins_name);
|
||||
bool ins__is_fused(struct arch *arch, const char *ins1, const char *ins2);
|
||||
|
||||
#define ANNOTATION__IPC_WIDTH 6
|
||||
@@ -219,7 +219,7 @@ int __annotation__scnprintf_samples_period(struct annotation *notes,
|
||||
struct perf_evsel *evsel,
|
||||
bool show_freq);
|
||||
|
||||
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw);
|
||||
int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw, int max_ins_name);
|
||||
size_t disasm__fprintf(struct list_head *head, FILE *fp);
|
||||
void symbol__calc_percent(struct symbol *sym, struct perf_evsel *evsel);
|
||||
|
||||
@@ -289,6 +289,7 @@ struct annotation {
|
||||
u8 target;
|
||||
u8 min_addr;
|
||||
u8 max_addr;
|
||||
u8 max_ins_name;
|
||||
} widths;
|
||||
bool have_cycles;
|
||||
struct annotated_source *src;
|
||||
|
@@ -1918,7 +1918,8 @@ static struct dso *load_dso(const char *name)
|
||||
if (!map)
|
||||
return NULL;
|
||||
|
||||
map__load(map);
|
||||
if (map__load(map) < 0)
|
||||
pr_err("File '%s' not found or has no symbols.\n", name);
|
||||
|
||||
dso = dso__get(map->dso);
|
||||
|
||||
|
@@ -156,7 +156,7 @@ getBPFObjectFromModule(llvm::Module *Module)
|
||||
#endif
|
||||
if (NotAdded) {
|
||||
llvm::errs() << "TargetMachine can't emit a file of this type\n";
|
||||
return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);;
|
||||
return std::unique_ptr<llvm::SmallVectorImpl<char>>(nullptr);
|
||||
}
|
||||
PM.run(*Module);
|
||||
|
||||
|
@@ -237,7 +237,7 @@ static int open_file(struct perf_data *data)
|
||||
open_file_read(data) : open_file_write(data);
|
||||
|
||||
if (fd < 0) {
|
||||
free(data->file.path);
|
||||
zfree(&data->file.path);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ int perf_data__open(struct perf_data *data)
|
||||
|
||||
void perf_data__close(struct perf_data *data)
|
||||
{
|
||||
free(data->file.path);
|
||||
zfree(&data->file.path);
|
||||
close(data->file.fd);
|
||||
}
|
||||
|
||||
|
@@ -510,18 +510,23 @@ int db_export__call_path(struct db_export *dbe, struct call_path *cp)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int db_export__call_return(struct db_export *dbe, struct call_return *cr)
|
||||
int db_export__call_return(struct db_export *dbe, struct call_return *cr,
|
||||
u64 *parent_db_id)
|
||||
{
|
||||
int err;
|
||||
|
||||
if (cr->db_id)
|
||||
return 0;
|
||||
|
||||
err = db_export__call_path(dbe, cr->cp);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
cr->db_id = ++dbe->call_return_last_db_id;
|
||||
if (!cr->db_id)
|
||||
cr->db_id = ++dbe->call_return_last_db_id;
|
||||
|
||||
if (parent_db_id) {
|
||||
if (!*parent_db_id)
|
||||
*parent_db_id = ++dbe->call_return_last_db_id;
|
||||
cr->parent_db_id = *parent_db_id;
|
||||
}
|
||||
|
||||
if (dbe->export_call_return)
|
||||
return dbe->export_call_return(dbe, cr);
|
||||
|
@@ -104,6 +104,7 @@ int db_export__sample(struct db_export *dbe, union perf_event *event,
|
||||
int db_export__branch_types(struct db_export *dbe);
|
||||
|
||||
int db_export__call_path(struct db_export *dbe, struct call_path *cp);
|
||||
int db_export__call_return(struct db_export *dbe, struct call_return *cr);
|
||||
int db_export__call_return(struct db_export *dbe, struct call_return *cr,
|
||||
u64 *parent_db_id);
|
||||
|
||||
#endif
|
||||
|
@@ -230,18 +230,33 @@ void perf_evlist__set_leader(struct perf_evlist *evlist)
|
||||
}
|
||||
}
|
||||
|
||||
void perf_event_attr__set_max_precise_ip(struct perf_event_attr *attr)
|
||||
void perf_event_attr__set_max_precise_ip(struct perf_event_attr *pattr)
|
||||
{
|
||||
attr->precise_ip = 3;
|
||||
struct perf_event_attr attr = {
|
||||
.type = PERF_TYPE_HARDWARE,
|
||||
.config = PERF_COUNT_HW_CPU_CYCLES,
|
||||
.exclude_kernel = 1,
|
||||
.precise_ip = 3,
|
||||
};
|
||||
|
||||
while (attr->precise_ip != 0) {
|
||||
int fd = sys_perf_event_open(attr, 0, -1, -1, 0);
|
||||
event_attr_init(&attr);
|
||||
|
||||
/*
|
||||
* Unnamed union member, not supported as struct member named
|
||||
* initializer in older compilers such as gcc 4.4.7
|
||||
*/
|
||||
attr.sample_period = 1;
|
||||
|
||||
while (attr.precise_ip != 0) {
|
||||
int fd = sys_perf_event_open(&attr, 0, -1, -1, 0);
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
break;
|
||||
}
|
||||
--attr->precise_ip;
|
||||
--attr.precise_ip;
|
||||
}
|
||||
|
||||
pattr->precise_ip = attr.precise_ip;
|
||||
}
|
||||
|
||||
int __perf_evlist__add_default(struct perf_evlist *evlist, bool precise)
|
||||
|
@@ -294,20 +294,12 @@ struct perf_evsel *perf_evsel__new_cycles(bool precise)
|
||||
|
||||
if (!precise)
|
||||
goto new_event;
|
||||
/*
|
||||
* Unnamed union member, not supported as struct member named
|
||||
* initializer in older compilers such as gcc 4.4.7
|
||||
*
|
||||
* Just for probing the precise_ip:
|
||||
*/
|
||||
attr.sample_period = 1;
|
||||
|
||||
perf_event_attr__set_max_precise_ip(&attr);
|
||||
/*
|
||||
* Now let the usual logic to set up the perf_event_attr defaults
|
||||
* to kick in when we return and before perf_evsel__open() is called.
|
||||
*/
|
||||
attr.sample_period = 0;
|
||||
new_event:
|
||||
evsel = perf_evsel__new(&attr);
|
||||
if (evsel == NULL)
|
||||
|
@@ -396,11 +396,8 @@ static int hist_entry__init(struct hist_entry *he,
|
||||
* adding new entries. So we need to save a copy.
|
||||
*/
|
||||
he->branch_info = malloc(sizeof(*he->branch_info));
|
||||
if (he->branch_info == NULL) {
|
||||
map__zput(he->ms.map);
|
||||
free(he->stat_acc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (he->branch_info == NULL)
|
||||
goto err;
|
||||
|
||||
memcpy(he->branch_info, template->branch_info,
|
||||
sizeof(*he->branch_info));
|
||||
@@ -419,22 +416,16 @@ static int hist_entry__init(struct hist_entry *he,
|
||||
|
||||
if (he->raw_data) {
|
||||
he->raw_data = memdup(he->raw_data, he->raw_size);
|
||||
|
||||
if (he->raw_data == NULL) {
|
||||
map__put(he->ms.map);
|
||||
if (he->branch_info) {
|
||||
map__put(he->branch_info->from.map);
|
||||
map__put(he->branch_info->to.map);
|
||||
free(he->branch_info);
|
||||
}
|
||||
if (he->mem_info) {
|
||||
map__put(he->mem_info->iaddr.map);
|
||||
map__put(he->mem_info->daddr.map);
|
||||
}
|
||||
free(he->stat_acc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (he->raw_data == NULL)
|
||||
goto err_infos;
|
||||
}
|
||||
|
||||
if (he->srcline) {
|
||||
he->srcline = strdup(he->srcline);
|
||||
if (he->srcline == NULL)
|
||||
goto err_rawdata;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&he->pairs.node);
|
||||
thread__get(he->thread);
|
||||
he->hroot_in = RB_ROOT_CACHED;
|
||||
@@ -444,6 +435,24 @@ static int hist_entry__init(struct hist_entry *he,
|
||||
he->leaf = true;
|
||||
|
||||
return 0;
|
||||
|
||||
err_rawdata:
|
||||
free(he->raw_data);
|
||||
|
||||
err_infos:
|
||||
if (he->branch_info) {
|
||||
map__put(he->branch_info->from.map);
|
||||
map__put(he->branch_info->to.map);
|
||||
free(he->branch_info);
|
||||
}
|
||||
if (he->mem_info) {
|
||||
map__put(he->mem_info->iaddr.map);
|
||||
map__put(he->mem_info->daddr.map);
|
||||
}
|
||||
err:
|
||||
map__zput(he->ms.map);
|
||||
free(he->stat_acc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static void *hist_entry__zalloc(size_t size)
|
||||
@@ -606,7 +615,7 @@ __hists__add_entry(struct hists *hists,
|
||||
.map = al->map,
|
||||
.sym = al->sym,
|
||||
},
|
||||
.srcline = al->srcline ? strdup(al->srcline) : NULL,
|
||||
.srcline = (char *) al->srcline,
|
||||
.socket = al->socket,
|
||||
.cpu = al->cpu,
|
||||
.cpumode = al->cpumode,
|
||||
@@ -963,7 +972,7 @@ iter_add_next_cumulative_entry(struct hist_entry_iter *iter,
|
||||
.map = al->map,
|
||||
.sym = al->sym,
|
||||
},
|
||||
.srcline = al->srcline ? strdup(al->srcline) : NULL,
|
||||
.srcline = (char *) al->srcline,
|
||||
.parent = iter->parent,
|
||||
.raw_data = sample->raw_data,
|
||||
.raw_size = sample->raw_size,
|
||||
|
@@ -328,35 +328,19 @@ static int intel_bts_get_next_insn(struct intel_bts_queue *btsq, u64 ip)
|
||||
{
|
||||
struct machine *machine = btsq->bts->machine;
|
||||
struct thread *thread;
|
||||
struct addr_location al;
|
||||
unsigned char buf[INTEL_PT_INSN_BUF_SZ];
|
||||
ssize_t len;
|
||||
int x86_64;
|
||||
uint8_t cpumode;
|
||||
bool x86_64;
|
||||
int err = -1;
|
||||
|
||||
if (machine__kernel_ip(machine, ip))
|
||||
cpumode = PERF_RECORD_MISC_KERNEL;
|
||||
else
|
||||
cpumode = PERF_RECORD_MISC_USER;
|
||||
|
||||
thread = machine__find_thread(machine, -1, btsq->tid);
|
||||
if (!thread)
|
||||
return -1;
|
||||
|
||||
if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso)
|
||||
goto out_put;
|
||||
|
||||
len = dso__data_read_addr(al.map->dso, al.map, machine, ip, buf,
|
||||
INTEL_PT_INSN_BUF_SZ);
|
||||
len = thread__memcpy(thread, machine, buf, ip, INTEL_PT_INSN_BUF_SZ, &x86_64);
|
||||
if (len <= 0)
|
||||
goto out_put;
|
||||
|
||||
/* Load maps to ensure dso->is_64_bit has been updated */
|
||||
map__load(al.map);
|
||||
|
||||
x86_64 = al.map->dso->is_64_bit;
|
||||
|
||||
if (intel_pt_get_insn(buf, len, x86_64, &btsq->intel_pt_insn))
|
||||
goto out_put;
|
||||
|
||||
|
@@ -2531,6 +2531,8 @@ int intel_pt_process_auxtrace_info(union perf_event *event,
|
||||
}
|
||||
|
||||
pt->timeless_decoding = intel_pt_timeless_decoding(pt);
|
||||
if (pt->timeless_decoding && !pt->tc.time_mult)
|
||||
pt->tc.time_mult = 1;
|
||||
pt->have_tsc = intel_pt_have_tsc(pt);
|
||||
pt->sampling_mode = false;
|
||||
pt->est_tsc = !pt->timeless_decoding;
|
||||
|
@@ -752,6 +752,19 @@ perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int pmu_max_precise(const char *name)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int max_precise = -1;
|
||||
|
||||
scnprintf(path, PATH_MAX,
|
||||
"bus/event_source/devices/%s/caps/max_precise",
|
||||
name);
|
||||
|
||||
sysfs__read_int(path, &max_precise);
|
||||
return max_precise;
|
||||
}
|
||||
|
||||
static struct perf_pmu *pmu_lookup(const char *name)
|
||||
{
|
||||
struct perf_pmu *pmu;
|
||||
@@ -784,6 +797,7 @@ static struct perf_pmu *pmu_lookup(const char *name)
|
||||
pmu->name = strdup(name);
|
||||
pmu->type = type;
|
||||
pmu->is_uncore = pmu_is_uncore(name);
|
||||
pmu->max_precise = pmu_max_precise(name);
|
||||
pmu_add_cpu_aliases(&aliases, pmu);
|
||||
|
||||
INIT_LIST_HEAD(&pmu->format);
|
||||
|
@@ -26,6 +26,7 @@ struct perf_pmu {
|
||||
__u32 type;
|
||||
bool selectable;
|
||||
bool is_uncore;
|
||||
int max_precise;
|
||||
struct perf_event_attr *default_config;
|
||||
struct cpu_map *cpus;
|
||||
struct list_head format; /* HEAD struct perf_pmu_format -> list */
|
||||
|
@@ -472,9 +472,12 @@ static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
|
||||
strcpy(reason, "(unknown)");
|
||||
} else
|
||||
dso__strerror_load(dso, reason, STRERR_BUFSIZE);
|
||||
if (!silent)
|
||||
pr_err("Failed to find the path for %s: %s\n",
|
||||
module ?: "kernel", reason);
|
||||
if (!silent) {
|
||||
if (module)
|
||||
pr_err("Module %s is not loaded, please specify its full path name.\n", module);
|
||||
else
|
||||
pr_err("Failed to find the path for the kernel: %s\n", reason);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
path = dso->long_name;
|
||||
|
@@ -1173,7 +1173,7 @@ static int python_export_call_return(struct db_export *dbe,
|
||||
u64 comm_db_id = cr->comm ? cr->comm->db_id : 0;
|
||||
PyObject *t;
|
||||
|
||||
t = tuple_new(11);
|
||||
t = tuple_new(12);
|
||||
|
||||
tuple_set_u64(t, 0, cr->db_id);
|
||||
tuple_set_u64(t, 1, cr->thread->db_id);
|
||||
@@ -1186,6 +1186,7 @@ static int python_export_call_return(struct db_export *dbe,
|
||||
tuple_set_u64(t, 8, cr->return_ref);
|
||||
tuple_set_u64(t, 9, cr->cp->parent->db_id);
|
||||
tuple_set_s32(t, 10, cr->flags);
|
||||
tuple_set_u64(t, 11, cr->parent_db_id);
|
||||
|
||||
call_object(tables->call_return_handler, t, "call_return_table");
|
||||
|
||||
@@ -1194,11 +1195,12 @@ static int python_export_call_return(struct db_export *dbe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int python_process_call_return(struct call_return *cr, void *data)
|
||||
static int python_process_call_return(struct call_return *cr, u64 *parent_db_id,
|
||||
void *data)
|
||||
{
|
||||
struct db_export *dbe = data;
|
||||
|
||||
return db_export__call_return(dbe, cr);
|
||||
return db_export__call_return(dbe, cr, parent_db_id);
|
||||
}
|
||||
|
||||
static void python_process_general_event(struct perf_sample *sample,
|
||||
|
@@ -140,7 +140,7 @@ struct perf_session *perf_session__new(struct perf_data *data,
|
||||
|
||||
if (perf_data__is_read(data)) {
|
||||
if (perf_session__open(session) < 0)
|
||||
goto out_close;
|
||||
goto out_delete;
|
||||
|
||||
/*
|
||||
* set session attributes that are present in perf.data
|
||||
@@ -181,8 +181,6 @@ struct perf_session *perf_session__new(struct perf_data *data,
|
||||
|
||||
return session;
|
||||
|
||||
out_close:
|
||||
perf_data__close(data);
|
||||
out_delete:
|
||||
perf_session__delete(session);
|
||||
out:
|
||||
|
@@ -49,6 +49,7 @@ enum retpoline_state_t {
|
||||
* @timestamp: timestamp (if known)
|
||||
* @ref: external reference (e.g. db_id of sample)
|
||||
* @branch_count: the branch count when the entry was created
|
||||
* @db_id: id used for db-export
|
||||
* @cp: call path
|
||||
* @no_call: a 'call' was not seen
|
||||
* @trace_end: a 'call' but trace ended
|
||||
@@ -59,6 +60,7 @@ struct thread_stack_entry {
|
||||
u64 timestamp;
|
||||
u64 ref;
|
||||
u64 branch_count;
|
||||
u64 db_id;
|
||||
struct call_path *cp;
|
||||
bool no_call;
|
||||
bool trace_end;
|
||||
@@ -280,12 +282,14 @@ static int thread_stack__call_return(struct thread *thread,
|
||||
.comm = ts->comm,
|
||||
.db_id = 0,
|
||||
};
|
||||
u64 *parent_db_id;
|
||||
|
||||
tse = &ts->stack[idx];
|
||||
cr.cp = tse->cp;
|
||||
cr.call_time = tse->timestamp;
|
||||
cr.return_time = timestamp;
|
||||
cr.branch_count = ts->branch_count - tse->branch_count;
|
||||
cr.db_id = tse->db_id;
|
||||
cr.call_ref = tse->ref;
|
||||
cr.return_ref = ref;
|
||||
if (tse->no_call)
|
||||
@@ -295,7 +299,14 @@ static int thread_stack__call_return(struct thread *thread,
|
||||
if (tse->non_call)
|
||||
cr.flags |= CALL_RETURN_NON_CALL;
|
||||
|
||||
return crp->process(&cr, crp->data);
|
||||
/*
|
||||
* The parent db_id must be assigned before exporting the child. Note
|
||||
* it is not possible to export the parent first because its information
|
||||
* is not yet complete because its 'return' has not yet been processed.
|
||||
*/
|
||||
parent_db_id = idx ? &(tse - 1)->db_id : NULL;
|
||||
|
||||
return crp->process(&cr, parent_db_id, crp->data);
|
||||
}
|
||||
|
||||
static int __thread_stack__flush(struct thread *thread, struct thread_stack *ts)
|
||||
@@ -484,7 +495,7 @@ void thread_stack__sample(struct thread *thread, int cpu,
|
||||
}
|
||||
|
||||
struct call_return_processor *
|
||||
call_return_processor__new(int (*process)(struct call_return *cr, void *data),
|
||||
call_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data),
|
||||
void *data)
|
||||
{
|
||||
struct call_return_processor *crp;
|
||||
@@ -537,6 +548,7 @@ static int thread_stack__push_cp(struct thread_stack *ts, u64 ret_addr,
|
||||
tse->no_call = no_call;
|
||||
tse->trace_end = trace_end;
|
||||
tse->non_call = false;
|
||||
tse->db_id = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@@ -55,6 +55,7 @@ enum {
|
||||
* @call_ref: external reference to 'call' sample (e.g. db_id)
|
||||
* @return_ref: external reference to 'return' sample (e.g. db_id)
|
||||
* @db_id: id used for db-export
|
||||
* @parent_db_id: id of parent call used for db-export
|
||||
* @flags: Call/Return flags
|
||||
*/
|
||||
struct call_return {
|
||||
@@ -67,6 +68,7 @@ struct call_return {
|
||||
u64 call_ref;
|
||||
u64 return_ref;
|
||||
u64 db_id;
|
||||
u64 parent_db_id;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
@@ -79,7 +81,7 @@ struct call_return {
|
||||
*/
|
||||
struct call_return_processor {
|
||||
struct call_path_root *cpr;
|
||||
int (*process)(struct call_return *cr, void *data);
|
||||
int (*process)(struct call_return *cr, u64 *parent_db_id, void *data);
|
||||
void *data;
|
||||
};
|
||||
|
||||
@@ -93,7 +95,7 @@ void thread_stack__free(struct thread *thread);
|
||||
size_t thread_stack__depth(struct thread *thread, int cpu);
|
||||
|
||||
struct call_return_processor *
|
||||
call_return_processor__new(int (*process)(struct call_return *cr, void *data),
|
||||
call_return_processor__new(int (*process)(struct call_return *cr, u64 *parent_db_id, void *data),
|
||||
void *data);
|
||||
void call_return_processor__free(struct call_return_processor *crp);
|
||||
int thread_stack__process(struct thread *thread, struct comm *comm,
|
||||
|
@@ -12,6 +12,7 @@
|
||||
#include "debug.h"
|
||||
#include "namespaces.h"
|
||||
#include "comm.h"
|
||||
#include "map.h"
|
||||
#include "symbol.h"
|
||||
#include "unwind.h"
|
||||
|
||||
@@ -393,3 +394,25 @@ struct thread *thread__main_thread(struct machine *machine, struct thread *threa
|
||||
|
||||
return machine__find_thread(machine, thread->pid_, thread->pid_);
|
||||
}
|
||||
|
||||
int thread__memcpy(struct thread *thread, struct machine *machine,
|
||||
void *buf, u64 ip, int len, bool *is64bit)
|
||||
{
|
||||
u8 cpumode = PERF_RECORD_MISC_USER;
|
||||
struct addr_location al;
|
||||
long offset;
|
||||
|
||||
if (machine__kernel_ip(machine, ip))
|
||||
cpumode = PERF_RECORD_MISC_KERNEL;
|
||||
|
||||
if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso ||
|
||||
al.map->dso->data.status == DSO_DATA_STATUS_ERROR ||
|
||||
map__load(al.map) < 0)
|
||||
return -1;
|
||||
|
||||
offset = al.map->map_ip(al.map, ip);
|
||||
if (is64bit)
|
||||
*is64bit = al.map->dso->is_64_bit;
|
||||
|
||||
return dso__data_read_offset(al.map->dso, machine, offset, buf, len);
|
||||
}
|
||||
|
@@ -113,6 +113,9 @@ struct symbol *thread__find_symbol_fb(struct thread *thread, u8 cpumode,
|
||||
void thread__find_cpumode_addr_location(struct thread *thread, u64 addr,
|
||||
struct addr_location *al);
|
||||
|
||||
int thread__memcpy(struct thread *thread, struct machine *machine,
|
||||
void *buf, u64 ip, int len, bool *is64bit);
|
||||
|
||||
static inline void *thread__priv(struct thread *thread)
|
||||
{
|
||||
return thread->priv;
|
||||
|
@@ -11,6 +11,8 @@
|
||||
#include "perf.h"
|
||||
#include "debug.h"
|
||||
#include "time-utils.h"
|
||||
#include "session.h"
|
||||
#include "evlist.h"
|
||||
|
||||
int parse_nsec_time(const char *str, u64 *ptime)
|
||||
{
|
||||
@@ -374,7 +376,7 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
|
||||
struct perf_time_interval *ptime;
|
||||
int i;
|
||||
|
||||
if ((timestamp == 0) || (num == 0))
|
||||
if ((!ptime_buf) || (timestamp == 0) || (num == 0))
|
||||
return false;
|
||||
|
||||
if (num == 1)
|
||||
@@ -396,6 +398,53 @@ bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
|
||||
return (i == num) ? true : false;
|
||||
}
|
||||
|
||||
int perf_time__parse_for_ranges(const char *time_str,
|
||||
struct perf_session *session,
|
||||
struct perf_time_interval **ranges,
|
||||
int *range_size, int *range_num)
|
||||
{
|
||||
struct perf_time_interval *ptime_range;
|
||||
int size, num, ret;
|
||||
|
||||
ptime_range = perf_time__range_alloc(time_str, &size);
|
||||
if (!ptime_range)
|
||||
return -ENOMEM;
|
||||
|
||||
if (perf_time__parse_str(ptime_range, time_str) != 0) {
|
||||
if (session->evlist->first_sample_time == 0 &&
|
||||
session->evlist->last_sample_time == 0) {
|
||||
pr_err("HINT: no first/last sample time found in perf data.\n"
|
||||
"Please use latest perf binary to execute 'perf record'\n"
|
||||
"(if '--buildid-all' is enabled, please set '--timestamp-boundary').\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
|
||||
num = perf_time__percent_parse_str(
|
||||
ptime_range, size,
|
||||
time_str,
|
||||
session->evlist->first_sample_time,
|
||||
session->evlist->last_sample_time);
|
||||
|
||||
if (num < 0) {
|
||||
pr_err("Invalid time string\n");
|
||||
ret = -EINVAL;
|
||||
goto error;
|
||||
}
|
||||
} else {
|
||||
num = 1;
|
||||
}
|
||||
|
||||
*range_size = size;
|
||||
*range_num = num;
|
||||
*ranges = ptime_range;
|
||||
return 0;
|
||||
|
||||
error:
|
||||
free(ptime_range);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz)
|
||||
{
|
||||
u64 sec = timestamp / NSEC_PER_SEC;
|
||||
|
@@ -23,6 +23,12 @@ bool perf_time__skip_sample(struct perf_time_interval *ptime, u64 timestamp);
|
||||
bool perf_time__ranges_skip_sample(struct perf_time_interval *ptime_buf,
|
||||
int num, u64 timestamp);
|
||||
|
||||
struct perf_session;
|
||||
|
||||
int perf_time__parse_for_ranges(const char *str, struct perf_session *session,
|
||||
struct perf_time_interval **ranges,
|
||||
int *range_size, int *range_num);
|
||||
|
||||
int timestamp__scnprintf_usec(u64 timestamp, char *buf, size_t sz);
|
||||
|
||||
int fetch_current_timestamp(char *buf, size_t sz);
|
||||
|
Ссылка в новой задаче
Block a user