Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Callgrind: use jmpkind from VEX for side exits.

To detect calls and returns, Callgrind's heuristic
starts with using the jumpkind got from VEX for
a control flow change instruction. However, for
side exits, it always assumed a (conditional) jump,
which holds true for x86, but e.g. not for ARM.

This fixes Callgrind to use the jumpkind found
by VEX for all exits, which should help making
Callgrind work for ARM. It also moves the check
whether a boring jump is actually a fall-through
to instrumentation time. This changes (fixes) the
result for indirect jumps to the next instruction,
which should not be classified as fall-through
(anyway, this case is probably very rare).

This patch introduces an own enum for jump kinds
in Callgrind. This is less confusing than misusing
the VEX jump kind type, as Callgrinds wants
to distinguish BB fall-throughs from real jumps
(which both are Ijk_Boring in VEX).
Also, setup_bbcc now stores separately whether the
jump kind is conditional or not.

git-svn-id: svn://svn.valgrind.org/valgrind/trunk@12269 a5019735-40e9-0310-863c-91ae7b9d1cf9
  • Loading branch information...
commit 28a23c05ce81a384283eb6d92d39ace2ff7283ca 1 parent add9ec4
weidendo authored
68 callgrind/bbcc.c
@@ -555,7 +555,9 @@ void CLG_(setup_bbcc)(BB* bb)
555 555 Addr sp;
556 556 BB* last_bb;
557 557 ThreadId tid;
558   - Int jmpkind, passed = 0, csp;
  558 + ClgJumpKind jmpkind;
  559 + Bool isConditionalJump;
  560 + Int passed = 0, csp;
