cd61cbc4 |
1 | #include <stdarg.h> |
2 | |
d06fc4d5 |
3 | /* Maximum number of callbacks per marker */ |
4 | #define LTT_NR_CALLBACKS 10 |
5 | |
cd61cbc4 |
6 | /* LTT flags |
7 | * |
8 | * LTT_FLAG_TRACE : first arg contains trace to write into. |
9 | * (type : struct ltt_trace_struct *) |
10 | * LTT_FLAG_CHANNEL : following arg contains channel index to write into. |
11 | * (type : uint8_t) |
12 | * LTT_FLAG_FORCE : Force write in disabled traces (internal ltt use) |
13 | */ |
14 | |
15 | #define _LTT_FLAG_TRACE 0 |
16 | #define _LTT_FLAG_CHANNEL 1 |
17 | #define _LTT_FLAG_FORCE 2 |
18 | |
19 | #define LTT_FLAG_TRACE (1 << _LTT_FLAG_TRACE) |
20 | #define LTT_FLAG_CHANNEL (1 << _LTT_FLAG_CHANNEL) |
21 | #define LTT_FLAG_FORCE (1 << _LTT_FLAG_FORCE) |
22 | |
084f56d4 |
23 | |
d06fc4d5 |
24 | char *(*ltt_serialize_cb)(char *buffer, int *cb_args, |
25 | const char *fmt, va_list args); |
084f56d4 |
26 | |
27 | |
28 | static int skip_atoi(const char **s) |
29 | { |
30 | int i=0; |
31 | |
32 | while (isdigit(**s)) |
33 | i = i*10 + *((*s)++) - '0'; |
34 | return i; |
35 | } |
36 | |
37 | /* Inspired from vsnprintf */ |
38 | /* New types : |
d06fc4d5 |
39 | * %r : serialized fixed length struct, union, array. |
40 | * %v : serialized sequence |
41 | * %k : callback |
084f56d4 |
42 | */ |
43 | static inline __attribute__((no_instrument_function)) |
d06fc4d5 |
44 | char *ltt_serialize_data(char *buffer, int *cb_args, |
45 | const char *fmt, va_list args) |
084f56d4 |
46 | { |
47 | int len; |
48 | const char *s; |
49 | int elem_size; /* Size of the integer for 'b' */ |
50 | /* Size of the data contained by 'r' */ |
51 | int elem_alignment; /* Element alignment for 'r' */ |
52 | int qualifier; /* 'h', 'l', or 'L' for integer fields */ |
53 | /* 'z' support added 23/7/1999 S.H. */ |
54 | /* 'z' changed to 'Z' --davidm 1/25/99 */ |
55 | /* 't' added for ptrdiff_t */ |
56 | char *str; /* Pointer to write to */ |
57 | ltt_serialize_cb cb; |
d06fc4d5 |
58 | int cb_arg_nr = 0; |
084f56d4 |
59 | |
60 | str = buf; |
61 | |
62 | for (; *fmt ; ++fmt) { |
63 | if (*fmt != '%') { |
64 | /* Skip text */ |
65 | continue; |
66 | } |
67 | |
68 | /* process flags : ignore standard print formats for now. */ |
69 | repeat: |
70 | ++fmt; /* this also skips first '%' */ |
71 | switch (*fmt) { |
72 | case '-': |
73 | case '+': |
74 | case ' ': |
75 | case '#': |
76 | case '0': goto repeat; |
77 | } |
78 | |
79 | /* get element size */ |
80 | elem_size = -1; |
81 | if (isdigit(*fmt)) |
82 | elem_size = skip_atoi(&fmt); |
83 | else if (*fmt == '*') { |
84 | ++fmt; |
85 | /* it's the next argument */ |
86 | elem_size = va_arg(args, int); |
87 | } |
88 | |
89 | /* get the alignment */ |
90 | elem_alignment = -1; |
91 | if (*fmt == '.') { |
92 | ++fmt; |
93 | if (isdigit(*fmt)) |
94 | elem_alignment = skip_atoi(&fmt); |
95 | else if (*fmt == '*') { |
96 | ++fmt; |
97 | /* it's the next argument */ |
98 | elem_alignment = va_arg(args, int); |
99 | } |
100 | } |
101 | |
102 | /* get the conversion qualifier */ |
103 | qualifier = -1; |
104 | if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L' || |
105 | *fmt =='Z' || *fmt == 'z' || *fmt == 't') { |
106 | qualifier = *fmt; |
107 | ++fmt; |
108 | if (qualifier == 'l' && *fmt == 'l') { |
109 | qualifier = 'L'; |
110 | ++fmt; |
111 | } |
112 | } |
113 | |
114 | switch (*fmt) { |
115 | case 'c': |
116 | if (buffer) |
117 | *str = (char) va_arg(args, int); |
118 | str += sizeof(char); |
119 | continue; |
120 | |
121 | case 's': |
122 | s = va_arg(args, char *); |
123 | if ((unsigned long)s < PAGE_SIZE) |
124 | s = "<NULL>"; |
125 | if (buffer) |
126 | strcpy(str, s); |
127 | str += strlen(s); |
d06fc4d5 |
128 | /* Following alignment for genevent |
129 | * compatibility */ |
130 | str += ltt_align(str, sizeof(void*)); |
084f56d4 |
131 | continue; |
132 | |
133 | case 'p': |
134 | str += ltt_align(str, sizeof(void*)); |
135 | if (buffer) |
136 | *(void**)str = va_arg(args, void *); |
137 | continue; |
138 | |
139 | case 'r': |
140 | /* For array, struct, union */ |
141 | if (elem_alignment < 0) |
142 | elem_alignment = sizeof(void*); |
143 | str += ltt_align(str, elem_alignment); |
144 | if (elem_size > 0) { |
145 | const char *src = va_arg(args, |
146 | const char *); |
147 | if (buffer) |
148 | memcpy(str, src, elem_size); |
149 | str += elem_size; |
150 | } |
151 | continue; |
152 | |
153 | case 'v': |
154 | /* For sequence */ |
155 | str += ltt_align(str, sizeof(int)); |
156 | if (buffer) |
157 | *(int*)str = elem_size; |
158 | str += sizeof(int); |
159 | if (elem_alignment > 0) |
160 | str += ltt_align(str, elem_alignment); |
161 | if (elem_size > 0) { |
162 | const char *src = va_arg(args, |
163 | const char *); |
164 | if (buffer) |
165 | memcpy(str, src, elem_size); |
166 | str += elem_size; |
167 | } |
d06fc4d5 |
168 | /* Following alignment for genevent |
169 | * compatibility */ |
170 | str += ltt_align(str, sizeof(void*)); |
084f56d4 |
171 | continue; |
172 | |
173 | case 'k': |
174 | /* For callback */ |
175 | cb = va_arg(args, ltt_serialize_cb); |
176 | /* The callback will take as many arguments |
177 | * as it needs from args. They won't be |
178 | * type verified. */ |
d06fc4d5 |
179 | if (cb_arg_nr < LTT_NR_CALLBACKS) |
180 | str = cb(str, &cb_args[cb_arg_nr++], |
181 | fmt, args); |
084f56d4 |
182 | continue; |
183 | |
184 | case 'n': |
185 | /* FIXME: |
d06fc4d5 |
186 | * What does C99 say about the overflow case |
187 | * here? */ |
084f56d4 |
188 | if (qualifier == 'l') { |
189 | long * ip = va_arg(args, long *); |
190 | *ip = (str - buf); |
d06fc4d5 |
191 | } else if (qualifier == 'Z' |
192 | || qualifier == 'z') { |
084f56d4 |
193 | size_t * ip = va_arg(args, size_t *); |
194 | *ip = (str - buf); |
195 | } else { |
196 | int * ip = va_arg(args, int *); |
197 | *ip = (str - buf); |
198 | } |
199 | continue; |
200 | |
201 | case '%': |
202 | continue; |
203 | |
204 | case 'o': |
205 | case 'X': |
206 | case 'x': |
207 | case 'd': |
208 | case 'i': |
209 | case 'u': |
210 | break; |
211 | |
212 | default: |
213 | if (!*fmt) |
214 | --fmt; |
215 | continue; |
216 | } |
217 | switch (qualifier) { |
218 | case 'L': |
219 | str += ltt_align(str, sizeof(long long)); |
220 | if (buffer) |
221 | *(long long*)str = va_arg(args, long long); |
222 | str += sizeof(long long); |
223 | break; |
224 | case 'l': |
225 | str += ltt_align(str, sizeof(long)); |
226 | if (buffer) |
227 | *(long*)str = va_arg(args, long); |
228 | str += sizeof(long); |
229 | break; |
230 | case 'Z': |
231 | case 'z': |
232 | str += ltt_align(str, sizeof(size_t)); |
233 | if (buffer) |
234 | *(size_t*)str = va_arg(args, size_t); |
235 | str += sizeof(size_t); |
236 | break; |
237 | case 't': |
238 | str += ltt_align(str, sizeof(ptrdiff_t)); |
239 | if (buffer) |
240 | *(ptrdiff_t*)str = va_arg(args, ptrdiff_t); |
241 | str += sizeof(ptrdiff_t); |
242 | break; |
243 | case 'h': |
244 | str += ltt_align(str, sizeof(short)); |
245 | if (buffer) |
246 | *(short*)str = (short) va_arg(args, int); |
247 | str += sizeof(short); |
248 | break; |
249 | case 'b': |
250 | if (elem_size > 0) |
251 | str += ltt_align(str, elem_size); |
252 | if (buffer) |
253 | switch (elem_size) { |
254 | case 1: |
d06fc4d5 |
255 | *(int8_t*)str = |
256 | (int8_t)va_arg(args, int); |
084f56d4 |
257 | break; |
258 | case 2: |
d06fc4d5 |
259 | *(int16_t*)str = |
260 | (int16_t)va_arg(args, int); |
084f56d4 |
261 | break; |
262 | case 4: |
263 | *(int32_t*)str = va_arg(args, int32_t); |
264 | break; |
265 | case 8: |
266 | *(int64_t*)str = va_arg(args, int64_t); |
267 | break; |
268 | } |
269 | str += elem_size; |
270 | default: |
271 | str += ltt_align(str, sizeof(int)); |
272 | if (buffer) |
273 | *(int*)str = va_arg(args, int); |
274 | str += sizeof(int); |
275 | } |
276 | } |
277 | return str; |
278 | } |
279 | |
cd61cbc4 |
280 | /* Calculate data size */ |
281 | /* Assume that the padding for alignment starts at a |
282 | * sizeof(void *) address. */ |
283 | static inline __attribute__((no_instrument_function)) |
284 | size_t ltt_get_data_size(ltt_facility_t fID, uint8_t eID, |
d06fc4d5 |
285 | int *cb_args, |
cd61cbc4 |
286 | const char *fmt, va_list args) |
287 | { |
084f56d4 |
288 | return (size_t)ltt_serialize_data(NULL, fmt, args); |
cd61cbc4 |
289 | } |
290 | |
291 | static inline __attribute__((no_instrument_function)) |
084f56d4 |
292 | void ltt_write_event_data(char *buffer, |
cd61cbc4 |
293 | ltt_facility_t fID, uint8_t eID, |
d06fc4d5 |
294 | int *cb_args, |
cd61cbc4 |
295 | const char *fmt, va_list args) |
296 | { |
084f56d4 |
297 | ltt_serialize_data(buffer, fmt, args); |
cd61cbc4 |
298 | } |
299 | |
300 | |
301 | __attribute__((no_instrument_function)) |
084f56d4 |
302 | void _vtrace(ltt_facility_t fID, uint8_t eID, long flags, |
cd61cbc4 |
303 | const char *fmt, va_list args) |
304 | { |
305 | size_t data_size, slot_size; |
084f56d4 |
306 | int channel_index; |
cd61cbc4 |
307 | struct ltt_channel_struct *channel; |
308 | struct ltt_trace_struct *trace, *dest_trace; |
309 | void *transport_data; |
310 | uint64_t tsc; |
311 | char *buffer; |
084f56d4 |
312 | va_list args_copy; |
d06fc4d5 |
313 | int cb_args[LTT_NR_CALLBACKS]; |
cd61cbc4 |
314 | |
315 | /* This test is useful for quickly exiting static tracing when no |
316 | * trace is active. */ |
d06fc4d5 |
317 | if (likely(ltt_traces.num_active_traces == 0 |
318 | && !(flags & LTT_FLAG_FORCE))) |
cd61cbc4 |
319 | return; |
320 | |
cd61cbc4 |
321 | preempt_disable(); |
322 | ltt_nesting[smp_processor_id()]++; |
323 | |
324 | if (unlikely(flags & LTT_FLAG_TRACE)) |
325 | dest_trace = va_arg(args, struct ltt_trace_struct *); |
326 | if (unlikely(flags & LTT_FLAG_CHANNEL)) |
084f56d4 |
327 | channel_index = va_arg(args, int); |
cd61cbc4 |
328 | else |
329 | channel_index = ltt_get_channel_index(fID, eID); |
330 | |
084f56d4 |
331 | va_copy(args_copy, args); /* Check : skip 2 st args if trace/ch */ |
d06fc4d5 |
332 | data_size = ltt_get_data_size(fID, eID, cb_args, fmt, args_copy); |
084f56d4 |
333 | va_end(args_copy); |
334 | |
cd61cbc4 |
335 | /* Iterate on each traces */ |
336 | list_for_each_entry_rcu(trace, <t_traces.head, list) { |
337 | if (unlikely(!trace->active && !(flags & LTT_FLAG_FORCE))) |
338 | continue; |
339 | if (unlikely(flags & LTT_FLAG_TRACE && trace != dest_trace)) |
340 | continue; |
341 | channel = ltt_get_channel_from_index(trace, channel_index); |
342 | /* reserve space : header and data */ |
343 | buffer = ltt_reserve_slot(trace, channel, &transport_data, |
344 | data_size, &slot_size, &tsc); |
345 | if (unlikely(!buffer)) |
346 | continue; /* buffer full */ |
347 | /* Out-of-order write : header and data */ |
348 | buffer = ltt_write_event_header(trace, channel, buffer, |
349 | fID, eID, data_size, tsc); |
084f56d4 |
350 | va_copy(args_copy, args); |
d06fc4d5 |
351 | ltt_write_event_data(buffer, fID, eID, cb_args, fmt, args_copy); |
084f56d4 |
352 | va_end(args_copy); |
cd61cbc4 |
353 | /* Out-of-order commit */ |
354 | ltt_commit_slot(channel, &transport_data, buffer, slot_size); |
355 | } |
356 | |
357 | ltt_nesting[smp_processor_id()]--; |
358 | preempt_enable(); |
359 | } |
360 | |
361 | __attribute__((no_instrument_function)) |
084f56d4 |
362 | void _trace(ltt_facility_t fID, uint8_t eID, long flags, const char *fmt, ...) |
cd61cbc4 |
363 | { |
364 | va_list args; |
365 | |
366 | va_start(args, fmt); |
084f56d4 |
367 | _vtrace(fID, eID, flags, fmt, args); |
cd61cbc4 |
368 | va_end(args); |
369 | } |
370 | |