FIX: Use system execinfo.h on OS X >= 10.5
[freefoam.git] / src / OSspecific / Unix / printStack.C
blob0d20a72e3c9a0f0c51bf3933192cd78d83496d57
1 /*---------------------------------------------------------------------------*\
2   =========                 |
3   \\      /  F ield         | OpenFOAM: The Open Source CFD Toolbox
4    \\    /   O peration     |
5     \\  /    A nd           | Copyright (C) 1991-2008 OpenCFD Ltd.
6      \\/     M anipulation  |
7 -------------------------------------------------------------------------------
8 License
9     This file is part of OpenFOAM.
11     OpenFOAM is free software; you can redistribute it and/or modify it
12     under the terms of the GNU General Public License as published by the
13     Free Software Foundation; either version 2 of the License, or (at your
14     option) any later version.
16     OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
19     for more details.
21     You should have received a copy of the GNU General Public License
22     along with OpenFOAM; if not, write to the Free Software Foundation,
23     Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25 \*---------------------------------------------------------------------------*/
27 #include <OpenFOAM/error.H>
28 #include <OpenFOAM/IStringStream.H>
29 #include <OpenFOAM/OStringStream.H>
30 #include <OpenFOAM/OSspecific.H>
31 #include <OpenFOAM/IFstream.H>
32 #include <OpenFOAM/readHexLabel.H>
34 #include <cxxabi.h>
35 #if !defined(darwin) || __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
36 #   include <execinfo.h>
37 #else
38 #   if defined(__x86_64__) || defined(__ppc64__)
39 #      error "Currently compiling FreeFOAM for the x86_64 and ppc64 architectures with Mac OS X target version < 10.5 is not possible!"
40 #   else
41 #      warning "Emulating backtrace and backtrace_symbols with a dirty hack!"
42 #   endif
43 #endif
44 #include <dlfcn.h>
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
48 namespace Foam
51 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
53 string pOpen(const string &cmd, label line=0)
55     const int MAX = 1000;
57     FILE *cmdPipe = popen(cmd.c_str(), "r");
59     if (cmdPipe)
60     {
61         // Read line number of lines
62         for (label cnt = 0; cnt <= line; cnt++)
63         {
64             char buffer[MAX];
65             char* s = fgets(buffer, MAX-1, cmdPipe);
67             if (s == NULL)
68             {
69                 return "";
70             }
72             if (cnt == line)
73             {
74                 string str(buffer);
75                 return str.substr(0, str.size()-1);
76             }
77         }
78         pclose(cmdPipe);
79     }
81     return "";
85 // use popen to call addr2line (using bfd.h directly would have
86 // meant relinking everything)
88 void printSourceFileAndLine
90     Ostream& os,
91     const HashTable<label, fileName>& addressMap,
92     const fileName& filename,
93     const word& address
96     word myAddress = address;
98 #ifndef darwin
99     if (filename.ext() == "so")
100 #else
101     if (filename.ext() == "dylib")
102 #endif
103     {
104         // Convert offset into .so into offset into executable.
106         void *addr;
107         sscanf(myAddress.c_str(), "%p",&addr);
109         Dl_info info;
111         dladdr(addr, &info);
113         unsigned long offset = reinterpret_cast<unsigned long>(info.dli_fbase);
115         IStringStream addressStr(address.substr(2));
116         label addressValue = readHexLabel(addressStr);
117         label relativeAddress = addressValue-offset;
119         // Reconstruct hex word from address
120         OStringStream nStream;
121         nStream << "0x" << hex << relativeAddress;
122         myAddress = nStream.str();
123     }
125     if (filename[0] == '/')
126     {
127         string line = pOpen
128         (
129             "addr2line -f --demangle=auto --exe "
130           + filename
131           + " "
132           + myAddress,
133             1
134         );
136         if (line == "")
137         {
138             os << " addr2line failed";
139         }
140         else if (line == "??:0")
141         {
142             os << " in " << filename;
143         }
144         else
145         {
146             string cwdLine(line.replaceAll(cwd() + '/', ""));
148             string homeLine(cwdLine.replaceAll(home(), '~'));
150             os << " at " << homeLine.c_str();
151         }
152     }
156 void getSymbolForRaw
158     Ostream& os,
159     const string& raw,
160     const fileName& filename,
161     const word& address
164     if (filename.size() > 0 && filename[0] == '/')
165     {
166         string fcnt = pOpen
167         (
168             "addr2line -f --demangle=auto --exe "
169           + filename
170           + " "
171           + address
172         );
174         if (fcnt != "")
175         {
176             os << fcnt.c_str();
177             return;
178         }
179     }
180     os << "Uninterpreted: " << raw.c_str();
183 #if defined(darwin) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1050
185 // Trying to emulate the original backtrace and backtrace_symbol from the glibc
186 // After an idea published by Rush Manbert at http://lists.apple.com/archives/xcode-users/2006/Apr/msg00528.html
188 template<int level>
189 void *getStackAddress()
191     const unsigned int stackLevel=level;
192     return (
193         __builtin_frame_address(level)
194         ? __builtin_return_address(stackLevel)
195         : (void *)0
196     );
199 #define GET_STACK_ADDRESS(lvl)              \
200     case lvl: {return getStackAddress<lvl>(); break; }
202 // please don't laugh. For some reason this is necessary (the compiler won't accept it otherwise)
203 void *getStackAddress(int level)
205     switch(level) {
206         GET_STACK_ADDRESS(0);
207         GET_STACK_ADDRESS(1);
208         GET_STACK_ADDRESS(2);
209         GET_STACK_ADDRESS(3);
210         GET_STACK_ADDRESS(4);
211         GET_STACK_ADDRESS(5);
212         GET_STACK_ADDRESS(6);
213         GET_STACK_ADDRESS(7);
214         GET_STACK_ADDRESS(8);
215         GET_STACK_ADDRESS(9);
216         GET_STACK_ADDRESS(10);
217         GET_STACK_ADDRESS(11);
218         GET_STACK_ADDRESS(12);
219         GET_STACK_ADDRESS(13);
220         GET_STACK_ADDRESS(14);
221         GET_STACK_ADDRESS(15);
222         GET_STACK_ADDRESS(16);
223         GET_STACK_ADDRESS(17);
224         GET_STACK_ADDRESS(18);
225         GET_STACK_ADDRESS(19);
226         GET_STACK_ADDRESS(20);
227         GET_STACK_ADDRESS(21);
228         default:
229             return (void *)0;
230             break;
231     }
234 unsigned backtrace(void **bt, unsigned maxAddrs)
236     unsigned valid=0;
237     bool ok=true;
239     for(int level=0;level<maxAddrs;level++) {
240         if(ok) {
241             bt[level]=getStackAddress(level);
243             if(bt[level]!=(void *)0) {
244                 valid=level;
245             } else {
246                 ok=false;
247             }
248         } else {
249             bt[level]=(void *)0;
250         }
251     }
253     return valid;
256 // This function is a potential memory leak. But I don't care because the program is terminating anyway
257 char **backtrace_symbols(void **bt,unsigned nr)
259     char **strings=(char **)malloc(sizeof(char *)*nr);
261     for(unsigned i=0;i<nr;i++) {
262         Dl_info info;
263         int result=dladdr(bt[i],&info);
265         char tmp[1000];
266         sprintf(tmp,"%s(%s+%p) [%p]",info.dli_fname,info.dli_sname,
267             reinterpret_cast<void *>(reinterpret_cast<unsigned int>(bt[i])-reinterpret_cast<unsigned int>(info.dli_saddr)),bt[i]);
268         strings[i]=(char *)malloc(strlen(tmp)+1);
269         strcpy(strings[i],tmp);
270     }
272     return strings;
275 #endif
277 void error::printStack(Ostream& os)
279     // Reads the starting addresses for the dynamically linked libraries
280     // from the /proc/pid/maps-file
281     // I'm afraid this works only for Linux 2.6-Kernels (may work on 2.4)
282     // Note2: the filenames in here will have softlinks resolved so will
283     // go wrong when having e.g. OpenFOAM installed under a softlink.
285     HashTable<label, fileName> addressMap;
286     {
287         IFstream is("/proc/" + name(pid()) + "/maps");
289         while(is.good())
290         {
291             string line;
292             is.getLine(line);
294             string::size_type space = line.rfind(' ') + 1;
295             fileName libPath = line.substr(space, line.size()-space);
297             if (libPath.size() > 0 && libPath[0] == '/')
298             {
299                 string offsetString(line.substr(0, line.find('-')));
300                 IStringStream offsetStr(offsetString);
301                 addressMap.insert(libPath, readHexLabel(offsetStr));
302             }
303         }
304     }
306     // Get raw stack symbols
307     void *array[100];
308     size_t size = backtrace(array, 100);
309     char **strings = backtrace_symbols(array, size);
311     // See if they contain function between () e.g. "(__libc_start_main+0xd0)"
312     // and see if cplus_demangle can make sense of part before +
313     for (size_t i = 0; i < size; i++)
314     {
315         string msg(strings[i]);
316         fileName programFile;
317         word address;
319         os << '#' << label(i) << "  ";
320         //os << "Raw   : " << msg << "\n\t";
321         {
322             string::size_type lPos = msg.find('[');
323             string::size_type rPos = msg.find(']');
325             if (lPos != string::npos && rPos != string::npos && lPos<rPos)
326             {
327                 address = msg.substr(lPos+1, rPos-lPos-1);
328                 msg = msg.substr(0, lPos);
329             }
331             string::size_type bracketPos = msg.find('(');
332             string::size_type spacePos = msg.find(' ');
333             if (bracketPos != string::npos || spacePos != string::npos)
334             {
335                 programFile = msg.substr(0, min(spacePos, bracketPos));
337                 // not an absolute path
338                 if (programFile[0] != '/')
339                 {
340                     string tmp = pOpen("which "+programFile);
341                     if (tmp[0] == '/' || tmp[0] == '~')
342                     {
343                         programFile = tmp;
344                     }
345                 }
346             }
347         }
349         string::size_type bracketPos = msg.find('(');
351         if (bracketPos != string::size_type(string::npos))
352         {
353             string::size_type start = bracketPos+1;
355             string::size_type plusPos = msg.find('+', start);
357             if (plusPos != string::size_type(string::npos))
358             {
359                 string cName(msg.substr(start, plusPos-start));
361                 int status;
362                 char* cplusNamePtr = abi::__cxa_demangle
363                 (
364                     cName.c_str(),
365                     NULL,                   // have it malloc itself
366                     0,
367                     &status
368                 );
370                 if (status == 0 && cplusNamePtr)
371                 {
372                     os << cplusNamePtr;
373                     free(cplusNamePtr);
374                 }
375                 else
376                 {
377                     os << cName.c_str();
378                 }
379             }
380             else
381             {
382                 string::size_type endBracketPos = msg.find(')', start);
384                 if (endBracketPos != string::size_type(string::npos))
385                 {
386                     string fullName(msg.substr(start, endBracketPos-start));
388                     os << fullName.c_str() << nl;
389                 }
390                 else
391                 {
392                     // Print raw message
393                     getSymbolForRaw(os, msg, programFile, address);
394                 }
395             }
396         }
397         else
398         {
399             // Print raw message
400             getSymbolForRaw(os, msg, programFile, address);
401         }
403         printSourceFileAndLine(os, addressMap, programFile, address);
405         os << nl;
406     }
408     free(strings);
412 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
414 } // End namespace Foam
416 // ************************ vim: set sw=4 sts=4 et: ************************ //