1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <inttypes.h> |
3 | #include "util/debug.h" |
4 | #include "util/dso.h" |
5 | #include "util/event.h" // struct perf_sample |
6 | #include "util/map.h" |
7 | #include "util/symbol.h" |
8 | #include "util/sort.h" |
9 | #include "util/evsel.h" |
10 | #include "util/machine.h" |
11 | #include "util/thread.h" |
12 | #include "tests/hists_common.h" |
13 | #include <linux/kernel.h> |
14 | #include <linux/perf_event.h> |
15 | |
16 | static struct { |
17 | u32 pid; |
18 | const char *comm; |
19 | } fake_threads[] = { |
20 | { FAKE_PID_PERF1, "perf" }, |
21 | { FAKE_PID_PERF2, "perf" }, |
22 | { FAKE_PID_BASH, "bash" }, |
23 | }; |
24 | |
25 | static struct { |
26 | u32 pid; |
27 | u64 start; |
28 | const char *filename; |
29 | } fake_mmap_info[] = { |
30 | { FAKE_PID_PERF1, FAKE_MAP_PERF, "perf" }, |
31 | { FAKE_PID_PERF1, FAKE_MAP_LIBC, "libc" }, |
32 | { FAKE_PID_PERF1, FAKE_MAP_KERNEL, "[kernel]" }, |
33 | { FAKE_PID_PERF2, FAKE_MAP_PERF, "perf" }, |
34 | { FAKE_PID_PERF2, FAKE_MAP_LIBC, "libc" }, |
35 | { FAKE_PID_PERF2, FAKE_MAP_KERNEL, "[kernel]" }, |
36 | { FAKE_PID_BASH, FAKE_MAP_BASH, "bash" }, |
37 | { FAKE_PID_BASH, FAKE_MAP_LIBC, "libc" }, |
38 | { FAKE_PID_BASH, FAKE_MAP_KERNEL, "[kernel]" }, |
39 | }; |
40 | |
41 | struct fake_sym { |
42 | u64 start; |
43 | u64 length; |
44 | const char *name; |
45 | }; |
46 | |
47 | static struct fake_sym perf_syms[] = { |
48 | { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, |
49 | { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "run_command" }, |
50 | { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "cmd_record" }, |
51 | }; |
52 | |
53 | static struct fake_sym bash_syms[] = { |
54 | { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "main" }, |
55 | { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "xmalloc" }, |
56 | { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "xfree" }, |
57 | }; |
58 | |
59 | static struct fake_sym libc_syms[] = { |
60 | { 700, 100, "malloc" }, |
61 | { 800, 100, "free" }, |
62 | { 900, 100, "realloc" }, |
63 | { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "malloc" }, |
64 | { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "free" }, |
65 | { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "realloc" }, |
66 | }; |
67 | |
68 | static struct fake_sym kernel_syms[] = { |
69 | { FAKE_SYM_OFFSET1, FAKE_SYM_LENGTH, "schedule" }, |
70 | { FAKE_SYM_OFFSET2, FAKE_SYM_LENGTH, "page_fault" }, |
71 | { FAKE_SYM_OFFSET3, FAKE_SYM_LENGTH, "sys_perf_event_open" }, |
72 | }; |
73 | |
74 | static struct { |
75 | const char *dso_name; |
76 | struct fake_sym *syms; |
77 | size_t nr_syms; |
78 | } fake_symbols[] = { |
79 | { "perf" , perf_syms, ARRAY_SIZE(perf_syms) }, |
80 | { "bash" , bash_syms, ARRAY_SIZE(bash_syms) }, |
81 | { "libc" , libc_syms, ARRAY_SIZE(libc_syms) }, |
82 | { "[kernel]" , kernel_syms, ARRAY_SIZE(kernel_syms) }, |
83 | }; |
84 | |
85 | struct machine *setup_fake_machine(struct machines *machines) |
86 | { |
87 | struct machine *machine = machines__find(machines, HOST_KERNEL_ID); |
88 | size_t i; |
89 | |
90 | if (machine == NULL) { |
91 | pr_debug("Not enough memory for machine setup\n" ); |
92 | return NULL; |
93 | } |
94 | |
95 | for (i = 0; i < ARRAY_SIZE(fake_threads); i++) { |
96 | struct thread *thread; |
97 | |
98 | thread = machine__findnew_thread(machine, fake_threads[i].pid, |
99 | fake_threads[i].pid); |
100 | if (thread == NULL) |
101 | goto out; |
102 | |
103 | thread__set_comm(thread, fake_threads[i].comm, 0); |
104 | thread__put(thread); |
105 | } |
106 | |
107 | for (i = 0; i < ARRAY_SIZE(fake_mmap_info); i++) { |
108 | struct perf_sample sample = { |
109 | .cpumode = PERF_RECORD_MISC_USER, |
110 | }; |
111 | union perf_event fake_mmap_event = { |
112 | .mmap = { |
113 | .pid = fake_mmap_info[i].pid, |
114 | .tid = fake_mmap_info[i].pid, |
115 | .start = fake_mmap_info[i].start, |
116 | .len = FAKE_MAP_LENGTH, |
117 | .pgoff = 0ULL, |
118 | }, |
119 | }; |
120 | |
121 | strcpy(fake_mmap_event.mmap.filename, |
122 | fake_mmap_info[i].filename); |
123 | |
124 | machine__process_mmap_event(machine, &fake_mmap_event, &sample); |
125 | } |
126 | |
127 | for (i = 0; i < ARRAY_SIZE(fake_symbols); i++) { |
128 | size_t k; |
129 | struct dso *dso; |
130 | |
131 | dso = machine__findnew_dso(machine, fake_symbols[i].dso_name); |
132 | if (dso == NULL) |
133 | goto out; |
134 | |
135 | /* emulate dso__load() */ |
136 | dso__set_loaded(dso); |
137 | |
138 | for (k = 0; k < fake_symbols[i].nr_syms; k++) { |
139 | struct symbol *sym; |
140 | struct fake_sym *fsym = &fake_symbols[i].syms[k]; |
141 | |
142 | sym = symbol__new(fsym->start, fsym->length, |
143 | STB_GLOBAL, STT_FUNC, fsym->name); |
144 | if (sym == NULL) { |
145 | dso__put(dso); |
146 | goto out; |
147 | } |
148 | |
149 | symbols__insert(&dso->symbols, sym); |
150 | } |
151 | |
152 | dso__put(dso); |
153 | } |
154 | |
155 | return machine; |
156 | |
157 | out: |
158 | pr_debug("Not enough memory for machine setup\n" ); |
159 | machine__delete_threads(machine); |
160 | return NULL; |
161 | } |
162 | |
163 | void print_hists_in(struct hists *hists) |
164 | { |
165 | int i = 0; |
166 | struct rb_root_cached *root; |
167 | struct rb_node *node; |
168 | |
169 | if (hists__has(hists, need_collapse)) |
170 | root = &hists->entries_collapsed; |
171 | else |
172 | root = hists->entries_in; |
173 | |
174 | pr_info("----- %s --------\n" , __func__); |
175 | node = rb_first_cached(root); |
176 | while (node) { |
177 | struct hist_entry *he; |
178 | |
179 | he = rb_entry(node, struct hist_entry, rb_node_in); |
180 | |
181 | if (!he->filtered) { |
182 | struct dso *dso = map__dso(he->ms.map); |
183 | |
184 | pr_info("%2d: entry: %-8s [%-8s] %20s: period = %" PRIu64"\n" , |
185 | i, thread__comm_str(he->thread), |
186 | dso->short_name, |
187 | he->ms.sym->name, he->stat.period); |
188 | } |
189 | |
190 | i++; |
191 | node = rb_next(node); |
192 | } |
193 | } |
194 | |
195 | void print_hists_out(struct hists *hists) |
196 | { |
197 | int i = 0; |
198 | struct rb_root_cached *root; |
199 | struct rb_node *node; |
200 | |
201 | root = &hists->entries; |
202 | |
203 | pr_info("----- %s --------\n" , __func__); |
204 | node = rb_first_cached(root); |
205 | while (node) { |
206 | struct hist_entry *he; |
207 | |
208 | he = rb_entry(node, struct hist_entry, rb_node); |
209 | |
210 | if (!he->filtered) { |
211 | struct dso *dso = map__dso(he->ms.map); |
212 | |
213 | pr_info("%2d: entry: %8s:%5d [%-8s] %20s: period = %" PRIu64"/%" PRIu64"\n" , |
214 | i, thread__comm_str(he->thread), thread__tid(he->thread), |
215 | dso->short_name, |
216 | he->ms.sym->name, he->stat.period, |
217 | he->stat_acc ? he->stat_acc->period : 0); |
218 | } |
219 | |
220 | i++; |
221 | node = rb_next(node); |
222 | } |
223 | } |
224 | |