2 /*--------------------------------------------------------------------*/
5 /*--------------------------------------------------------------------*/
8 This file is part of Callgrind, a Valgrind tool for call graph
11 Copyright (C) 2002-2017, Josef Weidendorfer (Josef.Weidendorfer@gmx.de)
13 This tool is derived from and contains code from Cachegrind
14 Copyright (C) 2002-2017 Nicholas Nethercote (njn@valgrind.org)
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the GNU General Public License as
18 published by the Free Software Foundation; either version 2 of the
19 License, or (at your option) any later version.
21 This program is distributed in the hope that it will be useful, but
22 WITHOUT ANY WARRANTY; without even the implied warranty of
23 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 General Public License for more details.
26 You should have received a copy of the GNU General Public License
27 along with this program; if not, write to the Free Software
28 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
31 The GNU General Public License is contained in the file COPYING.
35 #include "callgrind.h"
38 #include "pub_tool_threadstate.h"
39 #include "pub_tool_gdbserver.h"
40 #include "pub_tool_transtab.h" // VG_(discard_translations_safely)
42 #include "cg_branchpred.c"
44 /*------------------------------------------------------------*/
45 /*--- Global variables ---*/
46 /*------------------------------------------------------------*/
49 CommandLineOptions
CLG_(clo
);
50 Statistics
CLG_(stat
);
51 Bool
CLG_(instrument_state
) = True
; /* Instrumentation on ? */
53 /* thread and signal handler specific */
54 exec_state
CLG_(current_state
);
56 /* min of L1 and LL cache line sizes. This only gets set to a
57 non-zero value if we are doing cache simulation. */
58 Int
CLG_(min_line_size
) = 0;
61 /*------------------------------------------------------------*/
62 /*--- Statistics ---*/
63 /*------------------------------------------------------------*/
65 static void CLG_(init_statistics
)(Statistics
* s
)
70 s
->rec_call_counter
= 0;
74 s
->context_counter
= 0;
75 s
->bb_retranslations
= 0;
78 s
->distinct_files
= 0;
80 s
->distinct_contexts
= 0;
82 s
->distinct_bbccs
= 0;
83 s
->distinct_instrs
= 0;
84 s
->distinct_skips
= 0;
86 s
->bb_hash_resizes
= 0;
87 s
->bbcc_hash_resizes
= 0;
88 s
->jcc_hash_resizes
= 0;
89 s
->cxt_hash_resizes
= 0;
90 s
->fn_array_resizes
= 0;
91 s
->call_stack_resizes
= 0;
92 s
->fn_stack_resizes
= 0;
94 s
->full_debug_BBs
= 0;
95 s
->file_line_debug_BBs
= 0;
96 s
->fn_name_debug_BBs
= 0;
98 s
->bbcc_lru_misses
= 0;
99 s
->jcc_lru_misses
= 0;
100 s
->cxt_lru_misses
= 0;
105 /*------------------------------------------------------------*/
106 /*--- Simple callbacks (not cache similator) ---*/
107 /*------------------------------------------------------------*/
110 static void log_global_event(InstrInfo
* ii
)
114 CLG_DEBUG(6, "log_global_event: Ir %#lx/%u\n",
115 CLG_(bb_base
) + ii
->instr_offset
, ii
->instr_size
);
117 if (!CLG_(current_state
).collect
) return;
119 CLG_ASSERT( (ii
->eventset
->mask
& (1u<<EG_BUS
))>0 );
121 CLG_(current_state
).cost
[ fullOffset(EG_BUS
) ]++;
123 if (CLG_(current_state
).nonskipped
)
124 cost_Bus
= CLG_(current_state
).nonskipped
->skipped
+ fullOffset(EG_BUS
);
126 cost_Bus
= CLG_(cost_base
) + ii
->cost_offset
+ ii
->eventset
->offset
[EG_BUS
];
131 /* For branches, we consult two different predictors, one which
132 predicts taken/untaken for conditional branches, and the other
133 which predicts the branch target address for indirect branches
134 (jump-to-register style ones). */
137 void log_cond_branch(InstrInfo
* ii
, Word taken
)
143 CLG_DEBUG(6, "log_cond_branch: Ir %#lx, taken %ld\n",
144 CLG_(bb_base
) + ii
->instr_offset
, taken
);
146 miss
= 1 & do_cond_branch_predict(CLG_(bb_base
) + ii
->instr_offset
, taken
);
148 if (!CLG_(current_state
).collect
) return;
150 CLG_ASSERT( (ii
->eventset
->mask
& (1u<<EG_BC
))>0 );
152 if (CLG_(current_state
).nonskipped
)
153 cost_Bc
= CLG_(current_state
).nonskipped
->skipped
+ fullOffset(EG_BC
);
155 cost_Bc
= CLG_(cost_base
) + ii
->cost_offset
+ ii
->eventset
->offset
[EG_BC
];
157 fullOffset_Bc
= fullOffset(EG_BC
);
158 CLG_(current_state
).cost
[ fullOffset_Bc
]++;
161 CLG_(current_state
).cost
[ fullOffset_Bc
+1 ]++;
167 void log_ind_branch(InstrInfo
* ii
, UWord actual_dst
)
173 CLG_DEBUG(6, "log_ind_branch: Ir %#lx, dst %#lx\n",
174 CLG_(bb_base
) + ii
->instr_offset
, actual_dst
);
176 miss
= 1 & do_ind_branch_predict(CLG_(bb_base
) + ii
->instr_offset
, actual_dst
);
178 if (!CLG_(current_state
).collect
) return;
180 CLG_ASSERT( (ii
->eventset
->mask
& (1u<<EG_BI
))>0 );
182 if (CLG_(current_state
).nonskipped
)
183 cost_Bi
= CLG_(current_state
).nonskipped
->skipped
+ fullOffset(EG_BI
);
185 cost_Bi
= CLG_(cost_base
) + ii
->cost_offset
+ ii
->eventset
->offset
[EG_BI
];
187 fullOffset_Bi
= fullOffset(EG_BI
);
188 CLG_(current_state
).cost
[ fullOffset_Bi
]++;
191 CLG_(current_state
).cost
[ fullOffset_Bi
+1 ]++;
196 /*------------------------------------------------------------*/
197 /*--- Instrumentation structures and event queue handling ---*/
198 /*------------------------------------------------------------*/
200 /* Maintain an ordered list of memory events which are outstanding, in
201 the sense that no IR has yet been generated to do the relevant
202 helper calls. The BB is scanned top to bottom and memory events
203 are added to the end of the list, merging with the most recent
204 notified event where possible (Dw immediately following Dr and
205 having the same size and EA can be merged).
207 This merging is done so that for architectures which have
208 load-op-store instructions (x86, amd64), the insn is treated as if
209 it makes just one memory reference (a modify), rather than two (a
210 read followed by a write at the same address).
212 At various points the list will need to be flushed, that is, IR
213 generated from it. That must happen before any possible exit from
214 the block (the end, or an IRStmt_Exit). Flushing also takes place
215 when there is no space to add a new event.
217 If we require the simulation statistics to be up to date with
218 respect to possible memory exceptions, then the list would have to
219 be flushed before each memory reference. That would however lose
220 performance by inhibiting event-merging during flushing.
222 Flushing the list consists of walking it start to end and emitting
223 instrumentation IR for each event, in the order in which they
224 appear. It may be possible to emit a single call for two adjacent
225 events in order to reduce the number of helper function calls made.
226 For example, it could well be profitable to handle two adjacent Ir
227 events with a single helper call. */
235 Ev_Ir
, // Instruction read
238 Ev_Dm
, // Data modify (read then write)
239 Ev_Bc
, // branch conditional
240 Ev_Bi
, // branch indirect (to unknown destination)
241 Ev_G
// Global bus event
265 IRAtom
* taken
; /* :: Ity_I1 */
276 static void init_Event ( Event
* ev
) {
277 VG_(memset
)(ev
, 0, sizeof(Event
));
280 static IRAtom
* get_Event_dea ( Event
* ev
) {
282 case Ev_Dr
: return ev
->Ev
.Dr
.ea
;
283 case Ev_Dw
: return ev
->Ev
.Dw
.ea
;
284 case Ev_Dm
: return ev
->Ev
.Dm
.ea
;
285 default: tl_assert(0);
289 static Int
get_Event_dszB ( Event
* ev
) {
291 case Ev_Dr
: return ev
->Ev
.Dr
.szB
;
292 case Ev_Dw
: return ev
->Ev
.Dw
.szB
;
293 case Ev_Dm
: return ev
->Ev
.Dm
.szB
;
294 default: tl_assert(0);
299 /* Up to this many unnotified events are allowed. Number is
300 arbitrary. Larger numbers allow more event merging to occur, but
301 potentially induce more spilling due to extending live ranges of
302 address temporaries. */
306 /* A struct which holds all the running state during instrumentation.
307 Mostly to avoid passing loads of parameters everywhere. */
309 /* The current outstanding-memory-event list. */
310 Event events
[N_EVENTS
];
313 /* The array of InstrInfo's is part of BB struct. */
316 /* BB seen before (ie. re-instrumentation) */
319 /* Number InstrInfo bins 'used' so far. */
322 // current offset of guest instructions from BB start
325 /* The output SB being constructed. */
330 static void showEvent ( Event
* ev
)
334 VG_(printf
)("Ir (InstrInfo %p) at +%u\n",
335 ev
->inode
, ev
->inode
->instr_offset
);
338 VG_(printf
)("Dr (InstrInfo %p) at +%u %d EA=",
339 ev
->inode
, ev
->inode
->instr_offset
, ev
->Ev
.Dr
.szB
);
340 ppIRExpr(ev
->Ev
.Dr
.ea
);
344 VG_(printf
)("Dw (InstrInfo %p) at +%u %d EA=",
345 ev
->inode
, ev
->inode
->instr_offset
, ev
->Ev
.Dw
.szB
);
346 ppIRExpr(ev
->Ev
.Dw
.ea
);
350 VG_(printf
)("Dm (InstrInfo %p) at +%u %d EA=",
351 ev
->inode
, ev
->inode
->instr_offset
, ev
->Ev
.Dm
.szB
);
352 ppIRExpr(ev
->Ev
.Dm
.ea
);
356 VG_(printf
)("Bc %p GA=", ev
->inode
);
357 ppIRExpr(ev
->Ev
.Bc
.taken
);
361 VG_(printf
)("Bi %p DST=", ev
->inode
);
362 ppIRExpr(ev
->Ev
.Bi
.dst
);
366 VG_(printf
)("G %p\n", ev
->inode
);
374 /* Generate code for all outstanding memory events, and mark the queue
375 empty. Code is generated into cgs->sbOut, and this activity
376 'consumes' slots in cgs->bb. */
378 static void flushEvents ( ClgState
* clgs
)
380 Int i
, regparms
, inew
;
381 const HChar
* helperName
;
390 if (!clgs
->seen_before
) {
391 // extend event sets as needed
392 // available sets: D0 Dr
393 for(i
=0; i
<clgs
->events_used
; i
++) {
394 ev
= &clgs
->events
[i
];
397 // Ir event always is first for a guest instruction
398 CLG_ASSERT(ev
->inode
->eventset
== 0);
399 ev
->inode
->eventset
= CLG_(sets
).base
;
402 // extend event set by Dr counters
403 ev
->inode
->eventset
= CLG_(add_event_group
)(ev
->inode
->eventset
,
408 // extend event set by Dw counters
409 ev
->inode
->eventset
= CLG_(add_event_group
)(ev
->inode
->eventset
,
413 // extend event set by Bc counters
414 ev
->inode
->eventset
= CLG_(add_event_group
)(ev
->inode
->eventset
,
418 // extend event set by Bi counters
419 ev
->inode
->eventset
= CLG_(add_event_group
)(ev
->inode
->eventset
,
423 // extend event set by Bus counter
424 ev
->inode
->eventset
= CLG_(add_event_group
)(ev
->inode
->eventset
,
433 for(i
= 0; i
< clgs
->events_used
; i
= inew
) {
440 /* generate IR to notify event i and possibly the ones
441 immediately following it. */
442 tl_assert(i
>= 0 && i
< clgs
->events_used
);
444 ev
= &clgs
->events
[i
];
445 ev2
= ( i
< clgs
->events_used
-1 ? &clgs
->events
[i
+1] : NULL
);
446 ev3
= ( i
< clgs
->events_used
-2 ? &clgs
->events
[i
+2] : NULL
);
449 VG_(printf
)(" flush ");
453 i_node_expr
= mkIRExpr_HWord( (HWord
)ev
->inode
);
455 /* Decide on helper fn to call and args to pass it, and advance
457 Dm events have same effect as Dw events */
460 /* Merge an Ir with a following Dr. */
461 if (ev2
&& ev2
->tag
== Ev_Dr
) {
462 /* Why is this true? It's because we're merging an Ir
463 with a following Dr. The Ir derives from the
464 instruction's IMark and the Dr from data
465 references which follow it. In short it holds
466 because each insn starts with an IMark, hence an
467 Ev_Ir, and so these Dr must pertain to the
468 immediately preceding Ir. Same applies to analogous
469 assertions in the subsequent cases. */
470 tl_assert(ev2
->inode
== ev
->inode
);
471 helperName
= CLG_(cachesim
).log_1I1Dr_name
;
472 helperAddr
= CLG_(cachesim
).log_1I1Dr
;
473 argv
= mkIRExprVec_3( i_node_expr
,
475 mkIRExpr_HWord( get_Event_dszB(ev2
) ) );
479 /* Merge an Ir with a following Dw/Dm. */
481 if (ev2
&& (ev2
->tag
== Ev_Dw
|| ev2
->tag
== Ev_Dm
)) {
482 tl_assert(ev2
->inode
== ev
->inode
);
483 helperName
= CLG_(cachesim
).log_1I1Dw_name
;
484 helperAddr
= CLG_(cachesim
).log_1I1Dw
;
485 argv
= mkIRExprVec_3( i_node_expr
,
487 mkIRExpr_HWord( get_Event_dszB(ev2
) ) );
491 /* Merge an Ir with two following Irs. */
493 if (ev2
&& ev3
&& ev2
->tag
== Ev_Ir
&& ev3
->tag
== Ev_Ir
) {
494 helperName
= CLG_(cachesim
).log_3I0D_name
;
495 helperAddr
= CLG_(cachesim
).log_3I0D
;
496 argv
= mkIRExprVec_3( i_node_expr
,
497 mkIRExpr_HWord( (HWord
)ev2
->inode
),
498 mkIRExpr_HWord( (HWord
)ev3
->inode
) );
502 /* Merge an Ir with one following Ir. */
504 if (ev2
&& ev2
->tag
== Ev_Ir
) {
505 helperName
= CLG_(cachesim
).log_2I0D_name
;
506 helperAddr
= CLG_(cachesim
).log_2I0D
;
507 argv
= mkIRExprVec_2( i_node_expr
,
508 mkIRExpr_HWord( (HWord
)ev2
->inode
) );
512 /* No merging possible; emit as-is. */
514 helperName
= CLG_(cachesim
).log_1I0D_name
;
515 helperAddr
= CLG_(cachesim
).log_1I0D
;
516 argv
= mkIRExprVec_1( i_node_expr
);
522 /* Data read or modify */
523 helperName
= CLG_(cachesim
).log_0I1Dr_name
;
524 helperAddr
= CLG_(cachesim
).log_0I1Dr
;
525 argv
= mkIRExprVec_3( i_node_expr
,
527 mkIRExpr_HWord( get_Event_dszB(ev
) ) );
534 helperName
= CLG_(cachesim
).log_0I1Dw_name
;
535 helperAddr
= CLG_(cachesim
).log_0I1Dw
;
536 argv
= mkIRExprVec_3( i_node_expr
,
538 mkIRExpr_HWord( get_Event_dszB(ev
) ) );
543 /* Conditional branch */
544 helperName
= "log_cond_branch";
545 helperAddr
= &log_cond_branch
;
546 argv
= mkIRExprVec_2( i_node_expr
, ev
->Ev
.Bc
.taken
);
551 /* Branch to an unknown destination */
552 helperName
= "log_ind_branch";
553 helperAddr
= &log_ind_branch
;
554 argv
= mkIRExprVec_2( i_node_expr
, ev
->Ev
.Bi
.dst
);
559 /* Global bus event (CAS, LOCK-prefix, LL-SC, etc) */
560 helperName
= "log_global_event";
561 helperAddr
= &log_global_event
;
562 argv
= mkIRExprVec_1( i_node_expr
);
572 VG_(printf
)(" merge ");
576 VG_(printf
)(" merge ");
580 VG_(printf
)(" call %s (%p)\n",
581 helperName
, helperAddr
);
584 /* helper could be unset depending on the simulator used */
585 if (helperAddr
== 0) continue;
587 /* Add the helper. */
588 tl_assert(helperName
);
589 tl_assert(helperAddr
);
591 di
= unsafeIRDirty_0_N( regparms
,
592 helperName
, VG_(fnptr_to_fnentry
)( helperAddr
),
594 addStmtToIRSB( clgs
->sbOut
, IRStmt_Dirty(di
) );
597 clgs
->events_used
= 0;
600 static void addEvent_Ir ( ClgState
* clgs
, InstrInfo
* inode
)
603 tl_assert(clgs
->seen_before
|| (inode
->eventset
== 0));
604 if (!CLG_(clo
).simulate_cache
) return;
606 if (clgs
->events_used
== N_EVENTS
)
608 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
609 evt
= &clgs
->events
[clgs
->events_used
];
617 void addEvent_Dr ( ClgState
* clgs
, InstrInfo
* inode
, Int datasize
, IRAtom
* ea
)
620 tl_assert(isIRAtom(ea
));
621 tl_assert(datasize
>= 1);
622 if (!CLG_(clo
).simulate_cache
) return;
623 tl_assert(datasize
<= CLG_(min_line_size
));
625 if (clgs
->events_used
== N_EVENTS
)
627 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
628 evt
= &clgs
->events
[clgs
->events_used
];
632 evt
->Ev
.Dr
.szB
= datasize
;
638 void addEvent_Dw ( ClgState
* clgs
, InstrInfo
* inode
, Int datasize
, IRAtom
* ea
)
641 tl_assert(isIRAtom(ea
));
642 tl_assert(datasize
>= 1);
643 if (!CLG_(clo
).simulate_cache
) return;
644 tl_assert(datasize
<= CLG_(min_line_size
));
646 /* Is it possible to merge this write with the preceding read? */
647 if (clgs
->events_used
> 0) {
648 Event
* lastEvt
= &clgs
->events
[clgs
->events_used
-1];
649 if ( lastEvt
->tag
== Ev_Dr
650 && lastEvt
->Ev
.Dr
.szB
== datasize
651 && lastEvt
->inode
== inode
652 && eqIRAtom(lastEvt
->Ev
.Dr
.ea
, ea
))
654 lastEvt
->tag
= Ev_Dm
;
659 /* No. Add as normal. */
660 if (clgs
->events_used
== N_EVENTS
)
662 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
663 evt
= &clgs
->events
[clgs
->events_used
];
667 evt
->Ev
.Dw
.szB
= datasize
;
673 void addEvent_D_guarded ( ClgState
* clgs
, InstrInfo
* inode
,
674 Int datasize
, IRAtom
* ea
, IRAtom
* guard
,
677 tl_assert(isIRAtom(ea
));
679 tl_assert(isIRAtom(guard
));
680 tl_assert(datasize
>= 1);
681 if (!CLG_(clo
).simulate_cache
) return;
682 tl_assert(datasize
<= CLG_(min_line_size
));
684 /* Adding guarded memory actions and merging them with the existing
685 queue is too complex. Simply flush the queue and add this
686 action immediately. Since guarded loads and stores are pretty
687 rare, this is not thought likely to cause any noticeable
688 performance loss as a result of the loss of event-merging
690 tl_assert(clgs
->events_used
>= 0);
692 tl_assert(clgs
->events_used
== 0);
693 /* Same as case Ev_Dw / case Ev_Dr in flushEvents, except with guard */
695 const HChar
* helperName
;
700 i_node_expr
= mkIRExpr_HWord( (HWord
)inode
);
701 helperName
= isWrite
? CLG_(cachesim
).log_0I1Dw_name
702 : CLG_(cachesim
).log_0I1Dr_name
;
703 helperAddr
= isWrite
? CLG_(cachesim
).log_0I1Dw
704 : CLG_(cachesim
).log_0I1Dr
;
705 argv
= mkIRExprVec_3( i_node_expr
,
706 ea
, mkIRExpr_HWord( datasize
) );
708 di
= unsafeIRDirty_0_N(
710 helperName
, VG_(fnptr_to_fnentry
)( helperAddr
),
713 addStmtToIRSB( clgs
->sbOut
, IRStmt_Dirty(di
) );
717 void addEvent_Bc ( ClgState
* clgs
, InstrInfo
* inode
, IRAtom
* guard
)
720 tl_assert(isIRAtom(guard
));
721 tl_assert(typeOfIRExpr(clgs
->sbOut
->tyenv
, guard
)
722 == (sizeof(RegWord
)==4 ? Ity_I32
: Ity_I64
));
723 if (!CLG_(clo
).simulate_branch
) return;
725 if (clgs
->events_used
== N_EVENTS
)
727 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
728 evt
= &clgs
->events
[clgs
->events_used
];
732 evt
->Ev
.Bc
.taken
= guard
;
737 void addEvent_Bi ( ClgState
* clgs
, InstrInfo
* inode
, IRAtom
* whereTo
)
740 tl_assert(isIRAtom(whereTo
));
741 tl_assert(typeOfIRExpr(clgs
->sbOut
->tyenv
, whereTo
)
742 == (sizeof(RegWord
)==4 ? Ity_I32
: Ity_I64
));
743 if (!CLG_(clo
).simulate_branch
) return;
745 if (clgs
->events_used
== N_EVENTS
)
747 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
748 evt
= &clgs
->events
[clgs
->events_used
];
752 evt
->Ev
.Bi
.dst
= whereTo
;
757 void addEvent_G ( ClgState
* clgs
, InstrInfo
* inode
)
760 if (!CLG_(clo
).collect_bus
) return;
762 if (clgs
->events_used
== N_EVENTS
)
764 tl_assert(clgs
->events_used
>= 0 && clgs
->events_used
< N_EVENTS
);
765 evt
= &clgs
->events
[clgs
->events_used
];
772 /* Initialise or check (if already seen before) an InstrInfo for next insn.
773 We only can set instr_offset/instr_size here. The required event set and
774 resulting cost offset depend on events (Ir/Dr/Dw/Dm) in guest
775 instructions. The event set is extended as required on flush of the event
776 queue (when Dm events were determined), cost offsets are determined at
777 end of BB instrumentation. */
779 InstrInfo
* next_InstrInfo ( ClgState
* clgs
, UInt instr_size
)
782 tl_assert(clgs
->ii_index
>= 0);
783 tl_assert(clgs
->ii_index
< clgs
->bb
->instr_count
);
784 ii
= &clgs
->bb
->instr
[ clgs
->ii_index
];
786 if (clgs
->seen_before
) {
787 CLG_ASSERT(ii
->instr_offset
== clgs
->instr_offset
);
788 CLG_ASSERT(ii
->instr_size
== instr_size
);
791 ii
->instr_offset
= clgs
->instr_offset
;
792 ii
->instr_size
= instr_size
;
798 clgs
->instr_offset
+= instr_size
;
799 CLG_(stat
).distinct_instrs
++;
804 // return total number of cost values needed for this BB
806 UInt
update_cost_offsets( ClgState
* clgs
)
810 UInt cost_offset
= 0;
812 CLG_ASSERT(clgs
->bb
->instr_count
== clgs
->ii_index
);
813 for(i
=0; i
<clgs
->ii_index
; i
++) {
814 ii
= &clgs
->bb
->instr
[i
];
815 if (clgs
->seen_before
) {
816 CLG_ASSERT(ii
->cost_offset
== cost_offset
);
818 ii
->cost_offset
= cost_offset
;
819 cost_offset
+= ii
->eventset
? ii
->eventset
->size
: 0;
825 /*------------------------------------------------------------*/
826 /*--- Instrumentation ---*/
827 /*------------------------------------------------------------*/
829 #if defined(VG_BIGENDIAN)
830 # define CLGEndness Iend_BE
831 #elif defined(VG_LITTLEENDIAN)
832 # define CLGEndness Iend_LE
834 # error "Unknown endianness"
838 Addr
IRConst2Addr(IRConst
* con
)
842 if (sizeof(RegWord
) == 4) {
843 CLG_ASSERT( con
->tag
== Ico_U32
);
846 else if (sizeof(RegWord
) == 8) {
847 CLG_ASSERT( con
->tag
== Ico_U64
);
851 VG_(tool_panic
)("Callgrind: invalid Addr type");
856 /* First pass over a BB to instrument, counting instructions and jumps
857 * This is needed for the size of the BB struct to allocate
859 * Called from CLG_(get_bb)
861 void CLG_(collectBlockInfo
)(IRSB
* sbIn
,
862 /*INOUT*/ UInt
* instrs
,
863 /*INOUT*/ UInt
* cjmps
,
864 /*INOUT*/ Bool
* cjmp_inverted
)
868 Addr instrAddr
=0, jumpDst
;
870 Bool toNextInstr
= False
;
872 // Ist_Exit has to be ignored in preamble code, before first IMark:
873 // preamble code is added by VEX for self modifying code, and has
874 // nothing to do with client code
875 Bool inPreamble
= True
;
879 for (i
= 0; i
< sbIn
->stmts_used
; i
++) {
881 if (Ist_IMark
== st
->tag
) {
884 instrAddr
= st
->Ist
.IMark
.addr
;
885 instrLen
= st
->Ist
.IMark
.len
;
890 if (inPreamble
) continue;
891 if (Ist_Exit
== st
->tag
) {
892 jumpDst
= IRConst2Addr(st
->Ist
.Exit
.dst
);
893 toNextInstr
= (jumpDst
== instrAddr
+ instrLen
);
899 /* if the last instructions of BB conditionally jumps to next instruction
900 * (= first instruction of next BB in memory), this is a inverted by VEX.
902 *cjmp_inverted
= toNextInstr
;
906 void addConstMemStoreStmt( IRSB
* bbOut
, UWord addr
, UInt val
, IRType hWordTy
)
908 addStmtToIRSB( bbOut
,
909 IRStmt_Store(CLGEndness
,
910 IRExpr_Const(hWordTy
== Ity_I32
?
911 IRConst_U32( addr
) :
912 IRConst_U64( addr
)),
913 IRExpr_Const(IRConst_U32(val
)) ));
917 /* add helper call to setup_bbcc, with pointer to BB struct as argument
919 * precondition for setup_bbcc:
920 * - jmps_passed has number of cond.jumps passed in last executed BB
921 * - current_bbcc has a pointer to the BBCC of the last executed BB
922 * Thus, if bbcc_jmpkind is != -1 (JmpNone),
923 * current_bbcc->bb->jmp_addr
924 * gives the address of the jump source.
926 * the setup does 2 things:
928 * * Unwind own call stack, i.e sync our ESP with real ESP
929 * This is for ESP manipulation (longjmps, C++ exec handling) and RET
930 * * For CALLs or JMPs crossing objects, record call arg +
931 * push are on own call stack
933 * - prepare for cache log functions:
934 * set current_bbcc to BBCC that gets the costs for this BB execution
938 void addBBSetupCall(ClgState
* clgs
)
941 IRExpr
*arg1
, **argv
;
943 arg1
= mkIRExpr_HWord( (HWord
)clgs
->bb
);
944 argv
= mkIRExprVec_1(arg1
);
945 di
= unsafeIRDirty_0_N( 1, "setup_bbcc",
946 VG_(fnptr_to_fnentry
)( & CLG_(setup_bbcc
) ),
948 addStmtToIRSB( clgs
->sbOut
, IRStmt_Dirty(di
) );
953 IRSB
* CLG_(instrument
)( VgCallbackClosure
* closure
,
955 const VexGuestLayout
* layout
,
956 const VexGuestExtents
* vge
,
957 const VexArchInfo
* archinfo_host
,
958 IRType gWordTy
, IRType hWordTy
)
963 InstrInfo
* curr_inode
= NULL
;
966 IRTypeEnv
* tyenv
= sbIn
->tyenv
;
968 if (gWordTy
!= hWordTy
) {
969 /* We don't currently support this case. */
970 VG_(tool_panic
)("host/guest word size mismatch");
973 // No instrumentation if it is switched off
974 if (! CLG_(instrument_state
)) {
975 CLG_DEBUG(5, "instrument(BB %#lx) [Instrumentation OFF]\n",
976 (Addr
)closure
->readdr
);
980 CLG_DEBUG(3, "+ instrument(BB %#lx)\n", (Addr
)closure
->readdr
);
982 /* Set up SB for instrumented IR */
983 clgs
.sbOut
= deepCopyIRSBExceptStmts(sbIn
);
985 // Copy verbatim any IR preamble preceding the first IMark
987 while (i
< sbIn
->stmts_used
&& sbIn
->stmts
[i
]->tag
!= Ist_IMark
) {
988 addStmtToIRSB( clgs
.sbOut
, sbIn
->stmts
[i
] );
992 // Get the first statement, and origAddr from it
993 CLG_ASSERT(sbIn
->stmts_used
>0);
994 CLG_ASSERT(i
< sbIn
->stmts_used
);
996 CLG_ASSERT(Ist_IMark
== st
->tag
);
998 origAddr
= st
->Ist
.IMark
.addr
+ st
->Ist
.IMark
.delta
;
999 CLG_ASSERT(origAddr
== st
->Ist
.IMark
.addr
1000 + st
->Ist
.IMark
.delta
); // XXX: check no overflow
1002 /* Get BB struct (creating if necessary).
1003 * JS: The hash table is keyed with orig_addr_noredir -- important!
1004 * JW: Why? If it is because of different chasing of the redirection,
1005 * this is not needed, as chasing is switched off in callgrind
1007 clgs
.bb
= CLG_(get_bb
)(origAddr
, sbIn
, &(clgs
.seen_before
));
1009 addBBSetupCall(&clgs
);
1011 // Set up running state
1012 clgs
.events_used
= 0;
1014 clgs
.instr_offset
= 0;
1016 for (/*use current i*/; i
< sbIn
->stmts_used
; i
++) {
1018 st
= sbIn
->stmts
[i
];
1019 CLG_ASSERT(isFlatIRStmt(st
));
1030 Addr cia
= st
->Ist
.IMark
.addr
+ st
->Ist
.IMark
.delta
;
1031 UInt isize
= st
->Ist
.IMark
.len
;
1032 CLG_ASSERT(clgs
.instr_offset
== cia
- origAddr
);
1033 // If Vex fails to decode an instruction, the size will be zero.
1034 // Pretend otherwise.
1035 if (isize
== 0) isize
= VG_MIN_INSTR_SZB
;
1037 // Sanity-check size.
1038 tl_assert( (VG_MIN_INSTR_SZB
<= isize
&& isize
<= VG_MAX_INSTR_SZB
)
1039 || VG_CLREQ_SZB
== isize
);
1041 // Init the inode, record it as the current one.
1042 // Subsequent Dr/Dw/Dm events from the same instruction will
1044 curr_inode
= next_InstrInfo (&clgs
, isize
);
1046 addEvent_Ir( &clgs
, curr_inode
);
1051 IRExpr
* data
= st
->Ist
.WrTmp
.data
;
1052 if (data
->tag
== Iex_Load
) {
1053 IRExpr
* aexpr
= data
->Iex
.Load
.addr
;
1054 // Note also, endianness info is ignored. I guess
1055 // that's not interesting.
1056 addEvent_Dr( &clgs
, curr_inode
,
1057 sizeofIRType(data
->Iex
.Load
.ty
), aexpr
);
1063 IRExpr
* data
= st
->Ist
.Store
.data
;
1064 IRExpr
* aexpr
= st
->Ist
.Store
.addr
;
1065 addEvent_Dw( &clgs
, curr_inode
,
1066 sizeofIRType(typeOfIRExpr(sbIn
->tyenv
, data
)), aexpr
);
1071 IRStoreG
* sg
= st
->Ist
.StoreG
.details
;
1072 IRExpr
* data
= sg
->data
;
1073 IRExpr
* addr
= sg
->addr
;
1074 IRType type
= typeOfIRExpr(tyenv
, data
);
1075 tl_assert(type
!= Ity_INVALID
);
1076 addEvent_D_guarded( &clgs
, curr_inode
,
1077 sizeofIRType(type
), addr
, sg
->guard
,
1083 IRLoadG
* lg
= st
->Ist
.LoadG
.details
;
1084 IRType type
= Ity_INVALID
; /* loaded type */
1085 IRType typeWide
= Ity_INVALID
; /* after implicit widening */
1086 IRExpr
* addr
= lg
->addr
;
1087 typeOfIRLoadGOp(lg
->cvt
, &typeWide
, &type
);
1088 tl_assert(type
!= Ity_INVALID
);
1089 addEvent_D_guarded( &clgs
, curr_inode
,
1090 sizeofIRType(type
), addr
, lg
->guard
,
1091 False
/*!isWrite*/ );
1097 IRDirty
* d
= st
->Ist
.Dirty
.details
;
1098 if (d
->mFx
!= Ifx_None
) {
1099 /* This dirty helper accesses memory. Collect the details. */
1100 tl_assert(d
->mAddr
!= NULL
);
1101 tl_assert(d
->mSize
!= 0);
1102 dataSize
= d
->mSize
;
1103 // Large (eg. 28B, 108B, 512B on x86) data-sized
1104 // instructions will be done inaccurately, but they're
1105 // very rare and this avoids errors from hitting more
1106 // than two cache lines in the simulation.
1107 if (CLG_(clo
).simulate_cache
&& dataSize
> CLG_(min_line_size
))
1108 dataSize
= CLG_(min_line_size
);
1109 if (d
->mFx
== Ifx_Read
|| d
->mFx
== Ifx_Modify
)
1110 addEvent_Dr( &clgs
, curr_inode
, dataSize
, d
->mAddr
);
1111 if (d
->mFx
== Ifx_Write
|| d
->mFx
== Ifx_Modify
)
1112 addEvent_Dw( &clgs
, curr_inode
, dataSize
, d
->mAddr
);
1114 tl_assert(d
->mAddr
== NULL
);
1115 tl_assert(d
->mSize
== 0);
1121 /* We treat it as a read and a write of the location. I
1122 think that is the same behaviour as it was before IRCAS
1123 was introduced, since prior to that point, the Vex
1124 front ends would translate a lock-prefixed instruction
1125 into a (normal) read followed by a (normal) write. */
1127 IRCAS
* cas
= st
->Ist
.CAS
.details
;
1128 CLG_ASSERT(cas
->addr
&& isIRAtom(cas
->addr
));
1129 CLG_ASSERT(cas
->dataLo
);
1130 dataSize
= sizeofIRType(typeOfIRExpr(sbIn
->tyenv
, cas
->dataLo
));
1131 if (cas
->dataHi
!= NULL
)
1132 dataSize
*= 2; /* since this is a doubleword-cas */
1133 addEvent_Dr( &clgs
, curr_inode
, dataSize
, cas
->addr
);
1134 addEvent_Dw( &clgs
, curr_inode
, dataSize
, cas
->addr
);
1135 addEvent_G( &clgs
, curr_inode
);
1141 if (st
->Ist
.LLSC
.storedata
== NULL
) {
1143 dataTy
= typeOfIRTemp(sbIn
->tyenv
, st
->Ist
.LLSC
.result
);
1144 addEvent_Dr( &clgs
, curr_inode
,
1145 sizeofIRType(dataTy
), st
->Ist
.LLSC
.addr
);
1146 /* flush events before LL, should help SC to succeed */
1147 flushEvents( &clgs
);
1150 dataTy
= typeOfIRExpr(sbIn
->tyenv
, st
->Ist
.LLSC
.storedata
);
1151 addEvent_Dw( &clgs
, curr_inode
,
1152 sizeofIRType(dataTy
), st
->Ist
.LLSC
.addr
);
1153 /* I don't know whether the global-bus-lock cost should
1154 be attributed to the LL or the SC, but it doesn't
1155 really matter since they always have to be used in
1156 pairs anyway. Hence put it (quite arbitrarily) on
1158 addEvent_G( &clgs
, curr_inode
);
1164 Bool guest_exit
, inverted
;
1166 /* VEX code generation sometimes inverts conditional branches.
1167 * As Callgrind counts (conditional) jumps, it has to correct
1168 * inversions. The heuristic is the following:
1169 * (1) Callgrind switches off SB chasing and unrolling, and
1170 * therefore it assumes that a candidate for inversion only is
1171 * the last conditional branch in an SB.
1172 * (2) inversion is assumed if the branch jumps to the address of
1173 * the next guest instruction in memory.
1174 * This heuristic is precalculated in CLG_(collectBlockInfo)().
1176 * Branching behavior is also used for branch prediction. Note that
1177 * above heuristic is different from what Cachegrind does.
1178 * Cachegrind uses (2) for all branches.
1180 if (cJumps
+1 == clgs
.bb
->cjmp_count
)
1181 inverted
= clgs
.bb
->cjmp_inverted
;
1185 // call branch predictor only if this is a branch in guest code
1186 guest_exit
= (st
->Ist
.Exit
.jk
== Ijk_Boring
) ||
1187 (st
->Ist
.Exit
.jk
== Ijk_Call
) ||
1188 (st
->Ist
.Exit
.jk
== Ijk_Ret
);
1191 /* Stuff to widen the guard expression to a host word, so
1192 we can pass it to the branch predictor simulation
1193 functions easily. */
1194 IRType tyW
= hWordTy
;
1195 IROp widen
= tyW
==Ity_I32
? Iop_1Uto32
: Iop_1Uto64
;
1196 IROp opXOR
= tyW
==Ity_I32
? Iop_Xor32
: Iop_Xor64
;
1197 IRTemp guard1
= newIRTemp(clgs
.sbOut
->tyenv
, Ity_I1
);
1198 IRTemp guardW
= newIRTemp(clgs
.sbOut
->tyenv
, tyW
);
1199 IRTemp guard
= newIRTemp(clgs
.sbOut
->tyenv
, tyW
);
1200 IRExpr
* one
= tyW
==Ity_I32
? IRExpr_Const(IRConst_U32(1))
1201 : IRExpr_Const(IRConst_U64(1));
1203 /* Widen the guard expression. */
1204 addStmtToIRSB( clgs
.sbOut
,
1205 IRStmt_WrTmp( guard1
, st
->Ist
.Exit
.guard
));
1206 addStmtToIRSB( clgs
.sbOut
,
1207 IRStmt_WrTmp( guardW
,
1209 IRExpr_RdTmp(guard1
))) );
1210 /* If the exit is inverted, invert the sense of the guard. */
1215 inverted
? IRExpr_Binop(opXOR
, IRExpr_RdTmp(guardW
), one
)
1216 : IRExpr_RdTmp(guardW
)
1218 /* And post the event. */
1219 addEvent_Bc( &clgs
, curr_inode
, IRExpr_RdTmp(guard
) );
1222 /* We may never reach the next statement, so need to flush
1223 all outstanding transactions now. */
1224 flushEvents( &clgs
);
1226 CLG_ASSERT(clgs
.ii_index
>0);
1227 if (!clgs
.seen_before
) {
1230 if (st
->Ist
.Exit
.jk
== Ijk_Call
) jk
= jk_Call
;
1231 else if (st
->Ist
.Exit
.jk
== Ijk_Ret
) jk
= jk_Return
;
1233 if (IRConst2Addr(st
->Ist
.Exit
.dst
) ==
1234 origAddr
+ curr_inode
->instr_offset
+ curr_inode
->instr_size
)
1240 clgs
.bb
->jmp
[cJumps
].instr
= clgs
.ii_index
-1;
1241 clgs
.bb
->jmp
[cJumps
].jmpkind
= jk
;
1244 /* Update global variable jmps_passed before the jump
1245 * A correction is needed if VEX inverted the last jump condition
1247 UInt val
= inverted
? cJumps
+1 : cJumps
;
1248 addConstMemStoreStmt( clgs
.sbOut
,
1249 (UWord
) &CLG_(current_state
).jmps_passed
,
1261 /* Copy the original statement */
1262 addStmtToIRSB( clgs
.sbOut
, st
);
1265 VG_(printf
)(" pass ");
1271 /* Deal with branches to unknown destinations. Except ignore ones
1272 which are function returns as we assume the return stack
1273 predictor never mispredicts. */
1274 if ((sbIn
->jumpkind
== Ijk_Boring
) || (sbIn
->jumpkind
== Ijk_Call
)) {
1275 if (0) { ppIRExpr( sbIn
->next
); VG_(printf
)("\n"); }
1276 switch (sbIn
->next
->tag
) {
1278 break; /* boring - branch to known address */
1280 /* looks like an indirect branch (branch to unknown) */
1281 addEvent_Bi( &clgs
, curr_inode
, sbIn
->next
);
1284 /* shouldn't happen - if the incoming IR is properly
1285 flattened, should only have tmp and const cases to
1291 /* At the end of the bb. Flush outstandings. */
1292 flushEvents( &clgs
);
1294 /* Update global variable jmps_passed at end of SB.
1295 * As CLG_(current_state).jmps_passed is reset to 0 in setup_bbcc,
1296 * this can be omitted if there is no conditional jump in this SB.
1297 * A correction is needed if VEX inverted the last jump condition
1300 UInt jmps_passed
= cJumps
;
1301 if (clgs
.bb
->cjmp_inverted
) jmps_passed
--;
1302 addConstMemStoreStmt( clgs
.sbOut
,
1303 (UWord
) &CLG_(current_state
).jmps_passed
,
1304 jmps_passed
, hWordTy
);
1306 CLG_ASSERT(clgs
.bb
->cjmp_count
== cJumps
);
1307 CLG_ASSERT(clgs
.bb
->instr_count
== clgs
.ii_index
);
1309 /* Info for final exit from BB */
1313 if (sbIn
->jumpkind
== Ijk_Call
) jk
= jk_Call
;
1314 else if (sbIn
->jumpkind
== Ijk_Ret
) jk
= jk_Return
;
1317 if ((sbIn
->next
->tag
== Iex_Const
) &&
1318 (IRConst2Addr(sbIn
->next
->Iex
.Const
.con
) ==
1319 origAddr
+ clgs
.instr_offset
))
1322 clgs
.bb
->jmp
[cJumps
].jmpkind
= jk
;
1323 /* Instruction index of the call/ret at BB end
1324 * (it is wrong for fall-through, but does not matter) */
1325 clgs
.bb
->jmp
[cJumps
].instr
= clgs
.ii_index
-1;
1328 /* swap information of last exit with final exit if inverted */
1329 if (clgs
.bb
->cjmp_inverted
) {
1333 jk
= clgs
.bb
->jmp
[cJumps
].jmpkind
;
1334 clgs
.bb
->jmp
[cJumps
].jmpkind
= clgs
.bb
->jmp
[cJumps
-1].jmpkind
;
1335 clgs
.bb
->jmp
[cJumps
-1].jmpkind
= jk
;
1336 instr
= clgs
.bb
->jmp
[cJumps
].instr
;
1337 clgs
.bb
->jmp
[cJumps
].instr
= clgs
.bb
->jmp
[cJumps
-1].instr
;
1338 clgs
.bb
->jmp
[cJumps
-1].instr
= instr
;
1341 if (clgs
.seen_before
) {
1342 CLG_ASSERT(clgs
.bb
->cost_count
== update_cost_offsets(&clgs
));
1343 CLG_ASSERT(clgs
.bb
->instr_len
== clgs
.instr_offset
);
1346 clgs
.bb
->cost_count
= update_cost_offsets(&clgs
);
1347 clgs
.bb
->instr_len
= clgs
.instr_offset
;
1350 CLG_DEBUG(3, "- instrument(BB %#lx): byteLen %u, CJumps %u, CostLen %u\n",
1351 origAddr
, clgs
.bb
->instr_len
,
1352 clgs
.bb
->cjmp_count
, clgs
.bb
->cost_count
);
1354 CLG_DEBUG(3, " [ ");
1355 for (i
=0;i
<cJumps
;i
++)
1356 CLG_DEBUG(3, "%u ", clgs
.bb
->jmp
[i
].instr
);
1357 CLG_DEBUG(3, "], last inverted: %s \n",
1358 clgs
.bb
->cjmp_inverted
? "yes":"no");
1364 /*--------------------------------------------------------------------*/
1365 /*--- Discarding BB info ---*/
1366 /*--------------------------------------------------------------------*/
1368 // Called when a translation is removed from the translation cache for
1369 // any reason at all: to free up space, because the guest code was
1370 // unmapped or modified, or for any arbitrary reason.
1372 void clg_discard_superblock_info ( Addr orig_addr
, VexGuestExtents vge
)
1374 tl_assert(vge
.n_used
> 0);
1377 VG_(printf
)( "discard_superblock_info: %p, %p, %llu\n",
1379 (void*)vge
.base
[0], (ULong
)vge
.len
[0]);
1381 // Get BB info, remove from table, free BB info. Simple!
1382 // When created, the BB is keyed by the first instruction address,
1383 // (not orig_addr, but eventually redirected address). Thus, we
1384 // use the first instruction address in vge.
1385 CLG_(delete_bb
)(vge
.base
[0]);
1389 /*------------------------------------------------------------*/
1390 /*--- CLG_(fini)() and related function ---*/
1391 /*------------------------------------------------------------*/
1395 static void zero_thread_cost(thread_info
* t
)
1399 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1400 if (!CLG_(current_call_stack
).entry
[i
].jcc
) continue;
1402 /* reset call counters to current for active calls */
1403 CLG_(copy_cost
)( CLG_(sets
).full
,
1404 CLG_(current_call_stack
).entry
[i
].enter_cost
,
1405 CLG_(current_state
).cost
);
1406 CLG_(current_call_stack
).entry
[i
].jcc
->call_counter
= 0;
1409 CLG_(forall_bbccs
)(CLG_(zero_bbcc
));
1411 /* set counter for last dump */
1412 CLG_(copy_cost
)( CLG_(sets
).full
,
1413 t
->lastdump_cost
, CLG_(current_state
).cost
);
1416 void CLG_(zero_all_cost
)(Bool only_current_thread
)
1418 if (VG_(clo_verbosity
) > 1)
1419 VG_(message
)(Vg_DebugMsg
, " Zeroing costs...\n");
1421 if (only_current_thread
)
1422 zero_thread_cost(CLG_(get_current_thread
)());
1424 CLG_(forall_threads
)(zero_thread_cost
);
1426 if (VG_(clo_verbosity
) > 1)
1427 VG_(message
)(Vg_DebugMsg
, " ...done\n");
1431 void unwind_thread(thread_info
* t
)
1433 /* unwind signal handlers */
1434 while(CLG_(current_state
).sig
!=0)
1435 CLG_(post_signal
)(CLG_(current_tid
),CLG_(current_state
).sig
);
1437 /* unwind regular call stack */
1438 while(CLG_(current_call_stack
).sp
>0)
1439 CLG_(pop_call_stack
)();
1441 /* reset context and function stack for context generation */
1442 CLG_(init_exec_state
)( &CLG_(current_state
) );
1443 CLG_(current_fn_stack
).top
= CLG_(current_fn_stack
).bottom
;
1447 void zero_state_cost(thread_info
* t
)
1449 CLG_(zero_cost
)( CLG_(sets
).full
, CLG_(current_state
).cost
);
1452 void CLG_(set_instrument_state
)(const HChar
* reason
, Bool state
)
1454 if (CLG_(instrument_state
) == state
) {
1455 CLG_DEBUG(2, "%s: instrumentation already %s\n",
1456 reason
, state
? "ON" : "OFF");
1459 CLG_(instrument_state
) = state
;
1460 CLG_DEBUG(2, "%s: Switching instrumentation %s ...\n",
1461 reason
, state
? "ON" : "OFF");
1463 VG_(discard_translations_safely
)( (Addr
)0x1000, ~(SizeT
)0xfff, "callgrind");
1465 /* reset internal state: call stacks, simulator */
1466 CLG_(forall_threads
)(unwind_thread
);
1467 CLG_(forall_threads
)(zero_state_cost
);
1468 (*CLG_(cachesim
).clear
)();
1470 if (VG_(clo_verbosity
) > 1)
1471 VG_(message
)(Vg_DebugMsg
, "%s: instrumentation switched %s\n",
1472 reason
, state
? "ON" : "OFF");
1475 /* helper for dump_state_togdb */
1476 static void dump_state_of_thread_togdb(thread_info
* ti
)
1478 static FullCost sum
= 0, tmp
= 0;
1484 t
= CLG_(current_tid
);
1485 CLG_(init_cost_lz
)( CLG_(sets
).full
, &sum
);
1486 CLG_(copy_cost_lz
)( CLG_(sets
).full
, &tmp
, ti
->lastdump_cost
);
1487 CLG_(add_diff_cost
)( CLG_(sets
).full
, sum
, ti
->lastdump_cost
,
1488 ti
->states
.entry
[0]->cost
);
1489 CLG_(copy_cost
)( CLG_(sets
).full
, ti
->lastdump_cost
, tmp
);
1490 mcost
= CLG_(mappingcost_as_string
)(CLG_(dumpmap
), sum
);
1491 VG_(gdb_printf
)("events-%d: %s\n", t
, mcost
);
1493 VG_(gdb_printf
)("frames-%d: %d\n", t
, CLG_(current_call_stack
).sp
);
1496 for(i
= 0; i
< CLG_(current_call_stack
).sp
; i
++) {
1497 ce
= CLG_(get_call_entry
)(i
);
1498 /* if this frame is skipped, we don't have counters */
1499 if (!ce
->jcc
) continue;
1501 from
= ce
->jcc
->from
;
1502 VG_(gdb_printf
)("function-%d-%d: %s\n",t
, i
, from
->cxt
->fn
[0]->name
);
1503 VG_(gdb_printf
)("calls-%d-%d: %llu\n",t
, i
, ce
->jcc
->call_counter
);
1505 /* FIXME: EventSets! */
1506 CLG_(copy_cost
)( CLG_(sets
).full
, sum
, ce
->jcc
->cost
);
1507 CLG_(copy_cost
)( CLG_(sets
).full
, tmp
, ce
->enter_cost
);
1508 CLG_(add_diff_cost
)( CLG_(sets
).full
, sum
,
1509 ce
->enter_cost
, CLG_(current_state
).cost
);
1510 CLG_(copy_cost
)( CLG_(sets
).full
, ce
->enter_cost
, tmp
);
1512 mcost
= CLG_(mappingcost_as_string
)(CLG_(dumpmap
), sum
);
1513 VG_(gdb_printf
)("events-%d-%d: %s\n",t
, i
, mcost
);
1516 if (ce
&& ce
->jcc
) {
1518 VG_(gdb_printf
)("function-%d-%d: %s\n",t
, i
, to
->cxt
->fn
[0]->name
);
1522 /* Dump current state */
1523 static void dump_state_togdb(void)
1527 Int orig_tid
= CLG_(current_tid
);
1529 VG_(gdb_printf
)("instrumentation: %s\n",
1530 CLG_(instrument_state
) ? "on":"off");
1531 if (!CLG_(instrument_state
)) return;
1533 VG_(gdb_printf
)("executed-bbs: %llu\n", CLG_(stat
).bb_executions
);
1534 VG_(gdb_printf
)("executed-calls: %llu\n", CLG_(stat
).call_counter
);
1535 VG_(gdb_printf
)("distinct-bbs: %d\n", CLG_(stat
).distinct_bbs
);
1536 VG_(gdb_printf
)("distinct-calls: %d\n", CLG_(stat
).distinct_jccs
);
1537 VG_(gdb_printf
)("distinct-functions: %d\n", CLG_(stat
).distinct_fns
);
1538 VG_(gdb_printf
)("distinct-contexts: %d\n", CLG_(stat
).distinct_contexts
);
1540 /* "events:" line. Given here because it will be dynamic in the future */
1541 HChar
*evmap
= CLG_(eventmapping_as_string
)(CLG_(dumpmap
));
1542 VG_(gdb_printf
)("events: %s\n", evmap
);
1544 /* "part:" line (number of last part. Is 0 at start */
1545 VG_(gdb_printf
)("part: %d\n", CLG_(get_dump_counter
)());
1548 th
= CLG_(get_threads
)();
1549 VG_(gdb_printf
)("threads:");
1550 for(t
=1;t
<VG_N_THREADS
;t
++) {
1551 if (!th
[t
]) continue;
1552 VG_(gdb_printf
)(" %d", t
);
1554 VG_(gdb_printf
)("\n");
1555 VG_(gdb_printf
)("current-tid: %d\n", orig_tid
);
1556 CLG_(forall_threads
)(dump_state_of_thread_togdb
);
1560 static void print_monitor_help ( void )
1562 VG_(gdb_printf
) ("\n");
1563 VG_(gdb_printf
) ("callgrind monitor commands:\n");
1564 VG_(gdb_printf
) (" dump [<dump_hint>]\n");
1565 VG_(gdb_printf
) (" dump counters\n");
1566 VG_(gdb_printf
) (" zero\n");
1567 VG_(gdb_printf
) (" zero counters\n");
1568 VG_(gdb_printf
) (" status\n");
1569 VG_(gdb_printf
) (" print status\n");
1570 VG_(gdb_printf
) (" instrumentation [on|off]\n");
1571 VG_(gdb_printf
) (" get/set (if on/off given) instrumentation state\n");
1572 VG_(gdb_printf
) ("\n");
1575 /* return True if request recognised, False otherwise */
1576 static Bool
handle_gdb_monitor_command (ThreadId tid
, const HChar
*req
)
1579 HChar s
[VG_(strlen
)(req
) + 1]; /* copy for strtok_r */
1582 VG_(strcpy
) (s
, req
);
1584 wcmd
= VG_(strtok_r
) (s
, " ", &ssaveptr
);
1585 switch (VG_(keyword_id
) ("help dump zero status instrumentation",
1586 wcmd
, kwd_report_duplicated_matches
)) {
1587 case -2: /* multiple matches */
1589 case -1: /* not found */
1592 print_monitor_help();
1594 case 1: { /* dump */
1595 CLG_(dump_profile
)(req
, False
);
1598 case 2: { /* zero */
1599 CLG_(zero_all_cost
)(False
);
1603 case 3: { /* status */
1604 HChar
* arg
= VG_(strtok_r
) (0, " ", &ssaveptr
);
1605 if (arg
&& (VG_(strcmp
)(arg
, "internal") == 0)) {
1606 /* internal interface to callgrind_control */
1611 if (!CLG_(instrument_state
)) {
1612 VG_(gdb_printf
)("No status available as instrumentation is switched off\n");
1614 // Status information to be improved ...
1615 thread_info
** th
= CLG_(get_threads
)();
1617 for(t
=1;t
<VG_N_THREADS
;t
++)
1618 if (th
[t
]) tcount
++;
1619 VG_(gdb_printf
)("%d thread(s) running.\n", tcount
);
1624 case 4: { /* instrumentation */
1625 HChar
* arg
= VG_(strtok_r
) (0, " ", &ssaveptr
);
1627 VG_(gdb_printf
)("instrumentation: %s\n",
1628 CLG_(instrument_state
) ? "on":"off");
1631 CLG_(set_instrument_state
)("Command", VG_(strcmp
)(arg
,"off")!=0);
1642 Bool
CLG_(handle_client_request
)(ThreadId tid
, UWord
*args
, UWord
*ret
)
1644 if (!VG_IS_TOOL_USERREQ('C','T',args
[0])
1645 && VG_USERREQ__GDB_MONITOR_COMMAND
!= args
[0])
1649 case VG_USERREQ__DUMP_STATS
:
1650 CLG_(dump_profile
)("Client Request", True
);
1651 *ret
= 0; /* meaningless */
1654 case VG_USERREQ__DUMP_STATS_AT
:
1656 const HChar
*arg
= (HChar
*)args
[1];
1657 HChar buf
[30 + VG_(strlen
)(arg
)]; // large enough
1658 VG_(sprintf
)(buf
,"Client Request: %s", arg
);
1659 CLG_(dump_profile
)(buf
, True
);
1660 *ret
= 0; /* meaningless */
1664 case VG_USERREQ__ZERO_STATS
:
1665 CLG_(zero_all_cost
)(True
);
1666 *ret
= 0; /* meaningless */
1669 case VG_USERREQ__TOGGLE_COLLECT
:
1670 CLG_(current_state
).collect
= !CLG_(current_state
).collect
;
1671 CLG_DEBUG(2, "Client Request: toggled collection state to %s\n",
1672 CLG_(current_state
).collect
? "ON" : "OFF");
1673 *ret
= 0; /* meaningless */
1676 case VG_USERREQ__START_INSTRUMENTATION
:
1677 CLG_(set_instrument_state
)("Client Request", True
);
1678 *ret
= 0; /* meaningless */
1681 case VG_USERREQ__STOP_INSTRUMENTATION
:
1682 CLG_(set_instrument_state
)("Client Request", False
);
1683 *ret
= 0; /* meaningless */
1686 case VG_USERREQ__GDB_MONITOR_COMMAND
: {
1687 Bool handled
= handle_gdb_monitor_command (tid
, (HChar
*)args
[1]);
1702 /* Syscall Timing */
1704 /* struct timeval syscalltime[VG_N_THREADS]; */
1705 #if CLG_MICROSYSTIME
1712 void CLG_(pre_syscalltime
)(ThreadId tid
, UInt syscallno
,
1713 UWord
* args
, UInt nArgs
)
1715 if (CLG_(clo
).collect_systime
) {
1716 #if CLG_MICROSYSTIME
1717 struct vki_timeval tv_now
;
1718 VG_(gettimeofday
)(&tv_now
, NULL
);
1719 syscalltime
[tid
] = tv_now
.tv_sec
* 1000000ULL + tv_now
.tv_usec
;
1721 syscalltime
[tid
] = VG_(read_millisecond_timer
)();
1727 void CLG_(post_syscalltime
)(ThreadId tid
, UInt syscallno
,
1728 UWord
* args
, UInt nArgs
, SysRes res
)
1730 if (CLG_(clo
).collect_systime
&&
1731 CLG_(current_state
).bbcc
) {
1733 #if CLG_MICROSYSTIME
1734 struct vki_timeval tv_now
;
1737 VG_(gettimeofday
)(&tv_now
, NULL
);
1738 diff
= (tv_now
.tv_sec
* 1000000ULL + tv_now
.tv_usec
) - syscalltime
[tid
];
1740 UInt diff
= VG_(read_millisecond_timer
)() - syscalltime
[tid
];
1743 /* offset o is for "SysCount", o+1 for "SysTime" */
1744 o
= fullOffset(EG_SYS
);
1746 CLG_DEBUG(0," Time (Off %d) for Syscall %u: %llu\n", o
, syscallno
,
1749 CLG_(current_state
).cost
[o
] ++;
1750 CLG_(current_state
).cost
[o
+1] += diff
;
1751 if (!CLG_(current_state
).bbcc
->skipped
)
1752 CLG_(init_cost_lz
)(CLG_(sets
).full
,
1753 &(CLG_(current_state
).bbcc
->skipped
));
1754 CLG_(current_state
).bbcc
->skipped
[o
] ++;
1755 CLG_(current_state
).bbcc
->skipped
[o
+1] += diff
;
1759 static UInt
ULong_width(ULong n
)
1767 return w
+ (w
-1)/3; // add space for commas
1771 void branchsim_printstat(int l1
, int l2
, int l3
)
1773 static HChar fmt
[128]; // large enough
1775 ULong Bc_total_b
, Bc_total_mp
, Bi_total_b
, Bi_total_mp
;
1776 ULong B_total_b
, B_total_mp
;
1778 total
= CLG_(total_cost
);
1779 Bc_total_b
= total
[ fullOffset(EG_BC
) ];
1780 Bc_total_mp
= total
[ fullOffset(EG_BC
)+1 ];
1781 Bi_total_b
= total
[ fullOffset(EG_BI
) ];
1782 Bi_total_mp
= total
[ fullOffset(EG_BI
)+1 ];
1784 /* Make format string, getting width right for numbers */
1785 VG_(sprintf
)(fmt
, "%%s %%,%dllu (%%,%dllu cond + %%,%dllu ind)\n",
1788 if (0 == Bc_total_b
) Bc_total_b
= 1;
1789 if (0 == Bi_total_b
) Bi_total_b
= 1;
1790 B_total_b
= Bc_total_b
+ Bi_total_b
;
1791 B_total_mp
= Bc_total_mp
+ Bi_total_mp
;
1794 VG_(umsg
)(fmt
, "Branches: ",
1795 B_total_b
, Bc_total_b
, Bi_total_b
);
1797 VG_(umsg
)(fmt
, "Mispredicts: ",
1798 B_total_mp
, Bc_total_mp
, Bi_total_mp
);
1800 VG_(umsg
)("Mispred rate: %*.1f%% (%*.1f%% + %*.1f%% )\n",
1801 l1
, B_total_mp
* 100.0 / B_total_b
,
1802 l2
, Bc_total_mp
* 100.0 / Bc_total_b
,
1803 l3
, Bi_total_mp
* 100.0 / Bi_total_b
);
1807 void clg_print_stats(void)
1810 CLG_(stat
).full_debug_BBs
+
1811 CLG_(stat
).fn_name_debug_BBs
+
1812 CLG_(stat
).file_line_debug_BBs
+
1813 CLG_(stat
).no_debug_BBs
;
1815 /* Hash table stats */
1816 VG_(message
)(Vg_DebugMsg
, "Distinct objects: %d\n",
1817 CLG_(stat
).distinct_objs
);
1818 VG_(message
)(Vg_DebugMsg
, "Distinct files: %d\n",
1819 CLG_(stat
).distinct_files
);
1820 VG_(message
)(Vg_DebugMsg
, "Distinct fns: %d\n",
1821 CLG_(stat
).distinct_fns
);
1822 VG_(message
)(Vg_DebugMsg
, "Distinct contexts:%d\n",
1823 CLG_(stat
).distinct_contexts
);
1824 VG_(message
)(Vg_DebugMsg
, "Distinct BBs: %d\n",
1825 CLG_(stat
).distinct_bbs
);
1826 VG_(message
)(Vg_DebugMsg
, "Cost entries: %u (Chunks %u)\n",
1827 CLG_(costarray_entries
), CLG_(costarray_chunks
));
1828 VG_(message
)(Vg_DebugMsg
, "Distinct BBCCs: %d\n",
1829 CLG_(stat
).distinct_bbccs
);
1830 VG_(message
)(Vg_DebugMsg
, "Distinct JCCs: %d\n",
1831 CLG_(stat
).distinct_jccs
);
1832 VG_(message
)(Vg_DebugMsg
, "Distinct skips: %d\n",
1833 CLG_(stat
).distinct_skips
);
1834 VG_(message
)(Vg_DebugMsg
, "BB lookups: %d\n",
1837 VG_(message
)(Vg_DebugMsg
, "With full debug info:%3d%% (%d)\n",
1838 CLG_(stat
).full_debug_BBs
* 100 / BB_lookups
,
1839 CLG_(stat
).full_debug_BBs
);
1840 VG_(message
)(Vg_DebugMsg
, "With file/line debug info:%3d%% (%d)\n",
1841 CLG_(stat
).file_line_debug_BBs
* 100 / BB_lookups
,
1842 CLG_(stat
).file_line_debug_BBs
);
1843 VG_(message
)(Vg_DebugMsg
, "With fn name debug info:%3d%% (%d)\n",
1844 CLG_(stat
).fn_name_debug_BBs
* 100 / BB_lookups
,
1845 CLG_(stat
).fn_name_debug_BBs
);
1846 VG_(message
)(Vg_DebugMsg
, "With no debug info:%3d%% (%d)\n",
1847 CLG_(stat
).no_debug_BBs
* 100 / BB_lookups
,
1848 CLG_(stat
).no_debug_BBs
);
1850 VG_(message
)(Vg_DebugMsg
, "BBCC Clones: %d\n",
1851 CLG_(stat
).bbcc_clones
);
1852 VG_(message
)(Vg_DebugMsg
, "BBs Retranslated: %d\n",
1853 CLG_(stat
).bb_retranslations
);
1854 VG_(message
)(Vg_DebugMsg
, "Distinct instrs: %d\n",
1855 CLG_(stat
).distinct_instrs
);
1857 VG_(message
)(Vg_DebugMsg
, "LRU Contxt Misses: %d\n",
1858 CLG_(stat
).cxt_lru_misses
);
1859 VG_(message
)(Vg_DebugMsg
, "LRU BBCC Misses: %d\n",
1860 CLG_(stat
).bbcc_lru_misses
);
1861 VG_(message
)(Vg_DebugMsg
, "LRU JCC Misses: %d\n",
1862 CLG_(stat
).jcc_lru_misses
);
1863 VG_(message
)(Vg_DebugMsg
, "BBs Executed: %llu\n",
1864 CLG_(stat
).bb_executions
);
1865 VG_(message
)(Vg_DebugMsg
, "Calls: %llu\n",
1866 CLG_(stat
).call_counter
);
1867 VG_(message
)(Vg_DebugMsg
, "CondJMP followed: %llu\n",
1868 CLG_(stat
).jcnd_counter
);
1869 VG_(message
)(Vg_DebugMsg
, "Boring JMPs: %llu\n",
1870 CLG_(stat
).jump_counter
);
1871 VG_(message
)(Vg_DebugMsg
, "Recursive calls: %llu\n",
1872 CLG_(stat
).rec_call_counter
);
1873 VG_(message
)(Vg_DebugMsg
, "Returns: %llu\n",
1874 CLG_(stat
).ret_counter
);
1881 HChar fmt
[128]; // large enough
1885 CLG_DEBUG(0, "finish()\n");
1887 (*CLG_(cachesim
).finish
)();
1889 /* pop all remaining items from CallStack for correct sum
1891 CLG_(forall_threads
)(unwind_thread
);
1893 CLG_(dump_profile
)(0, False
);
1895 if (VG_(clo_verbosity
) == 0) return;
1897 if (VG_(clo_stats
)) {
1898 VG_(message
)(Vg_DebugMsg
, "\n");
1900 VG_(message
)(Vg_DebugMsg
, "\n");
1903 HChar
*evmap
= CLG_(eventmapping_as_string
)(CLG_(dumpmap
));
1904 VG_(message
)(Vg_UserMsg
, "Events : %s\n", evmap
);
1906 HChar
*mcost
= CLG_(mappingcost_as_string
)(CLG_(dumpmap
), CLG_(total_cost
));
1907 VG_(message
)(Vg_UserMsg
, "Collected : %s\n", mcost
);
1909 VG_(message
)(Vg_UserMsg
, "\n");
1911 /* determine value widths for statistics */
1912 total
= CLG_(total_cost
);
1913 l1
= ULong_width( total
[fullOffset(EG_IR
)] );
1915 if (CLG_(clo
).simulate_cache
) {
1916 l2
= ULong_width( total
[fullOffset(EG_DR
)] );
1917 l3
= ULong_width( total
[fullOffset(EG_DW
)] );
1919 if (CLG_(clo
).simulate_branch
) {
1920 int l2b
= ULong_width( total
[fullOffset(EG_BC
)] );
1921 int l3b
= ULong_width( total
[fullOffset(EG_BI
)] );
1922 if (l2b
> l2
) l2
= l2b
;
1923 if (l3b
> l3
) l3
= l3b
;
1926 /* Make format string, getting width right for numbers */
1927 VG_(sprintf
)(fmt
, "%%s %%,%dllu\n", l1
);
1929 /* Always print this */
1930 VG_(umsg
)(fmt
, "I refs: ", total
[fullOffset(EG_IR
)] );
1932 if (CLG_(clo
).simulate_cache
)
1933 (*CLG_(cachesim
).printstat
)(l1
, l2
, l3
);
1935 if (CLG_(clo
).simulate_branch
)
1936 branchsim_printstat(l1
, l2
, l3
);
1941 void CLG_(fini
)(Int exitcode
)
1947 /*--------------------------------------------------------------------*/
1949 /*--------------------------------------------------------------------*/
1951 static void clg_start_client_code_callback ( ThreadId tid
, ULong blocks_done
)
1953 static ULong last_blocks_done
= 0;
1956 VG_(printf
)("%d R %llu\n", (Int
)tid
, blocks_done
);
1958 /* throttle calls to CLG_(run_thread) by number of BBs executed */
1959 if (blocks_done
- last_blocks_done
< 5000) return;
1960 last_blocks_done
= blocks_done
;
1962 CLG_(run_thread
)( tid
);
1966 void CLG_(post_clo_init
)(void)
1968 if (VG_(clo_vex_control
).iropt_register_updates_default
1969 != VexRegUpdSpAtMemAccess
) {
1970 CLG_DEBUG(1, " Using user specified value for "
1971 "--vex-iropt-register-updates\n");
1974 " Using default --vex-iropt-register-updates="
1975 "sp-at-mem-access\n");
1978 if (CLG_(clo
).collect_systime
) {
1979 VG_(needs_syscall_wrapper
)(CLG_(pre_syscalltime
),
1980 CLG_(post_syscalltime
));
1981 syscalltime
= CLG_MALLOC("cl.main.pci.1",
1982 VG_N_THREADS
* sizeof syscalltime
[0]);
1983 for (UInt i
= 0; i
< VG_N_THREADS
; ++i
) {
1988 if (VG_(clo_px_file_backed
) != VexRegUpdSpAtMemAccess
) {
1989 CLG_DEBUG(1, " Using user specified value for "
1990 "--px-file-backed\n");
1993 " Using default --px-file-backed="
1994 "sp-at-mem-access\n");
1997 if (VG_(clo_vex_control
).iropt_unroll_thresh
!= 0) {
1998 VG_(message
)(Vg_UserMsg
,
1999 "callgrind only works with --vex-iropt-unroll-thresh=0\n"
2000 "=> resetting it back to 0\n");
2001 VG_(clo_vex_control
).iropt_unroll_thresh
= 0; // cannot be overridden.
2003 if (VG_(clo_vex_control
).guest_chase_thresh
!= 0) {
2004 VG_(message
)(Vg_UserMsg
,
2005 "callgrind only works with --vex-guest-chase-thresh=0\n"
2006 "=> resetting it back to 0\n");
2007 VG_(clo_vex_control
).guest_chase_thresh
= 0; // cannot be overridden.
2010 CLG_DEBUG(1, " dump threads: %s\n", CLG_(clo
).separate_threads
? "Yes":"No");
2011 CLG_DEBUG(1, " call sep. : %d\n", CLG_(clo
).separate_callers
);
2012 CLG_DEBUG(1, " rec. sep. : %d\n", CLG_(clo
).separate_recursions
);
2014 if (!CLG_(clo
).dump_line
&& !CLG_(clo
).dump_instr
&& !CLG_(clo
).dump_bb
) {
2015 VG_(message
)(Vg_UserMsg
, "Using source line as position.\n");
2016 CLG_(clo
).dump_line
= True
;
2021 (*CLG_(cachesim
).post_clo_init
)();
2023 CLG_(init_eventsets
)();
2024 CLG_(init_statistics
)(& CLG_(stat
));
2025 CLG_(init_cost_lz
)( CLG_(sets
).full
, &CLG_(total_cost
) );
2027 /* initialize hash tables */
2028 CLG_(init_obj_table
)();
2029 CLG_(init_cxt_table
)();
2030 CLG_(init_bb_hash
)();
2032 CLG_(init_threads
)();
2033 CLG_(run_thread
)(1);
2035 CLG_(instrument_state
) = CLG_(clo
).instrument_atstart
;
2037 if (VG_(clo_verbosity
) > 0) {
2038 VG_(message
)(Vg_UserMsg
,
2039 "For interactive control, run 'callgrind_control%s%s -h'.\n",
2040 (VG_(arg_vgdb_prefix
) ? " " : ""),
2041 (VG_(arg_vgdb_prefix
) ? VG_(arg_vgdb_prefix
) : ""));
2046 void CLG_(pre_clo_init
)(void)
2048 VG_(details_name
) ("Callgrind");
2049 VG_(details_version
) (NULL
);
2050 VG_(details_description
) ("a call-graph generating cache profiler");
2051 VG_(details_copyright_author
)("Copyright (C) 2002-2017, and GNU GPL'd, "
2052 "by Josef Weidendorfer et al.");
2053 VG_(details_bug_reports_to
) (VG_BUGS_TO
);
2054 VG_(details_avg_translation_sizeB
) ( 500 );
2056 VG_(clo_vex_control
).iropt_register_updates_default
2057 = VG_(clo_px_file_backed
)
2058 = VexRegUpdSpAtMemAccess
; // overridable by the user.
2060 VG_(clo_vex_control
).iropt_unroll_thresh
= 0; // cannot be overridden.
2061 VG_(clo_vex_control
).guest_chase_thresh
= 0; // cannot be overridden.
2063 VG_(basic_tool_funcs
) (CLG_(post_clo_init
),
2067 VG_(needs_superblock_discards
)(clg_discard_superblock_info
);
2070 VG_(needs_command_line_options
)(CLG_(process_cmd_line_option
),
2072 CLG_(print_debug_usage
));
2074 VG_(needs_client_requests
)(CLG_(handle_client_request
));
2075 VG_(needs_print_stats
) (clg_print_stats
);
2077 VG_(track_start_client_code
) ( & clg_start_client_code_callback
);
2078 VG_(track_pre_deliver_signal
) ( & CLG_(pre_signal
) );
2079 VG_(track_post_deliver_signal
)( & CLG_(post_signal
) );
2081 CLG_(set_clo_defaults
)();
2085 VG_DETERMINE_INTERFACE_VERSION(CLG_(pre_clo_init
))
2087 /*--------------------------------------------------------------------*/
2088 /*--- end main.c ---*/
2089 /*--------------------------------------------------------------------*/