- Prepare the valgrind source directory as described here: http://www.valgrind.org/docs/manual/writing-tools.html#writing-tools.gettingstarted
- Open your “??_main.c” file, where “??” is your prefix you’ve chosen. Be aware that you can’t just use libc functions, you have to use the functions provided by valgrind!
- Create the “pre_clo_init” function:
static void ??_pre_clo_init(void) { VG_(details_name) ("Calltrace"); VG_(details_version) (NULL); VG_(details_description) ("The best calltracer"); VG_(details_copyright_author)( "Copyright (C) 2002-2012, and GNU GPL'd, by btwotch!"); VG_(details_bug_reports_to) (VG_BUGS_TO); VG_(details_avg_translation_sizeB) ( 275 ); VG_(basic_tool_funcs) (??_post_clo_init, ??_instrument, ??_fini); /* No needs, no core events to track */ } VG_DETERMINE_INTERFACE_VERSION(??_pre_clo_init)
- Set the correct values for “details_name”, “details_version”, “details_description”, “details_copyright_author” and “details_bug_reports_to”.
- With “details_avg_translation_sizeB” you can set the “Average size of a translation, in bytes, so that the translation storage machinery can allocate memory appropriately.” (via http://codesearch.debian.net/show?file=valgrind_3.8.1-1%2Finclude%2Fpub_tool_tooliface.h&line=253&numfiles=28#L250). Setting this is optional.
- “basic_tool_funcs” is the most important function. Here you define the function pointers:
- Argument: Here you define what function has to be called when you do initialisation, especially important for using command line parameters
- Argument: That’s a pointer to your instrumentation function; that’s where all your magic(tm) will happen
- Argument: A function pointer to a cleanup function
More information: http://codesearch.debian.net/show?file=valgrind_3.8.1-1%2Finclude%2Fpub_tool_tooliface.h&line=73&numfiles=541#L73
- “VG_DETERMINE_INTERFACE_VERSION” specifies where to start
-
static void ??_post_clo_init(void) { }
As I don’t need to initialize anything I kept that function empty.
-
static void ??_fini(Int exitcode) { VG_(printf)("bye\n"); }
No cleanup is required.
- Now the most interesting function:
static IRSB* ??_instrument ( VgCallbackClosure* closure, IRSB* bb, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { int i, j; UInt nips; IRStmt *st; Addr ips[VG_(clo_backtrace_size)]; Addr sps[VG_(clo_backtrace_size)]; Addr fps[VG_(clo_backtrace_size)]; Addr x; for (i = 0; i < bb->stmts_used; i++) { st = bb->stmts[i]; if (st->tag == Ist_IMark) { nips = VG_(get_StackTrace)(VG_(get_running_tid)(), ips, VG_(clo_backtrace_size), sps, fps, 0); for (j = 0; j < nips; j++) { if (j > 0) VG_(printf)("\t>"); print_fn(ips[j]); } VG_(printf)(">> \n"); } } return bb; }
- “IRSB* bb” is the basic block; it contains “stmts_used” statements.
- “Ist_IMark” is a marker for information about the basic block; it contains the instruction address
- With VG_(get_StackTrace) we get a stacktrace with the following information:
- “ips”: address of the instruction
- “sps”: address of the stack pointer
- “fps”: address of the frame pointer
- Then we iterate over the addresses of the stacktrace
-
static void print_fn(Addr a) { Bool named = False; UInt linenum; ThreadId tid; Bool dirname_available; char filename[1024], dirname[1024], fnname[1024]; named = VG_(get_fnname)(a, fnname, 1024); tid = VG_(get_running_tid)(); VG_(printf)("tid: %u|addr: %p|fnname: %s", tid, (void*)a, (named == True) ? fnname : ""); if (VG_(get_filename_linenum)(a, filename, 1024, dirname, 1024, &dirname_available, &linenum) == True) VG_(printf)("|file: %s|dir: %s|line: %u", filename, dirname, linenum); VG_(printf)("\n"); }
- This function looks up the name of the function via “VG_(get_fnname)” and tries to get additional information about
the filename, linenumber and directory where that function is defined with “VG_(get_filename_linenum)”. The it prints that
information.
- This function looks up the name of the function via “VG_(get_fnname)” and tries to get additional information about
Now a complete example:
/* Copyright (C) 2002-2012 btwotch btwotch+vallgrindcalltrace@gmail.org 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 "pub_tool_basics.h" #include "pub_tool_tooliface.h" #include "pub_tool_debuginfo.h" #include "pub_tool_libcprint.h" #include "pub_tool_threadstate.h" #include "pub_tool_options.h" #include "pub_tool_stacktrace.h" #include "pub_tool_vki.h" #include "pub_tool_libcfile.h" static int fd; static void ct_post_clo_init(void) { } static UInt poor_fprintf(HChar* format, ...) { UInt ret; va_list vargs; char buf[8192]; if(fd < 0) VG_(printf)("fail, could not open tracefile\n"); va_start(vargs, format); ret = VG_(vsnprintf)(buf, 8192, format, vargs); //VG_(printf)("%s", buf); VG_(write)(fd, buf, ret); va_end(vargs); return ret; } // TODO: Future idea: convert address in tracepath static void print_fn(Addr a) { Bool named = False; UInt linenum; ThreadId tid; Bool dirname_available; char filename[1024], dirname[1024], fnname[1024]; named = VG_(get_fnname)(a, fnname, 1024); tid = VG_(get_running_tid)(); poor_fprintf("tid: %u|addr: %p|fnname: %s", tid, (void*)a, (named == True) ? fnname : ""); if (VG_(get_filename_linenum)(a, filename, 1024, dirname, 1024, &dirname_available, &linenum) == True) poor_fprintf("|file: %s|dir: %s|line: %u", filename, dirname, linenum); poor_fprintf("\n"); } static IRSB* ct_instrument ( VgCallbackClosure* closure, IRSB* bb, VexGuestLayout* layout, VexGuestExtents* vge, IRType gWordTy, IRType hWordTy ) { int i, j; UInt nips; IRStmt *st; Addr ips[VG_(clo_backtrace_size)]; Addr sps[VG_(clo_backtrace_size)]; Addr fps[VG_(clo_backtrace_size)]; Addr x; for (i = 0; i < bb->stmts_used; i++) { st = bb->stmts[i]; if (st->tag == Ist_IMark) { nips = VG_(get_StackTrace)(VG_(get_running_tid)(), ips, VG_(clo_backtrace_size), sps, fps, 0); for (j = 0; j < nips; j++) { if (j > 0) poor_fprintf("\t>"); print_fn(ips[j]); } poor_fprintf(">> \n"); poor_fprintf("\tsps: %p fps: %p\n", (void*)sps[0], (void*)fps[0]); for (x = sps[0]; x < fps[0] && x <= sps[0]+0x32; x+=4) poor_fprintf("\t%p: %x\n", (void*)x, *(int*)x); poor_fprintf("\n<<\n"); } } return bb; } static void ct_fini(Int exitcode) { VG_(close)(fd); VG_(printf)("log written to trace.log\n"); } static void ct_pre_clo_init(void) { VG_(details_name) ("Calltrace"); VG_(details_version) (NULL); VG_(details_description) ("The best calltracer"); VG_(details_copyright_author)( "Copyright (C) 2002-2012, and GNU GPL'd, by btwotch!"); VG_(details_bug_reports_to) (VG_BUGS_TO); VG_(details_avg_translation_sizeB) ( 275 ); fd = VG_(fd_open)("trace.log", VKI_O_WRONLY|VKI_O_CREAT, VKI_S_IRUSR|VKI_S_IWUSR); VG_(basic_tool_funcs) (ct_post_clo_init, ct_instrument, ct_fini); /* No needs, no core events to track */ } VG_DETERMINE_INTERFACE_VERSION(ct_pre_clo_init)
This code writes the information (including a stackdump) into a file called “trace.log”. It uses
- “VG_(fd_open)(“trace.log”, VKI_O_WRONLY|VKI_O_CREAT, VKI_S_IRUSR|VKI_S_IWUSR)” to open the file
- “VG_(close)(fd)”: to close the file
- “VG_(write)”: to write into that file and “poor_fprintf” to write formatted into the file