1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <inttypes.h> |
3 | #include <stdio.h> |
4 | #include <stdbool.h> |
5 | #include "util/evlist.h" |
6 | #include "evsel.h" |
7 | #include "util/evsel_fprintf.h" |
8 | #include "util/event.h" |
9 | #include "callchain.h" |
10 | #include "map.h" |
11 | #include "strlist.h" |
12 | #include "symbol.h" |
13 | #include "srcline.h" |
14 | #include "dso.h" |
15 | |
16 | #ifdef HAVE_LIBTRACEEVENT |
17 | #include <traceevent/event-parse.h> |
18 | #endif |
19 | |
20 | static int comma_fprintf(FILE *fp, bool *first, const char *fmt, ...) |
21 | { |
22 | va_list args; |
23 | int ret = 0; |
24 | |
25 | if (!*first) { |
26 | ret += fprintf(fp, "," ); |
27 | } else { |
28 | ret += fprintf(fp, ":" ); |
29 | *first = false; |
30 | } |
31 | |
32 | va_start(args, fmt); |
33 | ret += vfprintf(fp, fmt, args); |
34 | va_end(args); |
35 | return ret; |
36 | } |
37 | |
38 | static int __print_attr__fprintf(FILE *fp, const char *name, const char *val, void *priv) |
39 | { |
40 | return comma_fprintf(fp, (bool *)priv, " %s: %s" , name, val); |
41 | } |
42 | |
43 | int evsel__fprintf(struct evsel *evsel, struct perf_attr_details *details, FILE *fp) |
44 | { |
45 | bool first = true; |
46 | int printed = 0; |
47 | |
48 | if (details->event_group) { |
49 | struct evsel *pos; |
50 | |
51 | if (!evsel__is_group_leader(evsel)) |
52 | return 0; |
53 | |
54 | if (evsel->core.nr_members > 1) |
55 | printed += fprintf(fp, "%s{" , evsel->group_name ?: "" ); |
56 | |
57 | printed += fprintf(fp, "%s" , evsel__name(evsel)); |
58 | for_each_group_member(pos, evsel) |
59 | printed += fprintf(fp, ",%s" , evsel__name(evsel: pos)); |
60 | |
61 | if (evsel->core.nr_members > 1) |
62 | printed += fprintf(fp, "}" ); |
63 | goto out; |
64 | } |
65 | |
66 | printed += fprintf(fp, "%s" , evsel__name(evsel)); |
67 | |
68 | if (details->verbose) { |
69 | printed += perf_event_attr__fprintf(fp, &evsel->core.attr, |
70 | __print_attr__fprintf, &first); |
71 | } else if (details->freq) { |
72 | const char *term = "sample_freq" ; |
73 | |
74 | if (!evsel->core.attr.freq) |
75 | term = "sample_period" ; |
76 | |
77 | printed += comma_fprintf(fp, &first, " %s=%" PRIu64, |
78 | term, (u64)evsel->core.attr.sample_freq); |
79 | } |
80 | |
81 | #ifdef HAVE_LIBTRACEEVENT |
82 | if (details->trace_fields) { |
83 | struct tep_format_field *field; |
84 | |
85 | if (evsel->core.attr.type != PERF_TYPE_TRACEPOINT) { |
86 | printed += comma_fprintf(fp, &first, " (not a tracepoint)" ); |
87 | goto out; |
88 | } |
89 | |
90 | field = evsel->tp_format->format.fields; |
91 | if (field == NULL) { |
92 | printed += comma_fprintf(fp, &first, " (no trace field)" ); |
93 | goto out; |
94 | } |
95 | |
96 | printed += comma_fprintf(fp, &first, " trace_fields: %s" , field->name); |
97 | |
98 | field = field->next; |
99 | while (field) { |
100 | printed += comma_fprintf(fp, &first, "%s" , field->name); |
101 | field = field->next; |
102 | } |
103 | } |
104 | #endif |
105 | out: |
106 | fputc('\n', fp); |
107 | return ++printed; |
108 | } |
109 | |
110 | #ifndef PYTHON_PERF |
111 | int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, |
112 | unsigned int print_opts, struct callchain_cursor *cursor, |
113 | struct strlist *bt_stop_list, FILE *fp) |
114 | { |
115 | int printed = 0; |
116 | struct callchain_cursor_node *node; |
117 | int print_ip = print_opts & EVSEL__PRINT_IP; |
118 | int print_sym = print_opts & EVSEL__PRINT_SYM; |
119 | int print_dso = print_opts & EVSEL__PRINT_DSO; |
120 | int print_dsoff = print_opts & EVSEL__PRINT_DSOFF; |
121 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; |
122 | int print_oneline = print_opts & EVSEL__PRINT_ONELINE; |
123 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
124 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
125 | int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW; |
126 | int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED; |
127 | char s = print_oneline ? ' ' : '\t'; |
128 | bool first = true; |
129 | |
130 | if (cursor == NULL) |
131 | return fprintf(fp, "<not enough memory for the callchain cursor>%s" , print_oneline ? "" : "\n" ); |
132 | |
133 | if (sample->callchain) { |
134 | callchain_cursor_commit(cursor); |
135 | |
136 | while (1) { |
137 | struct symbol *sym; |
138 | struct map *map; |
139 | u64 addr = 0; |
140 | |
141 | node = callchain_cursor_current(cursor); |
142 | if (!node) |
143 | break; |
144 | |
145 | sym = node->ms.sym; |
146 | map = node->ms.map; |
147 | |
148 | if (sym && sym->ignore && print_skip_ignored) |
149 | goto next; |
150 | |
151 | printed += fprintf(fp, "%-*.*s" , left_alignment, left_alignment, " " ); |
152 | |
153 | if (print_arrow && !first) |
154 | printed += fprintf(fp, " <-" ); |
155 | |
156 | if (map) |
157 | addr = map__map_ip(map, ip_or_rip: node->ip); |
158 | |
159 | if (print_ip) |
160 | printed += fprintf(fp, "%c%16" PRIx64, s, node->ip); |
161 | |
162 | if (print_sym) { |
163 | struct addr_location node_al; |
164 | |
165 | addr_location__init(al: &node_al); |
166 | printed += fprintf(fp, " " ); |
167 | node_al.addr = addr; |
168 | node_al.map = map__get(map); |
169 | |
170 | if (print_symoffset) { |
171 | printed += __symbol__fprintf_symname_offs(sym, &node_al, |
172 | print_unknown_as_addr, |
173 | true, fp); |
174 | } else { |
175 | printed += __symbol__fprintf_symname(sym, &node_al, |
176 | print_unknown_as_addr, fp); |
177 | } |
178 | addr_location__exit(al: &node_al); |
179 | } |
180 | |
181 | if (print_dso && (!sym || !sym->inlined)) |
182 | printed += map__fprintf_dsoname_dsoff(map, print_dsoff, addr, fp); |
183 | |
184 | if (print_srcline) |
185 | printed += map__fprintf_srcline(map, addr, "\n " , fp); |
186 | |
187 | if (sym && sym->inlined) |
188 | printed += fprintf(fp, " (inlined)" ); |
189 | |
190 | if (!print_oneline) |
191 | printed += fprintf(fp, "\n" ); |
192 | |
193 | /* Add srccode here too? */ |
194 | if (bt_stop_list && sym && |
195 | strlist__has_entry(slist: bt_stop_list, entry: sym->name)) { |
196 | break; |
197 | } |
198 | |
199 | first = false; |
200 | next: |
201 | callchain_cursor_advance(cursor); |
202 | } |
203 | } |
204 | |
205 | return printed; |
206 | } |
207 | |
208 | int sample__fprintf_sym(struct perf_sample *sample, struct addr_location *al, |
209 | int left_alignment, unsigned int print_opts, |
210 | struct callchain_cursor *cursor, struct strlist *bt_stop_list, FILE *fp) |
211 | { |
212 | int printed = 0; |
213 | int print_ip = print_opts & EVSEL__PRINT_IP; |
214 | int print_sym = print_opts & EVSEL__PRINT_SYM; |
215 | int print_dso = print_opts & EVSEL__PRINT_DSO; |
216 | int print_dsoff = print_opts & EVSEL__PRINT_DSOFF; |
217 | int print_symoffset = print_opts & EVSEL__PRINT_SYMOFFSET; |
218 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
219 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
220 | |
221 | if (cursor != NULL) { |
222 | printed += sample__fprintf_callchain(sample, left_alignment, print_opts, |
223 | cursor, bt_stop_list, fp); |
224 | } else { |
225 | printed += fprintf(fp, "%-*.*s" , left_alignment, left_alignment, " " ); |
226 | |
227 | if (print_ip) |
228 | printed += fprintf(fp, "%16" PRIx64, sample->ip); |
229 | |
230 | if (print_sym) { |
231 | printed += fprintf(fp, " " ); |
232 | if (print_symoffset) { |
233 | printed += __symbol__fprintf_symname_offs(al->sym, al, |
234 | print_unknown_as_addr, |
235 | true, fp); |
236 | } else { |
237 | printed += __symbol__fprintf_symname(al->sym, al, |
238 | print_unknown_as_addr, fp); |
239 | } |
240 | } |
241 | |
242 | if (print_dso) |
243 | printed += map__fprintf_dsoname_dsoff(al->map, print_dsoff, al->addr, fp); |
244 | |
245 | if (print_srcline) |
246 | printed += map__fprintf_srcline(al->map, al->addr, "\n " , fp); |
247 | } |
248 | |
249 | return printed; |
250 | } |
251 | #endif /* PYTHON_PERF */ |
252 | |