continue implementation of resourceview
[lttv.git] / ltt / branches / poly / lttv / lttv / state.c
1 /* This file is part of the Linux Trace Toolkit viewer
2 * Copyright (C) 2003-2004 Michel Dagenais
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License Version 2 as
6 * published by the Free Software Foundation;
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
16 * MA 02111-1307, USA.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include <config.h>
21 #endif
22
23 #include <lttv/lttv.h>
24 #include <lttv/module.h>
25 #include <lttv/state.h>
26 #include <ltt/facility.h>
27 #include <ltt/trace.h>
28 #include <ltt/event.h>
29 #include <ltt/type.h>
30 #include <stdio.h>
31 #include <string.h>
32
33 /* Comment :
34 * Mathieu Desnoyers
35 * usertrace is there only to be able to update the current CPU of the
36 * usertraces when there is a schedchange. it is a way to link the ProcessState
37 * to the associated usertrace. Link only created upon thread creation.
38 *
39 * The cpu id is necessary : it gives us back the current ProcessState when we
40 * are considering data from the usertrace.
41 */
42
43 #define PREALLOCATED_EXECUTION_STACK 10
44
45 /* Facilities Quarks */
46
47 GQuark
48 LTT_FACILITY_KERNEL,
49 LTT_FACILITY_KERNEL_ARCH,
50 LTT_FACILITY_LIST,
51 LTT_FACILITY_FS,
52 LTT_FACILITY_USER_GENERIC;
53
54 /* Events Quarks */
55
56 GQuark
57 LTT_EVENT_SYSCALL_ENTRY,
58 LTT_EVENT_SYSCALL_EXIT,
59 LTT_EVENT_TRAP_ENTRY,
60 LTT_EVENT_TRAP_EXIT,
61 LTT_EVENT_IRQ_ENTRY,
62 LTT_EVENT_IRQ_EXIT,
63 LTT_EVENT_SOFT_IRQ_ENTRY,
64 LTT_EVENT_SOFT_IRQ_EXIT,
65 LTT_EVENT_SCHED_SCHEDULE,
66 LTT_EVENT_PROCESS_FORK,
67 LTT_EVENT_KTHREAD_CREATE,
68 LTT_EVENT_PROCESS_EXIT,
69 LTT_EVENT_PROCESS_FREE,
70 LTT_EVENT_EXEC,
71 LTT_EVENT_PROCESS_STATE,
72 LTT_EVENT_STATEDUMP_END,
73 LTT_EVENT_FUNCTION_ENTRY,
74 LTT_EVENT_FUNCTION_EXIT,
75 LTT_EVENT_THREAD_BRAND;
76
77 /* Fields Quarks */
78
79 GQuark
80 LTT_FIELD_SYSCALL_ID,
81 LTT_FIELD_TRAP_ID,
82 LTT_FIELD_IRQ_ID,
83 LTT_FIELD_SOFT_IRQ_ID,
84 LTT_FIELD_PREV_PID,
85 LTT_FIELD_NEXT_PID,
86 LTT_FIELD_PREV_STATE,
87 LTT_FIELD_PARENT_PID,
88 LTT_FIELD_CHILD_PID,
89 LTT_FIELD_PID,
90 LTT_FIELD_TGID,
91 LTT_FIELD_CHILD_TGID,
92 LTT_FIELD_FILENAME,
93 LTT_FIELD_NAME,
94 LTT_FIELD_TYPE,
95 LTT_FIELD_MODE,
96 LTT_FIELD_SUBMODE,
97 LTT_FIELD_STATUS,
98 LTT_FIELD_THIS_FN,
99 LTT_FIELD_CALL_SITE;
100
101 LttvExecutionMode
102 LTTV_STATE_MODE_UNKNOWN,
103 LTTV_STATE_USER_MODE,
104 LTTV_STATE_SYSCALL,
105 LTTV_STATE_TRAP,
106 LTTV_STATE_IRQ,
107 LTTV_STATE_SOFT_IRQ;
108
109 LttvExecutionSubmode
110 LTTV_STATE_SUBMODE_UNKNOWN,
111 LTTV_STATE_SUBMODE_NONE;
112
113 LttvProcessStatus
114 LTTV_STATE_UNNAMED,
115 LTTV_STATE_WAIT_FORK,
116 LTTV_STATE_WAIT_CPU,
117 LTTV_STATE_EXIT,
118 LTTV_STATE_ZOMBIE,
119 LTTV_STATE_WAIT,
120 LTTV_STATE_RUN,
121 LTTV_STATE_DEAD;
122
123 GQuark
124 LTTV_STATE_UNBRANDED;
125
126 LttvProcessType
127 LTTV_STATE_USER_THREAD,
128 LTTV_STATE_KERNEL_THREAD;
129
130 LttvCPUMode
131 LTTV_CPU_UNKNOWN,
132 LTTV_CPU_IDLE,
133 LTTV_CPU_BUSY;
134
135 static GQuark
136 LTTV_STATE_TRACEFILES,
137 LTTV_STATE_PROCESSES,
138 LTTV_STATE_PROCESS,
139 LTTV_STATE_RUNNING_PROCESS,
140 LTTV_STATE_EVENT,
141 LTTV_STATE_SAVED_STATES,
142 LTTV_STATE_SAVED_STATES_TIME,
143 LTTV_STATE_TIME,
144 LTTV_STATE_HOOKS,
145 LTTV_STATE_NAME_TABLES,
146 LTTV_STATE_TRACE_STATE_USE_COUNT;
147
148 static void create_max_time(LttvTraceState *tcs);
149
150 static void get_max_time(LttvTraceState *tcs);
151
152 static void free_max_time(LttvTraceState *tcs);
153
154 static void create_name_tables(LttvTraceState *tcs);
155
156 static void get_name_tables(LttvTraceState *tcs);
157
158 static void free_name_tables(LttvTraceState *tcs);
159
160 static void free_saved_state(LttvTraceState *tcs);
161
162 static void lttv_state_free_process_table(GHashTable *processes);
163
164 static void lttv_trace_states_read_raw(LttvTraceState *tcs, FILE *fp,
165 GPtrArray *quarktable);
166
167 void lttv_state_save(LttvTraceState *self, LttvAttribute *container)
168 {
169 LTTV_TRACE_STATE_GET_CLASS(self)->state_save(self, container);
170 }
171
172
173 void lttv_state_restore(LttvTraceState *self, LttvAttribute *container)
174 {
175 LTTV_TRACE_STATE_GET_CLASS(self)->state_restore(self, container);
176 }
177
178
179 void lttv_state_state_saved_free(LttvTraceState *self,
180 LttvAttribute *container)
181 {
182 LTTV_TRACE_STATE_GET_CLASS(self)->state_saved_free(self, container);
183 }
184
185
186 guint process_hash(gconstpointer key)
187 {
188 guint pid = ((const LttvProcessState *)key)->pid;
189 return (pid>>8 ^ pid>>4 ^ pid>>2 ^ pid) ;
190 }
191
192
193 /* If the hash table hash function is well distributed,
194 * the process_equal should compare different pid */
195 gboolean process_equal(gconstpointer a, gconstpointer b)
196 {
197 const LttvProcessState *process_a, *process_b;
198 gboolean ret = TRUE;
199
200 process_a = (const LttvProcessState *)a;
201 process_b = (const LttvProcessState *)b;
202
203 if(likely(process_a->pid != process_b->pid)) ret = FALSE;
204 else if(likely(process_a->pid == 0 &&
205 process_a->cpu != process_b->cpu)) ret = FALSE;
206
207 return ret;
208 }
209
210 static void delete_usertrace(gpointer key, gpointer value, gpointer user_data)
211 {
212 g_tree_destroy((GTree*)value);
213 }
214
215 static void lttv_state_free_usertraces(GHashTable *usertraces)
216 {
217 g_hash_table_foreach(usertraces, delete_usertrace, NULL);
218 g_hash_table_destroy(usertraces);
219 }
220
221
222
223 static void
224 restore_init_state(LttvTraceState *self)
225 {
226 guint i, nb_cpus;
227
228 LttvTracefileState *tfcs;
229
230 LttTime start_time, end_time;
231
232 /* Free the process tables */
233 if(self->processes != NULL) lttv_state_free_process_table(self->processes);
234 if(self->usertraces != NULL) lttv_state_free_usertraces(self->usertraces);
235 self->processes = g_hash_table_new(process_hash, process_equal);
236 self->usertraces = g_hash_table_new(g_direct_hash, g_direct_equal);
237 self->nb_event = 0;
238
239 /* Seek time to beginning */
240 // Mathieu : fix : don't seek traceset here : causes inconsistency in seek
241 // closest. It's the tracecontext job to seek the trace to the beginning
242 // anyway : the init state might be used at the middle of the trace as well...
243 //g_tree_destroy(self->parent.ts_context->pqueue);
244 //self->parent.ts_context->pqueue = g_tree_new(compare_tracefile);
245
246 ltt_trace_time_span_get(self->parent.t, &start_time, &end_time);
247
248 //lttv_process_trace_seek_time(&self->parent, ltt_time_zero);
249
250 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
251
252 /* Put the per cpu running_process to beginning state : process 0. */
253 for(i=0; i< nb_cpus; i++) {
254 LttvExecutionState *es;
255 self->running_process[i] = lttv_state_create_process(self, NULL, i, 0, 0,
256 LTTV_STATE_UNNAMED, &start_time);
257 /* We are not sure is it's a kernel thread or normal thread, put the
258 * bottom stack state to unknown */
259 self->running_process[i]->execution_stack =
260 g_array_set_size(self->running_process[i]->execution_stack, 1);
261 es = self->running_process[i]->state =
262 &g_array_index(self->running_process[i]->execution_stack,
263 LttvExecutionState, 0);
264 es->t = LTTV_STATE_MODE_UNKNOWN;
265 es->s = LTTV_STATE_UNNAMED;
266
267 //self->running_process[i]->state->s = LTTV_STATE_RUN;
268 self->running_process[i]->cpu = i;
269 }
270
271 #if 0
272 nb_tracefile = self->parent.tracefiles->len;
273
274 for(i = 0 ; i < nb_tracefile ; i++) {
275 tfcs =
276 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
277 LttvTracefileContext*, i));
278 ltt_trace_time_span_get(self->parent.t, &tfcs->parent.timestamp, NULL);
279 // tfcs->saved_position = 0;
280 tfcs->process = lttv_state_create_process(tfcs, NULL,0);
281 tfcs->process->state->s = LTTV_STATE_RUN;
282 tfcs->process->last_cpu = tfcs->cpu_name;
283 tfcs->process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfcs)->tf);
284 }
285 #endif //0
286 }
287
288 //static LttTime time_zero = {0,0};
289
290 static gint compare_usertraces(gconstpointer a, gconstpointer b,
291 gpointer user_data)
292 {
293 const LttTime *t1 = (const LttTime *)a;
294 const LttTime *t2 = (const LttTime *)b;
295
296 return ltt_time_compare(*t1, *t2);
297 }
298
299 static void free_usertrace_key(gpointer data)
300 {
301 g_free(data);
302 }
303
304 #define MAX_STRING_LEN 4096
305
306 static void
307 state_load_saved_states(LttvTraceState *tcs)
308 {
309 FILE *fp;
310 GPtrArray *quarktable;
311 char *trace_path;
312 char path[PATH_MAX];
313 guint count;
314 guint i;
315 tcs->has_precomputed_states = FALSE;
316 GQuark q;
317 gchar *string;
318 gint hdr;
319 gchar buf[MAX_STRING_LEN];
320 guint len;
321
322 trace_path = g_quark_to_string(ltt_trace_name(tcs->parent.t));
323 strncpy(path, trace_path, PATH_MAX-1);
324 count = strnlen(trace_path, PATH_MAX-1);
325 // quarktable : open, test
326 strncat(path, "/precomputed/quarktable", PATH_MAX-count-1);
327 fp = fopen(path, "r");
328 if(!fp) return;
329 quarktable = g_ptr_array_sized_new(4096);
330
331 /* Index 0 is null */
332 hdr = fgetc(fp);
333 if(hdr == EOF) return;
334 g_assert(hdr == HDR_QUARKS);
335 q = 1;
336 do {
337 hdr = fgetc(fp);
338 if(hdr == EOF) break;
339 g_assert(hdr == HDR_QUARK);
340 g_ptr_array_set_size(quarktable, q+1);
341 i=0;
342 while(1) {
343 fread(&buf[i], sizeof(gchar), 1, fp);
344 if(buf[i] == '\0' || feof(fp)) break;
345 i++;
346 }
347 len = strnlen(buf, MAX_STRING_LEN-1);
348 g_ptr_array_index (quarktable, q) = g_new(gchar, len+1);
349 strncpy(g_ptr_array_index (quarktable, q), buf, len+1);
350 q++;
351 } while(1);
352
353 fclose(fp);
354
355 // saved_states : open, test
356 strncpy(path, trace_path, PATH_MAX-1);
357 count = strnlen(trace_path, PATH_MAX-1);
358 strncat(path, "/precomputed/states", PATH_MAX-count-1);
359 fp = fopen(path, "r");
360 if(!fp) return;
361
362 hdr = fgetc(fp);
363 if(hdr != HDR_TRACE) goto end;
364
365 lttv_trace_states_read_raw(tcs, fp, quarktable);
366
367 tcs->has_precomputed_states = TRUE;
368
369 end:
370 fclose(fp);
371
372 /* Free the quarktable */
373 for(i=0; i<quarktable->len; i++) {
374 string = g_ptr_array_index (quarktable, i);
375 g_free(string);
376 }
377 g_ptr_array_free(quarktable, TRUE);
378 return;
379 }
380
381 static void
382 init(LttvTracesetState *self, LttvTraceset *ts)
383 {
384 guint i, j, nb_trace, nb_tracefile;
385
386 LttvTraceContext *tc;
387
388 LttvTraceState *tcs;
389
390 LttvTracefileState *tfcs;
391
392 LttvAttributeValue v;
393
394 LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))->
395 init((LttvTracesetContext *)self, ts);
396
397 nb_trace = lttv_traceset_number(ts);
398 for(i = 0 ; i < nb_trace ; i++) {
399 tc = self->parent.traces[i];
400 tcs = LTTV_TRACE_STATE(tc);
401 tcs->save_interval = LTTV_STATE_SAVE_INTERVAL;
402 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_TRACE_STATE_USE_COUNT,
403 LTTV_UINT, &v);
404 (*v.v_uint)++;
405
406 if(*(v.v_uint) == 1) {
407 create_name_tables(tcs);
408 create_max_time(tcs);
409 }
410 get_name_tables(tcs);
411 get_max_time(tcs);
412
413 nb_tracefile = tc->tracefiles->len;
414 tcs->processes = NULL;
415 tcs->usertraces = NULL;
416 tcs->running_process = g_new(LttvProcessState*,
417 ltt_trace_get_num_cpu(tc->t));
418 tcs->cpu_states = g_new(LttvCPUState,
419 ltt_trace_get_num_cpu(tc->t));
420 restore_init_state(tcs);
421 for(j = 0 ; j < nb_tracefile ; j++) {
422 tfcs =
423 LTTV_TRACEFILE_STATE(g_array_index(tc->tracefiles,
424 LttvTracefileContext*, j));
425 tfcs->tracefile_name = ltt_tracefile_name(tfcs->parent.tf);
426 tfcs->cpu = ltt_tracefile_cpu(tfcs->parent.tf);
427 tfcs->cpu_state = &(tcs->cpu_states[tfcs->cpu]);
428 if(ltt_tracefile_tid(tfcs->parent.tf) != 0) {
429 /* It's a Usertrace */
430 guint tid = ltt_tracefile_tid(tfcs->parent.tf);
431 GTree *usertrace_tree = (GTree*)g_hash_table_lookup(tcs->usertraces,
432 (gconstpointer)tid);
433 if(!usertrace_tree) {
434 usertrace_tree = g_tree_new_full(compare_usertraces,
435 NULL, free_usertrace_key, NULL);
436 g_hash_table_insert(tcs->usertraces,
437 (gpointer)tid, usertrace_tree);
438 }
439 LttTime *timestamp = g_new(LttTime, 1);
440 *timestamp = ltt_interpolate_time_from_tsc(tfcs->parent.tf,
441 ltt_tracefile_creation(tfcs->parent.tf));
442 g_tree_insert(usertrace_tree, timestamp, tfcs);
443 }
444 }
445
446 /* See if the trace has saved states */
447 state_load_saved_states(tcs);
448 }
449 }
450
451 static void
452 fini(LttvTracesetState *self)
453 {
454 guint i, nb_trace;
455
456 LttvTraceState *tcs;
457
458 LttvTracefileState *tfcs;
459
460 LttvAttributeValue v;
461
462 nb_trace = lttv_traceset_number(LTTV_TRACESET_CONTEXT(self)->ts);
463 for(i = 0 ; i < nb_trace ; i++) {
464 tcs = (LttvTraceState *)(LTTV_TRACESET_CONTEXT(self)->traces[i]);
465 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_TRACE_STATE_USE_COUNT,
466 LTTV_UINT, &v);
467
468 g_assert(*(v.v_uint) != 0);
469 (*v.v_uint)--;
470
471 if(*(v.v_uint) == 0) {
472 free_name_tables(tcs);
473 free_max_time(tcs);
474 free_saved_state(tcs);
475 }
476 g_free(tcs->running_process);
477 tcs->running_process = NULL;
478 lttv_state_free_process_table(tcs->processes);
479 lttv_state_free_usertraces(tcs->usertraces);
480 tcs->processes = NULL;
481 tcs->usertraces = NULL;
482 }
483 LTTV_TRACESET_CONTEXT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))->
484 fini((LttvTracesetContext *)self);
485 }
486
487
488 static LttvTracesetContext *
489 new_traceset_context(LttvTracesetContext *self)
490 {
491 return LTTV_TRACESET_CONTEXT(g_object_new(LTTV_TRACESET_STATE_TYPE, NULL));
492 }
493
494
495 static LttvTraceContext *
496 new_trace_context(LttvTracesetContext *self)
497 {
498 return LTTV_TRACE_CONTEXT(g_object_new(LTTV_TRACE_STATE_TYPE, NULL));
499 }
500
501
502 static LttvTracefileContext *
503 new_tracefile_context(LttvTracesetContext *self)
504 {
505 return LTTV_TRACEFILE_CONTEXT(g_object_new(LTTV_TRACEFILE_STATE_TYPE, NULL));
506 }
507
508
509 /* Write the process state of the trace */
510
511 static void write_process_state(gpointer key, gpointer value,
512 gpointer user_data)
513 {
514 LttvProcessState *process;
515
516 LttvExecutionState *es;
517
518 FILE *fp = (FILE *)user_data;
519
520 guint i;
521 guint64 address;
522
523 process = (LttvProcessState *)value;
524 fprintf(fp,
525 " <PROCESS CORE=%p PID=%u TGID=%u PPID=%u TYPE=\"%s\" CTIME_S=%lu CTIME_NS=%lu ITIME_S=%lu ITIME_NS=%lu NAME=\"%s\" BRAND=\"%s\" CPU=\"%u\">\n",
526 process, process->pid, process->tgid, process->ppid,
527 g_quark_to_string(process->type),
528 process->creation_time.tv_sec,
529 process->creation_time.tv_nsec,
530 process->insertion_time.tv_sec,
531 process->insertion_time.tv_nsec,
532 g_quark_to_string(process->name),
533 g_quark_to_string(process->brand),
534 process->cpu);
535
536 for(i = 0 ; i < process->execution_stack->len; i++) {
537 es = &g_array_index(process->execution_stack, LttvExecutionState, i);
538 fprintf(fp, " <ES MODE=\"%s\" SUBMODE=\"%s\" ENTRY_S=%lu ENTRY_NS=%lu",
539 g_quark_to_string(es->t), g_quark_to_string(es->n),
540 es->entry.tv_sec, es->entry.tv_nsec);
541 fprintf(fp, " CHANGE_S=%lu CHANGE_NS=%lu STATUS=\"%s\"/>\n",
542 es->change.tv_sec, es->change.tv_nsec, g_quark_to_string(es->s));
543 }
544
545 for(i = 0 ; i < process->user_stack->len; i++) {
546 address = &g_array_index(process->user_stack, guint64, i);
547 fprintf(fp, " <USER_STACK ADDRESS=\"%llu\"/>\n",
548 address);
549 }
550
551 if(process->usertrace) {
552 fprintf(fp, " <USERTRACE NAME=\"%s\" CPU=%u\n/>",
553 g_quark_to_string(process->usertrace->tracefile_name),
554 process->usertrace->cpu);
555 }
556
557
558 fprintf(fp, " </PROCESS>\n");
559 }
560
561
562 void lttv_state_write(LttvTraceState *self, LttTime t, FILE *fp)
563 {
564 guint i, nb_tracefile, nb_block, offset;
565 guint64 tsc;
566
567 LttvTracefileState *tfcs;
568
569 LttTracefile *tf;
570
571 LttEventPosition *ep;
572
573 guint nb_cpus;
574
575 ep = ltt_event_position_new();
576
577 fprintf(fp,"<PROCESS_STATE TIME_S=%lu TIME_NS=%lu>\n", t.tv_sec, t.tv_nsec);
578
579 g_hash_table_foreach(self->processes, write_process_state, fp);
580
581 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
582 for(i=0;i<nb_cpus;i++) {
583 fprintf(fp," <CPU NUM=%u RUNNING_PROCESS=%u>\n",
584 i, self->running_process[i]->pid);
585 }
586
587 nb_tracefile = self->parent.tracefiles->len;
588
589 for(i = 0 ; i < nb_tracefile ; i++) {
590 tfcs =
591 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
592 LttvTracefileContext*, i));
593 fprintf(fp, " <TRACEFILE TIMESTAMP_S=%lu TIMESTAMP_NS=%lu",
594 tfcs->parent.timestamp.tv_sec,
595 tfcs->parent.timestamp.tv_nsec);
596 LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf);
597 if(e == NULL) fprintf(fp,"/>\n");
598 else {
599 ltt_event_position(e, ep);
600 ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc);
601 fprintf(fp, " BLOCK=%u OFFSET=%u TSC=%llu/>\n", nb_block, offset,
602 tsc);
603 }
604 }
605 g_free(ep);
606 fprintf(fp,"</PROCESS_STATE>\n");
607 }
608
609
610 static void write_process_state_raw(gpointer key, gpointer value,
611 gpointer user_data)
612 {
613 LttvProcessState *process;
614
615 LttvExecutionState *es;
616
617 FILE *fp = (FILE *)user_data;
618
619 guint i;
620 guint64 address;
621
622 process = (LttvProcessState *)value;
623 fputc(HDR_PROCESS, fp);
624 //fwrite(&header, sizeof(header), 1, fp);
625 //fprintf(fp, "%s", g_quark_to_string(process->type));
626 //fputc('\0', fp);
627 fwrite(&process->type, sizeof(process->type), 1, fp);
628 //fprintf(fp, "%s", g_quark_to_string(process->name));
629 //fputc('\0', fp);
630 fwrite(&process->name, sizeof(process->name), 1, fp);
631 //fprintf(fp, "%s", g_quark_to_string(process->brand));
632 //fputc('\0', fp);
633 fwrite(&process->brand, sizeof(process->brand), 1, fp);
634 fwrite(&process->pid, sizeof(process->pid), 1, fp);
635 fwrite(&process->tgid, sizeof(process->tgid), 1, fp);
636 fwrite(&process->ppid, sizeof(process->ppid), 1, fp);
637 fwrite(&process->cpu, sizeof(process->cpu), 1, fp);
638 fwrite(&process->creation_time, sizeof(process->creation_time), 1, fp);
639 fwrite(&process->insertion_time, sizeof(process->insertion_time), 1, fp);
640
641 #if 0
642 fprintf(fp,
643 " <PROCESS CORE=%p PID=%u TGID=%u PPID=%u TYPE=\"%s\" CTIME_S=%lu CTIME_NS=%lu ITIME_S=%lu ITIME_NS=%lu NAME=\"%s\" BRAND=\"%s\" CPU=\"%u\" PROCESS_TYPE=%u>\n",
644 process, process->pid, process->tgid, process->ppid,
645 g_quark_to_string(process->type),
646 process->creation_time.tv_sec,
647 process->creation_time.tv_nsec,
648 process->insertion_time.tv_sec,
649 process->insertion_time.tv_nsec,
650 g_quark_to_string(process->name),
651 g_quark_to_string(process->brand),
652 process->cpu);
653 #endif //0
654
655 for(i = 0 ; i < process->execution_stack->len; i++) {
656 es = &g_array_index(process->execution_stack, LttvExecutionState, i);
657
658 fputc(HDR_ES, fp);
659 //fprintf(fp, "%s", g_quark_to_string(es->t));
660 //fputc('\0', fp);
661 fwrite(&es->t, sizeof(es->t), 1, fp);
662 //fprintf(fp, "%s", g_quark_to_string(es->n));
663 //fputc('\0', fp);
664 fwrite(&es->n, sizeof(es->n), 1, fp);
665 //fprintf(fp, "%s", g_quark_to_string(es->s));
666 //fputc('\0', fp);
667 fwrite(&es->s, sizeof(es->s), 1, fp);
668 fwrite(&es->entry, sizeof(es->entry), 1, fp);
669 fwrite(&es->change, sizeof(es->change), 1, fp);
670 fwrite(&es->cum_cpu_time, sizeof(es->cum_cpu_time), 1, fp);
671 #if 0
672 fprintf(fp, " <ES MODE=\"%s\" SUBMODE=\"%s\" ENTRY_S=%lu ENTRY_NS=%lu",
673 g_quark_to_string(es->t), g_quark_to_string(es->n),
674 es->entry.tv_sec, es->entry.tv_nsec);
675 fprintf(fp, " CHANGE_S=%lu CHANGE_NS=%lu STATUS=\"%s\"/>\n",
676 es->change.tv_sec, es->change.tv_nsec, g_quark_to_string(es->s));
677 #endif //0
678 }
679
680 for(i = 0 ; i < process->user_stack->len; i++) {
681 address = &g_array_index(process->user_stack, guint64, i);
682 fputc(HDR_USER_STACK, fp);
683 fwrite(&address, sizeof(address), 1, fp);
684 #if 0
685 fprintf(fp, " <USER_STACK ADDRESS=\"%llu\"/>\n",
686 address);
687 #endif //0
688 }
689
690 if(process->usertrace) {
691 fputc(HDR_USERTRACE, fp);
692 //fprintf(fp, "%s", g_quark_to_string(process->usertrace->tracefile_name));
693 //fputc('\0', fp);
694 fwrite(&process->usertrace->tracefile_name,
695 sizeof(process->usertrace->tracefile_name), 1, fp);
696 fwrite(&process->usertrace->cpu, sizeof(process->usertrace->cpu), 1, fp);
697 #if 0
698 fprintf(fp, " <USERTRACE NAME=\"%s\" CPU=%u\n/>",
699 g_quark_to_string(process->usertrace->tracefile_name),
700 process->usertrace->cpu);
701 #endif //0
702 }
703
704 }
705
706
707 void lttv_state_write_raw(LttvTraceState *self, LttTime t, FILE *fp)
708 {
709 guint i, nb_tracefile, nb_block, offset;
710 guint64 tsc;
711
712 LttvTracefileState *tfcs;
713
714 LttTracefile *tf;
715
716 LttEventPosition *ep;
717
718 guint nb_cpus;
719
720 ep = ltt_event_position_new();
721
722 //fprintf(fp,"<PROCESS_STATE TIME_S=%lu TIME_NS=%lu>\n", t.tv_sec, t.tv_nsec);
723 fputc(HDR_PROCESS_STATE, fp);
724 fwrite(&t, sizeof(t), 1, fp);
725
726 g_hash_table_foreach(self->processes, write_process_state_raw, fp);
727
728 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
729 for(i=0;i<nb_cpus;i++) {
730 fputc(HDR_CPU, fp);
731 fwrite(&i, sizeof(i), 1, fp); /* cpu number */
732 fwrite(&self->running_process[i]->pid,
733 sizeof(self->running_process[i]->pid), 1, fp);
734 //fprintf(fp," <CPU NUM=%u RUNNING_PROCESS=%u>\n",
735 // i, self->running_process[i]->pid);
736 }
737
738 nb_tracefile = self->parent.tracefiles->len;
739
740 for(i = 0 ; i < nb_tracefile ; i++) {
741 tfcs =
742 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
743 LttvTracefileContext*, i));
744 // fprintf(fp, " <TRACEFILE TIMESTAMP_S=%lu TIMESTAMP_NS=%lu",
745 // tfcs->parent.timestamp.tv_sec,
746 // tfcs->parent.timestamp.tv_nsec);
747 fputc(HDR_TRACEFILE, fp);
748 fwrite(&tfcs->parent.timestamp, sizeof(tfcs->parent.timestamp), 1, fp);
749 /* Note : if timestamp if LTT_TIME_INFINITE, there will be no
750 * position following : end of trace */
751 LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf);
752 if(e != NULL) {
753 ltt_event_position(e, ep);
754 ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc);
755 //fprintf(fp, " BLOCK=%u OFFSET=%u TSC=%llu/>\n", nb_block, offset,
756 // tsc);
757 fwrite(&nb_block, sizeof(nb_block), 1, fp);
758 fwrite(&offset, sizeof(offset), 1, fp);
759 fwrite(&tsc, sizeof(tsc), 1, fp);
760 }
761 }
762 g_free(ep);
763 }
764
765
766 /* Read process state from a file */
767
768 /* Called because a HDR_PROCESS was found */
769 static void read_process_state_raw(LttvTraceState *self, FILE *fp,
770 GPtrArray *quarktable)
771 {
772 LttvExecutionState *es;
773 LttvProcessState *process, *parent_process;
774 LttvProcessState tmp;
775 GQuark tmpq;
776
777 guint i;
778 guint64 *address;
779 guint cpu;
780
781 /* TODO : check return value */
782 fread(&tmp.type, sizeof(tmp.type), 1, fp);
783 fread(&tmp.name, sizeof(tmp.name), 1, fp);
784 fread(&tmp.brand, sizeof(tmp.brand), 1, fp);
785 fread(&tmp.pid, sizeof(tmp.pid), 1, fp);
786 fread(&tmp.tgid, sizeof(tmp.tgid), 1, fp);
787 fread(&tmp.ppid, sizeof(tmp.ppid), 1, fp);
788 fread(&tmp.cpu, sizeof(tmp.cpu), 1, fp);
789 fread(&tmp.creation_time, sizeof(tmp.creation_time), 1, fp);
790 fread(&tmp.insertion_time, sizeof(tmp.insertion_time), 1, fp);
791
792 if(tmp.pid == 0) {
793 process = lttv_state_find_process(self, tmp.cpu, tmp.pid);
794 } else {
795 /* We must link to the parent */
796 parent_process = lttv_state_find_process_or_create(self, ANY_CPU, tmp.ppid,
797 &ltt_time_zero);
798 process = lttv_state_find_process(self, ANY_CPU, tmp.pid);
799 if(process == NULL) {
800 process = lttv_state_create_process(self, parent_process, tmp.cpu,
801 tmp.pid, tmp.tgid,
802 g_quark_from_string((gchar*)g_ptr_array_index(quarktable, tmp.name)),
803 &tmp.creation_time);
804 }
805 }
806 process->insertion_time = tmp.insertion_time;
807 process->creation_time = tmp.creation_time;
808 process->type = g_quark_from_string(
809 (gchar*)g_ptr_array_index(quarktable, tmp.type));
810 process->tgid = tmp.tgid;
811 process->ppid = tmp.ppid;
812 process->brand = g_quark_from_string(
813 (gchar*)g_ptr_array_index(quarktable, tmp.brand));
814 process->name =
815 g_quark_from_string((gchar*)g_ptr_array_index(quarktable, tmp.name));
816
817
818 do {
819 if(feof(fp) || ferror(fp)) goto end_loop;
820
821 gint hdr = fgetc(fp);
822 if(hdr == EOF) goto end_loop;
823
824 switch(hdr) {
825 case HDR_ES:
826 process->execution_stack =
827 g_array_set_size(process->execution_stack,
828 process->execution_stack->len + 1);
829 es = &g_array_index(process->execution_stack, LttvExecutionState,
830 process->execution_stack->len-1);
831 process->state = es;
832
833 fread(&es->t, sizeof(es->t), 1, fp);
834 es->t = g_quark_from_string(
835 (gchar*)g_ptr_array_index(quarktable, es->t));
836 fread(&es->n, sizeof(es->n), 1, fp);
837 es->n = g_quark_from_string(
838 (gchar*)g_ptr_array_index(quarktable, es->n));
839 fread(&es->s, sizeof(es->s), 1, fp);
840 es->s = g_quark_from_string(
841 (gchar*)g_ptr_array_index(quarktable, es->s));
842 fread(&es->entry, sizeof(es->entry), 1, fp);
843 fread(&es->change, sizeof(es->change), 1, fp);
844 fread(&es->cum_cpu_time, sizeof(es->cum_cpu_time), 1, fp);
845 break;
846 case HDR_USER_STACK:
847 process->user_stack = g_array_set_size(process->user_stack,
848 process->user_stack->len + 1);
849 address = &g_array_index(process->user_stack, guint64,
850 process->user_stack->len-1);
851 fread(address, sizeof(address), 1, fp);
852 process->current_function = *address;
853 break;
854 case HDR_USERTRACE:
855 fread(&tmpq, sizeof(tmpq), 1, fp);
856 fread(&process->usertrace->cpu, sizeof(process->usertrace->cpu), 1, fp);
857 break;
858 default:
859 ungetc(hdr, fp);
860 goto end_loop;
861 };
862 } while(1);
863 end_loop:
864 return;
865 }
866
867
868 /* Called because a HDR_PROCESS_STATE was found */
869 /* Append a saved state to the trace states */
870 void lttv_state_read_raw(LttvTraceState *self, FILE *fp, GPtrArray *quarktable)
871 {
872 guint i, nb_tracefile, nb_block, offset;
873 guint64 tsc;
874 LttvTracefileState *tfcs;
875
876 LttEventPosition *ep;
877
878 guint nb_cpus;
879
880 int hdr;
881
882 LttTime t;
883
884 LttvAttribute *saved_states_tree, *saved_state_tree;
885
886 LttvAttributeValue value;
887 GTree *pqueue = self->parent.ts_context->pqueue;
888 ep = ltt_event_position_new();
889
890 restore_init_state(self);
891
892 fread(&t, sizeof(t), 1, fp);
893
894 do {
895 if(feof(fp) || ferror(fp)) goto end_loop;
896 hdr = fgetc(fp);
897 if(hdr == EOF) goto end_loop;
898
899 switch(hdr) {
900 case HDR_PROCESS:
901 /* Call read_process_state_raw */
902 read_process_state_raw(self, fp, quarktable);
903 break;
904 case HDR_TRACEFILE:
905 case HDR_TRACESET:
906 case HDR_TRACE:
907 case HDR_QUARKS:
908 case HDR_QUARK:
909 case HDR_ES:
910 case HDR_USER_STACK:
911 case HDR_USERTRACE:
912 case HDR_PROCESS_STATE:
913 case HDR_CPU:
914 ungetc(hdr, fp);
915 goto end_loop;
916 break;
917 default:
918 g_error("Error while parsing saved state file : unknown data header %d",
919 hdr);
920 };
921 } while(1);
922 end_loop:
923
924 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
925 for(i=0;i<nb_cpus;i++) {
926 int cpu_num;
927 hdr = fgetc(fp);
928 g_assert(hdr == HDR_CPU);
929 fread(&cpu_num, sizeof(cpu_num), 1, fp); /* cpu number */
930 g_assert(i == cpu_num);
931 fread(&self->running_process[i]->pid,
932 sizeof(self->running_process[i]->pid), 1, fp);
933 }
934
935 nb_tracefile = self->parent.tracefiles->len;
936
937 for(i = 0 ; i < nb_tracefile ; i++) {
938 tfcs =
939 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
940 LttvTracefileContext*, i));
941 // fprintf(fp, " <TRACEFILE TIMESTAMP_S=%lu TIMESTAMP_NS=%lu",
942 // tfcs->parent.timestamp.tv_sec,
943 // tfcs->parent.timestamp.tv_nsec);
944 g_tree_remove(pqueue, &tfcs->parent);
945 hdr = fgetc(fp);
946 g_assert(hdr == HDR_TRACEFILE);
947 fread(&tfcs->parent.timestamp, sizeof(tfcs->parent.timestamp), 1, fp);
948 /* Note : if timestamp if LTT_TIME_INFINITE, there will be no
949 * position following : end of trace */
950 if(ltt_time_compare(tfcs->parent.timestamp, ltt_time_infinite) != 0) {
951 fread(&nb_block, sizeof(nb_block), 1, fp);
952 fread(&offset, sizeof(offset), 1, fp);
953 fread(&tsc, sizeof(tsc), 1, fp);
954 ltt_event_position_set(ep, tfcs->parent.tf, nb_block, offset, tsc);
955 gint ret = ltt_tracefile_seek_position(tfcs->parent.tf, ep);
956 g_assert(ret == 0);
957 g_tree_insert(pqueue, &tfcs->parent, &tfcs->parent);
958 }
959 }
960 g_free(ep);
961
962 saved_states_tree = lttv_attribute_find_subdir(self->parent.t_a,
963 LTTV_STATE_SAVED_STATES);
964 saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
965 value = lttv_attribute_add(saved_states_tree,
966 lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT);
967 *(value.v_gobject) = (GObject *)saved_state_tree;
968 value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME);
969 *(value.v_time) = t;
970 lttv_state_save(self, saved_state_tree);
971 g_debug("Saving state at time %lu.%lu", t.tv_sec,
972 t.tv_nsec);
973
974 *(self->max_time_state_recomputed_in_seek) = t;
975
976 }
977
978 /* Called when a HDR_TRACE is found */
979 void lttv_trace_states_read_raw(LttvTraceState *tcs, FILE *fp,
980 GPtrArray *quarktable)
981 {
982 int hdr;
983
984 do {
985 if(feof(fp) || ferror(fp)) goto end_loop;
986 hdr = fgetc(fp);
987 if(hdr == EOF) goto end_loop;
988
989 switch(hdr) {
990 case HDR_PROCESS_STATE:
991 /* Call read_process_state_raw */
992 lttv_state_read_raw(tcs, fp, quarktable);
993 break;
994 case HDR_TRACEFILE:
995 case HDR_TRACESET:
996 case HDR_TRACE:
997 case HDR_QUARKS:
998 case HDR_QUARK:
999 case HDR_ES:
1000 case HDR_USER_STACK:
1001 case HDR_USERTRACE:
1002 case HDR_PROCESS:
1003 case HDR_CPU:
1004 g_error("Error while parsing saved state file :"
1005 " unexpected data header %d",
1006 hdr);
1007 break;
1008 default:
1009 g_error("Error while parsing saved state file : unknown data header %d",
1010 hdr);
1011 };
1012 } while(1);
1013 end_loop:
1014 *(tcs->max_time_state_recomputed_in_seek) = tcs->parent.time_span.end_time;
1015 restore_init_state(tcs);
1016 lttv_process_trace_seek_time(tcs, ltt_time_zero);
1017 return;
1018 }
1019
1020
1021
1022 /* Copy each process from an existing hash table to a new one */
1023
1024 static void copy_process_state(gpointer key, gpointer value,gpointer user_data)
1025 {
1026 LttvProcessState *process, *new_process;
1027
1028 GHashTable *new_processes = (GHashTable *)user_data;
1029
1030 guint i;
1031
1032 process = (LttvProcessState *)value;
1033 new_process = g_new(LttvProcessState, 1);
1034 *new_process = *process;
1035 new_process->execution_stack = g_array_sized_new(FALSE, FALSE,
1036 sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK);
1037 new_process->execution_stack =
1038 g_array_set_size(new_process->execution_stack,
1039 process->execution_stack->len);
1040 for(i = 0 ; i < process->execution_stack->len; i++) {
1041 g_array_index(new_process->execution_stack, LttvExecutionState, i) =
1042 g_array_index(process->execution_stack, LttvExecutionState, i);
1043 }
1044 new_process->state = &g_array_index(new_process->execution_stack,
1045 LttvExecutionState, new_process->execution_stack->len - 1);
1046 new_process->user_stack = g_array_sized_new(FALSE, FALSE,
1047 sizeof(guint64), 0);
1048 new_process->user_stack =
1049 g_array_set_size(new_process->user_stack,
1050 process->user_stack->len);
1051 for(i = 0 ; i < process->user_stack->len; i++) {
1052 g_array_index(new_process->user_stack, guint64, i) =
1053 g_array_index(process->user_stack, guint64, i);
1054 }
1055 new_process->current_function = process->current_function;
1056 g_hash_table_insert(new_processes, new_process, new_process);
1057 }
1058
1059
1060 static GHashTable *lttv_state_copy_process_table(GHashTable *processes)
1061 {
1062 GHashTable *new_processes = g_hash_table_new(process_hash, process_equal);
1063
1064 g_hash_table_foreach(processes, copy_process_state, new_processes);
1065 return new_processes;
1066 }
1067
1068
1069 /* The saved state for each trace contains a member "processes", which
1070 stores a copy of the process table, and a member "tracefiles" with
1071 one entry per tracefile. Each tracefile has a "process" member pointing
1072 to the current process and a "position" member storing the tracefile
1073 position (needed to seek to the current "next" event. */
1074
1075 static void state_save(LttvTraceState *self, LttvAttribute *container)
1076 {
1077 guint i, nb_tracefile, nb_cpus;
1078
1079 LttvTracefileState *tfcs;
1080
1081 LttvAttribute *tracefiles_tree, *tracefile_tree;
1082
1083 guint *running_process;
1084
1085 LttvAttributeType type;
1086
1087 LttvAttributeValue value;
1088
1089 LttvAttributeName name;
1090
1091 LttEventPosition *ep;
1092
1093 tracefiles_tree = lttv_attribute_find_subdir(container,
1094 LTTV_STATE_TRACEFILES);
1095
1096 value = lttv_attribute_add(container, LTTV_STATE_PROCESSES,
1097 LTTV_POINTER);
1098 *(value.v_pointer) = lttv_state_copy_process_table(self->processes);
1099
1100 /* Add the currently running processes array */
1101 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
1102 running_process = g_new(guint, nb_cpus);
1103 for(i=0;i<nb_cpus;i++) {
1104 running_process[i] = self->running_process[i]->pid;
1105 }
1106 value = lttv_attribute_add(container, LTTV_STATE_RUNNING_PROCESS,
1107 LTTV_POINTER);
1108 *(value.v_pointer) = running_process;
1109
1110 g_info("State save");
1111
1112 nb_tracefile = self->parent.tracefiles->len;
1113
1114 for(i = 0 ; i < nb_tracefile ; i++) {
1115 tfcs =
1116 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
1117 LttvTracefileContext*, i));
1118 tracefile_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
1119 value = lttv_attribute_add(tracefiles_tree, i,
1120 LTTV_GOBJECT);
1121 *(value.v_gobject) = (GObject *)tracefile_tree;
1122 #if 0
1123 value = lttv_attribute_add(tracefile_tree, LTTV_STATE_PROCESS,
1124 LTTV_UINT);
1125 *(value.v_uint) = tfcs->process->pid;
1126 #endif //0
1127 value = lttv_attribute_add(tracefile_tree, LTTV_STATE_EVENT,
1128 LTTV_POINTER);
1129 /* Only save the position if the tfs has not infinite time. */
1130 //if(!g_tree_lookup(self->parent.ts_context->pqueue, &tfcs->parent)
1131 // && current_tfcs != tfcs) {
1132 if(ltt_time_compare(tfcs->parent.timestamp, ltt_time_infinite) == 0) {
1133 *(value.v_pointer) = NULL;
1134 } else {
1135 LttEvent *e = ltt_tracefile_get_event(tfcs->parent.tf);
1136 ep = ltt_event_position_new();
1137 ltt_event_position(e, ep);
1138 *(value.v_pointer) = ep;
1139
1140 guint nb_block, offset;
1141 guint64 tsc;
1142 LttTracefile *tf;
1143 ltt_event_position_get(ep, &tf, &nb_block, &offset, &tsc);
1144 g_info("Block %u offset %u tsc %llu time %lu.%lu", nb_block, offset,
1145 tsc,
1146 tfcs->parent.timestamp.tv_sec, tfcs->parent.timestamp.tv_nsec);
1147 }
1148 }
1149 }
1150
1151
1152 static void state_restore(LttvTraceState *self, LttvAttribute *container)
1153 {
1154 guint i, nb_tracefile, pid, nb_cpus;
1155
1156 LttvTracefileState *tfcs;
1157
1158 LttvAttribute *tracefiles_tree, *tracefile_tree;
1159
1160 guint *running_process;
1161
1162 LttvAttributeType type;
1163
1164 LttvAttributeValue value;
1165
1166 LttvAttributeName name;
1167
1168 gboolean is_named;
1169
1170 LttEventPosition *ep;
1171
1172 LttvTracesetContext *tsc = self->parent.ts_context;
1173
1174 tracefiles_tree = lttv_attribute_find_subdir(container,
1175 LTTV_STATE_TRACEFILES);
1176
1177 type = lttv_attribute_get_by_name(container, LTTV_STATE_PROCESSES,
1178 &value);
1179 g_assert(type == LTTV_POINTER);
1180 lttv_state_free_process_table(self->processes);
1181 self->processes = lttv_state_copy_process_table(*(value.v_pointer));
1182
1183 /* Add the currently running processes array */
1184 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
1185 type = lttv_attribute_get_by_name(container, LTTV_STATE_RUNNING_PROCESS,
1186 &value);
1187 g_assert(type == LTTV_POINTER);
1188 running_process = *(value.v_pointer);
1189 for(i=0;i<nb_cpus;i++) {
1190 pid = running_process[i];
1191 self->running_process[i] = lttv_state_find_process(self, i, pid);
1192 g_assert(self->running_process[i] != NULL);
1193 }
1194
1195
1196 nb_tracefile = self->parent.tracefiles->len;
1197
1198 //g_tree_destroy(tsc->pqueue);
1199 //tsc->pqueue = g_tree_new(compare_tracefile);
1200
1201 for(i = 0 ; i < nb_tracefile ; i++) {
1202 tfcs =
1203 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
1204 LttvTracefileContext*, i));
1205 type = lttv_attribute_get(tracefiles_tree, i, &name, &value, &is_named);
1206 g_assert(type == LTTV_GOBJECT);
1207 tracefile_tree = *((LttvAttribute **)(value.v_gobject));
1208 #if 0
1209 type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_PROCESS,
1210 &value);
1211 g_assert(type == LTTV_UINT);
1212 pid = *(value.v_uint);
1213 tfcs->process = lttv_state_find_process_or_create(tfcs, pid);
1214 #endif //0
1215 type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_EVENT,
1216 &value);
1217 g_assert(type == LTTV_POINTER);
1218 //g_assert(*(value.v_pointer) != NULL);
1219 ep = *(value.v_pointer);
1220 g_assert(tfcs->parent.t_context != NULL);
1221
1222 LttvTracefileContext *tfc = LTTV_TRACEFILE_CONTEXT(tfcs);
1223 g_tree_remove(tsc->pqueue, tfc);
1224
1225 if(ep != NULL) {
1226 g_assert(ltt_tracefile_seek_position(tfc->tf, ep) == 0);
1227 tfc->timestamp = ltt_event_time(ltt_tracefile_get_event(tfc->tf));
1228 g_assert(ltt_time_compare(tfc->timestamp, ltt_time_infinite) != 0);
1229 g_tree_insert(tsc->pqueue, tfc, tfc);
1230 g_info("Restoring state for a tf at time %lu.%lu", tfc->timestamp.tv_sec, tfc->timestamp.tv_nsec);
1231 } else {
1232 tfc->timestamp = ltt_time_infinite;
1233 }
1234 }
1235 }
1236
1237
1238 static void state_saved_free(LttvTraceState *self, LttvAttribute *container)
1239 {
1240 guint i, nb_tracefile, nb_cpus;
1241
1242 LttvTracefileState *tfcs;
1243
1244 LttvAttribute *tracefiles_tree, *tracefile_tree;
1245
1246 guint *running_process;
1247
1248 LttvAttributeType type;
1249
1250 LttvAttributeValue value;
1251
1252 LttvAttributeName name;
1253
1254 gboolean is_named;
1255
1256 LttEventPosition *ep;
1257
1258 tracefiles_tree = lttv_attribute_find_subdir(container,
1259 LTTV_STATE_TRACEFILES);
1260 g_object_ref(G_OBJECT(tracefiles_tree));
1261 lttv_attribute_remove_by_name(container, LTTV_STATE_TRACEFILES);
1262
1263 type = lttv_attribute_get_by_name(container, LTTV_STATE_PROCESSES,
1264 &value);
1265 g_assert(type == LTTV_POINTER);
1266 lttv_state_free_process_table(*(value.v_pointer));
1267 *(value.v_pointer) = NULL;
1268 lttv_attribute_remove_by_name(container, LTTV_STATE_PROCESSES);
1269
1270 /* Free running processes array */
1271 nb_cpus = ltt_trace_get_num_cpu(self->parent.t);
1272 type = lttv_attribute_get_by_name(container, LTTV_STATE_RUNNING_PROCESS,
1273 &value);
1274 g_assert(type == LTTV_POINTER);
1275 running_process = *(value.v_pointer);
1276 g_free(running_process);
1277
1278 nb_tracefile = self->parent.tracefiles->len;
1279
1280 for(i = 0 ; i < nb_tracefile ; i++) {
1281 tfcs =
1282 LTTV_TRACEFILE_STATE(g_array_index(self->parent.tracefiles,
1283 LttvTracefileContext*, i));
1284 type = lttv_attribute_get(tracefiles_tree, i, &name, &value, &is_named);
1285 g_assert(type == LTTV_GOBJECT);
1286 tracefile_tree = *((LttvAttribute **)(value.v_gobject));
1287
1288 type = lttv_attribute_get_by_name(tracefile_tree, LTTV_STATE_EVENT,
1289 &value);
1290 g_assert(type == LTTV_POINTER);
1291 if(*(value.v_pointer) != NULL) g_free(*(value.v_pointer));
1292 }
1293 g_object_unref(G_OBJECT(tracefiles_tree));
1294 }
1295
1296
1297 static void free_saved_state(LttvTraceState *self)
1298 {
1299 guint i, nb;
1300
1301 LttvAttributeType type;
1302
1303 LttvAttributeValue value;
1304
1305 LttvAttributeName name;
1306
1307 gboolean is_named;
1308
1309 LttvAttribute *saved_states;
1310
1311 saved_states = lttv_attribute_find_subdir(self->parent.t_a,
1312 LTTV_STATE_SAVED_STATES);
1313
1314 nb = lttv_attribute_get_number(saved_states);
1315 for(i = 0 ; i < nb ; i++) {
1316 type = lttv_attribute_get(saved_states, i, &name, &value, &is_named);
1317 g_assert(type == LTTV_GOBJECT);
1318 state_saved_free(self, *((LttvAttribute **)value.v_gobject));
1319 }
1320
1321 lttv_attribute_remove_by_name(self->parent.t_a, LTTV_STATE_SAVED_STATES);
1322 }
1323
1324
1325 static void
1326 create_max_time(LttvTraceState *tcs)
1327 {
1328 LttvAttributeValue v;
1329
1330 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME,
1331 LTTV_POINTER, &v);
1332 g_assert(*(v.v_pointer) == NULL);
1333 *(v.v_pointer) = g_new(LttTime,1);
1334 *((LttTime *)*(v.v_pointer)) = ltt_time_zero;
1335 }
1336
1337
1338 static void
1339 get_max_time(LttvTraceState *tcs)
1340 {
1341 LttvAttributeValue v;
1342
1343 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME,
1344 LTTV_POINTER, &v);
1345 g_assert(*(v.v_pointer) != NULL);
1346 tcs->max_time_state_recomputed_in_seek = (LttTime *)*(v.v_pointer);
1347 }
1348
1349
1350 static void
1351 free_max_time(LttvTraceState *tcs)
1352 {
1353 LttvAttributeValue v;
1354
1355 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_SAVED_STATES_TIME,
1356 LTTV_POINTER, &v);
1357 g_free(*(v.v_pointer));
1358 *(v.v_pointer) = NULL;
1359 }
1360
1361
1362 typedef struct _LttvNameTables {
1363 // FIXME GQuark *eventtype_names;
1364 GQuark *syscall_names;
1365 guint nb_syscalls;
1366 GQuark *trap_names;
1367 guint nb_traps;
1368 GQuark *irq_names;
1369 guint nb_irqs;
1370 GQuark *soft_irq_names;
1371 guint nb_softirqs;
1372 } LttvNameTables;
1373
1374
1375 static void
1376 create_name_tables(LttvTraceState *tcs)
1377 {
1378 int i, nb;
1379
1380 GQuark f_name, e_name;
1381
1382 LttvTraceHook h;
1383
1384 LttvTraceHookByFacility *thf;
1385
1386 LttEventType *et;
1387
1388 LttType *t;
1389
1390 GString *fe_name = g_string_new("");
1391
1392 LttvNameTables *name_tables = g_new(LttvNameTables, 1);
1393
1394 LttvAttributeValue v;
1395
1396 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES,
1397 LTTV_POINTER, &v);
1398 g_assert(*(v.v_pointer) == NULL);
1399 *(v.v_pointer) = name_tables;
1400 #if 0 // Use iteration over the facilities_by_name and then list all event
1401 // types of each facility
1402 nb = ltt_trace_eventtype_number(tcs->parent.t);
1403 name_tables->eventtype_names = g_new(GQuark, nb);
1404 for(i = 0 ; i < nb ; i++) {
1405 et = ltt_trace_eventtype_get(tcs->parent.t, i);
1406 e_name = ltt_eventtype_name(et);
1407 f_name = ltt_facility_name(ltt_eventtype_facility(et));
1408 g_string_printf(fe_name, "%s.%s", f_name, e_name);
1409 name_tables->eventtype_names[i] = g_quark_from_string(fe_name->str);
1410 }
1411 #endif //0
1412 if(!lttv_trace_find_hook(tcs->parent.t,
1413 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_SYSCALL_ENTRY,
1414 LTT_FIELD_SYSCALL_ID, 0, 0,
1415 NULL, NULL, &h)) {
1416
1417 thf = lttv_trace_hook_get_first(&h);
1418
1419 t = ltt_field_type(thf->f1);
1420 nb = ltt_type_element_number(t);
1421
1422 lttv_trace_hook_destroy(&h);
1423
1424 name_tables->syscall_names = g_new(GQuark, nb);
1425 name_tables->nb_syscalls = nb;
1426
1427 for(i = 0 ; i < nb ; i++) {
1428 name_tables->syscall_names[i] = ltt_enum_string_get(t, i);
1429 if(!name_tables->syscall_names[i]) {
1430 GString *string = g_string_new("");
1431 g_string_printf(string, "syscall %u", i);
1432 name_tables->syscall_names[i] = g_quark_from_string(string->str);
1433 g_string_free(string, TRUE);
1434 }
1435 }
1436
1437 //name_tables->syscall_names = g_new(GQuark, 256);
1438 //for(i = 0 ; i < 256 ; i++) {
1439 // g_string_printf(fe_name, "syscall %d", i);
1440 // name_tables->syscall_names[i] = g_quark_from_string(fe_name->str);
1441 //}
1442 } else {
1443 name_tables->syscall_names = NULL;
1444 name_tables->nb_syscalls = 0;
1445 }
1446
1447 if(!lttv_trace_find_hook(tcs->parent.t, LTT_FACILITY_KERNEL_ARCH,
1448 LTT_EVENT_TRAP_ENTRY,
1449 LTT_FIELD_TRAP_ID, 0, 0,
1450 NULL, NULL, &h)) {
1451
1452 thf = lttv_trace_hook_get_first(&h);
1453
1454 t = ltt_field_type(thf->f1);
1455 //nb = ltt_type_element_number(t);
1456
1457 lttv_trace_hook_destroy(&h);
1458
1459 /*
1460 name_tables->trap_names = g_new(GQuark, nb);
1461 for(i = 0 ; i < nb ; i++) {
1462 name_tables->trap_names[i] = g_quark_from_string(
1463 ltt_enum_string_get(t, i));
1464 }
1465 */
1466 name_tables->nb_traps = 256;
1467 name_tables->trap_names = g_new(GQuark, 256);
1468 for(i = 0 ; i < 256 ; i++) {
1469 g_string_printf(fe_name, "trap %d", i);
1470 name_tables->trap_names[i] = g_quark_from_string(fe_name->str);
1471 }
1472 } else {
1473 name_tables->trap_names = NULL;
1474 name_tables->nb_traps = 0;
1475 }
1476
1477 if(!lttv_trace_find_hook(tcs->parent.t,
1478 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
1479 LTT_FIELD_IRQ_ID, 0, 0,
1480 NULL, NULL, &h)) {
1481
1482 thf = lttv_trace_hook_get_first(&h);
1483
1484 t = ltt_field_type(thf->f1);
1485 //nb = ltt_type_element_number(t);
1486
1487 lttv_trace_hook_destroy(&h);
1488
1489 /*
1490 name_tables->irq_names = g_new(GQuark, nb);
1491 for(i = 0 ; i < nb ; i++) {
1492 name_tables->irq_names[i] = g_quark_from_string(ltt_enum_string_get(t, i));
1493 }
1494 */
1495
1496 name_tables->nb_irqs = 256;
1497 name_tables->irq_names = g_new(GQuark, 256);
1498 for(i = 0 ; i < 256 ; i++) {
1499 g_string_printf(fe_name, "irq %d", i);
1500 name_tables->irq_names[i] = g_quark_from_string(fe_name->str);
1501 }
1502 } else {
1503 name_tables->nb_irqs = 0;
1504 name_tables->irq_names = NULL;
1505 }
1506 /*
1507 name_tables->soft_irq_names = g_new(GQuark, nb);
1508 for(i = 0 ; i < nb ; i++) {
1509 name_tables->soft_irq_names[i] = g_quark_from_string(ltt_enum_string_get(t, i));
1510 }
1511 */
1512
1513 name_tables->nb_softirqs = 256;
1514 name_tables->soft_irq_names = g_new(GQuark, 256);
1515 for(i = 0 ; i < 256 ; i++) {
1516 g_string_printf(fe_name, "softirq %d", i);
1517 name_tables->soft_irq_names[i] = g_quark_from_string(fe_name->str);
1518 }
1519
1520
1521 g_string_free(fe_name, TRUE);
1522 }
1523
1524
1525 static void
1526 get_name_tables(LttvTraceState *tcs)
1527 {
1528 LttvNameTables *name_tables;
1529
1530 LttvAttributeValue v;
1531
1532 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES,
1533 LTTV_POINTER, &v);
1534 g_assert(*(v.v_pointer) != NULL);
1535 name_tables = (LttvNameTables *)*(v.v_pointer);
1536 //tcs->eventtype_names = name_tables->eventtype_names;
1537 tcs->syscall_names = name_tables->syscall_names;
1538 tcs->nb_syscalls = name_tables->nb_syscalls;
1539 tcs->trap_names = name_tables->trap_names;
1540 tcs->nb_traps = name_tables->nb_traps;
1541 tcs->irq_names = name_tables->irq_names;
1542 tcs->soft_irq_names = name_tables->soft_irq_names;
1543 tcs->nb_irqs = name_tables->nb_irqs;
1544 tcs->nb_softirqs = name_tables->nb_softirqs;
1545 }
1546
1547
1548 static void
1549 free_name_tables(LttvTraceState *tcs)
1550 {
1551 LttvNameTables *name_tables;
1552
1553 LttvAttributeValue v;
1554
1555 lttv_attribute_find(tcs->parent.t_a, LTTV_STATE_NAME_TABLES,
1556 LTTV_POINTER, &v);
1557 name_tables = (LttvNameTables *)*(v.v_pointer);
1558 *(v.v_pointer) = NULL;
1559
1560 // g_free(name_tables->eventtype_names);
1561 if(name_tables->syscall_names) g_free(name_tables->syscall_names);
1562 if(name_tables->trap_names) g_free(name_tables->trap_names);
1563 if(name_tables->irq_names) g_free(name_tables->irq_names);
1564 if(name_tables->soft_irq_names) g_free(name_tables->soft_irq_names);
1565 if(name_tables) g_free(name_tables);
1566 }
1567
1568 #ifdef HASH_TABLE_DEBUG
1569
1570 static void test_process(gpointer key, gpointer value, gpointer user_data)
1571 {
1572 LttvProcessState *process = (LttvProcessState *)value;
1573
1574 /* Test for process corruption */
1575 guint stack_len = process->execution_stack->len;
1576 }
1577
1578 static void hash_table_check(GHashTable *table)
1579 {
1580 g_hash_table_foreach(table, test_process, NULL);
1581 }
1582
1583
1584 #endif
1585
1586
1587 static void push_state(LttvTracefileState *tfs, LttvExecutionMode t,
1588 guint state_id)
1589 {
1590 LttvExecutionState *es;
1591
1592 LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
1593 guint cpu = tfs->cpu;
1594
1595 #ifdef HASH_TABLE_DEBUG
1596 hash_table_check(ts->processes);
1597 #endif
1598 LttvProcessState *process = ts->running_process[cpu];
1599
1600 guint depth = process->execution_stack->len;
1601
1602 process->execution_stack =
1603 g_array_set_size(process->execution_stack, depth + 1);
1604 /* Keep in sync */
1605 process->state =
1606 &g_array_index(process->execution_stack, LttvExecutionState, depth - 1);
1607
1608 es = &g_array_index(process->execution_stack, LttvExecutionState, depth);
1609 es->t = t;
1610 es->n = state_id;
1611 es->entry = es->change = tfs->parent.timestamp;
1612 es->cum_cpu_time = ltt_time_zero;
1613 es->s = process->state->s;
1614 process->state = es;
1615 }
1616
1617 /* pop state
1618 * return 1 when empty, else 0 */
1619 int lttv_state_pop_state_cleanup(LttvProcessState *process,
1620 LttvTracefileState *tfs)
1621 {
1622 guint cpu = tfs->cpu;
1623 LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
1624
1625 guint depth = process->execution_stack->len;
1626
1627 if(depth == 1){
1628 return 1;
1629 }
1630
1631 process->execution_stack =
1632 g_array_set_size(process->execution_stack, depth - 1);
1633 process->state = &g_array_index(process->execution_stack, LttvExecutionState,
1634 depth - 2);
1635 process->state->change = tfs->parent.timestamp;
1636
1637 return 0;
1638 }
1639
1640 static void pop_state(LttvTracefileState *tfs, LttvExecutionMode t)
1641 {
1642 guint cpu = tfs->cpu;
1643 LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
1644 LttvProcessState *process = ts->running_process[cpu];
1645
1646 guint depth = process->execution_stack->len;
1647
1648 if(process->state->t != t){
1649 g_info("Different execution mode type (%lu.%09lu): ignore it\n",
1650 tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
1651 g_info("process state has %s when pop_int is %s\n",
1652 g_quark_to_string(process->state->t),
1653 g_quark_to_string(t));
1654 g_info("{ %u, %u, %s, %s, %s }\n",
1655 process->pid,
1656 process->ppid,
1657 g_quark_to_string(process->name),
1658 g_quark_to_string(process->brand),
1659 g_quark_to_string(process->state->s));
1660 return;
1661 }
1662
1663 if(depth == 1){
1664 g_info("Trying to pop last state on stack (%lu.%09lu): ignore it\n",
1665 tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
1666 return;
1667 }
1668
1669 process->execution_stack =
1670 g_array_set_size(process->execution_stack, depth - 1);
1671 process->state = &g_array_index(process->execution_stack, LttvExecutionState,
1672 depth - 2);
1673 process->state->change = tfs->parent.timestamp;
1674 }
1675
1676 struct search_result {
1677 const LttTime *time; /* Requested time */
1678 LttTime *best; /* Best result */
1679 };
1680
1681 static gint search_usertrace(gconstpointer a, gconstpointer b)
1682 {
1683 const LttTime *elem_time = (const LttTime*)a;
1684 /* Explicit non const cast */
1685 struct search_result *res = (struct search_result *)b;
1686
1687 if(ltt_time_compare(*elem_time, *(res->time)) < 0) {
1688 /* The usertrace was created before the schedchange */
1689 /* Get larger keys */
1690 return 1;
1691 } else if(ltt_time_compare(*elem_time, *(res->time)) >= 0) {
1692 /* The usertrace was created after the schedchange time */
1693 /* Get smaller keys */
1694 if(res->best) {
1695 if(ltt_time_compare(*elem_time, *res->best) < 0) {
1696 res->best = elem_time;
1697 }
1698 } else {
1699 res->best = elem_time;
1700 }
1701 return -1;
1702 }
1703 return 0;
1704 }
1705
1706 static LttvTracefileState *ltt_state_usertrace_find(LttvTraceState *tcs,
1707 guint pid, const LttTime *timestamp)
1708 {
1709 LttvTracefileState *tfs = NULL;
1710 struct search_result res;
1711 /* Find the usertrace associated with a pid and time interval.
1712 * Search in the usertraces by PID (within a hash) and then, for each
1713 * corresponding element of the array, find the first one with creation
1714 * timestamp the lowest, but higher or equal to "timestamp". */
1715 res.time = timestamp;
1716 res.best = NULL;
1717 GTree *usertrace_tree = g_hash_table_lookup(tcs->usertraces, (gpointer)pid);
1718 if(usertrace_tree) {
1719 g_tree_search(usertrace_tree, search_usertrace, &res);
1720 if(res.best)
1721 tfs = g_tree_lookup(usertrace_tree, res.best);
1722 }
1723
1724 return tfs;
1725 }
1726
1727
1728 LttvProcessState *
1729 lttv_state_create_process(LttvTraceState *tcs, LttvProcessState *parent,
1730 guint cpu, guint pid, guint tgid, GQuark name, const LttTime *timestamp)
1731 {
1732 LttvProcessState *process = g_new(LttvProcessState, 1);
1733
1734 LttvExecutionState *es;
1735
1736 LttvTraceContext *tc = (LttvTraceContext*)tcs;
1737
1738 char buffer[128];
1739
1740 process->pid = pid;
1741 process->tgid = tgid;
1742 process->cpu = cpu;
1743 process->name = name;
1744 process->brand = LTTV_STATE_UNBRANDED;
1745 //process->last_cpu = tfs->cpu_name;
1746 //process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfs)->tf);
1747 process->type = LTTV_STATE_USER_THREAD;
1748 process->usertrace = ltt_state_usertrace_find(tcs, pid, timestamp);
1749 process->current_function = 0; //function 0x0 by default.
1750
1751 g_info("Process %u, core %p", process->pid, process);
1752 g_hash_table_insert(tcs->processes, process, process);
1753
1754 if(parent) {
1755 process->ppid = parent->pid;
1756 process->creation_time = *timestamp;
1757 }
1758
1759 /* No parent. This process exists but we are missing all information about
1760 its creation. The birth time is set to zero but we remember the time of
1761 insertion */
1762
1763 else {
1764 process->ppid = 0;
1765 process->creation_time = ltt_time_zero;
1766 }
1767
1768 process->insertion_time = *timestamp;
1769 sprintf(buffer,"%d-%lu.%lu",pid, process->creation_time.tv_sec,
1770 process->creation_time.tv_nsec);
1771 process->pid_time = g_quark_from_string(buffer);
1772 process->cpu = cpu;
1773 //process->last_cpu = tfs->cpu_name;
1774 //process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)tfs)->tf);
1775 process->execution_stack = g_array_sized_new(FALSE, FALSE,
1776 sizeof(LttvExecutionState), PREALLOCATED_EXECUTION_STACK);
1777 process->execution_stack = g_array_set_size(process->execution_stack, 2);
1778 es = process->state = &g_array_index(process->execution_stack,
1779 LttvExecutionState, 0);
1780 es->t = LTTV_STATE_USER_MODE;
1781 es->n = LTTV_STATE_SUBMODE_NONE;
1782 es->entry = *timestamp;
1783 //g_assert(timestamp->tv_sec != 0);
1784 es->change = *timestamp;
1785 es->cum_cpu_time = ltt_time_zero;
1786 es->s = LTTV_STATE_RUN;
1787
1788 es = process->state = &g_array_index(process->execution_stack,
1789 LttvExecutionState, 1);
1790 es->t = LTTV_STATE_SYSCALL;
1791 es->n = LTTV_STATE_SUBMODE_NONE;
1792 es->entry = *timestamp;
1793 //g_assert(timestamp->tv_sec != 0);
1794 es->change = *timestamp;
1795 es->cum_cpu_time = ltt_time_zero;
1796 es->s = LTTV_STATE_WAIT_FORK;
1797
1798 /* Allocate an empty function call stack. If it's empty, use 0x0. */
1799 process->user_stack = g_array_sized_new(FALSE, FALSE,
1800 sizeof(guint64), 0);
1801
1802 return process;
1803 }
1804
1805 LttvProcessState *lttv_state_find_process(LttvTraceState *ts, guint cpu,
1806 guint pid)
1807 {
1808 LttvProcessState key;
1809 LttvProcessState *process;
1810
1811 key.pid = pid;
1812 key.cpu = cpu;
1813 process = g_hash_table_lookup(ts->processes, &key);
1814 return process;
1815 }
1816
1817 LttvProcessState *
1818 lttv_state_find_process_or_create(LttvTraceState *ts, guint cpu, guint pid,
1819 const LttTime *timestamp)
1820 {
1821 LttvProcessState *process = lttv_state_find_process(ts, cpu, pid);
1822 LttvExecutionState *es;
1823
1824 /* Put ltt_time_zero creation time for unexisting processes */
1825 if(unlikely(process == NULL)) {
1826 process = lttv_state_create_process(ts,
1827 NULL, cpu, pid, 0, LTTV_STATE_UNNAMED, timestamp);
1828 /* We are not sure is it's a kernel thread or normal thread, put the
1829 * bottom stack state to unknown */
1830 process->execution_stack =
1831 g_array_set_size(process->execution_stack, 1);
1832 process->state = es =
1833 &g_array_index(process->execution_stack, LttvExecutionState, 0);
1834 es->t = LTTV_STATE_MODE_UNKNOWN;
1835 es->s = LTTV_STATE_UNNAMED;
1836 }
1837 return process;
1838 }
1839
1840 /* FIXME : this function should be called when we receive an event telling that
1841 * release_task has been called in the kernel. In happens generally when
1842 * the parent waits for its child terminaison, but may also happen in special
1843 * cases in the child's exit : when the parent ignores its children SIGCCHLD or
1844 * has the flag SA_NOCLDWAIT. It can also happen when the child is part
1845 * of a killed thread ground, but isn't the leader.
1846 */
1847 static void exit_process(LttvTracefileState *tfs, LttvProcessState *process)
1848 {
1849 LttvTraceState *ts = LTTV_TRACE_STATE(tfs->parent.t_context);
1850 LttvProcessState key;
1851
1852 key.pid = process->pid;
1853 key.cpu = process->cpu;
1854 g_hash_table_remove(ts->processes, &key);
1855 g_array_free(process->execution_stack, TRUE);
1856 g_array_free(process->user_stack, TRUE);
1857 g_free(process);
1858 }
1859
1860
1861 static void free_process_state(gpointer key, gpointer value,gpointer user_data)
1862 {
1863 g_array_free(((LttvProcessState *)value)->execution_stack, TRUE);
1864 g_array_free(((LttvProcessState *)value)->user_stack, TRUE);
1865 g_free(value);
1866 }
1867
1868
1869 static void lttv_state_free_process_table(GHashTable *processes)
1870 {
1871 g_hash_table_foreach(processes, free_process_state, NULL);
1872 g_hash_table_destroy(processes);
1873 }
1874
1875
1876 static gboolean syscall_entry(void *hook_data, void *call_data)
1877 {
1878 LttvTracefileState *s = (LttvTracefileState *)call_data;
1879 guint cpu = s->cpu;
1880 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
1881 LttvProcessState *process = ts->running_process[cpu];
1882 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
1883 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
1884 LttField *f = thf->f1;
1885
1886 LttvExecutionSubmode submode;
1887
1888 guint nb_syscalls = ((LttvTraceState *)(s->parent.t_context))->nb_syscalls;
1889 guint syscall = ltt_event_get_unsigned(e, f);
1890
1891 if(syscall < nb_syscalls) {
1892 submode = ((LttvTraceState *)(s->parent.t_context))->syscall_names[
1893 syscall];
1894 } else {
1895 /* Fixup an incomplete syscall table */
1896 GString *string = g_string_new("");
1897 g_string_printf(string, "syscall %u", syscall);
1898 submode = g_quark_from_string(string->str);
1899 g_string_free(string, TRUE);
1900 }
1901 /* There can be no system call from PID 0 : unknown state */
1902 if(process->pid != 0)
1903 push_state(s, LTTV_STATE_SYSCALL, submode);
1904 return FALSE;
1905 }
1906
1907
1908 static gboolean syscall_exit(void *hook_data, void *call_data)
1909 {
1910 LttvTracefileState *s = (LttvTracefileState *)call_data;
1911 guint cpu = s->cpu;
1912 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
1913 LttvProcessState *process = ts->running_process[cpu];
1914
1915 /* There can be no system call from PID 0 : unknown state */
1916 if(process->pid != 0)
1917 pop_state(s, LTTV_STATE_SYSCALL);
1918 return FALSE;
1919 }
1920
1921
1922 static gboolean trap_entry(void *hook_data, void *call_data)
1923 {
1924 LttvTracefileState *s = (LttvTracefileState *)call_data;
1925 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
1926 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
1927 LttField *f = thf->f1;
1928
1929 LttvExecutionSubmode submode;
1930
1931 guint64 nb_traps = ((LttvTraceState *)(s->parent.t_context))->nb_traps;
1932 guint64 trap = ltt_event_get_long_unsigned(e, f);
1933
1934 if(trap < nb_traps) {
1935 submode = ((LttvTraceState *)(s->parent.t_context))->trap_names[trap];
1936 } else {
1937 /* Fixup an incomplete trap table */
1938 GString *string = g_string_new("");
1939 g_string_printf(string, "trap %llu", trap);
1940 submode = g_quark_from_string(string->str);
1941 g_string_free(string, TRUE);
1942 }
1943
1944 push_state(s, LTTV_STATE_TRAP, submode);
1945 return FALSE;
1946 }
1947
1948
1949 static gboolean trap_exit(void *hook_data, void *call_data)
1950 {
1951 LttvTracefileState *s = (LttvTracefileState *)call_data;
1952
1953 pop_state(s, LTTV_STATE_TRAP);
1954 return FALSE;
1955 }
1956
1957
1958 static gboolean irq_entry(void *hook_data, void *call_data)
1959 {
1960 LttvTracefileState *s = (LttvTracefileState *)call_data;
1961 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
1962 guint8 fac_id = ltt_event_facility_id(e);
1963 guint8 ev_id = ltt_event_eventtype_id(e);
1964 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
1965 // g_assert(lttv_trace_hook_get_first((LttvTraceHook *)hook_data)->f1 != NULL);
1966 g_assert(thf->f1 != NULL);
1967 // g_assert(thf == lttv_trace_hook_get_first((LttvTraceHook *)hook_data));
1968 LttField *f = thf->f1;
1969
1970 LttvExecutionSubmode submode;
1971 guint64 irq = ltt_event_get_long_unsigned(e, f);
1972 guint64 nb_irqs = ((LttvTraceState *)(s->parent.t_context))->nb_irqs;
1973 GString *string;
1974
1975 if(irq < nb_irqs) {
1976 submode = ((LttvTraceState *)(s->parent.t_context))->irq_names[irq];
1977 } else {
1978 /* Fixup an incomplete irq table */
1979 GString *string = g_string_new("");
1980 g_string_printf(string, "irq %llu", irq);
1981 submode = g_quark_from_string(string->str);
1982 g_string_free(string, TRUE);
1983 }
1984
1985 /* Do something with the info about being in user or system mode when int? */
1986 push_state(s, LTTV_STATE_IRQ, submode);
1987 return FALSE;
1988 }
1989
1990 static gboolean soft_irq_exit(void *hook_data, void *call_data)
1991 {
1992 LttvTracefileState *s = (LttvTracefileState *)call_data;
1993
1994 pop_state(s, LTTV_STATE_SOFT_IRQ);
1995 return FALSE;
1996 }
1997
1998
1999
2000 static gboolean irq_exit(void *hook_data, void *call_data)
2001 {
2002 LttvTracefileState *s = (LttvTracefileState *)call_data;
2003
2004 pop_state(s, LTTV_STATE_IRQ);
2005 return FALSE;
2006 }
2007
2008 static gboolean soft_irq_entry(void *hook_data, void *call_data)
2009 {
2010 LttvTracefileState *s = (LttvTracefileState *)call_data;
2011 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2012 guint8 fac_id = ltt_event_facility_id(e);
2013 guint8 ev_id = ltt_event_eventtype_id(e);
2014 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2015 // g_assert(lttv_trace_hook_get_first((LttvTraceHook *)hook_data)->f1 != NULL);
2016 g_assert(thf->f1 != NULL);
2017 // g_assert(thf == lttv_trace_hook_get_first((LttvTraceHook *)hook_data));
2018 LttField *f = thf->f1;
2019
2020 LttvExecutionSubmode submode;
2021 guint64 softirq = ltt_event_get_long_unsigned(e, f);
2022 guint64 nb_softirqs = ((LttvTraceState *)(s->parent.t_context))->nb_softirqs;
2023 GString *string;
2024
2025 if(softirq < nb_softirqs) {
2026 submode = ((LttvTraceState *)(s->parent.t_context))->soft_irq_names[softirq];
2027 } else {
2028 /* Fixup an incomplete irq table */
2029 GString *string = g_string_new("");
2030 g_string_printf(string, "softirq %llu", softirq);
2031 submode = g_quark_from_string(string->str);
2032 g_string_free(string, TRUE);
2033 }
2034
2035 /* Do something with the info about being in user or system mode when int? */
2036 push_state(s, LTTV_STATE_SOFT_IRQ, submode);
2037 return FALSE;
2038 }
2039
2040 static void push_function(LttvTracefileState *tfs, guint64 funcptr)
2041 {
2042 guint64 *new_func;
2043
2044 LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
2045 guint cpu = tfs->cpu;
2046 LttvProcessState *process = ts->running_process[cpu];
2047
2048 guint depth = process->user_stack->len;
2049
2050 process->user_stack =
2051 g_array_set_size(process->user_stack, depth + 1);
2052
2053 new_func = &g_array_index(process->user_stack, guint64, depth);
2054 *new_func = funcptr;
2055 process->current_function = funcptr;
2056 }
2057
2058 static void pop_function(LttvTracefileState *tfs, guint64 funcptr)
2059 {
2060 guint cpu = tfs->cpu;
2061 LttvTraceState *ts = (LttvTraceState*)tfs->parent.t_context;
2062 LttvProcessState *process = ts->running_process[cpu];
2063
2064 if(process->current_function != funcptr){
2065 g_info("Different functions (%lu.%09lu): ignore it\n",
2066 tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
2067 g_info("process state has %llu when pop_function is %llu\n",
2068 process->current_function, funcptr);
2069 g_info("{ %u, %u, %s, %s, %s }\n",
2070 process->pid,
2071 process->ppid,
2072 g_quark_to_string(process->name),
2073 g_quark_to_string(process->brand),
2074 g_quark_to_string(process->state->s));
2075 return;
2076 }
2077 guint depth = process->user_stack->len;
2078
2079 if(depth == 0){
2080 g_info("Trying to pop last function on stack (%lu.%09lu): ignore it\n",
2081 tfs->parent.timestamp.tv_sec, tfs->parent.timestamp.tv_nsec);
2082 return;
2083 }
2084
2085 process->user_stack =
2086 g_array_set_size(process->user_stack, depth - 1);
2087 process->current_function =
2088 g_array_index(process->user_stack, guint64, depth - 2);
2089 }
2090
2091
2092 static gboolean function_entry(void *hook_data, void *call_data)
2093 {
2094 LttvTracefileState *s = (LttvTracefileState *)call_data;
2095 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2096 guint8 fac_id = ltt_event_facility_id(e);
2097 guint8 ev_id = ltt_event_eventtype_id(e);
2098 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2099 g_assert(thf->f1 != NULL);
2100 LttField *f = thf->f1;
2101 guint64 funcptr = ltt_event_get_long_unsigned(e, f);
2102
2103 push_function(s, funcptr);
2104 return FALSE;
2105 }
2106
2107 static gboolean function_exit(void *hook_data, void *call_data)
2108 {
2109 LttvTracefileState *s = (LttvTracefileState *)call_data;
2110 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2111 guint8 fac_id = ltt_event_facility_id(e);
2112 guint8 ev_id = ltt_event_eventtype_id(e);
2113 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2114 g_assert(thf->f1 != NULL);
2115 LttField *f = thf->f1;
2116 guint64 funcptr = ltt_event_get_long_unsigned(e, f);
2117
2118 LttvExecutionSubmode submode;
2119
2120 pop_function(s, funcptr);
2121 return FALSE;
2122 }
2123
2124 static gboolean schedchange(void *hook_data, void *call_data)
2125 {
2126 LttvTracefileState *s = (LttvTracefileState *)call_data;
2127 guint cpu = s->cpu;
2128 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2129 LttvProcessState *process = ts->running_process[cpu];
2130 LttvProcessState *old_process = ts->running_process[cpu];
2131
2132 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2133 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2134 guint pid_in, pid_out;
2135 gint64 state_out;
2136
2137 pid_out = ltt_event_get_unsigned(e, thf->f1);
2138 pid_in = ltt_event_get_unsigned(e, thf->f2);
2139 state_out = ltt_event_get_long_int(e, thf->f3);
2140
2141 if(likely(process != NULL)) {
2142
2143 /* We could not know but it was not the idle process executing.
2144 This should only happen at the beginning, before the first schedule
2145 event, and when the initial information (current process for each CPU)
2146 is missing. It is not obvious how we could, after the fact, compensate
2147 the wrongly attributed statistics. */
2148
2149 //This test only makes sense once the state is known and if there is no
2150 //missing events. We need to silently ignore schedchange coming after a
2151 //process_free, or it causes glitches. (FIXME)
2152 //if(unlikely(process->pid != pid_out)) {
2153 // g_assert(process->pid == 0);
2154 //}
2155 if(process->pid == 0
2156 && process->state->t == LTTV_STATE_MODE_UNKNOWN) {
2157 if(pid_out == 0) {
2158 /* Scheduling out of pid 0 at beginning of the trace :
2159 * we know for sure it is in syscall mode at this point. */
2160 g_assert(process->execution_stack->len == 1);
2161 process->state->t = LTTV_STATE_SYSCALL;
2162 process->state->s = LTTV_STATE_WAIT;
2163 process->state->change = s->parent.timestamp;
2164 process->state->entry = s->parent.timestamp;
2165 }
2166 } else {
2167 if(unlikely(process->state->s == LTTV_STATE_EXIT)) {
2168 process->state->s = LTTV_STATE_ZOMBIE;
2169 process->state->change = s->parent.timestamp;
2170 } else {
2171 if(unlikely(state_out == 0)) process->state->s = LTTV_STATE_WAIT_CPU;
2172 else process->state->s = LTTV_STATE_WAIT;
2173 process->state->change = s->parent.timestamp;
2174 }
2175
2176 if(state_out == 32 || state_out == 128)
2177 exit_process(s, process); /* EXIT_DEAD || TASK_DEAD */
2178 /* see sched.h for states */
2179 }
2180 }
2181 process = ts->running_process[cpu] =
2182 lttv_state_find_process_or_create(
2183 (LttvTraceState*)s->parent.t_context,
2184 cpu, pid_in,
2185 &s->parent.timestamp);
2186 process->state->s = LTTV_STATE_RUN;
2187 process->cpu = cpu;
2188 if(process->usertrace)
2189 process->usertrace->cpu = cpu;
2190 // process->last_cpu_index = ltt_tracefile_num(((LttvTracefileContext*)s)->tf);
2191 process->state->change = s->parent.timestamp;
2192
2193 /* update cpu status */
2194 if(pid_in == 0)
2195 s->cpu_state->present_state = LTTV_CPU_IDLE;
2196 else
2197 s->cpu_state->present_state = LTTV_CPU_BUSY;
2198
2199 return FALSE;
2200 }
2201
2202 static gboolean process_fork(void *hook_data, void *call_data)
2203 {
2204 LttvTracefileState *s = (LttvTracefileState *)call_data;
2205 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2206 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2207 guint parent_pid;
2208 guint child_pid; /* In the Linux Kernel, there is one PID per thread. */
2209 guint child_tgid; /* tgid in the Linux kernel is the "real" POSIX PID. */
2210 LttvProcessState *zombie_process;
2211 guint cpu = s->cpu;
2212 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2213 LttvProcessState *process = ts->running_process[cpu];
2214 LttvProcessState *child_process;
2215
2216 /* Parent PID */
2217 parent_pid = ltt_event_get_unsigned(e, thf->f1);
2218
2219 /* Child PID */
2220 child_pid = ltt_event_get_unsigned(e, thf->f2);
2221 s->parent.target_pid = child_pid;
2222
2223 /* Child TGID */
2224 if(thf->f3) child_tgid = ltt_event_get_unsigned(e, thf->f3);
2225 else child_tgid = 0;
2226
2227 /* Mathieu : it seems like the process might have been scheduled in before the
2228 * fork, and, in a rare case, might be the current process. This might happen
2229 * in a SMP case where we don't have enough precision on the clocks.
2230 *
2231 * Test reenabled after precision fixes on time. (Mathieu) */
2232 #if 0
2233 zombie_process = lttv_state_find_process(ts, ANY_CPU, child_pid);
2234
2235 if(unlikely(zombie_process != NULL)) {
2236 /* Reutilisation of PID. Only now we are sure that the old PID
2237 * has been released. FIXME : should know when release_task happens instead.
2238 */
2239 guint num_cpus = ltt_trace_get_num_cpu(ts->parent.t);
2240 guint i;
2241 for(i=0; i< num_cpus; i++) {
2242 g_assert(zombie_process != ts->running_process[i]);
2243 }
2244
2245 exit_process(s, zombie_process);
2246 }
2247 #endif //0
2248 g_assert(process->pid != child_pid);
2249 // FIXME : Add this test in the "known state" section
2250 // g_assert(process->pid == parent_pid);
2251 child_process = lttv_state_find_process(ts, ANY_CPU, child_pid);
2252 if(child_process == NULL) {
2253 child_process = lttv_state_create_process(ts, process, cpu,
2254 child_pid, child_tgid,
2255 LTTV_STATE_UNNAMED, &s->parent.timestamp);
2256 } else {
2257 /* The process has already been created : due to time imprecision between
2258 * multiple CPUs : it has been scheduled in before creation. Note that we
2259 * shouldn't have this kind of imprecision.
2260 *
2261 * Simply put a correct parent.
2262 */
2263 g_assert(0); /* This is a problematic case : the process has been created
2264 before the fork event */
2265 child_process->ppid = process->pid;
2266 child_process->tgid = child_tgid;
2267 }
2268 g_assert(child_process->name == LTTV_STATE_UNNAMED);
2269 child_process->name = process->name;
2270 child_process->brand = process->brand;
2271
2272 return FALSE;
2273 }
2274
2275 /* We stamp a newly created process as kernel_thread.
2276 * The thread should not be running yet. */
2277 static gboolean process_kernel_thread(void *hook_data, void *call_data)
2278 {
2279 LttvTracefileState *s = (LttvTracefileState *)call_data;
2280 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2281 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2282 guint pid;
2283 guint cpu = s->cpu;
2284 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2285 LttvProcessState *process;
2286 LttvExecutionState *es;
2287
2288 /* PID */
2289 pid = (guint)ltt_event_get_long_unsigned(e, thf->f1);
2290 s->parent.target_pid = pid;
2291
2292 process = lttv_state_find_process_or_create(ts, ANY_CPU, pid,
2293 &ltt_time_zero);
2294 process->execution_stack =
2295 g_array_set_size(process->execution_stack, 1);
2296 es = process->state =
2297 &g_array_index(process->execution_stack, LttvExecutionState, 0);
2298 es->t = LTTV_STATE_SYSCALL;
2299 process->type = LTTV_STATE_KERNEL_THREAD;
2300
2301 return FALSE;
2302 }
2303
2304 static gboolean process_exit(void *hook_data, void *call_data)
2305 {
2306 LttvTracefileState *s = (LttvTracefileState *)call_data;
2307 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2308 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2309 LttField *f;
2310 guint pid;
2311 guint cpu = s->cpu;
2312 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2313 LttvProcessState *process; // = ts->running_process[cpu];
2314
2315 pid = ltt_event_get_unsigned(e, thf->f1);
2316 s->parent.target_pid = pid;
2317
2318 // FIXME : Add this test in the "known state" section
2319 // g_assert(process->pid == pid);
2320
2321 process = lttv_state_find_process(ts, ANY_CPU, pid);
2322 if(likely(process != NULL)) {
2323 process->state->s = LTTV_STATE_EXIT;
2324 }
2325 return FALSE;
2326 }
2327
2328 static gboolean process_free(void *hook_data, void *call_data)
2329 {
2330 LttvTracefileState *s = (LttvTracefileState *)call_data;
2331 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2332 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2333 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2334 guint release_pid;
2335 LttvProcessState *process;
2336
2337 /* PID of the process to release */
2338 release_pid = ltt_event_get_unsigned(e, thf->f1);
2339 s->parent.target_pid = release_pid;
2340
2341 g_assert(release_pid != 0);
2342
2343 process = lttv_state_find_process(ts, ANY_CPU, release_pid);
2344
2345 if(likely(process != NULL)) {
2346 /* release_task is happening at kernel level : we can now safely release
2347 * the data structure of the process */
2348 //This test is fun, though, as it may happen that
2349 //at time t : CPU 0 : process_free
2350 //at time t+150ns : CPU 1 : schedule out
2351 //Clearly due to time imprecision, we disable it. (Mathieu)
2352 //If this weird case happen, we have no choice but to put the
2353 //Currently running process on the cpu to 0.
2354 //I re-enable it following time precision fixes. (Mathieu)
2355 //Well, in the case where an process is freed by a process on another CPU
2356 //and still scheduled, it happens that this is the schedchange that will
2357 //drop the last reference count. Do not free it here!
2358 guint num_cpus = ltt_trace_get_num_cpu(ts->parent.t);
2359 guint i;
2360 for(i=0; i< num_cpus; i++) {
2361 //g_assert(process != ts->running_process[i]);
2362 if(process == ts->running_process[i]) {
2363 //ts->running_process[i] = lttv_state_find_process(ts, i, 0);
2364 break;
2365 }
2366 }
2367 if(i == num_cpus) /* process is not scheduled */
2368 exit_process(s, process);
2369 }
2370
2371 return FALSE;
2372 }
2373
2374
2375 static gboolean process_exec(void *hook_data, void *call_data)
2376 {
2377 LttvTracefileState *s = (LttvTracefileState *)call_data;
2378 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2379 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2380 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2381 //gchar *name;
2382 guint cpu = s->cpu;
2383 LttvProcessState *process = ts->running_process[cpu];
2384
2385 #if 0//how to use a sequence that must be transformed in a string
2386 /* PID of the process to release */
2387 guint64 name_len = ltt_event_field_element_number(e, thf->f1);
2388 //name = ltt_event_get_string(e, thf->f1);
2389 LttField *child = ltt_event_field_element_select(e, thf->f1, 0);
2390 gchar *name_begin =
2391 (gchar*)(ltt_event_data(e)+ltt_event_field_offset(e, child));
2392 gchar *null_term_name = g_new(gchar, name_len+1);
2393 memcpy(null_term_name, name_begin, name_len);
2394 null_term_name[name_len] = '\0';
2395 process->name = g_quark_from_string(null_term_name);
2396 #endif //0
2397
2398 process->name = g_quark_from_string(ltt_event_get_string(e, thf->f1));
2399 process->brand = LTTV_STATE_UNBRANDED;
2400 //g_free(null_term_name);
2401 return FALSE;
2402 }
2403
2404 static gboolean thread_brand(void *hook_data, void *call_data)
2405 {
2406 LttvTracefileState *s = (LttvTracefileState *)call_data;
2407 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2408 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2409 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2410 gchar *name;
2411 guint cpu = s->cpu;
2412 LttvProcessState *process = ts->running_process[cpu];
2413
2414 name = ltt_event_get_string(e, thf->f1);
2415 process->brand = g_quark_from_string(name);
2416
2417 return FALSE;
2418 }
2419
2420 static void fix_process(gpointer key, gpointer value,
2421 gpointer user_data)
2422 {
2423 LttvProcessState *process;
2424 LttvExecutionState *es;
2425 process = (LttvProcessState *)value;
2426 LttvTracefileContext *tfc = (LttvTracefileContext *)user_data;
2427 LttTime *timestamp = (LttTime*)user_data;
2428
2429 if(process->type == LTTV_STATE_KERNEL_THREAD) {
2430 es = &g_array_index(process->execution_stack, LttvExecutionState, 0);
2431 if(es->t == LTTV_STATE_MODE_UNKNOWN) {
2432 es->t = LTTV_STATE_SYSCALL;
2433 es->n = LTTV_STATE_SUBMODE_NONE;
2434 es->entry = *timestamp;
2435 es->change = *timestamp;
2436 es->cum_cpu_time = ltt_time_zero;
2437 if(es->s == LTTV_STATE_UNNAMED)
2438 es->s = LTTV_STATE_WAIT;
2439 }
2440 } else {
2441 es = &g_array_index(process->execution_stack, LttvExecutionState, 0);
2442 if(es->t == LTTV_STATE_MODE_UNKNOWN) {
2443 es->t = LTTV_STATE_USER_MODE;
2444 es->n = LTTV_STATE_SUBMODE_NONE;
2445 es->entry = *timestamp;
2446 //g_assert(timestamp->tv_sec != 0);
2447 es->change = *timestamp;
2448 es->cum_cpu_time = ltt_time_zero;
2449 if(es->s == LTTV_STATE_UNNAMED)
2450 es->s = LTTV_STATE_RUN;
2451
2452 if(process->execution_stack->len == 1) {
2453 /* Still in bottom unknown mode, means never did a system call
2454 * May be either in user mode, syscall mode, running or waiting.*/
2455 /* FIXME : we may be tagging syscall mode when being user mode */
2456 process->execution_stack =
2457 g_array_set_size(process->execution_stack, 2);
2458 es = process->state = &g_array_index(process->execution_stack,
2459 LttvExecutionState, 1);
2460 es->t = LTTV_STATE_SYSCALL;
2461 es->n = LTTV_STATE_SUBMODE_NONE;
2462 es->entry = *timestamp;
2463 //g_assert(timestamp->tv_sec != 0);
2464 es->change = *timestamp;
2465 es->cum_cpu_time = ltt_time_zero;
2466 if(es->s == LTTV_STATE_WAIT_FORK)
2467 es->s = LTTV_STATE_WAIT;
2468 }
2469 }
2470 }
2471 }
2472
2473 static gboolean statedump_end(void *hook_data, void *call_data)
2474 {
2475 LttvTracefileState *s = (LttvTracefileState *)call_data;
2476 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2477 LttvTracefileContext *tfc = (LttvTracefileContext *)call_data;
2478 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2479 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2480
2481 /* For all processes */
2482 /* if kernel thread, if stack[0] is unknown, set to syscall mode, wait */
2483 /* else, if stack[0] is unknown, set to user mode, running */
2484
2485 g_hash_table_foreach(ts->processes, fix_process, &tfc->timestamp);
2486 }
2487
2488 static gboolean enum_process_state(void *hook_data, void *call_data)
2489 {
2490 LttvTracefileState *s = (LttvTracefileState *)call_data;
2491 LttEvent *e = ltt_tracefile_get_event(s->parent.tf);
2492 //It's slow : optimise later by doing this before reading trace.
2493 LttEventType *et = ltt_event_eventtype(e);
2494 //
2495 LttvTraceHookByFacility *thf = (LttvTraceHookByFacility *)hook_data;
2496 guint parent_pid;
2497 guint pid;
2498 guint tgid;
2499 gchar * command;
2500 guint cpu = s->cpu;
2501 LttvTraceState *ts = (LttvTraceState*)s->parent.t_context;
2502 LttvProcessState *process = ts->running_process[cpu];
2503 LttvProcessState *parent_process;
2504 LttField *f4, *f5, *f6, *f7, *f8;
2505 GQuark type, mode, submode, status;
2506 LttvExecutionState *es;
2507 guint i, nb_cpus;
2508
2509 /* PID */
2510 pid = ltt_event_get_unsigned(e, thf->f1);
2511 s->parent.target_pid = pid;
2512
2513 /* Parent PID */
2514 parent_pid = ltt_event_get_unsigned(e, thf->f2);
2515
2516 /* Command name */
2517 command = ltt_event_get_string(e, thf->f3);
2518
2519 /* type */
2520 f4 = ltt_eventtype_field_by_name(et, LTT_FIELD_TYPE);
2521 type = ltt_enum_string_get(ltt_field_type(f4),
2522 ltt_event_get_unsigned(e, f4));
2523
2524 /* mode */
2525 f5 = ltt_eventtype_field_by_name(et, LTT_FIELD_MODE);
2526 mode = ltt_enum_string_get(ltt_field_type(f5),
2527 ltt_event_get_unsigned(e, f5));
2528
2529 /* submode */
2530 f6 = ltt_eventtype_field_by_name(et, LTT_FIELD_SUBMODE);
2531 submode = ltt_enum_string_get(ltt_field_type(f6),
2532 ltt_event_get_unsigned(e, f6));
2533
2534 /* status */
2535 f7 = ltt_eventtype_field_by_name(et, LTT_FIELD_STATUS);
2536 status = ltt_enum_string_get(ltt_field_type(f7),
2537 ltt_event_get_unsigned(e, f7));
2538
2539 /* TGID */
2540 f8 = ltt_eventtype_field_by_name(et, LTT_FIELD_TGID);
2541 if(f8) tgid = ltt_event_get_unsigned(e, f8);
2542 else tgid = 0;
2543
2544
2545 if(pid == 0) {
2546 nb_cpus = ltt_trace_get_num_cpu(ts->parent.t);
2547 for(i=0; i<nb_cpus; i++) {
2548 process = lttv_state_find_process(ts, i, pid);
2549 g_assert(process != NULL);
2550
2551 process->ppid = parent_pid;
2552 process->tgid = tgid;
2553 process->name = g_quark_from_string(command);
2554 es =
2555 &g_array_index(process->execution_stack, LttvExecutionState, 0);
2556 process->type = LTTV_STATE_KERNEL_THREAD;
2557 }
2558
2559 } else {
2560 /* The process might exist if a process was forked while performing the
2561 * state dump. */
2562 process = lttv_state_find_process(ts, ANY_CPU, pid);
2563 if(process == NULL) {
2564 parent_process = lttv_state_find_process(ts, ANY_CPU, parent_pid);
2565 process = lttv_state_create_process(ts, parent_process, cpu,
2566 pid, tgid, g_quark_from_string(command),
2567 &s->parent.timestamp);
2568
2569 /* Keep the stack bottom : a running user mode */
2570 /* Disabled because of inconsistencies in the current statedump states. */
2571 if(type == LTTV_STATE_KERNEL_THREAD) {
2572 /* Only keep the bottom
2573 * FIXME Kernel thread : can be in syscall or interrupt or trap. */
2574 /* Will cause expected trap when in fact being syscall (even after end of
2575 * statedump event)
2576 * Will cause expected interrupt when being syscall. (only before end of
2577 * statedump event) */
2578 // This will cause a "popping last state on stack, ignoring it."
2579 process->execution_stack = g_array_set_size(process->execution_stack, 1);
2580 es = process->state = &g_array_index(process->execution_stack,
2581 LttvExecutionState, 0);
2582 process->type = LTTV_STATE_KERNEL_THREAD;
2583 es->t = LTTV_STATE_MODE_UNKNOWN;
2584 es->s = LTTV_STATE_UNNAMED;
2585 es->n = LTTV_STATE_SUBMODE_UNKNOWN;
2586 #if 0
2587 es->t = LTTV_STATE_SYSCALL;
2588 es->s = status;
2589 es->n = submode;
2590 #endif //0
2591 } else {
2592 /* User space process :
2593 * bottom : user mode
2594 * either currently running or scheduled out.
2595 * can be scheduled out because interrupted in (user mode or in syscall)
2596 * or because of an explicit call to the scheduler in syscall. Note that
2597 * the scheduler call comes after the irq_exit, so never in interrupt
2598 * context. */
2599 // temp workaround : set size to 1 : only have user mode bottom of stack.
2600 // will cause g_info message of expected syscall mode when in fact being
2601 // in user mode. Can also cause expected trap when in fact being user
2602 // mode in the event of a page fault reenabling interrupts in the handler.
2603 // Expected syscall and trap can also happen after the end of statedump
2604 // This will cause a "popping last state on stack, ignoring it."
2605 process->execution_stack = g_array_set_size(process->execution_stack, 1);
2606 es = process->state = &g_array_index(process->execution_stack,
2607 LttvExecutionState, 0);
2608 es->t = LTTV_STATE_MODE_UNKNOWN;
2609 es->s = LTTV_STATE_UNNAMED;
2610 es->n = LTTV_STATE_SUBMODE_UNKNOWN;
2611 #if 0
2612 es->t = LTTV_STATE_USER_MODE;
2613 es->s = status;
2614 es->n = submode;
2615 #endif //0
2616 }
2617 #if 0
2618 /* UNKNOWN STATE */
2619 {
2620 es = process->state = &g_array_index(process->execution_stack,
2621 LttvExecutionState, 1);
2622 es->t = LTTV_STATE_MODE_UNKNOWN;
2623 es->s = LTTV_STATE_UNNAMED;
2624 es->n = LTTV_STATE_SUBMODE_UNKNOWN;
2625 }
2626 #endif //0
2627 } else {
2628 /* The process has already been created :
2629 * Probably was forked while dumping the process state or
2630 * was simply scheduled in prior to get the state dump event.
2631 */
2632 process->ppid = parent_pid;
2633 process->tgid = tgid;
2634 process->name = g_quark_from_string(command);
2635 process->type = type;
2636 es =
2637 &g_array_index(process->execution_stack, LttvExecutionState, 0);
2638 #if 0
2639 if(es->t == LTTV_STATE_MODE_UNKNOWN) {
2640 if(type == LTTV_STATE_KERNEL_THREAD)
2641 es->t = LTTV_STATE_SYSCALL;
2642 else
2643 es->t = LTTV_STATE_USER_MODE;
2644 }
2645 #endif //0
2646 /* Don't mess around with the stack, it will eventually become
2647 * ok after the end of state dump. */
2648 }
2649 }
2650
2651 return FALSE;
2652 }
2653
2654 gint lttv_state_hook_add_event_hooks(void *hook_data, void *call_data)
2655 {
2656 LttvTracesetState *tss = (LttvTracesetState*)(call_data);
2657
2658 lttv_state_add_event_hooks(tss);
2659
2660 return 0;
2661 }
2662
2663 void lttv_state_add_event_hooks(LttvTracesetState *self)
2664 {
2665 LttvTraceset *traceset = self->parent.ts;
2666
2667 guint i, j, k, l, nb_trace, nb_tracefile;
2668
2669 LttvTraceState *ts;
2670
2671 LttvTracefileState *tfs;
2672
2673 GArray *hooks;
2674
2675 LttvTraceHookByFacility *thf;
2676
2677 LttvTraceHook *hook;
2678
2679 LttvAttributeValue val;
2680
2681 gint ret;
2682 gint hn;
2683
2684 nb_trace = lttv_traceset_number(traceset);
2685 for(i = 0 ; i < nb_trace ; i++) {
2686 ts = (LttvTraceState *)self->parent.traces[i];
2687
2688 /* Find the eventtype id for the following events and register the
2689 associated by id hooks. */
2690
2691 hooks = g_array_sized_new(FALSE, FALSE, sizeof(LttvTraceHook), 19);
2692 hooks = g_array_set_size(hooks, 19); // Max possible number of hooks.
2693 hn = 0;
2694
2695 ret = lttv_trace_find_hook(ts->parent.t,
2696 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_SYSCALL_ENTRY,
2697 LTT_FIELD_SYSCALL_ID, 0, 0,
2698 syscall_entry, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2699 if(ret) hn--;
2700
2701 ret = lttv_trace_find_hook(ts->parent.t,
2702 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_SYSCALL_EXIT,
2703 0, 0, 0,
2704 syscall_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2705 if(ret) hn--;
2706
2707 ret = lttv_trace_find_hook(ts->parent.t,
2708 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_ENTRY,
2709 LTT_FIELD_TRAP_ID, 0, 0,
2710 trap_entry, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2711 if(ret) hn--;
2712
2713 ret = lttv_trace_find_hook(ts->parent.t,
2714 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_TRAP_EXIT,
2715 0, 0, 0,
2716 trap_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2717 if(ret) hn--;
2718
2719 ret = lttv_trace_find_hook(ts->parent.t,
2720 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_ENTRY,
2721 LTT_FIELD_IRQ_ID, 0, 0,
2722 irq_entry, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2723 if(ret) hn--;
2724
2725 ret = lttv_trace_find_hook(ts->parent.t,
2726 LTT_FACILITY_KERNEL, LTT_EVENT_IRQ_EXIT,
2727 0, 0, 0,
2728 irq_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2729 if(ret) hn--;
2730
2731 ret = lttv_trace_find_hook(ts->parent.t,
2732 LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_ENTRY,
2733 LTT_FIELD_SOFT_IRQ_ID, 0, 0,
2734 soft_irq_entry, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2735 if(ret) hn--;
2736
2737 ret = lttv_trace_find_hook(ts->parent.t,
2738 LTT_FACILITY_KERNEL, LTT_EVENT_SOFT_IRQ_EXIT,
2739 0, 0, 0,
2740 soft_irq_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2741 if(ret) hn--;
2742
2743 ret = lttv_trace_find_hook(ts->parent.t,
2744 LTT_FACILITY_KERNEL, LTT_EVENT_SCHED_SCHEDULE,
2745 LTT_FIELD_PREV_PID, LTT_FIELD_NEXT_PID, LTT_FIELD_PREV_STATE,
2746 schedchange, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2747 if(ret) hn--;
2748
2749 ret = lttv_trace_find_hook(ts->parent.t,
2750 LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_FORK,
2751 LTT_FIELD_PARENT_PID, LTT_FIELD_CHILD_PID, LTT_FIELD_CHILD_TGID,
2752 process_fork, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2753 if(ret) hn--;
2754
2755 ret = lttv_trace_find_hook(ts->parent.t,
2756 LTT_FACILITY_KERNEL_ARCH, LTT_EVENT_KTHREAD_CREATE,
2757 LTT_FIELD_PID, 0, 0,
2758 process_kernel_thread, NULL, &g_array_index(hooks, LttvTraceHook,
2759 hn++));
2760 if(ret) hn--;
2761
2762 ret = lttv_trace_find_hook(ts->parent.t,
2763 LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_EXIT,
2764 LTT_FIELD_PID, 0, 0,
2765 process_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2766 if(ret) hn--;
2767
2768 ret = lttv_trace_find_hook(ts->parent.t,
2769 LTT_FACILITY_KERNEL, LTT_EVENT_PROCESS_FREE,
2770 LTT_FIELD_PID, 0, 0,
2771 process_free, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2772 if(ret) hn--;
2773
2774 ret = lttv_trace_find_hook(ts->parent.t,
2775 LTT_FACILITY_FS, LTT_EVENT_EXEC,
2776 LTT_FIELD_FILENAME, 0, 0,
2777 process_exec, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2778 if(ret) hn--;
2779
2780 ret = lttv_trace_find_hook(ts->parent.t,
2781 LTT_FACILITY_USER_GENERIC, LTT_EVENT_THREAD_BRAND,
2782 LTT_FIELD_NAME, 0, 0,
2783 thread_brand, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2784 if(ret) hn--;
2785
2786 /* statedump-related hooks */
2787 ret = lttv_trace_find_hook(ts->parent.t,
2788 LTT_FACILITY_LIST, LTT_EVENT_PROCESS_STATE,
2789 LTT_FIELD_PID, LTT_FIELD_PARENT_PID, LTT_FIELD_NAME,
2790 enum_process_state, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2791 if(ret) hn--;
2792
2793 ret = lttv_trace_find_hook(ts->parent.t,
2794 LTT_FACILITY_LIST, LTT_EVENT_STATEDUMP_END,
2795 0, 0, 0,
2796 statedump_end, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2797 if(ret) hn--;
2798
2799 ret = lttv_trace_find_hook(ts->parent.t,
2800 LTT_FACILITY_USER_GENERIC, LTT_EVENT_FUNCTION_ENTRY,
2801 LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE, 0,
2802 function_entry, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2803 if(ret) hn--;
2804
2805 ret = lttv_trace_find_hook(ts->parent.t,
2806 LTT_FACILITY_USER_GENERIC, LTT_EVENT_FUNCTION_EXIT,
2807 LTT_FIELD_THIS_FN, LTT_FIELD_CALL_SITE, 0,
2808 function_exit, NULL, &g_array_index(hooks, LttvTraceHook, hn++));
2809 if(ret) hn--;
2810
2811 hooks = g_array_set_size(hooks, hn);
2812
2813 /* Add these hooks to each event_by_id hooks list */
2814
2815 nb_tracefile = ts->parent.tracefiles->len;
2816
2817 for(j = 0 ; j < nb_tracefile ; j++) {
2818 tfs =
2819 LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles,
2820 LttvTracefileContext*, j));
2821
2822 for(k = 0 ; k < hooks->len ; k++) {
2823 hook = &g_array_index(hooks, LttvTraceHook, k);
2824 for(l=0;l<hook->fac_list->len;l++) {
2825 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
2826 lttv_hooks_add(
2827 lttv_hooks_by_id_find(tfs->parent.event_by_id, thf->id),
2828 thf->h,
2829 thf,
2830 LTTV_PRIO_STATE);
2831 }
2832 }
2833 }
2834 lttv_attribute_find(ts->parent.a, LTTV_STATE_HOOKS, LTTV_POINTER, &val);
2835 *(val.v_pointer) = hooks;
2836 }
2837 }
2838
2839 gint lttv_state_hook_remove_event_hooks(void *hook_data, void *call_data)
2840 {
2841 LttvTracesetState *tss = (LttvTracesetState*)(call_data);
2842
2843 lttv_state_remove_event_hooks(tss);
2844
2845 return 0;
2846 }
2847
2848 void lttv_state_remove_event_hooks(LttvTracesetState *self)
2849 {
2850 LttvTraceset *traceset = self->parent.ts;
2851
2852 guint i, j, k, l, nb_trace, nb_tracefile;
2853
2854 LttvTraceState *ts;
2855
2856 LttvTracefileState *tfs;
2857
2858 GArray *hooks;
2859
2860 LttvTraceHook *hook;
2861
2862 LttvTraceHookByFacility *thf;
2863
2864 LttvAttributeValue val;
2865
2866 nb_trace = lttv_traceset_number(traceset);
2867 for(i = 0 ; i < nb_trace ; i++) {
2868 ts = LTTV_TRACE_STATE(self->parent.traces[i]);
2869
2870 lttv_attribute_find(ts->parent.a, LTTV_STATE_HOOKS, LTTV_POINTER, &val);
2871 hooks = *(val.v_pointer);
2872
2873 /* Remove these hooks from each event_by_id hooks list */
2874
2875 nb_tracefile = ts->parent.tracefiles->len;
2876
2877 for(j = 0 ; j < nb_tracefile ; j++) {
2878 tfs =
2879 LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles,
2880 LttvTracefileContext*, j));
2881
2882 for(k = 0 ; k < hooks->len ; k++) {
2883 hook = &g_array_index(hooks, LttvTraceHook, k);
2884 for(l=0;l<hook->fac_list->len;l++) {
2885 thf = g_array_index(hook->fac_list, LttvTraceHookByFacility*, l);
2886
2887 lttv_hooks_remove_data(
2888 lttv_hooks_by_id_find(tfs->parent.event_by_id, thf->id),
2889 thf->h,
2890 thf);
2891 }
2892 }
2893 }
2894 for(k = 0 ; k < hooks->len ; k++)
2895 lttv_trace_hook_destroy(&g_array_index(hooks, LttvTraceHook, k));
2896 g_array_free(hooks, TRUE);
2897 }
2898 }
2899
2900 static gboolean state_save_event_hook(void *hook_data, void *call_data)
2901 {
2902 guint *event_count = (guint*)hook_data;
2903
2904 /* Only save at LTTV_STATE_SAVE_INTERVAL */
2905 if(likely((*event_count)++ < LTTV_STATE_SAVE_INTERVAL))
2906 return FALSE;
2907 else
2908 *event_count = 0;
2909
2910 LttvTracefileState *self = (LttvTracefileState *)call_data;
2911
2912 LttvTracefileState *tfcs;
2913
2914 LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context);
2915
2916 LttEventPosition *ep;
2917
2918 guint i;
2919
2920 LttTracefile *tf;
2921
2922 LttvAttribute *saved_states_tree, *saved_state_tree;
2923
2924 LttvAttributeValue value;
2925
2926 saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a,
2927 LTTV_STATE_SAVED_STATES);
2928 saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
2929 value = lttv_attribute_add(saved_states_tree,
2930 lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT);
2931 *(value.v_gobject) = (GObject *)saved_state_tree;
2932 value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME);
2933 *(value.v_time) = self->parent.timestamp;
2934 lttv_state_save(tcs, saved_state_tree);
2935 g_debug("Saving state at time %lu.%lu", self->parent.timestamp.tv_sec,
2936 self->parent.timestamp.tv_nsec);
2937
2938 *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp;
2939
2940 return FALSE;
2941 }
2942
2943 static gboolean state_save_after_trace_hook(void *hook_data, void *call_data)
2944 {
2945 LttvTraceState *tcs = (LttvTraceState *)(call_data);
2946
2947 *(tcs->max_time_state_recomputed_in_seek) = tcs->parent.time_span.end_time;
2948
2949 return FALSE;
2950 }
2951
2952 guint lttv_state_current_cpu(LttvTracefileState *tfs)
2953 {
2954 return tfs->cpu;
2955 }
2956
2957
2958
2959 #if 0
2960 static gboolean block_start(void *hook_data, void *call_data)
2961 {
2962 LttvTracefileState *self = (LttvTracefileState *)call_data;
2963
2964 LttvTracefileState *tfcs;
2965
2966 LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context);
2967
2968 LttEventPosition *ep;
2969
2970 guint i, nb_block, nb_event, nb_tracefile;
2971
2972 LttTracefile *tf;
2973
2974 LttvAttribute *saved_states_tree, *saved_state_tree;
2975
2976 LttvAttributeValue value;
2977
2978 ep = ltt_event_position_new();
2979
2980 nb_tracefile = tcs->parent.tracefiles->len;
2981
2982 /* Count the number of events added since the last block end in any
2983 tracefile. */
2984
2985 for(i = 0 ; i < nb_tracefile ; i++) {
2986 tfcs =
2987 LTTV_TRACEFILE_STATE(&g_array_index(tcs->parent.tracefiles,
2988 LttvTracefileContext, i));
2989 ltt_event_position(tfcs->parent.e, ep);
2990 ltt_event_position_get(ep, &nb_block, &nb_event, &tf);
2991 tcs->nb_event += nb_event - tfcs->saved_position;
2992 tfcs->saved_position = nb_event;
2993 }
2994 g_free(ep);
2995
2996 if(tcs->nb_event >= tcs->save_interval) {
2997 saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a,
2998 LTTV_STATE_SAVED_STATES);
2999 saved_state_tree = g_object_new(LTTV_ATTRIBUTE_TYPE, NULL);
3000 value = lttv_attribute_add(saved_states_tree,
3001 lttv_attribute_get_number(saved_states_tree), LTTV_GOBJECT);
3002 *(value.v_gobject) = (GObject *)saved_state_tree;
3003 value = lttv_attribute_add(saved_state_tree, LTTV_STATE_TIME, LTTV_TIME);
3004 *(value.v_time) = self->parent.timestamp;
3005 lttv_state_save(tcs, saved_state_tree);
3006 tcs->nb_event = 0;
3007 g_debug("Saving state at time %lu.%lu", self->parent.timestamp.tv_sec,
3008 self->parent.timestamp.tv_nsec);
3009 }
3010 *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp;
3011 return FALSE;
3012 }
3013 #endif //0
3014
3015 #if 0
3016 static gboolean block_end(void *hook_data, void *call_data)
3017 {
3018 LttvTracefileState *self = (LttvTracefileState *)call_data;
3019
3020 LttvTraceState *tcs = (LttvTraceState *)(self->parent.t_context);
3021
3022 LttTracefile *tf;
3023
3024 LttEventPosition *ep;
3025
3026 guint nb_block, nb_event;
3027
3028 ep = ltt_event_position_new();
3029 ltt_event_position(self->parent.e, ep);
3030 ltt_event_position_get(ep, &nb_block, &nb_event, &tf);
3031 tcs->nb_event += nb_event - self->saved_position + 1;
3032 self->saved_position = 0;
3033 *(tcs->max_time_state_recomputed_in_seek) = self->parent.timestamp;
3034 g_free(ep);
3035
3036 return FALSE;
3037 }
3038 #endif //0
3039 #if 0
3040 void lttv_state_save_add_event_hooks(LttvTracesetState *self)
3041 {
3042 LttvTraceset *traceset = self->parent.ts;
3043
3044 guint i, j, nb_trace, nb_tracefile;
3045
3046 LttvTraceState *ts;
3047
3048 LttvTracefileState *tfs;
3049
3050 LttvTraceHook hook_start, hook_end;
3051
3052 nb_trace = lttv_traceset_number(traceset);
3053 for(i = 0 ; i < nb_trace ; i++) {
3054 ts = (LttvTraceState *)self->parent.traces[i];
3055
3056 lttv_trace_find_hook(ts->parent.t, "core","block_start",NULL,
3057 NULL, NULL, block_start, &hook_start);
3058 lttv_trace_find_hook(ts->parent.t, "core","block_end",NULL,
3059 NULL, NULL, block_end, &hook_end);
3060
3061 nb_tracefile = ts->parent.tracefiles->len;
3062
3063 for(j = 0 ; j < nb_tracefile ; j++) {
3064 tfs =
3065 LTTV_TRACEFILE_STATE(&g_array_index(ts->parent.tracefiles,
3066 LttvTracefileContext, j));
3067 lttv_hooks_add(lttv_hooks_by_id_find(tfs->parent.event_by_id,
3068 hook_start.id), hook_start.h, NULL, LTTV_PRIO_STATE);
3069 lttv_hooks_add(lttv_hooks_by_id_find(tfs->parent.event_by_id,
3070 hook_end.id), hook_end.h, NULL, LTTV_PRIO_STATE);
3071 }
3072 }
3073 }
3074 #endif //0
3075
3076 void lttv_state_save_add_event_hooks(LttvTracesetState *self)
3077 {
3078 LttvTraceset *traceset = self->parent.ts;
3079
3080 guint i, j, nb_trace, nb_tracefile;
3081
3082 LttvTraceState *ts;
3083
3084 LttvTracefileState *tfs;
3085
3086
3087 nb_trace = lttv_traceset_number(traceset);
3088 for(i = 0 ; i < nb_trace ; i++) {
3089
3090 ts = (LttvTraceState *)self->parent.traces[i];
3091 nb_tracefile = ts->parent.tracefiles->len;
3092
3093 if(ts->has_precomputed_states) continue;
3094
3095 guint *event_count = g_new(guint, 1);
3096 *event_count = 0;
3097
3098 for(j = 0 ; j < nb_tracefile ; j++) {
3099 tfs =
3100 LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles,
3101 LttvTracefileContext*, j));
3102 lttv_hooks_add(tfs->parent.event,
3103 state_save_event_hook,
3104 event_count,
3105 LTTV_PRIO_STATE);
3106
3107 }
3108 }
3109
3110 lttv_process_traceset_begin(&self->parent,
3111 NULL, NULL, NULL, NULL, NULL);
3112
3113 }
3114
3115 gint lttv_state_save_hook_add_event_hooks(void *hook_data, void *call_data)
3116 {
3117 LttvTracesetState *tss = (LttvTracesetState*)(call_data);
3118
3119 lttv_state_save_add_event_hooks(tss);
3120
3121 return 0;
3122 }
3123
3124
3125 #if 0
3126 void lttv_state_save_remove_event_hooks(LttvTracesetState *self)
3127 {
3128 LttvTraceset *traceset = self->parent.ts;
3129
3130 guint i, j, nb_trace, nb_tracefile;
3131
3132 LttvTraceState *ts;
3133
3134 LttvTracefileState *tfs;
3135
3136 LttvTraceHook hook_start, hook_end;
3137
3138 nb_trace = lttv_traceset_number(traceset);
3139 for(i = 0 ; i < nb_trace ; i++) {
3140 ts = LTTV_TRACE_STATE(self->parent.traces[i]);
3141
3142 lttv_trace_find_hook(ts->parent.t, "core","block_start",NULL,
3143 NULL, NULL, block_start, &hook_start);
3144
3145 lttv_trace_find_hook(ts->parent.t, "core","block_end",NULL,
3146 NULL, NULL, block_end, &hook_end);
3147
3148 nb_tracefile = ts->parent.tracefiles->len;
3149
3150 for(j = 0 ; j < nb_tracefile ; j++) {
3151 tfs =
3152 LTTV_TRACEFILE_STATE(&g_array_index(ts->parent.tracefiles,
3153 LttvTracefileContext, j));
3154 lttv_hooks_remove_data(lttv_hooks_by_id_find(
3155 tfs->parent.event_by_id, hook_start.id), hook_start.h, NULL);
3156 lttv_hooks_remove_data(lttv_hooks_by_id_find(
3157 tfs->parent.event_by_id, hook_end.id), hook_end.h, NULL);
3158 }
3159 }
3160 }
3161 #endif //0
3162
3163 void lttv_state_save_remove_event_hooks(LttvTracesetState *self)
3164 {
3165 LttvTraceset *traceset = self->parent.ts;
3166
3167 guint i, j, nb_trace, nb_tracefile;
3168
3169 LttvTraceState *ts;
3170
3171 LttvTracefileState *tfs;
3172
3173 LttvHooks *after_trace = lttv_hooks_new();
3174
3175 lttv_hooks_add(after_trace,
3176 state_save_after_trace_hook,
3177 NULL,
3178 LTTV_PRIO_STATE);
3179
3180
3181 lttv_process_traceset_end(&self->parent,
3182 NULL, after_trace, NULL, NULL, NULL);
3183
3184 lttv_hooks_destroy(after_trace);
3185
3186 nb_trace = lttv_traceset_number(traceset);
3187 for(i = 0 ; i < nb_trace ; i++) {
3188
3189 ts = (LttvTraceState *)self->parent.traces[i];
3190 nb_tracefile = ts->parent.tracefiles->len;
3191
3192 if(ts->has_precomputed_states) continue;
3193
3194 guint *event_count = NULL;
3195
3196 for(j = 0 ; j < nb_tracefile ; j++) {
3197 tfs =
3198 LTTV_TRACEFILE_STATE(g_array_index(ts->parent.tracefiles,
3199 LttvTracefileContext*, j));
3200 event_count = lttv_hooks_remove(tfs->parent.event,
3201 state_save_event_hook);
3202 }
3203 if(event_count) g_free(event_count);
3204 }
3205 }
3206
3207 gint lttv_state_save_hook_remove_event_hooks(void *hook_data, void *call_data)
3208 {
3209 LttvTracesetState *tss = (LttvTracesetState*)(call_data);
3210
3211 lttv_state_save_remove_event_hooks(tss);
3212
3213 return 0;
3214 }
3215
3216 void lttv_state_traceset_seek_time_closest(LttvTracesetState *self, LttTime t)
3217 {
3218 LttvTraceset *traceset = self->parent.ts;
3219
3220 guint i, nb_trace;
3221
3222 int min_pos, mid_pos, max_pos;
3223
3224 guint call_rest = 0;
3225
3226 LttvTraceState *tcs;
3227
3228 LttvAttributeValue value;
3229
3230 LttvAttributeType type;
3231
3232 LttvAttributeName name;
3233
3234 gboolean is_named;
3235
3236 LttvAttribute *saved_states_tree, *saved_state_tree, *closest_tree;
3237
3238 //g_tree_destroy(self->parent.pqueue);
3239 //self->parent.pqueue = g_tree_new(compare_tracefile);
3240
3241 g_info("Entering seek_time_closest for time %lu.%lu", t.tv_sec, t.tv_nsec);
3242
3243 nb_trace = lttv_traceset_number(traceset);
3244 for(i = 0 ; i < nb_trace ; i++) {
3245 tcs = (LttvTraceState *)self->parent.traces[i];
3246
3247 if(ltt_time_compare(t, *(tcs->max_time_state_recomputed_in_seek)) < 0) {
3248 saved_states_tree = lttv_attribute_find_subdir(tcs->parent.t_a,
3249 LTTV_STATE_SAVED_STATES);
3250 min_pos = -1;
3251
3252 if(saved_states_tree) {
3253 max_pos = lttv_attribute_get_number(saved_states_tree) - 1;
3254 mid_pos = max_pos / 2;
3255 while(min_pos < max_pos) {
3256 type = lttv_attribute_get(saved_states_tree, mid_pos, &name, &value,
3257 &is_named);
3258 g_assert(type == LTTV_GOBJECT);
3259 saved_state_tree = *((LttvAttribute **)(value.v_gobject));
3260 type = lttv_attribute_get_by_name(saved_state_tree, LTTV_STATE_TIME,
3261 &value);
3262 g_assert(type == LTTV_TIME);
3263 if(ltt_time_compare(*(value.v_time), t) < 0) {
3264 min_pos = mid_pos;
3265 closest_tree = saved_state_tree;
3266 }
3267 else max_pos = mid_pos - 1;
3268
3269 mid_pos = (min_pos + max_pos + 1) / 2;
3270 }
3271 }
3272
3273 /* restore the closest earlier saved state */
3274 if(min_pos != -1) {
3275 lttv_state_restore(tcs, closest_tree);
3276 call_rest = 1;
3277 }
3278
3279 /* There is no saved state, yet we want to have it. Restart at T0 */
3280 else {
3281 restore_init_state(tcs);
3282 lttv_process_trace_seek_time(&(tcs->parent), ltt_time_zero);
3283 }
3284 }
3285 /* We want to seek quickly without restoring/updating the state */
3286 else {
3287 restore_init_state(tcs);
3288 lttv_process_trace_seek_time(&(tcs->parent), t);
3289 }
3290 }
3291 if(!call_rest) g_info("NOT Calling restore");
3292 }
3293
3294
3295 static void
3296 traceset_state_instance_init (GTypeInstance *instance, gpointer g_class)
3297 {
3298 }
3299
3300
3301 static void
3302 traceset_state_finalize (LttvTracesetState *self)
3303 {
3304 G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACESET_CONTEXT_TYPE))->
3305 finalize(G_OBJECT(self));
3306 }
3307
3308
3309 static void
3310 traceset_state_class_init (LttvTracesetContextClass *klass)
3311 {
3312 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
3313
3314 gobject_class->finalize = (void (*)(GObject *self)) traceset_state_finalize;
3315 klass->init = (void (*)(LttvTracesetContext *self, LttvTraceset *ts))init;
3316 klass->fini = (void (*)(LttvTracesetContext *self))fini;
3317 klass->new_traceset_context = new_traceset_context;
3318 klass->new_trace_context = new_trace_context;
3319 klass->new_tracefile_context = new_tracefile_context;
3320 }
3321
3322
3323 GType
3324 lttv_traceset_state_get_type(void)
3325 {
3326 static GType type = 0;
3327 if (type == 0) {
3328 static const GTypeInfo info = {
3329 sizeof (LttvTracesetStateClass),
3330 NULL, /* base_init */
3331 NULL, /* base_finalize */
3332 (GClassInitFunc) traceset_state_class_init, /* class_init */
3333 NULL, /* class_finalize */
3334 NULL, /* class_data */
3335 sizeof (LttvTracesetState),
3336 0, /* n_preallocs */
3337 (GInstanceInitFunc) traceset_state_instance_init, /* instance_init */
3338 NULL /* value handling */
3339 };
3340
3341 type = g_type_register_static (LTTV_TRACESET_CONTEXT_TYPE, "LttvTracesetStateType",
3342 &info, 0);
3343 }
3344 return type;
3345 }
3346
3347
3348 static void
3349 trace_state_instance_init (GTypeInstance *instance, gpointer g_class)
3350 {
3351 }
3352
3353
3354 static void
3355 trace_state_finalize (LttvTraceState *self)
3356 {
3357 G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACE_CONTEXT_TYPE))->
3358 finalize(G_OBJECT(self));
3359 }
3360
3361
3362 static void
3363 trace_state_class_init (LttvTraceStateClass *klass)
3364 {
3365 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
3366
3367 gobject_class->finalize = (void (*)(GObject *self)) trace_state_finalize;
3368 klass->state_save = state_save;
3369 klass->state_restore = state_restore;
3370 klass->state_saved_free = state_saved_free;
3371 }
3372
3373
3374 GType
3375 lttv_trace_state_get_type(void)
3376 {
3377 static GType type = 0;
3378 if (type == 0) {
3379 static const GTypeInfo info = {
3380 sizeof (LttvTraceStateClass),
3381 NULL, /* base_init */
3382 NULL, /* base_finalize */
3383 (GClassInitFunc) trace_state_class_init, /* class_init */
3384 NULL, /* class_finalize */
3385 NULL, /* class_data */
3386 sizeof (LttvTraceState),
3387 0, /* n_preallocs */
3388 (GInstanceInitFunc) trace_state_instance_init, /* instance_init */
3389 NULL /* value handling */
3390 };
3391
3392 type = g_type_register_static (LTTV_TRACE_CONTEXT_TYPE,
3393 "LttvTraceStateType", &info, 0);
3394 }
3395 return type;
3396 }
3397
3398
3399 static void
3400 tracefile_state_instance_init (GTypeInstance *instance, gpointer g_class)
3401 {
3402 }
3403
3404
3405 static void
3406 tracefile_state_finalize (LttvTracefileState *self)
3407 {
3408 G_OBJECT_CLASS(g_type_class_peek(LTTV_TRACEFILE_CONTEXT_TYPE))->
3409 finalize(G_OBJECT(self));
3410 }
3411
3412
3413 static void
3414 tracefile_state_class_init (LttvTracefileStateClass *klass)
3415 {
3416 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
3417
3418 gobject_class->finalize = (void (*)(GObject *self)) tracefile_state_finalize;
3419 }
3420
3421
3422 GType
3423 lttv_tracefile_state_get_type(void)
3424 {
3425 static GType type = 0;
3426 if (type == 0) {
3427 static const GTypeInfo info = {
3428 sizeof (LttvTracefileStateClass),
3429 NULL, /* base_init */
3430 NULL, /* base_finalize */
3431 (GClassInitFunc) tracefile_state_class_init, /* class_init */
3432 NULL, /* class_finalize */
3433 NULL, /* class_data */
3434 sizeof (LttvTracefileState),
3435 0, /* n_preallocs */
3436 (GInstanceInitFunc) tracefile_state_instance_init, /* instance_init */
3437 NULL /* value handling */
3438 };
3439
3440 type = g_type_register_static (LTTV_TRACEFILE_CONTEXT_TYPE,
3441 "LttvTracefileStateType", &info, 0);
3442 }
3443 return type;
3444 }
3445
3446
3447 static void module_init()
3448 {
3449 LTTV_STATE_UNNAMED = g_quark_from_string("UNNAMED");
3450 LTTV_STATE_UNBRANDED = g_quark_from_string("UNBRANDED");
3451 LTTV_STATE_MODE_UNKNOWN = g_quark_from_string("MODE_UNKNOWN");
3452 LTTV_STATE_USER_MODE = g_quark_from_string("USER_MODE");
3453 LTTV_STATE_SYSCALL = g_quark_from_string("SYSCALL");
3454 LTTV_STATE_TRAP = g_quark_from_string("TRAP");
3455 LTTV_STATE_IRQ = g_quark_from_string("IRQ");
3456 LTTV_STATE_SOFT_IRQ = g_quark_from_string("SOFTIRQ");
3457 LTTV_STATE_SUBMODE_UNKNOWN = g_quark_from_string("UNKNOWN");
3458 LTTV_STATE_SUBMODE_NONE = g_quark_from_string("NONE");
3459 LTTV_STATE_WAIT_FORK = g_quark_from_string("WAIT_FORK");
3460 LTTV_STATE_WAIT_CPU = g_quark_from_string("WAIT_CPU");
3461 LTTV_STATE_EXIT = g_quark_from_string("EXIT");
3462 LTTV_STATE_ZOMBIE = g_quark_from_string("ZOMBIE");
3463 LTTV_STATE_WAIT = g_quark_from_string("WAIT");
3464 LTTV_STATE_RUN = g_quark_from_string("RUN");
3465 LTTV_STATE_DEAD = g_quark_from_string("DEAD");
3466 LTTV_STATE_USER_THREAD = g_quark_from_string("USER_THREAD");
3467 LTTV_STATE_KERNEL_THREAD = g_quark_from_string("KERNEL_THREAD");
3468 LTTV_STATE_TRACEFILES = g_quark_from_string("tracefiles");
3469 LTTV_STATE_PROCESSES = g_quark_from_string("processes");
3470 LTTV_STATE_PROCESS = g_quark_from_string("process");
3471 LTTV_STATE_RUNNING_PROCESS = g_quark_from_string("running_process");
3472 LTTV_STATE_EVENT = g_quark_from_string("event");
3473 LTTV_STATE_SAVED_STATES = g_quark_from_string("saved states");
3474 LTTV_STATE_SAVED_STATES_TIME = g_quark_from_string("saved states time");
3475 LTTV_STATE_TIME = g_quark_from_string("time");
3476 LTTV_STATE_HOOKS = g_quark_from_string("saved state hooks");
3477 LTTV_STATE_NAME_TABLES = g_quark_from_string("name tables");
3478 LTTV_STATE_TRACE_STATE_USE_COUNT =
3479 g_quark_from_string("trace_state_use_count");
3480
3481
3482 LTT_FACILITY_KERNEL = g_quark_from_string("kernel");
3483 LTT_FACILITY_KERNEL_ARCH = g_quark_from_string("kernel_arch");
3484 LTT_FACILITY_FS = g_quark_from_string("fs");
3485 LTT_FACILITY_LIST = g_quark_from_string("list");
3486 LTT_FACILITY_USER_GENERIC = g_quark_from_string("user_generic");
3487
3488
3489 LTT_EVENT_SYSCALL_ENTRY = g_quark_from_string("syscall_entry");
3490 LTT_EVENT_SYSCALL_EXIT = g_quark_from_string("syscall_exit");
3491 LTT_EVENT_TRAP_ENTRY = g_quark_from_string("trap_entry");
3492 LTT_EVENT_TRAP_EXIT = g_quark_from_string("trap_exit");
3493 LTT_EVENT_IRQ_ENTRY = g_quark_from_string("irq_entry");
3494 LTT_EVENT_IRQ_EXIT = g_quark_from_string("irq_exit");
3495 LTT_EVENT_SOFT_IRQ_ENTRY = g_quark_from_string("soft_irq_entry");
3496 LTT_EVENT_SOFT_IRQ_EXIT = g_quark_from_string("soft_irq_exit");
3497 LTT_EVENT_SCHED_SCHEDULE = g_quark_from_string("sched_schedule");
3498 LTT_EVENT_PROCESS_FORK = g_quark_from_string("process_fork");
3499 LTT_EVENT_KTHREAD_CREATE = g_quark_from_string("kthread_create");
3500 LTT_EVENT_PROCESS_EXIT = g_quark_from_string("process_exit");
3501 LTT_EVENT_PROCESS_FREE = g_quark_from_string("process_free");
3502 LTT_EVENT_EXEC = g_quark_from_string("exec");
3503 LTT_EVENT_PROCESS_STATE = g_quark_from_string("process_state");
3504 LTT_EVENT_STATEDUMP_END = g_quark_from_string("statedump_end");
3505 LTT_EVENT_FUNCTION_ENTRY = g_quark_from_string("function_entry");
3506 LTT_EVENT_FUNCTION_EXIT = g_quark_from_string("function_exit");
3507 LTT_EVENT_THREAD_BRAND = g_quark_from_string("thread_brand");
3508
3509
3510 LTT_FIELD_SYSCALL_ID = g_quark_from_string("syscall_id");
3511 LTT_FIELD_TRAP_ID = g_quark_from_string("trap_id");
3512 LTT_FIELD_IRQ_ID = g_quark_from_string("irq_id");
3513 LTT_FIELD_SOFT_IRQ_ID = g_quark_from_string("softirq_id");
3514 LTT_FIELD_PREV_PID = g_quark_from_string("prev_pid");
3515 LTT_FIELD_NEXT_PID = g_quark_from_string("next_pid");
3516 LTT_FIELD_PREV_STATE = g_quark_from_string("prev_state");
3517 LTT_FIELD_PARENT_PID = g_quark_from_string("parent_pid");
3518 LTT_FIELD_CHILD_PID = g_quark_from_string("child_pid");
3519 LTT_FIELD_PID = g_quark_from_string("pid");
3520 LTT_FIELD_TGID = g_quark_from_string("tgid");
3521 LTT_FIELD_CHILD_TGID = g_quark_from_string("child_tgid");
3522 LTT_FIELD_FILENAME = g_quark_from_string("filename");
3523 LTT_FIELD_NAME = g_quark_from_string("name");
3524 LTT_FIELD_TYPE = g_quark_from_string("type");
3525 LTT_FIELD_MODE = g_quark_from_string("mode");
3526 LTT_FIELD_SUBMODE = g_quark_from_string("submode");
3527 LTT_FIELD_STATUS = g_quark_from_string("status");
3528 LTT_FIELD_THIS_FN = g_quark_from_string("this_fn");
3529 LTT_FIELD_CALL_SITE = g_quark_from_string("call_site");
3530
3531 LTTV_CPU_UNKNOWN = g_quark_from_string("unknown");
3532 LTTV_CPU_IDLE = g_quark_from_string("idle");
3533 LTTV_CPU_BUSY = g_quark_from_string("busy");
3534 }
3535
3536 static void module_destroy()
3537 {
3538 }
3539
3540
3541 LTTV_MODULE("state", "State computation", \
3542 "Update the system state, possibly saving it at intervals", \
3543 module_init, module_destroy)
3544
3545
3546
This page took 0.101511 seconds and 4 git commands to generate.