Tools

Example use:zerospy

Here we take zerospy as an example to show the use of VClinic internal tools.

1. Create source file

Create a simple C program and save it as a file named sample.cc.

2. Compile your application

To generate an executable binary, compile your application with GCC Compiler.

$ gcc -g sample.cc -o sample

Tip

If the executable is built with -g opinion, the client will show more information, such as source code line number and source file path.

3. Run application using client

Run the generated binary sample using zerospy client.

$ build/bin64/drrun -t zerospy -- ./sample

The client outputs:

[ZEROSPY INFO] Sampling Disabled
  
--------------- Dumping INTEGER Redundancy Info ----------------

*************** Dump Data from Thread 0 ****************

 Total redundant bytes = 46.832358 %

 INFO : Total redundant bytes = 46.832358 % (2693549 / 5751470)


======= (14.850296) % of total Redundant, with local redundant 100.000000 % (400000 Bytes / 400000 Bytes) ======


======= with All Zero Redundant 100.000000 % (100000 / 100000) ======

======= Redundant byte map : [0]  [AccessLen=4] =======

---------------------Redundant load with---------------------------
#0   0x00007fa056474170 "mov    eax, dword ptr [rdx+rax]" in foo at [/home/lkl/samples/sample.cc:7]
#1   0x00007fa0564741a3 "call   0x00007fa056474129" in main at [/home/lkl/samples/sample.cc:11]
#2   0x00007fa0572f50b1 "call   rax" in __libc_start_main at [/home/lkl/samples/sample.cc:0]
#3   0x00007fa056474068 "call   <rel> qword ptr [0x00007fa056476fe0]" in _start at [:0]
#4   0xffffffffffffffff "<NULL>" in THREAD[0]_ROOT_CTXT at [<NULL>:0]
#5   0x0000000000000000 "<NULL>" in PROCESS[2057914]_ROOT_CTXT at [<NULL>:0]
======= (14.850296) % of total Redundant, with local redundant 100.000000 % (400000 Bytes / 400000 Bytes) ======


======= with All Zero Redundant 100.000000 % (100000 / 100000) ======

======= Redundant byte map : [0]  [AccessLen=4] =======

---------------------Redundant load with---------------------------
#0   0x00007fa0564741cc "mov    edx, dword ptr [rdx+rax]" in main at [/home/lkl/samples/sample.cc:13]
#1   0x00007fa0572f50b1 "call   rax" in __libc_start_main at [/home/lkl/samples/sample.cc:0]
#2   0x00007fa056474068 "call   <rel> qword ptr [0x00007fa056476fe0]" in _start at [:0]
#3   0xffffffffffffffff "<NULL>" in THREAD[0]_ROOT_CTXT at [<NULL>:0]
#4   0x0000000000000000 "<NULL>" in PROCESS[2057914]_ROOT_CTXT at [<NULL>:0]


======= (6.155188) % of total Redundant, with local redundant 41.448250 % (165793 Bytes / 400000 Bytes) ======


======= with All Zero Redundant 0.001000 % (1 / 100000) ======

======= Redundant byte map : [0]  [AccessLen=4] =======

---------------------Redundant load with---------------------------
#0   0x00007fa05647418e "add    dword ptr [rbp-0x04], 0x01" in foo at [/home/lkl/samples/sample.cc:6]
#1   0x00007fa0564741a3 "call   0x00007fa056474129" in main at [/home/lkl/samples/sample.cc:11]
#2   0x00007fa0572f50b1 "call   rax" in __libc_start_main at [/home/lkl/samples/sample.cc:0]
#3   0x00007fa056474068 "call   <rel> qword ptr [0x00007fa056476fe0]" in _start at [:0]
#4   0xffffffffffffffff "<NULL>" in THREAD[0]_ROOT_CTXT at [<NULL>:0]
#5   0x0000000000000000 "<NULL>" in PROCESS[2057914]_ROOT_CTXT at [<NULL>:0]

...

Tip

Intepreting the text output: The client zerospy identifys redundant zeros caused by useless computation and outputs its frequency as the metric, pc, assemble code, and full call path which reveals the locations where the redundant zeros happen in source lines and calling contexts. The full call path starts on the bottom with two dummy PROCESS_ROOT and THREAD_ROOT and ends on the top with the fuction that encloses the pc.

Build Your Own Tool

We demonstrate how to create and build a VClinic client by taking a deep dive into vprofile_all_opnd.

Basic structure of a client

This section describes the basic structure of a VClinic client.

The following code is the specific implementation of vprofile_all_opnd:

#include "dr_api.h"
#include "vprofile.h"

vtrace_t* vtrace;

template<int size, int esize, bool is_float>
void update(val_info_t *info) {
	return;
}

static void
ClientInit(int argc, const char *argv[])
{
}

static void
ClientExit(void)
{
    vprofile_unregister_trace(vtrace);
    vprofile_exit();
}

#ifdef __cplusplus
extern "C" {
#endif

DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
    dr_set_client_name("DynamoRIO Client 'vprofile_all_opnd'",
                       "http://dynamorio.org/issues");
    ClientInit(argc, argv);
    vprofile_init(VPROFILE_FILTER_ALL_INSTR, NULL, NULL, NULL,
                     VPROFILE_DEFAULT);
    vtrace = vprofile_allocate_trace(VPROFILE_TRACE_DEFAULT | VPROFILE_TRACE_BEFORE_WRITE);
    vprofile_register_trace_template_cb(vtrace, VPROFILE_FILTER_ALL_OPND, VPROFILE_OPND_MASK_ALL, update);
    dr_register_exit_event(ClientExit);
}

#ifdef __cplusplus
}
#endif

The diagram below shows the key functions in vprofile_all_opnd.cpp and how they relate to each other.

_images/simple_call_graph.png

The easiest way to understand the client is to think of it as event driven. Each function is called upon the occurence of an event during the application execution:

    1. DynamoRIO loads and runs the client, calling dr_client_main() before the application execution.

    1. In dr_client_main(), the client calls vprofile_init(), which initializes VClinic.

    1. In dr_client_main(), the client calls vprofile_allocate_trace() and vprofile_register_trace_template_cb(), which registers trace that can collect and provide any information you want in trace buffer with custom filter(VPROFILE_FILTER_ALL_OPND) and callback function(update).

    1. opnd_filter will Estimate the filled in slots for detecting potential trace buffer overflow.

    1. update callback function will provide the collected information to the user for customized analysis when the trace buffer is detected as full. In fact, vprofile_all_opnd does not use the collected information but returns directly in the callback function.

    1. The application stops running and DynamoRIO calls ClientExit(), where one can output the analysis results..

Add a new client to VClinic

If you want to add a new client, you need to create a folder with the client name in VClinic/src/clients, and put the client_name.cpp and CMakeLists.txt in it. Then, when you finish the programming, you can run “sh build.sh” in the VClinic root directory to build it. One can refer to any of the example clients to see how to edit the CMakeLists.txt.