rcolyer.net
RC Lib  Version 202403231100
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 
35 namespace RC {
36  /// The maximum size of the char array returned by ErrorMsg::what() and
37  /// ErrorMsg::GetError(), including the null.
38  const size_t ErrorMsg_text_size = 4095;
39  /// @cond UNDOC
40  const size_t ErrorMsg_text_bufsize = 4096;
41  const size_t ErrorMsg_what_bufsize = 4096+1024;
42  /// @endcond
43  /// The maximum size of the char array returned by ErrorMsg::GetType(),
44  /// including the null.
45  const size_t ErrorMsg_type_bufsize = 256;
46 
47  /// An exception class that records where the exception was thrown and
48  /// provides a stack trace.
49  /** Use with the macro Throw_RC_Error("Reason"); to automatically pick up
50  * the source filename and line number. This exception should be used
51  * liberally any time an error would be informative for resolving a bug.
52  *
53  * Stack tracing is implemented for Linux and Windows. For Linux with g++
54  * or clang compile with -rdynamic for full symbols in the stack trace
55  * output from what(). For Windows symbols cannot be automatically provided
56  * with mingw, but the addresses provided in what() can be resolved by
57  * providing them as stdin to "addr2line -pfe myprogram.exe" for any program
58  * compiled with -ggdb.
59  */
60  class ErrorMsg : virtual public std::exception {
61  size_t AmountWritten(size_t size, size_t written) {
62  return (size <= written) ? size-1 : written;
63  }
64  // Returns amount actually written.
65  /// @cond UNDOC
66  #define R_Safe_snprintf(str, size, ...) \
67  AmountWritten(size, size_t(snprintf(str, size, __VA_ARGS__)));
68  /// @endcond
69 
70  public:
71 
72  /// The default constructor.
73  /** Use the convenience macro Throw_RC_Error("Reason"); which uses
74  * __FILE__ and __LINE__ to extract the location of the throw.
75  * @param new_err_msg The reason for the exception.
76  * @param filename The source code file that the exception was triggered
77  * in.
78  * @param line_number The source code line number that the exception was
79  * triggered on.
80  */
81  ErrorMsg(const char* new_err_msg=NULL, const char* filename=NULL,
82  int line_number = 0) RC_NOEXCEPT {
83  size_t offset = 0;
84  char stacktrace_txt[ErrorMsg_text_bufsize] = {0};
85 
86  type_msg[0] = '\0';
87  if (new_err_msg == NULL) {
88  err_msg[0] = '\0';
89  }
90  else {
91  strncpy(err_msg, new_err_msg, ErrorMsg_text_size);
92  err_msg[ErrorMsg_text_bufsize-1] = '\0';
93  }
94 
95 #ifndef RC_NO_STACKTRACE
96  // Get the stack trace if we can.
97  int backtrace_cnt = 0;
98  const int backtrace_size = 128;
99  void *bt_buffer[backtrace_size];
100 #ifdef unix
101  char **backtrace_symb;
102  backtrace_cnt = backtrace(bt_buffer, backtrace_size);
103  backtrace_symb = backtrace_symbols(bt_buffer, backtrace_cnt);
104 
105  if (backtrace_symb != NULL) {
106  for (int i=0; i<backtrace_cnt; i++) {
107  offset += R_Safe_snprintf(stacktrace_txt+offset,
108  ErrorMsg_text_size-offset, "\n\t%s",
109  backtrace_symb[backtrace_cnt-i-1]);
110  }
111  offset += R_Safe_snprintf(stacktrace_txt+offset,
112  ErrorMsg_text_size-offset, "\n");
113 
114  SafeFree(backtrace_symb);
115  }
116 
117 // end unix
118 #elif defined(WIN32)
119  RC_DYNAMIC_LOAD_FUNC_RAW(SymInitialize, TEXT("Dbghelp.dll"));
120  RC_DYNAMIC_LOAD_FUNC_RAW(SymFromAddr, TEXT("Dbghelp.dll"));
121  RC_DYNAMIC_LOAD_FUNC_RAW(SymCleanup, TEXT("Dbghelp.dll"));
122 
123  if (SymInitialize && SymFromAddr && SymCleanup) {
124  HANDLE process = GetCurrentProcess();
125  SymInitialize(process, NULL, true);
126 
127  // Allocate this variable length SYMBOL_INFO structure on stack.
128  const size_t symbolnamelen = 255;
129  char symboldata[sizeof(SYMBOL_INFO)+(symbolnamelen+1)];
130  SYMBOL_INFO *symbol = reinterpret_cast<SYMBOL_INFO*>(symboldata);
131  symbol->MaxNameLen = symbolnamelen;
132  symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
133 
134  backtrace_cnt = CaptureStackBackTrace(0, backtrace_size,
135  bt_buffer, NULL);
136 
137  for(int i=0; i<backtrace_cnt; i++) {
138  offset += R_Safe_snprintf(stacktrace_txt+offset,
139  ErrorMsg_text_size-offset, "\r\n\t0x%lx : ",
140  static_cast<long unsigned int>(size_t(bt_buffer[i])));
141  if (SymFromAddr(process, DWORD64(bt_buffer[i]), 0, symbol)) {
142  offset += R_Safe_snprintf(stacktrace_txt+offset,
143  ErrorMsg_text_size-offset, "%s [0x%lx]", symbol->Name,
144  static_cast<long unsigned int>(size_t(symbol->Address)));
145  }
146  else {
147  offset += R_Safe_snprintf(stacktrace_txt+offset,
148  ErrorMsg_text_size-offset, "_ [0x0000]");
149  }
150  }
151  offset += R_Safe_snprintf(stacktrace_txt+offset,
152  ErrorMsg_text_size-offset, "\r\n");
153 
154  SymCleanup(process);
155  }
156 #endif // WIN32
157 #endif // RC_NO_STACKTRACE
158 
159  int written = 0;
160  if (filename != NULL && filename[0] != '\0') {
161  written = snprintf(what_msg, ErrorMsg_what_bufsize,
162  "%s, %s, line %d%s", err_msg, filename, line_number,
163  stacktrace_txt);
164  }
165  else {
166  written = snprintf(what_msg, ErrorMsg_what_bufsize, "%s%s", err_msg,
167  stacktrace_txt);
168  }
169  if (written >= int(ErrorMsg_what_bufsize)) {
170  for (size_t i=ErrorMsg_what_bufsize-4; i<ErrorMsg_what_bufsize-1;
171  i++) {
172  what_msg[i] = '.';
173  }
174  what_msg[ErrorMsg_what_bufsize-1] = '\0';
175  }
176  }
177 
178  #undef R_Safe_snprintf
179 
180 
181  /// Provides the reason given for the error.
182  /** @return The reason for the exception being thrown.
183  */
184  virtual const char* GetError() const RC_NOEXCEPT {
185  return err_msg;
186  }
187 
188 
189  /// Provides the type of the exception if this is a subclass.
190  /** An empty string is returned for the base class.
191  * @return The exception type defined in a subclass.
192  */
193  virtual const char* GetType() const RC_NOEXCEPT {
194  return type_msg;
195  }
196 
197 
198  /// Return true if the the reason for the exception matches test_err.
199  /** @param test_err An error message to compare with the reason given
200  * in the constructor.
201  * @return True if the error messages are identical.
202  */
203  virtual bool IsError(const char* test_err) const RC_NOEXCEPT {
204  return (0 == strncmp(err_msg, test_err, ErrorMsg_text_bufsize));
205  }
206 
207 
208  /// Returns a full descriptive error message with reason, source location,
209  /// and stack trace.
210  /** Note: The stack trace is only available if on a supported system,
211  * and if the RC_NO_STACKTRACE option was not given in RCconfig.h
212  */
213  virtual const char* what() const RC_NOEXCEPT {
214  return (what_msg);
215  }
216 
217 
218  private:
219  template<class T>
220  inline void SafeFree(T*& p) RC_NOEXCEPT {
221  free(p);
222  p = NULL;
223  }
224 
225 
226  char err_msg[ErrorMsg_text_bufsize];
227  char what_msg[ErrorMsg_what_bufsize];
228  protected:
229  /// @cond PROTECTED
230  char type_msg[ErrorMsg_type_bufsize];
231  /// @endcond
232  };
233 
234 /// \def RC_MAKE_ERROR_TYPE
235 /// Creates new RC::ErrorMsg subtypes.
236 /** Use as: "namespace RC { RC_MAKE_ERROR_TYPE(NewType) }" to make
237  * RC::ErrorMsgNewType
238  */
239 #define RC_MAKE_ERROR_TYPE(Type) \
240  class ErrorMsg##Type : virtual public RC::ErrorMsg {\
241  public:\
242  /** \brief The default constructor. */ \
243  /** Use the convenience macro Throw_RC_Type(Type, "Reason"); */ \
244  ErrorMsg##Type(const char* new_err_msg, const char* filename = "",\
245  int line_number = 0)\
246  : RC::ErrorMsg(new_err_msg, filename, line_number) {\
247  snprintf(type_msg, ErrorMsg_type_bufsize, #Type);\
248  }\
249  };
250 
251 
252  /// A subtype of RC::ErrorMsg for Fatal errors.
254  /// A subtype of RC::ErrorMsg for Null errors.
256  /// A subtype of RC::ErrorMsg for Bounds errors.
258  /// A subtype of RC::ErrorMsg for Memory errors.
260  /// A subtype of RC::ErrorMsg for Bad Cast errors.
262  /// A subtype of RC::ErrorMsg for File related errors.
264  /// A subtype of RC::ErrorMsg for Networking related errors.
266 }
267 
268 
269 /// \def Throw_RC_Error
270 /// Use this to throw an RC:ErrorMsg exception.
271 /** It automatically adds the source file name and line number.
272  * @param err (const char*) The reason for the exception.
273  */
274 #define Throw_RC_Error(err) throw RC::ErrorMsg(err, __FILE__, __LINE__)
275 /// \def Throw_RC_Type
276 /// Use this to throw an RC:ErrorMsg subtype exception.
277 /** It automatically adds the source file name and line number.
278  * @param Type The subtype. e.g. Throw_RC_Type(Null, "Reason")
279  * for ErrorMsgNull.
280  * @param err (const char*) The reason for the exception.
281  */
282 #define Throw_RC_Type(Type, err) throw RC::ErrorMsg##Type(err, __FILE__, __LINE__)
283 /// \def Catch_RC_Error
284 /// Place after a try block to catch RC errors and print the error text.
285 /** Note: Exits on ErrorMsgFatal with return value -1. */
286 #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()); }
287 /// \def Catch_RC_Error_Exit
288 /// Place after a try block to catch RC errors, print the error text, and exit.
289 #define Catch_RC_Error_Exit() catch (RC::ErrorMsg& err) { fprintf(stderr, "Error: %s\n", err.what()); exit(-1); }
290 
291 
292 namespace RC {
293  /// A static class for catching and throwing segfaults.
294  /** Call Segfault::SetHandler() at the beginning of the program, and
295  * segfaults will result in an ErrorMsgFatal with a descriptive stack
296  * trace. In gcc -fnon-call-exceptions is required for the exception to
297  * propagate out of this handler.
298  * Note: This is automatically called with the RC_MAIN { } macro.
299  */
300  class Segfault {
301  public:
302 
303  /// The default handler for a segfault. It throws ErrorMsgFatal.
304  static void Handler(int /*sig*/) {
305  Throw_RC_Type(Fatal, "Segmentation fault");
306  }
307 
308 
309  /// Call this to set the segfault handler to Segfault::Handler.
310  static void SetHandler() {
311 #ifdef WIN32
312  signal(SIGSEGV, Handler);
313 #else
314  struct sigaction sa;
315  memset (&sa, 0, sizeof(sa));
316  sa.sa_handler = Handler;
317  sigaction(SIGSEGV, &sa, NULL);
318 #endif
319  }
320  };
321 }
322 
323 #endif // RC_ERRORS_H
#define Throw_RC_Type(Type, err)
Use this to throw an RC:ErrorMsg subtype exception.
Definition: Errors.h:282
#define RC_MAKE_ERROR_TYPE(Type)
Creates new RC::ErrorMsg subtypes.
Definition: Errors.h:239
#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:60
virtual const char * GetType() const noexcept
Provides the type of the exception if this is a subclass.
Definition: Errors.h:193
virtual const char * GetError() const noexcept
Provides the reason given for the error.
Definition: Errors.h:184
virtual bool IsError(const char *test_err) const noexcept
Return true if the the reason for the exception matches test_err.
Definition: Errors.h:203
ErrorMsg(const char *new_err_msg=NULL, const char *filename=NULL, int line_number=0) noexcept
The default constructor.
Definition: Errors.h:81
virtual const char * what() const noexcept
Returns a full descriptive error message with reason, source location, and stack trace.
Definition: Errors.h:213
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:300
static void Handler(int)
The default handler for a segfault. It throws ErrorMsgFatal.
Definition: Errors.h:304
static void SetHandler()
Call this to set the segfault handler to Segfault::Handler.
Definition: Errors.h:310
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:38
const size_t ErrorMsg_type_bufsize
The maximum size of the char array returned by ErrorMsg::GetType(), including the null.
Definition: Errors.h:45
email address
— (c) 2015