a simple example for a calltracer tool with valgrind

  1. Prepare the valgrind source directory as described here: http://www.valgrind.org/docs/manual/writing-tools.html#writing-tools.gettingstarted
  2. 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!
  3. 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)
    
                
  4. static void ??_post_clo_init(void)
    {
    }
    

    As I don’t need to initialize anything I kept that function empty.

  5. static void ??_fini(Int exitcode)
    {
            VG_(printf)("bye\n");
    }
    

    No cleanup is required.

  6. 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
  7. 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.

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

Leave a comment