Mstdlib-1.24.0
|
Data Structures | |
struct | M_backtrace_callbacks |
Typedefs | |
typedef void(* | M_backtrace_filename_func) (char *fname, size_t fname_len) |
typedef void(* | M_backtrace_trace_data_func) (const unsigned char *data, size_t len) |
typedef void(* | M_backtrace_log_emergency_func) (int sig, const char *message) |
typedef void(* | M_backtrace_got_nonfatal_func) (int sig) |
typedef void(* | M_backtrace_got_fatal_func) (int sig) |
Enumerations | |
enum | M_backtrace_type_t { M_BACKTRACE_TYPE_BACKTRACE = 0 , M_BACKTRACE_TYPE_DUMP } |
enum | M_backtrace_flags_t { M_BACKTRACE_NONE = 0 , M_BACKTRACE_WRITE_FILE = 1 << 0 , M_BACKTRACE_EXTENDED_DUMP = 1 << 1 , M_BACKTRACE_CAPTURE_NONCRASH = 1 << 2 } |
Functions | |
M_bool | M_backtrace_enable (M_backtrace_type_t type, struct M_backtrace_callbacks *cbs, M_uint32 flags) |
void | M_backtrace_set_ignore_signal (int sig) |
void | M_backtrace_set_nonfatal_signal (int sig) |
void | M_backtrace_set_fatal_signal (int sig) |
void | M_backtrace_signal_use_default_handler (int sig) |
Generates a back trace when certain signal are singled. This is primarily used for capturing information about a crash.
Backtrace data can either be written directly to a file or passed to a configured callback but not both. When writing to a file the M_backtrace_filename_func callback is required to be implemented. When capturing trace data, the M_backtrace_trace_data_func is required to be implemented. All other callbacks are optional.
Windows has the ability to generate backtrace as well as a mini dump file. Minidump requires writing directly to a file.
Other OS cannot use dump output and only support backtrace output.
There are three types of events:
Only fatal events are supported on Windows.
Crash events are any events captured by SetUnhandledExceptionFilter.
Primary events (other could trigger):
Events are signals from singled.h.
Fatal signals (default captured):
Non-fatal signals (default captured):
Ignored signals (default captured):
Fatal signals are always enabled. Non-fatal and ignore will only be set during setup when the M_BACKTRACE_CAPTURE_NONCRASH flag is set. When not set only the default fatal signals are set to be captured.
Signals can be changed from one type to another and additional signals can be added to a given type using the M_backtrace_set_* functions. For example, some applications may want to treat SIGPIPE as non-fatal or ignore it altogether.
macOS will not return filename or line numbers. This is a limitation of the OS. Additional tools need to be used to evaluate the trace data. Given this trace data.
0 libsystem_platform.dylib 0x00007fff70c3bf5a _sigtramp + 26 1 APP_NAME 0x0000000101bc37af my_read + 95 2 APP_NAME 0x0000000101bff381 mm_listen_connection + 545
lldb can be used to get the source file and line from the function and offset.
(lldb) so l -a my_read+95
The trace may not show the exact line that caused the crash but only the generate area where it occurred. For example, my_read+95 may point to line within my_read where function my_crashing_function is called. The crash may be somewhere within my_crashing_function.
Example trace data when PDB is distributed with application (not typical outside of development):
EXCEPTION_ACCESS_VIOLATION at address 0x7ff614eb5e4a Invalid operation: write at address 0x00000000 0 - C:\Program Files\MY_APP\lib\mylib.dll + 21024 my_dt() at \cygdrive\c\build\my_file.c line 692 1 - C:\Program Files\MY_APP\lib\mylib.dll + 4202 my_do_thing() at \cygdrive\c\build\my_file.c line 1180 2 - C:\Program Files\MY_APP\lib\mylib.dll + 45 my_start() at \cygdrive\c\build\main.c line 316
Example trace data when PDB is NOT distributed with application (typical production deployment):
EXCEPTION_ACCESS_VIOLATION at address 0x7ff614eb5e4a Invalid operation: write at address 0x00000000 0 - C:\Program Files\MY_APP\app.exe + 292671 0 - C:\Program Files\MY_APP\app.exe + 286282
We have experienced the function name in the output being incorrect while the filename and line point to the correct location. For example my_dt() may not be correct and my_file.c line 692 is actually my_check_valid_dt. This is due to compiler optimizations such as inlining.
The location of '+ #' is the offset from the module base address. When using a PDB file for debugging you must get the base address from the debugger and add the offset to find the faulting address. PDB files use a virtual address that does not correspond to the address within the binary. Hence, an offset from the base address is provided by the backtrace.
If using Mingw PDB files can be generated by building with debug symbols, running cv2pdb, then stripping the binary.
Lookup by address is important when the application is not distributed withe PDB files. Which is typical for pretty much every application. When this is the case the backtrace will only list modules and offsets. It will not include file names or line numbers. Instead we need to look this up using the module and offset within the module recorded in the backtrace.
You are now at the line captured in the backtrace.
struct M_backtrace_callbacks |
Callbacks.
Data Fields | ||
---|---|---|
M_backtrace_filename_func | get_filename |
Get a filename when writing to file is enabled. Cannot be NULL if writing to a file. |
M_backtrace_trace_data_func | trace_data |
Backtrace data. Cannot be NULL when not writing to a file. |
M_backtrace_log_emergency_func | log_emergency |
Emergency log function with information about a crash event. |
M_backtrace_got_nonfatal_func | got_nonfatal |
Non fatal event captured. |
M_backtrace_got_fatal_func | got_fatal |
A fatal event occurred and the application is about to exit. Informational only to allow last logging. |
typedef void(* M_backtrace_filename_func) (char *fname, size_t fname_len) |
Callback to get file name to write data to.
If the filename cannot be opened or written to the data will be lost. There is no recovery in this instance so it is very important the location can be used. The file will be created if it does not exist.
[in] | fname | Buffer to store the filename. |
[in] | fname_len | Length of buffer. |
typedef void(* M_backtrace_trace_data_func) (const unsigned char *data, size_t len) |
Callback to receive backtrace data.
This can be called multiple times. For example, every line describing the trace could call this function.
Memory allocation or any function that could generate a signal should not be used within this callback. If writing to a file, it should be flushed after every write to reduce the risk of data loss.
[in] | data | Data describing the backtrace. |
[in] | len | Length of data. |
typedef void(* M_backtrace_log_emergency_func) (int sig, const char *message) |
Callback to receive information about a backtrace event.
Information about an event suitable for logging. This is general information such as "segfault detected" and does not contain backtrace information.
[in] | sig | Signal or exception that generated this event. |
[in] | message | Message. |
typedef void(* M_backtrace_got_nonfatal_func) (int sig) |
Callback to handle non-fatal events.
Non-fatal events do not generate a backtrace and are informational so the receiver can take additional action. For example, capturing the CTRL+C event to write to a log before exiting.
[in] | sig | Signal or exception that generated this event. |
typedef void(* M_backtrace_got_fatal_func) (int sig) |
Callback to signal a fatal even occurred and the application will exit.
This will be called after all trace_data and log_emergency calls.
[in] | sig | Signal or exception that generated this event. |
enum M_backtrace_type_t |
enum M_backtrace_flags_t |
Flags controlling behavior.
M_bool M_backtrace_enable | ( | M_backtrace_type_t | type, |
struct M_backtrace_callbacks * | cbs, | ||
M_uint32 | flags | ||
) |
Enable backtracing in the application.
Cannot be called multiple times and cannot be disabled. Once enabled it is enabled.
[in] | type | Type of tracing that should happen. |
[in] | cbs | Callbacks to handle events. |
[in] | flags | M_backtrace_flags_t Flags controlling behavior. |
void M_backtrace_set_ignore_signal | ( | int | sig | ) |
Ignore the signal.
This does not set SIG_IGN. Instead it sets a no-op function as the handler. If SIG_IGN is required it must be set using signal(sig, SIG_IGN)
directly.
A no-op is used because of how SIG_IGN interacts with child processes. Per POSIX.1-2001, wait/waitpid will return ECHILD if SIGCHLD is set to SIG_IGN. If that's the case then children may not become zombies and wait/waitpid will block until all children have exited and fail with ECHILD. If using WNOHANG you'll get ECHILD immediately instead of being informed the child has exited.
[in] | sig | Signal. |
void M_backtrace_set_nonfatal_signal | ( | int | sig | ) |
Consider the siginal as non-fatal
Will call the M_backtrace_got_fatal_func callback. If the callback not set the signal will be ignored.
[in] | sig | Signal. |
void M_backtrace_set_fatal_signal | ( | int | sig | ) |
Consider the signal fatal.
[in] | sig | Signal. |
void M_backtrace_signal_use_default_handler | ( | int | sig | ) |
Use the default sign handler.
Default is what is set by SIG_DFL
[in] | sig | Signal. |