File: eztrace_convert_gomp.c

package info (click to toggle)
eztrace 0.7-2-4
  • links: PTS, VCS
  • area: main
  • in suites: wheezy
  • size: 3,048 kB
  • sloc: ansic: 12,839; sh: 11,343; perl: 1,074; makefile: 567; f90: 204; cpp: 170; fortran: 49
file content (754 lines) | stat: -rw-r--r-- 22,498 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright  CNRS, INRIA, Universit Bordeaux 1
 * See COPYING in top-level directory.
 */

#define _GNU_SOURCE
#include <stdio.h>
#include <GTG.h>
#include <assert.h>
#include "gomp_ev_codes.h"
#include "eztrace_list.h"
#include "eztrace_stack.h"
#include "eztrace_convert.h"

static struct ezt_list_t section_list;

static int recording_stats = 0;
static int nb_parallel_region = 0;

#define GOMP_CHANGE() if(!recording_stats) CHANGE()

/* contains all the information of a parallel section */
struct gomp_section_t
{
  void (*func) (void *);
  app_ptr data;
  int id;
  int nb_threads;
  struct eztrace_container_t * main_thread;
  struct eztrace_container_t ** working_threads;
  char** link_value_start;
  char** link_id_start;

  char** link_value_end;
  char** link_id_end;
  float start_time;		/* time at which the parallel_start occurs */
  float* fork_time; 		/* time at which the fork happens for each thread */
  float* join_time;		/* time at which the thread reach join */
  float join_done_time;		/* time at which the join ends */
  ezt_stack_token_t token;
};

/* pointer to a parallel section. This structure is used in the 
 * section_stack field of the gomp_thread_info_t structure
 */
struct gomp_thread_section_t{
  struct gomp_section_t *process_section;
  ezt_stack_token_t token;  
};

/* contains the list of parallel sections that a thread is currently processing */
struct gomp_thread_info_t {
  ezt_stack_t section_stack;
  struct thread_info_t *p_thread;  
};

/* contains the list of parallel sections that a process is currently processing */
struct gomp_process_info_t {
  struct ezt_list_t section_list;
  struct process_info_t *p_process;
  struct ezt_list_t finished_section_list;
};

/* add a hook in the process structure in order to store information
 * about pending parallel sections
 */
static struct gomp_process_info_t *__register_process_hook(struct process_info_t *p_process)
{
  struct gomp_process_info_t *g_info = (struct gomp_process_info_t*) malloc(sizeof(struct gomp_process_info_t));
  g_info->p_process = p_process;
  ezt_list_new(&g_info->section_list);

  /* add the hook in the thread info structure */
  ezt_hook_list_add(&g_info->p_process->hooks, g_info, (uint8_t)GOMP_EVENTS_ID);
  return g_info;
}

/* add a hook in the thread structure in order to store information
 * about pending parallel sections
 */
struct gomp_thread_info_t *__register_thread_hook(int tid)
{
  DECLARE_CUR_PROCESS(p_process);
  struct gomp_thread_info_t *g_info = (struct gomp_thread_info_t*) malloc(sizeof(struct gomp_thread_info_t));
  g_info->p_thread = GET_THREAD_INFO(CUR_INDEX, tid);
  ezt_stack_new(&g_info->section_stack);

  /* add the hook in the thread info structure */
  ezt_hook_list_add(&g_info->p_thread->hooks, g_info, (uint8_t)GOMP_EVENTS_ID);
  return g_info;
}

/* declare a var variable that points to the process_info structure */
#define  INIT_GOMP_PROCESS_INFO(p_process, var)				\
  struct gomp_process_info_t *var = (struct gomp_process_info_t*)	\
    ezt_hook_list_retrieve_data(&p_process->hooks, (uint8_t)GOMP_EVENTS_ID); \
  if(!(var)) {								\
    var = __register_process_hook(p_process);				\
  }

/* declare a var variable that points to the thread_info structure */
#define INIT_GOMP_THREAD_INFO(p_thread_info, var)			\
  struct gomp_thread_info_t *var = (struct gomp_thread_info_t*)		\
    ezt_hook_list_retrieve_data(&p_thread_info->hooks, (uint8_t)GOMP_EVENTS_ID); \
  if(!(var)) {								\
    var = __register_thread_hook(CUR_THREAD_ID);				\
  }

