Commit | Line | Data |
---|---|---|
8f07d382 VA |
1 | /* |
2 | * This file is part of the Linux Trace Toolkit viewer | |
3 | * Copyright (C) 2003-2004 Michel Dagenais | |
4 | * 2005 Mathieu Desnoyers | |
5 | * 2011 Vincent Attard <vinc.attard@gmail.com> | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify | |
8 | * it under the terms of the GNU General Public License Version 2 as | |
9 | * published by the Free Software Foundation; | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, | |
19 | * MA 02111-1307, USA. | |
20 | */ | |
21 | ||
22 | /* | |
23 | * Formatted dump plugin prints a formatted output of each events in a trace. | |
24 | * The output format is defined as a parameter. It provides a default format | |
25 | * easy to read, a "strace-like" format and the original textDump format for | |
26 | * backward compatibility. | |
27 | */ | |
28 | ||
29 | #ifdef HAVE_CONFIG_H | |
30 | #include <config.h> | |
31 | #endif | |
32 | ||
33 | #include <lttv/lttv.h> | |
34 | #include <lttv/option.h> | |
35 | #include <lttv/module.h> | |
36 | #include <lttv/hook.h> | |
37 | #include <lttv/attribute.h> | |
38 | #include <lttv/iattribute.h> | |
39 | #include <lttv/stats.h> | |
40 | #include <lttv/filter.h> | |
41 | #include <lttv/print.h> | |
42 | #include <ltt/ltt.h> | |
43 | #include <ltt/event.h> | |
44 | #include <ltt/trace.h> | |
45 | #include <stdio.h> | |
46 | #include <inttypes.h> | |
47 | #include <string.h> | |
48 | #include <stdlib.h> | |
49 | ||
50 | static gboolean a_no_field_names; | |
51 | static gboolean a_state; | |
52 | static gboolean a_text; | |
53 | static gboolean a_strace; | |
54 | ||
ed7dd953 MD |
55 | static char *a_file_name; |
56 | static char *a_format; | |
8f07d382 VA |
57 | |
58 | static LttvHooks *before_traceset; | |
59 | static LttvHooks *event_hook; | |
60 | ||
61 | static const char default_format[] = | |
62 | "channel:%c event:%e timestamp:%t elapsed:%l cpu:%u pid:%d ppid:%i " | |
63 | "tgpid:%g process:%p brand:%b state:%a payload:{ %m }"; | |
64 | static const char textDump_format[] = | |
65 | "%c.%e: %s.%n (%r/%c_%u), %d, %g, %p, %b, %i, %y, %a { %m }"; | |
66 | static const char strace_format[] = "%e(%m) %s.%n"; | |
67 | ||
68 | static FILE *a_file; | |
69 | ||
70 | static GString *a_string; | |
71 | ||
72 | static gboolean open_output_file(void *hook_data, void *call_data) | |
73 | { | |
74 | g_info("Open the output file"); | |
75 | if (a_file_name == NULL) { | |
ed7dd953 | 76 | a_file = stdout; |
8f07d382 | 77 | } else { |
ed7dd953 | 78 | a_file = fopen(a_file_name, "w"); |
8f07d382 VA |
79 | } |
80 | if (a_file == NULL) { | |
81 | g_error("cannot open file %s", a_file_name); | |
82 | } | |
83 | return FALSE; | |
84 | } | |
85 | ||
86 | static int write_event_content(void *hook_data, void *call_data) | |
87 | { | |
88 | gboolean result; | |
89 | ||
90 | LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); | |
91 | ||
92 | LttvTracefileContext *tfc = (LttvTracefileContext *)call_data; | |
93 | ||
94 | LttvTracefileState *tfs = (LttvTracefileState *)call_data; | |
95 | ||
96 | LttEvent *e; | |
97 | ||
98 | LttvAttributeValue value_filter; | |
99 | ||
100 | LttvFilter *filter; | |
101 | ||
102 | guint cpu = tfs->cpu; | |
103 | LttvTraceState *ts = (LttvTraceState *)tfc->t_context; | |
104 | LttvProcessState *process = ts->running_process[cpu]; | |
105 | ||
106 | e = ltt_tracefile_get_event(tfc->tf); | |
107 | ||
108 | result = lttv_iattribute_find_by_path(attributes, "filter/lttv_filter", | |
109 | LTTV_POINTER, &value_filter); | |
110 | g_assert(result); | |
111 | filter = (LttvFilter *)*(value_filter.v_pointer); | |
112 | ||
113 | /* call to the filter if available */ | |
114 | if (filter->head != NULL) { | |
115 | if (!lttv_filter_tree_parse(filter->head, e, tfc->tf, | |
116 | tfc->t_context->t, tfc, NULL, NULL)) { | |
117 | return FALSE; | |
118 | } | |
119 | } | |
120 | ||
121 | lttv_event_to_string(e, a_string, TRUE, !a_no_field_names, tfs); | |
122 | ||
123 | if (a_state) { | |
124 | g_string_append_printf(a_string, "%s ", | |
125 | g_quark_to_string(process->state->s)); | |
126 | } | |
127 | ||
128 | g_string_append_printf(a_string, "\n"); | |
129 | ||
130 | fputs(a_string->str, a_file); | |
131 | return FALSE; | |
132 | } | |
133 | ||
134 | void lttv_event_to_string(LttEvent *e, GString *string_buffer, gboolean mandatory_fields, | |
135 | gboolean field_names, LttvTracefileState *tfs) | |
136 | { | |
137 | struct marker_field *field; | |
138 | struct marker_info *info; | |
139 | LttTime time; | |
140 | ||
141 | static LttTime time_prev = {0, 0}; | |
142 | /* | |
143 | * TODO: | |
144 | * Added this static value into state.c and reset each time you do a | |
145 | * seek for using it in the GUI. | |
146 | */ | |
147 | LttTime elapse; | |
148 | const char *fmt; | |
149 | int i; | |
150 | int len; | |
151 | guint cpu = tfs->cpu; | |
152 | LttvTraceState *ts = (LttvTraceState *)tfs->parent.t_context; | |
153 | LttvProcessState *process = ts->running_process[cpu]; | |
154 | ||
155 | info = marker_get_info_from_id(tfs->parent.tf->mdata, e->event_id); | |
156 | if (mandatory_fields) { | |
157 | time = ltt_event_time(e); | |
158 | /* Calculate elapsed time between current and previous event */ | |
159 | if (time_prev.tv_sec == 0 && time_prev.tv_nsec == 0) { | |
160 | time_prev = ltt_event_time(e); | |
161 | elapse.tv_sec = 0; | |
162 | elapse.tv_nsec = 0; | |
163 | /* | |
164 | * TODO: | |
165 | * Keep in mind that you should add the ability to | |
166 | * restore the previous event time to state.c if you | |
167 | * want to reuse this code into the GUI. | |
168 | */ | |
169 | } else { | |
170 | elapse = ltt_time_sub(time, time_prev); | |
171 | time_prev = time; | |
172 | } | |
173 | } | |
174 | if (a_text) { | |
175 | /* textDump format (used with -T command option) */ | |
176 | fmt = textDump_format; | |
177 | } else if (a_strace) { | |
178 | /* strace-like format (used with -S command option) */ | |
179 | fmt = strace_format; | |
180 | } else if (!a_format) { | |
181 | /* Default format (used if no option) */ | |
182 | fmt = default_format; | |
183 | } else { | |
184 | /* | |
185 | * formattedDump format | |
186 | * (used with -F command option following by the desired format) | |
187 | */ | |
188 | fmt = a_format; | |
189 | } | |
190 | ||
191 | g_string_set_size(string_buffer, 0); | |
192 | /* | |
193 | * Switch case: | |
194 | * all '%-' are replaced by the desired value in 'string_buffer' | |
195 | */ | |
196 | len = strlen(fmt); | |
197 | for (i = 0; i < len; i++) { | |
198 | if (fmt[i] == '%') { | |
199 | switch (fmt[++i]) { | |
200 | case 't': | |
201 | g_string_append_printf(string_buffer, | |
202 | "%ld:%02ld:%02ld.%09ld", | |
203 | time.tv_sec/3600, | |
204 | (time.tv_sec%3600)/60, | |
205 | time.tv_sec%60, | |
206 | time.tv_nsec); | |
207 | break; | |
208 | case 'c': | |
209 | g_string_append(string_buffer, | |
210 | g_quark_to_string(ltt_tracefile_name(tfs->parent.tf))); | |
211 | break; | |
212 | case 'e': | |
213 | g_string_append(string_buffer, | |
214 | g_quark_to_string(info->name)); | |
215 | break; | |
216 | case 'd': | |
217 | g_string_append_printf(string_buffer, "%u", | |
218 | process->pid); | |
219 | break; | |
220 | case 's': | |
221 | g_string_append_printf(string_buffer, "%ld", | |
222 | time.tv_sec); | |
223 | break; | |
224 | case 'n': | |
225 | g_string_append_printf(string_buffer, "%ld", | |
226 | time.tv_nsec); | |
227 | break; | |
228 | case 'i': | |
229 | g_string_append_printf(string_buffer, "%u", | |
230 | process->ppid); | |
231 | break; | |
232 | case 'g': | |
233 | g_string_append_printf(string_buffer, "%u", | |
234 | process->tgid); | |
235 | break; | |
236 | case 'p': | |
237 | g_string_append(string_buffer, | |
238 | g_quark_to_string(process->name)); | |
239 | break; | |
240 | case 'b': | |
241 | g_string_append_printf(string_buffer, "%u", | |
242 | process->brand); | |
243 | break; | |
244 | case 'u': | |
245 | g_string_append_printf(string_buffer, "%u", cpu); | |
246 | break; | |
247 | case 'l': | |
248 | g_string_append_printf(string_buffer, | |
249 | "%ld.%09ld", | |
250 | elapse.tv_sec, elapse.tv_nsec); | |
251 | break; | |
252 | case 'a': | |
253 | g_string_append(string_buffer, | |
254 | g_quark_to_string(process->state->t)); | |
255 | break; | |
256 | case 'm': | |
257 | { | |
258 | /* | |
259 | * Get and print markers and tracepoints fields | |
260 | * into 'string_buffer' | |
261 | */ | |
262 | if (marker_get_num_fields(info) == 0) | |
263 | break; | |
264 | for (field = marker_get_field(info, 0); | |
265 | field != marker_get_field(info, marker_get_num_fields(info)); | |
266 | field++) { | |
267 | if (field != marker_get_field(info, 0)) { | |
268 | g_string_append(string_buffer, ", "); | |
269 | } | |
270 | ||
271 | lttv_print_field(e, field, string_buffer, field_names, tfs); | |
272 | } | |
273 | } | |
274 | break; | |
275 | case 'r': | |
276 | g_string_append(string_buffer, g_quark_to_string( | |
277 | ltt_trace_name(ltt_tracefile_get_trace(tfs->parent.tf)))); | |
278 | break; | |
279 | case '%': | |
280 | g_string_append_c(string_buffer, '%'); | |
281 | break; | |
282 | case 'y': | |
283 | g_string_append_printf(string_buffer, | |
284 | "0x%" PRIx64, | |
285 | process->current_function); | |
286 | break; | |
287 | } | |
288 | } else { | |
289 | /* Copy every character if different of '%' */ | |
290 | g_string_append_c(string_buffer, fmt[i]); | |
291 | } | |
292 | } | |
293 | } | |
294 | ||
295 | static void init() | |
296 | { | |
297 | gboolean result; | |
298 | ||
299 | LttvAttributeValue value; | |
300 | ||
301 | LttvIAttribute *attributes = LTTV_IATTRIBUTE(lttv_global_attributes()); | |
302 | ||
303 | g_info("Init formattedDump.c"); | |
304 | ||
305 | a_string = g_string_new(""); | |
306 | ||
307 | a_file_name = NULL; | |
308 | lttv_option_add("output", 'o', | |
309 | "output file where the text is written", | |
310 | "file name", | |
311 | LTTV_OPT_STRING, &a_file_name, NULL, NULL); | |
312 | ||
313 | a_text = FALSE; | |
314 | lttv_option_add("text", 'T', | |
315 | "output the textDump format", | |
316 | "", | |
317 | LTTV_OPT_NONE, &a_text, NULL, NULL); | |
318 | ||
319 | a_strace = FALSE; | |
320 | lttv_option_add("strace", 'S', | |
321 | "output a \"strace-like\" format", | |
322 | "", | |
323 | LTTV_OPT_NONE, &a_strace, NULL, NULL); | |
324 | ||
325 | a_format = NULL; | |
326 | lttv_option_add("format", 'F', | |
327 | "output the desired format\n" | |
328 | " FORMAT controls the output. " | |
329 | "Interpreted sequences are:\n" | |
330 | "\n" | |
331 | " %c channel name\n" | |
332 | " %p process name\n" | |
333 | " %e event name\n" | |
334 | " %r path to trace\n" | |
335 | " %t timestamp (e.g., 2:08:54.025684145)\n" | |
336 | " %s seconds\n" | |
337 | " %n nanoseconds\n" | |
338 | " %l elapsed time with the previous event\n" | |
339 | " %d pid\n" | |
340 | " %i ppid\n" | |
341 | " %g tgid\n" | |
342 | " %u cpu\n" | |
343 | " %b brand\n" | |
344 | " %a state\n" | |
345 | " %y memory address\n" | |
346 | " %m markers and tracepoints fields\n", | |
347 | "format string (e.g., \"channel:%c event:%e process:%p\")", | |
348 | LTTV_OPT_STRING, &a_format, NULL, NULL); | |
349 | ||
350 | result = lttv_iattribute_find_by_path(attributes, "hooks/event", | |
351 | LTTV_POINTER, &value); | |
352 | g_assert(result); | |
353 | event_hook = *(value.v_pointer); | |
354 | g_assert(event_hook); | |
355 | lttv_hooks_add(event_hook, write_event_content, NULL, LTTV_PRIO_DEFAULT); | |
356 | ||
357 | result = lttv_iattribute_find_by_path(attributes, "hooks/traceset/before", | |
358 | LTTV_POINTER, &value); | |
359 | g_assert(result); | |
360 | before_traceset = *(value.v_pointer); | |
361 | g_assert(before_traceset); | |
362 | lttv_hooks_add(before_traceset, open_output_file, NULL, | |
363 | LTTV_PRIO_DEFAULT); | |
364 | ||
365 | } | |
366 | ||
367 | static void destroy() | |
368 | { | |
369 | g_info("Destroy formattedDump"); | |
370 | ||
371 | lttv_option_remove("format"); | |
372 | ||
373 | lttv_option_remove("output"); | |
374 | ||
375 | lttv_option_remove("text"); | |
376 | ||
377 | lttv_option_remove("strace"); | |
378 | ||
379 | g_string_free(a_string, TRUE); | |
380 | ||
381 | lttv_hooks_remove_data(event_hook, write_event_content, NULL); | |
382 | ||
383 | lttv_hooks_remove_data(before_traceset, open_output_file, NULL); | |
384 | ||
385 | } | |
386 | ||
387 | ||
388 | LTTV_MODULE("formattedDump", "Print events with desired format in a file", | |
389 | "Produce a detailed formatted text printout of a trace", | |
390 | init, destroy, "batchAnalysis", "option") |