File: install_sigsev_backtrace.cpp

package info (click to toggle)
sight 25.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 43,252 kB
  • sloc: cpp: 310,629; xml: 17,622; ansic: 9,960; python: 1,379; sh: 144; makefile: 33
file content (345 lines) | stat: -rw-r--r-- 10,417 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
/************************************************************************
 *
 * Copyright (C) 2009-2023 IRCAD France
 * Copyright (C) 2012-2018 IHU Strasbourg
 *
 * This file is part of Sight.
 *
 * Sight is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Sight is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with Sight. If not, see <https://www.gnu.org/licenses/>.
 *
 ***********************************************************************/

#include <core/base.hpp>

#include "install_sigsev_backtrace.hpp"

#ifndef WIN32

#include <csignal>
#include <cstdio>
#include <cstdlib>
#include <execinfo.h>

#include <cstring>
#include <cxxabi.h>
#include <iostream>
#include <sstream>
#include <string>
#include <array>

/* get REG_EIP/REG_RIP from ucontext.h */
#include <ucontext.h>

#if defined(REG_EIP)
constexpr int debugReg = REG_EIP;
#elif defined(REG_RIP)
constexpr int DEBUG_REG = REG_RIP;
#else
#error Neither REG_EIP nor REG_RIP is defined.
#endif

#else // WIN32

#include <list>
#include <string>
#include <sstream>

#include <boost/lexical_cast.hpp>
#include <Windows.h>
#include <DbgHelp.h>

#endif