/* retrieve the gomp_section structure that corresponds to a section id */
struct ezt_list_token_t *__find_matching_section(struct process_info_t*p_process, int id)
{
  INIT_GOMP_PROCESS_INFO(p_process, process_info)
    struct ezt_list_token_t *token = NULL;
  ezt_list_foreach(&process_info->section_list, token) {
    struct gomp_section_t * section = (struct gomp_section_t *) token->data;
    if(section->id == id)
      return token;
  }

  return NULL;
}

/* OpenMP */
void
handle_gomp_new_fork ()
{
  /* This event occurs when a parallel section begins.
   * This event is generated by the new thread (ie. not by the main thread
   * that called #omp parallel)
   */
  FUNC_NAME;

  /* We have to:
   * - retrieve the gomp_section structur that was created by the main thread
   * - add this section to the thread's stack
   * - create the links between the main thread and the current thread
   * - set the state of the current thread to 'working'
   */
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_GOMP_THREAD_INFO(p_thread, g_info);

  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);

  /* retrieve the gomp_section structure that was created by the main thread */
  int section_id = GET_PARAM(CUR_EV, 1);
  int my_id      = GET_PARAM(CUR_EV, 2);
  int nb_threads = GET_PARAM(CUR_EV, 3);

  struct ezt_list_token_t *token = __find_matching_section(p_process, section_id);
  assert(token);

  struct gomp_section_t * section = (struct gomp_section_t *) token->data;

  assert(section->main_thread);
  char* main_thread_str = section->main_thread->id;

  if(section->nb_threads) {
    assert(section->nb_threads == nb_threads);
  }


  if(!section->nb_threads) {
    /* We are the first thread that reach the section, allocate everything */
    section->id = section_id;
    section->nb_threads = nb_threads;
    section->working_threads = malloc(sizeof(struct eztrace_container_t*) * section->nb_threads);

    section->link_value_start = malloc(sizeof(char*) * section->nb_threads);
    section->link_id_start = malloc(sizeof(char*) * section->nb_threads);

    section->link_value_end = malloc(sizeof(char*) * section->nb_threads);
    section->link_id_end = malloc(sizeof(char*) * section->nb_threads);

    section->fork_time = malloc(sizeof(float) * section->nb_threads);
    section->join_time = malloc(sizeof(float) * section->nb_threads);
  }

  assert(section->id == section_id);

  section->working_threads[my_id] = GET_THREAD_CONTAINER(CUR_INDEX, CUR_THREAD_ID);
  section->fork_time[my_id] = CURRENT;

  /* create the links between the main thread and the current thread */
  asprintf(&section->link_id_start[my_id], "%d_%d_%d", CUR_RANK, section->id, my_id);
  asprintf(&section->link_value_start[my_id], "p#%d_section#%d_thread#%d", CUR_RANK, section->id, my_id);

  /* todo: plop ! je me suis arrete ici. a continue */

  /* add this section to the thread's stack */
  struct gomp_thread_section_t *p_th_section = (struct gomp_thread_section_t*) malloc(sizeof(struct gomp_thread_section_t));
  p_th_section->process_section = section;
  p_th_section->token.data = p_th_section;
  ezt_stack_push(&g_info->section_stack, &p_th_section->token);

  GOMP_CHANGE() startLink (section->start_time,
			   "GOMP_Section_Link",
			   "C_Prog",
			   main_thread_str,
			   thread_id,
			   section->link_value_start[my_id],
			   section->link_id_start[my_id]);
  GOMP_CHANGE() endLink (CURRENT,
			 "GOMP_Section_Link",
			 "C_Prog",
			 main_thread_str,
			 thread_id,
			 section->link_value_start[my_id],
			 section->link_id_start[my_id]);

  /* set the state of the current thread to 'working' */
  GOMP_CHANGE() setState (CURRENT, "ST_Thread", thread_id, "GOMP_Section_State");
}

void
handle_gomp_new_join ()
{
  /* This event occurs when a parallel section ends.
   * This event is generated by the new thread (ie. not by the main thread
   * that called #omp parallel)
   */
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);

  /* We have to:
   * - retrieve and remove the gomp_section structure that was stacked
   * - create the links between the main thread and the current thread
   * - set the state of the current thread to 'blocked'
   */

  DECLARE_CUR_THREAD(p_thread);
  INIT_GOMP_THREAD_INFO(p_thread, g_info);
  int my_id      = GET_PARAM(CUR_EV, 1);

  /* retrieve and remove the gomp_section structure that was stacked */
  ezt_stack_token_t *token = ezt_stack_get_top(&g_info->section_stack);
  assert(token);
  struct gomp_section_t *section = ((struct gomp_thread_section_t *) token->data)->process_section;

  section->join_time[my_id] = CURRENT;
  /* create the links between the main thread and the current thread */
  asprintf(&section->link_id_end[my_id], "%d_%d_%d", CUR_RANK, section->id, my_id);
  asprintf(&section->link_value_end[my_id], "p#%d_section#%d_thread#%d", CUR_RANK, section->id, my_id);

  GOMP_CHANGE() startLink (CURRENT,
			   "GOMP_Section_Link",
			   "C_Prog",
			   thread_id,
			   section->main_thread->id,
			   section->link_value_end[my_id],
			   section->link_id_end[my_id]);

  /* set the state of the current thread to 'blocked' */
  GOMP_CHANGE() setState (CURRENT, "ST_Thread", thread_id, "STV_Blocked");
}

