59121f0c |
1 | |
2 | #include "ltt_module.h" |
3 | |
4 | /* This module dumps all events in a simple ascii format */ |
5 | |
6 | static gboolean |
7 | ascii_dump = FALSE, |
8 | syscall_stats = FALSE; |
9 | |
10 | static gchar *dump_file = NULL; |
11 | |
12 | static FILE *dump_fp = stdout; |
13 | |
14 | struct poptOption |
15 | ascii_dump_option = { "ascii-dump", 'd', POPT_ARG_NONE, &ascii_dump, 0}, |
16 | ascii_dump_option = { "dump-file", 'f', POPT_ARG_STRING, &dump_file, 0}, |
17 | syscall_stats_option = { "syscall-stats", 's', POPT_ARG_NONE, |
18 | &syscall_stats, 0}; |
19 | |
20 | static void after_options_hook(gpointer hook_data, gpointer call_data); |
21 | |
22 | static void before_trace_hook(gpointer hook_data, gpointer call_data); |
23 | |
24 | static void after_trace_hook(gpointer hook_data, gpointer call_data); |
25 | |
26 | static void events_hook(gpointer hook_data, gpointer call_data); |
27 | |
28 | void init(int argc, char **argv) |
29 | { |
30 | ltt_add_command_option(&ascii_dump_option); |
31 | ltt_add_command_option(&syscall_stats_option); |
32 | ltt_add_hook(ltt_after_options_hooks,after_options_hook,NULL) |
33 | } |
34 | |
35 | /* Check the command line options and insert hooks to do the work */ |
36 | |
37 | static void after_options_hook(gpointer hook_data, gpointer call_data) |
38 | { |
39 | if(ascii_dump_option || syscall_stats) { |
40 | ltt_add_hook(ltt_before_process_each_trace_hooks,before_trace_hook,NULL); |
41 | if(dump_file != NULL) { |
42 | dump_fp = fopen(dump_file,"w"); |
43 | if(dump_fp == NULL) g_critical("cannot open output file %s",dump_file); |
44 | } |
45 | ltt_add_hook(ltt_after_process_each_trace_hooks,after_trace_hook,NULL); |
46 | } |
47 | } |
48 | |
49 | /* Insert the hooks to print the events and compute and print the statistics */ |
50 | |
51 | static unsigned *eventsCounters; |
52 | |
53 | struct CPUState { |
54 | lttProcess *current_process; |
55 | lttStatKey *key; |
56 | lttTime lastTime; |
57 | } *CPUStates; |
58 | |
59 | static void before_trace_hook(gpointer hook_data, gpointer call_data) { |
60 | ltt_add_hook(ltt_trace_events_hooks,events_hooks,NULL); |
61 | fprintf(dump_fp,"Trace %s\n",(struct trace *)call_data->name); |
62 | |
63 | if(ascii_dump) fprintf(dump_fp,"\nEvents\n"); |
64 | |
65 | /* To gather stats, register a few hooks */ |
66 | |
67 | if(syscall_stats) { |
68 | eventsCounters = g_new0(unsigned,nbEventType); |
69 | CPUStates = g_new0(struct CPUState, nbCPU); |
70 | /* initialize the state of each CPU and associated process */ |
71 | CHECK |
72 | } |
73 | } |
74 | |
75 | /* Print the events */ |
76 | |
77 | static void events_hook(gpointer hook_data, gpointer call_data) |
78 | { |
79 | event_struct event; |
80 | |
81 | int i; |
82 | |
83 | event = (struct_event *)call_data; |
84 | |
85 | if(ascii_dump) { |
86 | fprintf(dump_fp,"\n%s.%s t=%d.%d CPU%d",event->facility_handle->name, |
87 | event->event_handle->name, event->time.tv_seconds, |
88 | event->time.tv_nanoseconds,event->CPU_id); |
89 | |
90 | for(i = 0 ; i < event->base_field->nb_elements ; i++) { |
91 | field = event->base_field->fields + i; |
92 | fprintf(dump_fp," %s=",field->name); |
93 | switch(field->type) { |
94 | case INT: |
95 | fprintf(dump_fp,"%d",ltt_get_integer(field,event->data)); |
96 | break; |
97 | case UINT: |
98 | fprintf(dump_fp,"%u",ltt_get_uinteger(field,event->data)); |
99 | break; |
100 | case FLOAT: |
101 | fprintf(dump_fp,"%lg",ltt_get_float(field,event->data)); |
102 | break; |
103 | case DOUBLE: |
104 | fprintf(dump_fp,"%g",ltt_get_double(field,event->data)); |
105 | break; |
106 | case STRING: |
107 | fprintf(dump_fp,"%s",ltt_get_string(field,event->data)); |
108 | break; |
109 | case ENUM: |
110 | fprintf(dump_fp,"%d",ltt_get_integer(field,event->data)); |
111 | break; |
112 | case ARRAY: |
113 | fprintf(dump_fp,"<nested array>"); |
114 | break; |
115 | case SEQUENCE: |
116 | fprintf(dump_fp,"<nested sequence>"); |
117 | break; |
118 | case STRUCT: |
119 | fprintf(dump_fp,"<nested struct>"); |
120 | break; |
121 | } |
122 | } |
123 | } |
124 | |
125 | /* Collect statistics about each event type */ |
126 | |
127 | if(syscall_stats) { |
128 | /* Get the key for the corresponding CPU. It already contains the |
129 | path components for the ip, CPU, process, state, subState. |
130 | We add the event id and increment the statistic with that key. */ |
131 | |
132 | key = (GQuark *)CPUStates[event->CPUid]->key1; |
133 | path = key->data; |
134 | path[5] = eventsQuark[event->id]; |
135 | pval = ltt_get_integer(currentStats,key); |
136 | (*pval)++; |
137 | |
138 | /* Count the time spent in the current state. Could be done only |
139 | at state changes to optimize. */ |
140 | |
141 | key = (GQuark *)CPUStates[event->CPUid]->key2; |
142 | path = key->data; |
143 | ptime = ltt_get_time(currentStats,key); |
144 | (*ptime) = ltt_add_time((*ptime),ltt_sub_time(lastTime,event->time)); |
145 | } |
146 | } |
147 | |
148 | /* Specific hooks to note process and state changes, compute the following values: number of bytes read/written, |
149 | time elapsed, user, system, waiting, time spent in each system call, |
150 | name for each process. */ |
151 | |
152 | maintain the process table, process state, last time... what we are waiting for |
153 | |
154 | syscall_entry_hook |
155 | syscall_exit_hook |
156 | trap_entry_hook |
157 | trap_exit_hook |
158 | irq_entry_hook |
159 | irq_exit_hook |
160 | sched_change_hook -> not waiting |
161 | fork_hook -> wait fork |
162 | wait_hook -> waiting |
163 | wakeup_hook -> not waiting add up waiting time |
164 | exit_hook |
165 | exec_hook -> note file name |
166 | open_hook -> keep track of fd/name |
167 | close_hook -> keep track of fd |
168 | read_hook -> bytes read, if server CPU for client... |
169 | write_hook -> bytes written |
170 | select_hook -> wait reason |
171 | poll_hook -> wait reason |
172 | mmap_hook -> keep track of fd |
173 | munmap_hook -> keep track of fd |
174 | setitimer_hook -> wait reason |
175 | settimeout_hook -> wait reason |
176 | sockcreate_hook -> client/server |
177 | sockbind_hook -> client/server |
178 | sockaccept_hook -> client/server |
179 | sockconnect_hook -> client/server |
180 | /* Close the output file and print the statistics, globally for all CPUs and |
181 | processes, per CPU, per process. */ |
182 | |
183 | static void after_trace_hook(gpointer hook_data, gpointer call_data) |
184 | { |
185 | lttTrace t; |
186 | |
187 | unsigned nbEvents = 0; |
188 | |
189 | t = (lttTrace *)call_data; |
190 | |
191 | fprintf(dump_fp,"\n"); |
192 | fclose(dump_fp); |
193 | |
194 | if(syscall_stats) { |
195 | fprintf(dump_fp,"\n\nStatistics\n\n"); |
196 | |
197 | /* Trace start, end and duration */ |
198 | |
199 | fprintf(dump_fp,"Trace started %s, ended %s, duration %s", |
200 | ltt_format_time(t->startTime),ltt_format_time(t->endTime), |
201 | ltt_format_time(ltt_sub_time(t->endTime,t->startTime))); |
202 | |
203 | /* Number of events of each type */ |
204 | |
205 | for(i = 0 ; i < t->nbEventTypes ; i++) { |
206 | nbEvents += eventsCounters[i]; |
207 | if(eventsCounters[i] > 0) |
208 | fprintf(dump_fp,"%s: %u\n",t->types[i]->name,eventsCounters[i]); |
209 | } |
210 | fprintf(dump_fp,"\n\nTotal number of events: %u\n",nbEvents); |
211 | |
212 | /* Print the details for each process */ |
213 | } |
214 | } |
215 | |
216 | |
217 | |