diff options
Diffstat (limited to 'tools/perf/util')
80 files changed, 1827 insertions, 899 deletions
diff --git a/tools/perf/util/alias.c b/tools/perf/util/alias.c index e6d134773d0a..c0b43ee40d95 100644 --- a/tools/perf/util/alias.c +++ b/tools/perf/util/alias.c @@ -55,8 +55,7 @@ int split_cmdline(char *cmdline, const char ***argv) src++; c = cmdline[src]; if (!c) { - free(*argv); - *argv = NULL; + zfree(argv); return error("cmdline ends with \\"); } } @@ -68,8 +67,7 @@ int split_cmdline(char *cmdline, const char ***argv) cmdline[dst] = 0; if (quoted) { - free(*argv); - *argv = NULL; + zfree(argv); return error("unclosed quote"); } diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index cf6242c92ee2..469eb679fb9d 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c @@ -26,10 +26,10 @@ static int disasm_line__parse(char *line, char **namep, char **rawp); static void ins__delete(struct ins_operands *ops) { - free(ops->source.raw); - free(ops->source.name); - free(ops->target.raw); - free(ops->target.name); + zfree(&ops->source.raw); + zfree(&ops->source.name); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static int ins__raw_scnprintf(struct ins *ins, char *bf, size_t size, @@ -185,8 +185,7 @@ static int lock__parse(struct ins_operands *ops) return 0; out_free_ops: - free(ops->locked.ops); - ops->locked.ops = NULL; + zfree(&ops->locked.ops); return 0; } @@ -205,9 +204,9 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size, static void lock__delete(struct ins_operands *ops) { - free(ops->locked.ops); - free(ops->target.raw); - free(ops->target.name); + zfree(&ops->locked.ops); + zfree(&ops->target.raw); + zfree(&ops->target.name); } static struct ins_ops lock_ops = { @@ -256,8 +255,7 @@ static int mov__parse(struct ins_operands *ops) return 0; out_free_source: - free(ops->source.raw); - ops->source.raw = NULL; + zfree(&ops->source.raw); return -1; } @@ -464,17 +462,12 @@ void symbol__annotate_zero_histograms(struct symbol *sym) pthread_mutex_unlock(¬es->lock); } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, - int evidx, u64 addr) +static int __symbol__inc_addr_samples(struct symbol *sym, struct map *map, + struct annotation *notes, int evidx, u64 addr) { unsigned offset; - struct annotation *notes; struct sym_hist *h; - notes = symbol__annotation(sym); - if (notes->src == NULL) - return -ENOMEM; - pr_debug3("%s: addr=%#" PRIx64 "\n", __func__, map->unmap_ip(map, addr)); if (addr < sym->start || addr > sym->end) @@ -491,6 +484,33 @@ int symbol__inc_addr_samples(struct symbol *sym, struct map *map, return 0; } +static int symbol__inc_addr_samples(struct symbol *sym, struct map *map, + int evidx, u64 addr) +{ + struct annotation *notes; + + if (sym == NULL || use_browser != 1 || !sort__has_sym) + return 0; + + notes = symbol__annotation(sym); + if (notes->src == NULL) { + if (symbol__alloc_hist(sym) < 0) + return -ENOMEM; + } + + return __symbol__inc_addr_samples(sym, map, notes, evidx, addr); +} + +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx) +{ + return symbol__inc_addr_samples(ams->sym, ams->map, evidx, ams->al_addr); +} + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) +{ + return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); +} + static void disasm_line__init_ins(struct disasm_line *dl) { dl->ins = ins__find(dl->name); @@ -538,8 +558,7 @@ static int disasm_line__parse(char *line, char **namep, char **rawp) return 0; out_free_name: - free(*namep); - *namep = NULL; + zfree(namep); return -1; } @@ -564,7 +583,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, size_t privs return dl; out_free_line: - free(dl->line); + zfree(&dl->line); out_delete: free(dl); return NULL; @@ -572,8 +591,8 @@ out_delete: void disasm_line__free(struct disasm_line *dl) { - free(dl->line); - free(dl->name); + zfree(&dl->line); + zfree(&dl->name); if (dl->ins && dl->ins->ops->free) dl->ins->ops->free(&dl->ops); else @@ -900,7 +919,7 @@ fallback: * cache, or is just a kallsyms file, well, lets hope that this * DSO is the same as when 'perf record' ran. */ - filename = dso->long_name; + filename = (char *)dso->long_name; snprintf(symfs_filename, sizeof(symfs_filename), "%s%s", symbol_conf.symfs, filename); free_filename = false; @@ -1091,8 +1110,7 @@ static void symbol__free_source_line(struct symbol *sym, int len) src_line = (void *)src_line + sizeof_src_line; } - free(notes->src->lines); - notes->src->lines = NULL; + zfree(¬es->src->lines); } /* Get the filename:line for the colored entries */ @@ -1376,3 +1394,8 @@ int symbol__tty_annotate(struct symbol *sym, struct map *map, return 0; } + +int hist_entry__annotate(struct hist_entry *he, size_t privsize) +{ + return symbol__annotate(he->ms.sym, he->ms.map, privsize); +} diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 834b7b57b788..b2aef59d6bb2 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h @@ -132,12 +132,17 @@ static inline struct annotation *symbol__annotation(struct symbol *sym) return &a->annotation; } -int symbol__inc_addr_samples(struct symbol *sym, struct map *map, - int evidx, u64 addr); +int addr_map_symbol__inc_samples(struct addr_map_symbol *ams, int evidx); + +int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); + int symbol__alloc_hist(struct symbol *sym); void symbol__annotate_zero_histograms(struct symbol *sym); int symbol__annotate(struct symbol *sym, struct map *map, size_t privsize); + +int hist_entry__annotate(struct hist_entry *he, size_t privsize); + int symbol__annotate_init(struct map *map __maybe_unused, struct symbol *sym); int symbol__annotate_printf(struct symbol *sym, struct map *map, struct perf_evsel *evsel, bool full_paths, diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c index a92770c98cc7..6baabe63182b 100644 --- a/tools/perf/util/build-id.c +++ b/tools/perf/util/build-id.c @@ -89,7 +89,7 @@ int build_id__sprintf(const u8 *build_id, int len, char *bf) return raw - build_id; } -char *dso__build_id_filename(struct dso *dso, char *bf, size_t size) +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h index 929f28a7c14d..845ef865eced 100644 --- a/tools/perf/util/build-id.h +++ b/tools/perf/util/build-id.h @@ -10,7 +10,7 @@ extern struct perf_tool build_id__mark_dso_hit_ops; struct dso; int build_id__sprintf(const u8 *build_id, int len, char *bf); -char *dso__build_id_filename(struct dso *dso, char *bf, size_t size); +char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size); int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, diff --git a/tools/perf/util/callchain.c b/tools/perf/util/callchain.c index e3970e3eaacf..8d9db454f1a9 100644 --- a/tools/perf/util/callchain.c +++ b/tools/perf/util/callchain.c @@ -15,8 +15,12 @@ #include <errno.h> #include <math.h> +#include "asm/bug.h" + #include "hist.h" #include "util.h" +#include "sort.h" +#include "machine.h" #include "callchain.h" __thread struct callchain_cursor callchain_cursor; @@ -356,19 +360,14 @@ append_chain_children(struct callchain_node *root, /* lookup in childrens */ while (*p) { s64 ret; - struct callchain_list *cnode; parent = *p; rnode = rb_entry(parent, struct callchain_node, rb_node_in); - cnode = list_first_entry(&rnode->val, struct callchain_list, - list); - /* just check first entry */ - ret = match_chain(node, cnode); - if (ret == 0) { - append_chain(rnode, cursor, period); + /* If at least first entry matches, rely to children */ + ret = append_chain(rnode, cursor, period); + if (ret == 0) goto inc_children_hit; - } if (ret < 0) p = &parent->rb_left; @@ -389,11 +388,11 @@ append_chain(struct callchain_node *root, struct callchain_cursor *cursor, u64 period) { - struct callchain_cursor_node *curr_snap = cursor->curr; struct callchain_list *cnode; u64 start = cursor->pos; bool found = false; u64 matches; + int cmp = 0; /* * Lookup in the current node @@ -408,7 +407,8 @@ append_chain(struct callchain_node *root, if (!node) break; - if (match_chain(node, cnode) != 0) + cmp = match_chain(node, cnode); + if (cmp) break; found = true; @@ -418,9 +418,8 @@ append_chain(struct callchain_node *root, /* matches not, relay no the parent */ if (!found) { - cursor->curr = curr_snap; - cursor->pos = start; - return -1; + WARN_ONCE(!cmp, "Chain comparison error\n"); + return cmp; } matches = cursor->pos - start; @@ -531,3 +530,24 @@ int callchain_cursor_append(struct callchain_cursor *cursor, return 0; } + +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, + struct perf_evsel *evsel, struct addr_location *al, + int max_stack) +{ + if (sample->callchain == NULL) + return 0; + + if (symbol_conf.use_callchain || sort__has_parent) { + return machine__resolve_callchain(al->machine, evsel, al->thread, + sample, parent, al, max_stack); + } + return 0; +} + +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample) +{ + if (!symbol_conf.use_callchain) + return 0; + return callchain_append(he->callchain, &callchain_cursor, sample->period); +} diff --git a/tools/perf/util/callchain.h b/tools/perf/util/callchain.h index 4f7f989876ec..8ad97e9b119f 100644 --- a/tools/perf/util/callchain.h +++ b/tools/perf/util/callchain.h @@ -145,10 +145,16 @@ static inline void callchain_cursor_advance(struct callchain_cursor *cursor) } struct option; +struct hist_entry; -int record_parse_callchain(const char *arg, struct perf_record_opts *opts); +int record_parse_callchain(const char *arg, struct record_opts *opts); int record_parse_callchain_opt(const struct option *opt, const char *arg, int unset); int record_callchain_opt(const struct option *opt, const char *arg, int unset); +int sample__resolve_callchain(struct perf_sample *sample, struct symbol **parent, + struct perf_evsel *evsel, struct addr_location *al, + int max_stack); +int hist_entry__append_callchain(struct hist_entry *he, struct perf_sample *sample); + extern const char record_callchain_help[]; #endif /* __PERF_CALLCHAIN_H */ diff --git a/tools/perf/util/cgroup.c b/tools/perf/util/cgroup.c index 96bbda1ddb83..88f7be399432 100644 --- a/tools/perf/util/cgroup.c +++ b/tools/perf/util/cgroup.c @@ -81,7 +81,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str) /* * check if cgrp is already defined, if so we reuse it */ - list_for_each_entry(counter, &evlist->entries, node) { + evlist__for_each(evlist, counter) { cgrp = counter->cgrp; if (!cgrp) continue; @@ -110,7 +110,7 @@ static int add_cgroup(struct perf_evlist *evlist, char *str) * if add cgroup N, then need to find event N */ n = 0; - list_for_each_entry(counter, &evlist->entries, node) { + evlist__for_each(evlist, counter) { if (n == nr_cgroups) goto found; n++; @@ -133,7 +133,7 @@ void close_cgroup(struct cgroup_sel *cgrp) /* XXX: not reentrant */ if (--cgrp->refcnt == 0) { close(cgrp->fd); - free(cgrp->name); + zfree(&cgrp->name); free(cgrp); } } diff --git a/tools/perf/util/color.c b/tools/perf/util/color.c index 66e44a5019d5..87b8672eb413 100644 --- a/tools/perf/util/color.c +++ b/tools/perf/util/color.c @@ -1,6 +1,7 @@ #include <linux/kernel.h> #include "cache.h" #include "color.h" +#include <math.h> int perf_use_color_default = -1; @@ -298,10 +299,10 @@ const char *get_percent_color(double percent) * entries in green - and keep the low overhead places * normal: */ - if (percent >= MIN_RED) + if (fabs(percent) >= MIN_RED) color = PERF_COLOR_RED; else { - if (percent > MIN_GREEN) + if (fabs(percent) > MIN_GREEN) color = PERF_COLOR_GREEN; } return color; @@ -318,15 +319,19 @@ int percent_color_fprintf(FILE *fp, const char *fmt, double percent) return r; } +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value) +{ + const char *color = get_percent_color(value); + return color_snprintf(bf, size, color, fmt, value); +} + int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...) { va_list args; double percent; - const char *color; va_start(args, fmt); percent = va_arg(args, double); va_end(args); - color = get_percent_color(percent); - return color_snprintf(bf, size, color, fmt, percent); + return value_color_snprintf(bf, size, fmt, percent); } diff --git a/tools/perf/util/color.h b/tools/perf/util/color.h index fced3840e99c..7ff30a62a132 100644 --- a/tools/perf/util/color.h +++ b/tools/perf/util/color.h @@ -39,6 +39,7 @@ int color_fprintf(FILE *fp, const char *color, const char *fmt, ...); int color_snprintf(char *bf, size_t size, const char *color, const char *fmt, ...); int color_fprintf_ln(FILE *fp, const char *color, const char *fmt, ...); int color_fwrite_lines(FILE *fp, const char *color, size_t count, const char *buf); +int value_color_snprintf(char *bf, size_t size, const char *fmt, double value); int percent_color_snprintf(char *bf, size_t size, const char *fmt, ...); int percent_color_fprintf(FILE *fp, const char *fmt, double percent); const char *get_percent_color(double percent); diff --git a/tools/perf/util/comm.c b/tools/perf/util/comm.c index ee0df0e24cdb..f9e777629e21 100644 --- a/tools/perf/util/comm.c +++ b/tools/perf/util/comm.c @@ -21,7 +21,7 @@ static void comm_str__put(struct comm_str *cs) { if (!--cs->ref) { rb_erase(&cs->rb_node, &comm_str_root); - free(cs->str); + zfree(&cs->str); free(cs); } } @@ -94,19 +94,20 @@ struct comm *comm__new(const char *str, u64 timestamp) return comm; } -void comm__override(struct comm *comm, const char *str, u64 timestamp) +int comm__override(struct comm *comm, const char *str, u64 timestamp) { - struct comm_str *old = comm->comm_str; + struct comm_str *new, *old = comm->comm_str; - comm->comm_str = comm_str__findnew(str, &comm_str_root); - if (!comm->comm_str) { - comm->comm_str = old; - return; - } + new = comm_str__findnew(str, &comm_str_root); + if (!new) + return -ENOMEM; - comm->start = timestamp; - comm_str__get(comm->comm_str); + comm_str__get(new); comm_str__put(old); + comm->comm_str = new; + comm->start = timestamp; + + return 0; } void comm__free(struct comm *comm) diff --git a/tools/perf/util/comm.h b/tools/perf/util/comm.h index 7a86e5656710..fac5bd51befc 100644 --- a/tools/perf/util/comm.h +++ b/tools/perf/util/comm.h @@ -16,6 +16,6 @@ struct comm { void comm__free(struct comm *comm); struct comm *comm__new(const char *str, u64 timestamp); const char *comm__str(const struct comm *comm); -void comm__override(struct comm *comm, const char *str, u64 timestamp); +int comm__override(struct comm *comm, const char *str, u64 timestamp); #endif /* __PERF_COMM_H */ diff --git a/tools/perf/util/data.c b/tools/perf/util/data.c index 7d09faf85cf1..1fbcd8bdc11b 100644 --- a/tools/perf/util/data.c +++ b/tools/perf/util/data.c @@ -118,3 +118,9 @@ void perf_data_file__close(struct perf_data_file *file) { close(file->fd); } + +ssize_t perf_data_file__write(struct perf_data_file *file, + void *buf, size_t size) +{ + return writen(file->fd, buf, size); +} diff --git a/tools/perf/util/data.h b/tools/perf/util/data.h index 8c2df80152a5..2b15d0c95c7f 100644 --- a/tools/perf/util/data.h +++ b/tools/perf/util/data.h @@ -9,12 +9,12 @@ enum perf_data_mode { }; struct perf_data_file { - const char *path; - int fd; - bool is_pipe; - bool force; - unsigned long size; - enum perf_data_mode mode; + const char *path; + int fd; + bool is_pipe; + bool force; + unsigned long size; + enum perf_data_mode mode; }; static inline bool perf_data_file__is_read(struct perf_data_file *file) @@ -44,5 +44,7 @@ static inline unsigned long perf_data_file__size(struct perf_data_file *file) int perf_data_file__open(struct perf_data_file *file); void perf_data_file__close(struct perf_data_file *file); +ssize_t perf_data_file__write(struct perf_data_file *file, + void *buf, size_t size); #endif /* __PERF_DATA_H */ diff --git a/tools/perf/util/debug.c b/tools/perf/util/debug.c index 399e74c34c1a..299b55586502 100644 --- a/tools/perf/util/debug.c +++ b/tools/perf/util/debug.c @@ -16,23 +16,46 @@ int verbose; bool dump_trace = false, quiet = false; -int eprintf(int level, const char *fmt, ...) +static int _eprintf(int level, const char *fmt, va_list args) { - va_list args; int ret = 0; if (verbose >= level) { - va_start(args, fmt); if (use_browser >= 1) ui_helpline__vshow(fmt, args); else ret = vfprintf(stderr, fmt, args); - va_end(args); } return ret; } +int eprintf(int level, const char *fmt, ...) +{ + va_list args; + int ret; + + va_start(args, fmt); + ret = _eprintf(level, fmt, args); + va_end(args); + + return ret; +} + +/* + * Overloading libtraceevent standard info print + * function, display with -v in perf. + */ +void pr_stat(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + _eprintf(1, fmt, args); + va_end(args); + eprintf(1, "\n"); +} + int dump_printf(const char *fmt, ...) { va_list args; diff --git a/tools/perf/util/debug.h b/tools/perf/util/debug.h index efbd98805ad0..443694c36b03 100644 --- a/tools/perf/util/debug.h +++ b/tools/perf/util/debug.h @@ -17,4 +17,6 @@ void trace_event(union perf_event *event); int ui__error(const char *format, ...) __attribute__((format(printf, 1, 2))); int ui__warning(const char *format, ...) __attribute__((format(printf, 1, 2))); +void pr_stat(const char *fmt, ...); + #endif /* __PERF_DEBUG_H */ diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c index af4c687cc49b..4045d086d9d9 100644 --- a/tools/perf/util/dso.c +++ b/tools/perf/util/dso.c @@ -28,8 +28,9 @@ char dso__symtab_origin(const struct dso *dso) return origin[dso->symtab_type]; } -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, - char *root_dir, char *file, size_t size) +int dso__read_binary_type_filename(const struct dso *dso, + enum dso_binary_type type, + char *root_dir, char *filename, size_t size) { char build_id_hex[BUILD_ID_SIZE * 2 + 1]; int ret = 0; @@ -38,36 +39,36 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, case DSO_BINARY_TYPE__DEBUGLINK: { char *debuglink; - strncpy(file, dso->long_name, size); - debuglink = file + dso->long_name_len; - while (debuglink != file && *debuglink != '/') + strncpy(filename, dso->long_name, size); + debuglink = filename + dso->long_name_len; + while (debuglink != filename && *debuglink != '/') debuglink--; if (*debuglink == '/') debuglink++; filename__read_debuglink(dso->long_name, debuglink, - size - (debuglink - file)); + size - (debuglink - filename)); } break; case DSO_BINARY_TYPE__BUILD_ID_CACHE: /* skip the locally configured cache if a symfs is given */ if (symbol_conf.symfs[0] || - (dso__build_id_filename(dso, file, size) == NULL)) + (dso__build_id_filename(dso, filename, size) == NULL)) ret = -1; break; case DSO_BINARY_TYPE__FEDORA_DEBUGINFO: - snprintf(file, size, "%s/usr/lib/debug%s.debug", + snprintf(filename, size, "%s/usr/lib/debug%s.debug", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__UBUNTU_DEBUGINFO: - snprintf(file, size, "%s/usr/lib/debug%s", + snprintf(filename, size, "%s/usr/lib/debug%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO: { - char *last_slash; + const char *last_slash; size_t len; size_t dir_size; @@ -75,14 +76,14 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, while (last_slash != dso->long_name && *last_slash != '/') last_slash--; - len = scnprintf(file, size, "%s", symbol_conf.symfs); + len = scnprintf(filename, size, "%s", symbol_conf.symfs); dir_size = last_slash - dso->long_name + 2; if (dir_size > (size - len)) { ret = -1; break; } - len += scnprintf(file + len, dir_size, "%s", dso->long_name); - len += scnprintf(file + len , size - len, ".debug%s", + len += scnprintf(filename + len, dir_size, "%s", dso->long_name); + len += scnprintf(filename + len , size - len, ".debug%s", last_slash); break; } @@ -96,7 +97,7 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, build_id__sprintf(dso->build_id, sizeof(dso->build_id), build_id_hex); - snprintf(file, size, + snprintf(filename, size, "%s/usr/lib/debug/.build-id/%.2s/%s.debug", symbol_conf.symfs, build_id_hex, build_id_hex + 2); break; @@ -104,23 +105,23 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, case DSO_BINARY_TYPE__VMLINUX: case DSO_BINARY_TYPE__GUEST_VMLINUX: case DSO_BINARY_TYPE__SYSTEM_PATH_DSO: - snprintf(file, size, "%s%s", + snprintf(filename, size, "%s%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__GUEST_KMODULE: - snprintf(file, size, "%s%s%s", symbol_conf.symfs, + snprintf(filename, size, "%s%s%s", symbol_conf.symfs, root_dir, dso->long_name); break; case DSO_BINARY_TYPE__SYSTEM_PATH_KMODULE: - snprintf(file, size, "%s%s", symbol_conf.symfs, + snprintf(filename, size, "%s%s", symbol_conf.symfs, dso->long_name); break; case DSO_BINARY_TYPE__KCORE: case DSO_BINARY_TYPE__GUEST_KCORE: - snprintf(file, size, "%s", dso->long_name); + snprintf(filename, size, "%s", dso->long_name); break; default: @@ -137,19 +138,18 @@ int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, static int open_dso(struct dso *dso, struct machine *machine) { - char *root_dir = (char *) ""; - char *name; int fd; + char *root_dir = (char *)""; + char *name = malloc(PATH_MAX); - name = malloc(PATH_MAX); if (!name) return -ENOMEM; if (machine) root_dir = machine->root_dir; - if (dso__binary_type_file(dso, dso->data_type, - root_dir, name, PATH_MAX)) { + if (dso__read_binary_type_filename(dso, dso->binary_type, + root_dir, name, PATH_MAX)) { free(name); return -EINVAL; } @@ -161,26 +161,26 @@ static int open_dso(struct dso *dso, struct machine *machine) int dso__data_fd(struct dso *dso, struct machine *machine) { - static enum dso_binary_type binary_type_data[] = { + enum dso_binary_type binary_type_data[] = { DSO_BINARY_TYPE__BUILD_ID_CACHE, DSO_BINARY_TYPE__SYSTEM_PATH_DSO, DSO_BINARY_TYPE__NOT_FOUND, }; int i = 0; - if (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND) + if (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND) return open_dso(dso, machine); do { int fd; - dso->data_type = binary_type_data[i++]; + dso->binary_type = binary_type_data[i++]; fd = open_dso(dso, machine); if (fd >= 0) return fd; - } while (dso->data_type != DSO_BINARY_TYPE__NOT_FOUND); + } while (dso->binary_type != DSO_BINARY_TYPE__NOT_FOUND); return -EINVAL; } @@ -200,11 +200,10 @@ dso_cache__free(struct rb_root *root) } } -static struct dso_cache* -dso_cache__find(struct rb_root *root, u64 offset) +static struct dso_cache *dso_cache__find(const struct rb_root *root, u64 offset) { - struct rb_node **p = &root->rb_node; - struct rb_node *parent = NULL; + struct rb_node * const *p = &root->rb_node; + const struct rb_node *parent = NULL; struct dso_cache *cache; while (*p != NULL) { @@ -379,32 +378,63 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, * processing we had no idea this was the kernel dso. */ if (dso != NULL) { - dso__set_short_name(dso, short_name); + dso__set_short_name(dso, short_name, false); dso->kernel = dso_type; } return dso; } -void dso__set_long_name(struct dso *dso, char *name) +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated) { if (name == NULL) return; - dso->long_name = name; - dso->long_name_len = strlen(name); + + if (dso->long_name_allocated) + free((char *)dso->long_name); + + dso->long_name = name; + dso->long_name_len = strlen(name); + dso->long_name_allocated = name_allocated; } -void dso__set_short_name(struct dso *dso, const char *name) +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated) { if (name == NULL) return; - dso->short_name = name; - dso->short_name_len = strlen(name); + + if (dso->short_name_allocated) + free((char *)dso->short_name); + + dso->short_name = name; + dso->short_name_len = strlen(name); + dso->short_name_allocated = name_allocated; } static void dso__set_basename(struct dso *dso) { - dso__set_short_name(dso, basename(dso->long_name)); + /* + * basename() may modify path buffer, so we must pass + * a copy. + */ + char *base, *lname = strdup(dso->long_name); + + if (!lname) + return; + + /* + * basename() may return a pointer to internal + * storage which is reused in subsequent calls + * so copy the result. + */ + base = strdup(basename(lname)); + + free(lname); + + if (!base) + return; + + dso__set_short_name(dso, base, true); } int dso__name_len(const struct dso *dso) @@ -439,18 +469,19 @@ struct dso *dso__new(const char *name) if (dso != NULL) { int i; strcpy(dso->name, name); - dso__set_long_name(dso, dso->name); - dso__set_short_name(dso, dso->name); + dso__set_long_name(dso, dso->name, false); + dso__set_short_name(dso, dso->name, false); for (i = 0; i < MAP__NR_TYPES; ++i) dso->symbols[i] = dso->symbol_names[i] = RB_ROOT; dso->cache = RB_ROOT; dso->symtab_type = DSO_BINARY_TYPE__NOT_FOUND; - dso->data_type = DSO_BINARY_TYPE__NOT_FOUND; + dso->binary_type = DSO_BINARY_TYPE__NOT_FOUND; dso->loaded = 0; dso->rel = 0; dso->sorted_by_name = 0; dso->has_build_id = 0; dso->has_srcline = 1; + dso->a2l_fails = 1; dso->kernel = DSO_TYPE_USER; dso->needs_swap = DSO_SWAP__UNSET; INIT_LIST_HEAD(&dso->node); @@ -464,11 +495,20 @@ void dso__delete(struct dso *dso) int i; for (i = 0; i < MAP__NR_TYPES; ++i) symbols__delete(&dso->symbols[i]); - if (dso->sname_alloc) - free((char *)dso->short_name); - if (dso->lname_alloc) - free(dso->long_name); + + if (dso->short_name_allocated) { + zfree((char **)&dso->short_name); + dso->short_name_allocated = false; + } + + if (dso->long_name_allocated) { + zfree((char **)&dso->long_name); + dso->long_name_allocated = false; + } + dso_cache__free(&dso->cache); + dso__free_a2l(dso); + zfree(&dso->symsrc_filename); free(dso); } @@ -543,7 +583,7 @@ void dsos__add(struct list_head *head, struct dso *dso) list_add_tail(&dso->node, head); } -struct dso *dsos__find(struct list_head *head, const char *name, bool cmp_short) +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short) { struct dso *pos; diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h index 9ac666abbe7e..cd7d6f078cdd 100644 --- a/tools/perf/util/dso.h +++ b/tools/perf/util/dso.h @@ -77,23 +77,26 @@ struct dso { struct rb_root symbols[MAP__NR_TYPES]; struct rb_root symbol_names[MAP__NR_TYPES]; struct rb_root cache; + void *a2l; + char *symsrc_filename; + unsigned int a2l_fails; enum dso_kernel_type kernel; enum dso_swap_type needs_swap; enum dso_binary_type symtab_type; - enum dso_binary_type data_type; + enum dso_binary_type binary_type; u8 adjust_symbols:1; u8 has_build_id:1; u8 has_srcline:1; u8 hit:1; u8 annotate_warned:1; - u8 sname_alloc:1; - u8 lname_alloc:1; + u8 short_name_allocated:1; + u8 long_name_allocated:1; u8 sorted_by_name; u8 loaded; u8 rel; u8 build_id[BUILD_ID_SIZE]; const char *short_name; - char *long_name; + const char *long_name; u16 long_name_len; u16 short_name_len; char name[0]; @@ -107,8 +110,8 @@ static inline void dso__set_loaded(struct dso *dso, enum map_type type) struct dso *dso__new(const char *name); void dso__delete(struct dso *dso); -void dso__set_short_name(struct dso *dso, const char *name); -void dso__set_long_name(struct dso *dso, char *name); +void dso__set_short_name(struct dso *dso, const char *name, bool name_allocated); +void dso__set_long_name(struct dso *dso, const char *name, bool name_allocated); int dso__name_len(const struct dso *dso); @@ -125,8 +128,8 @@ void dso__read_running_kernel_build_id(struct dso *dso, int dso__kernel_module_get_build_id(struct dso *dso, const char *root_dir); char dso__symtab_origin(const struct dso *dso); -int dso__binary_type_file(struct dso *dso, enum dso_binary_type type, - char *root_dir, char *file, size_t size); +int dso__read_binary_type_filename(const struct dso *dso, enum dso_binary_type type, + char *root_dir, char *filename, size_t size); int dso__data_fd(struct dso *dso, struct machine *machine); ssize_t dso__data_read_offset(struct dso *dso, struct machine *machine, @@ -140,7 +143,7 @@ struct dso *dso__kernel_findnew(struct machine *machine, const char *name, const char *short_name, int dso_type); void dsos__add(struct list_head *head, struct dso *dso); -struct dso *dsos__find(struct list_head *head, const char *name, +struct dso *dsos__find(const struct list_head *head, const char *name, bool cmp_short); struct dso *__dsos__findnew(struct list_head *head, const char *name); bool __dsos__read_build_ids(struct list_head *head, bool with_hits); @@ -156,14 +159,16 @@ size_t dso__fprintf(struct dso *dso, enum map_type type, FILE *fp); static inline bool dso__is_vmlinux(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__VMLINUX || - dso->data_type == DSO_BINARY_TYPE__GUEST_VMLINUX; + return dso->binary_type == DSO_BINARY_TYPE__VMLINUX || + dso->binary_type == DSO_BINARY_TYPE__GUEST_VMLINUX; } static inline bool dso__is_kcore(struct dso *dso) { - return dso->data_type == DSO_BINARY_TYPE__KCORE || - dso->data_type == DSO_BINARY_TYPE__GUEST_KCORE; + return dso->binary_type == DSO_BINARY_TYPE__KCORE || + dso->binary_type == DSO_BINARY_TYPE__GUEST_KCORE; } +void dso__free_a2l(struct dso *dso); + #endif /* __PERF_DSO */ diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index bb788c109fe6..1fc1c2f04772 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -7,6 +7,7 @@ #include "strlist.h" #include "thread.h" #include "thread_map.h" +#include "symbol/kallsyms.h" static const char *perf_event__names[] = { [0] = "TOTAL", @@ -105,8 +106,12 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, memset(&event->comm, 0, sizeof(event->comm)); - tgid = perf_event__get_comm_tgid(pid, event->comm.comm, - sizeof(event->comm.comm)); + if (machine__is_host(machine)) + tgid = perf_event__get_comm_tgid(pid, event->comm.comm, + sizeof(event->comm.comm)); + else + tgid = machine->pid; + if (tgid < 0) goto out; @@ -128,7 +133,11 @@ static pid_t perf_event__synthesize_comm(struct perf_tool *tool, goto out; } - snprintf(filename, sizeof(filename), "/proc/%d/task", pid); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(filename, sizeof(filename), "%s/proc/%d/task", + machine->root_dir, pid); tasks = opendir(filename); if (tasks == NULL) { @@ -166,18 +175,22 @@ out: return tgid; } -static int perf_event__synthesize_mmap_events(struct perf_tool *tool, - union perf_event *event, - pid_t pid, pid_t tgid, - perf_event__handler_t process, - struct machine *machine, - bool mmap_data) +int perf_event__synthesize_mmap_events(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine, + bool mmap_data) { char filename[PATH_MAX]; FILE *fp; int rc = 0; - snprintf(filename, sizeof(filename), "/proc/%d/maps", pid); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(filename, sizeof(filename), "%s/proc/%d/maps", + machine->root_dir, pid); fp = fopen(filename, "r"); if (fp == NULL) { @@ -217,7 +230,10 @@ static int perf_event__synthesize_mmap_events(struct perf_tool *tool, /* * Just like the kernel, see __perf_event_mmap in kernel/perf_event.c */ - event->header.misc = PERF_RECORD_MISC_USER; + if (machine__is_host(machine)) + event->header.misc = PERF_RECORD_MISC_USER; + else + event->header.misc = PERF_RECORD_MISC_GUEST_USER; if (prot[2] != 'x') { if (!mmap_data || prot[0] != 'r') @@ -386,6 +402,7 @@ int perf_event__synthesize_threads(struct perf_tool *tool, struct machine *machine, bool mmap_data) { DIR *proc; + char proc_path[PATH_MAX]; struct dirent dirent, *next; union perf_event *comm_event, *mmap_event; int err = -1; @@ -398,7 +415,12 @@ int perf_event__synthesize_threads(struct perf_tool *tool, if (mmap_event == NULL) goto out_free_comm; - proc = opendir("/proc"); + if (machine__is_default_guest(machine)) + return 0; + + snprintf(proc_path, sizeof(proc_path), "%s/proc", machine->root_dir); + proc = opendir(proc_path); + if (proc == NULL) goto out_free_mmap; @@ -637,6 +659,7 @@ void thread__find_addr_map(struct thread *thread, struct map_groups *mg = &thread->mg; bool load_map = false; + al->machine = machine; al->thread = thread; al->addr = addr; al->cpumode = cpumode; @@ -657,15 +680,10 @@ void thread__find_addr_map(struct thread *thread, al->level = 'g'; mg = &machine->kmaps; load_map = true; + } else if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) { + al->level = 'u'; } else { - /* - * 'u' means guest os user space. - * TODO: We don't support guest user space. Might support late. - */ - if (cpumode == PERF_RECORD_MISC_GUEST_USER && perf_guest) - al->level = 'u'; - else - al->level = 'H'; + al->level = 'H'; al->map = NULL; if ((cpumode == PERF_RECORD_MISC_GUEST_USER || @@ -732,8 +750,7 @@ int perf_event__preprocess_sample(const union perf_event *event, if (thread == NULL) return -1; - if (symbol_conf.comm_list && - !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) + if (thread__is_filtered(thread)) goto out_filtered; dump_printf(" ... thread: %s:%d\n", thread__comm_str(thread), thread->tid); diff --git a/tools/perf/util/event.h b/tools/perf/util/event.h index 30fec9901e44..faf6e219be21 100644 --- a/tools/perf/util/event.h +++ b/tools/perf/util/event.h @@ -266,6 +266,13 @@ int perf_event__synthesize_sample(union perf_event *event, u64 type, const struct perf_sample *sample, bool swapped); +int perf_event__synthesize_mmap_events(struct perf_tool *tool, + union perf_event *event, + pid_t pid, pid_t tgid, + perf_event__handler_t process, + struct machine *machine, + bool mmap_data); + size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp); size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp); diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index bbc746aa5716..40bd2c04df8a 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c @@ -7,7 +7,7 @@ * Released under the GPL v2. (and only v2, not any later version) */ #include "util.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <poll.h> #include "cpumap.h" #include "thread_map.h" @@ -81,7 +81,7 @@ static void perf_evlist__update_id_pos(struct perf_evlist *evlist) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__calc_id_pos(evsel); perf_evlist__set_id_pos(evlist); @@ -91,7 +91,7 @@ static void perf_evlist__purge(struct perf_evlist *evlist) { struct perf_evsel *pos, *n; - list_for_each_entry_safe(pos, n, &evlist->entries, node) { + evlist__for_each_safe(evlist, n, pos) { list_del_init(&pos->node); perf_evsel__delete(pos); } @@ -101,14 +101,18 @@ static void perf_evlist__purge(struct perf_evlist *evlist) void perf_evlist__exit(struct perf_evlist *evlist) { - free(evlist->mmap); - free(evlist->pollfd); - evlist->mmap = NULL; - evlist->pollfd = NULL; + zfree(&evlist->mmap); + zfree(&evlist->pollfd); } void perf_evlist__delete(struct perf_evlist *evlist) { + perf_evlist__munmap(evlist); + perf_evlist__close(evlist); + cpu_map__delete(evlist->cpus); + thread_map__delete(evlist->threads); + evlist->cpus = NULL; + evlist->threads = NULL; perf_evlist__purge(evlist); perf_evlist__exit(evlist); free(evlist); @@ -144,7 +148,7 @@ void __perf_evlist__set_leader(struct list_head *list) leader->nr_members = evsel->idx - leader->idx + 1; - list_for_each_entry(evsel, list, node) { + __evlist__for_each(list, evsel) { evsel->leader = leader; } } @@ -203,7 +207,7 @@ static int perf_evlist__add_attrs(struct perf_evlist *evlist, return 0; out_delete_partial_list: - list_for_each_entry_safe(evsel, n, &head, node) + __evlist__for_each_safe(&head, n, evsel) perf_evsel__delete(evsel); return -1; } @@ -224,7 +228,7 @@ perf_evlist__find_tracepoint_by_id(struct perf_evlist *evlist, int id) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->attr.type == PERF_TYPE_TRACEPOINT && (int)evsel->attr.config == id) return evsel; @@ -239,7 +243,7 @@ perf_evlist__find_tracepoint_by_name(struct perf_evlist *evlist, { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if ((evsel->attr.type == PERF_TYPE_TRACEPOINT) && (strcmp(evsel->name, name) == 0)) return evsel; @@ -269,7 +273,7 @@ void perf_evlist__disable(struct perf_evlist *evlist) int nr_threads = thread_map__nr(evlist->threads); for (cpu = 0; cpu < nr_cpus; cpu++) { - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (!perf_evsel__is_group_leader(pos) || !pos->fd) continue; for (thread = 0; thread < nr_threads; thread++) @@ -287,7 +291,7 @@ void perf_evlist__enable(struct perf_evlist *evlist) int nr_threads = thread_map__nr(evlist->threads); for (cpu = 0; cpu < nr_cpus; cpu++) { - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (!perf_evsel__is_group_leader(pos) || !pos->fd) continue; for (thread = 0; thread < nr_threads; thread++) @@ -584,11 +588,13 @@ void perf_evlist__munmap(struct perf_evlist *evlist) { int i; + if (evlist->mmap == NULL) + return; + for (i = 0; i < evlist->nr_mmaps; i++) __perf_evlist__munmap(evlist, i); - free(evlist->mmap); - evlist->mmap = NULL; + zfree(&evlist->mmap); } static int perf_evlist__alloc_mmap(struct perf_evlist *evlist) @@ -624,7 +630,7 @@ static int perf_evlist__mmap_per_evsel(struct perf_evlist *evlist, int idx, { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { int fd = FD(evsel, cpu, thread); if (*output == -1) { @@ -732,11 +738,13 @@ static long parse_pages_arg(const char *str, unsigned long min, return -EINVAL; } - if ((pages == 0) && (min == 0)) { + if (pages == 0 && min == 0) { /* leave number of pages at 0 */ - } else if (pages < (1UL << 31) && !is_power_of_2(pages)) { + } else if (!is_power_of_2(pages)) { /* round pages up to next power of 2 */ - pages = next_pow2(pages); + pages = next_pow2_l(pages); + if (!pages) + return -EINVAL; pr_info("rounding mmap pages size to %lu bytes (%lu pages)\n", pages * page_size, pages); } @@ -754,7 +762,7 @@ int perf_evlist__parse_mmap_pages(const struct option *opt, const char *str, unsigned long max = UINT_MAX; long pages; - if (max < SIZE_MAX / page_size) + if (max > SIZE_MAX / page_size) max = SIZE_MAX / page_size; pages = parse_pages_arg(str, 1, max); @@ -798,7 +806,7 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages, pr_debug("mmap size %zuB\n", evlist->mmap_len); mask = evlist->mmap_len - page_size - 1; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if ((evsel->attr.read_format & PERF_FORMAT_ID) && evsel->sample_id == NULL && perf_evsel__alloc_id(evsel, cpu_map__nr(cpus), threads->nr) < 0) @@ -819,11 +827,7 @@ int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target) if (evlist->threads == NULL) return -1; - if (target->force_per_cpu) - evlist->cpus = cpu_map__new(target->cpu_list); - else if (target__has_task(target)) - evlist->cpus = cpu_map__dummy_new(); - else if (!target__has_cpu(target) && !target->uses_mmap) + if (target__uses_dummy_map(target)) evlist->cpus = cpu_map__dummy_new(); else evlist->cpus = cpu_map__new(target->cpu_list); @@ -838,14 +842,6 @@ out_delete_threads: return -1; } -void perf_evlist__delete_maps(struct perf_evlist *evlist) -{ - cpu_map__delete(evlist->cpus); - thread_map__delete(evlist->threads); - evlist->cpus = NULL; - evlist->threads = NULL; -} - int perf_evlist__apply_filters(struct perf_evlist *evlist) { struct perf_evsel *evsel; @@ -853,7 +849,7 @@ int perf_evlist__apply_filters(struct perf_evlist *evlist) const int ncpus = cpu_map__nr(evlist->cpus), nthreads = thread_map__nr(evlist->threads); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->filter == NULL) continue; @@ -872,7 +868,7 @@ int perf_evlist__set_filter(struct perf_evlist *evlist, const char *filter) const int ncpus = cpu_map__nr(evlist->cpus), nthreads = thread_map__nr(evlist->threads); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { err = perf_evsel__set_filter(evsel, ncpus, nthreads, filter); if (err) break; @@ -891,7 +887,7 @@ bool perf_evlist__valid_sample_type(struct perf_evlist *evlist) if (evlist->id_pos < 0 || evlist->is_pos < 0) return false; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (pos->id_pos != evlist->id_pos || pos->is_pos != evlist->is_pos) return false; @@ -907,7 +903,7 @@ u64 __perf_evlist__combined_sample_type(struct perf_evlist *evlist) if (evlist->combined_sample_type) return evlist->combined_sample_type; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) evlist->combined_sample_type |= evsel->attr.sample_type; return evlist->combined_sample_type; @@ -925,7 +921,7 @@ bool perf_evlist__valid_read_format(struct perf_evlist *evlist) u64 read_format = first->attr.read_format; u64 sample_type = first->attr.sample_type; - list_for_each_entry_continue(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (read_format != pos->attr.read_format) return false; } @@ -982,7 +978,7 @@ bool perf_evlist__valid_sample_id_all(struct perf_evlist *evlist) { struct perf_evsel *first = perf_evlist__first(evlist), *pos = first; - list_for_each_entry_continue(pos, &evlist->entries, node) { + evlist__for_each_continue(evlist, pos) { if (first->attr.sample_id_all != pos->attr.sample_id_all) return false; } @@ -1008,7 +1004,7 @@ void perf_evlist__close(struct perf_evlist *evlist) int ncpus = cpu_map__nr(evlist->cpus); int nthreads = thread_map__nr(evlist->threads); - list_for_each_entry_reverse(evsel, &evlist->entries, node) + evlist__for_each_reverse(evlist, evsel) perf_evsel__close(evsel, ncpus, nthreads); } @@ -1019,7 +1015,7 @@ int perf_evlist__open(struct perf_evlist *evlist) perf_evlist__update_id_pos(evlist); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { err = perf_evsel__open(evsel, evlist->cpus, evlist->threads); if (err < 0) goto out_err; @@ -1034,7 +1030,7 @@ out_err: int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, const char *argv[], bool pipe_output, - bool want_signal) + void (*exec_error)(int signo, siginfo_t *info, void *ucontext)) { int child_ready_pipe[2], go_pipe[2]; char bf; @@ -1078,12 +1074,25 @@ int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *tar execvp(argv[0], (char **)argv); - perror(argv[0]); - if (want_signal) - kill(getppid(), SIGUSR1); + if (exec_error) { + union sigval val; + + val.sival_int = errno; + if (sigqueue(getppid(), SIGUSR1, val)) + perror(argv[0]); + } else + perror(argv[0]); exit(-1); } + if (exec_error) { + struct sigaction act = { + .sa_flags = SA_SIGINFO, + .sa_sigaction = exec_error, + }; + sigaction(SIGUSR1, &act, NULL); + } + if (target__none(target)) evlist->threads->map[0] = evlist->workload.pid; @@ -1145,7 +1154,7 @@ size_t perf_evlist__fprintf(struct perf_evlist *evlist, FILE *fp) struct perf_evsel *evsel; size_t printed = 0; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { printed += fprintf(fp, "%s%s", evsel->idx ? ", " : "", perf_evsel__name(evsel)); } @@ -1193,8 +1202,7 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, "Error:\t%s.\n" "Hint:\tCheck /proc/sys/kernel/perf_event_paranoid setting.", emsg); - if (filename__read_int("/proc/sys/kernel/perf_event_paranoid", &value)) - break; + value = perf_event_paranoid(); printed += scnprintf(buf + printed, size - printed, "\nHint:\t"); @@ -1215,3 +1223,20 @@ int perf_evlist__strerror_open(struct perf_evlist *evlist __maybe_unused, return 0; } + +void perf_evlist__to_front(struct perf_evlist *evlist, + struct perf_evsel *move_evsel) +{ + struct perf_evsel *evsel, *n; + LIST_HEAD(move); + + if (move_evsel == perf_evlist__first(evlist)) + return; + + evlist__for_each_safe(evlist, n, evsel) { + if (evsel->leader == move_evsel->leader) + list_move_tail(&evsel->node, &move); + } + + list_splice(&move, &evlist->entries); +} diff --git a/tools/perf/util/evlist.h b/tools/perf/util/evlist.h index 649d6ea98a84..f5173cd63693 100644 --- a/tools/perf/util/evlist.h +++ b/tools/perf/util/evlist.h @@ -12,7 +12,7 @@ struct pollfd; struct thread_map; struct cpu_map; -struct perf_record_opts; +struct record_opts; #define PERF_EVLIST__HLIST_BITS 8 #define PERF_EVLIST__HLIST_SIZE (1 << PERF_EVLIST__HLIST_BITS) @@ -97,14 +97,14 @@ void perf_evlist__close(struct perf_evlist *evlist); void perf_evlist__set_id_pos(struct perf_evlist *evlist); bool perf_can_sample_identifier(void); -void perf_evlist__config(struct perf_evlist *evlist, - struct perf_record_opts *opts); -int perf_record_opts__config(struct perf_record_opts *opts); +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts); +int record_opts__config(struct record_opts *opts); int perf_evlist__prepare_workload(struct perf_evlist *evlist, struct target *target, const char *argv[], bool pipe_output, - bool want_signal); + void (*exec_error)(int signo, siginfo_t *info, + void *ucontext)); int perf_evlist__start_workload(struct perf_evlist *evlist); int perf_evlist__parse_mmap_pages(const struct option *opt, @@ -135,7 +135,6 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist, } int perf_evlist__create_maps(struct perf_evlist *evlist, struct target *target); -void perf_evlist__delete_maps(struct perf_evlist *evlist); int perf_evlist__apply_filters(struct perf_evlist *evlist); void __perf_evlist__set_leader(struct list_head *list); @@ -193,4 +192,74 @@ static inline void perf_mmap__write_tail(struct perf_mmap *md, pc->data_tail = tail; } +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str); +void perf_evlist__to_front(struct perf_evlist *evlist, + struct perf_evsel *move_evsel); + +/** + * __evlist__for_each - iterate thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each(list, evsel) \ + list_for_each_entry(evsel, list, node) + +/** + * evlist__for_each - iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each(evlist, evsel) \ + __evlist__for_each(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_continue - continue iteration thru all the evsels + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_continue(list, evsel) \ + list_for_each_entry_continue(evsel, list, node) + +/** + * evlist__for_each_continue - continue iteration thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_continue(evlist, evsel) \ + __evlist__for_each_continue(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @list: list_head instance to iterate + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_reverse(list, evsel) \ + list_for_each_entry_reverse(evsel, list, node) + +/** + * evlist__for_each_reverse - iterate thru all the evsels in reverse order + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + */ +#define evlist__for_each_reverse(evlist, evsel) \ + __evlist__for_each_reverse(&(evlist)->entries, evsel) + +/** + * __evlist__for_each_safe - safely iterate thru all the evsels + * @list: list_head instance to iterate + * @tmp: struct evsel temp iterator + * @evsel: struct evsel iterator + */ +#define __evlist__for_each_safe(list, tmp, evsel) \ + list_for_each_entry_safe(evsel, tmp, list, node) + +/** + * evlist__for_each_safe - safely iterate thru all the evsels + * @evlist: evlist instance to iterate + * @evsel: struct evsel iterator + * @tmp: struct evsel temp iterator + */ +#define evlist__for_each_safe(evlist, tmp, evsel) \ + __evlist__for_each_safe(&(evlist)->entries, tmp, evsel) + #endif /* __PERF_EVLIST_H */ diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 46dd4c2a41ce..22e18a26b7e6 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c @@ -9,7 +9,7 @@ #include <byteswap.h> #include <linux/bitops.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <traceevent/event-parse.h> #include <linux/hw_breakpoint.h> #include <linux/perf_event.h> @@ -23,6 +23,7 @@ #include "target.h" #include "perf_regs.h" #include "debug.h" +#include "trace-event.h" static struct { bool sample_id_all; @@ -162,6 +163,8 @@ void perf_evsel__init(struct perf_evsel *evsel, evsel->idx = idx; evsel->attr = *attr; evsel->leader = evsel; + evsel->unit = ""; + evsel->scale = 1.0; INIT_LIST_HEAD(&evsel->node); hists__init(&evsel->hists); evsel->sample_size = __perf_evsel__sample_size(attr->sample_type); @@ -178,47 +181,6 @@ struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx) return evsel; } -struct event_format *event_format__new(const char *sys, const char *name) -{ - int fd, n; - char *filename; - void *bf = NULL, *nbf; - size_t size = 0, alloc_size = 0; - struct event_format *format = NULL; - - if (asprintf(&filename, "%s/%s/%s/format", tracing_events_path, sys, name) < 0) - goto out; - - fd = open(filename, O_RDONLY); - if (fd < 0) - goto out_free_filename; - - do { - if (size == alloc_size) { - alloc_size += BUFSIZ; - nbf = realloc(bf, alloc_size); - if (nbf == NULL) - goto out_free_bf; - bf = nbf; - } - - n = read(fd, bf + size, alloc_size - size); - if (n < 0) - goto out_free_bf; - size += n; - } while (n > 0); - - pevent_parse_format(&format, bf, size, sys); - -out_free_bf: - free(bf); - close(fd); -out_free_filename: - free(filename); -out: - return format; -} - struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int idx) { struct perf_evsel *evsel = zalloc(sizeof(*evsel)); @@ -233,7 +195,7 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int if (asprintf(&evsel->name, "%s:%s", sys, name) < 0) goto out_free; - evsel->tp_format = event_format__new(sys, name); + evsel->tp_format = trace_event__tp_format(sys, name); if (evsel->tp_format == NULL) goto out_free; @@ -246,7 +208,7 @@ struct perf_evsel *perf_evsel__newtp_idx(const char *sys, const char *name, int return evsel; out_free: - free(evsel->name); + zfree(&evsel->name); free(evsel); return NULL; } @@ -566,12 +528,12 @@ int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size) * enable/disable events specifically, as there's no * initial traced exec call. */ -void perf_evsel__config(struct perf_evsel *evsel, - struct perf_record_opts *opts) +void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts) { struct perf_evsel *leader = evsel->leader; struct perf_event_attr *attr = &evsel->attr; int track = !evsel->idx; /* only the first counter needs these */ + bool per_cpu = opts->target.default_per_cpu && !opts->target.per_thread; attr->sample_id_all = perf_missing_features.sample_id_all ? 0 : 1; attr->inherit = !opts->no_inherit; @@ -645,7 +607,7 @@ void perf_evsel__config(struct perf_evsel *evsel, } } - if (target__has_cpu(&opts->target) || opts->target.force_per_cpu) + if (target__has_cpu(&opts->target)) perf_evsel__set_sample_bit(evsel, CPU); if (opts->period) @@ -653,7 +615,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (!perf_missing_features.sample_id_all && (opts->sample_time || !opts->no_inherit || - target__has_cpu(&opts->target) || opts->target.force_per_cpu)) + target__has_cpu(&opts->target) || per_cpu)) perf_evsel__set_sample_bit(evsel, TIME); if (opts->raw_samples) { @@ -665,7 +627,7 @@ void perf_evsel__config(struct perf_evsel *evsel, if (opts->sample_address) perf_evsel__set_sample_bit(evsel, DATA_SRC); - if (opts->no_delay) { + if (opts->no_buffering) { attr->watermark = 0; attr->wakeup_events = 1; } @@ -696,7 +658,8 @@ void perf_evsel__config(struct perf_evsel *evsel, * Setting enable_on_exec for independent events and * group leaders for traced executed by perf. */ - if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel)) + if (target__none(&opts->target) && perf_evsel__is_group_leader(evsel) && + !opts->initial_delay) attr->enable_on_exec = 1; } @@ -788,8 +751,7 @@ void perf_evsel__free_id(struct perf_evsel *evsel) { xyarray__delete(evsel->sample_id); evsel->sample_id = NULL; - free(evsel->id); - evsel->id = NULL; + zfree(&evsel->id); } void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) @@ -805,7 +767,7 @@ void perf_evsel__close_fd(struct perf_evsel *evsel, int ncpus, int nthreads) void perf_evsel__free_counts(struct perf_evsel *evsel) { - free(evsel->counts); + zfree(&evsel->counts); } void perf_evsel__exit(struct perf_evsel *evsel) @@ -819,10 +781,10 @@ void perf_evsel__delete(struct perf_evsel *evsel) { perf_evsel__exit(evsel); close_cgroup(evsel->cgrp); - free(evsel->group_name); + zfree(&evsel->group_name); if (evsel->tp_format) pevent_free_format(evsel->tp_format); - free(evsel->name); + zfree(&evsel->name); free(evsel); } @@ -1998,8 +1960,7 @@ bool perf_evsel__fallback(struct perf_evsel *evsel, int err, evsel->attr.type = PERF_TYPE_SOFTWARE; evsel->attr.config = PERF_COUNT_SW_CPU_CLOCK; - free(evsel->name); - evsel->name = NULL; + zfree(&evsel->name); return true; } diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 1ea7c92e6e33..f1b325665aae 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h @@ -68,6 +68,8 @@ struct perf_evsel { u32 ids; struct hists hists; char *name; + double scale; + const char *unit; struct event_format *tp_format; union { void *priv; @@ -94,7 +96,7 @@ struct perf_evsel { struct cpu_map; struct thread_map; struct perf_evlist; -struct perf_record_opts; +struct record_opts; struct perf_evsel *perf_evsel__new_idx(struct perf_event_attr *attr, int idx); @@ -118,7 +120,7 @@ void perf_evsel__exit(struct perf_evsel *evsel); void perf_evsel__delete(struct perf_evsel *evsel); void perf_evsel__config(struct perf_evsel *evsel, - struct perf_record_opts *opts); + struct record_opts *opts); int __perf_evsel__sample_size(u64 sample_type); void perf_evsel__calc_id_pos(struct perf_evsel *evsel); @@ -138,6 +140,7 @@ extern const char *perf_evsel__sw_names[PERF_COUNT_SW_MAX]; int __perf_evsel__hw_cache_type_op_res_name(u8 type, u8 op, u8 result, char *bf, size_t size); const char *perf_evsel__name(struct perf_evsel *evsel); + const char *perf_evsel__group_name(struct perf_evsel *evsel); int perf_evsel__group_desc(struct perf_evsel *evsel, char *buf, size_t size); diff --git a/tools/perf/util/header.c b/tools/perf/util/header.c index 1cd035708931..bb3e0ede6183 100644 --- a/tools/perf/util/header.c +++ b/tools/perf/util/header.c @@ -177,7 +177,7 @@ perf_header__set_cmdline(int argc, const char **argv) continue; \ else -static int write_buildid(char *name, size_t name_len, u8 *build_id, +static int write_buildid(const char *name, size_t name_len, u8 *build_id, pid_t pid, u16 misc, int fd) { int err; @@ -209,7 +209,7 @@ static int __dsos__write_buildid_table(struct list_head *head, dsos__for_each_with_build_id(pos, head) { int err; - char *name; + const char *name; size_t name_len; if (!pos->hit) @@ -387,7 +387,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine, { bool is_kallsyms = dso->kernel && dso->long_name[0] != '/'; bool is_vdso = is_vdso_map(dso->short_name); - char *name = dso->long_name; + const char *name = dso->long_name; char nm[PATH_MAX]; if (dso__is_kcore(dso)) { @@ -643,8 +643,7 @@ static int write_event_desc(int fd, struct perf_header *h __maybe_unused, if (ret < 0) return ret; - list_for_each_entry(evsel, &evlist->entries, node) { - + evlist__for_each(evlist, evsel) { ret = do_write(fd, &evsel->attr, sz); if (ret < 0) return ret; @@ -800,10 +799,10 @@ static void free_cpu_topo(struct cpu_topo *tp) return; for (i = 0 ; i < tp->core_sib; i++) - free(tp->core_siblings[i]); + zfree(&tp->core_siblings[i]); for (i = 0 ; i < tp->thread_sib; i++) - free(tp->thread_siblings[i]); + zfree(&tp->thread_siblings[i]); free(tp); } @@ -1092,7 +1091,7 @@ static int write_group_desc(int fd, struct perf_header *h __maybe_unused, if (ret < 0) return ret; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1) { const char *name = evsel->group_name ?: "{anon_group}"; @@ -1232,10 +1231,8 @@ static void free_event_desc(struct perf_evsel *events) return; for (evsel = events; evsel->attr.size; evsel++) { - if (evsel->name) - free(evsel->name); - if (evsel->id) - free(evsel->id); + zfree(&evsel->name); + zfree(&evsel->id); } free(events); @@ -1326,8 +1323,7 @@ read_event_desc(struct perf_header *ph, int fd) } } out: - if (buf) - free(buf); + free(buf); return events; error: if (events) @@ -1490,7 +1486,7 @@ static void print_group_desc(struct perf_header *ph, int fd __maybe_unused, session = container_of(ph, struct perf_session, header); - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (perf_evsel__is_group_leader(evsel) && evsel->nr_members > 1) { fprintf(fp, "# group: %s{%s", evsel->group_name ?: "", @@ -1709,7 +1705,7 @@ static int process_nrcpus(struct perf_file_section *section __maybe_unused, struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr; ret = readn(fd, &nr, sizeof(nr)); @@ -1753,7 +1749,7 @@ static int process_total_mem(struct perf_file_section *section __maybe_unused, void *data __maybe_unused) { uint64_t mem; - size_t ret; + ssize_t ret; ret = readn(fd, &mem, sizeof(mem)); if (ret != sizeof(mem)) @@ -1771,7 +1767,7 @@ perf_evlist__find_by_index(struct perf_evlist *evlist, int idx) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->idx == idx) return evsel; } @@ -1822,7 +1818,7 @@ static int process_cmdline(struct perf_file_section *section __maybe_unused, struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; char *str; u32 nr, i; struct strbuf sb; @@ -1858,7 +1854,7 @@ static int process_cpu_topology(struct perf_file_section *section __maybe_unused struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr, i; char *str; struct strbuf sb; @@ -1914,7 +1910,7 @@ static int process_numa_topology(struct perf_file_section *section __maybe_unuse struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; u32 nr, node, i; char *str; uint64_t mem_total, mem_free; @@ -1974,7 +1970,7 @@ static int process_pmu_mappings(struct perf_file_section *section __maybe_unused struct perf_header *ph, int fd, void *data __maybe_unused) { - size_t ret; + ssize_t ret; char *name; u32 pmu_num; u32 type; @@ -2074,7 +2070,7 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused, session->evlist->nr_groups = nr_groups; i = nr = 0; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (evsel->idx == (int) desc[i].leader_idx) { evsel->leader = evsel; /* {anon_group} is a dummy name */ @@ -2108,7 +2104,7 @@ static int process_group_desc(struct perf_file_section *section __maybe_unused, ret = 0; out_free: for (i = 0; i < nr_groups; i++) - free(desc[i].name); + zfree(&desc[i].name); free(desc); return ret; @@ -2301,7 +2297,7 @@ int perf_session__write_header(struct perf_session *session, lseek(fd, sizeof(f_header), SEEK_SET); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { evsel->id_offset = lseek(fd, 0, SEEK_CUR); err = do_write(fd, evsel->id, evsel->ids * sizeof(u64)); if (err < 0) { @@ -2312,7 +2308,7 @@ int perf_session__write_header(struct perf_session *session, attr_offset = lseek(fd, 0, SEEK_CUR); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { f_attr = (struct perf_file_attr){ .attr = evsel->attr, .ids = { @@ -2327,7 +2323,8 @@ int perf_session__write_header(struct perf_session *session, } } - header->data_offset = lseek(fd, 0, SEEK_CUR); + if (!header->data_offset) + header->data_offset = lseek(fd, 0, SEEK_CUR); header->feat_offset = header->data_offset + header->data_size; if (at_exit) { @@ -2534,7 +2531,7 @@ static int check_magic_endian(u64 magic, uint64_t hdr_sz, int perf_file_header__read(struct perf_file_header *header, struct perf_header *ph, int fd) { - int ret; + ssize_t ret; lseek(fd, 0, SEEK_SET); @@ -2628,7 +2625,7 @@ static int perf_file_header__read_pipe(struct perf_pipe_file_header *header, struct perf_header *ph, int fd, bool repipe) { - int ret; + ssize_t ret; ret = readn(fd, header, sizeof(*header)); if (ret <= 0) @@ -2669,7 +2666,7 @@ static int read_attr(int fd, struct perf_header *ph, struct perf_event_attr *attr = &f_attr->attr; size_t sz, left; size_t our_sz = sizeof(f_attr->attr); - int ret; + ssize_t ret; memset(f_attr, 0, sizeof(*f_attr)); @@ -2744,7 +2741,7 @@ static int perf_evlist__prepare_tracepoint_events(struct perf_evlist *evlist, { struct perf_evsel *pos; - list_for_each_entry(pos, &evlist->entries, node) { + evlist__for_each(evlist, pos) { if (pos->attr.type == PERF_TYPE_TRACEPOINT && perf_evsel__prepare_tracepoint_event(pos, pevent)) return -1; @@ -2834,11 +2831,11 @@ int perf_session__read_header(struct perf_session *session) symbol_conf.nr_events = nr_attrs; - perf_header__process_sections(header, fd, &session->pevent, + perf_header__process_sections(header, fd, &session->tevent, perf_file_section__process); if (perf_evlist__prepare_tracepoint_events(session->evlist, - session->pevent)) + session->tevent.pevent)) goto out_delete_evlist; return 0; @@ -2892,7 +2889,7 @@ int perf_event__synthesize_attrs(struct perf_tool *tool, struct perf_evsel *evsel; int err = 0; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { err = perf_event__synthesize_attr(tool, &evsel->attr, evsel->ids, evsel->id, process); if (err) { @@ -3003,7 +3000,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, lseek(fd, offset + sizeof(struct tracing_data_event), SEEK_SET); - size_read = trace_report(fd, &session->pevent, + size_read = trace_report(fd, &session->tevent, session->repipe); padding = PERF_ALIGN(size_read, sizeof(u64)) - size_read; @@ -3025,7 +3022,7 @@ int perf_event__process_tracing_data(struct perf_tool *tool __maybe_unused, } perf_evlist__prepare_tracepoint_events(session->evlist, - session->pevent); + session->tevent.pevent); return size_read + padding; } diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index 307c9aed972e..a2d047bdf4ef 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h @@ -77,16 +77,16 @@ struct perf_session_env { unsigned long long total_mem; int nr_cmdline; - char *cmdline; int nr_sibling_cores; - char *sibling_cores; int nr_sibling_threads; - char *sibling_threads; int nr_numa_nodes; - char *numa_nodes; int nr_pmu_mappings; - char *pmu_mappings; int nr_groups; + char *cmdline; + char *sibling_cores; + char *sibling_threads; + char *numa_nodes; + char *pmu_mappings; }; struct perf_header { diff --git a/tools/perf/util/help.c b/tools/perf/util/help.c index 8b1f6e891b8a..86c37c472263 100644 --- a/tools/perf/util/help.c +++ b/tools/perf/util/help.c @@ -22,8 +22,8 @@ static void clean_cmdnames(struct cmdnames *cmds) unsigned int i; for (i = 0; i < cmds->cnt; ++i) - free(cmds->names[i]); - free(cmds->names); + zfree(&cmds->names[i]); + zfree(&cmds->names); cmds->cnt = 0; cmds->alloc = 0; } @@ -263,9 +263,8 @@ static void add_cmd_list(struct cmdnames *cmds, struct cmdnames *old) for (i = 0; i < old->cnt; i++) cmds->names[cmds->cnt++] = old->names[i]; - free(old->names); + zfree(&old->names); old->cnt = 0; - old->names = NULL; } const char *help_unknown_cmd(const char *cmd) diff --git a/tools/perf/util/hist.c b/tools/perf/util/hist.c index 822903eaa201..e4e6249b87d4 100644 --- a/tools/perf/util/hist.c +++ b/tools/perf/util/hist.c @@ -1,4 +1,3 @@ -#include "annotate.h" #include "util.h" #include "build-id.h" #include "hist.h" @@ -182,21 +181,21 @@ void hists__output_recalc_col_len(struct hists *hists, int max_rows) } } -static void hist_entry__add_cpumode_period(struct hist_entry *he, - unsigned int cpumode, u64 period) +static void he_stat__add_cpumode_period(struct he_stat *he_stat, + unsigned int cpumode, u64 period) { switch (cpumode) { case PERF_RECORD_MISC_KERNEL: - he->stat.period_sys += period; + he_stat->period_sys += period; break; case PERF_RECORD_MISC_USER: - he->stat.period_us += period; + he_stat->period_us += period; break; case PERF_RECORD_MISC_GUEST_KERNEL: - he->stat.period_guest_sys += period; + he_stat->period_guest_sys += period; break; case PERF_RECORD_MISC_GUEST_USER: - he->stat.period_guest_us += period; + he_stat->period_guest_us += period; break; default: break; @@ -223,10 +222,10 @@ static void he_stat__add_stat(struct he_stat *dest, struct he_stat *src) dest->weight += src->weight; } -static void hist_entry__decay(struct hist_entry *he) +static void he_stat__decay(struct he_stat *he_stat) { - he->stat.period = (he->stat.period * 7) / 8; - he->stat.nr_events = (he->stat.nr_events * 7) / 8; + he_stat->period = (he_stat->period * 7) / 8; + he_stat->nr_events = (he_stat->nr_events * 7) / 8; /* XXX need decay for weight too? */ } @@ -237,7 +236,7 @@ static bool hists__decay_entry(struct hists *hists, struct hist_entry *he) if (prev_period == 0) return true; - hist_entry__decay(he); + he_stat__decay(&he->stat); if (!he->filtered) hists->stats.total_period -= prev_period - he->stat.period; @@ -342,15 +341,15 @@ static u8 symbol__parent_filter(const struct symbol *parent) } static struct hist_entry *add_hist_entry(struct hists *hists, - struct hist_entry *entry, - struct addr_location *al, - u64 period, - u64 weight) + struct hist_entry *entry, + struct addr_location *al) { struct rb_node **p; struct rb_node *parent = NULL; struct hist_entry *he; int64_t cmp; + u64 period = entry->stat.period; + u64 weight = entry->stat.weight; p = &hists->entries_in->rb_node; @@ -373,7 +372,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, * This mem info was allocated from machine__resolve_mem * and will not be used anymore. */ - free(entry->mem_info); + zfree(&entry->mem_info); /* If the map of an existing hist_entry has * become out-of-date due to an exec() or @@ -403,7 +402,7 @@ static struct hist_entry *add_hist_entry(struct hists *hists, rb_link_node(&he->rb_node_in, parent, p); rb_insert_color(&he->rb_node_in, hists->entries_in); out: - hist_entry__add_cpumode_period(he, al->cpumode, period); + he_stat__add_cpumode_period(&he->stat, al->cpumode, period); return he; } @@ -437,7 +436,7 @@ struct hist_entry *__hists__add_entry(struct hists *hists, .transaction = transaction, }; - return add_hist_entry(hists, &entry, al, period, weight); + return add_hist_entry(hists, &entry, al); } int64_t @@ -476,8 +475,8 @@ hist_entry__collapse(struct hist_entry *left, struct hist_entry *right) void hist_entry__free(struct hist_entry *he) { - free(he->branch_info); - free(he->mem_info); + zfree(&he->branch_info); + zfree(&he->mem_info); free_srcline(he->srcline); free(he); } @@ -807,16 +806,6 @@ void hists__filter_by_symbol(struct hists *hists) } } -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip) -{ - return symbol__inc_addr_samples(he->ms.sym, he->ms.map, evidx, ip); -} - -int hist_entry__annotate(struct hist_entry *he, size_t privsize) -{ - return symbol__annotate(he->ms.sym, he->ms.map, privsize); -} - void events_stats__inc(struct events_stats *stats, u32 type) { ++stats->nr_events[0]; diff --git a/tools/perf/util/hist.h b/tools/perf/util/hist.h index b621347a1585..a59743fa3ef7 100644 --- a/tools/perf/util/hist.h +++ b/tools/perf/util/hist.h @@ -111,9 +111,6 @@ size_t events_stats__fprintf(struct events_stats *stats, FILE *fp); size_t hists__fprintf(struct hists *hists, bool show_header, int max_rows, int max_cols, float min_pcnt, FILE *fp); -int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 addr); -int hist_entry__annotate(struct hist_entry *he, size_t privsize); - void hists__filter_by_dso(struct hists *hists); void hists__filter_by_thread(struct hists *hists); void hists__filter_by_symbol(struct hists *hists); diff --git a/tools/perf/util/include/asm/bug.h b/tools/perf/util/include/asm/bug.h deleted file mode 100644 index 7fcc6810adc2..000000000000 --- a/tools/perf/util/include/asm/bug.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef _PERF_ASM_GENERIC_BUG_H -#define _PERF_ASM_GENERIC_BUG_H - -#define __WARN_printf(arg...) do { fprintf(stderr, arg); } while (0) - -#define WARN(condition, format...) ({ \ - int __ret_warn_on = !!(condition); \ - if (unlikely(__ret_warn_on)) \ - __WARN_printf(format); \ - unlikely(__ret_warn_on); \ -}) - -#define WARN_ONCE(condition, format...) ({ \ - static int __warned; \ - int __ret_warn_once = !!(condition); \ - \ - if (unlikely(__ret_warn_once)) \ - if (WARN(!__warned, format)) \ - __warned = 1; \ - unlikely(__ret_warn_once); \ -}) -#endif diff --git a/tools/perf/util/include/linux/compiler.h b/tools/perf/util/include/linux/compiler.h deleted file mode 100644 index b003ad7200b2..000000000000 --- a/tools/perf/util/include/linux/compiler.h +++ /dev/null @@ -1,30 +0,0 @@ -#ifndef _PERF_LINUX_COMPILER_H_ -#define _PERF_LINUX_COMPILER_H_ - -#ifndef __always_inline -# define __always_inline inline __attribute__((always_inline)) -#endif - -#define __user - -#ifndef __attribute_const__ -# define __attribute_const__ -#endif - -#ifndef __maybe_unused -# define __maybe_unused __attribute__((unused)) -#endif - -#ifndef __packed -# define __packed __attribute__((__packed__)) -#endif - -#ifndef __force -# define __force -#endif - -#ifndef __weak -# define __weak __attribute__((weak)) -#endif - -#endif diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 84cdb072ac83..ded74590b92f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -9,6 +9,7 @@ #include "strlist.h" #include "thread.h" #include <stdbool.h> +#include <symbol/kallsyms.h> #include "unwind.h" int machine__init(struct machine *machine, const char *root_dir, pid_t pid) @@ -26,6 +27,7 @@ int machine__init(struct machine *machine, const char *root_dir, pid_t pid) machine->pid = pid; machine->symbol_filter = NULL; + machine->id_hdr_size = 0; machine->root_dir = strdup(root_dir); if (machine->root_dir == NULL) @@ -101,8 +103,7 @@ void machine__exit(struct machine *machine) map_groups__exit(&machine->kmaps); dsos__delete(&machine->user_dsos); dsos__delete(&machine->kernel_dsos); - free(machine->root_dir); - machine->root_dir = NULL; + zfree(&machine->root_dir); } void machine__delete(struct machine *machine) @@ -502,15 +503,11 @@ static u64 machine__get_kernel_start_addr(struct machine *machine) char path[PATH_MAX]; struct process_args args; - if (machine__is_host(machine)) { - filename = "/proc/kallsyms"; - } else { - if (machine__is_default_guest(machine)) - filename = (char *)symbol_conf.default_guest_kallsyms; - else { - sprintf(path, "%s/proc/kallsyms", machine->root_dir); - filename = path; - } + if (machine__is_default_guest(machine)) + filename = (char *)symbol_conf.default_guest_kallsyms; + else { + sprintf(path, "%s/proc/kallsyms", machine->root_dir); + filename = path; } if (symbol__restricted_filename(filename, "/proc/kallsyms")) @@ -565,11 +562,10 @@ void machine__destroy_kernel_maps(struct machine *machine) * on one of them. */ if (type == MAP__FUNCTION) { - free((char *)kmap->ref_reloc_sym->name); - kmap->ref_reloc_sym->name = NULL; - free(kmap->ref_reloc_sym); - } - kmap->ref_reloc_sym = NULL; + zfree((char **)&kmap->ref_reloc_sym->name); + zfree(&kmap->ref_reloc_sym); + } else + kmap->ref_reloc_sym = NULL; } map__delete(machine->vmlinux_maps[type]); @@ -767,8 +763,7 @@ static int map_groups__set_modules_path_dir(struct map_groups *mg, ret = -1; goto out; } - dso__set_long_name(map->dso, long_name); - map->dso->lname_alloc = 1; + dso__set_long_name(map->dso, long_name, true); dso__kernel_module_get_build_id(map->dso, ""); } } @@ -939,8 +934,7 @@ static int machine__process_kernel_mmap_event(struct machine *machine, if (name == NULL) goto out_problem; - map->dso->short_name = name; - map->dso->sname_alloc = 1; + dso__set_short_name(map->dso, name, true); map->end = map->start + event->mmap.len; } else if (is_kernel_mmap) { const char *symbol_name = (event->mmap.filename + @@ -1320,8 +1314,6 @@ static int machine__resolve_callchain_sample(struct machine *machine, *root_al = al; callchain_cursor_reset(&callchain_cursor); } - if (!symbol_conf.use_callchain) - break; } err = callchain_cursor_append(&callchain_cursor, diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index ef5bc913ca7a..9b9bd719aa19 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -11,6 +11,7 @@ #include "strlist.h" #include "vdso.h" #include "build-id.h" +#include "util.h" #include <linux/string.h> const char *map_type__name[MAP__NR_TYPES] = { @@ -252,6 +253,22 @@ size_t map__fprintf_dsoname(struct map *map, FILE *fp) return fprintf(fp, "%s", dsoname); } +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, + FILE *fp) +{ + char *srcline; + int ret = 0; + + if (map && map->dso) { + srcline = get_srcline(map->dso, + map__rip_2objdump(map, addr)); + if (srcline != SRCLINE_UNKNOWN) + ret = fprintf(fp, "%s%s", prefix, srcline); + free_srcline(srcline); + } + return ret; +} + /** * map__rip_2objdump - convert symbol start address to objdump address. * @map: memory map diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index e4e259c3ba16..18068c6b71c1 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -103,6 +103,8 @@ struct map *map__clone(struct map *map); int map__overlap(struct map *l, struct map *r); size_t map__fprintf(struct map *map, FILE *fp); size_t map__fprintf_dsoname(struct map *map, FILE *fp); +int map__fprintf_srcline(struct map *map, u64 addr, const char *prefix, + FILE *fp); int map__load(struct map *map, symbol_filter_t filter); struct symbol *map__find_symbol(struct map *map, diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 6de6f89c2a61..a7f1b6a91fdd 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c @@ -10,7 +10,7 @@ #include "symbol.h" #include "cache.h" #include "header.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "parse-events-bison.h" #define YY_EXTRA_TYPE int #include "parse-events-flex.h" @@ -204,7 +204,7 @@ struct tracepoint_path *tracepoint_id_to_path(u64 config) } path->name = malloc(MAX_EVENT_LENGTH); if (!path->name) { - free(path->system); + zfree(&path->system); free(path); return NULL; } @@ -236,8 +236,8 @@ struct tracepoint_path *tracepoint_name_to_path(const char *name) path->name = strdup(str+1); if (path->system == NULL || path->name == NULL) { - free(path->system); - free(path->name); + zfree(&path->system); + zfree(&path->name); free(path); path = NULL; } @@ -269,9 +269,10 @@ const char *event_type(int type) -static int __add_event(struct list_head *list, int *idx, - struct perf_event_attr *attr, - char *name, struct cpu_map *cpus) +static struct perf_evsel * +__add_event(struct list_head *list, int *idx, + struct perf_event_attr *attr, + char *name, struct cpu_map *cpus) { struct perf_evsel *evsel; @@ -279,19 +280,19 @@ static int __add_event(struct list_head *list, int *idx, evsel = perf_evsel__new_idx(attr, (*idx)++); if (!evsel) - return -ENOMEM; + return NULL; evsel->cpus = cpus; if (name) evsel->name = strdup(name); list_add_tail(&evsel->node, list); - return 0; + return evsel; } static int add_event(struct list_head *list, int *idx, struct perf_event_attr *attr, char *name) { - return __add_event(list, idx, attr, name, NULL); + return __add_event(list, idx, attr, name, NULL) ? 0 : -ENOMEM; } static int parse_aliases(char *str, const char *names[][PERF_EVSEL__MAX_ALIASES], int size) @@ -633,6 +634,9 @@ int parse_events_add_pmu(struct list_head *list, int *idx, { struct perf_event_attr attr; struct perf_pmu *pmu; + struct perf_evsel *evsel; + char *unit; + double scale; pmu = perf_pmu__find(name); if (!pmu) @@ -640,7 +644,7 @@ int parse_events_add_pmu(struct list_head *list, int *idx, memset(&attr, 0, sizeof(attr)); - if (perf_pmu__check_alias(pmu, head_config)) + if (perf_pmu__check_alias(pmu, head_config, &unit, &scale)) return -EINVAL; /* @@ -652,8 +656,14 @@ int parse_events_add_pmu(struct list_head *list, int *idx, if (perf_pmu__config(pmu, &attr, head_config)) return -EINVAL; - return __add_event(list, idx, &attr, pmu_event_name(head_config), - pmu->cpus); + evsel = __add_event(list, idx, &attr, pmu_event_name(head_config), + pmu->cpus); + if (evsel) { + evsel->unit = unit; + evsel->scale = scale; + } + + return evsel ? 0 : -ENOMEM; } int parse_events__modifier_group(struct list_head *list, @@ -810,8 +820,7 @@ int parse_events__modifier_event(struct list_head *list, char *str, bool add) if (!add && get_event_modifier(&mod, str, NULL)) return -EINVAL; - list_for_each_entry(evsel, list, node) { - + __evlist__for_each(list, evsel) { if (add && get_event_modifier(&mod, str, evsel)) return -EINVAL; @@ -835,7 +844,7 @@ int parse_events_name(struct list_head *list, char *name) { struct perf_evsel *evsel; - list_for_each_entry(evsel, list, node) { + __evlist__for_each(list, evsel) { if (!evsel->name) evsel->name = strdup(name); } @@ -907,7 +916,7 @@ int parse_events_terms(struct list_head *terms, const char *str) ret = parse_events__scanner(str, &data, PE_START_TERMS); if (!ret) { list_splice(data.terms, terms); - free(data.terms); + zfree(&data.terms); return 0; } diff --git a/tools/perf/util/parse-options.c b/tools/perf/util/parse-options.c index 31f404a032a9..d22e3f8017dc 100644 --- a/tools/perf/util/parse-options.c +++ b/tools/perf/util/parse-options.c @@ -78,6 +78,8 @@ static int get_value(struct parse_opt_ctx_t *p, case OPTION_BOOLEAN: *(bool *)opt->value = unset ? false : true; + if (opt->set) + *(bool *)opt->set = true; return 0; case OPTION_INCR: @@ -224,6 +226,24 @@ static int parse_long_opt(struct parse_opt_ctx_t *p, const char *arg, return 0; } if (!rest) { + if (!prefixcmp(options->long_name, "no-")) { + /* + * The long name itself starts with "no-", so + * accept the option without "no-" so that users + * do not have to enter "no-no-" to get the + * negation. + */ + rest = skip_prefix(arg, options->long_name + 3); + if (rest) { + flags |= OPT_UNSET; + goto match; + } + /* Abbreviated case */ + if (!prefixcmp(options->long_name + 3, arg)) { + flags |= OPT_UNSET; + goto is_abbreviated; + } + } /* abbreviated? */ if (!strncmp(options->long_name, arg, arg_end - arg)) { is_abbreviated: @@ -259,6 +279,7 @@ is_abbreviated: if (!rest) continue; } +match: if (*rest) { if (*rest != '=') continue; diff --git a/tools/perf/util/parse-options.h b/tools/perf/util/parse-options.h index b0241e28eaf7..cbf0149cf221 100644 --- a/tools/perf/util/parse-options.h +++ b/tools/perf/util/parse-options.h @@ -82,6 +82,9 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset); * OPTION_{BIT,SET_UINT,SET_PTR} store the {mask,integer,pointer} to put in * the value when met. * CALLBACKS can use it like they want. + * + * `set`:: + * whether an option was set by the user */ struct option { enum parse_opt_type type; @@ -94,6 +97,7 @@ struct option { int flags; parse_opt_cb *callback; intptr_t defval; + bool *set; }; #define check_vtype(v, type) ( BUILD_BUG_ON_ZERO(!__builtin_types_compatible_p(typeof(v), type)) + v ) @@ -103,6 +107,10 @@ struct option { #define OPT_GROUP(h) { .type = OPTION_GROUP, .help = (h) } #define OPT_BIT(s, l, v, h, b) { .type = OPTION_BIT, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h), .defval = (b) } #define OPT_BOOLEAN(s, l, v, h) { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), .value = check_vtype(v, bool *), .help = (h) } +#define OPT_BOOLEAN_SET(s, l, v, os, h) \ + { .type = OPTION_BOOLEAN, .short_name = (s), .long_name = (l), \ + .value = check_vtype(v, bool *), .help = (h), \ + .set = check_vtype(os, bool *)} #define OPT_INCR(s, l, v, h) { .type = OPTION_INCR, .short_name = (s), .long_name = (l), .value = check_vtype(v, int *), .help = (h) } #define OPT_SET_UINT(s, l, v, h, i) { .type = OPTION_SET_UINT, .short_name = (s), .long_name = (l), .value = check_vtype(v, unsigned int *), .help = (h), .defval = (i) } #define OPT_SET_PTR(s, l, v, h, p) { .type = OPTION_SET_PTR, .short_name = (s), .long_name = (l), .value = (v), .help = (h), .defval = (p) } diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index c232d8dd410b..d9cab4d27192 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c @@ -1,19 +1,23 @@ #include <linux/list.h> #include <sys/types.h> -#include <sys/stat.h> #include <unistd.h> #include <stdio.h> #include <dirent.h> #include "fs.h" +#include <locale.h> #include "util.h" #include "pmu.h" #include "parse-events.h" #include "cpumap.h" +#define UNIT_MAX_LEN 31 /* max length for event unit name */ + struct perf_pmu_alias { char *name; struct list_head terms; struct list_head list; + char unit[UNIT_MAX_LEN+1]; + double scale; }; struct perf_pmu_format { @@ -94,7 +98,80 @@ static int pmu_format(const char *name, struct list_head *format) return 0; } -static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) +static int perf_pmu__parse_scale(struct perf_pmu_alias *alias, char *dir, char *name) +{ + struct stat st; + ssize_t sret; + char scale[128]; + int fd, ret = -1; + char path[PATH_MAX]; + char *lc; + + snprintf(path, PATH_MAX, "%s/%s.scale", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + if (fstat(fd, &st) < 0) + goto error; + + sret = read(fd, scale, sizeof(scale)-1); + if (sret < 0) + goto error; + + scale[sret] = '\0'; + /* + * save current locale + */ + lc = setlocale(LC_NUMERIC, NULL); + + /* + * force to C locale to ensure kernel + * scale string is converted correctly. + * kernel uses default C locale. + */ + setlocale(LC_NUMERIC, "C"); + + alias->scale = strtod(scale, NULL); + + /* restore locale */ + setlocale(LC_NUMERIC, lc); + + ret = 0; +error: + close(fd); + return ret; +} + +static int perf_pmu__parse_unit(struct perf_pmu_alias *alias, char *dir, char *name) +{ + char path[PATH_MAX]; + ssize_t sret; + int fd; + + snprintf(path, PATH_MAX, "%s/%s.unit", dir, name); + + fd = open(path, O_RDONLY); + if (fd == -1) + return -1; + + sret = read(fd, alias->unit, UNIT_MAX_LEN); + if (sret < 0) + goto error; + + close(fd); + + alias->unit[sret] = '\0'; + + return 0; +error: + close(fd); + alias->unit[0] = '\0'; + return -1; +} + +static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FILE *file) { struct perf_pmu_alias *alias; char buf[256]; @@ -110,6 +187,9 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) return -ENOMEM; INIT_LIST_HEAD(&alias->terms); + alias->scale = 1.0; + alias->unit[0] = '\0'; + ret = parse_events_terms(&alias->terms, buf); if (ret) { free(alias); @@ -117,7 +197,14 @@ static int perf_pmu__new_alias(struct list_head *list, char *name, FILE *file) } alias->name = strdup(name); + /* + * load unit name and scale if available + */ + perf_pmu__parse_unit(alias, dir, name); + perf_pmu__parse_scale(alias, dir, name); + list_add_tail(&alias->list, list); + return 0; } @@ -129,6 +216,7 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) { struct dirent *evt_ent; DIR *event_dir; + size_t len; int ret = 0; event_dir = opendir(dir); @@ -143,13 +231,24 @@ static int pmu_aliases_parse(char *dir, struct list_head *head) if (!strcmp(name, ".") || !strcmp(name, "..")) continue; + /* + * skip .unit and .scale info files + * parsed in perf_pmu__new_alias() + */ + len = strlen(name); + if (len > 5 && !strcmp(name + len - 5, ".unit")) + continue; + if (len > 6 && !strcmp(name + len - 6, ".scale")) + continue; + snprintf(path, PATH_MAX, "%s/%s", dir, name); ret = -EINVAL; file = fopen(path, "r"); if (!file) break; - ret = perf_pmu__new_alias(head, name, file); + + ret = perf_pmu__new_alias(head, dir, name, file); fclose(file); } @@ -406,7 +505,7 @@ static __u64 pmu_format_value(unsigned long *format, __u64 value) /* * Setup one of config[12] attr members based on the - * user input data - temr parameter. + * user input data - term parameter. */ static int pmu_config_term(struct list_head *formats, struct perf_event_attr *attr, @@ -508,16 +607,42 @@ static struct perf_pmu_alias *pmu_find_alias(struct perf_pmu *pmu, return NULL; } + +static int check_unit_scale(struct perf_pmu_alias *alias, + char **unit, double *scale) +{ + /* + * Only one term in event definition can + * define unit and scale, fail if there's + * more than one. + */ + if ((*unit && alias->unit) || + (*scale && alias->scale)) + return -EINVAL; + + if (alias->unit) + *unit = alias->unit; + + if (alias->scale) + *scale = alias->scale; + + return 0; +} + /* * Find alias in the terms list and replace it with the terms * defined for the alias */ -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, + char **unit, double *scale) { struct parse_events_term *term, *h; struct perf_pmu_alias *alias; int ret; + *unit = NULL; + *scale = 0; + list_for_each_entry_safe(term, h, head_terms, list) { alias = pmu_find_alias(pmu, term); if (!alias) @@ -525,6 +650,11 @@ int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms) ret = pmu_alias_terms(alias, &term->list); if (ret) return ret; + + ret = check_unit_scale(alias, unit, scale); + if (ret) + return ret; + list_del(&term->list); free(term); } @@ -625,7 +755,7 @@ void print_pmu_events(const char *event_glob, bool name_only) continue; } printf(" %-50s [Kernel PMU event]\n", aliases[j]); - free(aliases[j]); + zfree(&aliases[j]); printed++; } if (printed) diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 1179b26f244a..9183380e2038 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h @@ -28,7 +28,8 @@ int perf_pmu__config(struct perf_pmu *pmu, struct perf_event_attr *attr, int perf_pmu__config_terms(struct list_head *formats, struct perf_event_attr *attr, struct list_head *head_terms); -int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms); +int perf_pmu__check_alias(struct perf_pmu *pmu, struct list_head *head_terms, + char **unit, double *scale); struct list_head *perf_pmu__alias(struct perf_pmu *pmu, struct list_head *head_terms); int perf_pmu_wrap(void); diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index 9c6989ca2bea..a8a9b6cd93a8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c @@ -40,7 +40,7 @@ #include "color.h" #include "symbol.h" #include "thread.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "trace-event.h" /* For __maybe_unused */ #include "probe-event.h" #include "probe-finder.h" @@ -72,6 +72,7 @@ static int e_snprintf(char *str, size_t size, const char *format, ...) static char *synthesize_perf_probe_point(struct perf_probe_point *pp); static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec); +static void clear_probe_trace_event(struct probe_trace_event *tev); static struct machine machine; /* Initialize symbol maps and path of vmlinux/modules */ @@ -154,7 +155,7 @@ static struct dso *kernel_get_module_dso(const char *module) vmlinux_name = symbol_conf.vmlinux_name; if (vmlinux_name) { - if (dso__load_vmlinux(dso, map, vmlinux_name, NULL) <= 0) + if (dso__load_vmlinux(dso, map, vmlinux_name, false, NULL) <= 0) return NULL; } else { if (dso__load_vmlinux_path(dso, map, NULL) <= 0) { @@ -186,6 +187,37 @@ static int init_user_exec(void) return ret; } +static int convert_exec_to_group(const char *exec, char **result) +{ + char *ptr1, *ptr2, *exec_copy; + char buf[64]; + int ret; + + exec_copy = strdup(exec); + if (!exec_copy) + return -ENOMEM; + + ptr1 = basename(exec_copy); + if (!ptr1) { + ret = -EINVAL; + goto out; + } + + ptr2 = strpbrk(ptr1, "-._"); + if (ptr2) + *ptr2 = '\0'; + ret = e_snprintf(buf, 64, "%s_%s", PERFPROBE_GROUP, ptr1); + if (ret < 0) + goto out; + + *result = strdup(buf); + ret = *result ? 0 : -ENOMEM; + +out: + free(exec_copy); + return ret; +} + static int convert_to_perf_probe_point(struct probe_trace_point *tp, struct perf_probe_point *pp) { @@ -261,6 +293,68 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, return 0; } +static int get_text_start_address(const char *exec, unsigned long *address) +{ + Elf *elf; + GElf_Ehdr ehdr; + GElf_Shdr shdr; + int fd, ret = -ENOENT; + + fd = open(exec, O_RDONLY); + if (fd < 0) + return -errno; + + elf = elf_begin(fd, PERF_ELF_C_READ_MMAP, NULL); + if (elf == NULL) + return -EINVAL; + + if (gelf_getehdr(elf, &ehdr) == NULL) + goto out; + + if (!elf_section_by_name(elf, &ehdr, &shdr, ".text", NULL)) + goto out; + + *address = shdr.sh_addr - shdr.sh_offset; + ret = 0; +out: + elf_end(elf); + return ret; +} + +static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs, + int ntevs, const char *exec) +{ + int i, ret = 0; + unsigned long offset, stext = 0; + char buf[32]; + + if (!exec) + return 0; + + ret = get_text_start_address(exec, &stext); + if (ret < 0) + return ret; + + for (i = 0; i < ntevs && ret >= 0; i++) { + offset = tevs[i].point.address - stext; + offset += tevs[i].point.offset; + tevs[i].point.offset = 0; + zfree(&tevs[i].point.symbol); + ret = e_snprintf(buf, 32, "0x%lx", offset); + if (ret < 0) + break; + tevs[i].point.module = strdup(exec); + tevs[i].point.symbol = strdup(buf); + if (!tevs[i].point.symbol || !tevs[i].point.module) { + ret = -ENOMEM; + break; + } + tevs[i].uprobes = true; + } + + return ret; +} + static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, int ntevs, const char *module) { @@ -290,12 +384,18 @@ static int add_module_to_probe_trace_events(struct probe_trace_event *tevs, } } - if (tmp) - free(tmp); - + free(tmp); return ret; } +static void clear_probe_trace_events(struct probe_trace_event *tevs, int ntevs) +{ + int i; + + for (i = 0; i < ntevs; i++) + clear_probe_trace_event(tevs + i); +} + /* Try to find perf_probe_event with debuginfo */ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs, @@ -305,15 +405,6 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct debuginfo *dinfo; int ntevs, ret = 0; - if (pev->uprobes) { - if (need_dwarf) { - pr_warning("Debuginfo-analysis is not yet supported" - " with -x/--exec option.\n"); - return -ENOSYS; - } - return convert_name_to_addr(pev, target); - } - dinfo = open_debuginfo(target); if (!dinfo) { @@ -332,9 +423,18 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev, if (ntevs > 0) { /* Succeeded to find trace events */ pr_debug("find %d probe_trace_events.\n", ntevs); - if (target) - ret = add_module_to_probe_trace_events(*tevs, ntevs, - target); + if (target) { + if (pev->uprobes) + ret = add_exec_to_probe_trace_events(*tevs, + ntevs, target); + else + ret = add_module_to_probe_trace_events(*tevs, + ntevs, target); + } + if (ret < 0) { + clear_probe_trace_events(*tevs, ntevs); + zfree(tevs); + } return ret < 0 ? ret : ntevs; } @@ -401,15 +501,13 @@ static int get_real_path(const char *raw_path, const char *comp_dir, case EFAULT: raw_path = strchr(++raw_path, '/'); if (!raw_path) { - free(*new_path); - *new_path = NULL; + zfree(new_path); return -ENOENT; } continue; default: - free(*new_path); - *new_path = NULL; + zfree(new_path); return -errno; } } @@ -580,7 +678,7 @@ static int show_available_vars_at(struct debuginfo *dinfo, */ fprintf(stdout, "\t@<%s+%lu>\n", vl->point.symbol, vl->point.offset); - free(vl->point.symbol); + zfree(&vl->point.symbol); nvars = 0; if (vl->vars) { strlist__for_each(node, vl->vars) { @@ -647,16 +745,14 @@ static int kprobe_convert_to_perf_probe(struct probe_trace_point *tp, static int try_to_find_probe_trace_events(struct perf_probe_event *pev, struct probe_trace_event **tevs __maybe_unused, - int max_tevs __maybe_unused, const char *target) + int max_tevs __maybe_unused, + const char *target __maybe_unused) { if (perf_probe_event_need_dwarf(pev)) { pr_warning("Debuginfo-analysis is not supported.\n"); return -ENOSYS; } - if (pev->uprobes) - return convert_name_to_addr(pev, target); - return 0; } @@ -678,6 +774,28 @@ int show_available_vars(struct perf_probe_event *pevs __maybe_unused, } #endif +void line_range__clear(struct line_range *lr) +{ + struct line_node *ln; + + free(lr->function); + free(lr->file); + free(lr->path); + free(lr->comp_dir); + while (!list_empty(&lr->line_list)) { + ln = list_first_entry(&lr->line_list, struct line_node, list); + list_del(&ln->list); + free(ln); + } + memset(lr, 0, sizeof(*lr)); +} + +void line_range__init(struct line_range *lr) +{ + memset(lr, 0, sizeof(*lr)); + INIT_LIST_HEAD(&lr->line_list); +} + static int parse_line_num(char **ptr, int *val, const char *what) { const char *start = *ptr; @@ -1278,8 +1396,7 @@ static char *synthesize_perf_probe_point(struct perf_probe_point *pp) error: pr_debug("Failed to synthesize perf probe point: %s\n", strerror(-ret)); - if (buf) - free(buf); + free(buf); return NULL; } @@ -1480,34 +1597,25 @@ void clear_perf_probe_event(struct perf_probe_event *pev) struct perf_probe_arg_field *field, *next; int i; - if (pev->event) - free(pev->event); - if (pev->group) - free(pev->group); - if (pp->file) - free(pp->file); - if (pp->function) - free(pp->function); - if (pp->lazy_line) - free(pp->lazy_line); + free(pev->event); + free(pev->group); + free(pp->file); + free(pp->function); + free(pp->lazy_line); + for (i = 0; i < pev->nargs; i++) { - if (pev->args[i].name) - free(pev->args[i].name); - if (pev->args[i].var) - free(pev->args[i].var); - if (pev->args[i].type) - free(pev->args[i].type); + free(pev->args[i].name); + free(pev->args[i].var); + free(pev->args[i].type); field = pev->args[i].field; while (field) { next = field->next; - if (field->name) - free(field->name); + zfree(&field->name); free(field); field = next; } } - if (pev->args) - free(pev->args); + free(pev->args); memset(pev, 0, sizeof(*pev)); } @@ -1516,21 +1624,14 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) struct probe_trace_arg_ref *ref, *next; int i; - if (tev->event) - free(tev->event); - if (tev->group) - free(tev->group); - if (tev->point.symbol) - free(tev->point.symbol); - if (tev->point.module) - free(tev->point.module); + free(tev->event); + free(tev->group); + free(tev->point.symbol); + free(tev->point.module); for (i = 0; i < tev->nargs; i++) { - if (tev->args[i].name) - free(tev->args[i].name); - if (tev->args[i].value) - free(tev->args[i].value); - if (tev->args[i].type) - free(tev->args[i].type); + free(tev->args[i].name); + free(tev->args[i].value); + free(tev->args[i].type); ref = tev->args[i].ref; while (ref) { next = ref->next; @@ -1538,8 +1639,7 @@ static void clear_probe_trace_event(struct probe_trace_event *tev) ref = next; } } - if (tev->args) - free(tev->args); + free(tev->args); memset(tev, 0, sizeof(*tev)); } @@ -1913,14 +2013,29 @@ static int convert_to_probe_trace_events(struct perf_probe_event *pev, int max_tevs, const char *target) { struct symbol *sym; - int ret = 0, i; + int ret, i; struct probe_trace_event *tev; + if (pev->uprobes && !pev->group) { + /* Replace group name if not given */ + ret = convert_exec_to_group(target, &pev->group); + if (ret != 0) { + pr_warning("Failed to make a group name.\n"); + return ret; + } + } + /* Convert perf_probe_event with debuginfo */ ret = try_to_find_probe_trace_events(pev, tevs, max_tevs, target); if (ret != 0) return ret; /* Found in debuginfo or got an error */ + if (pev->uprobes) { + ret = convert_name_to_addr(pev, target); + if (ret < 0) + return ret; + } + /* Allocate trace event buffer */ tev = *tevs = zalloc(sizeof(struct probe_trace_event)); if (tev == NULL) @@ -2056,7 +2171,7 @@ end: for (i = 0; i < npevs; i++) { for (j = 0; j < pkgs[i].ntevs; j++) clear_probe_trace_event(&pkgs[i].tevs[j]); - free(pkgs[i].tevs); + zfree(&pkgs[i].tevs); } free(pkgs); @@ -2281,7 +2396,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) struct perf_probe_point *pp = &pev->point; struct symbol *sym; struct map *map = NULL; - char *function = NULL, *name = NULL; + char *function = NULL; int ret = -EINVAL; unsigned long long vaddr = 0; @@ -2297,12 +2412,7 @@ static int convert_name_to_addr(struct perf_probe_event *pev, const char *exec) goto out; } - name = realpath(exec, NULL); - if (!name) { - pr_warning("Cannot find realpath for %s.\n", exec); - goto out; - } - map = dso__new_map(name); + map = dso__new_map(exec); if (!map) { pr_warning("Cannot find appropriate DSO for %s.\n", exec); goto out; @@ -2367,7 +2477,5 @@ out: } if (function) free(function); - if (name) - free(name); return ret; } diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index f9f3de8b4220..fcaf7273e85a 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h @@ -12,6 +12,7 @@ struct probe_trace_point { char *symbol; /* Base symbol */ char *module; /* Module name */ unsigned long offset; /* Offset from symbol */ + unsigned long address; /* Actual address of the trace point */ bool retprobe; /* Return probe flag */ }; @@ -119,6 +120,12 @@ extern void clear_perf_probe_event(struct perf_probe_event *pev); /* Command string to line-range */ extern int parse_line_range_desc(const char *cmd, struct line_range *lr); +/* Release line range members */ +extern void line_range__clear(struct line_range *lr); + +/* Initialize line range */ +extern void line_range__init(struct line_range *lr); + /* Internal use: Return kernel/module path */ extern const char *kernel_get_module_path(const char *module); diff --git a/tools/perf/util/probe-finder.c b/tools/perf/util/probe-finder.c index ffb657ffd327..061edb162b5b 100644 --- a/tools/perf/util/probe-finder.c +++ b/tools/perf/util/probe-finder.c @@ -226,10 +226,8 @@ struct debuginfo *debuginfo__new(const char *path) if (!dbg) return NULL; - if (debuginfo__init_offline_dwarf(dbg, path) < 0) { - free(dbg); - dbg = NULL; - } + if (debuginfo__init_offline_dwarf(dbg, path) < 0) + zfree(&dbg); return dbg; } @@ -241,10 +239,8 @@ struct debuginfo *debuginfo__new_online_kernel(unsigned long addr) if (!dbg) return NULL; - if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) { - free(dbg); - dbg = NULL; - } + if (debuginfo__init_online_kernel_dwarf(dbg, (Dwarf_Addr)addr) < 0) + zfree(&dbg); return dbg; } @@ -729,6 +725,7 @@ static int convert_to_trace_point(Dwarf_Die *sp_die, Dwfl_Module *mod, return -ENOENT; } tp->offset = (unsigned long)(paddr - sym.st_value); + tp->address = (unsigned long)paddr; tp->symbol = strdup(symbol); if (!tp->symbol) return -ENOMEM; @@ -1301,8 +1298,7 @@ int debuginfo__find_trace_events(struct debuginfo *dbg, ret = debuginfo__find_probes(dbg, &tf.pf); if (ret < 0) { - free(*tevs); - *tevs = NULL; + zfree(tevs); return ret; } @@ -1413,13 +1409,10 @@ int debuginfo__find_available_vars_at(struct debuginfo *dbg, if (ret < 0) { /* Free vlist for error */ while (af.nvls--) { - if (af.vls[af.nvls].point.symbol) - free(af.vls[af.nvls].point.symbol); - if (af.vls[af.nvls].vars) - strlist__delete(af.vls[af.nvls].vars); + zfree(&af.vls[af.nvls].point.symbol); + strlist__delete(af.vls[af.nvls].vars); } - free(af.vls); - *vls = NULL; + zfree(vls); return ret; } @@ -1523,10 +1516,7 @@ post: if (fname) { ppt->file = strdup(fname); if (ppt->file == NULL) { - if (ppt->function) { - free(ppt->function); - ppt->function = NULL; - } + zfree(&ppt->function); ret = -ENOMEM; goto end; } @@ -1580,8 +1570,7 @@ static int find_line_range_by_line(Dwarf_Die *sp_die, struct line_finder *lf) else ret = 0; /* Lines are not found */ else { - free(lf->lr->path); - lf->lr->path = NULL; + zfree(&lf->lr->path); } return ret; } diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources index 239036fb2b2c..595bfc73d2ed 100644 --- a/tools/perf/util/python-ext-sources +++ b/tools/perf/util/python-ext-sources @@ -18,4 +18,5 @@ util/cgroup.c util/rblist.c util/strlist.c util/fs.c +util/trace-event.c ../../lib/rbtree.c diff --git a/tools/perf/util/python.c b/tools/perf/util/python.c index 4bf8ace7f511..122669c18ff4 100644 --- a/tools/perf/util/python.c +++ b/tools/perf/util/python.c @@ -908,9 +908,10 @@ static PyObject *pyrf_evlist__item(PyObject *obj, Py_ssize_t i) if (i >= pevlist->evlist.nr_entries) return NULL; - list_for_each_entry(pos, &pevlist->evlist.entries, node) + evlist__for_each(&pevlist->evlist, pos) { if (i-- == 0) break; + } return Py_BuildValue("O", container_of(pos, struct pyrf_evsel, evsel)); } diff --git a/tools/perf/util/record.c b/tools/perf/util/record.c index c8845b107f60..373762501dad 100644 --- a/tools/perf/util/record.c +++ b/tools/perf/util/record.c @@ -74,8 +74,7 @@ bool perf_can_sample_identifier(void) return perf_probe_api(perf_probe_sample_identifier); } -void perf_evlist__config(struct perf_evlist *evlist, - struct perf_record_opts *opts) +void perf_evlist__config(struct perf_evlist *evlist, struct record_opts *opts) { struct perf_evsel *evsel; bool use_sample_identifier = false; @@ -90,19 +89,19 @@ void perf_evlist__config(struct perf_evlist *evlist, if (evlist->cpus->map[0] < 0) opts->no_inherit = true; - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__config(evsel, opts); if (evlist->nr_entries > 1) { struct perf_evsel *first = perf_evlist__first(evlist); - list_for_each_entry(evsel, &evlist->entries, node) { + evlist__for_each(evlist, evsel) { if (evsel->attr.sample_type == first->attr.sample_type) continue; use_sample_identifier = perf_can_sample_identifier(); break; } - list_for_each_entry(evsel, &evlist->entries, node) + evlist__for_each(evlist, evsel) perf_evsel__set_sample_id(evsel, use_sample_identifier); } @@ -123,7 +122,7 @@ static int get_max_rate(unsigned int *rate) return filename__read_int(path, (int *) rate); } -static int perf_record_opts__config_freq(struct perf_record_opts *opts) +static int record_opts__config_freq(struct record_opts *opts) { bool user_freq = opts->user_freq != UINT_MAX; unsigned int max_rate; @@ -173,7 +172,44 @@ static int perf_record_opts__config_freq(struct perf_record_opts *opts) return 0; } -int perf_record_opts__config(struct perf_record_opts *opts) +int record_opts__config(struct record_opts *opts) { - return perf_record_opts__config_freq(opts); + return record_opts__config_freq(opts); +} + +bool perf_evlist__can_select_event(struct perf_evlist *evlist, const char *str) +{ + struct perf_evlist *temp_evlist; + struct perf_evsel *evsel; + int err, fd, cpu; + bool ret = false; + + temp_evlist = perf_evlist__new(); + if (!temp_evlist) + return false; + + err = parse_events(temp_evlist, str); + if (err) + goto out_delete; + + evsel = perf_evlist__last(temp_evlist); + + if (!evlist || cpu_map__empty(evlist->cpus)) { + struct cpu_map *cpus = cpu_map__new(NULL); + + cpu = cpus ? cpus->map[0] : 0; + cpu_map__delete(cpus); + } else { + cpu = evlist->cpus->map[0]; + } + + fd = sys_perf_event_open(&evsel->attr, -1, cpu, -1, 0); + if (fd >= 0) { + close(fd); + ret = true; + } + +out_delete: + perf_evlist__delete(temp_evlist); + return ret; } diff --git a/tools/perf/util/scripting-engines/trace-event-perl.c b/tools/perf/util/scripting-engines/trace-event-perl.c index d5e5969f6fea..e108207c5de0 100644 --- a/tools/perf/util/scripting-engines/trace-event-perl.c +++ b/tools/perf/util/scripting-engines/trace-event-perl.c @@ -194,8 +194,7 @@ static void define_event_symbols(struct event_format *event, zero_flag_atom = 0; break; case PRINT_FIELD: - if (cur_field_name) - free(cur_field_name); + free(cur_field_name); cur_field_name = strdup(args->field.name); break; case PRINT_FLAGS: @@ -257,12 +256,9 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel) return event; } -static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, - struct perf_sample *sample, +static void perl_process_tracepoint(struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread, - struct addr_location *al) + struct thread *thread) { struct format_field *field; static char handler[256]; @@ -349,10 +345,7 @@ static void perl_process_tracepoint(union perf_event *perf_event __maybe_unused, static void perl_process_event_generic(union perf_event *event, struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread __maybe_unused, - struct addr_location *al __maybe_unused) + struct perf_evsel *evsel) { dSP; @@ -377,12 +370,11 @@ static void perl_process_event_generic(union perf_event *event, static void perl_process_event(union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, struct thread *thread, - struct addr_location *al) + struct addr_location *al __maybe_unused) { - perl_process_tracepoint(event, sample, evsel, machine, thread, al); - perl_process_event_generic(event, sample, evsel, machine, thread, al); + perl_process_tracepoint(sample, evsel, thread); + perl_process_event_generic(event, sample, evsel); } static void run_start_sub(void) diff --git a/tools/perf/util/scripting-engines/trace-event-python.c b/tools/perf/util/scripting-engines/trace-event-python.c index 53c20e7fd900..cd9774df3750 100644 --- a/tools/perf/util/scripting-engines/trace-event-python.c +++ b/tools/perf/util/scripting-engines/trace-event-python.c @@ -161,8 +161,7 @@ static void define_event_symbols(struct event_format *event, zero_flag_atom = 0; break; case PRINT_FIELD: - if (cur_field_name) - free(cur_field_name); + free(cur_field_name); cur_field_name = strdup(args->field.name); break; case PRINT_FLAGS: @@ -231,13 +230,10 @@ static inline struct event_format *find_cache_event(struct perf_evsel *evsel) return event; } -static void python_process_tracepoint(union perf_event *perf_event - __maybe_unused, - struct perf_sample *sample, - struct perf_evsel *evsel, - struct machine *machine __maybe_unused, - struct thread *thread, - struct addr_location *al) +static void python_process_tracepoint(struct perf_sample *sample, + struct perf_evsel *evsel, + struct thread *thread, + struct addr_location *al) { PyObject *handler, *retval, *context, *t, *obj, *dict = NULL; static char handler_name[256]; @@ -351,11 +347,8 @@ static void python_process_tracepoint(union perf_event *perf_event Py_DECREF(t); } -static void python_process_general_event(union perf_event *perf_event - __maybe_unused, - struct perf_sample *sample, +static void python_process_general_event(struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine __maybe_unused, struct thread *thread, struct addr_location *al) { @@ -411,22 +404,19 @@ exit: Py_DECREF(t); } -static void python_process_event(union perf_event *perf_event, +static void python_process_event(union perf_event *event __maybe_unused, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, struct thread *thread, struct addr_location *al) { switch (evsel->attr.type) { case PERF_TYPE_TRACEPOINT: - python_process_tracepoint(perf_event, sample, evsel, - machine, thread, al); + python_process_tracepoint(sample, evsel, thread, al); break; /* Reserve for future process_hw/sw/raw APIs */ default: - python_process_general_event(perf_event, sample, evsel, - machine, thread, al); + python_process_general_event(sample, evsel, thread, al); } } diff --git a/tools/perf/util/session.c b/tools/perf/util/session.c index f36d24a02445..7acc03e8f3b2 100644 --- a/tools/perf/util/session.c +++ b/tools/perf/util/session.c @@ -132,18 +132,18 @@ static void perf_session__delete_threads(struct perf_session *session) static void perf_session_env__delete(struct perf_session_env *env) { - free(env->hostname); - free(env->os_release); - free(env->version); - free(env->arch); - free(env->cpu_desc); - free(env->cpuid); + zfree(&env->hostname); + zfree(&env->os_release); + zfree(&env->version); + zfree(&env->arch); + zfree(&env->cpu_desc); + zfree(&env->cpuid); - free(env->cmdline); - free(env->sibling_cores); - free(env->sibling_threads); - free(env->numa_nodes); - free(env->pmu_mappings); + zfree(&env->cmdline); + zfree(&env->sibling_cores); + zfree(&env->sibling_threads); + zfree(&env->numa_nodes); + zfree(&env->pmu_mappings); } void perf_session__delete(struct perf_session *session) @@ -247,27 +247,6 @@ void perf_tool__fill_defaults(struct perf_tool *tool) } } -void mem_bswap_32(void *src, int byte_size) -{ - u32 *m = src; - while (byte_size > 0) { - *m = bswap_32(*m); - byte_size -= sizeof(u32); - ++m; - } -} - -void mem_bswap_64(void *src, int byte_size) -{ - u64 *m = src; - - while (byte_size > 0) { - *m = bswap_64(*m); - byte_size -= sizeof(u64); - ++m; - } -} - static void swap_sample_id_all(union perf_event *event, void *data) { void *end = (void *) event + event->header.size; @@ -851,6 +830,7 @@ static struct machine * struct perf_sample *sample) { const u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK; + struct machine *machine; if (perf_guest && ((cpumode == PERF_RECORD_MISC_GUEST_KERNEL) || @@ -863,7 +843,11 @@ static struct machine * else pid = sample->pid; - return perf_session__findnew_machine(session, pid); + machine = perf_session__find_machine(session, pid); + if (!machine) + machine = perf_session__findnew_machine(session, + DEFAULT_GUEST_KERNEL_ID); + return machine; } return &session->machines.host; @@ -1158,7 +1142,7 @@ static int __perf_session__process_pipe_events(struct perf_session *session, void *buf = NULL; int skip = 0; u64 head; - int err; + ssize_t err; void *p; perf_tool__fill_defaults(tool); @@ -1400,7 +1384,7 @@ bool perf_session__has_traces(struct perf_session *session, const char *msg) { struct perf_evsel *evsel; - list_for_each_entry(evsel, &session->evlist->entries, node) { + evlist__for_each(session->evlist, evsel) { if (evsel->attr.type == PERF_TYPE_TRACEPOINT) return true; } @@ -1458,7 +1442,7 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp) ret += events_stats__fprintf(&session->stats, fp); - list_for_each_entry(pos, &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { ret += fprintf(fp, "%s stats:\n", perf_evsel__name(pos)); ret += events_stats__fprintf(&pos->hists.stats, fp); } @@ -1480,35 +1464,30 @@ struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, { struct perf_evsel *pos; - list_for_each_entry(pos, &session->evlist->entries, node) { + evlist__for_each(session->evlist, pos) { if (pos->attr.type == type) return pos; } return NULL; } -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, - struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, + struct addr_location *al, unsigned int print_opts, unsigned int stack_depth) { - struct addr_location al; struct callchain_cursor_node *node; int print_ip = print_opts & PRINT_IP_OPT_IP; int print_sym = print_opts & PRINT_IP_OPT_SYM; int print_dso = print_opts & PRINT_IP_OPT_DSO; int print_symoffset = print_opts & PRINT_IP_OPT_SYMOFFSET; int print_oneline = print_opts & PRINT_IP_OPT_ONELINE; + int print_srcline = print_opts & PRINT_IP_OPT_SRCLINE; char s = print_oneline ? ' ' : '\t'; - if (perf_event__preprocess_sample(event, machine, &al, sample) < 0) { - error("problem processing %d event, skipping it.\n", - event->header.type); - return; - } - if (symbol_conf.use_callchain && sample->callchain) { + struct addr_location node_al; - if (machine__resolve_callchain(machine, evsel, al.thread, + if (machine__resolve_callchain(al->machine, evsel, al->thread, sample, NULL, NULL, PERF_MAX_STACK_DEPTH) != 0) { if (verbose) @@ -1517,20 +1496,31 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, } callchain_cursor_commit(&callchain_cursor); + if (print_symoffset) + node_al = *al; + while (stack_depth) { + u64 addr = 0; + node = callchain_cursor_current(&callchain_cursor); if (!node) break; + if (node->sym && node->sym->ignore) + goto next; + if (print_ip) printf("%c%16" PRIx64, s, node->ip); + if (node->map) + addr = node->map->map_ip(node->map, node->ip); + if (print_sym) { printf(" "); if (print_symoffset) { - al.addr = node->ip; - al.map = node->map; - symbol__fprintf_symname_offs(node->sym, &al, stdout); + node_al.addr = addr; + node_al.map = node->map; + symbol__fprintf_symname_offs(node->sym, &node_al, stdout); } else symbol__fprintf_symname(node->sym, stdout); } @@ -1541,32 +1531,42 @@ void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, printf(")"); } + if (print_srcline) + map__fprintf_srcline(node->map, addr, "\n ", + stdout); + if (!print_oneline) printf("\n"); - callchain_cursor_advance(&callchain_cursor); - stack_depth--; +next: + callchain_cursor_advance(&callchain_cursor); } } else { + if (al->sym && al->sym->ignore) + return; + if (print_ip) printf("%16" PRIx64, sample->ip); if (print_sym) { printf(" "); if (print_symoffset) - symbol__fprintf_symname_offs(al.sym, &al, + symbol__fprintf_symname_offs(al->sym, al, stdout); else - symbol__fprintf_symname(al.sym, stdout); + symbol__fprintf_symname(al->sym, stdout); } if (print_dso) { printf(" ("); - map__fprintf_dsoname(al.map, stdout); + map__fprintf_dsoname(al->map, stdout); printf(")"); } + + if (print_srcline) + map__fprintf_srcline(al->map, al->addr, "\n ", stdout); } } diff --git a/tools/perf/util/session.h b/tools/perf/util/session.h index 50f640958f0f..3140f8ae6148 100644 --- a/tools/perf/util/session.h +++ b/tools/perf/util/session.h @@ -1,6 +1,7 @@ #ifndef __PERF_SESSION_H #define __PERF_SESSION_H +#include "trace-event.h" #include "hist.h" #include "event.h" #include "header.h" @@ -32,7 +33,7 @@ struct perf_session { struct perf_header header; struct machines machines; struct perf_evlist *evlist; - struct pevent *pevent; + struct trace_event tevent; struct events_stats stats; bool repipe; struct ordered_samples ordered_samples; @@ -44,6 +45,7 @@ struct perf_session { #define PRINT_IP_OPT_DSO (1<<2) #define PRINT_IP_OPT_SYMOFFSET (1<<3) #define PRINT_IP_OPT_ONELINE (1<<4) +#define PRINT_IP_OPT_SRCLINE (1<<5) struct perf_tool; @@ -72,8 +74,6 @@ int perf_session__resolve_callchain(struct perf_session *session, bool perf_session__has_traces(struct perf_session *session, const char *msg); -void mem_bswap_64(void *src, int byte_size); -void mem_bswap_32(void *src, int byte_size); void perf_event__attr_swap(struct perf_event_attr *attr); int perf_session__create_kernel_maps(struct perf_session *session); @@ -105,8 +105,8 @@ size_t perf_session__fprintf_nr_events(struct perf_session *session, FILE *fp); struct perf_evsel *perf_session__find_first_evtype(struct perf_session *session, unsigned int type); -void perf_evsel__print_ip(struct perf_evsel *evsel, union perf_event *event, - struct perf_sample *sample, struct machine *machine, +void perf_evsel__print_ip(struct perf_evsel *evsel, struct perf_sample *sample, + struct addr_location *al, unsigned int print_opts, unsigned int stack_depth); int perf_session__cpu_bitmap(struct perf_session *session, diff --git a/tools/perf/util/setup.py b/tools/perf/util/setup.py index 58ea5ca6c255..d0aee4b9dfd4 100644 --- a/tools/perf/util/setup.py +++ b/tools/perf/util/setup.py @@ -25,7 +25,7 @@ cflags += ['-fno-strict-aliasing', '-Wno-write-strings', '-Wno-unused-parameter' build_lib = getenv('PYTHON_EXTBUILD_LIB') build_tmp = getenv('PYTHON_EXTBUILD_TMP') libtraceevent = getenv('LIBTRACEEVENT') -liblk = getenv('LIBLK') +libapikfs = getenv('LIBAPIKFS') ext_sources = [f.strip() for f in file('util/python-ext-sources') if len(f.strip()) > 0 and f[0] != '#'] @@ -34,7 +34,7 @@ perf = Extension('perf', sources = ext_sources, include_dirs = ['util/include'], extra_compile_args = cflags, - extra_objects = [libtraceevent, liblk], + extra_objects = [libtraceevent, libapikfs], ) setup(name='perf', diff --git a/tools/perf/util/sort.c b/tools/perf/util/sort.c index 8b0bb1f4494a..635cd8f8b22e 100644 --- a/tools/perf/util/sort.c +++ b/tools/perf/util/sort.c @@ -13,6 +13,7 @@ int have_ignore_callees = 0; int sort__need_collapse = 0; int sort__has_parent = 0; int sort__has_sym = 0; +int sort__has_dso = 0; enum sort_mode sort__mode = SORT_MODE__NORMAL; enum sort_type sort__first_dimension; @@ -161,6 +162,11 @@ struct sort_entry sort_dso = { /* --sort symbol */ +static int64_t _sort__addr_cmp(u64 left_ip, u64 right_ip) +{ + return (int64_t)(right_ip - left_ip); +} + static int64_t _sort__sym_cmp(struct symbol *sym_l, struct symbol *sym_r) { u64 ip_l, ip_r; @@ -183,15 +189,17 @@ sort__sym_cmp(struct hist_entry *left, struct hist_entry *right) int64_t ret; if (!left->ms.sym && !right->ms.sym) - return right->level - left->level; + return _sort__addr_cmp(left->ip, right->ip); /* * comparing symbol address alone is not enough since it's a * relative address within a dso. */ - ret = sort__dso_cmp(left, right); - if (ret != 0) - return ret; + if (!sort__has_dso) { + ret = sort__dso_cmp(left, right); + if (ret != 0) + return ret; + } return _sort__sym_cmp(left->ms.sym, right->ms.sym); } @@ -372,7 +380,7 @@ sort__sym_from_cmp(struct hist_entry *left, struct hist_entry *right) struct addr_map_symbol *from_r = &right->branch_info->from; if (!from_l->sym && !from_r->sym) - return right->level - left->level; + return _sort__addr_cmp(from_l->addr, from_r->addr); return _sort__sym_cmp(from_l->sym, from_r->sym); } @@ -384,7 +392,7 @@ sort__sym_to_cmp(struct hist_entry *left, struct hist_entry *right) struct addr_map_symbol *to_r = &right->branch_info->to; if (!to_l->sym && !to_r->sym) - return right->level - left->level; + return _sort__addr_cmp(to_l->addr, to_r->addr); return _sort__sym_cmp(to_l->sym, to_r->sym); } @@ -1056,6 +1064,8 @@ int sort_dimension__add(const char *tok) sort__has_parent = 1; } else if (sd->entry == &sort_sym) { sort__has_sym = 1; + } else if (sd->entry == &sort_dso) { + sort__has_dso = 1; } __sort_dimension__add(sd, i); diff --git a/tools/perf/util/srcline.c b/tools/perf/util/srcline.c index d11aefbc4b8d..f3e4bc5fe5d2 100644 --- a/tools/perf/util/srcline.c +++ b/tools/perf/util/srcline.c @@ -129,7 +129,7 @@ static struct a2l_data *addr2line_init(const char *path) out: if (a2l) { - free((void *)a2l->input); + zfree((char **)&a2l->input); free(a2l); } bfd_close(abfd); @@ -140,24 +140,30 @@ static void addr2line_cleanup(struct a2l_data *a2l) { if (a2l->abfd) bfd_close(a2l->abfd); - free((void *)a2l->input); - free(a2l->syms); + zfree((char **)&a2l->input); + zfree(&a2l->syms); free(a2l); } static int addr2line(const char *dso_name, unsigned long addr, - char **file, unsigned int *line) + char **file, unsigned int *line, struct dso *dso) { int ret = 0; - struct a2l_data *a2l; + struct a2l_data *a2l = dso->a2l; + + if (!a2l) { + dso->a2l = addr2line_init(dso_name); + a2l = dso->a2l; + } - a2l = addr2line_init(dso_name); if (a2l == NULL) { pr_warning("addr2line_init failed for %s\n", dso_name); return 0; } a2l->addr = addr; + a2l->found = false; + bfd_map_over_sections(a2l->abfd, find_address_in_section, a2l); if (a2l->found && a2l->filename) { @@ -168,14 +174,26 @@ static int addr2line(const char *dso_name, unsigned long addr, ret = 1; } - addr2line_cleanup(a2l); return ret; } +void dso__free_a2l(struct dso *dso) +{ + struct a2l_data *a2l = dso->a2l; + + if (!a2l) + return; + + addr2line_cleanup(a2l); + + dso->a2l = NULL; +} + #else /* HAVE_LIBBFD_SUPPORT */ static int addr2line(const char *dso_name, unsigned long addr, - char **file, unsigned int *line_nr) + char **file, unsigned int *line_nr, + struct dso *dso __maybe_unused) { FILE *fp; char cmd[PATH_MAX]; @@ -219,42 +237,58 @@ out: pclose(fp); return ret; } + +void dso__free_a2l(struct dso *dso __maybe_unused) +{ +} + #endif /* HAVE_LIBBFD_SUPPORT */ +/* + * Number of addr2line failures (without success) before disabling it for that + * dso. + */ +#define A2L_FAIL_LIMIT 123 + char *get_srcline(struct dso *dso, unsigned long addr) { char *file = NULL; unsigned line = 0; char *srcline; - char *dso_name = dso->long_name; - size_t size; + const char *dso_name; if (!dso->has_srcline) return SRCLINE_UNKNOWN; + if (dso->symsrc_filename) + dso_name = dso->symsrc_filename; + else + dso_name = dso->long_name; + if (dso_name[0] == '[') goto out; if (!strncmp(dso_name, "/tmp/perf-", 10)) goto out; - if (!addr2line(dso_name, addr, &file, &line)) + if (!addr2line(dso_name, addr, &file, &line, dso)) goto out; - /* just calculate actual length */ - size = snprintf(NULL, 0, "%s:%u", file, line) + 1; + if (asprintf(&srcline, "%s:%u", file, line) < 0) { + free(file); + goto out; + } - srcline = malloc(size); - if (srcline) - snprintf(srcline, size, "%s:%u", file, line); - else - srcline = SRCLINE_UNKNOWN; + dso->a2l_fails = 0; free(file); return srcline; out: - dso->has_srcline = 0; + if (dso->a2l_fails && ++dso->a2l_fails > A2L_FAIL_LIMIT) { + dso->has_srcline = 0; + dso__free_a2l(dso); + } return SRCLINE_UNKNOWN; } diff --git a/tools/perf/util/strbuf.c b/tools/perf/util/strbuf.c index cfa906882e2c..4abe23550c73 100644 --- a/tools/perf/util/strbuf.c +++ b/tools/perf/util/strbuf.c @@ -28,7 +28,7 @@ void strbuf_init(struct strbuf *sb, ssize_t hint) void strbuf_release(struct strbuf *sb) { if (sb->alloc) { - free(sb->buf); + zfree(&sb->buf); strbuf_init(sb, 0); } } diff --git a/tools/perf/util/strfilter.c b/tools/perf/util/strfilter.c index 3edd0538161f..79a757a2a15c 100644 --- a/tools/perf/util/strfilter.c +++ b/tools/perf/util/strfilter.c @@ -14,7 +14,7 @@ static void strfilter_node__delete(struct strfilter_node *node) { if (node) { if (node->p && !is_operator(*node->p)) - free((char *)node->p); + zfree((char **)&node->p); strfilter_node__delete(node->l); strfilter_node__delete(node->r); free(node); diff --git a/tools/perf/util/string.c b/tools/perf/util/string.c index f0b0c008c507..2553e5b55b89 100644 --- a/tools/perf/util/string.c +++ b/tools/perf/util/string.c @@ -128,7 +128,7 @@ void argv_free(char **argv) { char **p; for (p = argv; *p; p++) - free(*p); + zfree(p); free(argv); } diff --git a/tools/perf/util/strlist.c b/tools/perf/util/strlist.c index eabdce0a2daa..71f9d102b96f 100644 --- a/tools/perf/util/strlist.c +++ b/tools/perf/util/strlist.c @@ -5,6 +5,7 @@ */ #include "strlist.h" +#include "util.h" #include <errno.h> #include <stdio.h> #include <stdlib.h> @@ -38,7 +39,7 @@ out_delete: static void str_node__delete(struct str_node *snode, bool dupstr) { if (dupstr) - free((void *)snode->s); + zfree((char **)&snode->s); free(snode); } diff --git a/tools/perf/util/svghelper.c b/tools/perf/util/svghelper.c index 96c866045d60..43262b83c541 100644 --- a/tools/perf/util/svghelper.c +++ b/tools/perf/util/svghelper.c @@ -17,8 +17,12 @@ #include <stdlib.h> #include <unistd.h> #include <string.h> +#include <linux/bitops.h> +#include "perf.h" #include "svghelper.h" +#include "util.h" +#include "cpumap.h" static u64 first_time, last_time; static u64 turbo_frequency, max_freq; @@ -28,6 +32,8 @@ static u64 turbo_frequency, max_freq; #define SLOT_HEIGHT 25.0 int svg_page_width = 1000; +u64 svg_highlight; +const char *svg_highlight_name; #define MIN_TEXT_SIZE 0.01 @@ -39,9 +45,14 @@ static double cpu2slot(int cpu) return 2 * cpu + 1; } +static int *topology_map; + static double cpu2y(int cpu) { - return cpu2slot(cpu) * SLOT_MULT; + if (topology_map) + return cpu2slot(topology_map[cpu]) * SLOT_MULT; + else + return cpu2slot(cpu) * SLOT_MULT; } static double time2pixels(u64 __time) @@ -95,6 +106,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) total_height = (1 + rows + cpu2slot(cpus)) * SLOT_MULT; fprintf(svgfile, "<?xml version=\"1.0\" standalone=\"no\"?> \n"); + fprintf(svgfile, "<!DOCTYPE svg SYSTEM \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"); fprintf(svgfile, "<svg width=\"%i\" height=\"%" PRIu64 "\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n", svg_page_width, total_height); fprintf(svgfile, "<defs>\n <style type=\"text/css\">\n <![CDATA[\n"); @@ -103,6 +115,7 @@ void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end) fprintf(svgfile, " rect.process { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:1; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.process2 { fill:rgb(180,180,180); fill-opacity:0.9; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.sample { fill:rgb( 0, 0,255); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); + fprintf(svgfile, " rect.sample_hi{ fill:rgb(255,128, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.blocked { fill:rgb(255, 0, 0); fill-opacity:0.5; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.waiting { fill:rgb(224,214, 0); fill-opacity:0.8; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); fprintf(svgfile, " rect.WAITING { fill:rgb(255,214, 48); fill-opacity:0.6; stroke-width:0; stroke:rgb( 0, 0, 0); } \n"); @@ -128,14 +141,42 @@ void svg_box(int Yslot, u64 start, u64 end, const char *type) time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, type); } -void svg_sample(int Yslot, int cpu, u64 start, u64 end) +static char *time_to_string(u64 duration); +void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) +{ + if (!svgfile) + return; + + fprintf(svgfile, "<g>\n"); + fprintf(svgfile, "<title>#%d blocked %s</title>\n", cpu, + time_to_string(end - start)); + if (backtrace) + fprintf(svgfile, "<desc>Blocked on:\n%s</desc>\n", backtrace); + svg_box(Yslot, start, end, "blocked"); + fprintf(svgfile, "</g>\n"); +} + +void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) { double text_size; + const char *type; + if (!svgfile) return; - fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"sample\"/>\n", - time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT); + if (svg_highlight && end - start > svg_highlight) + type = "sample_hi"; + else + type = "sample"; + fprintf(svgfile, "<g>\n"); + + fprintf(svgfile, "<title>#%d running %s</title>\n", + cpu, time_to_string(end - start)); + if (backtrace) + fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); + fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"%s\"/>\n", + time2pixels(start), time2pixels(end)-time2pixels(start), Yslot * SLOT_MULT, SLOT_HEIGHT, + type); text_size = (time2pixels(end)-time2pixels(start)); if (cpu > 9) @@ -148,6 +189,7 @@ void svg_sample(int Yslot, int cpu, u64 start, u64 end) fprintf(svgfile, "<text x=\"%1.8f\" y=\"%1.8f\" font-size=\"%1.8fpt\">%i</text>\n", time2pixels(start), Yslot * SLOT_MULT + SLOT_HEIGHT - 1, text_size, cpu + 1); + fprintf(svgfile, "</g>\n"); } static char *time_to_string(u64 duration) @@ -168,7 +210,7 @@ static char *time_to_string(u64 duration) return text; } -void svg_waiting(int Yslot, u64 start, u64 end) +void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace) { char *text; const char *style; @@ -192,6 +234,9 @@ void svg_waiting(int Yslot, u64 start, u64 end) font_size = round_text_size(font_size); fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), Yslot * SLOT_MULT); + fprintf(svgfile, "<title>#%d waiting %s</title>\n", cpu, time_to_string(end - start)); + if (backtrace) + fprintf(svgfile, "<desc>Waiting on:\n%s</desc>\n", backtrace); fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", time2pixels(end)-time2pixels(start), SLOT_HEIGHT, style); if (font_size > MIN_TEXT_SIZE) @@ -242,28 +287,42 @@ void svg_cpu_box(int cpu, u64 __max_freq, u64 __turbo_freq) max_freq = __max_freq; turbo_frequency = __turbo_freq; + fprintf(svgfile, "<g>\n"); + fprintf(svgfile, "<rect x=\"%4.8f\" width=\"%4.8f\" y=\"%4.1f\" height=\"%4.1f\" class=\"cpu\"/>\n", time2pixels(first_time), time2pixels(last_time)-time2pixels(first_time), cpu2y(cpu), SLOT_MULT+SLOT_HEIGHT); - sprintf(cpu_string, "CPU %i", (int)cpu+1); + sprintf(cpu_string, "CPU %i", (int)cpu); fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\">%s</text>\n", 10+time2pixels(first_time), cpu2y(cpu) + SLOT_HEIGHT/2, cpu_string); fprintf(svgfile, "<text transform=\"translate(%4.8f,%4.8f)\" font-size=\"1.25pt\">%s</text>\n", 10+time2pixels(first_time), cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - 4, cpu_model()); + + fprintf(svgfile, "</g>\n"); } -void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name) +void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace) { double width; + const char *type; if (!svgfile) return; + if (svg_highlight && end - start >= svg_highlight) + type = "sample_hi"; + else if (svg_highlight_name && strstr(name, svg_highlight_name)) + type = "sample_hi"; + else + type = "sample"; fprintf(svgfile, "<g transform=\"translate(%4.8f,%4.8f)\">\n", time2pixels(start), cpu2y(cpu)); + fprintf(svgfile, "<title>%d %s running %s</title>\n", pid, name, time_to_string(end - start)); + if (backtrace) + fprintf(svgfile, "<desc>Switched because:\n%s</desc>\n", backtrace); fprintf(svgfile, "<rect x=\"0\" width=\"%4.8f\" y=\"0\" height=\"%4.1f\" class=\"%s\"/>\n", time2pixels(end)-time2pixels(start), SLOT_MULT+SLOT_HEIGHT, type); width = time2pixels(end)-time2pixels(start); @@ -288,6 +347,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type) return; + fprintf(svgfile, "<g>\n"); + if (type > 6) type = 6; sprintf(style, "c%i", type); @@ -306,6 +367,8 @@ void svg_cstate(int cpu, u64 start, u64 end, int type) if (width > MIN_TEXT_SIZE) fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"%3.8fpt\">C%i</text>\n", time2pixels(start), cpu2y(cpu)+width, width, type); + + fprintf(svgfile, "</g>\n"); } static char *HzToHuman(unsigned long hz) @@ -339,6 +402,8 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq) if (!svgfile) return; + fprintf(svgfile, "<g>\n"); + if (max_freq) height = freq * 1.0 / max_freq * (SLOT_HEIGHT + SLOT_MULT); height = 1 + cpu2y(cpu) + SLOT_MULT + SLOT_HEIGHT - height; @@ -347,10 +412,11 @@ void svg_pstate(int cpu, u64 start, u64 end, u64 freq) fprintf(svgfile, "<text x=\"%4.8f\" y=\"%4.8f\" font-size=\"0.25pt\">%s</text>\n", time2pixels(start), height+0.9, HzToHuman(freq)); + fprintf(svgfile, "</g>\n"); } -void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2) +void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace) { double height; @@ -358,6 +424,15 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc return; + fprintf(svgfile, "<g>\n"); + + fprintf(svgfile, "<title>%s wakes up %s</title>\n", + desc1 ? desc1 : "?", + desc2 ? desc2 : "?"); + + if (backtrace) + fprintf(svgfile, "<desc>%s</desc>\n", backtrace); + if (row1 < row2) { if (row1) { fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", @@ -395,9 +470,11 @@ void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc if (row1) fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", time2pixels(start), height); + + fprintf(svgfile, "</g>\n"); } -void svg_wakeline(u64 start, int row1, int row2) +void svg_wakeline(u64 start, int row1, int row2, const char *backtrace) { double height; @@ -405,6 +482,11 @@ void svg_wakeline(u64 start, int row1, int row2) return; + fprintf(svgfile, "<g>\n"); + + if (backtrace) + fprintf(svgfile, "<desc>%s</desc>\n", backtrace); + if (row1 < row2) fprintf(svgfile, "<line x1=\"%4.8f\" y1=\"%4.2f\" x2=\"%4.8f\" y2=\"%4.2f\" style=\"stroke:rgb(32,255,32);stroke-width:0.009\"/>\n", time2pixels(start), row1 * SLOT_MULT + SLOT_HEIGHT, time2pixels(start), row2 * SLOT_MULT); @@ -417,17 +499,28 @@ void svg_wakeline(u64 start, int row1, int row2) height += SLOT_HEIGHT; fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(32,255,32)\"/>\n", time2pixels(start), height); + + fprintf(svgfile, "</g>\n"); } -void svg_interrupt(u64 start, int row) +void svg_interrupt(u64 start, int row, const char *backtrace) { if (!svgfile) return; + fprintf(svgfile, "<g>\n"); + + fprintf(svgfile, "<title>Wakeup from interrupt</title>\n"); + + if (backtrace) + fprintf(svgfile, "<desc>%s</desc>\n", backtrace); + fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", time2pixels(start), row * SLOT_MULT); fprintf(svgfile, "<circle cx=\"%4.8f\" cy=\"%4.2f\" r = \"0.01\" style=\"fill:rgb(255,128,128)\"/>\n", time2pixels(start), row * SLOT_MULT + SLOT_HEIGHT); + + fprintf(svgfile, "</g>\n"); } void svg_text(int Yslot, u64 start, const char *text) @@ -455,6 +548,7 @@ void svg_legenda(void) if (!svgfile) return; + fprintf(svgfile, "<g>\n"); svg_legenda_box(0, "Running", "sample"); svg_legenda_box(100, "Idle","c1"); svg_legenda_box(200, "Deeper Idle", "c3"); @@ -462,6 +556,7 @@ void svg_legenda(void) svg_legenda_box(550, "Sleeping", "process2"); svg_legenda_box(650, "Waiting for cpu", "waiting"); svg_legenda_box(800, "Blocked on IO", "blocked"); + fprintf(svgfile, "</g>\n"); } void svg_time_grid(void) @@ -499,3 +594,123 @@ void svg_close(void) svgfile = NULL; } } + +#define cpumask_bits(maskp) ((maskp)->bits) +typedef struct { DECLARE_BITMAP(bits, MAX_NR_CPUS); } cpumask_t; + +struct topology { + cpumask_t *sib_core; + int sib_core_nr; + cpumask_t *sib_thr; + int sib_thr_nr; +}; + +static void scan_thread_topology(int *map, struct topology *t, int cpu, int *pos) +{ + int i; + int thr; + + for (i = 0; i < t->sib_thr_nr; i++) { + if (!test_bit(cpu, cpumask_bits(&t->sib_thr[i]))) + continue; + + for_each_set_bit(thr, + cpumask_bits(&t->sib_thr[i]), + MAX_NR_CPUS) + if (map[thr] == -1) + map[thr] = (*pos)++; + } +} + +static void scan_core_topology(int *map, struct topology *t) +{ + int pos = 0; + int i; + int cpu; + + for (i = 0; i < t->sib_core_nr; i++) + for_each_set_bit(cpu, + cpumask_bits(&t->sib_core[i]), + MAX_NR_CPUS) + scan_thread_topology(map, t, cpu, &pos); +} + +static int str_to_bitmap(char *s, cpumask_t *b) +{ + int i; + int ret = 0; + struct cpu_map *m; + int c; + + m = cpu_map__new(s); + if (!m) + return -1; + + for (i = 0; i < m->nr; i++) { + c = m->map[i]; + if (c >= MAX_NR_CPUS) { + ret = -1; + break; + } + + set_bit(c, cpumask_bits(b)); + } + + cpu_map__delete(m); + + return ret; +} + +int svg_build_topology_map(char *sib_core, int sib_core_nr, + char *sib_thr, int sib_thr_nr) +{ + int i; + struct topology t; + + t.sib_core_nr = sib_core_nr; + t.sib_thr_nr = sib_thr_nr; + t.sib_core = calloc(sib_core_nr, sizeof(cpumask_t)); + t.sib_thr = calloc(sib_thr_nr, sizeof(cpumask_t)); + + if (!t.sib_core || !t.sib_thr) { + fprintf(stderr, "topology: no memory\n"); + goto exit; + } + + for (i = 0; i < sib_core_nr; i++) { + if (str_to_bitmap(sib_core, &t.sib_core[i])) { + fprintf(stderr, "topology: can't parse siblings map\n"); + goto exit; + } + + sib_core += strlen(sib_core) + 1; + } + + for (i = 0; i < sib_thr_nr; i++) { + if (str_to_bitmap(sib_thr, &t.sib_thr[i])) { + fprintf(stderr, "topology: can't parse siblings map\n"); + goto exit; + } + + sib_thr += strlen(sib_thr) + 1; + } + + topology_map = malloc(sizeof(int) * MAX_NR_CPUS); + if (!topology_map) { + fprintf(stderr, "topology: no memory\n"); + goto exit; + } + + for (i = 0; i < MAX_NR_CPUS; i++) + topology_map[i] = -1; + + scan_core_topology(topology_map, &t); + + return 0; + +exit: + zfree(&t.sib_core); + zfree(&t.sib_thr); + + return -1; +} diff --git a/tools/perf/util/svghelper.h b/tools/perf/util/svghelper.h index e0781989cc31..f7b4d6e699ea 100644 --- a/tools/perf/util/svghelper.h +++ b/tools/perf/util/svghelper.h @@ -5,24 +5,29 @@ extern void open_svg(const char *filename, int cpus, int rows, u64 start, u64 end); extern void svg_box(int Yslot, u64 start, u64 end, const char *type); -extern void svg_sample(int Yslot, int cpu, u64 start, u64 end); -extern void svg_waiting(int Yslot, u64 start, u64 end); +extern void svg_blocked(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_running(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); +extern void svg_waiting(int Yslot, int cpu, u64 start, u64 end, const char *backtrace); extern void svg_cpu_box(int cpu, u64 max_frequency, u64 turbo_frequency); -extern void svg_process(int cpu, u64 start, u64 end, const char *type, const char *name); +extern void svg_process(int cpu, u64 start, u64 end, int pid, const char *name, const char *backtrace); extern void svg_cstate(int cpu, u64 start, u64 end, int type); extern void svg_pstate(int cpu, u64 start, u64 end, u64 freq); extern void svg_time_grid(void); extern void svg_legenda(void); -extern void svg_wakeline(u64 start, int row1, int row2); -extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2); -extern void svg_interrupt(u64 start, int row); +extern void svg_wakeline(u64 start, int row1, int row2, const char *backtrace); +extern void svg_partial_wakeline(u64 start, int row1, char *desc1, int row2, char *desc2, const char *backtrace); +extern void svg_interrupt(u64 start, int row, const char *backtrace); extern void svg_text(int Yslot, u64 start, const char *text); extern void svg_close(void); +extern int svg_build_topology_map(char *sib_core, int sib_core_nr, + char *sib_thr, int sib_thr_nr); extern int svg_page_width; +extern u64 svg_highlight; +extern const char *svg_highlight_name; #endif /* __PERF_SVGHELPER_H */ diff --git a/tools/perf/util/symbol-elf.c b/tools/perf/util/symbol-elf.c index eed0b96302af..759456728703 100644 --- a/tools/perf/util/symbol-elf.c +++ b/tools/perf/util/symbol-elf.c @@ -6,6 +6,7 @@ #include <inttypes.h> #include "symbol.h" +#include <symbol/kallsyms.h> #include "debug.h" #ifndef HAVE_ELF_GETPHDRNUM_SUPPORT @@ -135,9 +136,8 @@ static size_t elf_addr_to_index(Elf *elf, GElf_Addr addr) return -1; } -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name, - size_t *idx) +Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name, size_t *idx) { Elf_Scn *sec = NULL; size_t cnt = 1; @@ -553,7 +553,7 @@ bool symsrc__has_symtab(struct symsrc *ss) void symsrc__destroy(struct symsrc *ss) { - free(ss->name); + zfree(&ss->name); elf_end(ss->elf); close(ss->fd); } diff --git a/tools/perf/util/symbol-minimal.c b/tools/perf/util/symbol-minimal.c index 2d2dd0532b5a..bd15f490d04f 100644 --- a/tools/perf/util/symbol-minimal.c +++ b/tools/perf/util/symbol-minimal.c @@ -1,4 +1,5 @@ #include "symbol.h" +#include "util.h" #include <stdio.h> #include <fcntl.h> @@ -253,6 +254,7 @@ int symsrc__init(struct symsrc *ss, struct dso *dso __maybe_unused, if (!ss->name) goto out_close; + ss->fd = fd; ss->type = type; return 0; @@ -274,7 +276,7 @@ bool symsrc__has_symtab(struct symsrc *ss __maybe_unused) void symsrc__destroy(struct symsrc *ss) { - free(ss->name); + zfree(&ss->name); close(ss->fd); } diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index c0c36965fff0..39ce9adbaaf0 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c @@ -18,12 +18,9 @@ #include <elf.h> #include <limits.h> +#include <symbol/kallsyms.h> #include <sys/utsname.h> -#ifndef KSYM_NAME_LEN -#define KSYM_NAME_LEN 256 -#endif - static int dso__load_kernel_sym(struct dso *dso, struct map *map, symbol_filter_t filter); static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, @@ -446,62 +443,6 @@ size_t dso__fprintf_symbols_by_name(struct dso *dso, return ret; } -int kallsyms__parse(const char *filename, void *arg, - int (*process_symbol)(void *arg, const char *name, - char type, u64 start)) -{ - char *line = NULL; - size_t n; - int err = -1; - FILE *file = fopen(filename, "r"); - - if (file == NULL) - goto out_failure; - - err = 0; - - while (!feof(file)) { - u64 start; - int line_len, len; - char symbol_type; - char *symbol_name; - - line_len = getline(&line, &n, file); - if (line_len < 0 || !line) - break; - - line[--line_len] = '\0'; /* \n */ - - len = hex2u64(line, &start); - - len++; - if (len + 2 >= line_len) - continue; - - symbol_type = line[len]; - len += 2; - symbol_name = line + len; - len = line_len - len; - - if (len >= KSYM_NAME_LEN) { - err = -1; - break; - } - - err = process_symbol(arg, symbol_name, - symbol_type, start); - if (err) - break; - } - - free(line); - fclose(file); - return err; - -out_failure: - return -1; -} - int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, u64 start)) @@ -565,12 +506,34 @@ struct process_kallsyms_args { struct dso *dso; }; -static u8 kallsyms2elf_type(char type) +bool symbol__is_idle(struct symbol *sym) { - if (type == 'W') - return STB_WEAK; + const char * const idle_symbols[] = { + "cpu_idle", + "intel_idle", + "default_idle", + "native_safe_halt", + "enter_idle", + "exit_idle", + "mwait_idle", + "mwait_idle_with_hints", + "poll_idle", + "ppc64_runlatch_off", + "pseries_dedicated_idle_sleep", + NULL + }; + + int i; + + if (!sym) + return false; + + for (i = 0; idle_symbols[i]; i++) { + if (!strcmp(idle_symbols[i], sym->name)) + return true; + } - return isupper(type) ? STB_GLOBAL : STB_LOCAL; + return false; } static int map__process_kallsym_symbol(void *arg, const char *name, @@ -833,7 +796,7 @@ static void delete_modules(struct rb_root *modules) mi = rb_entry(next, struct module_info, rb_node); next = rb_next(&mi->rb_node); rb_erase(&mi->rb_node, modules); - free(mi->name); + zfree(&mi->name); free(mi); } } @@ -1126,10 +1089,10 @@ static int dso__load_kcore(struct dso *dso, struct map *map, * dso__data_read_addr(). */ if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_KCORE; + dso->binary_type = DSO_BINARY_TYPE__GUEST_KCORE; else - dso->data_type = DSO_BINARY_TYPE__KCORE; - dso__set_long_name(dso, strdup(kcore_filename)); + dso->binary_type = DSO_BINARY_TYPE__KCORE; + dso__set_long_name(dso, strdup(kcore_filename), true); close(fd); @@ -1295,8 +1258,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) enum dso_binary_type symtab_type = binary_type_symtab[i]; - if (dso__binary_type_file(dso, symtab_type, - root_dir, name, PATH_MAX)) + if (dso__read_binary_type_filename(dso, symtab_type, + root_dir, name, PATH_MAX)) continue; /* Name is now the name of the next image to try */ @@ -1306,6 +1269,8 @@ int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter) if (!syms_ss && symsrc__has_symtab(ss)) { syms_ss = ss; next_slot = true; + if (!dso->symsrc_filename) + dso->symsrc_filename = strdup(name); } if (!runtime_ss && symsrc__possibly_runtime(ss)) { @@ -1376,7 +1341,8 @@ struct map *map_groups__find_by_name(struct map_groups *mg, } int dso__load_vmlinux(struct dso *dso, struct map *map, - const char *vmlinux, symbol_filter_t filter) + const char *vmlinux, bool vmlinux_allocated, + symbol_filter_t filter) { int err = -1; struct symsrc ss; @@ -1402,10 +1368,10 @@ int dso__load_vmlinux(struct dso *dso, struct map *map, if (err > 0) { if (dso->kernel == DSO_TYPE_GUEST_KERNEL) - dso->data_type = DSO_BINARY_TYPE__GUEST_VMLINUX; + dso->binary_type = DSO_BINARY_TYPE__GUEST_VMLINUX; else - dso->data_type = DSO_BINARY_TYPE__VMLINUX; - dso__set_long_name(dso, (char *)vmlinux); + dso->binary_type = DSO_BINARY_TYPE__VMLINUX; + dso__set_long_name(dso, vmlinux, vmlinux_allocated); dso__set_loaded(dso, map->type); pr_debug("Using %s for symbols\n", symfs_vmlinux); } @@ -1424,21 +1390,16 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map, filename = dso__build_id_filename(dso, NULL, 0); if (filename != NULL) { - err = dso__load_vmlinux(dso, map, filename, filter); - if (err > 0) { - dso->lname_alloc = 1; + err = dso__load_vmlinux(dso, map, filename, true, filter); + if (err > 0) goto out; - } free(filename); } for (i = 0; i < vmlinux_path__nr_entries; ++i) { - err = dso__load_vmlinux(dso, map, vmlinux_path[i], filter); - if (err > 0) { - dso__set_long_name(dso, strdup(vmlinux_path[i])); - dso->lname_alloc = 1; + err = dso__load_vmlinux(dso, map, vmlinux_path[i], false, filter); + if (err > 0) break; - } } out: return err; @@ -1496,14 +1457,15 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) build_id__sprintf(dso->build_id, sizeof(dso->build_id), sbuild_id); + scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", buildid_dir, + sbuild_id); + /* Use /proc/kallsyms if possible */ if (is_host) { DIR *d; int fd; /* If no cached kcore go with /proc/kallsyms */ - scnprintf(path, sizeof(path), "%s/[kernel.kcore]/%s", - buildid_dir, sbuild_id); d = opendir(path); if (!d) goto proc_kallsyms; @@ -1528,6 +1490,10 @@ static char *dso__find_kallsyms(struct dso *dso, struct map *map) goto proc_kallsyms; } + /* Find kallsyms in build-id cache with kcore */ + if (!find_matching_kcore(map, path, sizeof(path))) + return strdup(path); + scnprintf(path, sizeof(path), "%s/[kernel.kallsyms]/%s", buildid_dir, sbuild_id); @@ -1570,15 +1536,8 @@ static int dso__load_kernel_sym(struct dso *dso, struct map *map, } if (!symbol_conf.ignore_vmlinux && symbol_conf.vmlinux_name != NULL) { - err = dso__load_vmlinux(dso, map, - symbol_conf.vmlinux_name, filter); - if (err > 0) { - dso__set_long_name(dso, - strdup(symbol_conf.vmlinux_name)); - dso->lname_alloc = 1; - return err; - } - return err; + return dso__load_vmlinux(dso, map, symbol_conf.vmlinux_name, + false, filter); } if (!symbol_conf.ignore_vmlinux && vmlinux_path != NULL) { @@ -1604,7 +1563,7 @@ do_kallsyms: free(kallsyms_allocated_filename); if (err > 0 && !dso__is_kcore(dso)) { - dso__set_long_name(dso, strdup("[kernel.kallsyms]")); + dso__set_long_name(dso, "[kernel.kallsyms]", false); map__fixup_start(map); map__fixup_end(map); } @@ -1634,7 +1593,8 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, */ if (symbol_conf.default_guest_vmlinux_name != NULL) { err = dso__load_vmlinux(dso, map, - symbol_conf.default_guest_vmlinux_name, filter); + symbol_conf.default_guest_vmlinux_name, + false, filter); return err; } @@ -1651,7 +1611,7 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, pr_debug("Using %s for symbols\n", kallsyms_filename); if (err > 0 && !dso__is_kcore(dso)) { machine__mmap_name(machine, path, sizeof(path)); - dso__set_long_name(dso, strdup(path)); + dso__set_long_name(dso, strdup(path), true); map__fixup_start(map); map__fixup_end(map); } @@ -1661,13 +1621,10 @@ static int dso__load_guest_kernel_sym(struct dso *dso, struct map *map, static void vmlinux_path__exit(void) { - while (--vmlinux_path__nr_entries >= 0) { - free(vmlinux_path[vmlinux_path__nr_entries]); - vmlinux_path[vmlinux_path__nr_entries] = NULL; - } + while (--vmlinux_path__nr_entries >= 0) + zfree(&vmlinux_path[vmlinux_path__nr_entries]); - free(vmlinux_path); - vmlinux_path = NULL; + zfree(&vmlinux_path); } static int vmlinux_path__init(void) @@ -1719,7 +1676,7 @@ out_fail: return -1; } -static int setup_list(struct strlist **list, const char *list_str, +int setup_list(struct strlist **list, const char *list_str, const char *list_name) { if (list_str == NULL) diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index 07de8fea2f48..fffe2888a1c7 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h @@ -52,6 +52,11 @@ static inline char *bfd_demangle(void __maybe_unused *v, # define PERF_ELF_C_READ_MMAP ELF_C_READ #endif +#ifdef HAVE_LIBELF_SUPPORT +extern Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, + GElf_Shdr *shp, const char *name, size_t *idx); +#endif + #ifndef DMGL_PARAMS #define DMGL_PARAMS (1 << 0) /* Include function args */ #define DMGL_ANSI (1 << 1) /* Include const, volatile, etc */ @@ -164,6 +169,7 @@ struct mem_info { }; struct addr_location { + struct machine *machine; struct thread *thread; struct map *map; struct symbol *sym; @@ -206,7 +212,8 @@ bool symsrc__possibly_runtime(struct symsrc *ss); int dso__load(struct dso *dso, struct map *map, symbol_filter_t filter); int dso__load_vmlinux(struct dso *dso, struct map *map, - const char *vmlinux, symbol_filter_t filter); + const char *vmlinux, bool vmlinux_allocated, + symbol_filter_t filter); int dso__load_vmlinux_path(struct dso *dso, struct map *map, symbol_filter_t filter); int dso__load_kallsyms(struct dso *dso, const char *filename, struct map *map, @@ -220,9 +227,6 @@ struct symbol *dso__first_symbol(struct dso *dso, enum map_type type); int filename__read_build_id(const char *filename, void *bf, size_t size); int sysfs__read_build_id(const char *filename, void *bf, size_t size); -int kallsyms__parse(const char *filename, void *arg, - int (*process_symbol)(void *arg, const char *name, - char type, u64 start)); int modules__parse(const char *filename, void *arg, int (*process_module)(void *arg, const char *name, u64 start)); @@ -240,6 +244,7 @@ size_t symbol__fprintf(struct symbol *sym, FILE *fp); bool symbol_type__is_a(char symbol_type, enum map_type map_type); bool symbol__restricted_filename(const char *filename, const char *restricted_filename); +bool symbol__is_idle(struct symbol *sym); int dso__load_sym(struct dso *dso, struct map *map, struct symsrc *syms_ss, struct symsrc *runtime_ss, symbol_filter_t filter, @@ -273,4 +278,7 @@ void kcore_extract__delete(struct kcore_extract *kce); int kcore_copy(const char *from_dir, const char *to_dir); int compare_proc_modules(const char *from, const char *to); +int setup_list(struct strlist **list, const char *list_str, + const char *list_name); + #endif /* __PERF_SYMBOL */ diff --git a/tools/perf/util/target.c b/tools/perf/util/target.c index 3c778a07b7cc..e74c5963dc7a 100644 --- a/tools/perf/util/target.c +++ b/tools/perf/util/target.c @@ -55,6 +55,13 @@ enum target_errno target__validate(struct target *target) ret = TARGET_ERRNO__UID_OVERRIDE_SYSTEM; } + /* THREAD and SYSTEM/CPU are mutually exclusive */ + if (target->per_thread && (target->system_wide || target->cpu_list)) { + target->per_thread = false; + if (ret == TARGET_ERRNO__SUCCESS) + ret = TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD; + } + return ret; } @@ -100,6 +107,7 @@ static const char *target__error_str[] = { "UID switch overriding CPU", "PID/TID switch overriding SYSTEM", "UID switch overriding SYSTEM", + "SYSTEM/CPU switch overriding PER-THREAD", "Invalid User: %s", "Problems obtaining information for user %s", }; @@ -131,7 +139,8 @@ int target__strerror(struct target *target, int errnum, msg = target__error_str[idx]; switch (errnum) { - case TARGET_ERRNO__PID_OVERRIDE_CPU ... TARGET_ERRNO__UID_OVERRIDE_SYSTEM: + case TARGET_ERRNO__PID_OVERRIDE_CPU ... + TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD: snprintf(buf, buflen, "%s", msg); break; diff --git a/tools/perf/util/target.h b/tools/perf/util/target.h index 2d0c50690892..7381b1ca4041 100644 --- a/tools/perf/util/target.h +++ b/tools/perf/util/target.h @@ -12,7 +12,8 @@ struct target { uid_t uid; bool system_wide; bool uses_mmap; - bool force_per_cpu; + bool default_per_cpu; + bool per_thread; }; enum target_errno { @@ -33,6 +34,7 @@ enum target_errno { TARGET_ERRNO__UID_OVERRIDE_CPU, TARGET_ERRNO__PID_OVERRIDE_SYSTEM, TARGET_ERRNO__UID_OVERRIDE_SYSTEM, + TARGET_ERRNO__SYSTEM_OVERRIDE_THREAD, /* for target__parse_uid() */ TARGET_ERRNO__INVALID_UID, @@ -61,4 +63,17 @@ static inline bool target__none(struct target *target) return !target__has_task(target) && !target__has_cpu(target); } +static inline bool target__uses_dummy_map(struct target *target) +{ + bool use_dummy = false; + + if (target->default_per_cpu) + use_dummy = target->per_thread ? true : false; + else if (target__has_task(target) || + (!target__has_cpu(target) && !target->uses_mmap)) + use_dummy = true; + + return use_dummy; +} + #endif /* _PERF_TARGET_H */ diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 49eaf1d7d89d..0358882c8910 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -66,10 +66,13 @@ struct comm *thread__comm(const struct thread *thread) int thread__set_comm(struct thread *thread, const char *str, u64 timestamp) { struct comm *new, *curr = thread__comm(thread); + int err; /* Override latest entry if it had no specific time coverage */ if (!curr->start) { - comm__override(curr, str, timestamp); + err = comm__override(curr, str, timestamp); + if (err) + return err; } else { new = comm__new(str, timestamp); if (!new) @@ -126,7 +129,7 @@ int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) if (!comm) return -ENOMEM; err = thread__set_comm(thread, comm, timestamp); - if (!err) + if (err) return err; thread->comm_set = true; } diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 897c1b2a750a..5b856bf942e1 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -6,6 +6,7 @@ #include <unistd.h> #include <sys/types.h> #include "symbol.h" +#include <strlist.h> struct thread { union { @@ -66,4 +67,15 @@ static inline void thread__set_priv(struct thread *thread, void *p) { thread->priv = p; } + +static inline bool thread__is_filtered(struct thread *thread) +{ + if (symbol_conf.comm_list && + !strlist__has_entry(symbol_conf.comm_list, thread__comm_str(thread))) { + return true; + } + + return false; +} + #endif /* __PERF_THREAD_H */ diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c index 9b5f856cc280..5d3215912105 100644 --- a/tools/perf/util/thread_map.c +++ b/tools/perf/util/thread_map.c @@ -9,6 +9,7 @@ #include "strlist.h" #include <string.h> #include "thread_map.h" +#include "util.h" /* Skip "." and ".." directories */ static int filter(const struct dirent *dir) @@ -40,7 +41,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid) } for (i=0; i<items; i++) - free(namelist[i]); + zfree(&namelist[i]); free(namelist); return threads; @@ -117,7 +118,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid) threads->map[threads->nr + i] = atoi(namelist[i]->d_name); for (i = 0; i < items; i++) - free(namelist[i]); + zfree(&namelist[i]); free(namelist); threads->nr += items; @@ -134,12 +135,11 @@ out_free_threads: out_free_namelist: for (i = 0; i < items; i++) - free(namelist[i]); + zfree(&namelist[i]); free(namelist); out_free_closedir: - free(threads); - threads = NULL; + zfree(&threads); goto out_closedir; } @@ -194,7 +194,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str) for (i = 0; i < items; i++) { threads->map[j++] = atoi(namelist[i]->d_name); - free(namelist[i]); + zfree(&namelist[i]); } threads->nr = total_tasks; free(namelist); @@ -206,12 +206,11 @@ out: out_free_namelist: for (i = 0; i < items; i++) - free(namelist[i]); + zfree(&namelist[i]); free(namelist); out_free_threads: - free(threads); - threads = NULL; + zfree(&threads); goto out; } @@ -262,8 +261,7 @@ out: return threads; out_free_threads: - free(threads); - threads = NULL; + zfree(&threads); goto out; } diff --git a/tools/perf/util/top.c b/tools/perf/util/top.c index ce793c7dd23c..8e517def925b 100644 --- a/tools/perf/util/top.c +++ b/tools/perf/util/top.c @@ -26,7 +26,7 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size) float samples_per_sec; float ksamples_per_sec; float esamples_percent; - struct perf_record_opts *opts = &top->record_opts; + struct record_opts *opts = &top->record_opts; struct target *target = &opts->target; size_t ret = 0; diff --git a/tools/perf/util/top.h b/tools/perf/util/top.h index 88cfeaff600b..dab14d0ad3d0 100644 --- a/tools/perf/util/top.h +++ b/tools/perf/util/top.h @@ -14,7 +14,7 @@ struct perf_session; struct perf_top { struct perf_tool tool; struct perf_evlist *evlist; - struct perf_record_opts record_opts; + struct record_opts record_opts; /* * Symbols will be added here in perf_event__process_sample and will * get out after decayed. diff --git a/tools/perf/util/trace-event-info.c b/tools/perf/util/trace-event-info.c index f3c9e551bd35..7e6fcfe8b438 100644 --- a/tools/perf/util/trace-event-info.c +++ b/tools/perf/util/trace-event-info.c @@ -38,7 +38,7 @@ #include "../perf.h" #include "trace-event.h" -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include "evsel.h" #define VERSION "0.5" @@ -397,8 +397,8 @@ put_tracepoints_path(struct tracepoint_path *tps) struct tracepoint_path *t = tps; tps = tps->next; - free(t->name); - free(t->system); + zfree(&t->name); + zfree(&t->system); free(t); } } @@ -562,10 +562,8 @@ out: output_fd = fd; } - if (err) { - free(tdata); - tdata = NULL; - } + if (err) + zfree(&tdata); put_tracepoints_path(tps); return tdata; diff --git a/tools/perf/util/trace-event-parse.c b/tools/perf/util/trace-event-parse.c index 6681f71f2f95..e0d6d07f6848 100644 --- a/tools/perf/util/trace-event-parse.c +++ b/tools/perf/util/trace-event-parse.c @@ -28,19 +28,6 @@ #include "util.h" #include "trace-event.h" -struct pevent *read_trace_init(int file_bigendian, int host_bigendian) -{ - struct pevent *pevent = pevent_alloc(); - - if (pevent != NULL) { - pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); - pevent_set_file_bigendian(pevent, file_bigendian); - pevent_set_host_bigendian(pevent, host_bigendian); - } - - return pevent; -} - static int get_common_field(struct scripting_context *context, int *offset, int *size, const char *type) { diff --git a/tools/perf/util/trace-event-read.c b/tools/perf/util/trace-event-read.c index f2112270c663..e113e180c48f 100644 --- a/tools/perf/util/trace-event-read.c +++ b/tools/perf/util/trace-event-read.c @@ -343,7 +343,7 @@ static int read_event_files(struct pevent *pevent) return 0; } -ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) +ssize_t trace_report(int fd, struct trace_event *tevent, bool __repipe) { char buf[BUFSIZ]; char test[] = { 23, 8, 68 }; @@ -356,11 +356,9 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) int host_bigendian; int file_long_size; int file_page_size; - struct pevent *pevent; + struct pevent *pevent = NULL; int err; - *ppevent = NULL; - repipe = __repipe; input_fd = fd; @@ -390,12 +388,17 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) file_bigendian = buf[0]; host_bigendian = bigendian(); - pevent = read_trace_init(file_bigendian, host_bigendian); - if (pevent == NULL) { - pr_debug("read_trace_init failed"); + if (trace_event__init(tevent)) { + pr_debug("trace_event__init failed"); goto out; } + pevent = tevent->pevent; + + pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); + pevent_set_file_bigendian(pevent, file_bigendian); + pevent_set_host_bigendian(pevent, host_bigendian); + if (do_read(buf, 1) < 0) goto out; file_long_size = buf[0]; @@ -432,11 +435,10 @@ ssize_t trace_report(int fd, struct pevent **ppevent, bool __repipe) pevent_print_printk(pevent); } - *ppevent = pevent; pevent = NULL; out: if (pevent) - pevent_free(pevent); + trace_event__cleanup(tevent); return size; } diff --git a/tools/perf/util/trace-event-scripting.c b/tools/perf/util/trace-event-scripting.c index 95199e4eea97..57aaccc1692e 100644 --- a/tools/perf/util/trace-event-scripting.c +++ b/tools/perf/util/trace-event-scripting.c @@ -38,9 +38,8 @@ static int stop_script_unsupported(void) static void process_event_unsupported(union perf_event *event __maybe_unused, struct perf_sample *sample __maybe_unused, struct perf_evsel *evsel __maybe_unused, - struct machine *machine __maybe_unused, struct thread *thread __maybe_unused, - struct addr_location *al __maybe_unused) + struct addr_location *al __maybe_unused) { } diff --git a/tools/perf/util/trace-event.c b/tools/perf/util/trace-event.c new file mode 100644 index 000000000000..6322d37164c5 --- /dev/null +++ b/tools/perf/util/trace-event.c @@ -0,0 +1,82 @@ + +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <linux/kernel.h> +#include <traceevent/event-parse.h> +#include "trace-event.h" +#include "util.h" + +/* + * global trace_event object used by trace_event__tp_format + * + * TODO There's no cleanup call for this. Add some sort of + * __exit function support and call trace_event__cleanup + * there. + */ +static struct trace_event tevent; + +int trace_event__init(struct trace_event *t) +{ + struct pevent *pevent = pevent_alloc(); + + if (pevent) { + t->plugin_list = traceevent_load_plugins(pevent); + t->pevent = pevent; + } + + return pevent ? 0 : -1; +} + +void trace_event__cleanup(struct trace_event *t) +{ + traceevent_unload_plugins(t->plugin_list, t->pevent); + pevent_free(t->pevent); +} + +static struct event_format* +tp_format(const char *sys, const char *name) +{ + struct pevent *pevent = tevent.pevent; + struct event_format *event = NULL; + char path[PATH_MAX]; + size_t size; + char *data; + + scnprintf(path, PATH_MAX, "%s/%s/%s/format", + tracing_events_path, sys, name); + + if (filename__read_str(path, &data, &size)) + return NULL; + + pevent_parse_format(pevent, &event, data, size, sys); + + free(data); + return event; +} + +struct event_format* +trace_event__tp_format(const char *sys, const char *name) +{ + static bool initialized; + + if (!initialized) { + int be = traceevent_host_bigendian(); + struct pevent *pevent; + + if (trace_event__init(&tevent)) + return NULL; + + pevent = tevent.pevent; + pevent_set_flag(pevent, PEVENT_NSEC_OUTPUT); + pevent_set_file_bigendian(pevent, be); + pevent_set_host_bigendian(pevent, be); + initialized = true; + } + + return tp_format(sys, name); +} diff --git a/tools/perf/util/trace-event.h b/tools/perf/util/trace-event.h index 04df63114109..7b6d68688327 100644 --- a/tools/perf/util/trace-event.h +++ b/tools/perf/util/trace-event.h @@ -3,17 +3,26 @@ #include <traceevent/event-parse.h> #include "parse-events.h" -#include "session.h" struct machine; struct perf_sample; union perf_event; struct perf_tool; struct thread; +struct plugin_list; + +struct trace_event { + struct pevent *pevent; + struct plugin_list *plugin_list; +}; + +int trace_event__init(struct trace_event *t); +void trace_event__cleanup(struct trace_event *t); +struct event_format* +trace_event__tp_format(const char *sys, const char *name); int bigendian(void); -struct pevent *read_trace_init(int file_bigendian, int host_bigendian); void event_format__print(struct event_format *event, int cpu, void *data, int size); @@ -27,7 +36,7 @@ raw_field_value(struct event_format *event, const char *name, void *data); void parse_proc_kallsyms(struct pevent *pevent, char *file, unsigned int size); void parse_ftrace_printk(struct pevent *pevent, char *file, unsigned int size); -ssize_t trace_report(int fd, struct pevent **pevent, bool repipe); +ssize_t trace_report(int fd, struct trace_event *tevent, bool repipe); struct event_format *trace_find_next_event(struct pevent *pevent, struct event_format *event); @@ -59,7 +68,6 @@ struct scripting_ops { void (*process_event) (union perf_event *event, struct perf_sample *sample, struct perf_evsel *evsel, - struct machine *machine, struct thread *thread, struct addr_location *al); int (*generate_script) (struct pevent *pevent, const char *outfile); diff --git a/tools/perf/util/unwind.c b/tools/perf/util/unwind.c index 0efd5393de85..742f23bf35ff 100644 --- a/tools/perf/util/unwind.c +++ b/tools/perf/util/unwind.c @@ -28,6 +28,7 @@ #include "session.h" #include "perf_regs.h" #include "unwind.h" +#include "symbol.h" #include "util.h" extern int @@ -158,23 +159,6 @@ static int __dw_read_encoded_value(u8 **p, u8 *end, u64 *val, __v; \ }) -static Elf_Scn *elf_section_by_name(Elf *elf, GElf_Ehdr *ep, - GElf_Shdr *shp, const char *name) -{ - Elf_Scn *sec = NULL; - - while ((sec = elf_nextscn(elf, sec)) != NULL) { - char *str; - - gelf_getshdr(sec, shp); - str = elf_strptr(elf, ep->e_shstrndx, shp->sh_name); - if (!strcmp(name, str)) - break; - } - - return sec; -} - static u64 elf_section_offset(int fd, const char *name) { Elf *elf; @@ -190,7 +174,7 @@ static u64 elf_section_offset(int fd, const char *name) if (gelf_getehdr(elf, &ehdr) == NULL) break; - if (!elf_section_by_name(elf, &ehdr, &shdr, name)) + if (!elf_section_by_name(elf, &ehdr, &shdr, name, NULL)) break; offset = shdr.sh_offset; @@ -340,10 +324,10 @@ find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi, /* Check the .debug_frame section for unwinding info */ if (!read_unwind_spec_debug_frame(map->dso, ui->machine, &segbase)) { memset(&di, 0, sizeof(di)); - dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, - map->start, map->end); - return dwarf_search_unwind_table(as, ip, &di, pi, - need_unwind_info, arg); + if (dwarf_find_debug_frame(0, &di, ip, 0, map->dso->name, + map->start, map->end)) + return dwarf_search_unwind_table(as, ip, &di, pi, + need_unwind_info, arg); } #endif diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 28a0a89c1f73..42ad667bb317 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c @@ -1,11 +1,17 @@ #include "../perf.h" #include "util.h" +#include "fs.h" #include <sys/mman.h> #ifdef HAVE_BACKTRACE_SUPPORT #include <execinfo.h> #endif #include <stdio.h> #include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <limits.h> +#include <byteswap.h> +#include <linux/kernel.h> /* * XXX We need to find a better place for these things... @@ -151,21 +157,40 @@ unsigned long convert_unit(unsigned long value, char *unit) return value; } -int readn(int fd, void *buf, size_t n) +static ssize_t ion(bool is_read, int fd, void *buf, size_t n) { void *buf_start = buf; + size_t left = n; - while (n) { - int ret = read(fd, buf, n); + while (left) { + ssize_t ret = is_read ? read(fd, buf, left) : + write(fd, buf, left); if (ret <= 0) return ret; - n -= ret; - buf += ret; + left -= ret; + buf += ret; } - return buf - buf_start; + BUG_ON((size_t)(buf - buf_start) != n); + return n; +} + +/* + * Read exactly 'n' bytes or return an error. + */ +ssize_t readn(int fd, void *buf, size_t n) +{ + return ion(true, fd, buf, n); +} + +/* + * Write exactly 'n' bytes or return an error. + */ +ssize_t writen(int fd, void *buf, size_t n) +{ + return ion(false, fd, buf, n); } size_t hex_width(u64 v) @@ -413,3 +438,102 @@ int filename__read_int(const char *filename, int *value) close(fd); return err; } + +int filename__read_str(const char *filename, char **buf, size_t *sizep) +{ + size_t size = 0, alloc_size = 0; + void *bf = NULL, *nbf; + int fd, n, err = 0; + + fd = open(filename, O_RDONLY); + if (fd < 0) + return -errno; + + do { + if (size == alloc_size) { + alloc_size += BUFSIZ; + nbf = realloc(bf, alloc_size); + if (!nbf) { + err = -ENOMEM; + break; + } + + bf = nbf; + } + + n = read(fd, bf + size, alloc_size - size); + if (n < 0) { + if (size) { + pr_warning("read failed %d: %s\n", + errno, strerror(errno)); + err = 0; + } else + err = -errno; + + break; + } + + size += n; + } while (n > 0); + + if (!err) { + *sizep = size; + *buf = bf; + } else + free(bf); + + close(fd); + return err; +} + +const char *get_filename_for_perf_kvm(void) +{ + const char *filename; + + if (perf_host && !perf_guest) + filename = strdup("perf.data.host"); + else if (!perf_host && perf_guest) + filename = strdup("perf.data.guest"); + else + filename = strdup("perf.data.kvm"); + + return filename; +} + +int perf_event_paranoid(void) +{ + char path[PATH_MAX]; + const char *procfs = procfs__mountpoint(); + int value; + + if (!procfs) + return INT_MAX; + + scnprintf(path, PATH_MAX, "%s/sys/kernel/perf_event_paranoid", procfs); + + if (filename__read_int(path, &value)) + return INT_MAX; + + return value; +} + +void mem_bswap_32(void *src, int byte_size) +{ + u32 *m = src; + while (byte_size > 0) { + *m = bswap_32(*m); + byte_size -= sizeof(u32); + ++m; + } +} + +void mem_bswap_64(void *src, int byte_size) +{ + u64 *m = src; + + while (byte_size > 0) { + *m = bswap_64(*m); + byte_size -= sizeof(u64); + ++m; + } +} diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h index c8f362daba87..6995d66f225c 100644 --- a/tools/perf/util/util.h +++ b/tools/perf/util/util.h @@ -71,8 +71,9 @@ #include <linux/magic.h> #include "types.h" #include <sys/ttydefaults.h> -#include <lk/debugfs.h> +#include <api/fs/debugfs.h> #include <termios.h> +#include <linux/bitops.h> extern const char *graph_line; extern const char *graph_dotted_line; @@ -185,6 +186,8 @@ static inline void *zalloc(size_t size) return calloc(1, size); } +#define zfree(ptr) ({ free(*ptr); *ptr = NULL; }) + static inline int has_extension(const char *filename, const char *ext) { size_t len = strlen(filename); @@ -253,7 +256,8 @@ bool strlazymatch(const char *str, const char *pat); int strtailcmp(const char *s1, const char *s2); char *strxfrchar(char *s, char from, char to); unsigned long convert_unit(unsigned long value, char *unit); -int readn(int fd, void *buf, size_t size); +ssize_t readn(int fd, void *buf, size_t n); +ssize_t writen(int fd, void *buf, size_t n); struct perf_event_attr; @@ -280,6 +284,17 @@ static inline unsigned next_pow2(unsigned x) return 1ULL << (32 - __builtin_clz(x - 1)); } +static inline unsigned long next_pow2_l(unsigned long x) +{ +#if BITS_PER_LONG == 64 + if (x <= (1UL << 31)) + return next_pow2(x); + return (unsigned long)next_pow2(x >> 32) << 32; +#else + return next_pow2(x); +#endif +} + size_t hex_width(u64 v); int hex2u64(const char *ptr, u64 *val); @@ -307,4 +322,11 @@ char *get_srcline(struct dso *dso, unsigned long addr); void free_srcline(char *srcline); int filename__read_int(const char *filename, int *value); +int filename__read_str(const char *filename, char **buf, size_t *sizep); +int perf_event_paranoid(void); + +void mem_bswap_64(void *src, int byte_size); +void mem_bswap_32(void *src, int byte_size); + +const char *get_filename_for_perf_kvm(void); #endif /* GIT_COMPAT_UTIL_H */ diff --git a/tools/perf/util/values.c b/tools/perf/util/values.c index 697c8b4e59cc..0fb3c1fcd3e6 100644 --- a/tools/perf/util/values.c +++ b/tools/perf/util/values.c @@ -31,14 +31,14 @@ void perf_read_values_destroy(struct perf_read_values *values) return; for (i = 0; i < values->threads; i++) - free(values->value[i]); - free(values->value); - free(values->pid); - free(values->tid); - free(values->counterrawid); + zfree(&values->value[i]); + zfree(&values->value); + zfree(&values->pid); + zfree(&values->tid); + zfree(&values->counterrawid); for (i = 0; i < values->counters; i++) - free(values->countername[i]); - free(values->countername); + zfree(&values->countername[i]); + zfree(&values->countername); } static void perf_read_values__enlarge_threads(struct perf_read_values *values) diff --git a/tools/perf/util/vdso.c b/tools/perf/util/vdso.c index 39159822d58f..0ddb3b8a89ec 100644 --- a/tools/perf/util/vdso.c +++ b/tools/perf/util/vdso.c @@ -103,7 +103,7 @@ struct dso *vdso__dso_findnew(struct list_head *head) dso = dso__new(VDSO__MAP_NAME); if (dso != NULL) { dsos__add(head, dso); - dso__set_long_name(dso, file); + dso__set_long_name(dso, file, false); } } |