blob: 4c6754ac6b2028a42479ac8aef904d49ccc91497 [file] [log] [blame]
Arnaldo Carvalho de Melob0a7d1a2012-10-06 16:26:02 -03001#include "debug.h"
2#include "event.h"
Arnaldo Carvalho de Melo9d2f8e22012-10-06 15:43:20 -03003#include "machine.h"
4#include "map.h"
5#include "thread.h"
6#include <stdbool.h>
7
8static struct thread *__machine__findnew_thread(struct machine *machine, pid_t pid,
9 bool create)
10{
11 struct rb_node **p = &machine->threads.rb_node;
12 struct rb_node *parent = NULL;
13 struct thread *th;
14
15 /*
16 * Font-end cache - PID lookups come in blocks,
17 * so most of the time we dont have to look up
18 * the full rbtree:
19 */
20 if (machine->last_match && machine->last_match->pid == pid)
21 return machine->last_match;
22
23 while (*p != NULL) {
24 parent = *p;
25 th = rb_entry(parent, struct thread, rb_node);
26
27 if (th->pid == pid) {
28 machine->last_match = th;
29 return th;
30 }
31
32 if (pid < th->pid)
33 p = &(*p)->rb_left;
34 else
35 p = &(*p)->rb_right;
36 }
37
38 if (!create)
39 return NULL;
40
41 th = thread__new(pid);
42 if (th != NULL) {
43 rb_link_node(&th->rb_node, parent, p);
44 rb_insert_color(&th->rb_node, &machine->threads);
45 machine->last_match = th;
46 }
47
48 return th;
49}
50
51struct thread *machine__findnew_thread(struct machine *machine, pid_t pid)
52{
53 return __machine__findnew_thread(machine, pid, true);
54}
55
56struct thread *machine__find_thread(struct machine *machine, pid_t pid)
57{
58 return __machine__findnew_thread(machine, pid, false);
59}
Arnaldo Carvalho de Melob0a7d1a2012-10-06 16:26:02 -030060
61int machine__process_comm_event(struct machine *machine, union perf_event *event)
62{
63 struct thread *thread = machine__findnew_thread(machine, event->comm.tid);
64
65 if (dump_trace)
66 perf_event__fprintf_comm(event, stdout);
67
68 if (thread == NULL || thread__set_comm(thread, event->comm.comm)) {
69 dump_printf("problem processing PERF_RECORD_COMM, skipping event.\n");
70 return -1;
71 }
72
73 return 0;
74}
75
76int machine__process_lost_event(struct machine *machine __maybe_unused,
77 union perf_event *event)
78{
79 dump_printf(": id:%" PRIu64 ": lost:%" PRIu64 "\n",
80 event->lost.id, event->lost.lost);
81 return 0;
82}
83
84static void machine__set_kernel_mmap_len(struct machine *machine,
85 union perf_event *event)
86{
Namhyung Kim4552cf0f2012-11-07 16:27:10 +090087 int i;
88
89 for (i = 0; i < MAP__NR_TYPES; i++) {
90 machine->vmlinux_maps[i]->start = event->mmap.start;
91 machine->vmlinux_maps[i]->end = (event->mmap.start +
92 event->mmap.len);
93 /*
94 * Be a bit paranoid here, some perf.data file came with
95 * a zero sized synthesized MMAP event for the kernel.
96 */
97 if (machine->vmlinux_maps[i]->end == 0)
98 machine->vmlinux_maps[i]->end = ~0ULL;
99 }
Arnaldo Carvalho de Melob0a7d1a2012-10-06 16:26:02 -0300100}
101
102static int machine__process_kernel_mmap_event(struct machine *machine,
103 union perf_event *event)
104{
105 struct map *map;
106 char kmmap_prefix[PATH_MAX];
107 enum dso_kernel_type kernel_type;
108 bool is_kernel_mmap;
109
110 machine__mmap_name(machine, kmmap_prefix, sizeof(kmmap_prefix));
111 if (machine__is_host(machine))
112 kernel_type = DSO_TYPE_KERNEL;
113 else
114 kernel_type = DSO_TYPE_GUEST_KERNEL;
115
116 is_kernel_mmap = memcmp(event->mmap.filename,
117 kmmap_prefix,
118 strlen(kmmap_prefix) - 1) == 0;
119 if (event->mmap.filename[0] == '/' ||
120 (!is_kernel_mmap && event->mmap.filename[0] == '[')) {
121
122 char short_module_name[1024];
123 char *name, *dot;
124
125 if (event->mmap.filename[0] == '/') {
126 name = strrchr(event->mmap.filename, '/');
127 if (name == NULL)
128 goto out_problem;
129
130 ++name; /* skip / */
131 dot = strrchr(name, '.');
132 if (dot == NULL)
133 goto out_problem;
134 snprintf(short_module_name, sizeof(short_module_name),
135 "[%.*s]", (int)(dot - name), name);
136 strxfrchar(short_module_name, '-', '_');
137 } else
138 strcpy(short_module_name, event->mmap.filename);
139
140 map = machine__new_module(machine, event->mmap.start,
141 event->mmap.filename);
142 if (map == NULL)
143 goto out_problem;
144
145 name = strdup(short_module_name);
146 if (name == NULL)
147 goto out_problem;
148
149 map->dso->short_name = name;
150 map->dso->sname_alloc = 1;
151 map->end = map->start + event->mmap.len;
152 } else if (is_kernel_mmap) {
153 const char *symbol_name = (event->mmap.filename +
154 strlen(kmmap_prefix));
155 /*
156 * Should be there already, from the build-id table in
157 * the header.
158 */
159 struct dso *kernel = __dsos__findnew(&machine->kernel_dsos,
160 kmmap_prefix);
161 if (kernel == NULL)
162 goto out_problem;
163
164 kernel->kernel = kernel_type;
165 if (__machine__create_kernel_maps(machine, kernel) < 0)
166 goto out_problem;
167
168 machine__set_kernel_mmap_len(machine, event);
169
170 /*
171 * Avoid using a zero address (kptr_restrict) for the ref reloc
172 * symbol. Effectively having zero here means that at record
173 * time /proc/sys/kernel/kptr_restrict was non zero.
174 */
175 if (event->mmap.pgoff != 0) {
176 maps__set_kallsyms_ref_reloc_sym(machine->vmlinux_maps,
177 symbol_name,
178 event->mmap.pgoff);
179 }
180
181 if (machine__is_default_guest(machine)) {
182 /*
183 * preload dso of guest kernel and modules
184 */
185 dso__load(kernel, machine->vmlinux_maps[MAP__FUNCTION],
186 NULL);
187 }
188 }
189 return 0;
190out_problem:
191 return -1;
192}
193
194int machine__process_mmap_event(struct machine *machine, union perf_event *event)
195{
196 u8 cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;
197 struct thread *thread;
198 struct map *map;
199 int ret = 0;
200
201 if (dump_trace)
202 perf_event__fprintf_mmap(event, stdout);
203
204 if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL ||
205 cpumode == PERF_RECORD_MISC_KERNEL) {
206 ret = machine__process_kernel_mmap_event(machine, event);
207 if (ret < 0)
208 goto out_problem;
209 return 0;
210 }
211
212 thread = machine__findnew_thread(machine, event->mmap.pid);
213 if (thread == NULL)
214 goto out_problem;
215 map = map__new(&machine->user_dsos, event->mmap.start,
216 event->mmap.len, event->mmap.pgoff,
217 event->mmap.pid, event->mmap.filename,
218 MAP__FUNCTION);
219 if (map == NULL)
220 goto out_problem;
221
222 thread__insert_map(thread, map);
223 return 0;
224
225out_problem:
226 dump_printf("problem processing PERF_RECORD_MMAP, skipping event.\n");
227 return 0;
228}
229
230int machine__process_fork_event(struct machine *machine, union perf_event *event)
231{
232 struct thread *thread = machine__findnew_thread(machine, event->fork.tid);
233 struct thread *parent = machine__findnew_thread(machine, event->fork.ptid);
234
235 if (dump_trace)
236 perf_event__fprintf_task(event, stdout);
237
238 if (thread == NULL || parent == NULL ||
239 thread__fork(thread, parent) < 0) {
240 dump_printf("problem processing PERF_RECORD_FORK, skipping event.\n");
241 return -1;
242 }
243
244 return 0;
245}
246
247int machine__process_exit_event(struct machine *machine, union perf_event *event)
248{
249 struct thread *thread = machine__find_thread(machine, event->fork.tid);
250
251 if (dump_trace)
252 perf_event__fprintf_task(event, stdout);
253
254 if (thread != NULL)
255 machine__remove_thread(machine, thread);
256
257 return 0;
258}
259
260int machine__process_event(struct machine *machine, union perf_event *event)
261{
262 int ret;
263
264 switch (event->header.type) {
265 case PERF_RECORD_COMM:
266 ret = machine__process_comm_event(machine, event); break;
267 case PERF_RECORD_MMAP:
268 ret = machine__process_mmap_event(machine, event); break;
269 case PERF_RECORD_FORK:
270 ret = machine__process_fork_event(machine, event); break;
271 case PERF_RECORD_EXIT:
272 ret = machine__process_exit_event(machine, event); break;
273 case PERF_RECORD_LOST:
274 ret = machine__process_lost_event(machine, event); break;
275 default:
276 ret = -1;
277 break;
278 }
279
280 return ret;
281}