]> granicus.if.org Git - icinga2/blob - lib/base/stacktrace.cpp
Update copyright headers for 2016
[icinga2] / lib / base / stacktrace.cpp
1 /******************************************************************************
2  * Icinga 2                                                                   *
3  * Copyright (C) 2012-2016 Icinga Development Team (https://www.icinga.org/)  *
4  *                                                                            *
5  * This program is free software; you can redistribute it and/or              *
6  * modify it under the terms of the GNU General Public License                *
7  * as published by the Free Software Foundation; either version 2             *
8  * of the License, or (at your option) any later version.                     *
9  *                                                                            *
10  * This program is distributed in the hope that it will be useful,            *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13  * GNU General Public License for more details.                               *
14  *                                                                            *
15  * You should have received a copy of the GNU General Public License          *
16  * along with this program; if not, write to the Free Software Foundation     *
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.             *
18  ******************************************************************************/
19
20 #include "base/stacktrace.hpp"
21 #include "base/utility.hpp"
22 #include "base/initialize.hpp"
23
24 #ifdef HAVE_BACKTRACE_SYMBOLS
25 #       include <execinfo.h>
26 #endif /* HAVE_BACKTRACE_SYMBOLS */
27
28 using namespace icinga;
29
30 INITIALIZE_ONCE(&StackTrace::StaticInitialize);
31
32 #ifdef _MSC_VER
33 #       pragma optimize("", off)
34 #endif /* _MSC_VER */
35
36 StackTrace::StackTrace(void)
37 {
38 #ifdef HAVE_BACKTRACE_SYMBOLS
39         m_Count = backtrace(m_Frames, sizeof(m_Frames) / sizeof(m_Frames[0]));
40 #else /* HAVE_BACKTRACE_SYMBOLS */
41 #       ifdef _WIN32
42         m_Count = CaptureStackBackTrace(0, sizeof(m_Frames) / sizeof(m_Frames), m_Frames, NULL);
43 #       else /* _WIN32 */
44         m_Count = 0;
45 #       endif /* _WIN32 */
46 #endif /* HAVE_BACKTRACE_SYMBOLS */
47 }
48
49 #ifdef _MSC_VER
50 #       pragma optimize("", on)
51 #endif /* _MSC_VER */
52
53 #ifdef _WIN32
54 StackTrace::StackTrace(PEXCEPTION_POINTERS exi)
55 {
56         STACKFRAME64 frame;
57         int architecture;
58
59 #ifdef _WIN64
60         architecture = IMAGE_FILE_MACHINE_AMD64;
61
62         frame.AddrPC.Offset = exi->ContextRecord->Rip;
63         frame.AddrFrame.Offset = exi->ContextRecord->Rbp;
64         frame.AddrStack.Offset = exi->ContextRecord->Rsp;
65 #else /* _WIN64 */
66         architecture = IMAGE_FILE_MACHINE_I386;
67
68         frame.AddrPC.Offset = exi->ContextRecord->Eip;
69         frame.AddrFrame.Offset = exi->ContextRecord->Ebp;
70         frame.AddrStack.Offset = exi->ContextRecord->Esp;
71 #endif  /* _WIN64 */
72
73         frame.AddrPC.Mode = AddrModeFlat;
74         frame.AddrFrame.Mode = AddrModeFlat;
75         frame.AddrStack.Mode = AddrModeFlat;
76
77         m_Count = 0;
78
79         while (StackWalk64(architecture, GetCurrentProcess(), GetCurrentThread(),
80             &frame, exi->ContextRecord, NULL, &SymFunctionTableAccess64,
81             &SymGetModuleBase64, NULL) && m_Count < sizeof(m_Frames) / sizeof(m_Frames[0])) {
82                 m_Frames[m_Count] = reinterpret_cast<void *>(frame.AddrPC.Offset);
83                 m_Count++;
84         }
85 }
86 #endif /* _WIN32 */
87
88 void StackTrace::StaticInitialize(void)
89 {
90 #ifdef _WIN32
91         (void) SymSetOptions(SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
92         (void) SymInitialize(GetCurrentProcess(), NULL, TRUE);
93 #endif /* _WIN32 */
94 }
95
96 /**
97  * Prints a stacktrace to the specified stream.
98  *
99  * @param fp The stream.
100  * @param ignoreFrames The number of stackframes to ignore (in addition to
101  *                     the one this function is executing in).
102  * @returns true if the stacktrace was printed, false otherwise.
103  */
104 void StackTrace::Print(std::ostream& fp, int ignoreFrames) const
105 {
106         fp << std::endl;
107
108 #ifndef _WIN32
109 #       ifdef HAVE_BACKTRACE_SYMBOLS
110         char **messages = backtrace_symbols(m_Frames, m_Count);
111
112         for (int i = ignoreFrames + 1; i < m_Count && messages != NULL; ++i) {
113                 String message = messages[i];
114
115                 char *sym_begin = strchr(messages[i], '(');
116
117                 if (sym_begin) {
118                         char *sym_end = strchr(sym_begin, '+');
119
120                         if (sym_end != NULL) {
121                                 String sym = String(sym_begin + 1, sym_end);
122                                 String sym_demangled = Utility::DemangleSymbolName(sym);
123
124                                 if (sym_demangled.IsEmpty())
125                                         sym_demangled = "<unknown function>";
126
127                                 String path = String(messages[i], sym_begin);
128
129                                 size_t slashp = path.RFind("/");
130
131                                 if (slashp != String::NPos)
132                                         path = path.SubStr(slashp + 1);
133
134                                 message = path + ": " + sym_demangled + " (" + String(sym_end);
135                         }
136                 }
137
138                 fp << "\t(" << i - ignoreFrames - 1 << ") " << message << std::endl;
139         }
140
141         std::free(messages);
142
143         fp << std::endl;
144 #       else /* HAVE_BACKTRACE_SYMBOLS */
145         fp << "(not available)" << std::endl;
146 #       endif /* HAVE_BACKTRACE_SYMBOLS */
147 #else /* _WIN32 */
148         for (int i = ignoreFrames + 1; i < m_Count; i++) {
149                 fp << "\t(" << i - ignoreFrames - 1 << "): "
150                    << Utility::GetSymbolName(m_Frames[i])
151                    << std::endl;
152         }
153 #endif /* _WIN32 */
154 }
155
156 std::ostream& icinga::operator<<(std::ostream& stream, const StackTrace& trace)
157 {
158         trace.Print(stream, 1);
159
160         return stream;
161 }