From 13e4f95ec878c3f3adbb8a241c40636865d0e363 Mon Sep 17 00:00:00 2001 From: "John F.X. Galea" Date: Thu, 8 Mar 2018 15:49:40 +0000 Subject: [PATCH] i#2833 user data: add module events with user data (#2837) Adds new versions of drmgr's module load/unload events that take in user data parameters. Adds tests of the new functions. Issue: #2833 --- api/docs/release.dox | 4 ++ ext/drmgr/drmgr.c | 72 +++++++++++++++++-- ext/drmgr/drmgr.h | 49 +++++++++++++ suite/tests/CMakeLists.txt | 2 +- suite/tests/client-interface/drmgr-test.c | 18 ++++- suite/tests/client-interface/drmgr-test.dll.c | 62 ++++++++++++++-- .../client-interface/drmgr-test.templatex | 4 ++ 7 files changed, 198 insertions(+), 13 deletions(-) diff --git a/api/docs/release.dox b/api/docs/release.dox index c0e1835a4f2..bdf2b61ef6d 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -209,6 +209,10 @@ Further non-compatibility-affecting changes include: drmgr_unregister_thread_init_event_user_data() to enable passing of user data. - Added drmgr_register_thread_exit_event_user_data() and drmgr_unregister_thread_exit_event_usr_data() to enable passing of user data. + - Added drmgr_register_module_load_event_user_data() and + drmgr_unregister_module_load_event_user_data() to enable passing of user data. + - Added drmgr_register_module_unload_event_user_data() and + drmgr_unregister_module_unload_event() to enable passing of user data. - Added a new drcachesim feature that records which cpu each thread executed on along with an optional simulator scheduling feature to schedule threads on simulated cores to match the recorded execution on diff --git a/ext/drmgr/drmgr.c b/ext/drmgr/drmgr.c index dfc292f86e8..4aae11b9762 100644 --- a/ext/drmgr/drmgr.c +++ b/ext/drmgr/drmgr.c @@ -126,8 +126,14 @@ typedef struct _generic_event_entry_t { void (*cls_cb)(void *, bool); bool (*presys_cb)(void *, int); void (*postsys_cb)(void *, int); - void (*modload_cb)(void *, const module_data_t *, bool); - void (*modunload_cb)(void *, const module_data_t *); + union { + void (*cb_no_user_data)(void *, const module_data_t *, bool); + void (*cb_user_data)(void *, const module_data_t *, bool, void *user_data); + } modload_cb; + union { + void (*cb_no_user_data)(void *, const module_data_t *); + void (*cb_user_data)(void *, const module_data_t *, void *user_data); + } modunload_cb; void (*kernel_xfer_cb)(void *, const dr_kernel_xfer_info_t *); #ifdef UNIX dr_signal_action_t (*signal_cb)(void *, dr_siginfo_t *); @@ -1458,6 +1464,17 @@ drmgr_register_module_load_event_ex(void (*func) (void (*)(void)) func, priority, false, NULL); } +DR_EXPORT +bool +drmgr_register_module_load_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + bool loaded, void *user_data), + drmgr_priority_t *priority, void *user_data) +{ + return drmgr_generic_event_add(&cblist_modload, modload_event_lock, + (void (*)(void)) func, priority, true, user_data); +} + DR_EXPORT bool drmgr_unregister_module_load_event(void (*func) @@ -1468,6 +1485,16 @@ drmgr_unregister_module_load_event(void (*func) (void (*)(void)) func); } +DR_EXPORT +bool +drmgr_unregister_module_load_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + bool loaded, void *user_data)) +{ + return drmgr_generic_event_remove(&cblist_modload, modload_event_lock, + (void (*)(void)) func); +} + static void drmgr_modload_event(void *drcontext, const module_data_t *info, bool loaded) @@ -1483,7 +1510,15 @@ drmgr_modload_event(void *drcontext, const module_data_t *info, for (i = 0; i < iter.num; i++) { if (!iter.cbs.generic[i].pri.valid) continue; - (*iter.cbs.generic[i].cb.modload_cb)(drcontext, info, loaded); + bool is_using_user_data = iter.cbs.generic[i].is_using_user_data; + void *user_data = iter.cbs.generic[i].user_data; + if (is_using_user_data == false) { + (*iter.cbs.generic[i].cb.modload_cb.cb_no_user_data)(drcontext, info, + loaded); + } else { + (*iter.cbs.generic[i].cb.modload_cb.cb_user_data)(drcontext, info, loaded, + user_data); + } } cblist_delete_local(drcontext, &iter, BUFFER_SIZE_ELEMENTS(local)); } @@ -1507,6 +1542,17 @@ drmgr_register_module_unload_event_ex(void (*func) (void (*)(void)) func, priority, false, NULL); } +DR_EXPORT +bool +drmgr_register_module_unload_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + void *user_data), + drmgr_priority_t *priority, void *user_data) +{ + return drmgr_generic_event_add(&cblist_modunload, modunload_event_lock, + (void (*)(void)) func, priority, true, user_data); +} + DR_EXPORT bool drmgr_unregister_module_unload_event(void (*func) @@ -1516,6 +1562,17 @@ drmgr_unregister_module_unload_event(void (*func) (void (*)(void)) func); } +DR_EXPORT +bool +drmgr_unregister_module_unload_event_user_data(void (*func) + (void *drcontext, + const module_data_t *info, + void *user_data)) +{ + return drmgr_generic_event_remove(&cblist_modunload, modunload_event_lock, + (void (*)(void)) func); +} + static void drmgr_modunload_event(void *drcontext, const module_data_t *info) { @@ -1530,7 +1587,14 @@ drmgr_modunload_event(void *drcontext, const module_data_t *info) for (i = 0; i < iter.num; i++) { if (!iter.cbs.generic[i].pri.valid) continue; - (*iter.cbs.generic[i].cb.modunload_cb)(drcontext, info); + bool is_using_user_data = iter.cbs.generic[i].is_using_user_data; + void *user_data = iter.cbs.generic[i].user_data; + if (is_using_user_data == false) + (*iter.cbs.generic[i].cb.modunload_cb.cb_no_user_data)(drcontext, info); + else { + (*iter.cbs.generic[i].cb.modunload_cb.cb_user_data)(drcontext, info, + user_data); + } } cblist_delete_local(drcontext, &iter, BUFFER_SIZE_ELEMENTS(local)); } diff --git a/ext/drmgr/drmgr.h b/ext/drmgr/drmgr.h index e57acdb1eea..7fc40d3debf 100644 --- a/ext/drmgr/drmgr.h +++ b/ext/drmgr/drmgr.h @@ -926,6 +926,19 @@ drmgr_register_module_load_event_ex(void (*func) bool loaded), drmgr_priority_t *priority); +DR_EXPORT +/** + * Registers a callback function for the module load event, which + * behaves just like DR's module load event dr_register_module_load_event(). + * Allows for the passing of user input \p user_data, which is available upon + * the execution of the callback. \return whether successful. + */ +bool +drmgr_register_module_load_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + bool loaded, void *user_data), + drmgr_priority_t *priority, void *user_data); + DR_EXPORT /** * Unregister a callback function for the module load event. @@ -937,6 +950,17 @@ drmgr_unregister_module_load_event(void (*func) (void *drcontext, const module_data_t *info, bool loaded)); +DR_EXPORT +/** + * Unregister a callback function, which takes user data as a parameter + * for the module load event. \return true if unregistration is successful + * and false if it is not (e.g., \p func was not registered). + */ +bool +drmgr_unregister_module_load_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + bool loaded, void *user_data)); + DR_EXPORT /** * Registers a callback function for the module unload event, which @@ -963,6 +987,19 @@ drmgr_register_module_unload_event_ex(void (*func) drmgr_priority_t *priority); DR_EXPORT +/** + * Registers a callback function for the module unload event, which + * behaves just like DR's module unload event dr_register_module_unload_event(). + * Allows for the passing of user data, \p user_data, which is available upon the + * execution of the callback. \return whether successful. + */ +bool +drmgr_register_module_unload_event_user_data(void (*func) + (void *drcontext, const module_data_t *info, + void *user_data), + drmgr_priority_t *priority, + void *user_data); +DR_EXPORT /** * Unregister a callback function for the module unload event. * \return true if unregistration is successful and false if it is not @@ -972,6 +1009,18 @@ bool drmgr_unregister_module_unload_event(void (*func) (void *drcontext, const module_data_t *info)); +DR_EXPORT +/** + * Unregister a callback function, that takes user data as a parameter, + * for the module unload event. \return true if unregistration is + * successful and false if it is not (e.g., \p func was not registered). + */ +bool +drmgr_unregister_module_unload_event_user_data(void (*func) + (void *drcontext, + const module_data_t *info, + void *user_data)); + DR_EXPORT /** * Registers a callback function for the kernel transfer event, which diff --git a/suite/tests/CMakeLists.txt b/suite/tests/CMakeLists.txt index e5df6994534..4ac220e02bb 100644 --- a/suite/tests/CMakeLists.txt +++ b/suite/tests/CMakeLists.txt @@ -2037,7 +2037,7 @@ if (CLIENT_INTERFACE) tobuild_ci(client.drcontainers-test client-interface/drcontainers-test.c "" "" "") use_DynamoRIO_extension(client.drcontainers-test.dll drcontainers) - tobuild_ci(client.drmgr-test client-interface/drmgr-test.c "" "" "") + tobuild_ci(client.drmgr-test client-interface/drmgr-test.c "" "" "${events_appdll_path}") use_DynamoRIO_extension(client.drmgr-test.dll drmgr) if (UNIX AND NOT ANDROID) # pthreads is inside Bionic on Android target_link_libraries(client.drmgr-test ${libpthread}) diff --git a/suite/tests/client-interface/drmgr-test.c b/suite/tests/client-interface/drmgr-test.c index 053cae1b111..5858ebb3b72 100755 --- a/suite/tests/client-interface/drmgr-test.c +++ b/suite/tests/client-interface/drmgr-test.c @@ -181,7 +181,7 @@ run_func(void * arg) } int -main() +main(int argc, char **argv) { int tid; HANDLE hThread; @@ -232,6 +232,14 @@ main() WaitForSingleObject(hThread, INFINITE); print("All done\n"); + HMODULE hmod; + + /* + * Load and unload a module to cause a module unload event + */ + hmod = LoadLibrary(argv[1]); + FreeLibrary(hmod); + return 0; } @@ -243,6 +251,7 @@ main() #include #include #include +# include volatile double pi = 0.0; /* Approximation to pi (shared) */ pthread_mutex_t pi_lock; /* Lock for above */ @@ -320,6 +329,13 @@ main(int argc, char **argv) exit(1); } + void *hmod; + hmod = dlopen(argv[1], RTLD_LAZY|RTLD_LOCAL); + if (hmod != NULL) + dlclose(hmod); + else + print("module load failed: %s\n", dlerror()); + /* Print the result */ print("Estimation of pi is %16.15f\n", pi); return 0; diff --git a/suite/tests/client-interface/drmgr-test.dll.c b/suite/tests/client-interface/drmgr-test.dll.c index 2b0bfbefb31..2f7f6d5813b 100755 --- a/suite/tests/client-interface/drmgr-test.dll.c +++ b/suite/tests/client-interface/drmgr-test.dll.c @@ -66,6 +66,8 @@ static int thread_exit_events; static int thread_exit_ex_events; static int thread_exit_user_data_events; static int thread_exit_null_user_data_events; +static int mod_load_events; +static int mod_unload_events; static void *syslock; static void *threadlock; @@ -89,6 +91,10 @@ static void event_thread_init_null_user_data(void *drcontext, void *user_data); static void event_thread_exit_null_user_data(void *drcontext, void *user_data); static void event_thread_context_init(void *drcontext, bool new_depth); static void event_thread_context_exit(void *drcontext, bool process_exit); +static void event_mod_load(void *drcontext, const module_data_t *mod, + bool loaded, void *user_data); +static void event_mod_unload(void *drcontext, const module_data_t *mod, + void *user_data); static bool event_filter_syscall(void *drcontext, int sysnum); static bool event_pre_sys_A(void *drcontext, int sysnum); static bool event_pre_sys_B(void *drcontext, int sysnum); @@ -122,7 +128,8 @@ static dr_emit_flags_t one_time_bb_event(void *drcontext, void *tag, instrlist_t bool for_trace, bool translating); static void event_kernel_xfer(void *drcontext, const dr_kernel_xfer_info_t *info); -static const uintptr_t user_data_test = 9090; +static const uintptr_t thread_user_data_test = 9090; +static const uintptr_t mod_user_data_test = 1070; DR_EXPORT void dr_init(client_id_t id) @@ -143,6 +150,13 @@ dr_init(client_id_t id) NULL, NULL, -1}; drmgr_priority_t thread_exit_pri = {sizeof(priority), "drmgr-thread-exit-test", NULL, NULL, 1}; + drmgr_priority_t thread_exit_user_data_pri = {sizeof(priority), + "drmgr-thread-exit-user-data-test", + NULL, NULL, 2}; + drmgr_priority_t thread_exit_null_user_data_pri = {sizeof(priority), + "drmgr-t-exit-null-usr-data-test", + NULL, NULL, 3}; + bool ok; drmgr_init(); @@ -153,13 +167,15 @@ dr_init(client_id_t id) drmgr_register_thread_exit_event_ex(event_thread_exit_ex, &thread_exit_pri); drmgr_register_thread_init_event_user_data(event_thread_init_user_data, &thread_init_user_data_pri, - (void *) user_data_test); - drmgr_register_thread_exit_event_user_data(event_thread_exit_user_data, NULL, - (void *) user_data_test); + (void *) thread_user_data_test); + drmgr_register_thread_exit_event_user_data(event_thread_exit_user_data, + &thread_exit_user_data_pri, + (void *) thread_user_data_test); drmgr_register_thread_init_event_user_data(event_thread_init_null_user_data, &thread_init_null_user_data_pri, NULL); - drmgr_register_thread_exit_event_user_data(event_thread_exit_null_user_data, NULL, + drmgr_register_thread_exit_event_user_data(event_thread_exit_null_user_data, + &thread_exit_null_user_data_pri, NULL); ok = drmgr_register_bb_instrumentation_event(event_bb_analysis, @@ -187,6 +203,12 @@ dr_init(client_id_t id) event_bb4_instru2instru, &priority4); + drmgr_register_module_load_event_user_data(event_mod_load, NULL, + (void *) mod_user_data_test); + + drmgr_register_module_unload_event_user_data(event_mod_unload, NULL, + (void *) mod_user_data_test); + tls_idx = drmgr_register_tls_field(); CHECK(tls_idx != -1, "drmgr_register_tls_field failed"); cls_idx = drmgr_register_cls_field(event_thread_context_init, @@ -235,6 +257,11 @@ event_exit(void) if (thread_exit_null_user_data_events > 0) dr_fprintf(STDERR, "saw event_thread_exit_null_user_data\n"); + if (mod_load_events > 0) + dr_fprintf(STDERR, "saw event_mod_load\n"); + if (mod_unload_events > 0) + dr_fprintf(STDERR, "saw event_mod_unload\n"); + if (!drmgr_unregister_bb_instrumentation_event(event_bb_analysis)) CHECK(false, "drmgr unregistration failed"); @@ -244,6 +271,12 @@ event_exit(void) event_bb4_instru2instru)) CHECK(false, "drmgr unregistration failed"); + if (!drmgr_unregister_module_load_event_user_data(event_mod_load)) + CHECK(false, "drmgr mod load unregistration failed"); + + if (!drmgr_unregister_module_unload_event_user_data(event_mod_unload)) + CHECK(false, "drmgr mod load unregistration failed"); + if (!drmgr_unregister_cls_field(event_thread_context_init, event_thread_context_exit, cls_idx)) CHECK(false, "drmgr unregistration failed"); @@ -293,7 +326,7 @@ event_thread_init_user_data(void *drcontext, void *user_data) dr_fprintf(STDERR, "in event_thread_init_user_data\n"); in_event_thread_init_user_data = true; - CHECK(user_data == (void *) user_data_test, "incorrect user data passed"); + CHECK(user_data == (void *) thread_user_data_test, "incorrect user data passed"); } dr_mutex_unlock(threadlock); } @@ -340,7 +373,7 @@ event_thread_exit_user_data(void *drcontext, void *user_data) { /* We do not print as on Win10 there are extra threads messing up the order. */ dr_mutex_lock(threadlock); - CHECK(user_data == (void *) user_data_test, "incorrect user data passed"); + CHECK(user_data == (void *) thread_user_data_test, "incorrect user data passed"); thread_exit_user_data_events++; dr_mutex_unlock(threadlock); } @@ -355,6 +388,21 @@ event_thread_exit_null_user_data(void *drcontext, void *user_data) dr_mutex_unlock(threadlock); } +static void +event_mod_load(void *drcontext, const module_data_t *mod, bool loaded, void *user_data) +{ + mod_load_events++; + CHECK(user_data == (void *) mod_user_data_test, "incorrect user data for mod load"); +} + + +static void +event_mod_unload(void *drcontext, const module_data_t *mod, void *user_data) +{ + mod_unload_events++; + CHECK(user_data == (void *) mod_user_data_test, "incorrect user data for mod unload"); +} + static void event_thread_context_init(void *drcontext, bool new_depth) { diff --git a/suite/tests/client-interface/drmgr-test.templatex b/suite/tests/client-interface/drmgr-test.templatex index ace5c8ff452..25ecf4d8bb4 100755 --- a/suite/tests/client-interface/drmgr-test.templatex +++ b/suite/tests/client-interface/drmgr-test.templatex @@ -22,11 +22,15 @@ Inside handler in wnd_callback 0x0*0008001 0 2 Got message 0x0*0008001 1 3 All done +appdll initialized #else +appdll initialized Estimation of pi is 3.142425985001098 #endif saw event_thread_exit saw event_thread_exit_ex saw event_thread_exit_user_data saw event_thread_exit_null_user_data +saw event_mod_load +saw event_mod_unload all done