Skip to content

Commit

Permalink
i#140 pcprof: add client pc sampling support (#2883)
Browse files Browse the repository at this point in the history
Added dr_where_am_i() to better support client self-profiling via
sampling.  This also provides the fragment tag, by refactoring
the pcprofile code into a new helper fcache_refine_whereami().

Renamed the whereami types to better avoid name conflicts:
s/WHERE_xxx/DR_WHERE_xxx/ and s/where_am_i_t/dr_where_am_i_t/.
Exported the dr_where_am_i_t enum.

Fixed a bug where an uninitialized mcontext was passed to a
client timer callback: we only filled in the mcontext for a
DR-internal callback.

Added a test of client sampling to client.timer

Issue: #140
  • Loading branch information
derekbruening committed Mar 15, 2018
1 parent 3f2dd1d commit ce3a9f4
Show file tree
Hide file tree
Showing 26 changed files with 312 additions and 211 deletions.
3 changes: 2 additions & 1 deletion api/docs/release.dox
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ Dr. Memory Framework (DRMF) in the same package as DynamoRIO. DRMF
provides the umbra, drsyscall, and drsymcache Extensions for use by
clients.

The changes between version \DR_VERSION and 6.2.0 include the following minor
The changes between version \DR_VERSION and 7.0.0 include the following minor
compatibility changes:

- The drltrace tool has been moved to the Dr.Memory Framework.
Expand Down Expand Up @@ -223,6 +223,7 @@ Further non-compatibility-affecting changes include:
drmgr_unregister_pre_syscall_event_user_data() to enable passing of user data.
- Added drmgr_register_post_syscall_event_user_data() and
drmgr_unregister_post_syscall_event_user_data() to enable passing of user data.
- Added dr_where_am_i() to better support client self-profiling via sampling.

**************************************************
<hr>
Expand Down
8 changes: 4 additions & 4 deletions core/arch/emit_utils_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -5638,11 +5638,11 @@ insert_entering_native(dcontext_t *dcontext, instrlist_t *ilist, instr_t *where,
*/

/* C equivalent:
* whereami = WHERE_APP
* whereami = DR_WHERE_APP
*/
PRE(ilist, where,
instr_create_save_immed_to_dc_via_reg(dcontext, reg_dc, WHEREAMI_OFFSET,
(ptr_int_t) WHERE_APP, OPSZ_4));
(ptr_int_t) DR_WHERE_APP, OPSZ_4));

/* skip C equivalent:
* STATS_INC(num_native_module_enter)
Expand Down Expand Up @@ -5703,11 +5703,11 @@ insert_entering_non_native(dcontext_t *dcontext, instrlist_t *ilist, instr_t *wh
ilist, where, reg_dc);

/* C equivalent:
* whereami = WHERE_FCACHE
* whereami = DR_WHERE_FCACHE
*/
PRE(ilist, where,
instr_create_save_immed_to_dc_via_reg(dcontext, reg_dc, WHEREAMI_OFFSET,
(ptr_int_t) WHERE_FCACHE, OPSZ_4));
(ptr_int_t) DR_WHERE_FCACHE, OPSZ_4));
}

