printStack.C 6.19 KB
Newer Older
1
2
3
4
/*---------------------------------------------------------------------------*\
  =========                 |
  \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
   \\    /   O peration     |
5
    \\  /    A nd           | Copyright (C) 2019 OpenCFD Ltd.
6
     \\/     M anipulation  |
OpenFOAM bot's avatar
OpenFOAM bot committed
7
8
-------------------------------------------------------------------------------
                            | Copyright (C) 2011-2016 OpenFOAM Foundation
9
10
11
12
-------------------------------------------------------------------------------
License
    This file is part of OpenFOAM.

13
14
15
16
    OpenFOAM is free software: you can redistribute it and/or modify it
    under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
17
18
19
20
21
22
23

    OpenFOAM 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 General Public License
    for more details.

    You should have received a copy of the GNU General Public License
24
    along with OpenFOAM.  If not, see <http://www.gnu.org/licenses/>.
25
26
27
28
29
30

\*---------------------------------------------------------------------------*/

#include "error.H"
#include "OSspecific.H"
#include "IFstream.H"
31
#include "StringStream.H"
32

33
#include <cinttypes>
34
35
36
37
38
39
40
41
42
43
44
#include <cxxabi.h>
#include <execinfo.h>
#include <dlfcn.h>

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

namespace Foam
{

// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

45
string pOpen(const string& cmd, label line=0)
46
{
47
    string res;
48
49
50
51

    FILE *cmdPipe = popen(cmd.c_str(), "r");
    if (cmdPipe)
    {
52
        char *buf = nullptr;
53

54
        // Read line number of lines
55
        for (label cnt = 0; cnt <= line; ++cnt)
56
        {
57
            size_t linecap = 0;
58
            ssize_t linelen = ::getline(&buf, &linecap, cmdPipe);
59

60
            if (linelen < 0)
61
            {
62
                break;
63
64
65
66
            }

            if (cnt == line)
            {
67
                res = string(buf);
68
69
70
71
72
                // Trim trailing newline
                if (res.size())
                {
                    res.resize(res.size()-1);
                }
73
                break;
74
            }
75
        }
76

77
        if (buf != nullptr)
78
79
80
81
        {
            free(buf);
        }

82
83
84
        pclose(cmdPipe);
    }

85
    return res;
86
87
88
}


89
90
inline word addressToWord(const uintptr_t addr)
{
91
    OStringStream os;
92
    #ifdef __APPLE__
93
94
    os << "0x" << hex << uint64_t(addr);
    #else
95
    os << "0x" << hex << addr;
96
    #endif
97
    return os.str();
98
99
}

100

101
102
103
104
105
106
107
108
inline string& shorterPath(string& s)
{
    s.replace(cwd() + '/', "");
    s.replace(home(), "~");
    return s;
}


109
110
111
112
void printSourceFileAndLine
(
    Ostream& os,
    const fileName& filename,
113
114
    Dl_info *info,
    void *addr
115
116
)
{
117
118
    uintptr_t address = uintptr_t(addr);
    word myAddress = addressToWord(address);
119

120
121
122
123
    // Can use relative addresses for executables and libraries with the
    // Darwin addr2line implementation.
    // On other systems (Linux), only use relative addresses for libraries.

124
    #ifndef __APPLE__
125
126
    if (filename.hasExt("so"))
    #endif
127
    {
128
129
130
131
        // Convert address into offset into dynamic library
        uintptr_t offset = uintptr_t(info->dli_fbase);
        intptr_t relativeAddress = address - offset;
        myAddress = addressToWord(relativeAddress);
132
133
134
135
136
137
138
139
140
141
142
143
144
    }

    if (filename[0] == '/')
    {
        string line = pOpen
        (
            "addr2line -f --demangle=auto --exe "
          + filename
          + " "
          + myAddress,
            1
        );

145
        if (line.empty())
146
        {
147
            os  << " addr2line failed";
148
149
150
        }
        else if (line == "??:0")
        {
151
152
            line = filename;
            os  << " in " << shorterPath(line).c_str();
153
154
155
        }
        else
        {
156
            os  << " at " << shorterPath(line).c_str();
157
158
159
160
161
        }
    }
}


162
fileName absolutePath(const char* fn)
163
{
164
165
166
    fileName fname(fn);

    if (fname[0] != '/' && fname[0] != '~')
167
    {
168
        string tmp = pOpen("which " + fname);
169

170
        if (tmp[0] == '/' || tmp[0] == '~')
171
        {
172
            fname = tmp;
173
174
        }
    }
175
176

    return fname;
177
178
179
}


180
word demangleSymbol(const char* sn)
181
{
182
183
184
185
    int st;
    char* cxx_sname = abi::__cxa_demangle
    (
        sn,
186
        nullptr,
187
188
189
190
191
        0,
        &st
    );

    if (st == 0 && cxx_sname)
192
    {
193
        word demangled(cxx_sname);
194
        free(cxx_sname);
195
196

        return demangled;
197
198
    }

199
    return sn;
200
}
201

202

203
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
204

205
} // End namespace Foam
206
207


208
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
209

210
211
void Foam::error::safePrintStack(std::ostream& os)
{
212
213
214
215
216
217
218
    // Get raw stack symbols
    void *array[100];
    size_t size = backtrace(array, 100);
    char **strings = backtrace_symbols(array, size);

    // See if they contain function between () e.g. "(__libc_start_main+0xd0)"
    // and see if cplus_demangle can make sense of part before +
219
    for (size_t i = 0; i < size; ++i)
220
221
222
223
224
    {
        string msg(strings[i]);
        fileName programFile;
        word address;

225
226
227
        os  << '#' << label(i) << '\t' << msg << std::endl;
    }
}
228

229

230
231
232
233
void Foam::error::printStack(Ostream& os)
{
    // Get raw stack symbols
    const size_t CALLSTACK_SIZE = 128;
234

235
236
    void *callstack[CALLSTACK_SIZE];
    size_t size = backtrace(callstack, CALLSTACK_SIZE);
237

238
    Dl_info *info = new Dl_info;
239

240
241
    fileName fname = "???";
    word address;
242

243
    for (size_t i=0; i<size; ++i)
244
245
246
247
    {
        int st = dladdr(callstack[i], info);

        os << '#' << label(i) << "  ";
248
        if (st != 0 && info->dli_fname != nullptr && info->dli_fname[0] != '\0')
249
250
251
252
253
        {
            fname = absolutePath(info->dli_fname);

            os <<
            (
254
                (info->dli_sname != nullptr)
255
256
257
              ? demangleSymbol(info->dli_sname)
              : "?"
            );
258
259
260
        }
        else
        {
261
            os << "?";
262
263
        }

264
265
        printSourceFileAndLine(os, fname, info, callstack[i]);
        os << nl;
266
267
    }

268
    delete info;
269
270
271
272
}


// ************************************************************************* //