| /* ********************************************************** |
| * Copyright (c) 2010-2024 Google, Inc. All rights reserved. |
| * Copyright (c) 2008-2010 VMware, Inc. All rights reserved. |
| * **********************************************************/ |
| |
| /* Dr. Memory: the memory debugger |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; |
| * version 2.1 of the License, and no later version. |
| |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. |
| */ |
| |
| /*************************************************************************** |
| * callstack.h: callstack recording |
| */ |
| |
| #ifndef _CALLSTACK_H_ |
| #define _CALLSTACK_H_ 1 |
| |
| #include "dr_api.h" |
| #include "crypto.h" |
| #include "utils.h" |
| #include "drsyscall.h" |
| |
| /**************************************************************************** |
| * Application locations |
| */ |
| |
| typedef enum { |
| APP_LOC_PC, |
| APP_LOC_SYSCALL, |
| /* for future use of perhaps replacing data addr, where I'm passing |
| * a low int for uninit error in register |
| */ |
| APP_LOC_REGISTER, |
| } app_loc_type_t; |
| |
| /* An error in a system call has the syscall number and an |
| * additional string describing which parameter (PR 525269). |
| */ |
| typedef struct { |
| drsys_sysnum_t sysnum; |
| /* syscalls store a string identifying param (PR 525269) */ |
| const char *syscall_aux; |
| } syscall_loc_t; |
| |
| /* Structure to represent a location within an application such as |
| * an instruction's program counter or a system call number |
| */ |
| typedef struct _app_loc_t { |
| app_loc_type_t type; |
| union { |
| /* Normally the error is at a particular instruction. For PR 494769 |
| * we may not have translated the cache pc yet so the pc field |
| * is not valid unless the valid field is true. |
| */ |
| struct { |
| bool valid; |
| app_pc pc; |
| } addr; |
| syscall_loc_t syscall; |
| } u; |
| } app_loc_t; |
| |
| void |
| pc_to_loc(app_loc_t *loc, app_pc pc); |
| |
| void |
| syscall_to_loc(app_loc_t *loc, drsys_sysnum_t sysnum, const char *aux); |
| |
| /* needs to be defined by the tool-specific code */ |
| app_pc |
| loc_to_pc(app_loc_t *loc); |
| |
| /* needs to be defined by the tool-specific code */ |
| app_pc |
| loc_to_print(app_loc_t *loc); |
| |
| /**************************************************************************** |
| * Callstacks |
| */ |
| |
| /* Values for the flags parameter to callstack_init. These control |
| * how our callstack walking looks for frame pointers when it |
| * encounters a discontinuity in the frame links. We arrange these |
| * such that 0 is a good default. |
| */ |
| enum { |
| /* Showing non module works fine, but there are issues with suppression - |
| * FIXME: PR 464809. |
| */ |
| FP_SHOW_NON_MODULE_FRAMES = 0x00000001, |
| FP_STOP_AT_BAD_NONZERO_FRAME = 0x00000002, |
| FP_STOP_AT_BAD_ZERO_FRAME = 0x00000004, |
| /* Only valid w/ FP_SEARCH_REQUIRE_FP */ |
| FP_SEARCH_MATCH_SINGLE_FRAME = 0x00000008, |
| /* Whether to look for fp,ra pairs during scan, which doesn't work with FPO */ |
| FP_SEARCH_REQUIRE_FP = 0x00000010, |
| /* For more speed but less accuracy (esp with FPO) can not check whether |
| * retaddr candidates during scan are post-OP_call |
| */ |
| FP_SEARCH_DO_NOT_DISASM = 0x00000020, |
| /* For more speed, optionally can not check retaddrs during fp chain walk */ |
| FP_DO_NOT_CHECK_RETADDR = 0x00000040, |
| /* By default, avoid 40% perf hit (on cfrac and roboop) by not checking |
| * retaddr during fp chain walk until have to do a scan: should be |
| * safe to assume fp's are genuine up to any scan. |
| */ |
| FP_CHECK_RETADDR_PRE_SCAN = 0x00000080, |
| /* We do want to check the very first retaddr since ebp might point at |
| * some stack var that happens to look like another fp,ra pair |
| */ |
| FP_DO_NOT_CHECK_FIRST_RETADDR = 0x00000100, |
| /* i#1265: normally we skip the "push ebp" of 32-bit Linux vsyscall |
| * on a 64-bit kernel, as it too often leads to skipped frames. |
| */ |
| FP_DO_NOT_SKIP_VSYSCALL_PUSH = 0x00000200, |
| /* i#703: avoid skipping interior frames due to FPO by never walking the |
| * frame pointer chain and scanning every time. This adds overhead of course. |
| */ |
| FP_DO_NOT_WALK_FP = 0x00000400, |
| /* i#703: avoid skipping interior frames due to FPO by verifying |
| * that the next retaddr actually calls the current one's |
| * containing function. This adds overhead of course. |
| * Either FP_DO_NOT_VERIFY_TARGET_IN_SCAN must be unset, or |
| * FP_DO_NOT_VERIFY_TARGET_IN_WALK must be unset, in order to enable this. |
| */ |
| FP_VERIFY_CALL_TARGET = 0x00000800, |
| /* Identical to FP_VERIFY_CALL_TARGET except this only checks targets on |
| * cross-module calls. |
| * Either FP_DO_NOT_VERIFY_TARGET_IN_SCAN must be unset, or |
| * FP_DO_NOT_VERIFY_TARGET_IN_WALK must be unset, in order to enable this. |
| */ |
| FP_VERIFY_CROSS_MODULE_TARGET = 0x00001000, |
| /* This controls whether to verify call targets while scanning, as |
| * opposed to during the fp walk. The verification performed is specified |
| * by FP_VERIFY_CALL_TARGET, FP_VERIFY_CROSS_MODULE_TARGET, and |
| * FP_DO_NOT_VERIFY_CROSS_MOD_IND. |
| */ |
| FP_DO_NOT_VERIFY_TARGET_IN_SCAN = 0x00002000, |
| /* This controls whether to verify call targets while walking frame |
| * pointers, as opposed to scanning. The verification performed is specified |
| * by FP_VERIFY_CALL_TARGET, FP_VERIFY_CROSS_MODULE_TARGET, and |
| * FP_DO_NOT_VERIFY_CROSS_MOD_IND. |
| */ |
| FP_DO_NOT_VERIFY_TARGET_IN_WALK = 0x00004000, |
| /* If not disabled, all cross-module frame transitions are checked to |
| * ensure an indirect call (or normal inter-library transition) is used, |
| * to rule out false positive frames. |
| * Either FP_DO_NOT_VERIFY_TARGET_IN_SCAN must be unset, or |
| * FP_DO_NOT_VERIFY_TARGET_IN_WALK must be unset, in order to enable this. |
| */ |
| FP_DO_NOT_VERIFY_CROSS_MOD_IND = 0x00008000, |
| /* By default we only consider retaddrs that are after call instructions |
| * that we've already executed. |
| */ |
| FP_SEARCH_ALLOW_UNSEEN_RETADDR = 0x00010000, |
| FP_SEARCH_AGGRESSIVE = (FP_SHOW_NON_MODULE_FRAMES | |
| FP_SEARCH_MATCH_SINGLE_FRAME), |
| }; |
| |
| /* Options for how to display callstacks. |
| * N.B.: postprocess.pl has a duplicate copy (once we have linux online syms |
| * that will go away), as does optionsx.h, so keep them in sync |
| */ |
| enum { |
| PRINT_FRAME_NUMBERS = 0x0001, |
| PRINT_ABS_ADDRESS = 0x0002, |
| PRINT_MODULE_OFFSETS = 0x0004, |
| PRINT_SYMBOL_OFFSETS = 0x0008, |
| PRINT_LINE_OFFSETS = 0x0010, |
| PRINT_SRCFILE_NEWLINE = 0x0020, |
| PRINT_SRCFILE_NO_COLON = 0x0040, |
| PRINT_SYMBOL_FIRST = 0x0080, |
| PRINT_ALIGN_COLUMNS = 0x0100, |
| PRINT_NOSYMS_OFFSETS = 0x0200, |
| PRINT_MODULE_ID = 0x0400, |
| PRINT_VSTUDIO_FILE_LINE = 0x0800, |
| PRINT_EXPAND_TEMPLATES = 0x1000, /* PDB only */ |
| |
| PRINT_FOR_VSTUDIO = (PRINT_SRCFILE_NEWLINE | PRINT_VSTUDIO_FILE_LINE), |
| |
| PRINT_FOR_POSTPROCESS = (PRINT_FRAME_NUMBERS | PRINT_ABS_ADDRESS | |
| PRINT_MODULE_OFFSETS | PRINT_MODULE_ID), |
| PRINT_FOR_LOG = (PRINT_ABS_ADDRESS | PRINT_MODULE_OFFSETS | |
| PRINT_SYMBOL_OFFSETS | PRINT_LINE_OFFSETS | |
| PRINT_MODULE_ID), |
| }; |
| |
| /* length of strings identifying module+offset addresses: |
| * e.g., "0x7d61f78c <ntdll.dll+0x1f78c>" |
| */ |
| #define MAX_MODULE_LEN IF_WINDOWS_ELSE(32,52) |
| /* this can't be an expression since we STRINGIFY it */ |
| #define MAX_MODOFF_LEN IF_WINDOWS_ELSE(34,54) /* for '<' and '>' */ |
| #define MAX_PFX_LEN (3/*'+0x'*/ + IF_X64_ELSE(16,8)/*%08x*/) |
| #define MAX_ADDR_LEN (MAX_MODULE_LEN + MAX_PFX_LEN) |
| /* max lengths for symbols */ |
| #define MAX_FUNC_LEN 256 /* C++ templates get pretty long */ |
| #define MAX_SYMBOL_LEN (MAX_MODULE_LEN + 1/*!*/ + MAX_FUNC_LEN) |
| #define MAX_FILENAME_LEN MAXIMUM_PATH |
| #define MAX_LINENO_DIGITS 6 |
| #define MAX_FILE_LINE_LEN (MAX_FILENAME_LEN + 1/*:*/ + MAX_LINENO_DIGITS) |
| |
| /* if a zero or bad fp is within this threshold of the lowest frame, |
| * do not scan further. i#246. |
| * XXX: could turn into an option if becomes important |
| */ |
| #define FP_NO_SCAN_NEAR_LOW_THRESH 64 |
| |
| /* Max length of error report lines prior to callstack. |
| * Since disass is separate, this is title + timestamp + info |
| * from PR 535568. Here is an example of how large it can get: |
| * Error #1: UNADDRESSABLE ACCESS beyond top of stack: reading 0x0835b39b-0x0835b39c 1 byte(s) |
| * Elapsed time = 0:00:00.109 in thread 18436 |
| * Note: prev lower malloc: 0x0835b358-0x0835b38b |
| * Note: next higher malloc: 0x0835b3b8-0x0835b3bb |
| * Note: 0x0835b39b-0x0835b39c overlaps freed memory 0x0835b398-0x0835b3ac |
| */ |
| #define MAX_ERROR_INITIAL_LINES 512 |
| |
| typedef struct _callstack_options_t { |
| size_t struct_size; /* for compatibility checking */ |
| /* This is the absolute maximum, used for setting buffer sizes up front. |
| * Individual callstacks pass in their own maximums, but they all must be |
| * <= this value. |
| */ |
| uint global_max_frames; |
| uint stack_swap_threshold; |
| uint fp_flags; /* set of FP_ flags */ |
| size_t fp_scan_sz; |
| uint print_flags; /* set of PRINT_ flags */ |
| /* optional: only needed if packed_callstack_record is passed a pc<64K */ |
| const char *(*get_syscall_name)(drsys_sysnum_t); |
| bool (*is_dword_defined)(void * /* drcontext */, byte *); |
| bool (*ignore_xbp)(void *, dr_mcontext_t *); |
| const char *truncate_below; |
| const char *modname_hide; |
| const char *srcfile_hide; |
| const char *srcfile_prefix; |
| void (*missing_syms_cb)(const char *); |
| bool old_retaddrs_zeroed; |
| const char *tool_lib_ignore; |
| const char *bad_fp_list; |
| uint dump_app_stack; /* debug-build-only */ |
| |
| /* Callbacks invoked on module load and unload, allowing the user to store |
| * per-module data. The return value of module_load is the data to store. |
| * It is propagated into each symbolized callstack frame that comes from |
| * that module, and can be queried with symbolized_callstack_frame_data(). |
| * It can also be quered via module_lookup_user_data(). |
| * It should be freed in module_unload. |
| */ |
| void * (*module_load)(const char * /*module path*/, const char * /* module name */, |
| byte * /*module base*/); |
| void (*module_unload)(const char * /*module path*/, |
| void * /*user data returned by module_load()*/); |
| |
| /* if this is set, raw scanning and frame pointer walking is not done. */ |
| bool use_unwind; |
| |
| /* Add new options here */ |
| } callstack_options_t; |
| |
| void |
| callstack_init(callstack_options_t *options); |
| |
| void |
| callstack_exit(void); |
| |
| void |
| callstack_thread_init(void *drcontext); |
| |
| void |
| callstack_thread_exit(void *drcontext); |
| |
| size_t |
| max_callstack_size(void); |
| |
| app_pc |
| callstack_next_retaddr(dr_mcontext_t *mc); |
| |
| #ifdef STATISTICS |
| void |
| callstack_dump_statistics(file_t f); |
| #endif |
| |
| /**************************************************************************** |
| * Binary callstacks for storing callstacks of allocation sites |
| */ |
| |
| struct _packed_callstack_t; |
| typedef struct _packed_callstack_t packed_callstack_t; |
| |
| void |
| packed_callstack_record(packed_callstack_t **pcs_out/*out*/, dr_mcontext_t *mc, |
| app_loc_t *loc, uint max_frames); |
| |
| void |
| packed_callstack_first_frame_retaddr(packed_callstack_t *pcs); |
| |
| void |
| packed_callstack_print(packed_callstack_t *pcs, uint num_frames, |
| char *buf, size_t bufsz, size_t *sofar, const char *prefix); |
| |
| #ifdef DEBUG |
| /* writes callstack to f, using the main logfile if passed INVALID_FILE */ |
| void |
| packed_callstack_log(packed_callstack_t *pcs, file_t f); |
| #endif |
| |
| uint |
| packed_callstack_free(packed_callstack_t *pcs); |
| |
| uint |
| packed_callstack_refcount(packed_callstack_t *pcs); |
| |
| void |
| packed_callstack_add_ref(packed_callstack_t *pcs); |
| |
| packed_callstack_t * |
| packed_callstack_clone(packed_callstack_t *src); |
| |
| uint |
| packed_callstack_hash(packed_callstack_t *pcs); |
| |
| bool |
| packed_callstack_cmp(packed_callstack_t *pcs1, packed_callstack_t *pcs2); |
| |
| void |
| packed_callstack_md5(packed_callstack_t *pcs, byte digest[MD5_RAW_BYTES]); |
| |
| void |
| packed_callstack_crc32(packed_callstack_t *pcs, uint crc[2]); |
| |
| uint |
| packed_callstack_num_frames(packed_callstack_t *pcs); |
| |
| /* destroy the packted callstack */ |
| void |
| packed_callstack_destroy(packed_callstack_t *pcs); |
| |
| /* add the packed callstack into the hashtable, assuming the caller is holding the lock */ |
| packed_callstack_t * |
| packed_callstack_add_to_table(hashtable_t *table, packed_callstack_t *pcs |
| _IF_STATS(uint *callstack_count)); |
| |
| /* The user must call this from a DR dr_register_module_load_event() event */ |
| void |
| callstack_module_load(void *drcontext, const module_data_t *info, bool loaded); |
| |
| /* The user must call this from a DR dr_register_module_unload_event() event */ |
| void |
| callstack_module_unload(void *drcontext, const module_data_t *info); |
| |
| bool |
| is_in_module(byte *pc); |
| |
| /* Returns the full path of the module that contains pc, or NULL if the pc |
| * is not in a known module. |
| */ |
| const char * |
| module_lookup_path(byte *pc); |
| |
| /* Returns the preferred name of the module that contains pc, or NULL if the pc |
| * is not in a known module. |
| */ |
| const char * |
| module_lookup_preferred_name(byte *pc); |
| |
| /* Returns the data stored for the module containing pc by |
| * callstack_options_t.module_load, or NULL if the pc is not in a known module. |
| * Optionally returns the module bounds as well. |
| */ |
| void * |
| module_lookup_user_data(byte *pc, app_pc *start DR_PARAM_OUT, size_t *size DR_PARAM_OUT); |
| |
| /* Warns once about modules that don't have symbols, and records them in a |
| * logfile so they can be fetched at the end of execution. |
| * |
| * Call this after making a real symbol query on the module. We can't do this |
| * from the module load event because it forces dbghelp to load the module. |
| * Loading all modules would defeat the purpose of the symcache and hurt startup |
| * performance. This way we only fetch modules that were actually needed to |
| * symbolize callstacks or to prime the symbol cache. |
| */ |
| void |
| module_check_for_symbols(const char *modpath); |
| |
| /**************************************************************************** |
| * Symbolized callstacks |
| */ |
| |
| struct _symbolized_frame_t; |
| typedef struct _symbolized_frame_t symbolized_frame_t; |
| |
| typedef struct _symbolized_callstack_t { |
| ushort num_frames; |
| ushort num_frames_allocated; /* in case truncated, we don't realloc */ |
| symbolized_frame_t *frames; |
| } symbolized_callstack_t; |
| |
| void |
| packed_callstack_to_symbolized(packed_callstack_t *pcs DR_PARAM_IN, |
| symbolized_callstack_t *scs DR_PARAM_OUT); |
| |
| void |
| symbolized_callstack_print(const symbolized_callstack_t *scs DR_PARAM_IN, |
| char *buf, size_t bufsz, size_t *sofar, |
| const char *prefix, bool for_log); |
| |
| void |
| symbolized_callstack_free(symbolized_callstack_t *scs); |
| |
| bool |
| symbolized_callstack_frame_is_module(const symbolized_callstack_t *scs, uint frame); |
| |
| char * |
| symbolized_callstack_frame_modname(const symbolized_callstack_t *scs, uint frame); |
| |
| char * |
| symbolized_callstack_frame_modoffs(const symbolized_callstack_t *scs, uint frame); |
| |
| /* The module base may be inaccurate if scs was created from an old packed |
| * callstack, as the packed callstack does not store the base. |
| */ |
| app_pc |
| symbolized_callstack_frame_modbase(const symbolized_callstack_t *scs, uint frame); |
| |
| char * |
| symbolized_callstack_frame_func(const symbolized_callstack_t *scs, uint frame); |
| |
| char * |
| symbolized_callstack_frame_file(const symbolized_callstack_t *scs, uint frame); |
| |
| /* Returns the data stored for this frame's module by callstack_options_t.module_load */ |
| void * |
| symbolized_callstack_frame_data(const symbolized_callstack_t *scs, uint frame); |
| |
| /**************************************************************************** |
| * Printing routines |
| */ |
| |
| /* Returns whether a new frame was added (won't be if module_only and pc |
| * is not in a module) |
| */ |
| bool |
| print_address(char *buf, size_t bufsz, size_t *sofar, |
| app_pc pc, module_data_t *mod_in /*optional*/, bool for_log); |
| |
| void |
| print_callstack(char *buf, size_t bufsz, size_t *sofar, dr_mcontext_t *mc, |
| bool print_fps, packed_callstack_t *pcs, int num_frames_printed, |
| bool for_log, uint max_frames, |
| bool (*frame_cb)(app_pc pc, byte *fp, void *user_data), |
| void *user_data); |
| |
| void |
| print_buffer(file_t f, char *buf); |
| |
| #ifdef DEBUG |
| void |
| print_callstack_to_file(void *drcontext, dr_mcontext_t *mc, app_pc pc, file_t f, |
| uint max_frames); |
| #endif |
| |
| bool |
| print_symbol(byte *addr, char *buf, size_t bufsz, size_t *sofar, |
| bool use_custom_flags, uint custom_flags); |
| |
| #endif /* _CALLSTACK_H_ */ |