Mstdlib-1.24.0
m_backtrace.h
1/* The MIT License (MIT)
2 *
3 * Copyright (c) 2018 Monetra Technologies, LLC.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
24#ifndef __M_BACKTRACE_H__
25#define __M_BACKTRACE_H__
26
27/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
28
29#include <mstdlib/base/m_defs.h>
30#include <mstdlib/base/m_types.h>
31#include <mstdlib/base/m_list_u64.h>
32
33/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
34
35__BEGIN_DECLS
36
37/*! \addtogroup m_backtrace_trace Backtrace
38 * \ingroup m_backtrace
39 *
40 * Generates a back trace when certain signal are singled. This is primarily
41 * used for capturing information about a crash.
42 *
43 * # Use
44 *
45 * Backtrace data can either be written directly to a file or passed to
46 * a configured callback but not both. When writing to a file the
47 * M_backtrace_filename_func callback is required to be implemented.
48 * When capturing trace data, the M_backtrace_trace_data_func is required
49 * to be implemented. All other callbacks are optional.
50 *
51 * Windows has the ability to generate backtrace as well as a mini dump file.
52 * Minidump requires writing directly to a file.
53 *
54 * Other OS cannot use dump output and only support backtrace output.
55 *
56 * # Events
57 *
58 * There are three types of events:
59 * - Fatal - Generate backtrace and exit the application.
60 * - Non-Fatal - Informational events that allow application to take an action.
61 * - Ignored - Events that are silenced and ignored.
62 *
63 * ## Windows
64 *
65 * Only fatal events are supported on Windows.
66 *
67 * Crash events are any events captured by SetUnhandledExceptionFilter.
68 *
69 * Primary events (other could trigger):
70 * - EXCEPTION_ACCESS_VIOLATION
71 * - EXCEPTION_ARRAY_BOUNDS_EXCEEDED
72 * - EXCEPTION_BREAKPOINT
73 * - EXCEPTION_DATATYPE_MISALIGNMENT
74 * - EXCEPTION_FLT_DENORMAL_OPERAND
75 * - EXCEPTION_FLT_DIVIDE_BY_ZERO
76 * - EXCEPTION_FLT_INEXACT_RESULT
77 * - EXCEPTION_FLT_INVALID_OPERATION
78 * - EXCEPTION_FLT_OVERFLOW
79 * - EXCEPTION_FLT_STACK_CHECK
80 * - EXCEPTION_FLT_UNDERFLOW
81 * - EXCEPTION_ILLEGAL_INSTRUCTION
82 * - EXCEPTION_IN_PAGE_ERROR
83 * - EXCEPTION_INT_DIVIDE_BY_ZERO
84 * - EXCEPTION_INT_OVERFLOW
85 * - EXCEPTION_INVALID_DISPOSITION
86 * - EXCEPTION_NONCONTINUABLE_EXCEPTION
87 * - EXCEPTION_PRIV_INSTRUCTION
88 * - EXCEPTION_SINGLE_STEP
89 * - EXCEPTION_STACK_OVERFLOW
90 *
91 * ## Other OS
92 *
93 * Events are signals from singled.h.
94 *
95 * \note Signals are global and their signal handler can be changed outside of this library.
96 * A backtrace will not be generated if the signal handler is changed.
97 *
98 * Fatal signals (default captured):
99 * - SIGPIPE
100 * - SIGSEGV
101 * - SIGBUS
102 * - SIGILL
103 * - SIGFPE
104 *
105 * Non-fatal signals (default captured):
106 * - SIGINT
107 * - SIGQUIT
108 * - SIGTERM
109 * - SIGXFSZ
110 *
111 * Ignored signals (default captured):
112 * - SIGCHLD
113 * - SIGUSR1
114 * - SIGUSR2
115 *
116 * Fatal signals are always enabled. Non-fatal and ignore will only be set during setup
117 * when the M_BACKTRACE_CAPTURE_NONCRASH flag is set. When not set only the default
118 * fatal signals are set to be captured.
119 *
120 * Signals can be changed from one type to another and additional signals can be added
121 * to a given type using the M_backtrace_set_* functions. For example, some applications
122 * may want to treat SIGPIPE as non-fatal or ignore it altogether.
123 *
124 * # Platform notes
125 *
126 * ## macOS
127 *
128 * macOS will not return filename or line numbers. This is a limitation of
129 * the OS. Additional tools need to be used to evaluate the trace data.
130 * Given this trace data.
131 *
132 * 0 libsystem_platform.dylib 0x00007fff70c3bf5a _sigtramp + 26
133 * 1 APP_NAME 0x0000000101bc37af my_read + 95
134 * 2 APP_NAME 0x0000000101bff381 mm_listen_connection + 545
135 *
136 * lldb can be used to get the source file and line from the function and offset.
137 *
138 * (lldb) so l -a my_read+95
139 *
140 * The trace may not show the exact line that caused the crash but only the
141 * generate area where it occurred. For example, my_read+95 may point to line within
142 * my_read where function my_crashing_function is called. The crash may be somewhere
143 * within my_crashing_function.
144 *
145 * ## Windows
146 *
147 * Example trace data when PDB is distributed with application (not typical outside of development):
148 *
149 * EXCEPTION_ACCESS_VIOLATION at address 0x7ff614eb5e4a Invalid operation: write at address 0x00000000
150 * 0 - C:\Program Files\MY_APP\lib\mylib.dll + 21024 my_dt() at \cygdrive\c\build\my_file.c line 692
151 * 1 - C:\Program Files\MY_APP\lib\mylib.dll + 4202 my_do_thing() at \cygdrive\c\build\my_file.c line 1180
152 * 2 - C:\Program Files\MY_APP\lib\mylib.dll + 45 my_start() at \cygdrive\c\build\main.c line 316
153 *
154 * Example trace data when PDB is _NOT_ distributed with application (typical production deployment):
155 *
156 * EXCEPTION_ACCESS_VIOLATION at address 0x7ff614eb5e4a Invalid operation: write at address 0x00000000
157 * 0 - C:\Program Files\MY_APP\app.exe + 292671
158 * 0 - C:\Program Files\MY_APP\app.exe + 286282
159 *
160 * We have experienced the function name in the output being incorrect while the
161 * filename and line point to the correct location. For example my_dt() may not be correct and my_file.c line 692
162 * is actually my_check_valid_dt. This is due to compiler optimizations such as inlining.
163 *
164 * The location of '+ #' is the offset from the module base address. When using a PDB file for debugging you
165 * must get the base address from the debugger and add the offset to find the faulting address. PDB files use
166 * a virtual address that does not correspond to the address within the binary. Hence, an offset from the
167 * base address is provided by the backtrace.
168 *
169 * If using Mingw PDB files can be generated by building with debug symbols, running cv2pdb, then stripping
170 * the binary.
171 *
172 * ### Using Visual Studio to Lookup Addresses
173 *
174 * Lookup by address is important when the application is not distributed withe PDB files. Which is
175 * typical for pretty much every application. When this is the case the backtrace will only list modules
176 * and offsets. It will not include file names or line numbers. Instead we need to look this up using
177 * the module and offset within the module recorded in the backtrace.
178 *
179 * 1. Open Visual Studio
180 * 2. Choose File -> Open -> Project/Solution
181 * 3. Choose the exe (app.exe in above example). This will create a temporary/pseudo project to work in.
182 * 4. Choose Debug -> Options
183 * 5. Go to Debugging -> Symbols
184 * 6. Add the location of the PDB files
185 * 7. Right click the solution in the solution explorer and choose Properties
186 * 8. Go to Common Properties -> Debug Source Files
187 * 9. Add the location of the source files
188 * 10. Choose Debug -> Step Into. This will start the debugger and break on main
189 * 11. Chose Debug -> Windows -> Modules
190 * 12. Find the entry for the application or DLL referenced in the backtrace you're interested in
191 * 13. The Address column will include the start and end addresses for the module. Get the start address
192 * 14. Add the offset to the start address for the entry in the backtrace you're interested in
193 * 15. Choose Debug -> Windows -> Disassembly
194 * 16. In the Address entry put in the address you've calculated using the start and offset
195 * 17. The code will be inline with the disassemtly
196 * 18. Right click on the line corresponding to the address and choose Go To Source
197 *
198 * You are now at the line captured in the backtrace.
199 *
200 * @{
201 */
202
203
204/*! Callback to get file name to write data to.
205 *
206 * If the filename cannot be opened or written to the data
207 * will be lost. There is no recovery in this instance so it
208 * is very important the location can be used. The file will
209 * be created if it does not exist.
210 *
211 * \param[in] fname Buffer to store the filename.
212 * \param[in] fname_len Length of buffer.
213 */
214typedef void (*M_backtrace_filename_func)(char *fname, size_t fname_len);
215
216
217/*! Callback to receive backtrace data.
218 *
219 * This can be called multiple times. For example, every line describing
220 * the trace could call this function.
221 *
222 * Memory allocation or any function that could generate a signal should
223 * not be used within this callback. If writing to a file, it should be
224 * flushed after every write to reduce the risk of data loss.
225 *
226 * \param[in] data Data describing the backtrace.
227 * \param[in] len Length of data.
228 */
229typedef void (*M_backtrace_trace_data_func)(const unsigned char *data, size_t len);
230
231
232/*! Callback to receive information about a backtrace event.
233 *
234 * Information about an event suitable for logging. This is general information
235 * such as "segfault detected" and does not contain backtrace information.
236 *
237 * \param[in] sig Signal or exception that generated this event.
238 * \param[in] message Message.
239 */
240typedef void (*M_backtrace_log_emergency_func)(int sig, const char *message);
241
242
243/*! Callback to handle non-fatal events.
244 *
245 * \note UNIX only.
246 *
247 * Non-fatal events do not generate a backtrace and are informational
248 * so the receiver can take additional action. For example, capturing
249 * the CTRL+C event to write to a log before exiting.
250 *
251 * \param[in] sig Signal or exception that generated this event.
252 */
253typedef void (*M_backtrace_got_nonfatal_func)(int sig);
254
255
256/*! Callback to signal a fatal even occurred and the application will exit.
257 *
258 * This will be called after all trace_data and log_emergency calls.
259 *
260 * \param[in] sig Signal or exception that generated this event.
261 */
262typedef void (*M_backtrace_got_fatal_func)(int sig);
263
264
265/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
266
267/*! Type of backtrae that should be generated. */
268typedef enum {
269 M_BACKTRACE_TYPE_BACKTRACE = 0, /*!< Stack trace. */
270 M_BACKTRACE_TYPE_DUMP, /*!< Binary dump of trace data. Windows only and generates a minidump.
271 On all other OS will be treated as BACKTRACE. */
273
274
275/*! Flags controlling behavior. */
276typedef enum {
277 M_BACKTRACE_NONE = 0, /*!< Normal behavior. */
278 M_BACKTRACE_WRITE_FILE = 1 << 0, /*!< Write data to a file using the get_filename callback. */
279 M_BACKTRACE_EXTENDED_DUMP = 1 << 1, /*!< When using dump type capture additional information about the event.
280 This will produce a much larger dump file and can capture sensitive
281 data in memory. For example, encryption keys. */
282 M_BACKTRACE_CAPTURE_NONCRASH = 1 << 2 /*!< Setup default non-fatal and ignore signal handling on non-Windows OS. */
284
285
286/*! Callbacks. */
288 M_backtrace_filename_func get_filename; /*!< Get a filename when writing to file is enabled. Cannot be
289 NULL if writing to a file. */
290 M_backtrace_trace_data_func trace_data; /*!< Backtrace data. Cannot be NULL when not writing to a file. */
291 M_backtrace_log_emergency_func log_emergency; /*!< Emergency log function with information about a crash event. */
292 M_backtrace_got_nonfatal_func got_nonfatal; /*!< Non fatal event captured. */
293 M_backtrace_got_fatal_func got_fatal; /*!< A fatal event occurred and the application is about to exit.
294 Informational only to allow last logging. */
295};
296
297
298/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
299
300/*! Enable backtracing in the application.
301 *
302 * Cannot be called multiple times and cannot be disabled. Once enabled it is enabled.
303 *
304 * \param[in] type Type of tracing that should happen.
305 * \param[in] cbs Callbacks to handle events.
306 * \param[in] flags M_backtrace_flags_t Flags controlling behavior.
307 */
308M_API M_bool M_backtrace_enable(M_backtrace_type_t type, struct M_backtrace_callbacks *cbs, M_uint32 flags);
309
310
311/*! Ignore the signal.
312 *
313 * This does _not_ set SIG_IGN. Instead it sets a no-op function as
314 * the handler. If SIG_IGN is required it must be set using `signal(sig, SIG_IGN)`
315 * directly.
316 *
317 * A no-op is used because of how SIG_IGN interacts with child
318 * processes. Per POSIX.1-2001, wait/waitpid will return ECHILD
319 * if SIGCHLD is set to SIG_IGN. If that's the case then children
320 * may not become zombies and wait/waitpid will block until all
321 * children have exited and fail with ECHILD. If using WNOHANG you'll
322 * get ECHILD immediately instead of being informed the child has exited.
323 *
324 * \note Unix only.
325 *
326 * \param[in] sig Signal.
327 */
329
330
331/*! Consider the siginal as non-fatal
332 *
333 * Will call the M_backtrace_got_fatal_func callback. If the callback not set the signal will be ignored.
334 *
335 * \note Unix only.
336 *
337 * \param[in] sig Signal.
338 */
340
341
342/*! Consider the signal fatal.
343 *
344 * \note Unix only.
345 *
346 * \param[in] sig Signal.
347 */
349
350
351/*! Use the default sign handler.
352 *
353 * Default is what is set by SIG_DFL
354 *
355 * \note Unix only.
356 *
357 * \param[in] sig Signal.
358 */
360
361
362/*! @} */
363
364__END_DECLS
365
366#endif /* __M_BACKTRACE_H__ */
M_backtrace_got_nonfatal_func got_nonfatal
Definition: m_backtrace.h:292
M_backtrace_filename_func get_filename
Definition: m_backtrace.h:288
M_backtrace_got_fatal_func got_fatal
Definition: m_backtrace.h:293
M_backtrace_log_emergency_func log_emergency
Definition: m_backtrace.h:291
M_backtrace_trace_data_func trace_data
Definition: m_backtrace.h:290
void(* M_backtrace_filename_func)(char *fname, size_t fname_len)
Definition: m_backtrace.h:214
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_fatal_signal(int sig)
M_backtrace_type_t
Definition: m_backtrace.h:268
void(* M_backtrace_log_emergency_func)(int sig, const char *message)
Definition: m_backtrace.h:240
M_backtrace_flags_t
Definition: m_backtrace.h:276
void(* M_backtrace_trace_data_func)(const unsigned char *data, size_t len)
Definition: m_backtrace.h:229
void(* M_backtrace_got_fatal_func)(int sig)
Definition: m_backtrace.h:262
void M_backtrace_set_nonfatal_signal(int sig)
void M_backtrace_signal_use_default_handler(int sig)
void(* M_backtrace_got_nonfatal_func)(int sig)
Definition: m_backtrace.h:253
@ M_BACKTRACE_TYPE_DUMP
Definition: m_backtrace.h:270
@ M_BACKTRACE_TYPE_BACKTRACE
Definition: m_backtrace.h:269
@ M_BACKTRACE_NONE
Definition: m_backtrace.h:277
@ M_BACKTRACE_EXTENDED_DUMP
Definition: m_backtrace.h:279
@ M_BACKTRACE_CAPTURE_NONCRASH
Definition: m_backtrace.h:282
@ M_BACKTRACE_WRITE_FILE
Definition: m_backtrace.h:278
Definition: m_backtrace.h:287