libfoedus-core
FOEDUS Core Library
error_stack.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2014-2015, Hewlett-Packard Development Company, LP.
3  * This program is free software; you can redistribute it and/or modify it
4  * under the terms of the GNU General Public License as published by the Free
5  * Software Foundation; either version 2 of the License, or (at your option)
6  * any later version.
7  *
8  * This program is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
11  * more details. You should have received a copy of the GNU General Public
12  * License along with this program; if not, write to the Free Software
13  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14  *
15  * HP designates this particular file as subject to the "Classpath" exception
16  * as provided by HP in the LICENSE.txt file that accompanied this code.
17  */
18 #ifndef FOEDUS_ERROR_STACK_HPP_
19 #define FOEDUS_ERROR_STACK_HPP_
20 
21 #include <errno.h>
22 #include <stdint.h>
23 
24 #include <cstring>
25 #include <iosfwd>
26 #include <string>
27 
28 #include "foedus/assert_nd.hpp"
29 #include "foedus/compiler.hpp"
30 #include "foedus/cxx11.hpp"
31 #include "foedus/error_code.hpp"
32 
33 namespace foedus {
34 
81 class ErrorStack {
82  public:
84  enum Constants {
87  };
88 
90  ErrorStack();
91 
100  explicit ErrorStack(ErrorCode code);
101 
115  ErrorStack(const char* filename, const char* func, uint32_t linenum, ErrorCode code,
116  const char* custom_message = CXX11_NULLPTR);
117 
119  ErrorStack(const ErrorStack &other);
120 
122  ErrorStack(const ErrorStack &other, const char* filename, const char* func, uint32_t linenum,
123  const char* more_custom_message = CXX11_NULLPTR);
124 
126  ErrorStack& operator=(const ErrorStack &other);
127 
129  ~ErrorStack();
130 
132  bool is_error() const;
133 
135  ErrorCode get_error_code() const;
136 
138  const char* get_message() const;
139 
141  const char* get_custom_message() const;
142 
146  void copy_custom_message(const char* message);
147 
149  void clear_custom_message();
150 
152  void append_custom_message(const char* more_custom_message);
153 
155  uint16_t get_stack_depth() const;
156 
158  uint32_t get_linenum(uint16_t stack_index) const;
159 
161  const char* get_filename(uint16_t stack_index) const;
162 
164  const char* get_func(uint16_t stack_index) const;
165 
167  int get_os_errno() const;
168 
170  void verify() const;
171 
173  void output(std::ostream* ptr) const;
174 
179  void dump_and_abort(const char *abort_message) const;
181  static std::string get_recent_dump_and_abort();
182 
183 
184  friend std::ostream& operator<<(std::ostream& o, const ErrorStack& obj);
185 
186  private:
197  const char* filenames_[kMaxStackDepth];
198 
200  const char* funcs_[kMaxStackDepth];
201 
203  uint32_t linenums_[kMaxStackDepth];
204 
211  mutable const char* custom_message_;
212 
220  int os_errno_;
221 
230  ErrorCode error_code_;
231 
237  uint16_t stack_depth_;
238 
240  mutable bool checked_;
241 };
242 
252 
254  : custom_message_(CXX11_NULLPTR), os_errno_(0), error_code_(kErrorCodeOk),
255  stack_depth_(0), checked_(true) {
256 }
257 
259  : custom_message_(CXX11_NULLPTR), os_errno_(errno), error_code_(code),
260  stack_depth_(0), checked_(false) {
261 }
262 
263 inline ErrorStack::ErrorStack(const char* filename, const char* func, uint32_t linenum,
264  ErrorCode code, const char* custom_message)
265  : custom_message_(CXX11_NULLPTR), os_errno_(errno), error_code_(code), stack_depth_(1),
266  checked_(false) {
267  ASSERT_ND(code != kErrorCodeOk);
268  filenames_[0] = filename;
269  funcs_[0] = func;
270  linenums_[0] = linenum;
271  copy_custom_message(custom_message);
272 }
273 
274 inline ErrorStack::ErrorStack(const ErrorStack &other)
275  : custom_message_(CXX11_NULLPTR) {
276  operator=(other);
277 }
278 
279 inline ErrorStack::ErrorStack(const ErrorStack &other, const char* filename,
280  const char* func, uint32_t linenum, const char* more_custom_message)
281  : custom_message_(CXX11_NULLPTR) {
282  // Invariant: if kErrorCodeOk, no more processing
283  if (LIKELY(other.error_code_ == kErrorCodeOk)) {
284  this->error_code_ = kErrorCodeOk;
285  return;
286  }
287 
288  operator=(other);
289  // augment stacktrace
290  if (stack_depth_ != 0 && stack_depth_ < kMaxStackDepth) {
291  filenames_[stack_depth_] = filename;
292  funcs_[stack_depth_] = func;
293  linenums_[stack_depth_] = linenum;
294  ++stack_depth_;
295  }
296  // augment custom error message
297  if (more_custom_message) {
298  append_custom_message(more_custom_message);
299  }
300 }
301 
303  // Invariant: if kErrorCodeOk, no more processing
304  if (LIKELY(other.error_code_ == kErrorCodeOk)) {
305  this->error_code_ = kErrorCodeOk;
306  return *this;
307  }
308 
309  // this copy assignment is actually a move assignment.
310  // checked_/custom_message_ are mutable for that.
311  custom_message_ = other.custom_message_; // steal.
312  other.custom_message_ = CXX11_NULLPTR; // simply stolen. much more efficient.
313  stack_depth_ = other.stack_depth_;
314  for (int i = 0; i < other.stack_depth_; ++i) {
315  filenames_[i] = other.filenames_[i];
316  funcs_[i] = other.funcs_[i];
317  linenums_[i] = other.linenums_[i];
318  }
319  os_errno_ = other.os_errno_;
320  error_code_ = other.error_code_;
321  checked_ = false;
322  other.checked_ = true;
323  return *this;
324 }
325 
327  // Invariant: if kErrorCodeOk, no more processing
328  if (LIKELY(error_code_ == kErrorCodeOk)) {
329  return;
330  }
331 #ifdef DEBUG
332  // We output warning if some error code is not checked, but we don't do so in release mode.
333  verify();
334 #endif // DEBUG
336 }
337 
338 
340  if (UNLIKELY(custom_message_)) {
341  delete[] custom_message_;
342  custom_message_ = CXX11_NULLPTR;
343  }
344 }
345 
346 inline void ErrorStack::copy_custom_message(const char* message) {
347  // Invariant: if kErrorCodeOk, no more processing
348  if (LIKELY(error_code_ == kErrorCodeOk)) {
349  return;
350  }
351 
353  if (message) {
354  // do NOT use strdup to make sure new/delete everywhere.
355  size_t len = std::strlen(message);
356  char *copied = new char[len + 1]; // +1 for null terminator
357  if (copied) {
358  custom_message_ = copied;
359  std::memcpy(copied, message, len + 1);
360  }
361  }
362 }
363 
364 inline void ErrorStack::append_custom_message(const char* more_custom_message) {
365  // Invariant: if kErrorCodeOk, no more processing
366  if (LIKELY(error_code_ == kErrorCodeOk)) {
367  return;
368  }
369  // augment custom error message
370  if (custom_message_) {
371  // concat
372  size_t cur_len = std::strlen(custom_message_);
373  size_t more_len = std::strlen(more_custom_message);
374  char *copied = new char[cur_len + more_len + 1];
375  if (copied) {
376  custom_message_ = copied;
377  std::memcpy(copied, custom_message_, cur_len);
378  std::memcpy(copied + cur_len, more_custom_message, more_len + 1);
379  }
380  } else {
381  copy_custom_message(more_custom_message); // just put the new message
382  }
383 }
384 
385 inline bool ErrorStack::is_error() const {
386  checked_ = true;
387  return error_code_ != kErrorCodeOk;
388 }
389 
391  checked_ = true;
392  return error_code_;
393 }
394 
395 inline const char* ErrorStack::get_message() const {
396  return get_error_message(error_code_);
397 }
398 
399 inline const char* ErrorStack::get_custom_message() const {
400  // Invariant: if kErrorCodeOk, no more processing
401  if (error_code_ == kErrorCodeOk) {
402  return CXX11_NULLPTR;
403  }
404  return custom_message_;
405 }
406 
407 inline uint16_t ErrorStack::get_stack_depth() const {
408  // Invariant: if kErrorCodeOk, no more processing
409  if (error_code_ == kErrorCodeOk) {
410  return 0;
411  }
412  return stack_depth_;
413 }
414 
415 inline uint32_t ErrorStack::get_linenum(uint16_t stack_index) const {
416  // Invariant: if kErrorCodeOk, no more processing
417  if (error_code_ == kErrorCodeOk) {
418  return 0;
419  }
420  ASSERT_ND(stack_index < stack_depth_);
421  return linenums_[stack_index];
422 }
423 
424 inline const char* ErrorStack::get_filename(uint16_t stack_index) const {
425  // Invariant: if kErrorCodeOk, no more processing
426  if (error_code_ == kErrorCodeOk) {
427  return CXX11_NULLPTR;
428  }
429  ASSERT_ND(stack_index < stack_depth_);
430  return filenames_[stack_index];
431 }
432 
433 inline const char* ErrorStack::get_func(uint16_t stack_index) const {
434  // Invariant: if kErrorCodeOk, no more processing
435  if (error_code_ == kErrorCodeOk) {
436  return CXX11_NULLPTR;
437  }
438  ASSERT_ND(stack_index < stack_depth_);
439  return funcs_[stack_index];
440 }
441 
442 inline int ErrorStack::get_os_errno() const {
443  // Invariant: if kErrorCodeOk, no more processing
444  if (error_code_ == kErrorCodeOk) {
445  return 0;
446  }
447  return os_errno_;
448 }
449 
450 inline void ErrorStack::verify() const {
451  // Invariant: if kErrorCodeOk, no more processing
452  if (LIKELY(error_code_ == kErrorCodeOk)) {
453  return;
454  }
455  if (!checked_) {
456  dump_and_abort("Return value is not checked. ErrorStack must be checked");
457  }
458 }
459 
460 } // namespace foedus
461 
462 // The followings are macros. So, they belong to no namespaces.
463 
480 #define ERROR_STACK(e) foedus::ErrorStack(__FILE__, __FUNCTION__, __LINE__, e)
481 
498 #define ERROR_STACK_MSG(e, m) foedus::ErrorStack(__FILE__, __FUNCTION__, __LINE__, e, m)
499 
517 #define CHECK_ERROR(x)\
518 {\
519  foedus::ErrorStack __e(x);\
520  if (UNLIKELY(__e.is_error())) {\
521  return foedus::ErrorStack(__e, __FILE__, __FUNCTION__, __LINE__);\
522  }\
523 }
524 
533 #define WRAP_ERROR_CODE(x)\
534 {\
535  foedus::ErrorCode __e = x;\
536  if (UNLIKELY(__e != foedus::kErrorCodeOk)) {return ERROR_STACK(__e);}\
537 }
538 
547 #define UNWRAP_ERROR_STACK(x)\
548 {\
549  foedus::ErrorStack __e = x;\
550  if (UNLIKELY(__e.is_error())) { return __e.get_error_code(); }\
551 }
552 
566 #define CHECK_ERROR_MSG(x, m)\
567 {\
568  foedus::ErrorStack __e(x);\
569  if (UNLIKELY(__e.is_error())) {\
570  return foedus::ErrorStack(__e, __FILE__, __FUNCTION__, __LINE__, m);\
571  }\
572 }
573 
590 #define CHECK_OUTOFMEMORY(ptr)\
591 if (UNLIKELY(!ptr)) {\
592  return foedus::ErrorStack(__FILE__, __FUNCTION__, __LINE__, kErrorCodeOutofmemory);\
593 }
594 
610 #define COERCE_ERROR(x)\
611 {\
612  foedus::ErrorStack __e(x);\
613  if (UNLIKELY(__e.is_error())) {\
614  __e.dump_and_abort("Unexpected error happened");\
615  }\
616 }
617 
618 
625 #define COERCE_ERROR_CODE(x)\
626 {\
627  foedus::ErrorCode __e = x;\
628  if (UNLIKELY(__e != foedus::kErrorCodeOk)) {\
629  ERROR_STACK(__e).dump_and_abort("Unexpected error happened");\
630  }\
631 }
632 
633 #endif // FOEDUS_ERROR_STACK_HPP_
static std::string get_recent_dump_and_abort()
Signal handler can get the dump information via this.
Definition: error_stack.cpp:74
#define CXX11_NULLPTR
Used in public headers in place of "nullptr" of C++11.
Definition: cxx11.hpp:132
Root package of FOEDUS (Fast Optimistic Engine for Data Unification Services).
Definition: assert_nd.hpp:44
Brings error stacktrace information as return value of functions.
Definition: error_stack.hpp:81
#define LIKELY(x)
Hints that x is highly likely true.
Definition: compiler.hpp:103
ErrorStack & operator=(const ErrorStack &other)
Assignment operator.
void append_custom_message(const char *more_custom_message)
Appends more custom error message at the end.
const char * get_filename(uint16_t stack_index) const
Returns the file name of the given stack position.
ErrorCode get_error_code() const
Return the integer error code.
0 means no-error.
Definition: error_code.hpp:87
friend std::ostream & operator<<(std::ostream &o, const ErrorStack &obj)
Definition: error_stack.cpp:78
Maximum stack trace depth.
Definition: error_stack.hpp:86
uint16_t get_stack_depth() const
Returns the depth of stack this error code has collected.
void dump_and_abort(const char *abort_message) const
Describe this object to std::cerr and then abort.
Definition: error_stack.cpp:61
uint32_t get_linenum(uint16_t stack_index) const
Returns the line number of the given stack position.
const char * get_custom_message() const
Returns the custom error message.
const char * get_func(uint16_t stack_index) const
Returns the function name of the given stack position.
void output(std::ostream *ptr) const
Describe this object to the given stream.
Definition: error_stack.cpp:30
const char * get_message() const
Returns the error message inferred by the error code.
const ErrorStack kRetOk
Normal return value for no-error case.
const char * get_error_message(ErrorCode code)
Returns the error messages corresponding to ErrorCode enum defined in error_code.xmacro.
Definition: error_code.hpp:120
void copy_custom_message(const char *message)
Copy the given custom message into this object.
Constants
Constant values.
Definition: error_stack.hpp:84
~ErrorStack()
Will warn in stderr if the error code is not checked yet.
int get_os_errno() const
Global errno of the system as of instantiation of this error stack.
#define UNLIKELY(x)
Hints that x is highly likely false.
Definition: compiler.hpp:104
#define ASSERT_ND(x)
A warning-free wrapper macro of assert() that has no performance effect in release mode even when 'x'...
Definition: assert_nd.hpp:72
void clear_custom_message()
Deletes custom message from this object.
void verify() const
Output a warning to stderr if the error is not checked yet.
ErrorCode
Enum of error codes defined in error_code.xmacro.
Definition: error_code.hpp:85
bool is_error() const
Returns if this return code is not kErrorCodeOk.
ErrorStack()
Empty constructor.