blob: b198e1b45dc6567dd3e1a2ae8ba271166de167d3 [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2014-2018 Google, Inc. All rights reserved.
* Copyright (c) 2003-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Code Manipulation API Sample:
* countcalls.c
*
* Reports the dynamic execution count for direct calls, indirect
* calls, and returns in the target application. Illustrates how to
* perform performant inline increments and use per-thread data
* structures.
*/
#include <stddef.h> /* for offsetof */
#include "dr_api.h"
#include "drmgr.h"
#include "drreg.h"
#ifdef WINDOWS
# define DISPLAY_STRING(msg) dr_messagebox(msg)
#else
# define DISPLAY_STRING(msg) dr_printf("%s\n", msg);
#endif
#define NULL_TERMINATE(buf) (buf)[(sizeof((buf)) / sizeof((buf)[0])) - 1] = '\0'
/* keep separate counters for each thread, in this thread-local data
* structure:
*/
typedef struct {
int num_direct_calls;
int num_indirect_calls;
int num_returns;
} per_thread_t;
static int tls_idx;
/* keep a global count as well */
static per_thread_t global_count = { 0 };
static void
event_exit(void);
static void
event_thread_init(void *drcontext);
static void
event_thread_exit(void *drcontext);
static dr_emit_flags_t
event_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data);
DR_EXPORT void
dr_client_main(client_id_t id, int argc, const char *argv[])
{
drreg_options_t ops = { sizeof(ops), 2 /*max slots needed*/, false };
dr_set_client_name("DynamoRIO Sample Client 'countcalls'",
"http://dynamorio.org/issues");
if (!drmgr_init() || drreg_init(&ops) != DRREG_SUCCESS)
DR_ASSERT(false);
/* register events */
dr_register_exit_event(event_exit);
drmgr_register_thread_init_event(event_thread_init);
drmgr_register_thread_exit_event(event_thread_exit);
drmgr_register_bb_instrumentation_event(NULL, event_instruction, NULL);
tls_idx = drmgr_register_tls_field();
/* make it easy to tell, by looking at log file, which client executed */
dr_log(NULL, DR_LOG_ALL, 1, "Client 'countcalls' initializing\n");
#ifdef SHOW_RESULTS
/* also give notification to stderr */
if (dr_is_notify_on()) {
# ifdef WINDOWS
/* ask for best-effort printing to cmd window. must be called at init. */
dr_enable_console_printing();
# endif
dr_fprintf(STDERR, "Client countcalls is running\n");
}
#endif
}
static void
display_results(per_thread_t *data, const char *thread_note)
{
#ifdef SHOW_RESULTS
char msg[512];
int len;
len = dr_snprintf(msg, sizeof(msg) / sizeof(msg[0]),
"%sInstrumentation results:\n"
" saw %d direct calls\n"
" saw %d indirect calls\n"
" saw %d returns\n",
thread_note, data->num_direct_calls, data->num_indirect_calls,
data->num_returns);
DR_ASSERT(len > 0);
NULL_TERMINATE(msg);
DISPLAY_STRING(msg);
#endif /* SHOW_RESULTS */
}
static void
event_exit(void)
{
display_results(&global_count, "");
if (!drmgr_unregister_bb_insertion_event(event_instruction) ||
!drmgr_unregister_thread_init_event(event_thread_init) ||
!drmgr_unregister_thread_exit_event(event_thread_exit) ||
drreg_exit() != DRREG_SUCCESS)
DR_ASSERT(false);
drmgr_exit();
}
static void
event_thread_init(void *drcontext)
{
/* create an instance of our data structure for this thread */
per_thread_t *data = (per_thread_t *)dr_thread_alloc(drcontext, sizeof(per_thread_t));
/* store it in the slot provided in the drcontext */
drmgr_set_tls_field(drcontext, tls_idx, data);
data->num_direct_calls = 0;
data->num_indirect_calls = 0;
data->num_returns = 0;
dr_log(drcontext, DR_LOG_ALL, 1, "countcalls: set up for thread " TIDFMT "\n",
dr_get_thread_id(drcontext));
}
static void
event_thread_exit(void *drcontext)
{
per_thread_t *data = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx);
char msg[512];
int len;
len = dr_snprintf(msg, sizeof(msg) / sizeof(msg[0]), "Thread %d exited - ",
dr_get_thread_id(drcontext));
DR_ASSERT(len > 0);
NULL_TERMINATE(msg);
/* display thread private counts data */
display_results(data, msg);
/* clean up memory */
dr_thread_free(drcontext, data, sizeof(per_thread_t));
}
static void
insert_counter_update(void *drcontext, instrlist_t *bb, instr_t *where, int offset)
{
/* Since the inc instruction clobbers 5 of the arithmetic eflags,
* we have to save them around the inc. We could be more efficient
* by not bothering to save the overflow flag and constructing our
* own sequence of instructions to save the other 5 flags (using
* lahf).
*/
if (drreg_reserve_aflags(drcontext, bb, where) != DRREG_SUCCESS) {
DR_ASSERT(false); /* cannot recover */
return;
}
/* Increment the global counter using the lock prefix to make it atomic
* across threads. It would be cheaper to aggregate the thread counters
* in the exit events, but this sample is intended to illustrate inserted
* instrumentation.
*/
instrlist_meta_preinsert(
bb, where,
LOCK(INSTR_CREATE_inc(
drcontext, OPND_CREATE_ABSMEM(((byte *)&global_count) + offset, OPSZ_4))));
/* Increment the thread private counter. */
if (dr_using_all_private_caches()) {
per_thread_t *data = (per_thread_t *)drmgr_get_tls_field(drcontext, tls_idx);
/* private caches - we can use an absolute address */
instrlist_meta_preinsert(
bb, where,
INSTR_CREATE_inc(drcontext,
OPND_CREATE_ABSMEM(((byte *)&data) + offset, OPSZ_4)));
} else {
/* shared caches - we must indirect via thread local storage */
reg_id_t scratch;
if (drreg_reserve_register(drcontext, bb, where, NULL, &scratch) != DRREG_SUCCESS)
DR_ASSERT(false);
drmgr_insert_read_tls_field(drcontext, tls_idx, bb, where, scratch);
instrlist_meta_preinsert(
bb, where, INSTR_CREATE_inc(drcontext, OPND_CREATE_MEM32(scratch, offset)));
if (drreg_unreserve_register(drcontext, bb, where, scratch) != DRREG_SUCCESS)
DR_ASSERT(false);
}
if (drreg_unreserve_aflags(drcontext, bb, where) != DRREG_SUCCESS)
DR_ASSERT(false); /* cannot recover */
}
static dr_emit_flags_t
event_instruction(void *drcontext, void *tag, instrlist_t *bb, instr_t *instr,
bool for_trace, bool translating, void *user_data)
{
/* ignore tool-inserted instrumentation */
if (!instr_is_app(instr))
return DR_EMIT_DEFAULT;
/* instrument calls and returns -- ignore far calls/rets */
if (instr_is_call_direct(instr)) {
insert_counter_update(drcontext, bb, instr,
offsetof(per_thread_t, num_direct_calls));
} else if (instr_is_call_indirect(instr)) {
insert_counter_update(drcontext, bb, instr,
offsetof(per_thread_t, num_indirect_calls));
} else if (instr_is_return(instr)) {
insert_counter_update(drcontext, bb, instr, offsetof(per_thread_t, num_returns));
}
return DR_EMIT_DEFAULT;
}