| // SPDX-License-Identifier: GPL-2.0 |
| #include <inttypes.h> |
| #include <stdio.h> |
| #include <stdbool.h> |
| #include "util/evlist.h" |
| #include "evsel.h" |
| #include "util/evsel_fprintf.h" |
| #include "util/event.h" |
| #include "callchain.h" |
| #include "map.h" |
| #include "strlist.h" |
| #include "symbol.h" |
| #include "srcline.h" |
| #include "dso.h" |
| |
| #ifdef HAVE_LIBTRACEEVENT |
| #include <traceevent/event-parse.h> |
| #endif |
| |
| static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) |
| { |
| va_list args; |
| int ret = 0; |
| |
| if (!*first) { |
| ret += fprintf(fp, ","); |
| } else { |
| ret += fprintf(fp, ":"); |
| *first = false; |
| } |
| |
| va_start(args, fmt); |
| ret += vfprintf(fp, fmt, args); |
| va_end(args); |
| return ret; |
| } |
| |
| static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) |
| { |
| return comma_fprintf(fp, (bool *)priv, " %s: %s", name, val); |
| } |
| |
| int evsel__fprintf(struct evsel *evsel, struct perf_attr_details *details, FILE *fp) |
| { |
| bool first = true; |
| int printed = 0; |
| |
| if (details->event_group) { |
| struct evsel *pos; |
| |
| if (!evsel__is_group_leader(evsel)) |
| return 0; |
| |
| if (evsel->core.nr_members > 1) |
| printed += fprintf(fp, "%s{", evsel->group_name ?: ""); |
| |
| printed += fprintf(fp, "%s", evsel__name(evsel)); |
| for_each_group_member(pos, evsel) |
| printed += fprintf(fp, ",%s", evsel__name(pos)); |
| |
| if (evsel->core.nr_members > 1) |
| printed += fprintf(fp, "}"); |
| goto out; |
| } |
| |
| printed += fprintf(fp, "%s", evsel__name(evsel)); |
| |
| if (details->verbose) { |
| printed += perf_event_attr__fprintf(fp, &evsel->core.attr, |
| __print_attr__fprintf, &first); |
| } else if (details->freq) { |
| const char *term = "sample_freq"; |
| |
| if (!evsel->core.attr.freq) |
| term = "sample_period"; |
| |
| printed += comma_fprintf(fp, &first, " %s=%" PRIu64, |
| term, (u64)evsel->core.attr.sample_freq); |
| } |
| |
| #ifdef HAVE_LIBTRACEEVENT |
| if (details->trace_fields) { |
| struct tep_format_field *field; |
| |
| if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) { |
| printed += comma_fprintf(fp, &first, " (not a tracepoint)"); |
| goto out; |
| } |
| |
| field = evsel->tp_format->format.fields; |
| if (field == NULL) { |
| printed += comma_fprintf(fp, &first, " (no trace field)"); |
| goto out; |
| } |
| |
| printed += comma_fprintf(fp, &first, " trace_fields: %s", field->name); |
| |
| field = field->next; |
| while (field) { |
| printed += comma_fprintf(fp, &first, "%s", field->name); |
| field = field->next; |
| } |
| } |
| #endif |
| out: |
| fputc('\n', fp); |
| return ++printed; |
| } |
| |
| int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, |
| unsigned int print_opts, struct callchain_cursor *cursor, |
| struct strlist *bt_stop_list, FILE *fp) |
| { |
| int printed = 0; |
| struct callchain_cursor_node *node; |
| int print_ip = print_opts & EVSEL__PRINT_IP; |
| int print_sym = print_opts & EVSEL__PRINT_SYM; |
| int print_dso = print_opts & EVSEL__PRINT_DSO; |
| int print_dsoff = print_opts & EVSEL__PRINT_DSOFF; |
| int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; |
| int print_oneline = print_opts & EVSEL__PRINT_ONELINE; |
| int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
| int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
| int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW; |
| int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED; |
| char s = print_oneline ? ' ' : '\t'; |
| bool first = true; |
| |
| if (cursor == NULL) |
| return fprintf(fp, "<not enough memory for the callchain cursor>%s", print_oneline ? "" : "\n"); |
| |
| if (sample->callchain) { |
| callchain_cursor_commit(cursor); |
| |
| while (1) { |
| struct symbol *sym; |
| struct map *map; |
| u64 addr = 0; |
| |
| node = callchain_cursor_current(cursor); |
| if (!node) |
| break; |
| |
| sym = node->ms.sym; |
| map = node->ms.map; |
| |
| if (sym && sym->ignore && print_skip_ignored) |
| goto next; |
| |
| printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
| |
| if (print_arrow && !first) |
| printed += fprintf(fp, " <-"); |
| |
| if (map) |
| addr = map__map_ip(map, node->ip); |
| |
| if (print_ip) |
| printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); |
| |
| if (print_sym) { |
| struct addr_location node_al; |
| |
| addr_location__init(&node_al); |
| printed += fprintf(fp, " "); |
| node_al.addr = addr; |
| node_al.map = map__get(map); |
| |
| if (print_symoffset) { |
| printed += __symbol__fprintf_symname_offs(sym, &node_al, |
| print_unknown_as_addr, |
| true, fp); |
| } else { |
| printed += __symbol__fprintf_symname(sym, &node_al, |
| print_unknown_as_addr, fp); |
| } |
| addr_location__exit(&node_al); |
| } |
| |
| if (print_dso && (!sym || !sym->inlined)) |
| printed += map__fprintf_dsoname_dsoff(map, print_dsoff, addr, fp); |
| |
| if (print_srcline) |
| printed += map__fprintf_srcline(map, addr, "\n ", fp); |
| |
| if (sym && sym->inlined) |
| printed += fprintf(fp, " (inlined)"); |
| |
| if (!print_oneline) |
| printed += fprintf(fp, "\n"); |
| |
| /* Add srccode here too? */ |
| if (bt_stop_list && sym && |
| strlist__has_entry(bt_stop_list, sym->name)) { |
| break; |
| } |
| |
| first = false; |
| next: |
| callchain_cursor_advance(cursor); |
| } |
| } |
| |
| return printed; |
| } |
| |
| int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, |
| int left_alignment, unsigned int print_opts, |
| struct callchain_cursor *cursor, struct strlist *bt_stop_list, FILE *fp) |
| { |
| int printed = 0; |
| int print_ip = print_opts & EVSEL__PRINT_IP; |
| int print_sym = print_opts & EVSEL__PRINT_SYM; |
| int print_dso = print_opts & EVSEL__PRINT_DSO; |
| int print_dsoff = print_opts & EVSEL__PRINT_DSOFF; |
| int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; |
| int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
| int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
| |
| if (cursor != NULL) { |
| printed += sample__fprintf_callchain(sample, left_alignment, print_opts, |
| cursor, bt_stop_list, fp); |
| } else { |
| printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
| |
| if (print_ip) |
| printed += fprintf(fp, "%16" PRIx64, sample->ip); |
| |
| if (print_sym) { |
| printed += fprintf(fp, " "); |
| if (print_symoffset) { |
| printed += __symbol__fprintf_symname_offs(al->sym, al, |
| print_unknown_as_addr, |
| true, fp); |
| } else { |
| printed += __symbol__fprintf_symname(al->sym, al, |
| print_unknown_as_addr, fp); |
| } |
| } |
| |
| if (print_dso) |
| printed += map__fprintf_dsoname_dsoff(al->map, print_dsoff, al->addr, fp); |
| |
| if (print_srcline) |
| printed += map__fprintf_srcline(al->map, al->addr, "\n ", fp); |
| } |
| |
| return printed; |
| } |