diff --git a/api/docs/release.dox b/api/docs/release.dox index 9d67b85b65e..f26726cae43 100644 --- a/api/docs/release.dox +++ b/api/docs/release.dox @@ -144,6 +144,10 @@ changes: This is a binary compatibility change for the OPSZ_ enum. - Added a new encoding hint field to #instr_t. - Added a requirement that a C++11-complient compiler be used with \ref page_droption. + - Changed the syntax of the drcachesim -record_function option. It now takes + pairs instead of triples, with the identifier computed by the tracer. + The tracer writes out a file listing the "library!symbol" functions traced, + with the line ordinal indicating the identifier. The changes between version \DR_VERSION and 7.1.0 include the following minor compatibility changes: diff --git a/clients/drcachesim/common/options.cpp b/clients/drcachesim/common/options.cpp index bb875fcc5af..35bf617017b 100644 --- a/clients/drcachesim/common/options.cpp +++ b/clients/drcachesim/common/options.cpp @@ -405,22 +405,24 @@ droption_t op_record_function( "Record invocations trace for the specified function(s).", "Record invocations trace for the specified function(s) in the option" " value. Default value is empty. The value should fit this format:" - " function_name|function_id|func_args_num" - " (e.g., -record_function \"memset|10|3\"). The trace would contain" + " function_name|func_args_num" + " (e.g., -record_function \"memset|3\"). The trace would contain" " information for function return address, function argument value(s)," " and function return value. We only record pointer-sized arguments and" - " return value. The trace is labeled with the function_id via an ID entry" - " prior to each set of value entries." + " return values. The trace identifies which function is involved" + " via a numeric ID entry prior to each set of value entries." + " The mapping from numeric ID to library-qualified symbolic name is recorded" + " during tracing in a file \"funclist.log\" which simply lists the names in ID" + " order starting from 0, one name per line." " If the target function is in the dynamic symbol table, then the function_name" " should be a mangled name (e.g. \"_Znwm\" for \"operator new\", \"_ZdlPv\" for" " \"operator delete\"). Otherwise, the function_name should be a demangled name." " Recording multiple functions can be achieved by using the separator" " \"" OP_RECORD_FUNC_ITEM_SEP - "\" (e.g., -record_function \"memset|10|3" OP_RECORD_FUNC_ITEM_SEP - "memcpy|11|3\"), or" + "\" (e.g., -record_function \"memset|3" OP_RECORD_FUNC_ITEM_SEP "memcpy|3\"), or" " specifying multiple -record_function options (e.g., -record_function" - " \"memset|10|3\" -record_function \"memcpy|11|3\")." - " Note that the provided function id should be unique, and not collide with" + " \"memset|3\" -record_function \"memcpy|3\")." + " Note that the provided function name should be unique, and not collide with" " existing heap functions (see -record_heap_value) if -record_heap" " option is enabled."); droption_t op_record_heap( @@ -433,10 +435,10 @@ droption_t op_record_heap( droption_t op_record_heap_value( DROPTION_SCOPE_ALL, "record_heap_value", DROPTION_FLAG_ACCUMULATE, OP_RECORD_FUNC_ITEM_SEP, - "malloc|0|1" OP_RECORD_FUNC_ITEM_SEP "free|1|1" OP_RECORD_FUNC_ITEM_SEP - "tc_malloc|2|1" OP_RECORD_FUNC_ITEM_SEP "tc_free|3|1" OP_RECORD_FUNC_ITEM_SEP - "__libc_malloc|4|1" OP_RECORD_FUNC_ITEM_SEP "__libc_free|5|1" OP_RECORD_FUNC_ITEM_SEP - "calloc|6|2", + "malloc|1" OP_RECORD_FUNC_ITEM_SEP "free|1" OP_RECORD_FUNC_ITEM_SEP + "tc_malloc|1" OP_RECORD_FUNC_ITEM_SEP "tc_free|1" OP_RECORD_FUNC_ITEM_SEP + "__libc_malloc|1" OP_RECORD_FUNC_ITEM_SEP "__libc_free|1" OP_RECORD_FUNC_ITEM_SEP + "calloc|2", "Functions recorded by -record_heap", "Functions recorded by -record_heap. The option value should fit the same" " format required by -record_function. These functions will not" diff --git a/clients/drcachesim/tests/burst_malloc.cpp b/clients/drcachesim/tests/burst_malloc.cpp index bac54d6b5f4..9313127613a 100644 --- a/clients/drcachesim/tests/burst_malloc.cpp +++ b/clients/drcachesim/tests/burst_malloc.cpp @@ -39,8 +39,11 @@ /* Like burst_static we deliberately do not include configure.h here */ #include "dr_api.h" +#include "drmemtrace/drmemtrace.h" #include #include +#include +#include #include #include @@ -86,6 +89,28 @@ do_some_work(int arg) return (temp > 0); } +static void +exit_cb(void *) +{ + const char *funclist_path; + drmemtrace_status_t res = drmemtrace_get_funclist_path(&funclist_path); + assert(res == DRMEMTRACE_SUCCESS); + std::ifstream stream(funclist_path); + assert(stream.good()); + std::string line; + bool found_malloc = false; + bool found_return_big_value = false; + while (std::getline(stream, line)) { + assert(line.find('!') != std::string::npos); + if (line.find("!return_big_value") != std::string::npos) + found_return_big_value = true; + if (line.find("!malloc") != std::string::npos) + found_malloc = true; + } + assert(found_malloc); + assert(found_return_big_value); +} + int main(int argc, const char *argv[]) { @@ -94,9 +119,12 @@ main(int argc, const char *argv[]) "-stderr_mask 0xc -rstats_to_stderr" " -client_lib ';;-offline -record_heap" // Test large values that require two entries. - " -record_function \"malloc|0|1&return_big_value|42|1\"'")) + " -record_function \"malloc|1&return_big_value|1\"'")) std::cerr << "failed to set env var!\n"; + drmemtrace_status_t res = drmemtrace_buffer_handoff(nullptr, exit_cb, nullptr); + assert(res == DRMEMTRACE_SUCCESS); + for (int i = 0; i < 3; i++) { std::cerr << "pre-DR init\n"; dr_app_setup(); diff --git a/clients/drcachesim/tests/offline-burst_malloc.templatex b/clients/drcachesim/tests/offline-burst_malloc.templatex index d76a4524d0b..967d96a0ae0 100644 --- a/clients/drcachesim/tests/offline-burst_malloc.templatex +++ b/clients/drcachesim/tests/offline-burst_malloc.templatex @@ -1,5 +1,5 @@ pre-DR init -Warning: duplicated function id .* +Warning: duplicated function name .* .* pre-DR start pre-DR detach diff --git a/clients/drcachesim/tracer/drmemtrace.h b/clients/drcachesim/tracer/drmemtrace.h index 027041d274d..a87bc30da44 100644 --- a/clients/drcachesim/tracer/drmemtrace.h +++ b/clients/drcachesim/tracer/drmemtrace.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2016-2018 Google, Inc. All rights reserved. + * Copyright (c) 2016-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -213,9 +213,16 @@ drmemtrace_buffer_handoff(drmemtrace_handoff_func_t handoff_func, * Its creation can be customized using drmemtrace_custom_module_data() * and then modified before passing to raw2trace via * drmodtrack_add_custom_data() and drmodtrack_offline_write(). + * Use drmemtrace_get_modlist_path() to obtain the full path. */ #define DRMEMTRACE_MODULE_LIST_FILENAME "modules.log" +/** + * The name of the file in -offline mode where function tracing names + * are written. Use drmemtrace_get_funclist_path() to obtain the full path. + */ +#define DRMEMTRACE_FUNCTION_MAP_FILENAME "funclist.log" + DR_EXPORT /** * Retrieves the full path to the output directory in -offline mode @@ -233,6 +240,16 @@ DR_EXPORT drmemtrace_status_t drmemtrace_get_modlist_path(OUT const char **path); +DR_EXPORT +/** + * Retrieves the full path to the file in -offline mode where function tracing + * information is written. Each "library!symbol" function that was traced occupies + * one line of the file, with the line number (starting from 0) equal to the + * identifier recorded in the function trace entries. + */ +drmemtrace_status_t +drmemtrace_get_funclist_path(OUT const char **path); + DR_EXPORT /** * Adds custom data stored with each module in the module list produced for diff --git a/clients/drcachesim/tracer/func_trace.cpp b/clients/drcachesim/tracer/func_trace.cpp index 52fce73925f..a3b9d4afd3c 100644 --- a/clients/drcachesim/tracer/func_trace.cpp +++ b/clients/drcachesim/tracer/func_trace.cpp @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2016-2018 Google, Inc. All rights reserved. + * Copyright (c) 2016-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -59,20 +59,36 @@ static int func_trace_init_count; static int tls_idx; static func_trace_append_entry_vec_t append_entry_vec; -static drvector_t funcs; +static drvector_t func_names; + +static void *funcs_wrapped_lock; +/* These are protected by funcs_wrapped_lock. */ +static drvector_t funcs_wrapped; +static int wrap_id; + static std::string funcs_str, funcs_str_sep; +static ssize_t (*write_file_func)(file_t file, const void *data, size_t count); +static file_t funclist_fd; + +/* The maximum supported length of a function name to trace. + * We expect this to be longer than any C/C++ symbol we'll see. + */ +#define DRMEMTRACE_MAX_FUNC_NAME_LEN 2048 + +/* The maximum length of a line in #DRMEMTRACE_FUNCTION_MAP_FILENAME. */ +#define DRMEMTRACE_MAX_QUALIFIED_FUNC_LEN (DRMEMTRACE_MAX_FUNC_NAME_LEN + 256) typedef struct { - char name[2048]; // probably the maximum length of C/C++ symbol + char name[DRMEMTRACE_MAX_FUNC_NAME_LEN]; int id; int arg_num; } func_metadata_t; static func_metadata_t * -create_func_metadata(std::string name, int id, int arg_num) +create_func_metadata(const char *name, int id, int arg_num) { func_metadata_t *f = (func_metadata_t *)dr_global_alloc(sizeof(func_metadata_t)); - strncpy(f->name, name.c_str(), BUFFER_SIZE_ELEMENTS(f->name)); + strncpy(f->name, name, BUFFER_SIZE_ELEMENTS(f->name)); f->id = id; f->arg_num = arg_num; return f; @@ -103,7 +119,7 @@ func_pre_hook(void *wrapcxt, INOUT void **user_data) func_trace_entry_vector_t *v = (func_trace_entry_vector_t *)pt_data; v->size = 0; size_t idx = (size_t)*user_data; - func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&funcs, (uint)idx); + func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&funcs_wrapped, (uint)idx); uintptr_t retaddr = (uintptr_t)drwrap_get_retaddr(wrapcxt); uintptr_t f_id = (uintptr_t)f->id; @@ -129,7 +145,7 @@ func_post_hook(void *wrapcxt, void *user_data) func_trace_entry_vector_t *v = (func_trace_entry_vector_t *)pt_data; v->size = 0; size_t idx = (size_t)user_data; - func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&funcs, (uint)idx); + func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&funcs_wrapped, (uint)idx); uintptr_t retval = (uintptr_t)drwrap_get_retval(wrapcxt); uintptr_t f_id = (uintptr_t)f->id; @@ -147,7 +163,7 @@ get_pc_by_symbol(const module_data_t *mod, const char *symbol) // Try to find the symbol in the dynamic symbol table. app_pc pc = (app_pc)dr_get_proc_address(mod->handle, symbol); if (pc != NULL) { - NOTIFY(1, "dr_get_proc_address found symbol %s at pc=" PFX "\n", symbol, pc); + NOTIFY(2, "dr_get_proc_address found symbol %s at pc=" PFX "\n", symbol, pc); return pc; } else { // If failed to find the symbol in the dynamic symbol table, then we try to find @@ -165,10 +181,10 @@ get_pc_by_symbol(const module_data_t *mod, const char *symbol) } if (err == DRSYM_SUCCESS) { pc = mod->start + offset; - NOTIFY(1, "drsym_lookup_symbol found symbol %s at pc=" PFX "\n", symbol, pc); + NOTIFY(2, "drsym_lookup_symbol found symbol %s at pc=" PFX "\n", symbol, pc); return pc; } else { - NOTIFY(1, "Failed to find symbol %s, drsym_error_t=%d\n", symbol, err); + NOTIFY(2, "Failed to find symbol %s, drsym_error_t=%d\n", symbol, err); return NULL; } } @@ -180,16 +196,76 @@ instru_funcs_module_load(void *drcontext, const module_data_t *mod, bool loaded) if (drcontext == NULL || mod == NULL) return; - NOTIFY(1, "instru_funcs_module_load, mod->full_path=%s\n", - mod->full_path == NULL ? "" : mod->full_path); - for (size_t i = 0; i < funcs.entries; i++) { - func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&funcs, (uint)i); + const char *mod_name = dr_module_preferred_name(mod); + if (mod_name == nullptr) { + const char *slash = strrchr(mod->full_path, '/'); +#ifdef WINDOWS + const char *bslash = strrchr(mod->full_path, '\\'); + if (bslash != nullptr && bslash > slash) + slash = bslash; +#endif + if (slash != nullptr) + mod_name = slash + 1; + } + if (mod_name == nullptr) + mod_name = ""; + NOTIFY(2, "instru_funcs_module_load for %s\n", mod_name); + for (size_t i = 0; i < func_names.entries; i++) { + func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&func_names, (uint)i); app_pc f_pc = get_pc_by_symbol(mod, f->name); if (f_pc != NULL) { - if (drwrap_wrap_ex(f_pc, func_pre_hook, func_post_hook, (void *)i, 0)) { - NOTIFY(1, "Inserted hooks for function %s\n", f->name); + dr_mutex_lock(funcs_wrapped_lock); + int id = wrap_id++; + drvector_append(&funcs_wrapped, + create_func_metadata(f->name, id, f->arg_num)); + char qual[DRMEMTRACE_MAX_QUALIFIED_FUNC_LEN]; + int len = dr_snprintf(qual, BUFFER_SIZE_ELEMENTS(qual), "%s!%s\n", mod_name, + f->name); + if (len < 0 || len == BUFFER_SIZE_ELEMENTS(qual)) { + NOTIFY(0, "Qualified name is too long and was truncated: %s!%s\n", + mod_name, f->name); + } + NULL_TERMINATE_BUFFER(qual); + size_t sz = strlen(qual); + if (write_file_func(funclist_fd, qual, sz) != static_cast(sz)) + NOTIFY(0, "Failed to write to funclist file\n"); + dr_mutex_unlock(funcs_wrapped_lock); + if (drwrap_wrap_ex(f_pc, func_pre_hook, func_post_hook, + (void *)(ptr_uint_t)id, 0)) { + NOTIFY(1, "Inserted hooks for %s!%s == id %d\n", mod_name, f->name, id); } else { - NOTIFY(1, "Failed to insert hooks for function %s\n", f->name); + // We could not write to the file and try to re-use the failed id (or + // write "symbol,id" to the file) to indicate this was not traced, but + // that seems like more complexity than it's worth: it seems simpler to + // list it as something we tried to trace and just have no data in the + // trace, counting on this warning to notify the user. + // + // TODO: The most likely reason to come here is two symbols mapping to + // the same address, which is common with sets of heap routines such as + // operator new[] and operator new. Is there some simple way we can let + // the user know those are both traced even though only one id is + // recorded? Again, if we had "symbol,id" in the file we could list + // both with the same id? Though then users have to handle duplicates. + NOTIFY(0, "Failed to insert hooks for %s!%s == id %d\n", mod_name, + f->name, id); + } + } + } +} + +static void +instru_funcs_module_unload(void *drcontext, const module_data_t *mod) +{ + if (drcontext == NULL || mod == NULL) + return; + for (size_t i = 0; i < func_names.entries; i++) { + func_metadata_t *f = (func_metadata_t *)drvector_get_entry(&func_names, (uint)i); + app_pc f_pc = get_pc_by_symbol(mod, f->name); + if (f_pc != NULL) { + if (drwrap_unwrap(f_pc, func_pre_hook, func_post_hook)) { + NOTIFY(1, "Removed hooks for function %s\n", f->name); + } else { + NOTIFY(1, "Failed to remove hooks for function %s\n", f->name); } } } @@ -246,7 +322,9 @@ event_thread_exit(void *drcontext) } bool -func_trace_init(func_trace_append_entry_vec_t append_entry_vec_) +func_trace_init(func_trace_append_entry_vec_t append_entry_vec_, + ssize_t (*write_file)(file_t file, const void *data, size_t count), + file_t funclist_file) { if (append_entry_vec_ == NULL) return false; @@ -260,9 +338,15 @@ func_trace_init(func_trace_append_entry_vec_t append_entry_vec_) if (funcs_str.empty()) return true; + write_file_func = write_file; + funclist_fd = funclist_file; + funcs_wrapped_lock = dr_mutex_create(); + wrap_id = 0; + auto op_values = split_by(funcs_str, funcs_str_sep); - std::set existing_ids; - if (!drvector_init(&funcs, (uint)op_values.size(), false, free_func_entry)) { + std::set existing_names; + if (!drvector_init(&func_names, (uint)op_values.size(), false, free_func_entry) || + !drvector_init(&funcs_wrapped, (uint)op_values.size(), false, free_func_entry)) { DR_ASSERT(false); goto failed; } @@ -270,37 +354,41 @@ func_trace_init(func_trace_append_entry_vec_t append_entry_vec_) for (auto &single_op_value : op_values) { auto items = split_by(single_op_value, PATTERN_SEPARATOR); - if (items.size() != 3) { + if (items.size() != 2) { NOTIFY(0, - "Warning: -record_function or -record_heap_value was not" - " passed a triplet, input=%s\n", + "Error: -record_function or -record_heap_value was not" + " passed a pair: %s\n", funcs_str.c_str()); - continue; + return false; } std::string name = items[0]; - int id = atoi(items[1].c_str()); - int arg_num = atoi(items[2].c_str()); + int arg_num = atoi(items[1].c_str()); if (name.empty()) { - NOTIFY(0, "Warning: -record_function name should not be empty"); - continue; + NOTIFY(0, "Error: -record_function name should not be empty"); + return false; } - if (existing_ids.find(id) != existing_ids.end()) { + if (existing_names.find(name) != existing_names.end()) { NOTIFY(0, - "Warning: duplicated function id in -record_function or" - " -record_heap_value, input=%s\n", - funcs_str.c_str()); + "Warning: duplicated function name %s in -record_function or" + " -record_heap_value %s\n", + name.c_str(), funcs_str.c_str()); continue; } + if (name.size() > DRMEMTRACE_MAX_FUNC_NAME_LEN - 1 /*newline*/) { + NOTIFY(0, "The function name %s should not be larger than %d\n", name.c_str(), + DRMEMTRACE_MAX_FUNC_NAME_LEN - 1); + return false; + } if (arg_num > MAX_FUNC_TRACE_ENTRY_VEC_CAP - 2) { - NOTIFY(0, "arg_num of the function %s should not be larger than %d\n", + NOTIFY(0, "The arg_num of the function %s should not be larger than %d\n", funcs_str.c_str(), MAX_FUNC_TRACE_ENTRY_VEC_CAP - 2); - continue; + return false; } - dr_log(NULL, DR_LOG_ALL, 1, "Trace func name=%s, id=%d, arg_num=%d\n", - name.c_str(), id, arg_num); - existing_ids.insert(id); - drvector_append(&funcs, create_func_metadata(name, id, arg_num)); + dr_log(NULL, DR_LOG_ALL, 1, "Trace func name=%s, arg_num=%d\n", name.c_str(), + arg_num); + existing_names.insert(name); + drvector_append(&func_names, create_func_metadata(name.c_str(), 0, arg_num)); } if (!(drsym_init(0) == DRSYM_SUCCESS)) { @@ -329,7 +417,8 @@ func_trace_init(func_trace_append_entry_vec_t append_entry_vec_) goto failed; } - if (!drmgr_register_module_load_event(instru_funcs_module_load)) { + if (!drmgr_register_module_load_event(instru_funcs_module_load) || + !drmgr_register_module_unload_event(instru_funcs_module_unload)) { DR_ASSERT(false); goto failed; } @@ -348,9 +437,11 @@ func_trace_exit() if (funcs_str.empty()) return; - if (!drvector_delete(&funcs)) + if (!drvector_delete(&funcs_wrapped) || !drvector_delete(&func_names)) DR_ASSERT(false); + dr_mutex_destroy(funcs_wrapped_lock); if (!drmgr_unregister_module_load_event(instru_funcs_module_load) || + !drmgr_unregister_module_unload_event(instru_funcs_module_unload) || !drmgr_unregister_thread_init_event(event_thread_init) || !drmgr_unregister_thread_exit_event(event_thread_exit) || !(drsym_exit() == DRSYM_SUCCESS)) diff --git a/clients/drcachesim/tracer/func_trace.h b/clients/drcachesim/tracer/func_trace.h index ab5f6b37053..65825c47369 100644 --- a/clients/drcachesim/tracer/func_trace.h +++ b/clients/drcachesim/tracer/func_trace.h @@ -1,5 +1,5 @@ /* ********************************************************** - * Copyright (c) 2016-2018 Google, Inc. All rights reserved. + * Copyright (c) 2016-2020 Google, Inc. All rights reserved. * **********************************************************/ /* @@ -65,7 +65,9 @@ typedef void (*func_trace_append_entry_vec_t)(void *, func_trace_entry_vector_t // Initializes the func_trace module. Each call must be paired with a // corresponding call to func_trace_exit(). bool -func_trace_init(func_trace_append_entry_vec_t append_entry_vec_); +func_trace_init(func_trace_append_entry_vec_t append_entry_vec_, + ssize_t (*write_file)(file_t file, const void *data, size_t count), + file_t funclist_file); // Cleans up the func_trace module void diff --git a/clients/drcachesim/tracer/tracer.cpp b/clients/drcachesim/tracer/tracer.cpp index adef3a0cb48..2c83f0d740b 100644 --- a/clients/drcachesim/tracer/tracer.cpp +++ b/clients/drcachesim/tracer/tracer.cpp @@ -95,6 +95,7 @@ DR_DISALLOW_UNSAFE_STATIC static char logsubdir[MAXIMUM_PATH]; static char subdir_prefix[MAXIMUM_PATH]; /* Holds op_subdir_prefix. */ static file_t module_file; +static file_t funclist_file; /* Max number of entries a buffer can have. It should be big enough * to hold all entries between clean calls. @@ -250,6 +251,7 @@ drmemtrace_buffer_handoff(drmemtrace_handoff_func_t handoff_func, } static char modlist_path[MAXIMUM_PATH]; +static char funclist_path[MAXIMUM_PATH]; drmemtrace_status_t drmemtrace_get_output_path(OUT const char **path) @@ -269,6 +271,15 @@ drmemtrace_get_modlist_path(OUT const char **path) return DRMEMTRACE_SUCCESS; } +drmemtrace_status_t +drmemtrace_get_funclist_path(OUT const char **path) +{ + if (path == NULL) + return DRMEMTRACE_ERROR_INVALID_PARAMETER; + *path = funclist_path; + return DRMEMTRACE_SUCCESS; +} + drmemtrace_status_t drmemtrace_custom_module_data(void *(*load_cb)(module_data_t *module), int (*print_cb)(void *data, char *dst, size_t max_len), @@ -1591,9 +1602,11 @@ event_exit(void) instru->~instru_t(); dr_global_free(instru, MAX_INSTRU_SIZE); - if (op_offline.get_value()) + if (op_offline.get_value()) { file_ops_func.close_file(module_file); - else + if (funclist_file != INVALID_FILE) + file_ops_func.close_file(funclist_file); + } else ipc_pipe.close(); if (file_ops_func.exit_cb != NULL) @@ -1686,7 +1699,14 @@ init_offline_dir(void) NULL_TERMINATE_BUFFER(modlist_path); module_file = file_ops_func.open_file( modlist_path, DR_FILE_WRITE_REQUIRE_NEW IF_UNIX(| DR_FILE_CLOSE_ON_FORK)); - return (module_file != INVALID_FILE); + + dr_snprintf(funclist_path, BUFFER_SIZE_ELEMENTS(funclist_path), "%s%s%s", logsubdir, + DIRSEP, DRMEMTRACE_FUNCTION_MAP_FILENAME); + NULL_TERMINATE_BUFFER(funclist_path); + funclist_file = file_ops_func.open_file( + funclist_path, DR_FILE_WRITE_REQUIRE_NEW IF_UNIX(| DR_FILE_CLOSE_ON_FORK)); + + return (module_file != INVALID_FILE && funclist_file != INVALID_FILE); } #ifdef UNIX @@ -1744,9 +1764,6 @@ drmemtrace_client_main(client_id_t id, int argc, const char *argv[]) FATAL("Usage error: L0I_size and L0D_size must be 0 or powers of 2."); } - if (!func_trace_init(append_marker_seg_base)) - DR_ASSERT(false); - drreg_init_and_fill_vector(&scratch_reserve_vec, true); #ifdef X86 if (op_L0_filter.get_value()) { @@ -1801,6 +1818,11 @@ drmemtrace_client_main(client_id_t id, int argc, const char *argv[]) NOTIFY(1, "Failed to maximize pipe buffer: performance may suffer.\n"); } + if (!func_trace_init(append_marker_seg_base, file_ops_func.write_file, + funclist_file)) { + FATAL("Failed to initialized function tracing.\n"); + } + /* We need an extra for -L0_filter. */ if (op_L0_filter.get_value()) ++ops.num_spill_slots;