void
handle_gomp_join_done ()
{
  /* This event occurs when a parallel section ends.
   * This event is generated by the main thread when all the
   * threads have reached the end of the parallel section
   */
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);

  /* We have to:
   * - retrieve the parallel section structure
   * - end all the links that were started by the new_join event
   * - free the section structure and set the current state to working
   */
  DECLARE_CUR_PROCESS(p_process);
  DECLARE_CUR_THREAD(p_thread);
  INIT_GOMP_THREAD_INFO(p_thread, g_info);
  INIT_GOMP_PROCESS_INFO(p_process, process_info);

  /* retrieve the parallel section structure */
  ezt_stack_token_t *token = ezt_stack_pop(&g_info->section_stack);
  assert(token);
  struct gomp_section_t * section = ((struct gomp_thread_section_t *) token->data)->process_section;
  int ret;

  if(!section) {
    /* This should never happen!
     * todo: add an assert here
     */
    fprintf(stderr, "hum... no section...\n");
    goto next;
  }

  section->join_done_time = CURRENT;

  /* end all the links that were started by the new_join event */
  int i;
  for(i=0; i< section->nb_threads; i++) {
    GOMP_CHANGE() endLink (CURRENT, "GOMP_Section_Link", "C_Prog",
			   section->working_threads[i]->id, thread_id,
			   section->link_value_end[i], section->link_id_end[i]);
  }

  /* free the section structure and set the current state to working */
  struct ezt_list_token_t *process_section_token = __find_matching_section(p_process, section->id);
  assert(process_section_token);
  ezt_list_remove(process_section_token);
  ezt_list_add(&process_info->finished_section_list, &section->token);

 next:
  GOMP_CHANGE() setState (CURRENT, "ST_Thread", thread_id, "STV_Working");
}

void
handle_gomp_parallel_start ()
{
  /* This event occurs when a parallel section begins.
   * This event is generated by the main thread
   */
  FUNC_NAME;
  struct gomp_section_t * section = (struct gomp_section_t*) malloc(sizeof(struct gomp_section_t));

  nb_parallel_region++;
  /* Initialize the section strcuture */
  section->id = GET_PARAM(CUR_EV, 4);
  section->nb_threads = 0;
  section->main_thread = GET_THREAD_CONTAINER(CUR_INDEX, CUR_THREAD_ID);
  section->working_threads = NULL;
  section->start_time = CURRENT;
  section->token.data = section;
  DECLARE_CUR_PROCESS(p_process);
  INIT_GOMP_PROCESS_INFO(p_process, g_info);

  /* add the section structure to the list of pending sections */
  ezt_list_add(&g_info->section_list, &section->token);
}

void
handle_gomp_critical_start ()
{
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked");
}

void
handle_gomp_critical_start_done ()
{
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Critical");
}

void
handle_gomp_critical_stop ()
{
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}


void handle_pomp_finalize() {
}

void handle_pomp_atomic_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Atomic");
}

void handle_pomp_atomic_exit() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}

void handle_pomp_critical_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "STV_Blocked");
}

void handle_pomp_critical_begin() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Critical");
}

void handle_pomp_critical_end() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}

void handle_pomp_critical_exit() {
}



void handle_pomp_barrier_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Barrier");
}

void handle_pomp_barrier_exit() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}

void handle_pomp_flush_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Flush");
}

void handle_pomp_flush_exit() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}


void handle_pomp_task_begin() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Task");
}

void handle_pomp_task_end() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}


void handle_pomp_taskwait_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_TaskWait");
}

void handle_pomp_taskwait_exit() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}


void handle_pomp_for_enter() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_For");
}

void handle_pomp_for_exit() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}

void handle_pomp_master_begin() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() pushState(CURRENT, "ST_Thread", thread_id, "GOMP_Master");
}

