1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2008 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
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
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>
35 #if !defined(darwin) || __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050
36 # include <execinfo.h>
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!"
41 # warning "Emulating backtrace and backtrace_symbols with a dirty hack!"
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
53 string pOpen(const string &cmd, label line=0)
57 FILE *cmdPipe = popen(cmd.c_str(), "r");
61 // Read line number of lines
62 for (label cnt = 0; cnt <= line; cnt++)
65 char* s = fgets(buffer, MAX-1, cmdPipe);
75 return str.substr(0, str.size()-1);
85 // use popen to call addr2line (using bfd.h directly would have
86 // meant relinking everything)
88 void printSourceFileAndLine
91 const HashTable<label, fileName>& addressMap,
92 const fileName& filename,
96 word myAddress = address;
99 if (filename.ext() == "so")
101 if (filename.ext() == "dylib")
104 // Convert offset into .so into offset into executable.
107 sscanf(myAddress.c_str(), "%p",&addr);
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();
125 if (filename[0] == '/')
129 "addr2line -f --demangle=auto --exe "
138 os << " addr2line failed";
140 else if (line == "??:0")
142 os << " in " << filename;
146 string cwdLine(line.replaceAll(cwd() + '/', ""));
148 string homeLine(cwdLine.replaceAll(home(), '~'));
150 os << " at " << homeLine.c_str();
160 const fileName& filename,
164 if (filename.size() > 0 && filename[0] == '/')
168 "addr2line -f --demangle=auto --exe "
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
189 void *getStackAddress()
191 const unsigned int stackLevel=level;
193 __builtin_frame_address(level)
194 ? __builtin_return_address(stackLevel)
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)
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);
234 unsigned backtrace(void **bt, unsigned maxAddrs)
239 for(int level=0;level<maxAddrs;level++) {
241 bt[level]=getStackAddress(level);
243 if(bt[level]!=(void *)0) {
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++) {
263 int result=dladdr(bt[i],&info);
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);
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;
287 IFstream is("/proc/" + name(pid()) + "/maps");
294 string::size_type space = line.rfind(' ') + 1;
295 fileName libPath = line.substr(space, line.size()-space);
297 if (libPath.size() > 0 && libPath[0] == '/')
299 string offsetString(line.substr(0, line.find('-')));
300 IStringStream offsetStr(offsetString);
301 addressMap.insert(libPath, readHexLabel(offsetStr));
306 // Get raw stack symbols
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++)
315 string msg(strings[i]);
316 fileName programFile;
319 os << '#' << label(i) << " ";
320 //os << "Raw : " << msg << "\n\t";
322 string::size_type lPos = msg.find('[');
323 string::size_type rPos = msg.find(']');
325 if (lPos != string::npos && rPos != string::npos && lPos<rPos)
327 address = msg.substr(lPos+1, rPos-lPos-1);
328 msg = msg.substr(0, lPos);
331 string::size_type bracketPos = msg.find('(');
332 string::size_type spacePos = msg.find(' ');
333 if (bracketPos != string::npos || spacePos != string::npos)
335 programFile = msg.substr(0, min(spacePos, bracketPos));
337 // not an absolute path
338 if (programFile[0] != '/')
340 string tmp = pOpen("which "+programFile);
341 if (tmp[0] == '/' || tmp[0] == '~')
349 string::size_type bracketPos = msg.find('(');
351 if (bracketPos != string::size_type(string::npos))
353 string::size_type start = bracketPos+1;
355 string::size_type plusPos = msg.find('+', start);
357 if (plusPos != string::size_type(string::npos))
359 string cName(msg.substr(start, plusPos-start));
362 char* cplusNamePtr = abi::__cxa_demangle
365 NULL, // have it malloc itself
370 if (status == 0 && cplusNamePtr)
382 string::size_type endBracketPos = msg.find(')', start);
384 if (endBracketPos != string::size_type(string::npos))
386 string fullName(msg.substr(start, endBracketPos-start));
388 os << fullName.c_str() << nl;
393 getSymbolForRaw(os, msg, programFile, address);
400 getSymbolForRaw(os, msg, programFile, address);
403 printSourceFileAndLine(os, addressMap, programFile, address);
412 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
414 } // End namespace Foam
416 // ************************ vim: set sw=4 sts=4 et: ************************ //