printStack.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2019-2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "error.H"
30 #include "OSspecific.H"
31 
32 #include <cinttypes>
33 #include <sstream>
34 #include <cxxabi.h>
35 #include <dlfcn.h>
36 #include <execinfo.h>
37 
38 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
39 
40 namespace
41 {
42 
43 // Read up to and including lineNum from the piped command
44 // Return the final line read
45 std::string pipeOpen(const std::string& cmd, const int lineNum = 0)
46 {
47  std::string str;
48 
49  FILE *handle = popen(cmd.c_str(), "r");
50  if (!handle) return str;
51 
52  char* buf = nullptr;
53  size_t len = 0;
54  ssize_t nread;
55 
56  // Read lineNum number of lines
57  for
58  (
59  int cnt = 0;
60  cnt <= lineNum && (nread = ::getline(&buf, &len, handle)) >= 0;
61  ++cnt
62  )
63  {
64  if (cnt == lineNum)
65  {
66  // Retain the last line, trimming trailing newline
67  str.assign(buf);
68 
69  if (str.size())
70  {
71  str.resize(str.size()-1);
72  }
73  }
74  }
75 
76  free(buf);
77  pclose(handle);
78 
79  return str;
80 }
81 
82 
83 inline std::string addressToWord(const uintptr_t addr)
84 {
85  std::ostringstream buf;
86  buf.setf(std::ios_base::hex, std::ios_base::basefield);
87 
88  buf << "0x"; // Same as setf(std::ios::showbase)
89 
90  #ifdef __APPLE__
91  buf << uint64_t(addr);
92  #else
93  buf << addr;
94  #endif
95 
96  return buf.str(); // Needs no stripping
97 }
98 
99 
100 // Note: demangle requires symbols only - without extra '(' etc.
101 inline std::string demangleSymbol(const char* sn)
102 {
103  int st = 0;
104 
105  char* cxx_sname = abi::__cxa_demangle(sn, nullptr, nullptr, &st);
106 
107  if (st == 0 && cxx_sname)
108  {
109  std::string demangled(cxx_sname);
110  free(cxx_sname);
111 
112  return demangled;
113  }
114 
115  return sn;
116 }
117 
118 
119 inline Foam::string& shorterPath(Foam::string& s)
120 {
121  s.replace(Foam::cwd() + '/', "");
122  s.replace(Foam::home(), "~");
123  return s;
124 }
125 
126 
127 void printSourceFileAndLine
128 (
129  Foam::Ostream& os,
130  const Foam::fileName& filename,
131  const Dl_info& info,
132  void *addr
133 )
134 {
135  uintptr_t address = uintptr_t(addr);
136  std::string myAddress = addressToWord(address);
137 
138  // Can use relative addresses for executables and libraries with the
139  // Darwin addr2line implementation.
140  // On other systems (Linux), only use relative addresses for libraries.
141 
142  #ifndef __APPLE__
143  if (filename.has_ext("so"))
144  #endif
145  {
146  // Convert address into offset into dynamic library
147  uintptr_t offset = uintptr_t(info.dli_fbase);
148  intptr_t relativeAddress = address - offset;
149  myAddress = addressToWord(relativeAddress);
150  }
151 
152  if (filename[0] == '/')
153  {
154  Foam::string line = pipeOpen
155  (
156  "addr2line -f --demangle=auto --exe "
157  + filename
158  + " "
159  + myAddress,
160  1
161  );
162 
163  if (line.empty())
164  {
165  os << " addr2line failed";
166  }
167  else if (line == "??:0" || line == "??:?" )
168  {
169  line = filename;
170  os << " in " << shorterPath(line).c_str();
171  }
172  else
173  {
174  os << " at " << shorterPath(line).c_str();
175  }
176  }
177 }
178 
179 
180 // Uses 'which' to find executable on PATH
181 // - could also iterate through PATH directly
182 inline Foam::fileName whichPath(const char* fn)
183 {
184  Foam::fileName fname(fn);
185 
186  if (!fname.empty() && fname[0] != '/' && fname[0] != '~')
187  {
188  std::string s = pipeOpen("which " + fname);
189 
190  if (s[0] == '/' || s[0] == '~')
191  {
192  fname = s;
193  }
194  }
195 
196  return fname;
197 }
198 
199 } // End anonymous namespace
200 
201 
202 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
203 
204 void Foam::error::safePrintStack(std::ostream& os, int size)
205 {
206  // Get raw stack symbols
207  void *callstack[100];
208  size = backtrace(callstack, (size > 0 && size < 100) ? size + 1 : 100);
209 
210  char **strings = backtrace_symbols(callstack, size);
211  size_t rdelim;
212 
213  // Frame 0 is 'printStack()' - report something more meaningful
214  os << "[stack trace]" << std::endl
215  << "=============" << std::endl;
216 
217  for (int i = 1; i < size; ++i)
218  {
219  std::string str(strings[i]);
220 
221  os << '#' << i << '\t';
222 
223  // Possibly shorten paths that appear to correspond to OpenFOAM
224  // locations (platforms).
225  //
226  // Eg, "/path/openfoam/platforms/linux64GccDPInt32Opt/lib/libxyz.so"
227  // --> "platforms/linux64GccDPInt32Opt/lib/libxyz.so"
228 
229  auto ldelim = str.find('(');
230  auto beg = str.find("/platforms/");
231 
232  if (beg == std::string::npos || !beg || beg > ldelim)
233  {
234  beg = 0;
235  }
236  else
237  {
238  ++beg;
239  }
240 
241  if
242  (
243  (ldelim != std::string::npos)
244  && (rdelim = str.find('+', ldelim+1)) != std::string::npos
245  && (rdelim > ldelim+1)
246  )
247  {
248  // Found function between () e.g. "(__libc_start_main+0xd0)"
249  // - demangle function name (before the '+' offset)
250  // - preserve trailing [0xAddr]
251 
252  os << str.substr(beg, ldelim-beg)
253  << ' '
254  << demangleSymbol
255  (
256  str.substr(ldelim+1, rdelim-ldelim-1).c_str()
257  );
258 
259  if ((rdelim = str.find('[', rdelim)) != std::string::npos)
260  {
261  os << ' ' << str.substr(rdelim);
262  }
263  }
264  else if (beg)
265  {
266  // With shortened path name
267  os << str.substr(beg);
268  }
269  else
270  {
271  // No modification to string
272  os << str;
273  }
274  os << std::endl;
275  }
276 
277  os << "=============" << std::endl;
278 
279  free(strings);
280 }
281 
282 
283 void Foam::error::printStack(Ostream& os, int size)
284 {
285  // Get raw stack symbols
286  void *callstack[100];
287  size = backtrace(callstack, (size > 0 && size < 100) ? size + 1 : 100);
288 
289  Dl_info info;
290  fileName fname;
291 
292  // Frame 0 is 'printStack()' - report something more meaningful
293  os << "[stack trace]" << nl
294  << "=============" << nl;
295 
296  for (int i = 1; i < size; ++i)
297  {
298  int st = dladdr(callstack[i], &info);
299 
300  os << '#' << i << " ";
301  if (st != 0 && info.dli_fname != nullptr && *(info.dli_fname))
302  {
303  fname = whichPath(info.dli_fname);
304 
305  if (info.dli_sname)
306  {
307  os << demangleSymbol(info.dli_sname).c_str();
308  }
309  else
310  {
311  os << '?';
312  }
313  }
314  else
315  {
316  fname = "???";
317  os << '?';
318  }
319 
320  printSourceFileAndLine(os, fname, info, callstack[i]);
321  os << nl;
322  }
323 
324  os << "=============" << nl;
325 }
326 
327 
328 // ************************************************************************* //
A class for handling file names.
Definition: fileName.H:71
IOstream & hex(IOstream &io)
Definition: IOstream.H:545
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:440
Functions used by OpenFOAM that are specific to POSIX compliant operating systems and need to be repl...
static void safePrintStack(std::ostream &os, int size=-1)
Helper function to print a stack, with optional upper limit. Used when OpenFOAM IO not yet initialise...
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:55
OBJstream os(runTime.globalPath()/outputName)
fileName cwd()
The physical or logical current working directory path name.
Definition: POSIX.C:590
bool has_ext() const
Various checks for extensions.
Definition: stringI.H:43
static void printStack(Ostream &os, int size=-1)
Helper function to print a stack, with optional upper limit.
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
A class for handling character strings derived from std::string.
Definition: string.H:72