void handle_pomp_master_end() {
  FUNC_NAME;
  DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
  GOMP_CHANGE() popState(CURRENT, "ST_Thread", thread_id);
}

void handle_pomp_parallel_begin() {
  /* Since the runtime functions provide more information, let's forget these compiler functions */
}

void handle_pomp_parallel_end() {
  /* Since the runtime functions provide more information, let's forget these compiler functions */
}

void handle_pomp_parallel_fork() {
  /* Since the runtime functions provide more information, let's forget these compiler functions */
}

void handle_pomp_parallel_join() {
  /* Since the runtime functions provide more information, let's forget these compiler functions */
}

void handle_pomp_sections_enter() {
  /* todo */
}

void handle_pomp_sections_exit() {
  /* todo */
}

void handle_pomp_section_begin() {
  /* todo */
}

void handle_pomp_section_end() {
  /* todo */
}

void handle_pomp_single_enter() {
  /* todo */
}

void handle_pomp_single_exit() {
  /* todo */
}

void handle_pomp_single_begin() {
  /* todo */
}

void handle_pomp_single_end() {
  /* todo */
}

void handle_pomp_workshare_enter() {
  /* todo */
}

void handle_pomp_workshare_exit() {
  /* todo */
}

int
eztrace_convert_gomp_init()
{
  if(get_mode() == EZTRACE_CONVERT) {
    addLinkType ("GOMP_Section_Link", "OpenMP Parallel Section", "CT_Program", "CT_Thread", "CT_Thread");

    addEntityValue("GOMP_Section_State", "ST_Thread", "GOMP_Section_State", GTG_PINK);
    addEntityValue("GOMP_Critical", "ST_Thread", "GOMP_Critical", GTG_GREEN);
    addEntityValue("GOMP_Atomic", "ST_Thread", "GOMP_Atomic", GTG_GREEN);
    addEntityValue("GOMP_Barrier", "ST_Thread", "GOMP_Barrier", GTG_ORANGE);

    addEntityValue("GOMP_TaskWait", "ST_Thread", "GOMP_TaskWait", GTG_ORANGE);

    addEntityValue("GOMP_Task", "ST_Thread", "GOMP_Task", GTG_WHITE);
    addEntityValue("GOMP_For", "ST_Thread", "GOMP_For", GTG_LIGHTGREY);
    addEntityValue("GOMP_Master", "ST_Thread", "GOMP_Master", GTG_WHITE);
  }
  ezt_list_new(&section_list);
  return 0;
}

