| /* ********************************************************** |
| * Copyright (c) 2011-2014 Google, 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 Google, 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 GOOGLE, 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. |
| */ |
| |
| /* Tests the drsyms extension. Relies on the drwrap extension. */ |
| |
| #include "dr_api.h" |
| #include "drsyms.h" |
| #include "drwrap.h" |
| #include "client_tools.h" |
| |
| #include <limits.h> |
| #include <string.h> |
| |
| /* DR's build system usually disables warnings we're not interested in, but the |
| * flags don't seem to make it to the compiler for this file, maybe because |
| * it's C++. Disable them with pragmas instead. |
| */ |
| #ifdef WINDOWS |
| # pragma warning( disable : 4100 ) /* Unreferenced formal parameter. */ |
| #endif |
| |
| static void event_exit(void); |
| static void lookup_exe_syms(void); |
| static void lookup_dll_syms(void *dc, const module_data_t *dll_data, |
| bool loaded); |
| static void check_enumerate_dll_syms(const char *dll_path); |
| #ifdef UNIX |
| static void lookup_glibc_syms(void *dc, const module_data_t *dll_data); |
| #endif |
| static void test_demangle(void); |
| #ifdef WINDOWS |
| static void lookup_overloads(const char *exe_path); |
| static void lookup_templates(const char *exe_path); |
| static void lookup_type_by_name(const char *exe_path); |
| #endif |
| |
| #ifdef WINDOWS |
| static dr_os_version_info_t os_version = {sizeof(os_version),}; |
| #endif |
| |
| extern "C" DR_EXPORT void |
| dr_init(client_id_t id) |
| { |
| drsym_error_t r = drsym_init(0); |
| ASSERT(r == DRSYM_SUCCESS); |
| drwrap_init(); |
| dr_register_exit_event(event_exit); |
| |
| lookup_exe_syms(); |
| dr_register_module_load_event(lookup_dll_syms); |
| test_demangle(); |
| |
| #ifdef WINDOWS |
| if (!dr_get_os_version(&os_version)) |
| ASSERT(false); |
| #endif |
| } |
| |
| /* Count intercepted calls. */ |
| static int call_count = 0; |
| |
| static void |
| pre_func(void *wrapcxt, void **user_data) |
| { |
| call_count++; |
| } |
| |
| /* Assuming prologue has "push xbp, mov xsp -> xbp", this struct is at the base |
| * of every frame. |
| */ |
| typedef struct _frame_base_t { |
| struct _frame_base_t *parent; |
| app_pc ret_addr; |
| } frame_base_t; |
| |
| #ifdef WINDOWS |
| # define FULL_PDB_DEBUG_KIND \ |
| (DRSYM_SYMBOLS | DRSYM_LINE_NUMS | DRSYM_PDB) |
| # define FULL_PECOFF_DEBUG_KIND \ |
| (DRSYM_SYMBOLS | DRSYM_LINE_NUMS | \ |
| DRSYM_PECOFF_SYMTAB | DRSYM_DWARF_LINE) |
| #else |
| # define FULL_DEBUG_KIND \ |
| (DRSYM_SYMBOLS | DRSYM_LINE_NUMS | \ |
| DRSYM_ELF_SYMTAB | DRSYM_DWARF_LINE) |
| #endif |
| |
| static bool |
| debug_kind_is_full(drsym_debug_kind_t debug_kind) |
| { |
| return |
| #ifdef WINDOWS |
| TESTALL(FULL_PDB_DEBUG_KIND, debug_kind) || |
| TESTALL(FULL_PECOFF_DEBUG_KIND, debug_kind); |
| #else |
| TESTALL(FULL_DEBUG_KIND, debug_kind); |
| #endif |
| } |
| |
| #define MAX_FUNC_LEN 1024 |
| /* Take and symbolize a stack trace. Assumes no frame pointer omission. |
| */ |
| static void |
| pre_stack_trace(void *wrapcxt, void **user_data) |
| { |
| dr_mcontext_t *mc = drwrap_get_mcontext(wrapcxt); |
| frame_base_t inner_frame; |
| frame_base_t *frame; |
| int depth; |
| |
| /* This should use safe_read and all that, but this is a test case. */ |
| dr_fprintf(STDERR, "stack trace:\n"); |
| frame = &inner_frame; |
| /* It's impossible to get frame pointers on Win x64, so we only print one |
| * frame. |
| */ |
| #if defined(WINDOWS) && defined(X64) |
| frame->parent = NULL; |
| #else |
| frame->parent = (frame_base_t*)mc->xbp; |
| #endif |
| frame->ret_addr = *(app_pc*)mc->xsp; |
| depth = 0; |
| while (frame != NULL) { |
| drsym_error_t r; |
| module_data_t *mod; |
| drsym_info_t sym_info; |
| char name[MAX_FUNC_LEN]; |
| char file[MAXIMUM_PATH]; |
| size_t modoffs; |
| const char *basename; |
| |
| sym_info.struct_size = sizeof(sym_info); |
| sym_info.name = name; |
| sym_info.name_size = MAX_FUNC_LEN; |
| sym_info.file = file; |
| sym_info.file_size = MAXIMUM_PATH; |
| |
| mod = dr_lookup_module(frame->ret_addr); |
| modoffs = frame->ret_addr - mod->start; |
| /* gcc says the next line starts at the return address. Back up one to |
| * get the line that the call was on. |
| */ |
| modoffs--; |
| r = drsym_lookup_address(mod->full_path, modoffs, &sym_info, |
| DRSYM_DEMANGLE); |
| dr_free_module_data(mod); |
| ASSERT(r == DRSYM_SUCCESS); |
| if (!debug_kind_is_full(sym_info.debug_kind)) { |
| dr_fprintf(STDERR, "unexpected debug_kind: %x\n", |
| sym_info.debug_kind); |
| } |
| if (sym_info.file_available_size == 0) |
| basename = "/<unknown>"; |
| else { |
| basename = strrchr(sym_info.file, IF_WINDOWS_ELSE('\\', '/')); |
| #ifdef WINDOWS |
| if (basename == NULL) |
| basename = strrchr(sym_info.file, '/'); |
| #endif |
| } |
| ASSERT(basename != NULL); |
| basename++; |
| dr_fprintf(STDERR, "%s:%d!%s\n", |
| basename, (int)sym_info.line, sym_info.name); |
| |
| /* Stop after main. */ |
| if (strstr(sym_info.name, "main")) |
| break; |
| |
| frame = frame->parent; |
| depth++; |
| if (depth > 20) { |
| dr_fprintf(STDERR, "20 frames deep, stopping trace.\n"); |
| break; |
| } |
| } |
| } |
| |
| static void |
| post_func(void *wrapcxt, void *user_data) |
| { |
| } |
| |
| /* Use dr_get_proc_addr to get the exported address of a symbol. Attempt to |
| * look through any export table jumps so that we get the address for the |
| * symbol that would be returned by looking at debug information. |
| */ |
| static app_pc |
| get_real_proc_addr(module_handle_t mod_handle, const char *symbol) |
| { |
| instr_t instr; |
| app_pc next_pc = NULL; |
| app_pc export_addr = (app_pc)dr_get_proc_address(mod_handle, symbol); |
| void *dc = dr_get_current_drcontext(); |
| |
| #ifdef WINDOWS |
| /* use a symbol forwarded by DR to ntdll that's not an intrinsic to test |
| * duplicate link issues vs libcmt |
| */ |
| if (isdigit(symbol[0])) |
| next_pc = next_pc; /* avoid warning about empty statement */ |
| #endif |
| |
| instr_init(dc, &instr); |
| if (export_addr != NULL) |
| next_pc = decode(dc, export_addr, &instr); |
| if (next_pc != NULL && instr_is_ubr(&instr)) { |
| /* This is a jump to the real function entry point. */ |
| export_addr = opnd_get_pc(instr_get_target(&instr)); |
| } |
| instr_reset(dc, &instr); |
| |
| return export_addr; |
| } |
| |
| static size_t |
| lookup_and_wrap(const char *modpath, app_pc modbase, const char *modname, const |
| char *symbol, uint flags) |
| { |
| drsym_error_t r; |
| size_t modoffs = 0; |
| bool ok; |
| char lookup_str[256]; |
| |
| dr_snprintf(lookup_str, sizeof(lookup_str), "%s!%s", modname, symbol); |
| r = drsym_lookup_symbol(modpath, lookup_str, &modoffs, flags); |
| if (r != DRSYM_SUCCESS || modoffs == 0) { |
| dr_fprintf(STDERR, "Failed to lookup %s => %d\n", lookup_str, r); |
| } else { |
| ok = drwrap_wrap(modbase + modoffs, pre_func, post_func); |
| ASSERT(ok); |
| } |
| return modoffs; |
| } |
| |
| /* Lookup symbols in the exe and wrap them. */ |
| static void |
| lookup_exe_syms(void) |
| { |
| module_data_t *exe_data; |
| const char *exe_path; |
| app_pc exe_base; |
| app_pc exe_export_addr; |
| size_t exe_export_offs; |
| size_t exe_public_offs; |
| drsym_info_t unused_info; |
| drsym_error_t r; |
| drsym_debug_kind_t debug_kind; |
| const char *appname = dr_get_application_name(); |
| char appbase[MAXIMUM_PATH]; |
| size_t len; |
| |
| len = dr_snprintf(appbase, BUFFER_SIZE_ELEMENTS(appbase), "%s", appname); |
| ASSERT(len > 0); |
| #ifdef WINDOWS |
| /* blindly assuming ends in .exe */ |
| appbase[strlen(appname) - 4/*subtract .exe*/] = '\0'; |
| #endif |
| |
| exe_data = dr_lookup_module_by_name(appname); |
| ASSERT(exe_data != NULL); |
| exe_path = exe_data->full_path; |
| exe_base = exe_data->start; |
| |
| /* We expect to have full debug info for this module. */ |
| r = drsym_get_module_debug_kind(exe_path, &debug_kind); |
| ASSERT(r == DRSYM_SUCCESS); |
| if (!debug_kind_is_full(debug_kind)) { |
| dr_fprintf(STDERR, "unexpected debug_kind: %x\n", debug_kind); |
| } |
| |
| exe_export_addr = get_real_proc_addr(exe_data->handle, "exe_export"); |
| exe_export_offs = lookup_and_wrap(exe_path, exe_base, appbase, |
| "exe_export", DRSYM_DEFAULT_FLAGS); |
| ASSERT(exe_export_addr == exe_base + exe_export_offs); |
| |
| /* exe_public is a function in the exe we wouldn't be able to find without |
| * drsyms and debug info. |
| */ |
| (void)lookup_and_wrap(exe_path, exe_base, appbase, |
| "exe_public", DRSYM_DEFAULT_FLAGS); |
| |
| /* Test symbol not found error handling. */ |
| r = drsym_lookup_symbol(exe_path, "nonexistent_sym", &exe_public_offs, |
| DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_ERROR_SYMBOL_NOT_FOUND); |
| |
| /* Test invalid parameter errors. */ |
| r = drsym_lookup_symbol(NULL, "malloc", &exe_public_offs, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_ERROR_INVALID_PARAMETER); |
| r = drsym_lookup_symbol(exe_path, NULL, &exe_public_offs, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_ERROR_INVALID_PARAMETER); |
| r = drsym_enumerate_symbols(exe_path, NULL, NULL, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_ERROR_INVALID_PARAMETER); |
| r = drsym_lookup_address(NULL, 0xDEADBEEFUL, &unused_info, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_ERROR_INVALID_PARAMETER); |
| |
| #ifdef WINDOWS |
| if (TEST(DRSYM_PDB, debug_kind)) { /* else NYI */ |
| lookup_overloads(exe_path); |
| lookup_templates(exe_path); |
| /* Test drsym_get_type_by_name function */ |
| lookup_type_by_name(exe_path); |
| } |
| #endif |
| |
| dr_free_module_data(exe_data); |
| } |
| |
| #ifdef WINDOWS |
| # define NUM_OVERLOADED_CLASS 3 |
| typedef struct _overloaded_params_t { |
| const char *exe_path; |
| bool overloaded_char; |
| bool overloaded_wchar; |
| bool overloaded_int; |
| bool overloaded_void_ptr; |
| bool overloaded_void; |
| uint overloaded_class; |
| } overloaded_params_t; |
| |
| static bool |
| overloaded_cb(const char *name, size_t modoffs, void *data) |
| { |
| overloaded_params_t *p = (overloaded_params_t*)data; |
| drsym_func_type_t *func_type; |
| static char buf[4096]; |
| drsym_error_t r; |
| |
| if (strcmp(name, "overloaded") != 0) |
| return true; |
| r = drsym_get_func_type(p->exe_path, modoffs, buf, sizeof(buf), &func_type); |
| if (r != DRSYM_SUCCESS) { |
| dr_fprintf(STDERR, "drsym_get_func_type failed: %d\n", (int)r); |
| return true; |
| } |
| if (func_type->num_args == 1 && |
| func_type->arg_types[0]->kind == DRSYM_TYPE_PTR) { |
| drsym_ptr_type_t *arg_type = (drsym_ptr_type_t*)func_type->arg_types[0]; |
| size_t arg_int_size = arg_type->elt_type->size; |
| if (arg_type->elt_type->kind == DRSYM_TYPE_INT) { |
| switch (arg_int_size) { |
| case 1: p->overloaded_char = true; break; |
| case 2: p->overloaded_wchar = true; break; |
| case 4: p->overloaded_int = true; break; |
| default: break; |
| } |
| } else if (arg_type->elt_type->kind == DRSYM_TYPE_VOID) { |
| p->overloaded_void_ptr = true; |
| } else if (arg_type->elt_type->kind == DRSYM_TYPE_COMPOUND) { |
| drsym_compound_type_t *ctype = (drsym_compound_type_t *) arg_type->elt_type; |
| int i; |
| ASSERT(ctype->field_types == NULL); /* drsym_get_func_type does not expand */ |
| p->overloaded_class++; |
| dr_fprintf(STDERR, "compound arg %s has %d field(s), size %d\n", |
| ctype->name, ctype->num_fields, ctype->type.size); |
| r = drsym_expand_type(p->exe_path, ctype->type.id, UINT_MAX, |
| buf, sizeof(buf), (drsym_type_t **)&ctype); |
| if (r != DRSYM_SUCCESS) { |
| dr_fprintf(STDERR, "drsym_expand_type failed: %d\n", (int)r); |
| } else { |
| ASSERT(ctype->type.kind == DRSYM_TYPE_COMPOUND); |
| for (i=0; i < ctype->num_fields; i++) { |
| dr_fprintf(STDERR, " class field %d is type %d and size %d\n", |
| i, ctype->field_types[i]->kind, |
| ctype->field_types[i]->size); |
| if (ctype->field_types[i]->kind == DRSYM_TYPE_FUNC) { |
| drsym_func_type_t *ftype = (drsym_func_type_t *) |
| ctype->field_types[i]; |
| dr_fprintf(STDERR, " func has %d args\n", ftype->num_args); |
| for (i=0; i < ftype->num_args; i++) { |
| dr_fprintf(STDERR, " arg %d is type %d and size %d\n", |
| i, ftype->arg_types[i]->kind, |
| ftype->arg_types[i]->size); |
| } |
| } |
| } |
| } |
| } else { |
| dr_fprintf(STDERR, "overloaded() arg has unexpected type!\n"); |
| } |
| } else if (func_type->num_args == 0) { |
| /* no arg so not really an overload, but we need to test no-arg func */ |
| p->overloaded_void = true; |
| } else { |
| dr_fprintf(STDERR, "overloaded() has unexpected args\n"); |
| } |
| |
| return true; |
| } |
| |
| static void |
| lookup_overloads(const char *exe_path) |
| { |
| overloaded_params_t p; |
| drsym_error_t r; |
| |
| p.exe_path = exe_path; |
| p.overloaded_char = false; |
| p.overloaded_wchar = false; |
| p.overloaded_int = false; |
| p.overloaded_void = false; |
| p.overloaded_void_ptr = false; |
| p.overloaded_class = 0; |
| r = drsym_enumerate_symbols(exe_path, overloaded_cb, &p, |
| DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_SUCCESS); |
| if (!p.overloaded_char) dr_fprintf(STDERR, "overloaded_char missing!\n"); |
| if (!p.overloaded_wchar) dr_fprintf(STDERR, "overloaded_wchar missing!\n"); |
| if (!p.overloaded_int) dr_fprintf(STDERR, "overloaded_int missing!\n"); |
| if (!p.overloaded_void) dr_fprintf(STDERR, "overloaded_void missing!\n"); |
| if (!p.overloaded_void_ptr) dr_fprintf(STDERR, "overloaded_void_ptr missing!\n"); |
| if (p.overloaded_class != NUM_OVERLOADED_CLASS) |
| dr_fprintf(STDERR, "overloaded_class missing!\n"); |
| if (p.overloaded_char && |
| p.overloaded_wchar && |
| p.overloaded_int && |
| p.overloaded_void && |
| p.overloaded_void_ptr && |
| p.overloaded_class == NUM_OVERLOADED_CLASS) { |
| dr_fprintf(STDERR, "found all overloads\n"); |
| } |
| } |
| |
| static bool |
| search_templates_cb(const char *name, size_t modoffs, void *data) |
| { |
| /* See below about i#1376 and unnamed-tag */ |
| if (strstr(name, "::templated_func") != NULL) |
| dr_fprintf(STDERR, "found %s\n", name); |
| return true; |
| } |
| |
| static bool |
| search_ex_templates_cb(drsym_info_t *out, drsym_error_t status, void *data) |
| { |
| /* i#1376: VS2013 PDB seems to not have qualified unnamed-tag entries so |
| * in the interests of cross-platform non-flaky tests we don't |
| * print them out anymore. We're talking about this: |
| * name_outer::name_middle::name_inner::sample_class<char>::nested_class<int>::<unnamed-tag> |
| */ |
| if (strstr(out->name, "::templated_func") != NULL) |
| dr_fprintf(STDERR, "found %s\n", out->name); |
| return true; |
| } |
| |
| static void |
| lookup_templates(const char *exe_path) |
| { |
| /* These should collapse the templates */ |
| drsym_error_t r = |
| drsym_search_symbols(exe_path, "*!*nested*", true, search_templates_cb, NULL); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_search_symbols_ex(exe_path, "*!*nested*", DRSYM_FULL_SEARCH|DRSYM_DEMANGLE, |
| search_ex_templates_cb, sizeof(drsym_info_t), NULL); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_enumerate_symbols(exe_path, search_templates_cb, NULL, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_enumerate_symbols_ex(exe_path, search_ex_templates_cb, sizeof(drsym_info_t), |
| NULL, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_SUCCESS); |
| /* These should expand the templates */ |
| r = drsym_search_symbols_ex(exe_path, "*!*nested*", |
| DRSYM_FULL_SEARCH | DRSYM_DEMANGLE | |
| DRSYM_DEMANGLE_PDB_TEMPLATES, |
| search_ex_templates_cb, sizeof(drsym_info_t), NULL); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_enumerate_symbols(exe_path, search_templates_cb, NULL, |
| DRSYM_DEMANGLE|DRSYM_DEMANGLE_PDB_TEMPLATES); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_enumerate_symbols_ex(exe_path, search_ex_templates_cb, sizeof(drsym_info_t), |
| NULL, DRSYM_DEMANGLE|DRSYM_DEMANGLE_PDB_TEMPLATES); |
| ASSERT(r == DRSYM_SUCCESS); |
| } |
| |
| /* This routine assumes it's called only at init time. */ |
| static void |
| lookup_type_by_name(const char *exe_path) |
| { |
| drsym_type_t *type; |
| drsym_error_t r; |
| static char buf[4096]; |
| /* It should successfully return valid type info */ |
| r = drsym_get_type_by_name(exe_path, "`anonymous-namespace'::HasFields", |
| buf, BUFFER_SIZE_BYTES(buf), &type); |
| ASSERT(r == DRSYM_SUCCESS); |
| dr_fprintf(STDERR, "drsym_get_type_by_name successfully found HasFields type\n"); |
| } |
| #endif /* WINDOWS */ |
| |
| static bool |
| enum_line_cb(drsym_line_info_t *info, void *data) |
| { |
| static bool found_tools_h, found_appdll; |
| const module_data_t *dll_data = (const module_data_t *) data; |
| ASSERT(info->line_addr <= (size_t)(dll_data->end - dll_data->start)); |
| if (info->file != NULL) { |
| if (!found_tools_h && strstr(info->file, "tools.h") != NULL) { |
| found_tools_h = true; |
| dr_fprintf(STDERR, "found tools.h\n"); |
| } |
| if (!found_appdll && strstr(info->file, "drsyms-test.appdll.cpp") != NULL) { |
| found_appdll = true; |
| dr_fprintf(STDERR, "found drsyms-test.appdll.cpp\n"); |
| } |
| } |
| return true; |
| } |
| |
| static void |
| test_line_iteration(const module_data_t *dll_data) |
| { |
| drsym_error_t res = drsym_enumerate_lines(dll_data->full_path, enum_line_cb, |
| (void *) dll_data); |
| ASSERT(res == DRSYM_SUCCESS); |
| } |
| |
| /* Lookup symbols in the appdll and wrap them. */ |
| static void |
| lookup_dll_syms(void *dc, const module_data_t *dll_data, bool loaded) |
| { |
| const char *dll_path; |
| app_pc dll_base; |
| app_pc dll_export_addr; |
| size_t dll_export_offs; |
| size_t stack_trace_offs; |
| drsym_error_t r; |
| bool ok; |
| drsym_debug_kind_t debug_kind; |
| const char *dll_name = dr_module_preferred_name(dll_data); |
| char base_name[MAXIMUM_PATH]; |
| size_t len; |
| |
| dll_path = dll_data->full_path; |
| dll_base = dll_data->start; |
| |
| #ifdef UNIX |
| if (strstr(dll_path, "/libc-")) { |
| lookup_glibc_syms(dc, dll_data); |
| return; |
| } |
| #endif |
| |
| /* Avoid running on any module other than the appdll. */ |
| if (!strstr(dll_path, "appdll")) |
| return; |
| |
| len = dr_snprintf(base_name, BUFFER_SIZE_ELEMENTS(base_name), "%s", dll_name); |
| ASSERT(len > 0); |
| #ifdef WINDOWS |
| /* blindly assuming ends in .dll */ |
| base_name[strlen(dll_name) - 4/*subtract .dll*/] = '\0'; |
| #else |
| /* blindly assuming ends in .so */ |
| base_name[strlen(dll_name) - 3/*subtract .so*/] = '\0'; |
| #endif |
| |
| /* We expect to have full debug info for this module. */ |
| r = drsym_get_module_debug_kind(dll_path, &debug_kind); |
| ASSERT(r == DRSYM_SUCCESS); |
| if (!debug_kind_is_full(debug_kind)) { |
| dr_fprintf(STDERR, "unexpected debug_kind: %x\n", debug_kind); |
| } |
| |
| dll_export_addr = get_real_proc_addr(dll_data->handle, "dll_export"); |
| dll_export_offs = lookup_and_wrap(dll_path, dll_base, base_name, |
| "dll_export", DRSYM_DEFAULT_FLAGS); |
| ASSERT(dll_export_addr == dll_base + dll_export_offs); |
| |
| /* dll_public is a function in the dll we wouldn't be able to find without |
| * drsyms and debug info. |
| */ |
| (void)lookup_and_wrap(dll_path, dll_base, base_name, |
| "dll_public", DRSYM_DEFAULT_FLAGS); |
| |
| /* stack_trace is a static function in the DLL that we use to get PCs of all |
| * the functions we've looked up so far. |
| */ |
| dr_snprintf(base_name + strlen(base_name), |
| BUFFER_SIZE_ELEMENTS(base_name) - strlen(base_name), "!stack_trace"); |
| r = drsym_lookup_symbol(dll_path, base_name, &stack_trace_offs, DRSYM_DEFAULT_FLAGS); |
| ASSERT(r == DRSYM_SUCCESS); |
| ok = drwrap_wrap(dll_base + stack_trace_offs, pre_stack_trace, post_func); |
| ASSERT(ok); |
| |
| check_enumerate_dll_syms(dll_path); |
| |
| test_line_iteration(dll_data); |
| |
| drsym_free_resources(dll_path); |
| } |
| |
| static const char *dll_syms[] = { |
| "dll_export", |
| "dll_static", |
| "dll_public", |
| "stack_trace", |
| NULL |
| }; |
| |
| /* FIXME: We don't support getting mangled or fully demangled symbols on |
| * Windows PDB. |
| */ |
| static const char *dll_syms_mangled_pdb[] = { |
| "dll_export", |
| "dll_static", |
| "dll_public", |
| "stack_trace", |
| NULL |
| |
| }; |
| |
| static const char *dll_syms_mangled[] = { |
| "dll_export", |
| "_ZL10dll_statici", |
| "_Z10dll_publici", |
| "_Z11stack_tracev", |
| NULL |
| }; |
| |
| static const char *dll_syms_short_pdb[] = { |
| "dll_export", |
| "dll_static", |
| "dll_public", |
| "stack_trace", |
| NULL |
| }; |
| |
| static const char *dll_syms_short[] = { |
| "dll_export", |
| "dll_static", |
| "dll_public", |
| "stack_trace", |
| NULL |
| }; |
| |
| static const char *dll_syms_full_pdb[] = { |
| "dll_export", |
| "dll_static", |
| "dll_public", |
| "stack_trace", |
| NULL |
| }; |
| |
| static const char *dll_syms_full[] = { |
| "dll_export", |
| "dll_static(int)", |
| "dll_public(int)", |
| "stack_trace(void)", |
| NULL |
| }; |
| |
| typedef struct { |
| bool syms_found[BUFFER_SIZE_ELEMENTS(dll_syms) - 1]; |
| const char **syms_expected; |
| const char *dll_path; |
| uint flags_expected; |
| char prev_name[MAXIMUM_PATH]; |
| } dll_syms_found_t; |
| |
| /* If this was a symbol we expected that we haven't found yet, mark it found, |
| * and check the mangling to see if it matches our expected mangling. |
| */ |
| static bool |
| enum_sym_cb(const char *name, size_t modoffs, void *data) |
| { |
| dll_syms_found_t *syms_found = (dll_syms_found_t*)data; |
| uint i; |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found->syms_found); i++) { |
| if (syms_found->syms_found[i]) |
| continue; |
| if (strstr(name, dll_syms[i]) != NULL) { |
| syms_found->syms_found[i] = true; |
| const char *expected = syms_found->syms_expected[i]; |
| char alternative[MAX_FUNC_LEN] = "\xff"; /* invalid string */ |
| /* If the expected mangling is _ZL*, try _Z* too. Different gccs |
| * from Cyginw, MinGW, and Linux all do different things. |
| */ |
| if (strncmp(expected, "_ZL", 3) == 0) { |
| dr_snprintf(alternative, BUFFER_SIZE_ELEMENTS(alternative), |
| "%.2s%s", expected, expected+3); |
| NULL_TERMINATE_BUFFER(alternative); |
| } |
| if (strcmp(name, expected) != 0 && |
| strcmp(name, alternative) != 0) { |
| dr_fprintf(STDERR, "symbol had wrong mangling:\n" |
| " expected: %s\n actual: %s\n", |
| syms_found->syms_expected[i], name); |
| } |
| } |
| } |
| return true; |
| } |
| |
| static bool |
| enum_sym_ex_cb(drsym_info_t *out, drsym_error_t status, void *data) |
| { |
| dll_syms_found_t *syms_found = (dll_syms_found_t *) data; |
| uint i; |
| |
| ASSERT(status == DRSYM_ERROR_LINE_NOT_AVAILABLE); |
| /* XXX: heads up that dbghelp that comes with DTFW 6.3 has the |
| * available size as one larger! We assume nobody is using that. |
| */ |
| ASSERT(strlen(out->name) == out->name_available_size); |
| ASSERT((out->file == NULL && out->file_available_size == 0) || |
| (strlen(out->file) == out->file_available_size)); |
| |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found->syms_found); i++) { |
| if (syms_found->syms_found[i]) |
| continue; |
| if (strstr(out->name, dll_syms[i]) != NULL) |
| syms_found->syms_found[i] = true; |
| } |
| |
| ASSERT(out->flags == syms_found->flags_expected |
| IF_WINDOWS(|| syms_found->flags_expected == DRSYM_LEAVE_MANGLED |
| || (out->flags == |
| (syms_found->flags_expected & ~DRSYM_DEMANGLE_FULL)))); |
| |
| if (TEST(DRSYM_PDB, out->debug_kind)) { /* else types NYI */ |
| static char buf[4096]; |
| drsym_type_t *type; |
| drsym_error_t r = drsym_get_type(syms_found->dll_path, out->start_offs, 1, |
| buf, sizeof(buf), &type); |
| if (r == DRSYM_SUCCESS) { |
| /* XXX: I'm seeing error 126 (ERROR_MOD_NOT_FOUND) from |
| * SymFromAddr for some symbols that the enum finds: strange. |
| * On another machine I saw mismatches in type id: |
| * error for __initiallocinfo: 481 != 483, kind = 5 |
| * Grrr! Do we really have to go and compare all the properties |
| * of the type to ensure it's the same?!? |
| * |
| * Plus, with VS2008 dbghelp we get a lot of even worse mismatches |
| * here (part of i#1196). We only use it on pre-Vista so we relax this |
| * check there. |
| */ |
| ASSERT(IF_WINDOWS(os_version.version < DR_WINDOWS_VERSION_VISTA ||) |
| type->id == out->type_id || |
| /* Unknown type has id cleared to 0 */ |
| (type->kind == DRSYM_TYPE_OTHER && type->id == 0) || |
| /* Some __ types seem to have varying id's */ |
| strstr(out->name, "__") == out->name || |
| /* i#1376: if we use a recent dbghelp.dll, |
| * we see weird duplicate names w/ different ids |
| */ |
| strcmp(out->name, syms_found->prev_name) == 0); |
| } |
| } |
| dr_snprintf(syms_found->prev_name, BUFFER_SIZE_ELEMENTS(syms_found->prev_name), |
| "%s", out->name); |
| NULL_TERMINATE_BUFFER(syms_found->prev_name); |
| return true; |
| } |
| |
| static void |
| enum_syms_with_flags(const char *dll_path, const char **syms_expected, |
| uint flags) |
| { |
| dll_syms_found_t syms_found; |
| drsym_error_t r; |
| uint i; |
| drsym_debug_kind_t debug_kind; |
| r = drsym_get_module_debug_kind(dll_path, &debug_kind); |
| ASSERT(r == DRSYM_SUCCESS); |
| |
| memset(&syms_found, 0, sizeof(syms_found)); |
| syms_found.syms_expected = syms_expected; |
| r = drsym_enumerate_symbols(dll_path, enum_sym_cb, &syms_found, flags); |
| ASSERT(r == DRSYM_SUCCESS); |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found.syms_found); i++) { |
| if (!syms_found.syms_found[i]) |
| dr_fprintf(STDERR, "failed to find symbol for %s!\n", dll_syms[i]); |
| } |
| |
| /* Test the _ex version */ |
| memset(&syms_found, 0, sizeof(syms_found)); |
| syms_found.dll_path = dll_path; |
| syms_found.flags_expected = flags; |
| r = drsym_enumerate_symbols_ex(dll_path, enum_sym_ex_cb, |
| sizeof(drsym_info_t), &syms_found, flags); |
| ASSERT(r == DRSYM_SUCCESS); |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found.syms_found); i++) { |
| if (!syms_found.syms_found[i]) |
| dr_fprintf(STDERR, "_ex failed to find symbol for %s!\n", dll_syms[i]); |
| } |
| |
| #ifdef WINDOWS |
| if (TEST(DRSYM_PDB, debug_kind)) { |
| /* drsym_search_symbols should find the same symbols with the short |
| * mangling, regardless of the flags used by the previous enumerations. |
| */ |
| memset(&syms_found, 0, sizeof(syms_found)); |
| syms_found.syms_expected = dll_syms_short_pdb; |
| r = drsym_search_symbols(dll_path, "*!*dll_*", false, enum_sym_cb, |
| &syms_found); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_search_symbols(dll_path, "*!*stack_trace*", false, enum_sym_cb, |
| &syms_found); |
| ASSERT(r == DRSYM_SUCCESS); |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found.syms_found); i++) { |
| if (!syms_found.syms_found[i]) |
| dr_fprintf(STDERR, "search failed to find %s!\n", dll_syms[i]); |
| } |
| |
| /* Test the _ex version */ |
| memset(&syms_found, 0, sizeof(syms_found)); |
| syms_found.dll_path = dll_path; |
| r = drsym_search_symbols_ex(dll_path, "*!*dll_*", DRSYM_DEMANGLE, enum_sym_ex_cb, |
| sizeof(drsym_info_t), &syms_found); |
| ASSERT(r == DRSYM_SUCCESS); |
| r = drsym_search_symbols_ex(dll_path, "*!*stack_trace*", DRSYM_DEMANGLE, |
| enum_sym_ex_cb, sizeof(drsym_info_t), &syms_found); |
| ASSERT(r == DRSYM_SUCCESS); |
| for (i = 0; i < BUFFER_SIZE_ELEMENTS(syms_found.syms_found); i++) { |
| if (!syms_found.syms_found[i]) |
| dr_fprintf(STDERR, "search _ex failed to find %s!\n", dll_syms[i]); |
| } |
| } |
| #endif |
| } |
| |
| |
| /* Enumerate all symbols in the dll and verify that we at least find the ones |
| * we expected to be there, and that DRSYM_LEAVE_MANGLED was respected. |
| */ |
| static void |
| check_enumerate_dll_syms(const char *dll_path) |
| { |
| drsym_debug_kind_t debug_kind; |
| drsym_error_t r = drsym_get_module_debug_kind(dll_path, &debug_kind); |
| ASSERT(r == DRSYM_SUCCESS); |
| |
| dr_fprintf(STDERR, "enumerating with DRSYM_LEAVE_MANGLED\n"); |
| enum_syms_with_flags(dll_path, TEST(DRSYM_PDB, debug_kind) ? dll_syms_mangled_pdb : |
| dll_syms_mangled, DRSYM_LEAVE_MANGLED); |
| dr_fprintf(STDERR, "enumerating with DRSYM_DEMANGLE\n"); |
| enum_syms_with_flags(dll_path, TEST(DRSYM_PDB, debug_kind) ? dll_syms_short_pdb : |
| dll_syms_short, DRSYM_DEMANGLE); |
| dr_fprintf(STDERR, "enumerating with DRSYM_DEMANGLE_FULL\n"); |
| enum_syms_with_flags(dll_path, TEST(DRSYM_PDB, debug_kind) ? dll_syms_full_pdb : |
| dll_syms_full, (DRSYM_DEMANGLE|DRSYM_DEMANGLE_FULL)); |
| } |
| |
| #ifdef UNIX |
| /* Test if we can look up glibc symbols. This only works if the user is using |
| * glibc (and not some other libc) and is has debug info installed for it, so we |
| * avoid making assertions if we can't find the symbols. The purpose of this |
| * test is really to see if we can follow the .gnu_debuglink section into |
| * /usr/lib/debug/$mod_dir/$debuglink. |
| */ |
| static void |
| lookup_glibc_syms(void *dc, const module_data_t *dll_data) |
| { |
| const char *libc_path; |
| app_pc libc_base; |
| size_t malloc_offs; |
| size_t gi_malloc_offs; |
| drsym_error_t r; |
| |
| /* i#479: DR loads a private copy of libc. The result should be the same |
| * both times, so avoid running twice. |
| */ |
| static bool already_called = false; |
| if (already_called) { |
| return; |
| } |
| already_called = true; |
| |
| libc_path = dll_data->full_path; |
| libc_base = dll_data->start; |
| |
| /* FIXME: When drsyms can read .dynsym we should always find malloc. */ |
| malloc_offs = 0; |
| r = drsym_lookup_symbol(libc_path, "libc!malloc", &malloc_offs, |
| DRSYM_DEFAULT_FLAGS); |
| if (r == DRSYM_SUCCESS) |
| ASSERT(malloc_offs != 0); |
| |
| /* __GI___libc_malloc is glibc's internal reference to malloc. They use |
| * these internal symbols so that glibc calls to exported functions are |
| * never pre-empted by other libraries. |
| */ |
| gi_malloc_offs = 0; |
| r = drsym_lookup_symbol(libc_path, "libc!__GI___libc_malloc", |
| &gi_malloc_offs, DRSYM_DEFAULT_FLAGS); |
| /* We can't compare the offsets because the exported offset and internal |
| * offset are probably going to be different. |
| */ |
| if (r == DRSYM_SUCCESS) |
| ASSERT(gi_malloc_offs != 0); |
| |
| if (malloc_offs != 0 && gi_malloc_offs != 0) { |
| dr_fprintf(STDERR, "found glibc malloc and __GI___libc_malloc.\n"); |
| } else { |
| dr_fprintf(STDERR, "couldn't find glibc malloc or __GI___libc_malloc.\n"); |
| } |
| } |
| #endif /* UNIX */ |
| |
| typedef struct { |
| const char *mangled; |
| const char *dem_full; |
| const char *demangled; |
| } cpp_name_t; |
| |
| /* Table of mangled and unmangled symbols taken as a random sample from a |
| * 32-bit Linux Chromium binary. |
| */ |
| static cpp_name_t symbols_unix[] = { |
| {"_ZN4baseL9kDeadTaskE", |
| "base::kDeadTask", |
| "base::kDeadTask"}, |
| {"xmlRelaxNGParseImportRefs", |
| "xmlRelaxNGParseImportRefs", |
| "xmlRelaxNGParseImportRefs"}, |
| {"_ZL16piOverFourDouble", |
| "piOverFourDouble", |
| "piOverFourDouble"}, |
| {"_ZL8kint8min", |
| "kint8min", |
| "kint8min"}, |
| {"_ZZN7WebCore19SVGAnimatedProperty20LookupOrCreateHelperINS_32SVGAnimatedStaticPropertyTearOffIbEEbLb1EE21lookupOrCreateWrapperEPNS_10SVGElementEPKNS_15SVGPropertyInfoERbE19__PRETTY_FUNCTION__", |
| "WebCore::SVGAnimatedProperty::LookupOrCreateHelper<WebCore::SVGAnimatedStaticPropertyTearOff<bool>, bool, true, E>::lookupOrCreateWrapper(WebCore::SVGElement*, WebCore::SVGPropertyInfo const*, bool&)::__PRETTY_FUNCTION__", |
| "WebCore::SVGAnimatedProperty::LookupOrCreateHelper<>::lookupOrCreateWrapper::__PRETTY_FUNCTION__"}, |
| {"_ZL26GrNextArrayAllocationCounti", |
| "GrNextArrayAllocationCount(int)", |
| "GrNextArrayAllocationCount"}, |
| {"_ZN18safe_browsing_util25GeneratePhishingReportUrlERKSsS1_b", |
| "safe_browsing_util::GeneratePhishingReportUrl(std::string const&, std::string, bool)", |
| "safe_browsing_util::GeneratePhishingReportUrl"}, |
| {"_ZN9__gnu_cxx8hash_mapIjPN10disk_cache9EntryImplENS_4hashIjEESt8equal_toIjESaIS3_EE4findERKj", |
| "__gnu_cxx::hash_map<unsigned int, disk_cache::EntryImpl*, __gnu_cxx::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<disk_cache::EntryImpl*> >::find(unsigned int const&)", |
| "__gnu_cxx::hash_map<>::find"}, |
| {"_ZN18shortcuts_provider8ShortcutC2ERKSbItN4base20string16_char_traitsESaItEERK4GURLS6_RKSt6vectorIN17AutocompleteMatch21ACMatchClassificationESaISC_EES6_SG_", |
| "shortcuts_provider::Shortcut::Shortcut(std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const&, GURL const&, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const, std::vector<AutocompleteMatch::ACMatchClassification, std::allocator<AutocompleteMatch> > const&, std::basic_string<unsigned short, base::string16_char_traits, std::allocator<unsigned short> > const, std::vector<AutocompleteMatch::ACMatchClassification, std::allocator<AutocompleteMatch> > const)", |
| "shortcuts_provider::Shortcut::Shortcut"}, |
| /* FIXME: We should teach libelftc how to demangle this. */ |
| {"_ZN10linked_ptrIN12CrxInstaller14WhitelistEntryEE4copyIS1_EEvPKS_IT_E", |
| "_ZN10linked_ptrIN12CrxInstaller14WhitelistEntryEE4copyIS1_EEvPKS_IT_E", |
| "linked_ptr<>::copy<>"}, |
| {"_ZN27ScopedRunnableMethodFactoryIN6webkit5ppapi18PPB_Scrollbar_ImplEED1Ev", |
| "ScopedRunnableMethodFactory<webkit::ppapi::PPB_Scrollbar_Impl>::~ScopedRunnableMethodFactory(void)", |
| "ScopedRunnableMethodFactory<>::~ScopedRunnableMethodFactory"}, |
| {"_ZN2v88internal9HashTableINS0_21StringDictionaryShapeEPNS0_6StringEE9FindEntryEPNS0_7IsolateES4_", |
| "v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>::FindEntry(v8::internal::Isolate*, v8::internal::HashTable<v8::internal::StringDictionaryShape, v8::internal::String*>)", |
| "v8::internal::HashTable<>::FindEntry"}, |
| {"_ZNK7WebCore8Settings19localStorageEnabledEv", |
| "WebCore::Settings::localStorageEnabled(void) const", |
| "WebCore::Settings::localStorageEnabled"}, |
| {"_ZNSt4listIPN5media12VideoCapture12EventHandlerESaIS3_EE14_M_create_nodeERKS3_", |
| "std::list<media::VideoCapture::EventHandler*, std::allocator<media::VideoCapture::EventHandler*> >::_M_create_node(media::VideoCapture::EventHandler* const&)", |
| "std::list<>::_M_create_node"}, |
| {"_ZNK9__gnu_cxx13new_allocatorISt13_Rb_tree_nodeISt4pairIKiP20RenderWidgetHostViewEEE8max_sizeEv", |
| "__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<int const, RenderWidgetHostView*> >>::max_size(void) const", |
| "__gnu_cxx::new_allocator<>::max_size"}, |
| }; |
| |
| #ifdef WINDOWS |
| static cpp_name_t symbols_pdb[] = { |
| {"?synchronizeRequiredExtensions@SVGSVGElement@WebCore@@EAEXXZ", |
| "WebCore::SVGSVGElement::synchronizeRequiredExtensions(void)", |
| "WebCore::SVGSVGElement::synchronizeRequiredExtensions"}, |
| {"??$?0$04@WebString@WebKit@@QAE@AAY04$$CBD@Z", |
| "WebKit::WebString::WebString<5>(char const (&)[5])", |
| "WebKit::WebString::WebString<>"}, |
| {"?createParser@PluginDocument@WebCore@@EAE?AV?$PassRefPtr@VDocumentParser@WebCore@@@WTF@@XZ", |
| "WebCore::PluginDocument::createParser(void)", |
| "WebCore::PluginDocument::createParser"}, |
| {"?_Compat@?$_Vector_const_iterator@V?$_Iterator@$00@?$list@U?$pair@$$CBHPAVWebIDBCursor@WebKit@@@std@@V?$allocator@U?$pair@$$CBHPAVWebIDBCursor@WebKit@@@std@@@2@@std@@V?$allocator@V?$_Iterator@$00@?$list@U?$pair@$$CBHPAVWebIDBCursor@WebKit@@@std@@V?$allocator@U?$pair@$$CBHPAVWebIDBCursor@WebKit@@@std@@@2@@std@@@3@@std@@QBEXABV12@@Z", |
| "std::_Vector_const_iterator<class std::list<struct std::pair<int const ,class WebKit::WebIDBCursor *>,class std::allocator<struct std::pair<int const ,class WebKit::WebIDBCursor *> > >::_Iterator<1>,class std::allocator<class std::list<struct std::pair<int const ,class WebKit::WebIDBCursor *>,class std::allocator<struct std::pair<int const ,class WebKit::WebIDBCursor *> > >::_Iterator<1> > >::_Compat(class std::_Vector_const_iterator<class std::list<struct std::pair<int const ,class WebKit::WebIDBCursor *>,class std::allocator<struct std::pair<int const ,class WebKit::WebIDBCursor *> > >::_Iterator<1>,class std::allocator<class std::list<struct std::pair<int const ,class WebKit::WebIDBCursor *>,class std::allocator<struct std::pair<int const ,class WebKit::WebIDBCursor *> > >::_Iterator<1> > > const &)const ", |
| "std::_Vector_const_iterator<>::_Compat"}, |
| {"??$MatchAndExplain@VNotificationDetails@@@?$PropertyMatcher@V?$Details@$$CBVAutofillCreditCardChange@@@@PBVAutofillCreditCardChange@@@internal@testing@@QBE_NABVNotificationDetails@@PAVMatchResultListener@2@@Z", |
| "testing::internal::PropertyMatcher<class Details<class AutofillCreditCardChange const >,class AutofillCreditCardChange const *>::MatchAndExplain<class NotificationDetails>(class NotificationDetails const &,class testing::MatchResultListener *)const ", |
| "testing::internal::PropertyMatcher<>::MatchAndExplain<>"}, |
| {"?MD5Sum@base@@YAXPBXIPAUMD5Digest@1@@Z", |
| "base::MD5Sum(void const *,unsigned int,struct base::MD5Digest *)", |
| "base::MD5Sum"}, |
| {"?create@EntryCallbacks@WebCore@@SA?AV?$PassOwnPtr@VEntryCallbacks@WebCore@@@WTF@@V?$PassRefPtr@VEntryCallback@WebCore@@@4@V?$PassRefPtr@VErrorCallback@WebCore@@@4@V?$PassRefPtr@VDOMFileSystemBase@WebCore@@@4@ABVString@4@_N@Z", |
| "WebCore::EntryCallbacks::create(class WTF::PassRefPtr<class WebCore::EntryCallback>,class WTF::PassRefPtr<class WebCore::ErrorCallback>,class WTF::PassRefPtr<class WebCore::DOMFileSystemBase>,class WTF::String const &,bool)", |
| "WebCore::EntryCallbacks::create"}, |
| {"?ReadReplyParam@ClipboardHostMsg_ReadAsciiText@@SA_NPBVMessage@IPC@@PAU?$Tuple1@V?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@std@@@@@Z", |
| "ClipboardHostMsg_ReadAsciiText::ReadReplyParam(class IPC::Message const *,struct Tuple1<class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > > *)", |
| "ClipboardHostMsg_ReadAsciiText::ReadReplyParam"}, |
| {"?begin@?$HashMap@PAVValue@v8@@PAVGlobalHandleInfo@WebCore@@U?$PtrHash@PAVValue@v8@@@WTF@@U?$HashTraits@PAVValue@v8@@@6@U?$HashTraits@PAVGlobalHandleInfo@WebCore@@@6@@WTF@@QAE?AU?$HashTableIteratorAdapter@V?$HashTable@PAVValue@v8@@U?$pair@PAVValue@v8@@PAVGlobalHandleInfo@WebCore@@@std@@U?$PairFirstExtractor@U?$pair@PAVValue@v8@@PAVGlobalHandleInfo@WebCore@@@std@@@WTF@@U?$PtrHash@PAVValue@v8@@@6@U?$PairHashTraits@U?$HashTraits@PAVValue@v8@@@WTF@@U?$HashTraits@PAVGlobalHandleInfo@WebCore@@@2@@6@U?$HashTraits@PAVValue@v8@@@6@@WTF@@U?$pair@PAVValue@v8@@PAVGlobalHandleInfo@WebCore@@@std@@@2@XZ", |
| "WTF::HashMap<class v8::Value *,class WebCore::GlobalHandleInfo *,struct WTF::PtrHash<class v8::Value *>,struct WTF::HashTraits<class v8::Value *>,struct WTF::HashTraits<class WebCore::GlobalHandleInfo *> >::begin(void)", |
| "WTF::HashMap<>::begin"}, |
| {"??D?$_Deque_iterator@V?$linked_ptr@V?$CallbackRunner@U?$Tuple1@H@@@@@@V?$allocator@V?$linked_ptr@V?$CallbackRunner@U?$Tuple1@H@@@@@@@std@@$00@std@@QBEAAV?$linked_ptr@V?$CallbackRunner@U?$Tuple1@H@@@@@@XZ", |
| "std::_Deque_iterator<class linked_ptr<class CallbackRunner<struct Tuple1<int> > >,class std::allocator<class linked_ptr<class CallbackRunner<struct Tuple1<int> > > >,1>::operator*(void)const ", |
| "std::_Deque_iterator<>::operator*"}, |
| {"??$PerformAction@$$A6AXABVFilePath@@0PBVDictionaryValue@base@@PBVExtension@@@Z@?$ActionResultHolder@X@internal@testing@@SAPAV012@ABV?$Action@$$A6AXABVFilePath@@0PBVDictionaryValue@base@@PBVExtension@@@Z@2@ABV?$tuple@ABVFilePath@@ABV1@PBVDictionaryValue@base@@PBVExtension@@XXXXXX@tr1@std@@@Z", |
| "testing::internal::ActionResultHolder<void>::PerformAction<void (class FilePath const &,class FilePath const &,class base::DictionaryValue const *,class Extension const *)>(class testing::Action<void (class FilePath const &,class FilePath const &,class base::DictionaryValue const *,class Extension const *)> const &,class std::tr1::tuple<class FilePath const &,class FilePath const &,class base::DictionaryValue const *,class Extension const *,void,void,void,void,void,void> const &)", |
| "testing::internal::ActionResultHolder<>::PerformAction<>"}, |
| {"?ClassifyInputEvent@ppapi@webkit@@YA?AW4PP_InputEvent_Class@@W4Type@WebInputEvent@WebKit@@@Z", |
| "webkit::ppapi::ClassifyInputEvent(enum WebKit::WebInputEvent::Type)", |
| "webkit::ppapi::ClassifyInputEvent"}, |
| /* Test removal of template parameters. I don't have the mangled forms of |
| * these b/c I'm drawing them from Chromium private symbols, which are never |
| * decorated. |
| */ |
| {"std::operator<<<std::char_traits<char> >", |
| "std::operator<<<std::char_traits<char> >", |
| "std::operator<<<>"}, |
| {"std::operator<<std::char_traits<char> >", |
| "std::operator<<std::char_traits<char> >", |
| "std::operator<<>"}, |
| {"std::operator<=<std::char_traits<char> >", |
| "std::operator<=<std::char_traits<char> >", |
| "std::operator<=<>"}, |
| {"std::operator<<=<std::char_traits<char> >", |
| "std::operator<<=<std::char_traits<char> >", |
| "std::operator<<=<>"}, |
| {"myclass<foo<bar<baz> > >::std::operator-><std::char_traits<char> >", |
| "myclass<foo<bar<baz> > >::std::operator-><std::char_traits<char> >", |
| "myclass<>::std::operator-><>"}, |
| {"std::operator-><std::char_traits<char, truncated", |
| "std::operator-><std::char_traits<char, truncated", |
| /* Truncated => we just close <> */ |
| "std::operator-><>"}, |
| {"std::operator<<<<<std::char_traits<char, truncated", |
| "std::operator<<<<<std::char_traits<char, truncated", |
| "<failure to unmangle>"}, |
| /* Test non-template <> */ |
| {"<CrtImplementationDetails>::NativeDll::ProcessVerifier", |
| "<CrtImplementationDetails>::NativeDll::ProcessVerifier", |
| "<CrtImplementationDetails>::NativeDll::ProcessVerifier"}, |
| {"foo::<unamed-tag>::<not a template>::template<foo::<bar>>", |
| "foo::<unamed-tag>::<not a template>::template<foo::<bar>>", |
| "foo::<unamed-tag>::<not a template>::template<>"}, |
| /* Test malformed */ |
| {"bogus<::std::operator-><std::char_traits<char> >", |
| "bogus<::std::operator-><std::char_traits<char> >", |
| /* Is this what we want? Should we add a more sophisticated parser to |
| * detect this as malformed? |
| */ |
| "bogus<><>"}, |
| }; |
| #endif |
| |
| static void |
| test_demangle_symbols(cpp_name_t *symbols, size_t symbols_sz) |
| { |
| char sym_buf[2048]; |
| unsigned i; |
| size_t len; |
| |
| for (i = 0; i < symbols_sz; i++) { |
| cpp_name_t *sym = &symbols[i]; |
| |
| /* Full demangling. */ |
| len = drsym_demangle_symbol(sym_buf, sizeof(sym_buf), sym->mangled, |
| DRSYM_DEMANGLE_FULL); |
| if (len == 0 || len >= sizeof(sym_buf)) { |
| dr_fprintf(STDERR, "Failed to unmangle %s\n", sym->mangled); |
| } else if (strcmp(sym_buf, sym->dem_full) != 0) { |
| dr_fprintf(STDERR, "Unexpected unmangling:\n" |
| " actual: %s\n expected: %s\n", |
| sym_buf, sym->dem_full); |
| } |
| |
| /* Short demangling (no templates or overloads). */ |
| len = drsym_demangle_symbol(sym_buf, sizeof(sym_buf), sym->mangled, |
| DRSYM_DEMANGLE); |
| if (len == 0 || len >= sizeof(sym_buf)) { |
| dr_fprintf(STDERR, "Failed to unmangle %s\n", sym->mangled); |
| } else if (strcmp(sym_buf, sym->demangled) != 0) { |
| dr_fprintf(STDERR, "Unexpected unmangling:\n" |
| " actual: %s\n expected: %s\n", |
| sym_buf, sym->demangled); |
| } |
| } |
| |
| /* Test overflow. */ |
| len = drsym_demangle_symbol(sym_buf, 6, symbols[0].mangled, DRSYM_DEMANGLE_FULL); |
| if (len == 0) { |
| dr_fprintf(STDERR, "got error instead of overflow\n"); |
| } else if (len <= 6) { |
| dr_fprintf(STDERR, "unexpected demangling success\n"); |
| } else { |
| size_t old_len = 6; |
| dr_fprintf(STDERR, "got correct overflow\n"); |
| /* Resize the buffer in a loop until it demangles correctly. */ |
| while (len > old_len && len < sizeof(sym_buf)) { |
| old_len = len; |
| len = drsym_demangle_symbol(sym_buf, old_len, symbols[0].mangled, |
| DRSYM_DEMANGLE_FULL); |
| } |
| if (strcmp(sym_buf, symbols[0].dem_full) != 0) { |
| dr_fprintf(STDERR, "retrying with demangle return value failed.\n"); |
| } |
| } |
| } |
| |
| static void |
| test_demangle(void) |
| { |
| test_demangle_symbols(symbols_unix, BUFFER_SIZE_ELEMENTS(symbols_unix)); |
| #ifdef WINDOWS |
| test_demangle_symbols(symbols_pdb, BUFFER_SIZE_ELEMENTS(symbols_pdb)); |
| #endif |
| dr_fprintf(STDERR, "finished unmangling.\n"); |
| } |
| |
| static void |
| event_exit(void) |
| { |
| drwrap_exit(); |
| drsym_exit(); |
| /* Check that all symbols we looked up got called. */ |
| ASSERT(call_count == 4); |
| dr_fprintf(STDERR, "all done\n"); |
| } |