559 561 Bool ret_without_call = False;
560 562 Int popcount_on_return = 1;
561 563
@@ -581,16 +583,8 @@ void CLG_(setup_bbcc)(BB* bb)
581 583 if (last_bb) {
582 584 passed = CLG_(current_state).jmps_passed;
583 585 CLG_ASSERT(passed <= last_bb->cjmp_count);
584   - if (passed == last_bb->cjmp_count) {
585   - jmpkind = last_bb->jmpkind;
586   -
587   - /* VEX always gives a Boring jump kind also when passed trough */
588   - if ((jmpkind == Ijk_Boring) &&
589   - (last_bb->offset + last_bb->instr_len == bb->offset))
590   - jmpkind = JmpNone;
591   - }
592   - else
593   - jmpkind = JmpCond;
  586 + jmpkind = last_bb->jmp[passed].jmpkind;
  587 + isConditionalJump = (passed < last_bb->cjmp_count);
594 588
595 589 /* if we are in a function which is skipped in the call graph, we
596 590 * do not increment the exe counter to produce cost (if simulation off),
@@ -612,7 +606,8 @@ void CLG_(setup_bbcc)(BB* bb)
612 606 }
613 607 }
614 608 else {
615   - jmpkind = JmpNone;
  609 + jmpkind = jk_None;
  610 + isConditionalJump = False;
616 611 }
617 612
618 613 /* Manipulate JmpKind if needed, only using BB specific info */
@@ -620,7 +615,7 @@ void CLG_(setup_bbcc)(BB* bb)
620 615 csp = CLG_(current_call_stack).sp;
621 616
622 617 /* A return not matching the top call in our callstack is a jump */
623   - if ( (jmpkind == Ijk_Ret) && (csp >0)) {
  618 + if ( (jmpkind == jk_Return) && (csp >0)) {
624 619 Int csp_up = csp-1;
625 620 call_entry* top_ce = &(CLG_(current_call_stack).entry[csp_up]);
626 621
@@ -650,14 +645,14 @@ void CLG_(setup_bbcc)(BB* bb)
650 645 }
651 646 }
652 647 if (popcount_on_return == 0) {
653   - jmpkind = Ijk_Boring;
  648 + jmpkind = jk_Jump;
654 649 ret_without_call = True;
655 650 }
656 651 }
657 652
658 653 /* Should this jump be converted to call or pop/call ? */
659   - if (( jmpkind != Ijk_Ret) &&
660   - ( jmpkind != Ijk_Call) && last_bb) {
  654 + if (( jmpkind != jk_Return) &&
  655 + ( jmpkind != jk_Call) && last_bb) {
661 656
662 657 /* We simulate a JMP/Cont to be a CALL if
663 658 * - jump is in another ELF object or section kind
@@ -701,30 +696,32 @@ void CLG_(setup_bbcc)(BB* bb)
701 696 }
702 697 }
703 698
704   - jmpkind = Ijk_Call;
  699 + jmpkind = jk_Call;
705 700 call_emulation = True;
706 701 }
707 702 }
708 703
709   - if (jmpkind == Ijk_Call)
  704 + if (jmpkind == jk_Call)
710 705 skip = CLG_(get_fn_node)(bb)->skip;
711 706
712 707 CLG_DEBUGIF(1) {
713   - if (jmpkind == JmpCond)
714   - VG_(printf)("Conditional");
715   - else if (jmpkind == JmpNone)
716   - VG_(printf)("None");
717   - else
718   - ppIRJumpKind( jmpkind );
719   -
720   - VG_(printf)(" %08lx -> %08lx, SP %08lx\n",
721   - last_bb ? bb_jmpaddr(last_bb) : 0,
722   - bb_addr(bb), sp);
  708 + if (isConditionalJump)
  709 + VG_(printf)("Cond-");
  710 + switch(jmpkind) {
  711 + case jk_None: VG_(printf)("Fall-through"); break;
  712 + case jk_Jump: VG_(printf)("Jump"); break;
  713 + case jk_Call: VG_(printf)("Call"); break;
  714 + case jk_Return: VG_(printf)("Return"); break;
  715 + default: tl_assert(0);
  716 + }
  717 + VG_(printf)(" %08lx -> %08lx, SP %08lx\n",
  718 + last_bb ? bb_jmpaddr(last_bb) : 0,
  719 + bb_addr(bb), sp);
723 720 }
724 721
725 722 /* Handle CALL/RET and update context to get correct BBCC */
726 723
727   - if (jmpkind == Ijk_Ret) {
  724 + if (jmpkind == jk_Return) {
728 725
729 726 if ((csp == 0) ||
730 727 ((CLG_(current_fn_stack).top > CLG_(current_fn_stack).bottom) &&
@@ -745,10 +742,10 @@ void CLG_(setup_bbcc)(BB* bb)
745 742 Int unwind_count = CLG_(unwind_call_stack)(sp, 0);
746 743 if (unwind_count > 0) {
747 744 /* if unwinding was done, this actually is a return */
748   - jmpkind = Ijk_Ret;
  745 + jmpkind = jk_Return;
749 746 }
750 747
751   - if (jmpkind == Ijk_Call) {
  748 + if (jmpkind == jk_Call) {
752 749 delayed_push = True;
753 750
754 751 csp = CLG_(current_call_stack).sp;
@@ -848,8 +845,7 @@ void CLG_(setup_bbcc)(BB* bb)
848 845 bbcc, sp, skip);
849 846 }
850 847
851   - if (CLG_(clo).collect_jumps &&
852   - ((jmpkind == JmpCond) || (jmpkind == Ijk_Boring))) {
  848 + if (CLG_(clo).collect_jumps && (jmpkind == jk_Jump)) {
853 849
854 850 /* Handle conditional jumps followed, i.e. trace arcs
855 851 * This uses JCC structures, too */
@@ -857,15 +853,15 @@ void CLG_(setup_bbcc)(BB* bb)
857 853 jCC* jcc = CLG_(get_jcc)(last_bbcc, passed, bbcc);
858 854 CLG_ASSERT(jcc != 0);
859 855 // Change from default, and check if already changed
860   - if (jcc->jmpkind == Ijk_Call)
861   - jcc->jmpkind = jmpkind;
  856 + if (jcc->jmpkind == jk_Call)
  857 + jcc->jmpkind = isConditionalJump ? jk_CondJump : jk_Jump;
862 858 else {
863 859 // FIXME: Why can this fail?
864 860 // CLG_ASSERT(jcc->jmpkind == jmpkind);
865 861 }
866 862
867 863 jcc->call_counter++;
868   - if (jmpkind == JmpCond)
  864 + if (isConditionalJump)
869 865 CLG_(stat).jcnd_counter++;
870 866 else
871 867 CLG_(stat).jump_counter++;
10 callgrind/callstack.c
@@ -235,8 +235,14 @@ void CLG_(push_call_stack)(BBCC* from, UInt jmp, BBCC* to, Addr sp, Bool skip)
235 235
236 236 /* return address is only is useful with a real call;
237 237 * used to detect RET w/o CALL */
238   - ret_addr = (from->bb->jmpkind == Ijk_Call) ?
239   - bb_addr(from->bb) + from->bb->instr_len : 0;
  238 + if (from->bb->jmp[jmp].jmpkind == jk_Call) {
  239 + UInt instr = from->bb->jmp[jmp].instr;
  240 + ret_addr = bb_addr(from->bb) +
  241 + from->bb->instr[instr].instr_offset +
  242 + from->bb->instr[instr].instr_size;
  243 + }
  244 + else
  245 + ret_addr = 0;
240 246
241 247 /* put jcc on call stack */
242 248 current_entry->jcc = jcc;
12 callgrind/dump.c
@@ -669,7 +669,7 @@ static void fprint_jcc(Int fd, jCC* jcc, AddrPos* curr, AddrPos* last, ULong eco
669 669 target.file = last->file;
670 670 }
671 671
672   - if ((jcc->jmpkind == JmpCond) || (jcc->jmpkind == Ijk_Boring)) {
  672 + if ((jcc->jmpkind == jk_CondJump) || (jcc->jmpkind == jk_Jump)) {
673 673
674 674 /* this is a JCC for a followed conditional or boring jump. */
675 675 CLG_ASSERT(CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost));
@@ -703,7 +703,7 @@ static void fprint_jcc(Int fd, jCC* jcc, AddrPos* curr, AddrPos* last, ULong eco
703 703 print_fn(fd, outbuf, "jfn", jcc->to->cxt->fn[0]);
704 704 }
705 705
706   - if (jcc->jmpkind == JmpCond) {
  706 + if (jcc->jmpkind == jk_CondJump) {
707 707 /* format: jcnd=<followed>/<executions> <target> */
708 708 VG_(sprintf)(outbuf, "jcnd=%llu/%llu ",
709 709 jcc->call_counter, ecounter);
@@ -834,7 +834,7 @@ static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
834 834 if (bb->jmp[jmp].instr == instr) {
835 835 jcc_count=0;
836 836 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from)
837   - if (((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
  837 + if (((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
838 838 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
839 839 jcc_count++;
840 840
@@ -848,7 +848,7 @@ static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
848 848 fprint_apos(fd, &(currCost->p), last, bbcc->cxt->fn[0]->file);
849 849 something_written = True;
850 850 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
851   - if (((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
  851 + if (((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
852 852 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
853 853 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
854 854 }
@@ -867,7 +867,7 @@ static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
867 867 jcc_count = 0;
868 868 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
869 869 /* yes, if JCC only counts jmp arcs or cost >0 */
870   - if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
  870 + if ( ((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
871 871 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
872 872 jcc_count++;
873 873 }
@@ -901,7 +901,7 @@ static Bool fprint_bbcc(Int fd, BBCC* bbcc, AddrPos* last)
901 901 if (jcc_count > 0)
902 902 for(jcc=bbcc->jmp[jmp].jcc_list; jcc; jcc=jcc->next_from) {
903 903 CLG_ASSERT(jcc->jmp == jmp);
904   - if ( ((jcc->jmpkind != Ijk_Call) && (jcc->call_counter >0)) ||
  904 + if ( ((jcc->jmpkind != jk_Call) && (jcc->call_counter >0)) ||
905 905 (!CLG_(is_zero_cost)( CLG_(sets).full, jcc->cost )))
906 906
907 907 fprint_jcc(fd, jcc, &(currCost->p), last, ecounter);
32 callgrind/global.h
@@ -229,6 +229,18 @@ typedef ULong* UserCost;
229 229 typedef ULong* FullCost; /* Simulator + User */
230 230
231 231
  232 +/* The types of control flow changes that can happen between
  233 + * execution of two BBs in a thread.
  234 + */
  235 +typedef enum {
  236 + jk_None = 0, /* no explicit change by a guest instruction */
  237 + jk_Jump, /* regular jump */
  238 + jk_Call,
  239 + jk_Return,
  240 + jk_CondJump /* conditional jump taken (only used as jCC type) */
  241 +} ClgJumpKind;
  242 +
  243 +
232 244 /* JmpCall cost center
233 245 * for subroutine call (from->bb->jmp_addr => to->bb->addr)
234 246 *
@@ -248,11 +260,9 @@ typedef ULong* FullCost; /* Simulator + User */
248 260 * After updating, <last> is set to current event counters. Thus,
249 261 * events are not counted twice for recursive calls (TODO: True?)
250 262 */
251   -#define JmpNone (Ijk_Boring+30)
252   -#define JmpCond (Ijk_Boring+31)
253 263
254 264 struct _jCC {
255   - Int jmpkind; /* JmpCall, JmpBoring, JmpCond */
  265 + ClgJumpKind jmpkind; /* jk_Call, jk_Jump, jk_CondJump */
256 266 jCC* next_hash; /* for hash entry chain */
257 267 jCC* next_from; /* next JCC from a BBCC */
258 268 BBCC *from, *to; /* call arc from/to this BBCC */
@@ -276,13 +286,14 @@ struct _InstrInfo {
276 286 };
277 287
278 288
  289 +
279 290 /*
280   - * Info for a conditional jump in a basic block
  291 + * Info for a side exit in a BB
281 292 */
282 293 typedef struct _CJmpInfo CJmpInfo;
283 294 struct _CJmpInfo {
284   - UInt instr; /* instruction index in this basic block */
285   - Bool skip; /* Cond.Jumps to next instruction should be ignored */
  295 + UInt instr; /* instruction index for BB.instr array */
  296 + ClgJumpKind jmpkind; /* jump kind when leaving BB at this side exit */
286 297 };
287 298
288 299
@@ -319,11 +330,10 @@ struct _BB {
319 330 BBCC* last_bbcc; /* Temporary: Cached for faster access (LRU) */
320 331
321 332 /* filled by CLG_(instrument) if not seen before */
322   - UInt cjmp_count; /* number of conditional exits */
  333 + UInt cjmp_count; /* number of side exits */
323 334 CJmpInfo* jmp; /* array of info for condition jumps,
324 335 * allocated directly after this struct */
325   - Int jmpkind; /* remember jump kind of final exit */
326   - Bool cjmp_inverted; /* condition of last cond.jump can be inverted by VEX */
  336 + Bool cjmp_inverted; /* is last side exit actually fall through? */
327 337
328 338 UInt instr_len;
329 339 UInt cost_count;
@@ -357,12 +367,12 @@ struct _Context {
357 367
358 368
359 369 /*
360   - * Info for a conditional jump in a basic block
  370 + * Cost info for a side exits from a BB
361 371 */
362 372 typedef struct _JmpData JmpData;
363 373 struct _JmpData {
364 374 ULong ecounter; /* number of times the BB was left at this exit */
365   - jCC* jcc_list; /* JCCs for Cond.Jumps from this exit */
  375 + jCC* jcc_list; /* JCCs used for this exit */
366 376 };
367 377
368 378
2  callgrind/jumps.c
@@ -152,7 +152,7 @@ static jCC* new_jcc(BBCC* from, UInt jmp, BBCC* to)
152 152 jcc->from = from;
153 153 jcc->jmp = jmp;
154 154 jcc->to = to;
155   - jcc->jmpkind = Ijk_Call;
  155 + jcc->jmpkind = jk_Call;
156 156 jcc->call_counter = 0;
157 157 jcc->cost = 0;
158 158
51 callgrind/main.c
@@ -1146,8 +1146,20 @@ IRSB* CLG_(instrument)( VgCallbackClosure* closure,
1146 1146
1147 1147 CLG_ASSERT(clgs.ii_index>0);
1148 1148 if (!clgs.seen_before) {
1149   - clgs.bb->jmp[cJumps].instr = clgs.ii_index-1;
1150   - clgs.bb->jmp[cJumps].skip = False;
  1149 + ClgJumpKind jk;
  1150 +
  1151 + if (st->Ist.Exit.jk == Ijk_Call) jk = jk_Call;
  1152 + else if (st->Ist.Exit.jk == Ijk_Ret) jk = jk_Return;
  1153 + else {
  1154 + if (IRConst2Addr(st->Ist.Exit.dst) ==
  1155 + origAddr + curr_inode->instr_offset + curr_inode->instr_size)
  1156 + jk = jk_None;
  1157 + else
  1158 + jk = jk_Jump;
  1159 + }
  1160 +
  1161 + clgs.bb->jmp[cJumps].instr = clgs.ii_index-1;
  1162 + clgs.bb->jmp[cJumps].jmpkind = jk;
1151 1163 }
1152 1164
1153 1165 /* Update global variable jmps_passed before the jump
@@ -1212,18 +1224,45 @@ IRSB* CLG_(instrument)( VgCallbackClosure* closure,
1212 1224 CLG_ASSERT(clgs.bb->cjmp_count == cJumps);
1213 1225 CLG_ASSERT(clgs.bb->instr_count = clgs.ii_index);
1214 1226
1215   - /* This stores the instr of the call/ret at BB end */
1216   - clgs.bb->jmp[cJumps].instr = clgs.ii_index-1;
  1227 + /* Info for final exit from BB */
  1228 + {
  1229 + ClgJumpKind jk;
  1230 +
  1231 + if (sbIn->jumpkind == Ijk_Call) jk = jk_Call;
  1232 + else if (sbIn->jumpkind == Ijk_Ret) jk = jk_Return;
  1233 + else {
  1234 + jk = jk_Jump;
  1235 + if ((sbIn->next->tag == Iex_Const) &&
  1236 + (IRConst2Addr(sbIn->next->Iex.Const.con) ==
  1237 + origAddr + clgs.instr_offset))
  1238 + jk = jk_None;
  1239 + }
  1240 + clgs.bb->jmp[cJumps].jmpkind = jk;
  1241 + /* Instruction index of the call/ret at BB end
  1242 + * (it is wrong for fall-through, but does not matter) */
  1243 + clgs.bb->jmp[cJumps].instr = clgs.ii_index-1;
  1244 + }
  1245 +
  1246 + /* swap information of last exit with final exit if inverted */
  1247 + if (clgs.bb->cjmp_inverted) {
  1248 + ClgJumpKind jk;
  1249 + UInt instr;
  1250 +
  1251 + jk = clgs.bb->jmp[cJumps].jmpkind;
  1252 + clgs.bb->jmp[cJumps].jmpkind = clgs.bb->jmp[cJumps-1].jmpkind;
  1253 + clgs.bb->jmp[cJumps-1].jmpkind = jk;
  1254 + instr = clgs.bb->jmp[cJumps].instr;
  1255 + clgs.bb->jmp[cJumps].instr = clgs.bb->jmp[cJumps-1].instr;
  1256 + clgs.bb->jmp[cJumps-1].instr = instr;
  1257 + }
1217 1258
1218 1259 if (clgs.seen_before) {
1219 1260 CLG_ASSERT(clgs.bb->cost_count == update_cost_offsets(&clgs));
1220 1261 CLG_ASSERT(clgs.bb->instr_len = clgs.instr_offset);
1221   - CLG_ASSERT(clgs.bb->jmpkind == sbIn->jumpkind);
1222 1262 }
1223 1263 else {
1224 1264 clgs.bb->cost_count = update_cost_offsets(&clgs);
1225 1265 clgs.bb->instr_len = clgs.instr_offset;
1226   - clgs.bb->jmpkind = sbIn->jumpkind;
1227 1266 }
1228 1267
1229 1268 CLG_DEBUG(3, "- instrument(BB %#lx): byteLen %u, CJumps %u, CostLen %u\n",

0 comments on commit 28a23c0

Please sign in to comment.
Something went wrong with that request. Please try again.