/* return 1 if the event was handled */
int
handle_gomp_events(struct fxt_ev_64 *ev)
{
  if(!STARTED)
    return 0;

  switch(ev->code)
    {
      /* OpenMP */
    case FUT_GOMP_NEW_FORK:
      handle_gomp_new_fork ();
      break;
    case FUT_GOMP_NEW_JOIN:
      handle_gomp_new_join ();
      break;
    case FUT_GOMP_JOIN_DONE:
      handle_gomp_join_done ();
      break;
    case FUT_GOMP_PARALLEL_START:
      handle_gomp_parallel_start ();
      break;

    case FUT_GOMP_CRITICAL_START:
      handle_gomp_critical_start ();
      break;
    case FUT_GOMP_CRITICAL_START_DONE:
      handle_gomp_critical_start_done ();
      break;
    case FUT_GOMP_CRITICAL_STOP:
      handle_gomp_critical_stop ();
      break;


    case FUT_POMP_FINALIZE:
      handle_pomp_finalize();
      break;
    case FUT_POMP_ATOMIC_ENTER:
      handle_pomp_atomic_enter();
      break;
    case FUT_POMP_ATOMIC_EXIT:
      handle_pomp_atomic_exit();
      break;
    case FUT_POMP_BARRIER_ENTER:
      handle_pomp_barrier_enter();
      break;
    case FUT_POMP_BARRIER_EXIT:
      handle_pomp_barrier_exit();
      break;
    case FUT_POMP_FLUSH_ENTER:
      handle_pomp_flush_enter();
      break;
    case FUT_POMP_FLUSH_EXIT:
      handle_pomp_flush_exit();
      break;
    case FUT_POMP_CRITICAL_ENTER:
      handle_pomp_critical_enter();
      break;
    case FUT_POMP_CRITICAL_EXIT:
      handle_pomp_critical_exit();
      break;
    case FUT_POMP_CRITICAL_BEGIN:
      handle_pomp_critical_begin();
      break;
    case FUT_POMP_CRITICAL_END:
      handle_pomp_critical_end();
      break;
    case FUT_POMP_TASK_BEGIN:
      handle_pomp_task_begin();
      break;
    case FUT_POMP_TASK_END:
      handle_pomp_task_end();
      break;
    case FUT_POMP_TASKWAIT_ENTER:
      handle_pomp_taskwait_enter();
      break;
    case FUT_POMP_TASKWAIT_EXIT:
      handle_pomp_taskwait_exit();
      break;
    case FUT_POMP_FOR_ENTER:
      handle_pomp_for_enter();
      break;
    case FUT_POMP_FOR_EXIT:
      handle_pomp_for_exit();
      break;
    case FUT_POMP_MASTER_BEGIN:
      handle_pomp_master_begin();
      break;
    case FUT_POMP_MASTER_END:
      handle_pomp_master_end();
      break;
    case FUT_POMP_PARALLEL_BEGIN:
      handle_pomp_parallel_begin();
      break;
    case FUT_POMP_PARALLEL_END:
      handle_pomp_parallel_end();
      break;
    case FUT_POMP_PARALLEL_FORK:
      handle_pomp_parallel_fork();
      break;
    case FUT_POMP_PARALLEL_JOIN:
      handle_pomp_parallel_join();
      break;
    case FUT_POMP_SECTIONS_ENTER:
      handle_pomp_sections_enter();
      break;
    case FUT_POMP_SECTIONS_EXIT:
      handle_pomp_sections_exit();
      break;
    case FUT_POMP_SECTION_BEGIN:
      handle_pomp_section_begin();
      break;
    case FUT_POMP_SECTION_END:
      handle_pomp_section_end();
      break;
    case FUT_POMP_SINGLE_ENTER:
      handle_pomp_single_enter();
      break;
    case FUT_POMP_SINGLE_EXIT:
      handle_pomp_single_exit();
      break;
    case FUT_POMP_SINGLE_BEGIN:
      handle_pomp_single_begin();
      break;
    case FUT_POMP_SINGLE_END:
      handle_pomp_single_end();
      break;
    case FUT_POMP_WORKSHARE_ENTER:
      handle_pomp_workshare_enter();
      break;
    case FUT_POMP_WORKSHARE_EXIT:
      handle_pomp_workshare_exit();
      break;
    default:
      return 0;
    }
  return 1;
}

/* return 1 if the event was handled */
int
handle_gomp_stats(struct fxt_ev_64 *ev)
{
  recording_stats = 1;
  return handle_gomp_events(ev);
}

void print_gomp_stats() {
  printf ( "\nGNU OpenMP:\n");
  printf   ( "----------\n");
  printf ( "%d parallel regions\n", nb_parallel_region);
  /* todo:
   * add:
   *  - min/max/average duration of parallel regions
   *  - maximum depth (nested)
   */

  /* browse the processes finished_section_list and extract statistics */
  int i;
  for(i=0; i<NB_TRACES; i++) {
    struct process_info_t *p_process = GET_PROCESS_INFO(i);
    struct gomp_process_info_t *p_info = (struct gomp_process_info_t*) ezt_hook_list_retrieve_data(&p_process->hooks, (uint8_t)GOMP_EVENTS_ID);

    if(!p_info)
      continue;			/* No gomp process info attached, skip this process */

    /* todo: implement other statistics (min/max duration, depth, etc.) */
    unsigned nb_sections = 0;
    float total_duration = 0;
    ezt_stack_token_t *token;
    ezt_list_foreach(&p_info->finished_section_list, token) {
      struct gomp_section_t * section = (struct gomp_section_t *) token->data;

      total_duration += section->join_done_time - section->start_time;
      nb_sections++;
    }
    printf("%s: \t %d parallel regions (average duration: %f us)\n", p_process->container->name,
	   nb_sections, total_duration/nb_sections);
  }
}

struct eztrace_convert_module gomp_module;
void libinit(void) __attribute__ ((constructor));
void libinit(void)
{
  gomp_module.api_version = EZTRACE_API_VERSION;
  gomp_module.init = eztrace_convert_gomp_init;
  gomp_module.handle = handle_gomp_events;
  gomp_module.handle_stats = handle_gomp_stats;
  gomp_module.print_stats = print_gomp_stats;
  gomp_module.module_prefix = GOMP_EVENTS_ID;
  asprintf(&gomp_module.name, "omp");
  asprintf(&gomp_module.description, "Module for OpenMP parallel regions");

  gomp_module.token.data = &gomp_module;
  eztrace_convert_register_module(&gomp_module);
}

void libfinalize(void) __attribute__ ((destructor));
void libfinalize(void)
{
}