lttng-modules v0.19-stable: setup_trace_write: Fix recursive locking
[lttng-modules.git] / ltt-ascii.c
CommitLineData
1c8284eb
MD
1/*
2 * LTT ascii binary buffer to ascii converter.
3 *
4 * Copyright 2008 - 2009 Lai Jiangshan (laijs@cn.fujitsu.com)
5 * Copyright 2009 - Mathieu Desnoyers mathieu.desnoyers@polymtl.ca
6 *
7 * Dual LGPL v2.1/GPL v2 license.
8 */
9
10/*
11 * TODO
12 *
13 * Move to new switch behavior: Wait for data for the duration of the
14 * timer interval + safety, if none is coming, consider that no activity occured
15 * in the buffer.
16 *
17 * Fix case when having a text file open and destroying trace.
18 *
19 * - Automate periodical switch:
20 *
21 * The debugfs file "switch_timer" receives a timer period as parameter
22 * (e.g. echo 100 > switch_timer) to activate the timer per channel. This can
23 * also be accessed through the internal API _before the trace session starts_.
24 * This timer will insure that we periodically have subbuffers to read, and
25 * therefore that the merge-sort does not wait endlessly for a subbuffer.
26 *
27 * - If a channel is switched and read without data, make sure it is still
28 * considered afterward (not removed from the queue).
29 *
30 * - Create a ascii/tracename/ALL file to merge-sort all active channels.
31 * - Create a ascii/tracename/README file to contain the text output legend.
32 * - Remove leading zeroes from timestamps.
33 * - Enhance pretty-printing to make sure all types used for addesses output in
34 * the form 0xAB00000000 (not decimal). This is true for %p and 0x%...X.
35 * - Hotplug support
36 */
37
38
39
40
41#include <linux/module.h>
42#include <linux/seq_file.h>
43#include <linux/debugfs.h>
44#include <linux/module.h>
45#include <linux/string.h>
46#include <linux/delay.h>
47#include <linux/slab.h>
48#include <linux/cpu.h>
49#include <linux/fs.h>
50
51#include "ltt-tracer.h"
52#include "ltt-relay.h"
53#include "ltt-relay-lockless.h"
54
55#if 0
56#define DEBUGP printk
57#else
58#define DEBUGP(fmt , a...)
59#endif
60
61struct dentry *ltt_ascii_dir_dentry;
62EXPORT_SYMBOL_GPL(ltt_ascii_dir_dentry);
63
64struct ltt_relay_iter;
65
66struct ltt_relay_cpu_iter {
67 /* cpu buffer information */
68 struct ltt_chanbuf *buf;
69 struct ltt_relay_iter *iter;
70 int sb_ref; /* holding a reference to a subbuffer */
71 long read_sb_offset; /* offset of the subbuffer read */
72
73 /* current event information */
74 struct ltt_subbuffer_header *header;
75 long hdr_offset; /* event header offset */
76 long payload_offset; /* event payload offset */
77 u64 tsc; /* full 64-bits timestamp value */
78 u32 data_size;
79 u16 chID; /* channel ID, const */
80 u16 eID;
81};
82
83struct ltt_relay_iter {
84 struct ltt_relay_cpu_iter iter_cpu[NR_CPUS];
85 struct ltt_chan *chan;
86 loff_t pos;
87 int cpu;
88 int nr_refs;
89};
90
91/*
92 * offset of 0 in subbuffer means "subbuf size" (filled subbuffer).
93 */
94static int is_subbuffer_offset_end(struct ltt_relay_cpu_iter *citer,
95 long offset)
96{
97 struct ltt_chan *chan = container_of(citer->buf->a.chan,
98 struct ltt_chan, a);
99 long sub_offset = SUBBUF_OFFSET(offset - 1, chan) + 1;
100
101 return (sub_offset <= citer->header->data_size);
102}
103
104static u64 calculate_tsc(u64 pre_tsc, u64 read_tsc, unsigned int rflags)
105{
106 u64 new_tsc = read_tsc;
107
108 if (rflags != LTT_RFLAG_ID_SIZE_TSC) {
109 BUG_ON(read_tsc >> LTT_TSC_BITS);
110
111 new_tsc = (pre_tsc & ~LTT_TSC_MASK) + read_tsc;
112 if (read_tsc < (pre_tsc & LTT_TSC_MASK))
113 new_tsc += 1UL << LTT_TSC_BITS;
114 }
115
116 return new_tsc;
117}
118
119/*
120 * calculate payload offset */
121static inline long calculate_payload_offset(long offset, u16 chID, u16 eID)
122{
123 const char *fmt;
124
125 if (!ltt_get_alignment())
126 return offset;
127
128 fmt = marker_get_fmt_from_id(chID, eID);
129 BUG_ON(!fmt);
130
131 return offset + ltt_fmt_largest_align(offset, fmt);
132}
133
134static void update_new_event(struct ltt_relay_cpu_iter *citer, long hdr_offset)
135{
136 u64 read_tsc;
137 unsigned int rflags;
138 long tmp_offset;
139
140 WARN_ON_ONCE(hdr_offset != citer->hdr_offset);
141
142 tmp_offset = ltt_read_event_header(&citer->buf->a, hdr_offset,
143 &read_tsc, &citer->data_size,
144 &citer->eID, &rflags);
145 citer->payload_offset = calculate_payload_offset(tmp_offset,
146 citer->chID,
147 citer->eID);
148
149 citer->tsc = calculate_tsc(citer->tsc, read_tsc, rflags);
150}
151
152static void update_event_size(struct ltt_relay_cpu_iter *citer, long hdr_offset)
153{
154 char output[1];
155 const char *fmt;
156 size_t data_size;
157
158 if (citer->data_size != INT_MAX)
159 return;
160
161 fmt = marker_get_fmt_from_id(citer->chID, citer->eID);
162 BUG_ON(!fmt);
163 ltt_serialize_printf(citer->buf, citer->payload_offset,
164 &data_size, output, 0, fmt);
165 citer->data_size = data_size;
166}
167
168static void update_cpu_iter(struct ltt_relay_cpu_iter *citer, long hdr_offset)
169{
170 if (unlikely((!citer->sb_ref)
171 || is_subbuffer_offset_end(citer, hdr_offset))) {
172 citer->header = NULL;
173 return;
174 }
175 update_new_event(citer, hdr_offset);
176 update_event_size(citer, hdr_offset);
177}
178
179/*
180 * returns 0 if we get a subbuffer reference.
181 * else, the buffer has not available data, try again later.
182 */
183static int subbuffer_start(struct ltt_relay_cpu_iter *citer, long *offset)
184{
185 int ret;
186 struct ltt_relay_iter *iter = citer->iter;
187
188 ret = ltt_chanbuf_get_subbuf(citer->buf, offset);
189 if (!ret) {
190 citer->header = ltt_relay_read_offset_address(&citer->buf->a,
191 *offset);
192 citer->hdr_offset = (*offset) + ltt_sb_header_size();
193 citer->tsc = citer->header->cycle_count_begin;
194 iter->nr_refs++;
195 citer->sb_ref = 1;
196 return 0;
197 } else {
198 if (ltt_chanbuf_is_finalized(citer->buf))
199 return -ENODATA;
200 else
201 return -EAGAIN;
202 }
203}
204
205static void subbuffer_stop(struct ltt_relay_cpu_iter *citer,
206 long offset)
207{
208 int ret;
209 struct ltt_relay_iter *iter = citer->iter;
210
211 WARN_ON_ONCE(!citer->sb_ref);
212 ret = ltt_chanbuf_put_subbuf(citer->buf, offset);
213 WARN_ON_ONCE(ret);
214 citer->sb_ref = 0;
215 iter->nr_refs--;
216}
217
218static void ltt_relay_advance_cpu_iter(struct ltt_relay_cpu_iter *citer)
219{
220 long old_offset = citer->payload_offset;
221 long new_offset = citer->payload_offset;
222 int ret;
223
224 /* find that whether we read all data in this subbuffer */
225 if (unlikely(is_subbuffer_offset_end(citer,
226 old_offset + citer->data_size))) {
227 DEBUGP(KERN_DEBUG "LTT ASCII stop cpu %d offset %lX\n",
228 citer->buf->a.cpu, citer->read_sb_offset);
229 subbuffer_stop(citer, citer->read_sb_offset);
230 for (;;) {
231 ret = subbuffer_start(citer, &citer->read_sb_offset);
232 DEBUGP(KERN_DEBUG
233 "LTT ASCII start cpu %d ret %d offset %lX\n",
234 citer->buf->a.cpu, ret, citer->read_sb_offset);
235 if (!ret || ret == -ENODATA) {
236 break; /* got data, or finalized */
237 } else { /* -EAGAIN */
238 if (signal_pending(current))
239 break;
240 schedule_timeout_interruptible(1);
241 //TODO: check for no-data delay. take ref. break
242 }
243 }
244 } else {
245 new_offset += citer->data_size;
246 citer->hdr_offset = new_offset + ltt_align(new_offset, sizeof(struct ltt_event_header));
247 DEBUGP(KERN_DEBUG
248 "LTT ASCII old_offset %lX new_offset %lX cpu %d\n",
249 old_offset, new_offset, citer->buf->a.cpu);
250 }
251
252 update_cpu_iter(citer, citer->hdr_offset);
253}
254
255static int cpu_iter_eof(struct ltt_relay_cpu_iter *citer)
256{
257 return !citer->sb_ref;
258}
259
260static int ltt_relay_iter_eof(struct ltt_relay_iter *iter)
261{
262 return iter->nr_refs == 0;
263}
264
265static void ltt_relay_advance_iter(struct ltt_relay_iter *iter)
266{
267 int i;
268 struct ltt_relay_cpu_iter *curr, *min = NULL;
269 iter->cpu = -1;
270
271 /*
272 * find the event with the minimum tsc.
273 * TODO: use min-heep for 4096CPUS
274 */
275 for_each_possible_cpu(i) {
276 curr = &iter->iter_cpu[i];
277
278 if (!curr->buf->a.allocated || !curr->header)
279 continue;
280
281 if (cpu_iter_eof(curr))
282 continue;
283
284 if (!min || curr->tsc < min->tsc) {
285 min = curr;
286 iter->cpu = i;
287 }
288 }
289
290 /* update cpu_iter for next ltt_relay_advance_iter() */
291 if (min)
292 ltt_relay_advance_cpu_iter(min);
293}
294
295static void *ascii_next(struct seq_file *m, void *v, loff_t *ppos)
296{
297 struct ltt_relay_iter *iter = m->private;
298
299 WARN_ON_ONCE(!iter->nr_refs);
300 BUG_ON(v != iter);
301
302 ltt_relay_advance_iter(iter);
303 return (ltt_relay_iter_eof(iter) || signal_pending(current))
304 ? NULL : iter;
305}
306
307static void *ascii_start(struct seq_file *m, loff_t *ppos)
308{
309 struct ltt_relay_iter *iter = m->private;
310
311 ltt_relay_advance_iter(iter);
312 return (ltt_relay_iter_eof(iter) || signal_pending(current))
313 ? NULL : iter;
314}
315
316static void ascii_stop(struct seq_file *m, void *v)
317{
318}
319
320static
321int seq_serialize(struct seq_file *m, struct ltt_chanbuf *buf,
322 size_t buf_offset, const char *fmt, size_t *data_size)
323{
324 int len;
325
326 if (m->count < m->size) {
327 len = ltt_serialize_printf(buf, buf_offset, data_size,
328 m->buf + m->count,
329 m->size - m->count, fmt);
330 if (m->count + len < m->size) {
331 m->count += len;
332 return 0;
333 }
334 }
335
336 m->count = m->size;
337 return -1;
338}
339
340static int ascii_show(struct seq_file *m, void *v)
341{
342 struct ltt_relay_iter *iter = v;
343 struct ltt_relay_cpu_iter *citer;
344 const char *name;
345 const char *fmt;
346 unsigned long long tsc;
347 size_t data_size;
348
349 if (iter->cpu == -1)
350 return 0;
351
352 citer = &iter->iter_cpu[iter->cpu];
353 WARN_ON_ONCE(!citer->sb_ref);
354 /*
355 * Nothing to show, we are at the end of the last subbuffer currently
356 * having data.
357 */
358 if (!citer->header)
359 return 0;
360
361 tsc = citer->tsc;
362 name = marker_get_name_from_id(citer->chID, citer->eID);
363 fmt = marker_get_fmt_from_id(citer->chID, citer->eID);
364
365 if (!name || !fmt)
366 return 0;
367
368 seq_printf(m, "event:%16.16s: cpu:%2d time:%20.20llu ",
369 name, iter->cpu, tsc);
370 seq_serialize(m, citer->buf, citer->payload_offset, fmt, &data_size);
371 seq_puts(m, "\n");
372 if (citer->data_size == INT_MAX)
373 citer->data_size = data_size;
374
375 return 0;
376}
377
378static struct seq_operations ascii_seq_ops = {
379 .start = ascii_start,
380 .next = ascii_next,
381 .stop = ascii_stop,
382 .show = ascii_show,
383};
384
385/* FIXME : cpu hotplug support */
386static int ltt_relay_iter_open_channel(struct ltt_relay_iter *iter,
387 struct ltt_chan *chan)
388{
389 int i, ret;
390 u16 chID = ltt_channels_get_index_from_name(chan->a.filename);
391
392 /* we don't need lock relay_channels_mutex */
393 for_each_possible_cpu(i) {
394 struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i];
395
396 citer->buf = per_cpu_ptr(chan->a.buf, i);
397 if (!citer->buf->a.allocated)
398 continue;
399
400 citer->iter = iter; /* easy lazy parent info */
401 citer->chID = chID;
402
403 ret = ltt_chanbuf_open_read(citer->buf);
404 if (ret) {
405 /* Failed to open a percpu buffer, close everything. */
406 citer->buf = NULL;
407 goto error;
408 }
409
410 for (;;) {
411 ret = subbuffer_start(citer,
412 &citer->read_sb_offset);
413 DEBUGP(KERN_DEBUG
414 "LTT ASCII open start "
415 "cpu %d ret %d offset %lX\n",
416 citer->buf->a.cpu, ret, citer->read_sb_offset);
417 if (!ret || ret == -ENODATA) {
418 break; /* got data, or finalized */
419 } else { /* -EAGAIN */
420 if (signal_pending(current))
421 break;
422 schedule_timeout_interruptible(1);
423 }
424 }
425 update_cpu_iter(citer, citer->hdr_offset);
426 }
427 if (!iter->nr_refs) {
428 ret = -ENODATA;
429 goto error;
430 }
431
432 return 0;
433
434error:
435 for_each_possible_cpu(i) {
436 struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i];
437
438 if (!citer->buf)
439 break;
440
441 if (citer->buf->a.allocated)
442 ltt_chanbuf_release_read(citer->buf);
443 }
444 return ret;
445}
446
447/* FIXME : cpu hotplug support */
448static int ltt_relay_iter_release_channel(struct ltt_relay_iter *iter)
449{
450 int i;
451
452 for_each_possible_cpu(i) {
453 struct ltt_relay_cpu_iter *citer = &iter->iter_cpu[i];
454
455 if (citer->sb_ref) {
456 WARN_ON_ONCE(!citer->buf->a.allocated);
457 DEBUGP(KERN_DEBUG
458 "LTT ASCII release stop cpu %d offset %lX\n",
459 citer->buf->a.cpu, citer->read_sb_offset);
460 subbuffer_stop(&iter->iter_cpu[i],
461 citer->read_sb_offset);
462 }
463 if (citer->buf->a.allocated)
464 ltt_chanbuf_release_read(citer->buf);
465 }
466 WARN_ON_ONCE(iter->nr_refs);
467 return 0;
468}
469
470static int ltt_relay_ascii_open(struct inode *inode, struct file *file)
471{
472 int ret;
473 struct ltt_chan *chan = inode->i_private;
474 struct ltt_relay_iter *iter = kzalloc(sizeof(*iter), GFP_KERNEL);
475 if (!iter)
476 return -ENOMEM;
477
478 iter->chan = chan;
479 ret = ltt_relay_iter_open_channel(iter, chan);
480 if (ret)
481 goto error_free_alloc;
482
483 ret = seq_open(file, &ascii_seq_ops);
484 if (ret)
485 goto error_release_channel;
486 ((struct seq_file *)file->private_data)->private = iter;
487 return 0;
488
489error_release_channel:
490 ltt_relay_iter_release_channel(iter);
491error_free_alloc:
492 kfree(iter);
493 return ret;
494}
495
496static int ltt_relay_ascii_release(struct inode *inode, struct file *file)
497{
498 struct seq_file *seq = file->private_data;
499 struct ltt_relay_iter *iter = seq->private;
500
501 ltt_relay_iter_release_channel(iter);
502 kfree(iter);
503 return 0;
504}
505
506static struct file_operations ltt_ascii_fops =
507{
508 .read = seq_read,
509 .open = ltt_relay_ascii_open,
510 .release = ltt_relay_ascii_release,
511 .llseek = no_llseek,
512 .owner = THIS_MODULE,
513};
514
515int ltt_ascii_create(struct ltt_chan *chan)
516{
517 struct dentry *dentry;
518
519 dentry = debugfs_create_file(chan->a.filename,
520 S_IRUSR | S_IRGRP,
521 chan->a.trace->dentry.ascii_root,
522 chan, &ltt_ascii_fops);
523 if (IS_ERR(dentry))
524 return PTR_ERR(dentry);
525
526 if (!dentry)
527 return -EEXIST;
528
529 chan->a.ascii_dentry = dentry;
530 dentry->d_inode->i_private = chan;
531 return 0;
532}
533EXPORT_SYMBOL_GPL(ltt_ascii_create);
534
535void ltt_ascii_remove(struct ltt_chan *chan)
536{
537 struct dentry *dentry;
538
539 dentry = dget(chan->a.ascii_dentry);
540 debugfs_remove(dentry);
541 /* TODO: wait / wakeup instead */
542 /*
543 * Wait for every reference to the dentry to be gone,
544 * except us.
545 */
0bc7b654 546 while (ACCESS_ONCE(dentry->d_count) != 1)
1c8284eb
MD
547 msleep(100);
548 dput(dentry);
549}
550EXPORT_SYMBOL_GPL(ltt_ascii_remove);
551
552int ltt_ascii_create_dir(struct ltt_trace *new_trace)
553{
554 new_trace->dentry.ascii_root = debugfs_create_dir(new_trace->trace_name,
555 ltt_ascii_dir_dentry);
556 if (!new_trace->dentry.ascii_root)
557 return -EEXIST;
558 return 0;
559}
560EXPORT_SYMBOL_GPL(ltt_ascii_create_dir);
561
562void ltt_ascii_remove_dir(struct ltt_trace *trace)
563{
564 debugfs_remove(trace->dentry.ascii_root);
565}
566EXPORT_SYMBOL_GPL(ltt_ascii_remove_dir);
567
13c60117 568__init int ltt_ascii_init(void)
1c8284eb
MD
569{
570 ltt_ascii_dir_dentry = debugfs_create_dir(LTT_ASCII, get_ltt_root());
1c8284eb
MD
571
572 return ltt_ascii_dir_dentry ? 0 : -EFAULT;
573}
574
13c60117 575__exit void ltt_ascii_exit(void)
1c8284eb
MD
576{
577 debugfs_remove(ltt_ascii_dir_dentry);
2e6246b4 578 put_ltt_root();
1c8284eb
MD
579}
580
1c8284eb
MD
581MODULE_LICENSE("GPL and additional rights");
582MODULE_AUTHOR("Lai Jiangshan@FNST and Mathieu Desnoyers");
583MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Ascii Converter");
This page took 0.042759 seconds and 4 git commands to generate.