Commit | Line | Data |
---|---|---|
36994e58 FW |
1 | /* |
2 | * Memory allocator tracing | |
3 | * | |
4 | * Copyright (C) 2008 Eduard - Gabriel Munteanu | |
5 | * Copyright (C) 2008 Pekka Enberg <penberg@cs.helsinki.fi> | |
6 | * Copyright (C) 2008 Frederic Weisbecker <fweisbec@gmail.com> | |
7 | */ | |
8 | ||
c826e3cd IM |
9 | #include <linux/tracepoint.h> |
10 | #include <linux/seq_file.h> | |
36994e58 | 11 | #include <linux/debugfs.h> |
c826e3cd | 12 | #include <linux/dcache.h> |
36994e58 | 13 | #include <linux/fs.h> |
c826e3cd | 14 | |
02af61bb | 15 | #include <linux/kmemtrace.h> |
36994e58 | 16 | |
36994e58 | 17 | #include "trace_output.h" |
c826e3cd | 18 | #include "trace.h" |
36994e58 FW |
19 | |
20 | /* Select an alternative, minimalistic output than the original one */ | |
21 | #define TRACE_KMEM_OPT_MINIMAL 0x1 | |
22 | ||
23 | static struct tracer_opt kmem_opts[] = { | |
24 | /* Default disable the minimalistic output */ | |
25 | { TRACER_OPT(kmem_minimalistic, TRACE_KMEM_OPT_MINIMAL) }, | |
26 | { } | |
27 | }; | |
28 | ||
29 | static struct tracer_flags kmem_tracer_flags = { | |
c826e3cd IM |
30 | .val = 0, |
31 | .opts = kmem_opts | |
36994e58 FW |
32 | }; |
33 | ||
36994e58 FW |
34 | static struct trace_array *kmemtrace_array; |
35 | ||
ca2b84cb EGM |
36 | /* Trace allocations */ |
37 | static inline void kmemtrace_alloc(enum kmemtrace_type_id type_id, | |
38 | unsigned long call_site, | |
39 | const void *ptr, | |
40 | size_t bytes_req, | |
41 | size_t bytes_alloc, | |
42 | gfp_t gfp_flags, | |
43 | int node) | |
44 | { | |
ca2b84cb | 45 | struct trace_array *tr = kmemtrace_array; |
c826e3cd IM |
46 | struct kmemtrace_alloc_entry *entry; |
47 | struct ring_buffer_event *event; | |
ca2b84cb EGM |
48 | |
49 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry)); | |
50 | if (!event) | |
51 | return; | |
c826e3cd IM |
52 | |
53 | entry = ring_buffer_event_data(event); | |
ca2b84cb EGM |
54 | tracing_generic_entry_update(&entry->ent, 0, 0); |
55 | ||
c826e3cd IM |
56 | entry->ent.type = TRACE_KMEM_ALLOC; |
57 | entry->type_id = type_id; | |
58 | entry->call_site = call_site; | |
59 | entry->ptr = ptr; | |
60 | entry->bytes_req = bytes_req; | |
61 | entry->bytes_alloc = bytes_alloc; | |
62 | entry->gfp_flags = gfp_flags; | |
63 | entry->node = node; | |
ca2b84cb EGM |
64 | |
65 | ring_buffer_unlock_commit(tr->buffer, event); | |
66 | ||
67 | trace_wake_up(); | |
68 | } | |
69 | ||
70 | static inline void kmemtrace_free(enum kmemtrace_type_id type_id, | |
71 | unsigned long call_site, | |
72 | const void *ptr) | |
73 | { | |
ca2b84cb | 74 | struct trace_array *tr = kmemtrace_array; |
c826e3cd IM |
75 | struct kmemtrace_free_entry *entry; |
76 | struct ring_buffer_event *event; | |
ca2b84cb EGM |
77 | |
78 | event = ring_buffer_lock_reserve(tr->buffer, sizeof(*entry)); | |
79 | if (!event) | |
80 | return; | |
81 | entry = ring_buffer_event_data(event); | |
82 | tracing_generic_entry_update(&entry->ent, 0, 0); | |
83 | ||
c826e3cd IM |
84 | entry->ent.type = TRACE_KMEM_FREE; |
85 | entry->type_id = type_id; | |
86 | entry->call_site = call_site; | |
87 | entry->ptr = ptr; | |
ca2b84cb EGM |
88 | |
89 | ring_buffer_unlock_commit(tr->buffer, event); | |
90 | ||
91 | trace_wake_up(); | |
92 | } | |
93 | ||
94 | static void kmemtrace_kmalloc(unsigned long call_site, | |
95 | const void *ptr, | |
96 | size_t bytes_req, | |
97 | size_t bytes_alloc, | |
98 | gfp_t gfp_flags) | |
99 | { | |
100 | kmemtrace_alloc(KMEMTRACE_TYPE_KMALLOC, call_site, ptr, | |
101 | bytes_req, bytes_alloc, gfp_flags, -1); | |
102 | } | |
103 | ||
104 | static void kmemtrace_kmem_cache_alloc(unsigned long call_site, | |
105 | const void *ptr, | |
106 | size_t bytes_req, | |
107 | size_t bytes_alloc, | |
108 | gfp_t gfp_flags) | |
109 | { | |
110 | kmemtrace_alloc(KMEMTRACE_TYPE_CACHE, call_site, ptr, | |
111 | bytes_req, bytes_alloc, gfp_flags, -1); | |
112 | } | |
113 | ||
114 | static void kmemtrace_kmalloc_node(unsigned long call_site, | |
115 | const void *ptr, | |
116 | size_t bytes_req, | |
117 | size_t bytes_alloc, | |
118 | gfp_t gfp_flags, | |
119 | int node) | |
120 | { | |
121 | kmemtrace_alloc(KMEMTRACE_TYPE_KMALLOC, call_site, ptr, | |
122 | bytes_req, bytes_alloc, gfp_flags, node); | |
123 | } | |
124 | ||
125 | static void kmemtrace_kmem_cache_alloc_node(unsigned long call_site, | |
126 | const void *ptr, | |
127 | size_t bytes_req, | |
128 | size_t bytes_alloc, | |
129 | gfp_t gfp_flags, | |
130 | int node) | |
131 | { | |
132 | kmemtrace_alloc(KMEMTRACE_TYPE_CACHE, call_site, ptr, | |
133 | bytes_req, bytes_alloc, gfp_flags, node); | |
134 | } | |
135 | ||
136 | static void kmemtrace_kfree(unsigned long call_site, const void *ptr) | |
137 | { | |
138 | kmemtrace_free(KMEMTRACE_TYPE_KMALLOC, call_site, ptr); | |
139 | } | |
140 | ||
141 | static void kmemtrace_kmem_cache_free(unsigned long call_site, const void *ptr) | |
142 | { | |
143 | kmemtrace_free(KMEMTRACE_TYPE_CACHE, call_site, ptr); | |
144 | } | |
145 | ||
146 | static int kmemtrace_start_probes(void) | |
147 | { | |
148 | int err; | |
149 | ||
150 | err = register_trace_kmalloc(kmemtrace_kmalloc); | |
151 | if (err) | |
152 | return err; | |
153 | err = register_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc); | |
154 | if (err) | |
155 | return err; | |
156 | err = register_trace_kmalloc_node(kmemtrace_kmalloc_node); | |
157 | if (err) | |
158 | return err; | |
159 | err = register_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node); | |
160 | if (err) | |
161 | return err; | |
162 | err = register_trace_kfree(kmemtrace_kfree); | |
163 | if (err) | |
164 | return err; | |
165 | err = register_trace_kmem_cache_free(kmemtrace_kmem_cache_free); | |
166 | ||
167 | return err; | |
168 | } | |
169 | ||
170 | static void kmemtrace_stop_probes(void) | |
171 | { | |
172 | unregister_trace_kmalloc(kmemtrace_kmalloc); | |
173 | unregister_trace_kmem_cache_alloc(kmemtrace_kmem_cache_alloc); | |
174 | unregister_trace_kmalloc_node(kmemtrace_kmalloc_node); | |
175 | unregister_trace_kmem_cache_alloc_node(kmemtrace_kmem_cache_alloc_node); | |
176 | unregister_trace_kfree(kmemtrace_kfree); | |
177 | unregister_trace_kmem_cache_free(kmemtrace_kmem_cache_free); | |
178 | } | |
179 | ||
36994e58 FW |
180 | static int kmem_trace_init(struct trace_array *tr) |
181 | { | |
182 | int cpu; | |
183 | kmemtrace_array = tr; | |
184 | ||
185 | for_each_cpu_mask(cpu, cpu_possible_map) | |
186 | tracing_reset(tr, cpu); | |
187 | ||
ca2b84cb | 188 | kmemtrace_start_probes(); |
36994e58 FW |
189 | |
190 | return 0; | |
191 | } | |
192 | ||
193 | static void kmem_trace_reset(struct trace_array *tr) | |
194 | { | |
ca2b84cb | 195 | kmemtrace_stop_probes(); |
36994e58 FW |
196 | } |
197 | ||
198 | static void kmemtrace_headers(struct seq_file *s) | |
199 | { | |
200 | /* Don't need headers for the original kmemtrace output */ | |
201 | if (!(kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL)) | |
202 | return; | |
203 | ||
204 | seq_printf(s, "#\n"); | |
205 | seq_printf(s, "# ALLOC TYPE REQ GIVEN FLAGS " | |
206 | " POINTER NODE CALLER\n"); | |
207 | seq_printf(s, "# FREE | | | | " | |
208 | " | | | |\n"); | |
209 | seq_printf(s, "# |\n\n"); | |
210 | } | |
211 | ||
212 | /* | |
42af9054 EGM |
213 | * The following functions give the original output from kmemtrace, |
214 | * plus the origin CPU, since reordering occurs in-kernel now. | |
36994e58 | 215 | */ |
42af9054 EGM |
216 | |
217 | #define KMEMTRACE_USER_ALLOC 0 | |
218 | #define KMEMTRACE_USER_FREE 1 | |
219 | ||
220 | struct kmemtrace_user_event { | |
c826e3cd IM |
221 | u8 event_id; |
222 | u8 type_id; | |
223 | u16 event_size; | |
224 | u32 cpu; | |
225 | u64 timestamp; | |
226 | unsigned long call_site; | |
227 | unsigned long ptr; | |
42af9054 EGM |
228 | }; |
229 | ||
230 | struct kmemtrace_user_event_alloc { | |
c826e3cd IM |
231 | size_t bytes_req; |
232 | size_t bytes_alloc; | |
233 | unsigned gfp_flags; | |
234 | int node; | |
42af9054 EGM |
235 | }; |
236 | ||
36994e58 | 237 | static enum print_line_t |
42af9054 EGM |
238 | kmemtrace_print_alloc_user(struct trace_iterator *iter, |
239 | struct kmemtrace_alloc_entry *entry) | |
36994e58 | 240 | { |
c826e3cd | 241 | struct kmemtrace_user_event_alloc *ev_alloc; |
36994e58 | 242 | struct trace_seq *s = &iter->seq; |
42af9054 | 243 | struct kmemtrace_user_event *ev; |
36994e58 | 244 | |
42af9054 EGM |
245 | ev = trace_seq_reserve(s, sizeof(*ev)); |
246 | if (!ev) | |
247 | return TRACE_TYPE_PARTIAL_LINE; | |
c826e3cd IM |
248 | |
249 | ev->event_id = KMEMTRACE_USER_ALLOC; | |
250 | ev->type_id = entry->type_id; | |
251 | ev->event_size = sizeof(*ev) + sizeof(*ev_alloc); | |
252 | ev->cpu = iter->cpu; | |
253 | ev->timestamp = iter->ts; | |
254 | ev->call_site = entry->call_site; | |
255 | ev->ptr = (unsigned long)entry->ptr; | |
42af9054 EGM |
256 | |
257 | ev_alloc = trace_seq_reserve(s, sizeof(*ev_alloc)); | |
258 | if (!ev_alloc) | |
36994e58 | 259 | return TRACE_TYPE_PARTIAL_LINE; |
c826e3cd IM |
260 | |
261 | ev_alloc->bytes_req = entry->bytes_req; | |
262 | ev_alloc->bytes_alloc = entry->bytes_alloc; | |
263 | ev_alloc->gfp_flags = entry->gfp_flags; | |
264 | ev_alloc->node = entry->node; | |
36994e58 FW |
265 | |
266 | return TRACE_TYPE_HANDLED; | |
267 | } | |
268 | ||
269 | static enum print_line_t | |
42af9054 EGM |
270 | kmemtrace_print_free_user(struct trace_iterator *iter, |
271 | struct kmemtrace_free_entry *entry) | |
36994e58 FW |
272 | { |
273 | struct trace_seq *s = &iter->seq; | |
42af9054 | 274 | struct kmemtrace_user_event *ev; |
36994e58 | 275 | |
42af9054 EGM |
276 | ev = trace_seq_reserve(s, sizeof(*ev)); |
277 | if (!ev) | |
36994e58 | 278 | return TRACE_TYPE_PARTIAL_LINE; |
c826e3cd IM |
279 | |
280 | ev->event_id = KMEMTRACE_USER_FREE; | |
281 | ev->type_id = entry->type_id; | |
282 | ev->event_size = sizeof(*ev); | |
283 | ev->cpu = iter->cpu; | |
284 | ev->timestamp = iter->ts; | |
285 | ev->call_site = entry->call_site; | |
286 | ev->ptr = (unsigned long)entry->ptr; | |
36994e58 FW |
287 | |
288 | return TRACE_TYPE_HANDLED; | |
289 | } | |
290 | ||
36994e58 FW |
291 | /* The two other following provide a more minimalistic output */ |
292 | static enum print_line_t | |
293 | kmemtrace_print_alloc_compress(struct trace_iterator *iter, | |
294 | struct kmemtrace_alloc_entry *entry) | |
295 | { | |
296 | struct trace_seq *s = &iter->seq; | |
297 | int ret; | |
298 | ||
299 | /* Alloc entry */ | |
300 | ret = trace_seq_printf(s, " + "); | |
301 | if (!ret) | |
302 | return TRACE_TYPE_PARTIAL_LINE; | |
303 | ||
304 | /* Type */ | |
305 | switch (entry->type_id) { | |
306 | case KMEMTRACE_TYPE_KMALLOC: | |
307 | ret = trace_seq_printf(s, "K "); | |
308 | break; | |
309 | case KMEMTRACE_TYPE_CACHE: | |
310 | ret = trace_seq_printf(s, "C "); | |
311 | break; | |
312 | case KMEMTRACE_TYPE_PAGES: | |
313 | ret = trace_seq_printf(s, "P "); | |
314 | break; | |
315 | default: | |
316 | ret = trace_seq_printf(s, "? "); | |
317 | } | |
318 | ||
319 | if (!ret) | |
320 | return TRACE_TYPE_PARTIAL_LINE; | |
321 | ||
322 | /* Requested */ | |
ecf441b5 | 323 | ret = trace_seq_printf(s, "%4zu ", entry->bytes_req); |
36994e58 FW |
324 | if (!ret) |
325 | return TRACE_TYPE_PARTIAL_LINE; | |
326 | ||
327 | /* Allocated */ | |
ecf441b5 | 328 | ret = trace_seq_printf(s, "%4zu ", entry->bytes_alloc); |
36994e58 FW |
329 | if (!ret) |
330 | return TRACE_TYPE_PARTIAL_LINE; | |
331 | ||
332 | /* Flags | |
333 | * TODO: would be better to see the name of the GFP flag names | |
334 | */ | |
335 | ret = trace_seq_printf(s, "%08x ", entry->gfp_flags); | |
336 | if (!ret) | |
337 | return TRACE_TYPE_PARTIAL_LINE; | |
338 | ||
339 | /* Pointer to allocated */ | |
340 | ret = trace_seq_printf(s, "0x%tx ", (ptrdiff_t)entry->ptr); | |
341 | if (!ret) | |
342 | return TRACE_TYPE_PARTIAL_LINE; | |
343 | ||
344 | /* Node */ | |
345 | ret = trace_seq_printf(s, "%4d ", entry->node); | |
346 | if (!ret) | |
347 | return TRACE_TYPE_PARTIAL_LINE; | |
348 | ||
349 | /* Call site */ | |
350 | ret = seq_print_ip_sym(s, entry->call_site, 0); | |
351 | if (!ret) | |
352 | return TRACE_TYPE_PARTIAL_LINE; | |
353 | ||
354 | if (!trace_seq_printf(s, "\n")) | |
355 | return TRACE_TYPE_PARTIAL_LINE; | |
356 | ||
357 | return TRACE_TYPE_HANDLED; | |
358 | } | |
359 | ||
360 | static enum print_line_t | |
361 | kmemtrace_print_free_compress(struct trace_iterator *iter, | |
c826e3cd | 362 | struct kmemtrace_free_entry *entry) |
36994e58 FW |
363 | { |
364 | struct trace_seq *s = &iter->seq; | |
365 | int ret; | |
366 | ||
367 | /* Free entry */ | |
368 | ret = trace_seq_printf(s, " - "); | |
369 | if (!ret) | |
370 | return TRACE_TYPE_PARTIAL_LINE; | |
371 | ||
372 | /* Type */ | |
373 | switch (entry->type_id) { | |
374 | case KMEMTRACE_TYPE_KMALLOC: | |
375 | ret = trace_seq_printf(s, "K "); | |
376 | break; | |
377 | case KMEMTRACE_TYPE_CACHE: | |
378 | ret = trace_seq_printf(s, "C "); | |
379 | break; | |
380 | case KMEMTRACE_TYPE_PAGES: | |
381 | ret = trace_seq_printf(s, "P "); | |
382 | break; | |
383 | default: | |
384 | ret = trace_seq_printf(s, "? "); | |
385 | } | |
386 | ||
387 | if (!ret) | |
388 | return TRACE_TYPE_PARTIAL_LINE; | |
389 | ||
390 | /* Skip requested/allocated/flags */ | |
391 | ret = trace_seq_printf(s, " "); | |
392 | if (!ret) | |
393 | return TRACE_TYPE_PARTIAL_LINE; | |
394 | ||
395 | /* Pointer to allocated */ | |
396 | ret = trace_seq_printf(s, "0x%tx ", (ptrdiff_t)entry->ptr); | |
397 | if (!ret) | |
398 | return TRACE_TYPE_PARTIAL_LINE; | |
399 | ||
400 | /* Skip node */ | |
401 | ret = trace_seq_printf(s, " "); | |
402 | if (!ret) | |
403 | return TRACE_TYPE_PARTIAL_LINE; | |
404 | ||
405 | /* Call site */ | |
406 | ret = seq_print_ip_sym(s, entry->call_site, 0); | |
407 | if (!ret) | |
408 | return TRACE_TYPE_PARTIAL_LINE; | |
409 | ||
410 | if (!trace_seq_printf(s, "\n")) | |
411 | return TRACE_TYPE_PARTIAL_LINE; | |
412 | ||
413 | return TRACE_TYPE_HANDLED; | |
414 | } | |
415 | ||
416 | static enum print_line_t kmemtrace_print_line(struct trace_iterator *iter) | |
417 | { | |
418 | struct trace_entry *entry = iter->ent; | |
419 | ||
420 | switch (entry->type) { | |
421 | case TRACE_KMEM_ALLOC: { | |
422 | struct kmemtrace_alloc_entry *field; | |
c826e3cd | 423 | |
36994e58 FW |
424 | trace_assign_type(field, entry); |
425 | if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL) | |
426 | return kmemtrace_print_alloc_compress(iter, field); | |
427 | else | |
42af9054 | 428 | return kmemtrace_print_alloc_user(iter, field); |
36994e58 FW |
429 | } |
430 | ||
431 | case TRACE_KMEM_FREE: { | |
432 | struct kmemtrace_free_entry *field; | |
c826e3cd | 433 | |
36994e58 FW |
434 | trace_assign_type(field, entry); |
435 | if (kmem_tracer_flags.val & TRACE_KMEM_OPT_MINIMAL) | |
436 | return kmemtrace_print_free_compress(iter, field); | |
437 | else | |
42af9054 | 438 | return kmemtrace_print_free_user(iter, field); |
36994e58 FW |
439 | } |
440 | ||
441 | default: | |
442 | return TRACE_TYPE_UNHANDLED; | |
443 | } | |
444 | } | |
445 | ||
36994e58 | 446 | static struct tracer kmem_tracer __read_mostly = { |
c826e3cd IM |
447 | .name = "kmemtrace", |
448 | .init = kmem_trace_init, | |
449 | .reset = kmem_trace_reset, | |
450 | .print_line = kmemtrace_print_line, | |
451 | .print_header = kmemtrace_headers, | |
452 | .flags = &kmem_tracer_flags | |
36994e58 FW |
453 | }; |
454 | ||
3e806802 IM |
455 | void kmemtrace_init(void) |
456 | { | |
457 | /* earliest opportunity to start kmem tracing */ | |
458 | } | |
459 | ||
36994e58 FW |
460 | static int __init init_kmem_tracer(void) |
461 | { | |
462 | return register_tracer(&kmem_tracer); | |
463 | } | |
36994e58 | 464 | device_initcall(init_kmem_tracer); |