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-2025 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  if (nread > 0)
68  {
69  buf[nread-1] = '\0';
70  }
71  str.assign(buf);
72  }
73  }
74 
75  free(buf);
76  pclose(handle);
77 
78  return str;
79 }
80 
81 
82 inline std::string addressToWord(const uintptr_t addr)
83 {
84  std::ostringstream buf;
85  buf.setf(std::ios_base::hex, std::ios_base::basefield);
86 
87  buf << "0x"; // Same as setf(std::ios::showbase)
88 
89  #ifdef __APPLE__
90  buf << uint64_t(addr);
91  #else
92  buf << addr;
93  #endif
94 
95  return buf.str(); // Needs no stripping
96 }
97 
98 
99 // Note: demangle requires symbols only - without extra '(' etc.
100 inline std::string demangleSymbol(const char* sn)
101 {
102  int st = 0;
103 
104  char* cxx_sname = abi::__cxa_demangle(sn, nullptr, nullptr, &st);
105 
106  if (st == 0 && cxx_sname)
107  {
108  std::string demangled(cxx_sname);
109  free(cxx_sname);
110 
111  return demangled;
112  }
113 
114  return sn;
115 }
116 
117 
118 inline Foam::string& shorterPath(Foam::string& s)
119 {
120  s.replace(Foam::cwd() + '/', "");
121  s.replace(Foam::home(), "~");
122  return s;
123 }
124 
125 
126 void printSourceFileAndLine
127 (
128  Foam::Ostream& os,
129  const Foam::fileName& filename,
130  const Dl_info& info,
131  void *addr
132 )
133 {
134  uintptr_t address = uintptr_t(addr);
135  std::string myAddress = addressToWord(address);
136 
137  // Can use relative addresses for executables and libraries with the
138  // Darwin addr2line implementation.
139  // On other systems (Linux), only use relative addresses for libraries.
140 
141  #ifndef __APPLE__
142  if (filename.has_ext("so"))
143  #endif
144  {
145  // Convert address into offset into dynamic library
146  uintptr_t offset = uintptr_t(info.dli_fbase);
147  intptr_t relativeAddress = address - offset;
148  myAddress = addressToWord(relativeAddress);
149  }
150 
151  if (filename[0] == '/')
152  {
153  Foam::string line = pipeOpen
154  (
155  "addr2line -f --demangle=auto --exe "
156  + filename
157  + " "
158  + myAddress,
159  1
160  );
161 
162  if (line.empty())
163  {
164  os << " addr2line failed";
165  }
166  else if (line == "??:0" || line == "??:?" )
167  {
168  line = filename;
169  os << " in " << shorterPath(line).c_str();
170  }
171  else
172  {
173  os << " at " << shorterPath(line).c_str();
174  }
175  }
176 }
177 
178 
179 // Uses 'which' to find executable on PATH
180 // - could also iterate through PATH directly
181 inline Foam::fileName whichPath(const char* fn)
182 {
183  Foam::fileName fname(fn);
184 
185  if (!fname.empty() && fname[0] != '/' && fname[0] != '~')
186  {
187  std::string s = pipeOpen("which " + fname);
188 
189  if (s[0] == '/' || s[0] == '~')
190  {
191  fname = s;
192  }
193  }
194 
195  return fname;
196 }
197 
198 } // End anonymous namespace
199 
200 
201 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
202 
203 void Foam::error::safePrintStack(std::ostream& os, int size)
204 {
205  // Get raw stack symbols
206  void *callstack[100];
207  size = backtrace(callstack, (size > 0 && size < 100) ? size + 1 : 100);
208 
209  char **strings = backtrace_symbols(callstack, size);
210  size_t rdelim;
211 
212  // Frame 0 is 'printStack()' - report something more meaningful
213  os << "[stack trace]" << std::endl
214  << "=============" << std::endl;
215 
216  for (int i = 1; i < size; ++i)
217  {
218  std::string str(strings[i]);
219 
220  os << '#' << i << '\t';
221 
222  // Possibly shorten paths that appear to correspond to OpenFOAM
223  // locations (platforms).
224  //
225  // Eg, "/path/openfoam/platforms/linux64GccDPInt32Opt/lib/libxyz.so"
226  // --> "platforms/linux64GccDPInt32Opt/lib/libxyz.so"
227 
228  auto ldelim = str.find('(');
229  auto beg = str.find("/platforms/");
230 
231  if (beg == std::string::npos || !beg || beg > ldelim)
232  {
233  beg = 0;
234  }
235  else
236  {
237  ++beg;
238  }
239 
240  if
241  (
242  (ldelim != std::string::npos)
243  && (rdelim = str.find('+', ldelim+1)) != std::string::npos
244  && (rdelim > ldelim+1)
245  )
246  {
247  // Found function between () e.g. "(__libc_start_main+0xd0)"
248  // - demangle function name (before the '+' offset)
249  // - preserve trailing [0xAddr]
250 
251  os << str.substr(beg, ldelim-beg)
252  << ' '
253  << demangleSymbol
254  (
255  str.substr(ldelim+1, rdelim-ldelim-1).c_str()
256  );
257 
258  if ((rdelim = str.find('[', rdelim)) != std::string::npos)
259  {
260  os << ' ' << str.substr(rdelim);
261  }
262  }
263  else if (beg)
264  {
265  // With shortened path name
266  os << str.substr(beg);
267  }
268  else
269  {
270  // No modification to string
271  os << str;
272  }
273  os << std::endl;
274  }
275 
276  os << "=============" << std::endl;
277 
278  free(strings);
279 }
280 
281 
282 void Foam::error::printStack(Ostream& os, int size)
283 {
284  // Get raw stack symbols
285  void *callstack[100];
286  size = backtrace(callstack, (size > 0 && size < 100) ? size + 1 : 100);
287 
288  Dl_info info;
289  fileName fname;
290 
291  // Frame 0 is 'printStack()' - report something more meaningful
292  os << "[stack trace]" << nl
293  << "=============" << nl;
294 
295  for (int i = 1; i < size; ++i)
296  {
297  int st = dladdr(callstack[i], &info);
298 
299  os << '#' << i << " ";
300  if (st != 0 && info.dli_fname != nullptr && *(info.dli_fname))
301  {
302  fname = whichPath(info.dli_fname);
303 
304  if (info.dli_sname)
305  {
306  os << demangleSymbol(info.dli_sname).c_str();
307  }
308  else
309  {
310  os << '?';
311  }
312  }
313  else
314  {
315  fname = "???";
316  os << '?';
317  }
318 
319  printSourceFileAndLine(os, fname, info, callstack[i]);
320  os << nl;
321  }
322 
323  os << "=============" << nl;
324 }
325 
326 
327 // ************************************************************************* //
A class for handling file names.
Definition: fileName.H:72
IOstream & hex(IOstream &io)
Definition: IOstream.H:560
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:529
fileName home()
Return home directory path name for the current user.
Definition: POSIX.C:442
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:56
OBJstream os(runTime.globalPath()/outputName)
fileName cwd()
The physical or logical current working directory path name.
Definition: POSIX.C:592
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