1 /*
2  fuzzylite (R), a fuzzy logic control library in C++.
3  Copyright (C) 2010-2017 FuzzyLite Limited. All rights reserved.
4  Author: Juan Rada-Vilela, Ph.D. <jcrada@fuzzylite.com>
5 
6  This file is part of fuzzylite.
7 
8  fuzzylite is free software: you can redistribute it and/or modify it under
9  the terms of the FuzzyLite License included with the software.
10 
11  You should have received a copy of the FuzzyLite License along with
12  fuzzylite. If not, see <http://www.fuzzylite.com/license/>.
13 
14  fuzzylite is a registered trademark of FuzzyLite Limited.
15  */
16 
17 #include "fl/Exception.h"
18 
19 
20 #ifdef FL_BACKTRACE
21 
22 #ifdef FL_UNIX
23 #include <execinfo.h>
24 
25 #elif defined FL_WINDOWS
26 #include <windows.h>
27 #include <winbase.h>
28 
29 #ifndef __MINGW32__
30 /*Disable warning 8.1\Include\um\dbghelp.h(1544):
31 warning C4091: 'typedef ': ignored on left of '' when no variable is declared*/
32 #pragma warning (push)
33 #pragma warning (disable:4091)
34 #include <dbghelp.h>
35 #pragma warning (pop)
36 #endif
37 
38 #endif
39 
40 #endif
41 
42 
43 #include <csignal>
44 #include <cstring>
45 
46 namespace fl {
47 
Exception(const std::string & what)48     Exception::Exception(const std::string& what)
49     : std::exception(), _what(what) {
50         FL_DBG(this->what());
51     }
52 
Exception(const std::string & what,const std::string & file,int line,const std::string & function)53     Exception::Exception(const std::string& what, const std::string& file, int line,
54             const std::string& function)
55     : std::exception(), _what(what) {
56         append(file, line, function);
57         FL_DBG(this->what());
58     }
59 
~Exception()60     Exception::~Exception() FL_INOEXCEPT { }
61 
setWhat(const std::string & what)62     void Exception::setWhat(const std::string& what) {
63         this->_what = what;
64     }
65 
getWhat() const66     std::string Exception::getWhat() const {
67         return this->_what;
68     }
69 
what() const70     const char* Exception::what() const FL_INOEXCEPT {
71         return this->_what.c_str();
72     }
73 
append(const std::string & whatElse)74     void Exception::append(const std::string& whatElse) {
75         this->_what += whatElse + "\n";
76     }
77 
append(const std::string & file,int line,const std::string & function)78     void Exception::append(const std::string& file, int line, const std::string& function) {
79         std::ostringstream ss;
80         ss << "\n{at " << file << "::" << function << "() [line:" << line << "]}";
81         _what += ss.str();
82     }
83 
append(const std::string & whatElse,const std::string & file,int line,const std::string & function)84     void Exception::append(const std::string& whatElse,
85             const std::string& file, int line, const std::string& function) {
86         append(whatElse);
87         append(file, line, function);
88     }
89 
btCallStack()90     std::string Exception::btCallStack() {
91 #ifndef FL_BACKTRACE
92         return "[backtrace disabled] fuzzylite was built without -DFL_BACKTRACE";
93 #elif defined FL_UNIX
94         std::ostringstream btStream;
95         const int bufferSize = 30;
96         void* buffer[bufferSize];
97         int backtraceSize = ::backtrace(buffer, bufferSize);
98         char **btSymbols = ::backtrace_symbols(buffer, backtraceSize);
99         if (btSymbols == fl::null) {
100             btStream << "[backtrace error] no symbols could be retrieved";
101         } else {
102             if (backtraceSize == 0) {
103                 btStream << "[backtrace is empty]";
104             }
105             for (int i = 0; i < backtraceSize; ++i) {
106                 btStream << btSymbols[i] << "\n";
107             }
108         }
109         ::free(btSymbols);
110         return btStream.str();
111 
112 
113 #elif defined FL_WINDOWS && ! defined __MINGW32__
114         std::ostringstream btStream;
115         const int bufferSize = 30;
116         void* buffer[bufferSize];
117         SymInitialize(GetCurrentProcess(), fl::null, TRUE);
118 
119         int backtraceSize = CaptureStackBackTrace(0, bufferSize, buffer, fl::null);
120         SYMBOL_INFO* btSymbol = (SYMBOL_INFO *) calloc(sizeof ( SYMBOL_INFO) + 256 * sizeof ( char), 1);
121         if (not btSymbol) {
122             btStream << "[backtrace error] no symbols could be retrieved";
123         } else {
124             btSymbol->MaxNameLen = 255;
125             btSymbol->SizeOfStruct = sizeof ( SYMBOL_INFO);
126             if (backtraceSize == 0) {
127                 btStream << "[backtrace is empty]";
128             }
129             for (int i = 0; i < backtraceSize; ++i) {
130                 SymFromAddr(GetCurrentProcess(), (DWORD64) (buffer[ i ]), 0, btSymbol);
131                 btStream << (backtraceSize - i - 1) << ": " <<
132                         btSymbol->Name << " at 0x" << btSymbol->Address << "\n";
133             }
134         }
135         ::free(btSymbol);
136         return btStream.str();
137 #else
138         return "[backtrace missing] supported only in Unix and Windows platforms";
139 #endif
140     }
141 
signalHandler(int unixSignal)142     void Exception::signalHandler(int unixSignal) {
143         std::ostringstream ex;
144         ex << "[unexpected signal " << unixSignal << "] ";
145 #ifdef FL_UNIX
146         ex << ::strsignal(unixSignal);
147 #endif
148         ex << "\nBACKTRACE:\n" << btCallStack();
149         Exception::catchException(Exception(ex.str(), FL_AT));
150         ::exit(EXIT_FAILURE);
151     }
152 
convertToException(int unixSignal)153     void Exception::convertToException(int unixSignal) {
154         std::string signalDescription;
155 #ifdef FL_UNIX
156         //Unblock the signal
157         sigset_t empty;
158         sigemptyset(&empty);
159         sigaddset(&empty, unixSignal);
160         sigprocmask(SIG_UNBLOCK, &empty, fl::null);
161         signalDescription = ::strsignal(unixSignal);
162 #endif
163         std::ostringstream ex;
164         ex << "[signal " << unixSignal << "] " << signalDescription << "\n";
165         ex << "BACKTRACE:\n" << btCallStack();
166         throw Exception(ex.str(), FL_AT);
167     }
168 
terminate()169     void Exception::terminate() {
170         Exception::catchException(Exception("[unexpected exception] BACKTRACE:\n" + btCallStack(), FL_AT));
171         ::exit(EXIT_FAILURE);
172     }
173 
catchException(const std::exception & exception)174     void Exception::catchException(const std::exception& exception) {
175         std::ostringstream ss;
176         ss << exception.what();
177         std::string backtrace = btCallStack();
178         if (not backtrace.empty()) {
179             ss << "\n\nBACKTRACE:\n" << backtrace;
180         }
181         FL_LOG(ss.str());
182     }
183 
184 }
185