2 * LTT ascii binary buffer to ascii converter.
4 * Copyright 2008 - 2009 Lai Jiangshan (laijs@cn.fujitsu.com)
5 * Copyright 2009 - Mathieu Desnoyers mathieu.desnoyers@polymtl.ca
7 * Dual LGPL v2.1/GPL v2 license.
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
17 * Fix case when having a text file open and destroying trace.
19 * - Automate periodical switch:
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.
27 * - If a channel is switched and read without data, make sure it is still
28 * considered afterward (not removed from the queue).
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.
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>
51 #include "ltt-tracer.h"
52 #include "ltt-relay.h"
53 #include "ltt-relay-lockless.h"
58 #define DEBUGP(fmt , a...)
61 struct dentry
*ltt_ascii_dir_dentry
;
62 EXPORT_SYMBOL_GPL(ltt_ascii_dir_dentry
);
64 struct ltt_relay_iter
;
66 struct 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 */
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 */
79 u16 chID
; /* channel ID, const */
83 struct ltt_relay_iter
{
84 struct ltt_relay_cpu_iter iter_cpu
[NR_CPUS
];
85 struct ltt_chan
*chan
;
92 * offset of 0 in subbuffer means "subbuf size" (filled subbuffer).
94 static int is_subbuffer_offset_end(struct ltt_relay_cpu_iter
*citer
,
97 struct ltt_chan
*chan
= container_of(citer
->buf
->a
.chan
,
99 long sub_offset
= SUBBUF_OFFSET(offset
- 1, chan
) + 1;
101 return (sub_offset
<= citer
->header
->data_size
);
104 static u64
calculate_tsc(u64 pre_tsc
, u64 read_tsc
, unsigned int rflags
)
106 u64 new_tsc
= read_tsc
;
108 if (rflags
!= LTT_RFLAG_ID_SIZE_TSC
) {
109 BUG_ON(read_tsc
>> LTT_TSC_BITS
);
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
;
120 * calculate payload offset */
121 static inline long calculate_payload_offset(long offset
, u16 chID
, u16 eID
)
125 if (!ltt_get_alignment())
128 fmt
= marker_get_fmt_from_id(chID
, eID
);
131 return offset
+ ltt_fmt_largest_align(offset
, fmt
);
134 static void update_new_event(struct ltt_relay_cpu_iter
*citer
, long hdr_offset
)
140 WARN_ON_ONCE(hdr_offset
!= citer
->hdr_offset
);
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
,
149 citer
->tsc
= calculate_tsc(citer
->tsc
, read_tsc
, rflags
);
152 static void update_event_size(struct ltt_relay_cpu_iter
*citer
, long hdr_offset
)
158 if (citer
->data_size
!= INT_MAX
)
161 fmt
= marker_get_fmt_from_id(citer
->chID
, citer
->eID
);
163 ltt_serialize_printf(citer
->buf
, citer
->payload_offset
,
164 &data_size
, output
, 0, fmt
);
165 citer
->data_size
= data_size
;
168 static void update_cpu_iter(struct ltt_relay_cpu_iter
*citer
, long hdr_offset
)
170 if (unlikely((!citer
->sb_ref
)
171 || is_subbuffer_offset_end(citer
, hdr_offset
))) {
172 citer
->header
= NULL
;
175 update_new_event(citer
, hdr_offset
);
176 update_event_size(citer
, hdr_offset
);
180 * returns 0 if we get a subbuffer reference.
181 * else, the buffer has not available data, try again later.
183 static int subbuffer_start(struct ltt_relay_cpu_iter
*citer
, long *offset
)
186 struct ltt_relay_iter
*iter
= citer
->iter
;
188 ret
= ltt_chanbuf_get_subbuf(citer
->buf
, offset
);
190 citer
->header
= ltt_relay_read_offset_address(&citer
->buf
->a
,
192 citer
->hdr_offset
= (*offset
) + ltt_sb_header_size();
193 citer
->tsc
= citer
->header
->cycle_count_begin
;
198 if (ltt_chanbuf_is_finalized(citer
->buf
))
205 static void subbuffer_stop(struct ltt_relay_cpu_iter
*citer
,
209 struct ltt_relay_iter
*iter
= citer
->iter
;
211 WARN_ON_ONCE(!citer
->sb_ref
);
212 ret
= ltt_chanbuf_put_subbuf(citer
->buf
, offset
);
218 static void ltt_relay_advance_cpu_iter(struct ltt_relay_cpu_iter
*citer
)
220 long old_offset
= citer
->payload_offset
;
221 long new_offset
= citer
->payload_offset
;
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
);
231 ret
= subbuffer_start(citer
, &citer
->read_sb_offset
);
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
))
240 schedule_timeout_interruptible(1);
241 //TODO: check for no-data delay. take ref. break
245 new_offset
+= citer
->data_size
;
246 citer
->hdr_offset
= new_offset
+ ltt_align(new_offset
, sizeof(struct ltt_event_header
));
248 "LTT ASCII old_offset %lX new_offset %lX cpu %d\n",
249 old_offset
, new_offset
, citer
->buf
->a
.cpu
);
252 update_cpu_iter(citer
, citer
->hdr_offset
);
255 static int cpu_iter_eof(struct ltt_relay_cpu_iter
*citer
)
257 return !citer
->sb_ref
;
260 static int ltt_relay_iter_eof(struct ltt_relay_iter
*iter
)
262 return iter
->nr_refs
== 0;
265 static void ltt_relay_advance_iter(struct ltt_relay_iter
*iter
)
268 struct ltt_relay_cpu_iter
*curr
, *min
= NULL
;
272 * find the event with the minimum tsc.
273 * TODO: use min-heep for 4096CPUS
275 for_each_possible_cpu(i
) {
276 curr
= &iter
->iter_cpu
[i
];
278 if (!curr
->buf
->a
.allocated
|| !curr
->header
)
281 if (cpu_iter_eof(curr
))
284 if (!min
|| curr
->tsc
< min
->tsc
) {
290 /* update cpu_iter for next ltt_relay_advance_iter() */
292 ltt_relay_advance_cpu_iter(min
);
295 static void *ascii_next(struct seq_file
*m
, void *v
, loff_t
*ppos
)
297 struct ltt_relay_iter
*iter
= m
->private;
299 WARN_ON_ONCE(!iter
->nr_refs
);
302 ltt_relay_advance_iter(iter
);
303 return (ltt_relay_iter_eof(iter
) || signal_pending(current
))
307 static void *ascii_start(struct seq_file
*m
, loff_t
*ppos
)
309 struct ltt_relay_iter
*iter
= m
->private;
311 ltt_relay_advance_iter(iter
);
312 return (ltt_relay_iter_eof(iter
) || signal_pending(current
))
316 static void ascii_stop(struct seq_file
*m
, void *v
)
321 int seq_serialize(struct seq_file
*m
, struct ltt_chanbuf
*buf
,
322 size_t buf_offset
, const char *fmt
, size_t *data_size
)
326 if (m
->count
< m
->size
) {
327 len
= ltt_serialize_printf(buf
, buf_offset
, data_size
,
329 m
->size
- m
->count
, fmt
);
330 if (m
->count
+ len
< m
->size
) {
340 static int ascii_show(struct seq_file
*m
, void *v
)
342 struct ltt_relay_iter
*iter
= v
;
343 struct ltt_relay_cpu_iter
*citer
;
346 unsigned long long tsc
;
352 citer
= &iter
->iter_cpu
[iter
->cpu
];
353 WARN_ON_ONCE(!citer
->sb_ref
);
355 * Nothing to show, we are at the end of the last subbuffer currently
362 name
= marker_get_name_from_id(citer
->chID
, citer
->eID
);
363 fmt
= marker_get_fmt_from_id(citer
->chID
, citer
->eID
);
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
);
372 if (citer
->data_size
== INT_MAX
)
373 citer
->data_size
= data_size
;
378 static struct seq_operations ascii_seq_ops
= {
379 .start
= ascii_start
,
385 /* FIXME : cpu hotplug support */
386 static int ltt_relay_iter_open_channel(struct ltt_relay_iter
*iter
,
387 struct ltt_chan
*chan
)
390 u16 chID
= ltt_channels_get_index_from_name(chan
->a
.filename
);
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
];
396 citer
->buf
= per_cpu_ptr(chan
->a
.buf
, i
);
397 if (!citer
->buf
->a
.allocated
)
400 citer
->iter
= iter
; /* easy lazy parent info */
403 ret
= ltt_chanbuf_open_read(citer
->buf
);
405 /* Failed to open a percpu buffer, close everything. */
411 ret
= subbuffer_start(citer
,
412 &citer
->read_sb_offset
);
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
))
422 schedule_timeout_interruptible(1);
425 update_cpu_iter(citer
, citer
->hdr_offset
);
427 if (!iter
->nr_refs
) {
435 for_each_possible_cpu(i
) {
436 struct ltt_relay_cpu_iter
*citer
= &iter
->iter_cpu
[i
];
441 if (citer
->buf
->a
.allocated
)
442 ltt_chanbuf_release_read(citer
->buf
);
447 /* FIXME : cpu hotplug support */
448 static int ltt_relay_iter_release_channel(struct ltt_relay_iter
*iter
)
452 for_each_possible_cpu(i
) {
453 struct ltt_relay_cpu_iter
*citer
= &iter
->iter_cpu
[i
];
456 WARN_ON_ONCE(!citer
->buf
->a
.allocated
);
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
);
463 if (citer
->buf
->a
.allocated
)
464 ltt_chanbuf_release_read(citer
->buf
);
466 WARN_ON_ONCE(iter
->nr_refs
);
470 static int ltt_relay_ascii_open(struct inode
*inode
, struct file
*file
)
473 struct ltt_chan
*chan
= inode
->i_private
;
474 struct ltt_relay_iter
*iter
= kzalloc(sizeof(*iter
), GFP_KERNEL
);
479 ret
= ltt_relay_iter_open_channel(iter
, chan
);
481 goto error_free_alloc
;
483 ret
= seq_open(file
, &ascii_seq_ops
);
485 goto error_release_channel
;
486 ((struct seq_file
*)file
->private_data
)->private = iter
;
489 error_release_channel
:
490 ltt_relay_iter_release_channel(iter
);
496 static int ltt_relay_ascii_release(struct inode
*inode
, struct file
*file
)
498 struct seq_file
*seq
= file
->private_data
;
499 struct ltt_relay_iter
*iter
= seq
->private;
501 ltt_relay_iter_release_channel(iter
);
506 static struct file_operations ltt_ascii_fops
=
509 .open
= ltt_relay_ascii_open
,
510 .release
= ltt_relay_ascii_release
,
512 .owner
= THIS_MODULE
,
515 int ltt_ascii_create(struct ltt_chan
*chan
)
517 struct dentry
*dentry
;
519 dentry
= debugfs_create_file(chan
->a
.filename
,
521 chan
->a
.trace
->dentry
.ascii_root
,
522 chan
, <t_ascii_fops
);
524 return PTR_ERR(dentry
);
529 chan
->a
.ascii_dentry
= dentry
;
530 dentry
->d_inode
->i_private
= chan
;
533 EXPORT_SYMBOL_GPL(ltt_ascii_create
);
535 void ltt_ascii_remove(struct ltt_chan
*chan
)
537 struct dentry
*dentry
;
539 dentry
= dget(chan
->a
.ascii_dentry
);
540 debugfs_remove(dentry
);
541 /* TODO: wait / wakeup instead */
543 * Wait for every reference to the dentry to be gone,
546 while (atomic_read(&dentry
->d_count
) != 1)
550 EXPORT_SYMBOL_GPL(ltt_ascii_remove
);
552 int ltt_ascii_create_dir(struct ltt_trace
*new_trace
)
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
)
560 EXPORT_SYMBOL_GPL(ltt_ascii_create_dir
);
562 void ltt_ascii_remove_dir(struct ltt_trace
*trace
)
564 debugfs_remove(trace
->dentry
.ascii_root
);
566 EXPORT_SYMBOL_GPL(ltt_ascii_remove_dir
);
568 __init
int ltt_ascii_init(void)
570 ltt_ascii_dir_dentry
= debugfs_create_dir(LTT_ASCII
, get_ltt_root());
572 return ltt_ascii_dir_dentry
? 0 : -EFAULT
;
575 __exit
void ltt_ascii_exit(void)
577 debugfs_remove(ltt_ascii_dir_dentry
);
581 MODULE_LICENSE("GPL and additional rights");
582 MODULE_AUTHOR("Lai Jiangshan@FNST and Mathieu Desnoyers");
583 MODULE_DESCRIPTION("Linux Trace Toolkit Next Generation Ascii Converter");