diff options
Diffstat (limited to 'litmus/trace.c')
-rw-r--r-- | litmus/trace.c | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/litmus/trace.c b/litmus/trace.c new file mode 100644 index 000000000000..39200c8ff74e --- /dev/null +++ b/litmus/trace.c | |||
@@ -0,0 +1,213 @@ | |||
1 | #include <linux/sched.h> | ||
2 | #include <linux/module.h> | ||
3 | #include <linux/uaccess.h> | ||
4 | |||
5 | #include <litmus/ftdev.h> | ||
6 | #include <litmus/litmus.h> | ||
7 | #include <litmus/trace.h> | ||
8 | |||
9 | /******************************************************************************/ | ||
10 | /* Allocation */ | ||
11 | /******************************************************************************/ | ||
12 | |||
13 | static struct ftdev overhead_dev; | ||
14 | |||
15 | #define trace_ts_buf overhead_dev.minor[0].buf | ||
16 | |||
17 | static unsigned int ts_seq_no = 0; | ||
18 | |||
19 | static inline void __save_timestamp_cpu(unsigned long event, | ||
20 | uint8_t type, uint8_t cpu) | ||
21 | { | ||
22 | unsigned int seq_no; | ||
23 | struct timestamp *ts; | ||
24 | seq_no = fetch_and_inc((int *) &ts_seq_no); | ||
25 | if (ft_buffer_start_write(trace_ts_buf, (void**) &ts)) { | ||
26 | ts->event = event; | ||
27 | ts->timestamp = ft_timestamp(); | ||
28 | ts->seq_no = seq_no; | ||
29 | ts->cpu = cpu; | ||
30 | ts->task_type = type; | ||
31 | ft_buffer_finish_write(trace_ts_buf, ts); | ||
32 | } | ||
33 | } | ||
34 | |||
35 | static void __add_timestamp_user(struct timestamp *pre_recorded) | ||
36 | { | ||
37 | unsigned int seq_no; | ||
38 | struct timestamp *ts; | ||
39 | seq_no = fetch_and_inc((int *) &ts_seq_no); | ||
40 | |||
41 | if (ft_buffer_start_write(trace_ts_buf, (void**) &ts)) { | ||
42 | *ts = *pre_recorded; | ||
43 | ts->seq_no = seq_no; | ||
44 | ft_buffer_finish_write(trace_ts_buf, ts); | ||
45 | } | ||
46 | } | ||
47 | |||
48 | static inline void __save_timestamp(unsigned long event, | ||
49 | uint8_t type) | ||
50 | { | ||
51 | __save_timestamp_cpu(event, type, raw_smp_processor_id()); | ||
52 | } | ||
53 | |||
54 | /* hack: fake timestamp to user-reported time, and record parts of the PID */ | ||
55 | feather_callback void save_timestamp_time(unsigned long event, unsigned long ptr) | ||
56 | { | ||
57 | uint64_t* time = (uint64_t*) ptr; | ||
58 | unsigned int seq_no; | ||
59 | struct timestamp *ts; | ||
60 | seq_no = fetch_and_inc((int *) &ts_seq_no); | ||
61 | if (ft_buffer_start_write(trace_ts_buf, (void**) &ts)) { | ||
62 | ts->event = event; | ||
63 | ts->timestamp = *time; | ||
64 | ts->seq_no = seq_no; | ||
65 | /* type takes lowest byte of PID */ | ||
66 | ts->task_type = (uint8_t) current->pid; | ||
67 | /* cpu takes second-lowest byte of PID*/ | ||
68 | ts->cpu = (uint8_t) (current->pid >> 8); | ||
69 | |||
70 | ft_buffer_finish_write(trace_ts_buf, ts); | ||
71 | } | ||
72 | } | ||
73 | |||
74 | feather_callback void save_timestamp_pid(unsigned long event) | ||
75 | { | ||
76 | /* Abuse existing fields to partially export PID. */ | ||
77 | __save_timestamp_cpu(event, | ||
78 | /* type takes lowest byte of PID */ | ||
79 | (uint8_t) current->pid, | ||
80 | /* cpu takes second-lowest byte of PID*/ | ||
81 | (uint8_t) (current->pid >> 8)); | ||
82 | } | ||
83 | |||
84 | feather_callback void save_timestamp(unsigned long event) | ||
85 | { | ||
86 | __save_timestamp(event, TSK_UNKNOWN); | ||
87 | } | ||
88 | |||
89 | feather_callback void save_timestamp_def(unsigned long event, | ||
90 | unsigned long type) | ||
91 | { | ||
92 | __save_timestamp(event, (uint8_t) type); | ||
93 | } | ||
94 | |||
95 | feather_callback void save_timestamp_task(unsigned long event, | ||
96 | unsigned long t_ptr) | ||
97 | { | ||
98 | int rt = is_realtime((struct task_struct *) t_ptr); | ||
99 | __save_timestamp(event, rt ? TSK_RT : TSK_BE); | ||
100 | } | ||
101 | |||
102 | feather_callback void save_timestamp_cpu(unsigned long event, | ||
103 | unsigned long cpu) | ||
104 | { | ||
105 | __save_timestamp_cpu(event, TSK_UNKNOWN, cpu); | ||
106 | } | ||
107 | |||
108 | feather_callback void save_task_latency(unsigned long event, | ||
109 | unsigned long when_ptr) | ||
110 | { | ||
111 | lt_t now = litmus_clock(); | ||
112 | lt_t *when = (lt_t*) when_ptr; | ||
113 | unsigned int seq_no; | ||
114 | int cpu = raw_smp_processor_id(); | ||
115 | struct timestamp *ts; | ||
116 | |||
117 | seq_no = fetch_and_inc((int *) &ts_seq_no); | ||
118 | if (ft_buffer_start_write(trace_ts_buf, (void**) &ts)) { | ||
119 | ts->event = event; | ||
120 | ts->timestamp = now - *when; | ||
121 | ts->seq_no = seq_no; | ||
122 | ts->cpu = cpu; | ||
123 | ts->task_type = TSK_RT; | ||
124 | ft_buffer_finish_write(trace_ts_buf, ts); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | /******************************************************************************/ | ||
129 | /* DEVICE FILE DRIVER */ | ||
130 | /******************************************************************************/ | ||
131 | |||
132 | /* | ||
133 | * should be 8M; it is the max we can ask to buddy system allocator (MAX_ORDER) | ||
134 | * and we might not get as much | ||
135 | */ | ||
136 | #define NO_TIMESTAMPS (2 << 16) | ||
137 | |||
138 | static int alloc_timestamp_buffer(struct ftdev* ftdev, unsigned int idx) | ||
139 | { | ||
140 | unsigned int count = NO_TIMESTAMPS; | ||
141 | while (count && !trace_ts_buf) { | ||
142 | printk("time stamp buffer: trying to allocate %u time stamps.\n", count); | ||
143 | ftdev->minor[idx].buf = alloc_ft_buffer(count, sizeof(struct timestamp)); | ||
144 | count /= 2; | ||
145 | } | ||
146 | return ftdev->minor[idx].buf ? 0 : -ENOMEM; | ||
147 | } | ||
148 | |||
149 | static void free_timestamp_buffer(struct ftdev* ftdev, unsigned int idx) | ||
150 | { | ||
151 | free_ft_buffer(ftdev->minor[idx].buf); | ||
152 | ftdev->minor[idx].buf = NULL; | ||
153 | } | ||
154 | |||
155 | static ssize_t write_timestamp_from_user(struct ft_buffer* buf, size_t len, | ||
156 | const char __user *from) | ||
157 | { | ||
158 | ssize_t consumed = 0; | ||
159 | struct timestamp ts; | ||
160 | |||
161 | /* don't give us partial timestamps */ | ||
162 | if (len % sizeof(ts)) | ||
163 | return -EINVAL; | ||
164 | |||
165 | while (len >= sizeof(ts)) { | ||
166 | if (copy_from_user(&ts, from, sizeof(ts))) { | ||
167 | consumed = -EFAULT; | ||
168 | goto out; | ||
169 | } | ||
170 | len -= sizeof(ts); | ||
171 | from += sizeof(ts); | ||
172 | consumed += sizeof(ts); | ||
173 | |||
174 | __add_timestamp_user(&ts); | ||
175 | } | ||
176 | |||
177 | out: | ||
178 | return consumed; | ||
179 | } | ||
180 | |||
181 | static int __init init_ft_overhead_trace(void) | ||
182 | { | ||
183 | int err; | ||
184 | |||
185 | printk("Initializing Feather-Trace overhead tracing device.\n"); | ||
186 | err = ftdev_init(&overhead_dev, THIS_MODULE, 1, "ft_trace"); | ||
187 | if (err) | ||
188 | goto err_out; | ||
189 | |||
190 | overhead_dev.alloc = alloc_timestamp_buffer; | ||
191 | overhead_dev.free = free_timestamp_buffer; | ||
192 | overhead_dev.write = write_timestamp_from_user; | ||
193 | |||
194 | err = register_ftdev(&overhead_dev); | ||
195 | if (err) | ||
196 | goto err_dealloc; | ||
197 | |||
198 | return 0; | ||
199 | |||
200 | err_dealloc: | ||
201 | ftdev_exit(&overhead_dev); | ||
202 | err_out: | ||
203 | printk(KERN_WARNING "Could not register ft_trace module.\n"); | ||
204 | return err; | ||
205 | } | ||
206 | |||
207 | static void __exit exit_ft_overhead_trace(void) | ||
208 | { | ||
209 | ftdev_exit(&overhead_dev); | ||
210 | } | ||
211 | |||
212 | module_init(init_ft_overhead_trace); | ||
213 | module_exit(exit_ft_overhead_trace); | ||