Linux Perf
hists_link.c
Go to the documentation of this file.
1 // SPDX-License-Identifier: GPL-2.0
2 #include "perf.h"
3 #include "tests.h"
4 #include "debug.h"
5 #include "symbol.h"
6 #include "sort.h"
7 #include "evsel.h"
8 #include "evlist.h"
9 #include "machine.h"
10 #include "thread.h"
11 #include "parse-events.h"
12 #include "hists_common.h"
13 #include <errno.h>
14 #include <linux/kernel.h>
15 
16 struct sample {
17  u32 pid;
18  u64 ip;
19  struct thread *thread;
20  struct map *map;
21  struct symbol *sym;
22 };
23 
24 /* For the numbers, see hists_common.c */
25 static struct sample fake_common_samples[] = {
26  /* perf [kernel] schedule() */
28  /* perf [perf] main() */
29  { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_MAIN, },
30  /* perf [perf] cmd_record() */
31  { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_PERF_CMD_RECORD, },
32  /* bash [bash] xmalloc() */
33  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XMALLOC, },
34  /* bash [libc] malloc() */
35  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, },
36 };
37 
38 static struct sample fake_samples[][5] = {
39  {
40  /* perf [perf] run_command() */
42  /* perf [libc] malloc() */
43  { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_LIBC_MALLOC, },
44  /* perf [kernel] page_fault() */
45  { .pid = FAKE_PID_PERF1, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
46  /* perf [kernel] sys_perf_event_open() */
48  /* bash [libc] free() */
49  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_FREE, },
50  },
51  {
52  /* perf [libc] free() */
53  { .pid = FAKE_PID_PERF2, .ip = FAKE_IP_LIBC_FREE, },
54  /* bash [libc] malloc() */
55  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_MALLOC, }, /* will be merged */
56  /* bash [bash] xfee() */
57  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_BASH_XFREE, },
58  /* bash [libc] realloc() */
59  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_LIBC_REALLOC, },
60  /* bash [kernel] page_fault() */
61  { .pid = FAKE_PID_BASH, .ip = FAKE_IP_KERNEL_PAGE_FAULT, },
62  },
63 };
64 
65 static int add_hist_entries(struct perf_evlist *evlist, struct machine *machine)
66 {
67  struct perf_evsel *evsel;
68  struct addr_location al;
69  struct hist_entry *he;
70  struct perf_sample sample = { .period = 1, .weight = 1, };
71  size_t i = 0, k;
72 
73  /*
74  * each evsel will have 10 samples - 5 common and 5 distinct.
75  * However the second evsel also has a collapsed entry for
76  * "bash [libc] malloc" so total 9 entries will be in the tree.
77  */
78  evlist__for_each_entry(evlist, evsel) {
79  struct hists *hists = evsel__hists(evsel);
80 
81  for (k = 0; k < ARRAY_SIZE(fake_common_samples); k++) {
82  sample.cpumode = PERF_RECORD_MISC_USER;
83  sample.pid = fake_common_samples[k].pid;
84  sample.tid = fake_common_samples[k].pid;
85  sample.ip = fake_common_samples[k].ip;
86 
87  if (machine__resolve(machine, &al, &sample) < 0)
88  goto out;
89 
90  he = hists__add_entry(hists, &al, NULL,
91  NULL, NULL, &sample, true);
92  if (he == NULL) {
93  addr_location__put(&al);
94  goto out;
95  }
96 
97  fake_common_samples[k].thread = al.thread;
98  fake_common_samples[k].map = al.map;
99  fake_common_samples[k].sym = al.sym;
100  }
101 
102  for (k = 0; k < ARRAY_SIZE(fake_samples[i]); k++) {
103  sample.pid = fake_samples[i][k].pid;
104  sample.tid = fake_samples[i][k].pid;
105  sample.ip = fake_samples[i][k].ip;
106  if (machine__resolve(machine, &al, &sample) < 0)
107  goto out;
108 
109  he = hists__add_entry(hists, &al, NULL,
110  NULL, NULL, &sample, true);
111  if (he == NULL) {
112  addr_location__put(&al);
113  goto out;
114  }
115 
116  fake_samples[i][k].thread = al.thread;
117  fake_samples[i][k].map = al.map;
118  fake_samples[i][k].sym = al.sym;
119  }
120  i++;
121  }
122 
123  return 0;
124 
125 out:
126  pr_debug("Not enough memory for adding a hist entry\n");
127  return -1;
128 }
129 
130 static int find_sample(struct sample *samples, size_t nr_samples,
131  struct thread *t, struct map *m, struct symbol *s)
132 {
133  while (nr_samples--) {
134  if (samples->thread == t && samples->map == m &&
135  samples->sym == s)
136  return 1;
137  samples++;
138  }
139  return 0;
140 }
141 
142 static int __validate_match(struct hists *hists)
143 {
144  size_t count = 0;
145  struct rb_root *root;
146  struct rb_node *node;
147 
148  /*
149  * Only entries from fake_common_samples should have a pair.
150  */
151  if (hists__has(hists, need_collapse))
152  root = &hists->entries_collapsed;
153  else
154  root = hists->entries_in;
155 
156  node = rb_first(root);
157  while (node) {
158  struct hist_entry *he;
159 
160  he = rb_entry(node, struct hist_entry, rb_node_in);
161 
162  if (hist_entry__has_pairs(he)) {
163  if (find_sample(fake_common_samples,
164  ARRAY_SIZE(fake_common_samples),
165  he->thread, he->ms.map, he->ms.sym)) {
166  count++;
167  } else {
168  pr_debug("Can't find the matched entry\n");
169  return -1;
170  }
171  }
172 
173  node = rb_next(node);
174  }
175 
176  if (count != ARRAY_SIZE(fake_common_samples)) {
177  pr_debug("Invalid count for matched entries: %zd of %zd\n",
178  count, ARRAY_SIZE(fake_common_samples));
179  return -1;
180  }
181 
182  return 0;
183 }
184 
185 static int validate_match(struct hists *leader, struct hists *other)
186 {
187  return __validate_match(leader) || __validate_match(other);
188 }
189 
190 static int __validate_link(struct hists *hists, int idx)
191 {
192  size_t count = 0;
193  size_t count_pair = 0;
194  size_t count_dummy = 0;
195  struct rb_root *root;
196  struct rb_node *node;
197 
198  /*
199  * Leader hists (idx = 0) will have dummy entries from other,
200  * and some entries will have no pair. However every entry
201  * in other hists should have (dummy) pair.
202  */
203  if (hists__has(hists, need_collapse))
204  root = &hists->entries_collapsed;
205  else
206  root = hists->entries_in;
207 
208  node = rb_first(root);
209  while (node) {
210  struct hist_entry *he;
211 
212  he = rb_entry(node, struct hist_entry, rb_node_in);
213 
214  if (hist_entry__has_pairs(he)) {
215  if (!find_sample(fake_common_samples,
216  ARRAY_SIZE(fake_common_samples),
217  he->thread, he->ms.map, he->ms.sym) &&
218  !find_sample(fake_samples[idx],
219  ARRAY_SIZE(fake_samples[idx]),
220  he->thread, he->ms.map, he->ms.sym)) {
221  count_dummy++;
222  }
223  count_pair++;
224  } else if (idx) {
225  pr_debug("A entry from the other hists should have pair\n");
226  return -1;
227  }
228 
229  count++;
230  node = rb_next(node);
231  }
232 
233  /*
234  * Note that we have a entry collapsed in the other (idx = 1) hists.
235  */
236  if (idx == 0) {
237  if (count_dummy != ARRAY_SIZE(fake_samples[1]) - 1) {
238  pr_debug("Invalid count of dummy entries: %zd of %zd\n",
239  count_dummy, ARRAY_SIZE(fake_samples[1]) - 1);
240  return -1;
241  }
242  if (count != count_pair + ARRAY_SIZE(fake_samples[0])) {
243  pr_debug("Invalid count of total leader entries: %zd of %zd\n",
244  count, count_pair + ARRAY_SIZE(fake_samples[0]));
245  return -1;
246  }
247  } else {
248  if (count != count_pair) {
249  pr_debug("Invalid count of total other entries: %zd of %zd\n",
250  count, count_pair);
251  return -1;
252  }
253  if (count_dummy > 0) {
254  pr_debug("Other hists should not have dummy entries: %zd\n",
255  count_dummy);
256  return -1;
257  }
258  }
259 
260  return 0;
261 }
262 
263 static int validate_link(struct hists *leader, struct hists *other)
264 {
265  return __validate_link(leader, 0) || __validate_link(other, 1);
266 }
267 
268 int test__hists_link(struct test *test __maybe_unused, int subtest __maybe_unused)
269 {
270  int err = -1;
271  struct hists *hists, *first_hists;
272  struct machines machines;
273  struct machine *machine = NULL;
274  struct perf_evsel *evsel, *first;
275  struct perf_evlist *evlist = perf_evlist__new();
276 
277  if (evlist == NULL)
278  return -ENOMEM;
279 
280  err = parse_events(evlist, "cpu-clock", NULL);
281  if (err)
282  goto out;
283  err = parse_events(evlist, "task-clock", NULL);
284  if (err)
285  goto out;
286 
287  err = TEST_FAIL;
288  /* default sort order (comm,dso,sym) will be used */
289  if (setup_sorting(NULL) < 0)
290  goto out;
291 
292  machines__init(&machines);
293 
294  /* setup threads/dso/map/symbols also */
295  machine = setup_fake_machine(&machines);
296  if (!machine)
297  goto out;
298 
299  if (verbose > 1)
300  machine__fprintf(machine, stderr);
301 
302  /* process sample events */
303  err = add_hist_entries(evlist, machine);
304  if (err < 0)
305  goto out;
306 
307  evlist__for_each_entry(evlist, evsel) {
308  hists = evsel__hists(evsel);
309  hists__collapse_resort(hists, NULL);
310 
311  if (verbose > 2)
312  print_hists_in(hists);
313  }
314 
315  first = perf_evlist__first(evlist);
316  evsel = perf_evlist__last(evlist);
317 
318  first_hists = evsel__hists(first);
319  hists = evsel__hists(evsel);
320 
321  /* match common entries */
322  hists__match(first_hists, hists);
323  err = validate_match(first_hists, hists);
324  if (err)
325  goto out;
326 
327  /* link common and/or dummy entries */
328  hists__link(first_hists, hists);
329  err = validate_link(first_hists, hists);
330  if (err)
331  goto out;
332 
333  err = 0;
334 
335 out:
336  /* tear down everything */
337  perf_evlist__delete(evlist);
339  machines__exit(&machines);
340 
341  return err;
342 }
#define FAKE_IP_BASH_XMALLOC
Definition: hists_common.h:27
static void setup_sorting(struct perf_sched *sched, const struct option *options, const char *const usage_msg[])
struct rb_root * entries_in
Definition: hist.h:73
Definition: mem2node.c:7
void machines__exit(struct machines *machines)
Definition: machine.c:228
#define FAKE_IP_KERNEL_SYS_PERF_EVENT_OPEN
Definition: hists_common.h:34
int machine__resolve(struct machine *machine, struct addr_location *al, struct perf_sample *sample)
Definition: event.c:1601
static bool hist_entry__has_pairs(struct hist_entry *he)
Definition: sort.h:159
void reset_output_field(void)
Definition: sort.c:2997
struct map_symbol ms
Definition: sort.h:98
struct symbol * sym
Definition: symbol.h:181
int hists__link(struct hists *leader, struct hists *other)
Definition: hist.c:2374
struct map * map
Definition: symbol.h:180
#define FAKE_IP_BASH_XFREE
Definition: hists_common.h:28
struct hist_entry * hists__add_entry(struct hists *hists, struct addr_location *al, struct symbol *sym_parent, struct branch_info *bi, struct mem_info *mi, struct perf_sample *sample, bool sample_self)
Definition: hist.c:627
int int err
Definition: 5sec.c:44
int hists__collapse_resort(struct hists *hists, struct ui_progress *prog)
Definition: hist.c:1464
struct rb_root root
Definition: block-range.c:6
#define FAKE_IP_LIBC_MALLOC
Definition: hists_common.h:29
void perf_evlist__delete(struct perf_evlist *evlist)
Definition: evlist.c:133
#define FAKE_IP_LIBC_REALLOC
Definition: hists_common.h:31
static struct hists * evsel__hists(struct perf_evsel *evsel)
Definition: hist.h:217
struct map * map
u64 ip
Definition: event.h:192
struct thread * thread
Definition: sort.h:99
Definition: sort.h:89
int parse_events(struct perf_evlist *evlist, const char *str, struct parse_events_error *err)
void addr_location__put(struct addr_location *al)
Definition: event.c:1661
struct map * map
Definition: symbol.h:210
Definition: thread.h:18
struct symbol * sym
#define pr_debug(fmt,...)
Definition: json.h:27
#define FAKE_IP_PERF_MAIN
Definition: hists_common.h:23
#define FAKE_IP_KERNEL_SCHEDULE
Definition: hists_common.h:32
#define evlist__for_each_entry(evlist, evsel)
Definition: evlist.h:247
void print_hists_in(struct hists *hists)
Definition: hists_common.c:161
#define FAKE_PID_PERF2
Definition: hists_common.h:9
u64 period
Definition: event.h:198
u32 pid
Definition: event.h:193
u32 tid
Definition: event.h:193
#define hists__has(__h, __f)
Definition: hist.h:94
struct symbol * sym
Definition: symbol.h:211
#define FAKE_IP_PERF_CMD_RECORD
Definition: hists_common.h:25
struct thread * thread
Definition: symbol.h:209
struct rb_node rb_node_in
Definition: sort.h:90
#define FAKE_PID_PERF1
Definition: hists_common.h:8
static struct perf_evsel * perf_evlist__first(struct perf_evlist *evlist)
Definition: evlist.h:215
Definition: jevents.c:228
void hists__match(struct hists *leader, struct hists *other)
Definition: hist.c:2299
void machines__init(struct machines *machines)
Definition: machine.c:222
Definition: tests.h:30
#define FAKE_IP_PERF_RUN_COMMAND
Definition: hists_common.h:24
#define FAKE_IP_KERNEL_PAGE_FAULT
Definition: hists_common.h:33
int verbose
Definition: jevents.c:53
Definition: symbol.h:55
static struct perf_evsel * perf_evlist__last(struct perf_evlist *evlist)
Definition: evlist.h:220
struct machine * setup_fake_machine(struct machines *machines)
Definition: hists_common.c:83
#define FAKE_PID_BASH
Definition: hists_common.h:10
Definition: hist.h:71
struct rb_root entries_collapsed
Definition: hist.h:75
struct thread * thread
#define FAKE_IP_LIBC_FREE
Definition: hists_common.h:30
u8 cpumode
Definition: event.h:207
size_t machine__fprintf(struct machine *machine, FILE *fp)
Definition: machine.c:756
struct perf_evlist * perf_evlist__new(void)
Definition: evlist.c:54