rcolyer.net
RC Lib  Version 202503311402
Errors.h
Go to the documentation of this file.
1 /////////////////////////////////////////////////////////////////////
2 //
3 // RC Library, (c) 2011-2015, Ryan A. Colyer
4 // Distributed under the Boost Software License, v1.0. (LICENSE.txt)
5 //
6 /// \file Errors.h
7 /// Provides informative exception handling.
8 /////////////////////////////////////////////////////////////////////
9 
10 #ifndef RC_ERRORS_H
11 #define RC_ERRORS_H
12 
13 #include "RCconfig.h"
14 #include "Types.h"
15 
16 #include <signal.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <typeinfo>
21 
22 
23 #ifndef RC_NO_STACKTRACE
24 #ifdef unix
25 #include <execinfo.h>
26 #elif defined(WIN32)
27 #include <winsock2.h>
28 #include <windows.h>
29 #include <dbghelp.h>
30 #include <tchar.h>
31 #endif
32 #endif // RC_NO_STACKTRACE
33 
34 #ifdef MACOS
35 #include <mach/mach_init.h>
36 #include <sys/sysctl.h>
37 #include <mach/mach_vm.h>
38 #endif
39 
40 
41 namespace RC {
42  /// The maximum size of the char array returned by ErrorMsg::what() and
43  /// ErrorMsg::GetError(), including the null.
44  const size_t ErrorMsg_text_size = 4095;
45  /// @cond UNDOC
46  const size_t ErrorMsg_text_bufsize = 4096;
47  const size_t ErrorMsg_what_bufsize = 4096+1024;
48  /// @endcond
49  /// The maximum size of the char array returned by ErrorMsg::GetType(),
50  /// including the null.
51  const size_t ErrorMsg_type_bufsize = 256;
52 
53  /// An exception class that records where the exception was thrown and
54  /// provides a stack trace.
55  /** Use with the macro Throw_RC_Error("Reason"); to automatically pick up
56  * the source filename and line number. This exception should be used
57  * liberally any time an error would be informative for resolving a bug.
58  *
59  * Stack tracing is implemented for Linux and Windows. For Linux with g++
60  * or clang compile with -rdynamic for full symbols in the stack trace
61  * output from what(). For Windows symbols cannot be automatically provided
62  * with mingw, but the addresses provided in what() can be resolved by
63  * providing them as stdin to "addr2line -pfe myprogram.exe" for any program
64  * compiled with -ggdb.
65  */
66  class ErrorMsg : virtual public std::exception {
67  public:
68 
69  static size_t TaskPid(size_t pid=0) {
70  static size_t task_pid = 0;
71  if (pid != 0) {
72  task_pid = pid;
73  }
74  return task_pid;
75  }
76 
77  protected:
78 
79  size_t GetLoadAddress() {
80 #ifdef MACOS
81  vm_map_offset_t vmoffset;
82  vm_map_size_t vmsize;
83  uint32_t nesting_depth = 0;
84  struct vm_region_submap_info_64 vbr;
85  mach_msg_type_number_t vbrcount = 16;
86  kern_return_t kr;
87 
88  if ((kr = mach_vm_region_recurse(TaskPid(), &vmoffset, &vmsize,
89  &nesting_depth,
90  (vm_region_recurse_info_t)&vbr,
91  &vbrcount)) != KERN_SUCCESS)
92  {
93  return 0;
94  } else {
95  return vmoffset;
96  }
97 #elif defined(unix)
98  // TODO: JPB: (feature) Implement LoadAddress for Linux
99  return 0;
100 #elif defined(WIN32)
101  // TODO: JPB: (feature) Implement LoadAddress for Windows
102  return 0;
103 #else
104  return 0;
105 #endif
106  }
107 
108  size_t AmountWritten(size_t size, size_t written) {
109  return (size <= written) ? size-1 : written;
110  }
111  // Returns amount actually written.
112  /// @cond UNDOC
113  #define R_Safe_snprintf(str, size, ...) \
114  AmountWritten(size, size_t(snprintf(str, size, __VA_ARGS__)));
115  /// @endcond
116 
117  public:
118 
119  /// The default constructor.
120  /** Use the convenience macro Throw_RC_Error("Reason"); which uses
121  * __FILE__ and __LINE__ to extract the location of the throw.
122  * @param new_err_msg The reason for the exception.
123  * @param filename The source code file that the exception was triggered
124  * in.
125  * @param line_number The source code line number that the exception was
126  * triggered on.
127  */
128  ErrorMsg(const char* new_err_msg=NULL, const char* filename=NULL,
129  int line_number = 0) RC_NOEXCEPT {
130  size_t offset = 0;
131  char stacktrace_txt[ErrorMsg_text_bufsize] = {0};
132 
133  type_msg[0] = '\0';
134  if (new_err_msg == NULL) {
135  err_msg[0] = '\0';
136  }
137  else {
138  strncpy(err_msg, new_err_msg, ErrorMsg_text_size);
139  err_msg[ErrorMsg_text_bufsize-1] = '\0';
140  }
141 
142 #ifndef RC_NO_STACKTRACE
143  // Get the stack trace if we can.
144  int backtrace_cnt = 0;
145  const int backtrace_size = 128;
146  void *bt_buffer[backtrace_size];
147 #ifdef unix
148  char **backtrace_symb;
149  backtrace_cnt = backtrace(bt_buffer, backtrace_size);
150  backtrace_symb = backtrace_symbols(bt_buffer, backtrace_cnt);
151 
152  if (backtrace_symb != NULL) {
153  for (int i=0; i<backtrace_cnt; i++) {
154  offset += R_Safe_snprintf(stacktrace_txt+offset,
155  ErrorMsg_text_size-offset, "\n\t%s",
156  backtrace_symb[backtrace_cnt-i-1]);
157  }
158  offset += R_Safe_snprintf(stacktrace_txt+offset,
159  ErrorMsg_text_size-offset, "\n");
160 
161  SafeFree(backtrace_symb);
162  }
163 
164 // end unix
165 #elif defined(WIN32)
166  RC_DYNAMIC_LOAD_FUNC_RAW(SymInitialize, TEXT("Dbghelp.dll"));
167  RC_DYNAMIC_LOAD_FUNC_RAW(SymFromAddr, TEXT("Dbghelp.dll"));
168  RC_DYNAMIC_LOAD_FUNC_RAW(SymCleanup, TEXT("Dbghelp.dll"));
169 
170  if (SymInitialize && SymFromAddr && SymCleanup) {
171  HANDLE process = GetCurrentProcess();
172  SymInitialize(process, NULL, true);
173 
174  // Allocate this variable length SYMBOL_INFO structure on stack.
175  const size_t symbolnamelen = 255;
176  char symboldata[sizeof(SYMBOL_INFO)+(symbolnamelen+1)];
177  SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO*>(symboldata);
178  symbol->MaxNameLen = symbolnamelen;
179  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
180 
181  backtrace_cnt = CaptureStackBackTrace(0, backtrace_size,
182  bt_buffer, NULL);
183 
184  for(int i=0; i<backtrace_cnt; i++) {
185  offset += R_Safe_snprintf(stacktrace_txt+offset,
186  ErrorMsg_text_size-offset, "\r\n\t0x%lx : ",
187  static_cast<long unsigned int>(size_t(bt_buffer[i])));
188  if (SymFromAddr(process, DWORD64(bt_buffer[i]), 0, symbol)) {
189  offset += R_Safe_snprintf(stacktrace_txt+offset,
190  ErrorMsg_text_size-offset, "%s [0x%lx]", symbol->Name,
191  static_cast<long unsigned int>(size_t(symbol->Address)));
192  }
193  else {
194  offset += R_Safe_snprintf(stacktrace_txt+offset,
195  ErrorMsg_text_size-offset, "_ [0x0000]");
196  }
197  }
198  offset += R_Safe_snprintf(stacktrace_txt+offset,
199  ErrorMsg_text_size-offset, "\r\n");
200 
201  SymCleanup(process);
202  }
203 #endif // WIN32
204 #endif // RC_NO_STACKTRACE
205 
206  int written = 0;
207  if (filename != NULL && filename[0] != '\0') {
208 #ifdef MACOS
209  written = snprintf(what_msg, ErrorMsg_what_bufsize,
210  "%s, %s, line %d, load address %p%s", err_msg, filename,
211  line_number, (void *)GetLoadAddress(), stacktrace_txt);
212 #else
213  written = snprintf(what_msg, ErrorMsg_what_bufsize,
214  "%s, %s, line %d%s", err_msg, filename, line_number,
215  stacktrace_txt);
216 #endif
217  }
218  else {
219  written = snprintf(what_msg, ErrorMsg_what_bufsize, "%s%s", err_msg,
220  stacktrace_txt);
221  }
222  if (written >= int(ErrorMsg_what_bufsize)) {
223  for (size_t i=ErrorMsg_what_bufsize-4; i<ErrorMsg_what_bufsize-1;
224  i++) {
225  what_msg[i] = '.';
226  }
227  what_msg[ErrorMsg_what_bufsize-1] = '\0';
228  }
229  }
230 
231  #undef R_Safe_snprintf
232 
233 
234  /// Provides the reason given for the error.
235  /** @return The reason for the exception being thrown.
236  */
237  virtual const char* GetError() const RC_NOEXCEPT {
238  return err_msg;
239  }
240 
241 
242  /// Provides the type of the exception if this is a subclass.
243  /** An empty string is returned for the base class.
244  * @return The exception type defined in a subclass.
245  */
246  virtual const char* GetType() const RC_NOEXCEPT {
247  return type_msg;
248  }
249 
250 
251  /// Return true if the the reason for the exception matches test_err.
252  /** @param test_err An error message to compare with the reason given
253  * in the constructor.
254  * @return True if the error messages are identical.
255  */
256  virtual bool IsError(const char* test_err) const RC_NOEXCEPT {
257  return (0 == strncmp(err_msg, test_err, ErrorMsg_text_bufsize));
258  }
259 
260 
261  /// Returns a full descriptive error message with reason, source location,
262  /// and stack trace.
263  /** Note: The stack trace is only available if on a supported system,
264  * and if the RC_NO_STACKTRACE option was not given in RCconfig.h
265  */
266  virtual const char* what() const RC_NOEXCEPT {
267  return (what_msg);
268  }
269 
270 
271  private:
272  template<class T>
273  inline void SafeFree(T*& p) RC_NOEXCEPT {
274  free(p);
275  p = NULL;
276  }
277 
278 
279  char err_msg[ErrorMsg_text_bufsize];
280  char what_msg[ErrorMsg_what_bufsize];
281  protected:
282  /// @cond PROTECTED
283  char type_msg[ErrorMsg_type_bufsize];
284  /// @endcond
285  };
286 
287 /// \def RC_MAKE_ERROR_TYPE
288 /// Creates new RC::ErrorMsg subtypes.
289 /** Use as: "namespace RC { RC_MAKE_ERROR_TYPE(NewType) }" to make
290  * RC::ErrorMsgNewType
291  */
292 #define RC_MAKE_ERROR_TYPE(Type) \
293  class ErrorMsg##Type : virtual public RC::ErrorMsg {\
294  public:\
295  /** \brief The default constructor. */ \
296  /** Use the convenience macro Throw_RC_Type(Type, "Reason"); */ \
297  ErrorMsg##Type(const char* new_err_msg, const char* filename = "",\
298  int line_number = 0)\
299  : RC::ErrorMsg(new_err_msg, filename, line_number) {\
300  snprintf(type_msg, ErrorMsg_type_bufsize, #Type);\
301  }\
302  };
303 
304 
305  /// A subtype of RC::ErrorMsg for Fatal errors.
307  /// A subtype of RC::ErrorMsg for Null errors.
309  /// A subtype of RC::ErrorMsg for Bounds errors.
311  /// A subtype of RC::ErrorMsg for Memory errors.
313  /// A subtype of RC::ErrorMsg for Bad Cast errors.
315  /// A subtype of RC::ErrorMsg for File related errors.
317  /// A subtype of RC::ErrorMsg for Networking related errors.
319 }
320 
321 
322 /// \def Throw_RC_Error
323 /// Use this to throw an RC:ErrorMsg exception.
324 /** It automatically adds the source file name and line number.
325  * @param err (const char*) The reason for the exception.
326  */
327 #define Throw_RC_Error(err) throw RC::ErrorMsg(err, __FILE__, __LINE__)
328 /// \def Throw_RC_Type
329 /// Use this to throw an RC:ErrorMsg subtype exception.
330 /** It automatically adds the source file name and line number.
331  * @param Type The subtype. e.g. Throw_RC_Type(Null, "Reason")
332  * for ErrorMsgNull.
333  * @param err (const char*) The reason for the exception.
334  */
335 #define Throw_RC_Type(Type, err) throw RC::ErrorMsg##Type(err, __FILE__, __LINE__)
336 /// \def Catch_RC_Error
337 /// Place after a try block to catch RC errors and print the error text.
338 /** Note: Exits on ErrorMsgFatal with return value -1. */
339 #define Catch_RC_Error() catch (RC::ErrorMsgFatal& err) { fprintf(stderr, "Fatal Error: %s\n", err.what()); exit(-1); } catch (RC::ErrorMsg& err) { fprintf(stderr, "Error: %s\n", err.what()); }
340 /// \def Catch_RC_Error_Exit
341 /// Place after a try block to catch RC errors, print the error text, and exit.
342 #define Catch_RC_Error_Exit() catch (RC::ErrorMsg& err) { fprintf(stderr, "Error: %s\n", err.what()); exit(-1); }
343 
344 
345 namespace RC {
346  /// A static class for catching and throwing segfaults.
347  /** Call Segfault::SetHandler() at the beginning of the program, and
348  * segfaults will result in an ErrorMsgFatal with a descriptive stack
349  * trace. In gcc -fnon-call-exceptions is required for the exception to
350  * propagate out of this handler.
351  * Note: This is automatically called with the RC_MAIN { } macro.
352  */
353  class Segfault {
354  public:
355 
356  /// The default handler for a segfault. It throws ErrorMsgFatal.
357  static void Handler(int /*sig*/) {
358  Throw_RC_Type(Fatal, "Segmentation fault");
359  }
360 
361 
362  /// Call this to set the segfault handler to Segfault::Handler.
363  static void SetHandler() {
364 #ifdef WIN32
365  signal(SIGSEGV, Handler);
366 #else
367  struct sigaction sa;
368  memset (&sa, 0, sizeof(sa));
369  sa.sa_handler = Handler;
370  sigaction(SIGSEGV, &sa, NULL);
371 #endif
372  }
373  };
374 }
375 
376 #endif // RC_ERRORS_H
#define Throw_RC_Type(Type, err)
Use this to throw an RC:ErrorMsg subtype exception.
Definition: Errors.h:335
#define RC_MAKE_ERROR_TYPE(Type)
Creates new RC::ErrorMsg subtypes.
Definition: Errors.h:292
#define RC_DYNAMIC_LOAD_FUNC_RAW(FuncName, Library)
Use as RC_DYNAMIC_LOAD_FUNC_RAW(FuncName,Library) to load FuncName from the dynamic library file Libr...
Definition: Macros.h:197
Ptr< Type > Cast()
Dynamically casts to an RC::Ptr of the type used as a template parameter on this function.
Definition: PtrCommon.h:57
The version information and configuration settings for RC Lib.
Provides typedefs and routines for working with primitives.
#define RC_NOEXCEPT
Provides noexcept if available.
Definition: Types.h:64
An exception class that records where the exception was thrown and provides a stack trace.
Definition: Errors.h:66
virtual const char * GetType() const noexcept
Provides the type of the exception if this is a subclass.
Definition: Errors.h:246
virtual const char * GetError() const noexcept
Provides the reason given for the error.
Definition: Errors.h:237
virtual bool IsError(const char *test_err) const noexcept
Return true if the the reason for the exception matches test_err.
Definition: Errors.h:256
ErrorMsg(const char *new_err_msg=NULL, const char *filename=NULL, int line_number=0) noexcept
The default constructor.
Definition: Errors.h:128
virtual const char * what() const noexcept
Returns a full descriptive error message with reason, source location, and stack trace.
Definition: Errors.h:266
A class with static methods for file and directory info and manipulation.
Definition: File.h:1214
Provides both client and server sides of blocking TCP connections.
Definition: Net.h:429
A static class for catching and throwing segfaults.
Definition: Errors.h:353
static void Handler(int)
The default handler for a segfault. It throws ErrorMsgFatal.
Definition: Errors.h:357
static void SetHandler()
Call this to set the segfault handler to Segfault::Handler.
Definition: Errors.h:363
Definition: APtr.h:25
const size_t ErrorMsg_text_size
The maximum size of the char array returned by ErrorMsg::what() and ErrorMsg::GetError(),...
Definition: Errors.h:44
const size_t ErrorMsg_type_bufsize
The maximum size of the char array returned by ErrorMsg::GetType(), including the null.
Definition: Errors.h:51
email address
— (c) 2015