PageRenderTime 366ms CodeModel.GetById 201ms app.highlight 77ms RepoModel.GetById 84ms app.codeStats 1ms

/mordor/exception.cpp

http://github.com/mozy/mordor
C++ | 375 lines | 341 code | 29 blank | 5 comment | 24 complexity | c903ccbc975cbac7d424dd8d03855912 MD5 | raw file
  1// Copyright (c) 2009 - Mozy, Inc.
  2
  3#include "exception.h"
  4
  5#ifdef WINDOWS
  6#include <dbghelp.h>
  7
  8#include "runtime_linking.h"
  9
 10#pragma comment(lib, "dbghelp")
 11#else
 12#include <errno.h>
 13#include <execinfo.h>
 14#include <netdb.h>
 15#include <string.h>
 16#endif
 17
 18#include <boost/thread/mutex.hpp>
 19
 20#include "socket.h"
 21
 22namespace Mordor {
 23
 24#ifdef WINDOWS
 25static BOOL g_useSymbols;
 26
 27namespace {
 28
 29static struct Initializer {
 30    Initializer()
 31    {
 32        SymSetOptions(SYMOPT_DEFERRED_LOADS |
 33            SYMOPT_FAIL_CRITICAL_ERRORS |
 34            SYMOPT_LOAD_LINES |
 35            SYMOPT_NO_PROMPTS);
 36        g_useSymbols = SymInitialize(GetCurrentProcess(), NULL, TRUE);
 37    }
 38
 39    ~Initializer()
 40    {
 41        if (g_useSymbols)
 42            SymCleanup(GetCurrentProcess());
 43    }
 44} g_init;
 45
 46}
 47#endif
 48
 49std::string to_string(const std::vector<void *> backtrace)
 50{
 51#ifdef WINDOWS
 52    static boost::mutex s_mutex;
 53    boost::mutex::scoped_lock lock(s_mutex);
 54#endif
 55    std::ostringstream os;
 56#ifdef POSIX
 57    boost::shared_ptr<char *> symbols(backtrace_symbols(&backtrace[0],
 58        backtrace.size()), &free);
 59#endif
 60    for (size_t i = 0; i < backtrace.size(); ++i) {
 61        if (i != 0)
 62            os << std::endl;
 63#ifdef WINDOWS
 64        os << backtrace[i];
 65        if (g_useSymbols) {
 66            char buf[sizeof(SYMBOL_INFO) + MAX_SYM_NAME - 1];
 67            SYMBOL_INFO *symbol = (SYMBOL_INFO*)buf;
 68            symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
 69            symbol->MaxNameLen = MAX_SYM_NAME;
 70            DWORD64 displacement64 = 0;
 71            if (pSymFromAddr(GetCurrentProcess(), (DWORD64)backtrace[i],
 72                &displacement64, symbol)) {
 73                os << ": " << symbol->Name << "+" << displacement64;
 74            }
 75            IMAGEHLP_LINE64 line;
 76            line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
 77            DWORD displacement = 0;
 78            if (pSymGetLineFromAddr64(GetCurrentProcess(),
 79                (DWORD64)backtrace[i], &displacement, &line)) {
 80                os << ": " << line.FileName << "(" << line.LineNumber << ")+"
 81                    << displacement;
 82            }
 83        }
 84#else
 85        if (symbols)
 86            os << symbols.get()[i];
 87        else
 88            os << backtrace[i];
 89#endif
 90    }
 91    return os.str();
 92}
 93
 94std::string to_string( errinfo_backtrace const &bt )
 95{
 96    return to_string(bt.value());
 97}
 98
 99#ifdef WINDOWS
100std::string to_string( errinfo_lasterror const &e)
101{
102    return boost::lexical_cast<std::string>(e.value());
103}
104#else
105std::string to_string( errinfo_gaierror const &e)
106{
107    std::ostringstream os;
108    os << e.value() << ", \"" << gai_strerror(e.value()) << "\"";
109    return os.str();
110}
111#endif
112
113std::vector<void *> backtrace(int framesToSkip)
114{
115    std::vector<void *> result;
116#ifdef WINDOWS
117    result.resize(64);
118    WORD count = pRtlCaptureStackBackTrace(1 + framesToSkip, 61 - framesToSkip, &result[0], NULL);
119    result.resize(count);
120#else
121    result.resize(64);
122    int count = ::backtrace(&result[0], 64);
123    result.resize(count);
124    framesToSkip = std::min(count, framesToSkip + 1);
125    result.erase(result.begin(), result.begin() + framesToSkip);
126#endif
127    return result;
128}
129
130void removeTopFrames(boost::exception &ex, int framesToSkip)
131{
132    const std::vector<void *> *oldbt = boost::get_error_info<errinfo_backtrace>(ex);
133    if (oldbt && !oldbt->empty()) {
134        std::vector<void *> newbt(*oldbt);
135        std::vector<void *> bt = backtrace();
136        size_t count = 0;
137        ++framesToSkip;
138        for (; count + framesToSkip < newbt.size() &&
139            count + framesToSkip < bt.size(); ++count) {
140            if (bt[bt.size() - count - 1] != newbt[newbt.size() - count - 1])
141                break;
142        }
143        count -= framesToSkip;
144        newbt.resize(newbt.size() > count ? newbt.size() - count : 0);
145        ex << errinfo_backtrace(newbt);
146    }
147}
148
149void rethrow_exception(boost::exception_ptr const & ep)
150{
151    // Take the backtrace from here, to avoid additional frames from the
152    // exception handler
153    std::vector<void *> bt = backtrace(1);
154    try {
155        boost::rethrow_exception(ep);
156    } catch (boost::exception &e) {
157        const std::vector<void *> *oldbt =
158            boost::get_error_info<errinfo_backtrace>(e);
159        if (oldbt)
160            bt.insert(bt.begin(), oldbt->begin(), oldbt->end());
161        e << errinfo_backtrace(bt);
162        throw;
163    }
164}
165
166#ifdef WINDOWS
167#define WSA(error) WSA ## error
168#else
169#define WSA(error) error
170#endif
171
172static void throwSocketException(error_t error)
173{
174    switch (error) {
175        case WSA(EAFNOSUPPORT):
176            throw boost::enable_current_exception(OperationNotSupportedException())
177                << errinfo_nativeerror(error);
178        case WSA(EADDRINUSE):
179#ifdef WINDOWS
180        // WSAEACESS is returned from bind when you set SO_REUSEADDR, and
181        // another socket has set SO_EXCLUSIVEADDRUSE
182        case WSAEACCES:
183        case ERROR_ADDRESS_ALREADY_ASSOCIATED:
184#endif
185            throw boost::enable_current_exception(AddressInUseException())
186                << errinfo_nativeerror(error);
187        case WSA(ECONNABORTED):
188#ifdef WINDOWS
189        case ERROR_CONNECTION_ABORTED:
190#endif
191            throw boost::enable_current_exception(ConnectionAbortedException())
192                << errinfo_nativeerror(error);
193        case WSA(ECONNRESET):
194            throw boost::enable_current_exception(ConnectionResetException())
195                << errinfo_nativeerror(error);
196        case WSA(ECONNREFUSED):
197#ifdef WINDOWS
198        case ERROR_CONNECTION_REFUSED:
199#endif
200            throw boost::enable_current_exception(ConnectionRefusedException())
201                << errinfo_nativeerror(error);
202        case WSA(EHOSTDOWN):
203            throw boost::enable_current_exception(HostDownException())
204                << errinfo_nativeerror(error);
205        case WSA(EHOSTUNREACH):
206#ifdef WINDOWS
207        case ERROR_HOST_UNREACHABLE:
208#endif
209            throw boost::enable_current_exception(HostUnreachableException())
210                << errinfo_nativeerror(error);
211        case WSA(ENETDOWN):
212            throw boost::enable_current_exception(NetworkDownException())
213                << errinfo_nativeerror(error);
214        case WSA(ENETRESET):
215#ifdef WINDOWS
216        case ERROR_NETNAME_DELETED:
217#endif
218            throw boost::enable_current_exception(NetworkResetException())
219                << errinfo_nativeerror(error);
220        case WSA(ENETUNREACH):
221#ifdef WINDOWS
222        case ERROR_NETWORK_UNREACHABLE:
223#endif
224            throw boost::enable_current_exception(NetworkUnreachableException())
225                << errinfo_nativeerror(error);
226        case WSA(EPROTOTYPE):
227            throw boost::enable_current_exception(WrongProtocolTypeException())
228                << errinfo_nativeerror(error);
229        case WSA(ETIMEDOUT):
230            throw boost::enable_current_exception(TimedOutException())
231                << errinfo_nativeerror(error);
232        default:
233            break;
234    }
235}
236
237#ifdef WINDOWS
238error_t lastError()
239{
240    return GetLastError();
241}
242
243void lastError(error_t error)
244{
245    SetLastError(error);
246}
247
248std::ostream &operator <<(std::ostream &os, error_t error)
249{
250    if (error == ERROR_SUCCESS)
251        return os << "0, \"The operation completed successfully.\"";
252    os << (DWORD)error;
253    std::string result;
254    char *desc;
255    DWORD numChars = FormatMessageA(
256        FORMAT_MESSAGE_ALLOCATE_BUFFER |
257        FORMAT_MESSAGE_FROM_SYSTEM |
258        FORMAT_MESSAGE_IGNORE_INSERTS,
259        NULL,
260        error, 0,
261        (char*)&desc, 0, NULL);
262    if (numChars > 0) {
263        if (desc[numChars - 1] == '\n') {
264            desc[numChars - 1] = '\0';
265            if (desc[numChars - 2] == '\r')
266                desc[numChars - 2] = '\0';
267        }
268        try {
269            os << ", \"" << desc << "\"";
270        } catch (...) {
271            LocalFree((HANDLE)desc);
272            throw;
273        }
274        LocalFree((HANDLE)desc);
275    }
276    return os;
277}
278
279void throwExceptionFromLastError(error_t error)
280{
281    switch (error) {
282        case ERROR_INVALID_HANDLE:
283        case WSAENOTSOCK:
284            throw boost::enable_current_exception(BadHandleException())
285                << errinfo_nativeerror(error);
286        case ERROR_FILE_NOT_FOUND:
287            throw boost::enable_current_exception(FileNotFoundException())
288                << errinfo_nativeerror(error);
289        case ERROR_ACCESS_DENIED:
290            throw boost::enable_current_exception(AccessDeniedException())
291                << errinfo_nativeerror(error);
292        case ERROR_OPERATION_ABORTED:
293            throw boost::enable_current_exception(OperationAbortedException())
294                << errinfo_nativeerror(error);
295        case ERROR_BROKEN_PIPE:
296            throw boost::enable_current_exception(UnexpectedEofException())
297                << errinfo_nativeerror(error);
298        case WSAESHUTDOWN:
299            throw boost::enable_current_exception(BrokenPipeException())
300                << errinfo_nativeerror(error);
301        case ERROR_SHARING_VIOLATION:
302        case ERROR_LOCK_VIOLATION:
303            throw boost::enable_current_exception(SharingViolation())
304                << errinfo_nativeerror(error);
305        case ERROR_CANT_RESOLVE_FILENAME:
306            throw boost::enable_current_exception(UnresolvablePathException())
307                << errinfo_nativeerror(error);
308        case ERROR_DISK_FULL:
309            throw boost::enable_current_exception(OutOfDiskSpaceException())
310                << errinfo_nativeerror(error);
311        case ERROR_NO_UNICODE_TRANSLATION:
312            throw boost::enable_current_exception(InvalidUnicodeException())
313                << errinfo_nativeerror(error);
314        default:
315            throwSocketException(error);
316            throw boost::enable_current_exception(NativeException())
317                << errinfo_nativeerror(error);
318    }
319}
320#else
321
322error_t lastError()
323{
324    return errno;
325}
326
327void lastError(error_t error)
328{
329    errno = error;
330}
331
332std::ostream &operator <<(std::ostream &os, error_t error)
333{
334    return os << (int)error << ", \"" << strerror(error) << "\"";
335}
336
337void throwExceptionFromLastError(error_t error)
338{
339    switch (error) {
340        case EBADF:
341            throw boost::enable_current_exception(BadHandleException())
342                << errinfo_nativeerror(error);
343        case ENOENT:
344            throw boost::enable_current_exception(FileNotFoundException())
345                << errinfo_nativeerror(error);
346        case EACCES:
347            throw boost::enable_current_exception(AccessDeniedException())
348                << errinfo_nativeerror(error);
349        case ECANCELED:
350            throw boost::enable_current_exception(OperationAbortedException())
351                << errinfo_nativeerror(error);
352        case EPIPE:
353            throw boost::enable_current_exception(BrokenPipeException())
354                << errinfo_nativeerror(error);
355        case EISDIR:
356            throw boost::enable_current_exception(IsDirectoryException())
357                << errinfo_nativeerror(error);
358        case ENOTDIR:
359            throw boost::enable_current_exception(IsNotDirectoryException())
360                << errinfo_nativeerror(error);
361        case ELOOP:
362            throw boost::enable_current_exception(TooManySymbolicLinksException())
363                << errinfo_nativeerror(error);
364        case ENOSPC:
365            throw boost::enable_current_exception(OutOfDiskSpaceException())
366            << errinfo_nativeerror(error);
367        default:
368            throwSocketException(error);
369            throw boost::enable_current_exception(NativeException())
370                << errinfo_nativeerror(error);
371    }
372}
373#endif
374
375}