namespace sight::module::debug
{

#ifndef WIN32
//------------------------------------------------------------------------------

std::string demangle(const std::string& _mangled)
{
    return core::demangler(_mangled).get_classname();
}

//------------------------------------------------------------------------------

std::string decode(char* _message)
{
    std::string msg(_message);
    std::string res(_message);

    std::string::size_type popen = msg.find('(');
    if(popen != std::string::npos)
    {
        std::string::size_type plus = msg.find('+');
        res = std::string(_message, popen + 1) + " ";
        std::string mangled(_message, popen + 1, plus - popen - 1);
        res += demangle(mangled) + " ";
        res += std::string(_message + plus, _message + strlen(_message));
    }

    return res;
}

//------------------------------------------------------------------------------

void bt_sighandler(
    int _sig,
    siginfo_t* _info,
    void* _secret
)
{
    std::array<void*, 16> trace {};
    int i          = 0;
    int trace_size = 0;
    const auto* uc = reinterpret_cast<const ucontext_t*>(_secret);

    std::stringstream ss;
    ss << "Got signal " << _sig;

    /* Do something useful with siginfo_t */
    if(_sig == SIGSEGV)
    {
        ss << " faulty address is " << _info->si_addr;
        ss << " from " << uc->uc_mcontext.gregs[DEBUG_REG];
    }

    ss << std::endl;

    trace_size = backtrace(trace.data(), 16);
    /* overwrite sigaction with caller's address */
    trace[1] = reinterpret_cast<void*>(uc->uc_mcontext.gregs[DEBUG_REG]);

    char** messages = backtrace_symbols(trace.data(), trace_size);
    /* skip first stack frame (points here) */
    ss << "    [bt] Execution path:" << std::endl;
    for(i = 1 ; i < trace_size ; ++i)
    {
        ss << "    [bt] " << decode(messages[i]) << std::endl;
    }

    if(_sig == SIGSEGV)
    {
        SIGHT_FATAL("SIGSEV signal " + ss.str());
        exit(0);
    }
    else
    {
        SIGHT_ERROR("SIGUSR1 signal " + ss.str());
    }
}

//------------------------------------------------------------------------------

void install_sigsev_backtrace()
{
    struct sigaction sa {};

    sa.sa_sigaction = bt_sighandler;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART | SA_SIGINFO;

    sigaction(SIGSEGV, &sa, nullptr);
    sigaction(SIGUSR1, &sa, nullptr);
}
#else // if  win32
const std::size_t nbChar = 100;
#if _MSC_VER > 1499 // Visual C++ 2008 only
//------------------------------------------------------------------------------

BOOL CALLBACK EnumerateLoadedModules(LPCSTR ModuleName, DWORD64 ModuleBase, ULONG, PVOID UserContext)
#else
//------------------------------------------------------------------------------

BOOL CALLBACK EnumerateLoadedModules(LPSTR ModuleName, DWORD64 ModuleBase, ULONG, PVOID UserContext)
#endif
{
    std::list<std::string>* pLoadedModules = reinterpret_cast<std::list<std::string>*>(UserContext);
    pLoadedModules->push_back(std::string((char*) ModuleName) + "\t" + boost::lexical_cast<std::string>(ModuleBase));
    return true;
}

/**
 * Dumps the backtrace on a stream
 */
void printDump(
    std::list<std::string>& loadedModules,
    std::list<std::string>& callStack,
    std::list<std::string>& fileStack
)
{
    std::stringstream stream;

    // Dumps the loaded modules on the stream
    stream << "-----------------------------------------" << std::endl;
    stream << "\nLoaded Modules\n";
    for(std::list<std::string>::const_iterator it = loadedModules.begin() ; it != loadedModules.end() ; ++it)
    {
        stream << "> " << *it << std::endl;
    }

    stream << "-----------------------------------------" << std::endl;
    // Dumps the call stack on the stream
    stream << "\nCallStack\n";
    for(std::list<std::string>::const_iterator it = callStack.begin(), it2 = fileStack.begin() ;
        it != callStack.end() && it2 != fileStack.end() ; ++it, ++it2)
    {
        stream << "> " << *it << std::endl;
        stream << "   " << *it2 << std::endl;
    }

    stream << "-----------------------------------------" << std::endl;

    SIGHT_ERROR(stream.str());
}

/**
 * Loads the elements of the call stack in a list
 * @param exceptionInfos are useful information on the exception
 */
void LoadCallStack(
    EXCEPTION_POINTERS* exceptionInfos,
    HANDLE& hProcess,
    std::list<std::string>& callStack,
    std::list<std::string>& fileStack
)
{
    STACKFRAME64 tempStackFrame;
    CONTEXT context = *(exceptionInfos->ContextRecord);
    memset(&tempStackFrame, 0, sizeof(STACKFRAME64));
    DWORD machineType;

#ifdef _M_IX86
    machineType                     = IMAGE_FILE_MACHINE_I386;
    tempStackFrame.AddrPC.Offset    = context.Eip;
    tempStackFrame.AddrPC.Mode      = AddrModeFlat;
    tempStackFrame.AddrStack.Offset = context.Esp;
    tempStackFrame.AddrStack.Mode   = AddrModeFlat;
    tempStackFrame.AddrFrame.Offset = context.Ebp;
    tempStackFrame.AddrFrame.Mode   = AddrModeFlat;
#elif _M_X64
    machineType                     = IMAGE_FILE_MACHINE_AMD64;
    tempStackFrame.AddrPC.Offset    = context.Rip;
    tempStackFrame.AddrPC.Mode      = AddrModeFlat;
    tempStackFrame.AddrFrame.Offset = context.Rsp;
    tempStackFrame.AddrFrame.Mode   = AddrModeFlat;
    tempStackFrame.AddrStack.Offset = context.Rsp;
    tempStackFrame.AddrStack.Mode   = AddrModeFlat;
#elif _M_IA64
    machineType                      = IMAGE_FILE_MACHINE_IA64;
    tempStackFrame.AddrPC.Offset     = context.StIIP;
    tempStackFrame.AddrPC.Mode       = AddrModeFlat;
    tempStackFrame.AddrFrame.Offset  = context.IntSp;
    tempStackFrame.AddrFrame.Mode    = AddrModeFlat;
    tempStackFrame.AddrBStore.Offset = context.RsBSP;
    tempStackFrame.AddrBStore.Mode   = AddrModeFlat;
    tempStackFrame.AddrStack.Offset  = context.IntSp;
    tempStackFrame.AddrStack.Mode    = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

    ULONG64 buffer[(sizeof(SYMBOL_INFO) + nbChar * sizeof(TCHAR) + sizeof(ULONG64) + 1) / sizeof(ULONG64)];
    PSYMBOL_INFO pSymbol = reinterpret_cast<PSYMBOL_INFO>(buffer);
    PSTR undecoratedName = (PSTR) malloc(sizeof(TCHAR) * nbChar);

    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    pSymbol->MaxNameLen   = nbChar;
    DWORD lineDisplacement;
    IMAGEHLP_LINE64 lineInfo = {sizeof(IMAGEHLP_LINE64)};

    while(StackWalk64(
              machineType,
              hProcess,
              GetCurrentThread(),
              &tempStackFrame,
              &context,
              nullptr,
              SymFunctionTableAccess64,
              SymGetModuleBase64,
              nullptr
    ))
    {
        // Sanity stack check
        if(tempStackFrame.AddrPC.Offset == 0)
        {
            break;
        }

        DWORD64 symDisplacement = 0;
        // Try to get the symbol name
        if(SymFromAddr(hProcess, tempStackFrame.AddrPC.Offset, &symDisplacement, pSymbol))
        {
            UnDecorateSymbolName(pSymbol->Name, undecoratedName, MAX_SYM_NAME, UNDNAME_COMPLETE);
            callStack.push_back(
                std::string((char*) undecoratedName) + "+"
                + boost::lexical_cast<std::string>(symDisplacement)
            );

            if(SymGetLineFromAddr64(hProcess, tempStackFrame.AddrPC.Offset, &lineDisplacement, &lineInfo))
            {
                fileStack.push_back(
                    std::string(lineInfo.FileName) + "\tl:"
                    + boost::lexical_cast<std::string>(lineInfo.LineNumber)
                );
            }
            else
            {
                fileStack.push_back("No info");
            }
        }
        else
        {
        }
    }

    free(undecoratedName);
}

/// The exception filter that will display the stack
static LONG WINAPI UnhandledExpFilter(PEXCEPTION_POINTERS pExceptionInfo)
{
    /// Handle to the process
    HANDLE hProcess;
    /// List of the loaded modules
    std::list<std::string> loadedModules;
    /// List of the elements on the call stack
    std::list<std::string> callStack;
    /// List of the elements on the file stack
    std::list<std::string> fileStack;

    SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES);
    hProcess = GetCurrentProcess();

    if(SymInitialize(hProcess, nullptr, TRUE))
    {
        LoadCallStack(pExceptionInfo, hProcess, callStack, fileStack);
        ::EnumerateLoadedModules64(hProcess, EnumerateLoadedModules, &loadedModules);
        ::SymCleanup(hProcess);
    }

    printDump(loadedModules, callStack, fileStack);

    return EXCEPTION_CONTINUE_SEARCH;
}

//------------------------------------------------------------------------------

void install_sigsev_backtrace()
{
    SetUnhandledExceptionFilter(UnhandledExpFilter);
}
#endif // ifndef WIN32

} // namespace sight::module::debug