| |
| /*--------------------------------------------------------------------*/ |
| /*--- Simple tool for counting UInstrs, using a C helper. ---*/ |
| /*--- lk_main.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |
| /* |
| This file is part of Lackey, an example Valgrind tool that does |
| some simple program measurement. |
| |
| Copyright (C) 2002-2004 Nicholas Nethercote |
| njn25@cam.ac.uk |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| #include "tool.h" |
| |
| /* Nb: use ULongs because the numbers can get very big */ |
| static ULong n_dlrr_calls = 0; |
| static ULong n_BBs = 0; |
| static ULong n_UInstrs = 0; |
| static ULong n_machine_instrs = 0; |
| static ULong n_Jccs = 0; |
| static ULong n_Jccs_untaken = 0; |
| |
| static void add_one_dlrr_call(void) |
| { |
| n_dlrr_calls++; |
| } |
| |
| /* See comment above SK_(instrument) for reason why n_machine_instrs is |
| incremented here. */ |
| static void add_one_BB(void) |
| { |
| n_BBs++; |
| n_machine_instrs++; |
| } |
| |
| static void add_one_UInstr(void) |
| { |
| n_UInstrs++; |
| } |
| |
| static void add_one_machine_instr(void) |
| { |
| n_machine_instrs++; |
| } |
| |
| static void add_one_Jcc(void) |
| { |
| n_Jccs++; |
| } |
| |
| static void add_one_Jcc_untaken(void) |
| { |
| n_Jccs_untaken++; |
| } |
| |
| void SK_(pre_clo_init)(void) |
| { |
| VG_(details_name) ("Lackey"); |
| VG_(details_version) (NULL); |
| VG_(details_description) ("an example Valgrind tool"); |
| VG_(details_copyright_author)( |
| "Copyright (C) 2002-2004, and GNU GPL'd, by Nicholas Nethercote."); |
| VG_(details_bug_reports_to) (VG_BUGS_TO); |
| VG_(details_avg_translation_sizeB) ( 175 ); |
| |
| VG_(register_compact_helper)((Addr) & add_one_dlrr_call); |
| VG_(register_compact_helper)((Addr) & add_one_BB); |
| VG_(register_compact_helper)((Addr) & add_one_machine_instr); |
| VG_(register_compact_helper)((Addr) & add_one_UInstr); |
| VG_(register_compact_helper)((Addr) & add_one_Jcc); |
| VG_(register_compact_helper)((Addr) & add_one_Jcc_untaken); |
| } |
| |
| void SK_(post_clo_init)(void) |
| { |
| } |
| |
| /* Note: machine instructions are marked by an INCEIP at the end of each one, |
| except for the final one in the basic block which ends in an |
| unconditional JMP. Sometimes the final unconditional JMP is preceded by |
| a conditional JMP (Jcc), and thus it isn't reached. Eg: |
| |
| <code a> |
| INCEIP ... |
| |
| <code b> |
| Jcc ... |
| JMP ... (will not be reached if Jcc succeeds) |
| |
| If we simplemindedly added calls to add_one_machine_instr() before INCEIPs |
| and unconditional JMPs, we'd sometimes miss the final call (when a |
| preceding conditional JMP succeeds), underestimating the machine instruction |
| count. |
| |
| <code a> |
| call add_one_machine_instr() |
| INCEIP ... |
| |
| <code b> |
| Jcc ... |
| call add_one_machine_instr() |
| JMP ... |
| |
| Instead we add a call before each INCEIP, and also one at the start of the |
| block, but not one at the end, viz: |
| |
| call add_one_machine_instr() |
| |
| <code a> |
| call add_one_machine_instr() |
| INCEIP ... |
| |
| <code b> |
| Jcc ... |
| JMP ... |
| |
| Which gives us the right answer. And just to avoid two C calls, we fold |
| the basic-block-beginning call in with add_one_BB(). Phew. |
| */ |
| UCodeBlock* SK_(instrument)(UCodeBlock* cb_in, Addr orig_addr) |
| { |
| UCodeBlock* cb; |
| Int i; |
| UInstr* u; |
| Char fnname[100]; |
| |
| cb = VG_(setup_UCodeBlock)(cb_in); |
| |
| /* Count call to dlrr(), if this BB is dlrr()'s entry point */ |
| if (VG_(get_fnname_if_entry)(orig_addr, fnname, 100) && |
| 0 == VG_(strcmp)(fnname, "_dl_runtime_resolve")) |
| { |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_dlrr_call); |
| } |
| |
| /* Count basic block */ |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_BB); |
| |
| for (i = 0; i < VG_(get_num_instrs)(cb_in); i++) { |
| u = VG_(get_instr)(cb_in, i); |
| |
| switch (u->opcode) { |
| case NOP: case LOCK: case CALLM_S: case CALLM_E: |
| break; |
| |
| case INCEIP: |
| /* Count machine instr */ |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_machine_instr); |
| VG_(copy_UInstr)(cb, u); |
| break; |
| |
| case JMP: |
| if (u->cond != CondAlways) { |
| /* Count Jcc */ |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_Jcc); |
| VG_(copy_UInstr)(cb, u); |
| /* Count non-taken Jcc */ |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_Jcc_untaken); |
| } else { |
| VG_(copy_UInstr)(cb, u); |
| } |
| break; |
| |
| default: |
| /* Count UInstr */ |
| VG_(call_helper_0_0)(cb, (Addr) & add_one_UInstr); |
| VG_(copy_UInstr)(cb, u); |
| break; |
| } |
| } |
| |
| VG_(free_UCodeBlock)(cb_in); |
| return cb; |
| } |
| |
| void SK_(fini)(Int exitcode) |
| { |
| VG_(message)(Vg_UserMsg, |
| "Counted %d calls to _dl_runtime_resolve()", n_dlrr_calls); |
| |
| VG_(message)(Vg_UserMsg, ""); |
| VG_(message)(Vg_UserMsg, "Executed:"); |
| VG_(message)(Vg_UserMsg, " BBs: %llu", n_BBs); |
| VG_(message)(Vg_UserMsg, " machine instrs: %llu", n_machine_instrs); |
| VG_(message)(Vg_UserMsg, " UInstrs: %llu", n_UInstrs); |
| |
| VG_(message)(Vg_UserMsg, ""); |
| VG_(message)(Vg_UserMsg, "Jccs:"); |
| VG_(message)(Vg_UserMsg, " total: %llu", n_Jccs); |
| VG_(message)(Vg_UserMsg, " %% taken: %llu%%", |
| (n_Jccs - n_Jccs_untaken)*100 / n_Jccs); |
| |
| VG_(message)(Vg_UserMsg, ""); |
| VG_(message)(Vg_UserMsg, "Ratios:"); |
| VG_(message)(Vg_UserMsg, " machine instrs : BB = %3llu : 10", |
| 10 * n_machine_instrs / n_BBs); |
| VG_(message)(Vg_UserMsg, " UInstrs : BB = %3llu : 10", |
| 10 * n_UInstrs / n_BBs); |
| VG_(message)(Vg_UserMsg, " UInstrs : machine_instr = %3llu : 10", |
| 10 * n_UInstrs / n_machine_instrs); |
| |
| VG_(message)(Vg_UserMsg, ""); |
| VG_(message)(Vg_UserMsg, "Exit code: %d", exitcode); |
| } |
| |
| VG_DETERMINE_INTERFACE_VERSION(SK_(pre_clo_init), 0) |
| |
| |
| /*--------------------------------------------------------------------*/ |
| /*--- end lk_main.c ---*/ |
| /*--------------------------------------------------------------------*/ |
| |