/* Emit code to transfer execution from native module to code cache of non-native
Expand Down
4 changes: 2 additions & 2 deletions core/arch/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -5150,10 +5150,10 @@ build_basic_block_fragment(dcontext_t *dcontext, app_pc start, uint initial_flag
{
fragment_t *f;
build_bb_t bb;
where_am_i_t wherewasi = dcontext->whereami;
dr_where_am_i_t wherewasi = dcontext->whereami;
bool image_entry;
KSTART(bb_building);
dcontext->whereami = WHERE_INTERP;
dcontext->whereami = DR_WHERE_INTERP;

/* Neither thin_client nor hotp_only should be building any bbs. */
ASSERT(!RUNNING_WITHOUT_CODE_CACHE());
Expand Down
16 changes: 8 additions & 8 deletions core/arch/mangle_shared.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* ******************************************************************************
* Copyright (c) 2010-2017 Google, Inc. All rights reserved.
* Copyright (c) 2010-2018 Google, Inc. All rights reserved.
* Copyright (c) 2010 Massachusetts Institute of Technology All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* ******************************************************************************/
Expand Down Expand Up @@ -437,7 +437,7 @@ insert_meta_call_vargs(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr,
XINST_CREATE_store(dcontext,
OPND_CREATE_MEMPTR(SCRATCH_REG0, 0),
opnd_create_reg(SCRATCH_REG1)));
instrlist_insert_mov_immed_ptrsz(dcontext, (ptr_int_t)WHERE_CLEAN_CALLEE,
instrlist_insert_mov_immed_ptrsz(dcontext, (ptr_int_t)DR_WHERE_CLEAN_CALLEE,
opnd_create_reg(SCRATCH_REG1),
ilist, instr, NULL, NULL);
PRE(ilist, instr,
Expand All @@ -449,14 +449,14 @@ insert_meta_call_vargs(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr,
OPND_CREATE_MEMPTR(SCRATCH_REG0, 0)));
# else
PRE(ilist, instr,
instr_create_save_immed_to_dc_via_reg(dcontext, SCRATCH_REG0,
WHEREAMI_OFFSET,
(uint) WHERE_CLEAN_CALLEE, OPSZ_4));
instr_create_save_immed_to_dc_via_reg
(dcontext, SCRATCH_REG0, WHEREAMI_OFFSET,
(uint) DR_WHERE_CLEAN_CALLEE, OPSZ_4));
# endif
} else {
PRE(ilist, instr, XINST_CREATE_store(dcontext,
opnd_create_dcontext_field(dcontext, WHEREAMI_OFFSET),
OPND_CREATE_INT32(WHERE_CLEAN_CALLEE)));
OPND_CREATE_INT32(DR_WHERE_CLEAN_CALLEE)));
}
}
#endif
Expand All @@ -480,9 +480,9 @@ insert_meta_call_vargs(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr,
uint whereami;

if (TEST(META_CALL_RETURNS_TO_NATIVE, flags))
whereami = (uint) WHERE_APP;
whereami = (uint) DR_WHERE_APP;
else
whereami = (uint) WHERE_FCACHE;
whereami = (uint) DR_WHERE_FCACHE;

if (SCRATCH_ALWAYS_TLS()) {
/* SCRATCH_REG0 is dead here: restore of the app stack will clobber xax */
Expand Down
2 changes: 1 addition & 1 deletion core/arch/sideline.c
Original file line number Diff line number Diff line change
Expand Up @@ -591,7 +591,7 @@ sideline_optimize(fragment_t *f,
pause_for_sideline = dcontext->owning_thread;
ASSERT(is_thread_known(pause_for_sideline));

if (dcontext->whereami != WHERE_FCACHE) {
if (dcontext->whereami != DR_WHERE_FCACHE) {
/* wait for thread to reach waiting point in dispatch */
LOG(logfile, LOG_SIDELINE, VERB_3,
"\nsideline_optimize: waiting for target thread "TIDFMT"\n",
Expand Down
2 changes: 1 addition & 1 deletion core/arch/x86_code.c
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ nt_continue_setup(priv_mcontext_t *mc)
dcontext->next_tag = next_pc;
ASSERT(dcontext->next_tag != NULL);
set_last_exit(dcontext, (linkstub_t *) get_asynch_linkstub());
dcontext->whereami = WHERE_TRAMPOLINE;
dcontext->whereami = DR_WHERE_TRAMPOLINE;

*get_mcontext(dcontext) = *mc;
/* clear pc */
Expand Down
38 changes: 19 additions & 19 deletions core/dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ dispatch(dcontext_t *dcontext)
# endif
ASSERT(dcontext == get_thread_private_dcontext() ||
/* i#813: the app hit our post-sysenter hook while native */
(dcontext->whereami == WHERE_APP &&
(dcontext->whereami == DR_WHERE_APP &&
dcontext->last_exit == get_syscall_linkstub()));
#else
# ifdef UNIX
Expand Down Expand Up @@ -517,7 +517,7 @@ dispatch_enter_fcache(dcontext_t *dcontext, fragment_t *targetf)
* to be safe for unlinking.
*/
KSTOP_NOT_MATCHING(fcache_default);
dcontext->whereami = WHERE_DISPATCH;
dcontext->whereami = DR_WHERE_DISPATCH;
enter_couldbelinking(dcontext, NULL, true);
dcontext->next_tag = dcontext->asynch_target;
LOG(THREAD, LOG_DISPATCH, 2,
Expand Down Expand Up @@ -569,7 +569,7 @@ enter_fcache(dcontext_t *dcontext, fcache_enter_func_t entry, cache_pc pc)
}
#endif

dcontext->whereami = WHERE_FCACHE;
dcontext->whereami = DR_WHERE_FCACHE;
(*entry)(dcontext);
IF_WINDOWS(ASSERT_NOT_REACHED()); /* returns for signals on unix */
}
Expand Down Expand Up @@ -711,7 +711,7 @@ dispatch_enter_native(dcontext_t *dcontext)
#endif
}
set_fcache_target(dcontext, dcontext->next_tag);
dcontext->whereami = WHERE_APP;
dcontext->whereami = DR_WHERE_APP;
#ifdef UNIX
do {
(*go_native)(dcontext);
Expand All @@ -731,14 +731,14 @@ static void
dispatch_enter_dynamorio(dcontext_t *dcontext)
{
/* We're transitioning to DynamoRIO from somewhere: either the fcache,
* the kernel (WHERE_TRAMPOLINE), or the app itself via our start/stop API.
* N.B.: set whereami to WHERE_APP iff this is the first dispatch() entry
* the kernel (DR_WHERE_TRAMPOLINE), or the app itself via our start/stop API.
* N.B.: set whereami to DR_WHERE_APP iff this is the first dispatch() entry
* for this thread!
*/
where_am_i_t wherewasi = dcontext->whereami;
dr_where_am_i_t wherewasi = dcontext->whereami;
#ifdef UNIX
if (!(wherewasi == WHERE_FCACHE || wherewasi == WHERE_TRAMPOLINE ||
wherewasi == WHERE_APP)) {
if (!(wherewasi == DR_WHERE_FCACHE || wherewasi == DR_WHERE_TRAMPOLINE ||
wherewasi == DR_WHERE_APP)) {
/* This is probably our own syscalls hitting our own sysenter
* hook (PR 212570), since we're not completely user library
* independent (PR 206369).
Expand All @@ -756,9 +756,9 @@ dispatch_enter_dynamorio(dcontext_t *dcontext)
"DR's own syscall (via user library) hit the sysenter hook");
}
#endif
ASSERT(wherewasi == WHERE_FCACHE || wherewasi == WHERE_TRAMPOLINE ||
wherewasi == WHERE_APP);
dcontext->whereami = WHERE_DISPATCH;
ASSERT(wherewasi == DR_WHERE_FCACHE || wherewasi == DR_WHERE_TRAMPOLINE ||
wherewasi == DR_WHERE_APP);
dcontext->whereami = DR_WHERE_DISPATCH;
ASSERT_LOCAL_HEAP_UNPROTECTED(dcontext);
ASSERT(check_should_be_protected(DATASEC_RARELY_PROT));
/* CANNOT hold any locks across cache execution, as our thread synch
Expand All @@ -775,7 +775,7 @@ dispatch_enter_dynamorio(dcontext_t *dcontext)
#endif

DOLOG(2, LOG_INTERP, {
if (wherewasi == WHERE_APP) {
if (wherewasi == DR_WHERE_APP) {
LOG(THREAD, LOG_INTERP, 2, "\ninitial dispatch: target = "PFX"\n",
dcontext->next_tag);
dump_mcontext_callstack(dcontext);
Expand All @@ -791,7 +791,7 @@ dispatch_enter_dynamorio(dcontext_t *dcontext)
* messy that we're violating assumption of no ptrs...
*/

if (wherewasi == WHERE_APP) { /* first entrance */
if (wherewasi == DR_WHERE_APP) { /* first entrance */
if (dcontext->last_exit == get_syscall_linkstub()) {
/* i#813: the app hit our post-sysenter hook while native.
* XXX: should we try to process ni syscalls here? But we're only
Expand Down Expand Up @@ -870,7 +870,7 @@ dispatch_enter_dynamorio(dcontext_t *dcontext)
/* KSWITCHed next time around for a better explanation */
KSTART_DC(dcontext, dispatch_num_exits);

if (wherewasi != WHERE_APP) { /* if not first entrance */
if (wherewasi != DR_WHERE_APP) { /* if not first entrance */
if (get_at_syscall(dcontext))
handle_post_system_call(dcontext);

Expand Down Expand Up @@ -967,7 +967,7 @@ dispatch_enter_dynamorio(dcontext_t *dcontext)
ASSERT(LINKSTUB_FAKE(dcontext->last_exit));
}

if (wherewasi != WHERE_APP) { /* if not first entrance */
if (wherewasi != DR_WHERE_APP) { /* if not first entrance */
/* now fully process the last cache exit as couldbelinking */
dispatch_exit_fcache(dcontext);
}
Expand Down Expand Up @@ -2047,7 +2047,7 @@ handle_system_call(dcontext_t *dcontext)
/* avoid synch errors with dispatch -- since enter_fcache will set
* whereami for prev dcontext, not real one!
*/
tmp_dcontext->whereami = WHERE_FCACHE;
tmp_dcontext->whereami = DR_WHERE_FCACHE;
}
#endif

Expand Down Expand Up @@ -2085,7 +2085,7 @@ handle_system_call(dcontext_t *dcontext)
if (dcontext->signals_pending) {
/* i#2019: see comments in dispatch_enter_fcache() */
KSTOP(syscall_fcache);
dcontext->whereami = WHERE_DISPATCH;
dcontext->whereami = DR_WHERE_DISPATCH;
set_at_syscall(dcontext, false);
/* We need to remember both the post-syscall resumption point and
* the fact that we need to execute a syscall, but we only have
Expand Down Expand Up @@ -2237,7 +2237,7 @@ handle_callback_return(dcontext_t *dcontext)
SELF_PROTECT_LOCAL(dcontext, READONLY);

/* obey flushing protocol, plus set whereami (both using real dcontext) */
dcontext->whereami = WHERE_FCACHE;
dcontext->whereami = DR_WHERE_FCACHE;
set_at_syscall(dcontext, true); /* will be set to false on other end's post-syscall */
ASSERT(!is_couldbelinking(dcontext));

Expand Down
2 changes: 1 addition & 1 deletion core/dynamo.c
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ initialize_dynamo_context(dcontext_t *dcontext)
*/
memset(dcontext->upcontext_ptr, 0, sizeof(unprotected_context_t));
dcontext->initialized = true;
dcontext->whereami = WHERE_APP;
dcontext->whereami = DR_WHERE_APP;
dcontext->next_tag = NULL;
dcontext->native_exec_postsyscall = NULL;
memset(dcontext->native_retstack, 0, sizeof(dcontext->native_retstack));
Expand Down
41 changes: 40 additions & 1 deletion core/fcache.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2011-2017 Google, Inc. All rights reserved.
* Copyright (c) 2011-2018 Google, Inc. All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* **********************************************************/

Expand Down Expand Up @@ -1176,6 +1176,45 @@ fcache_fragment_pclookup(dcontext_t *dcontext, cache_pc lookup_pc, fragment_t *w
return found;
}

/* This is safe to call from a signal handler. */
dr_where_am_i_t
fcache_refine_whereami(dcontext_t *dcontext, dr_where_am_i_t whereami, app_pc pc,
OUT fragment_t **containing_fragment)
{
if (whereami != DR_WHERE_FCACHE) {
if (containing_fragment != NULL)
*containing_fragment = NULL;
return whereami;
}
fragment_t wrapper;
fragment_t *fragment = fragment_pclookup(dcontext, pc, &wrapper);
if (fragment == NULL) {
/* Since we're DR_WHERE_FCACHE, our locks shouldn't be held.
* XXX: we could double-check fcache_unit_areas.lock before
* calling (case 1317) and assert on it.
*/
if (in_fcache(pc)) {
whereami = DR_WHERE_UNKNOWN;
} else {
/* Identify parts of our assembly code now.
* It's all generated and post-process can't identify.
* Assume code order is as follows:
*/
if (in_context_switch_code(dcontext, (cache_pc)pc)) {
whereami = DR_WHERE_CONTEXT_SWITCH;
} else if (in_indirect_branch_lookup_code(dcontext,
(cache_pc)pc)) {
whereami = DR_WHERE_IBL;
} else {
whereami = DR_WHERE_UNKNOWN;
}
}
}
if (containing_fragment != NULL)
*containing_fragment = fragment;
return whereami;
}

#ifdef DEBUG
static bool
fcache_pc_in_live_unit(fcache_t *cache, cache_pc pc)
Expand Down
6 changes: 6 additions & 0 deletions core/fcache.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* **********************************************************
* Copyright (c) 2018 Google, Inc. All rights reserved.
* Copyright (c) 2000-2008 VMware, Inc. All rights reserved.
* **********************************************************/

Expand Down Expand Up @@ -141,6 +142,11 @@ bool fragment_lookup_deleted(dcontext_t *dcontext, app_pc tag);
fragment_t *
fcache_fragment_pclookup(dcontext_t *dcontext, cache_pc lookup_pc, fragment_t *wrapper);

/* This is safe to call from a signal handler. */
dr_where_am_i_t
fcache_refine_whereami(dcontext_t *dcontext, dr_where_am_i_t whereami, app_pc pc,
OUT fragment_t **containing_fragment);

void
fcache_coarse_cache_delete(dcontext_t *dcontext, coarse_info_t *info);

Expand Down
41 changes: 23 additions & 18 deletions core/globals.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* **********************************************************
* Copyright (c) 2011-2017 Google, Inc. All rights reserved.
* Copyright (c) 2011-2018 Google, Inc. All rights reserved.
* Copyright (c) 2000-2010 VMware, Inc. All rights reserved.
* **********************************************************/

Expand Down Expand Up @@ -598,25 +598,30 @@ extern mutex_t bb_building_lock;
extern volatile bool bb_lock_start;
extern recursive_lock_t change_linking_lock;

/* where the current app thread's control is */
/* DR_API EXPORT BEGIN */
/**
* Identifies where a thread's control is at any one point.
* Used with client PC sampling using dr_set_itimer().
*/
typedef enum {
WHERE_APP=0,
WHERE_INTERP,
WHERE_DISPATCH,
WHERE_MONITOR,
WHERE_SYSCALL_HANDLER,
WHERE_SIGNAL_HANDLER,
WHERE_TRAMPOLINE,
WHERE_CONTEXT_SWITCH,
WHERE_IBL,
WHERE_FCACHE,
WHERE_CLEAN_CALLEE,
WHERE_UNKNOWN,
DR_WHERE_APP=0, /**< Control is in native application code. */
DR_WHERE_INTERP, /**< Control is in basic block building. */
DR_WHERE_DISPATCH, /**< Control is in dispatch. */
DR_WHERE_MONITOR, /**< Control is in trace building. */
DR_WHERE_SYSCALL_HANDLER, /**< Control is in system call handling. */
DR_WHERE_SIGNAL_HANDLER, /**< Control is in signal handling. */
DR_WHERE_TRAMPOLINE, /**< Control is in trampoline hooks. */
DR_WHERE_CONTEXT_SWITCH, /**< Control is in context switching. */
DR_WHERE_IBL, /**< Control is in inlined indirect branch lookup. */
DR_WHERE_FCACHE, /**< Control is in the code cache. */
DR_WHERE_CLEAN_CALLEE, /**< Control is in a clean call. */
DR_WHERE_UNKNOWN, /**< Control is in an unknown location. */
#ifdef HOT_PATCHING_INTERFACE
WHERE_HOTPATCH,
DR_WHERE_HOTPATCH, /**< Control is in hotpatching. */
#endif
WHERE_LAST
} where_am_i_t;
DR_WHERE_LAST /**< Equals the count of DR_WHERE_xxx locations. */
} dr_where_am_i_t;
/* DR_API EXPORT END */

/* make args easier to read for protection change calls
* since only two possibilities not using new type
Expand Down Expand Up @@ -786,7 +791,7 @@ struct _dcontext_t {
coarse_info_t *dir_exit;
} coarse_exit;

where_am_i_t whereami; /* where control is at the moment */
dr_where_am_i_t whereami; /* where control is at the moment */
#ifdef UNIX
char signals_pending; /* != 0: pending; < 0: currently handling one */
#endif
Expand Down
Loading

0 comments on commit ce3a9f4

Please sign in to comment.