ENH: improve demangled symbol names for safePrintStack

- parse out symbols and use abi::__cxa_demangle for more readable
  names in safePrintStack.

- shorten prefixed /path/openfoam/platforms/lib/... to start
  with "platforms/lib/..." to avoid unreadably long lines.

- improved file-scope localization of helper functions.

STYLE: use std::ios_base::basefield instead of dec|oct|hex for masking
This commit is contained in:
Mark Olesen
2022-07-20 15:26:17 +02:00
parent 20f1afd9f7
commit ac83b41aaf
7 changed files with 260 additions and 204 deletions

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2019 OpenCFD Ltd.
Copyright (C) 2016-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -40,8 +40,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef fileStat_H
#define fileStat_H
#ifndef Foam_fileStat_H
#define Foam_fileStat_H
#include <sys/stat.h>
#include <sys/types.h>
@ -54,7 +54,7 @@ SourceFiles
namespace Foam
{
// Forward declarations
// Forward Declarations
class fileStat;
Istream& operator>>(Istream& is, fileStat& fs);
@ -67,7 +67,7 @@ Ostream& operator<<(Ostream& os, const fileStat& fs);
class fileStat
{
// Private data
// Private Data
struct stat status_;
@ -78,7 +78,7 @@ public:
// Constructors
//- Empty constructor
//- Default construct
fileStat();
//- Construct from components.
@ -89,7 +89,7 @@ public:
// \param maxTime The timeout value.
//
// \note An empty filename is a no-op.
fileStat
explicit fileStat
(
const char* fName,
const bool followLink = true,
@ -104,7 +104,7 @@ public:
// \param maxTime The timeout value.
//
// \note An empty filename is a no-op.
fileStat
explicit fileStat
(
const fileName& fName,
const bool followLink = true,
@ -119,27 +119,33 @@ public:
// Access
//- Raw status
const struct stat& status() const
{
return status_;
}
//- Was file-stat successful?
bool valid() const
//- True if file-stat was successful
bool valid() const noexcept
{
return valid_;
}
//- Size in bytes. Zero for an invalid file-stat.
//- True if file-stat was successful
explicit operator bool() const noexcept
{
return valid_;
}
//- The raw status
const struct stat& status() const noexcept
{
return status_;
}
//- Size in bytes, 0 for an invalid file-stat.
label size() const;
//- Return the modification time in seconds.
// Zero for an invalid file-stat.
//- The modification time in seconds,
//- 0 for an invalid file-stat.
time_t modTime() const;
//- Return the modification time in seconds (nanosecond resolution)
// Zero for an invalid file-stat.
//- The modification time in seconds (nanosecond resolution),
//- 0 for an invalid file-stat.
double dmodTime() const;

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2015 OpenFOAM Foundation
Copyright (C) 2016-2019 OpenCFD Ltd.
Copyright (C) 2016-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -40,8 +40,8 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef fileStat_H
#define fileStat_H
#ifndef Foam_fileStat_H
#define Foam_fileStat_H
#include <sys/stat.h>
#include <sys/types.h>
@ -54,7 +54,7 @@ SourceFiles
namespace Foam
{
// Forward declarations
// Forward Declarations
class fileStat;
Istream& operator>>(Istream& is, fileStat& fs);
@ -67,7 +67,7 @@ Ostream& operator<<(Ostream& os, const fileStat& fs);
class fileStat
{
// Private data
// Private Data
struct stat status_;
@ -78,7 +78,7 @@ public:
// Constructors
//- Empty constructor
//- Default construct
fileStat();
//- Construct from components.
@ -89,7 +89,7 @@ public:
// \param maxTime The timeout value.
//
// \note An empty filename is a no-op.
fileStat
explicit fileStat
(
const char* fName,
const bool followLink = true,
@ -104,7 +104,7 @@ public:
// \param maxTime The timeout value.
//
// \note An empty filename is a no-op.
fileStat
explicit fileStat
(
const fileName& fName,
const bool followLink = true,
@ -119,27 +119,33 @@ public:
// Access
//- Raw status
const struct stat& status() const
{
return status_;
}
//- Was file-stat successful?
bool valid() const
//- True if file-stat was successful
bool valid() const noexcept
{
return valid_;
}
//- Size in bytes. Zero for an invalid file-stat.
//- True if file-stat was successful
explicit operator bool() const noexcept
{
return valid_;
}
//- The raw status
const struct stat& status() const noexcept
{
return status_;
}
//- Size in bytes, 0 for an invalid file-stat.
label size() const;
//- Return the modification time in seconds.
// Zero for an invalid file-stat.
//- The modification time in seconds,
//- 0 for an invalid file-stat.
time_t modTime() const;
//- Return the modification time in seconds (nanosecond resolution)
// Zero for an invalid file-stat.
//- The modification time in seconds (nanosecond resolution),
//- 0 for an invalid file-stat.
double dmodTime() const;

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2019 OpenCFD Ltd.
Copyright (C) 2019-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,95 +28,112 @@ License
#include "error.H"
#include "OSspecific.H"
#include "IFstream.H"
#include "StringStream.H"
#include <cinttypes>
#include <sstream>
#include <cxxabi.h>
#include <execinfo.h>
#include <dlfcn.h>
#include <execinfo.h>
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
namespace
{
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
string pOpen(const string& cmd, label line=0)
// Read up to and including lineNum from the piped command
// Return the final line read
std::string pipeOpen(const std::string& cmd, const int lineNum = 0)
{
string res;
std::string str;
FILE *cmdPipe = popen(cmd.c_str(), "r");
if (cmdPipe)
FILE *handle = popen(cmd.c_str(), "r");
if (!handle) return str;
char* buf = nullptr;
size_t len = 0;
ssize_t nread;
// Read lineNum number of lines
for
(
int cnt = 0;
cnt <= lineNum && (nread = ::getline(&buf, &len, handle)) >= 0;
++cnt
)
{
char *buf = nullptr;
// Read line number of lines
for (label cnt = 0; cnt <= line; ++cnt)
if (cnt == lineNum)
{
size_t linecap = 0;
ssize_t linelen = ::getline(&buf, &linecap, cmdPipe);
// Retain the last line, trimming trailing newline
str.assign(buf);
if (linelen < 0)
if (str.size())
{
break;
}
if (cnt == line)
{
res = string(buf);
// Trim trailing newline
if (res.size())
{
res.resize(res.size()-1);
}
break;
str.resize(str.size()-1);
}
}
if (buf != nullptr)
{
free(buf);
}
pclose(cmdPipe);
}
return res;
free(buf);
pclose(handle);
return str;
}
inline word addressToWord(const uintptr_t addr)
inline std::string addressToWord(const uintptr_t addr)
{
OStringStream os;
std::ostringstream buf;
buf.setf(std::ios_base::hex, std::ios_base::basefield);
buf << "0x"; // Same as setf(std::ios::showbase)
#ifdef __APPLE__
os << "0x" << hex << uint64_t(addr);
buf << uint64_t(addr);
#else
os << "0x" << hex << addr;
buf << addr;
#endif
return os.str();
return buf.str(); // Needs no stripping
}
inline string& shorterPath(string& s)
// Note: demangle requires symbols only - without extra '(' etc.
inline std::string demangleSymbol(const char* sn)
{
s.replace(cwd() + '/', "");
s.replace(home(), "~");
int st = 0;
char* cxx_sname = abi::__cxa_demangle(sn, nullptr, nullptr, &st);
if (st == 0 && cxx_sname)
{
std::string demangled(cxx_sname);
free(cxx_sname);
return demangled;
}
return sn;
}
inline Foam::string& shorterPath(Foam::string& s)
{
s.replace(Foam::cwd() + '/', "");
s.replace(Foam::home(), "~");
return s;
}
void printSourceFileAndLine
(
Ostream& os,
const fileName& filename,
Dl_info *info,
Foam::Ostream& os,
const Foam::fileName& filename,
const Dl_info& info,
void *addr
)
{
uintptr_t address = uintptr_t(addr);
word myAddress = addressToWord(address);
std::string myAddress = addressToWord(address);
// Can use relative addresses for executables and libraries with the
// Darwin addr2line implementation.
@ -127,14 +144,14 @@ void printSourceFileAndLine
#endif
{
// Convert address into offset into dynamic library
uintptr_t offset = uintptr_t(info->dli_fbase);
uintptr_t offset = uintptr_t(info.dli_fbase);
intptr_t relativeAddress = address - offset;
myAddress = addressToWord(relativeAddress);
}
if (filename[0] == '/')
{
string line = pOpen
Foam::string line = pipeOpen
(
"addr2line -f --demangle=auto --exe "
+ filename
@ -147,7 +164,7 @@ void printSourceFileAndLine
{
os << " addr2line failed";
}
else if (line == "??:0")
else if (line == "??:0" || line == "??:?" )
{
line = filename;
os << " in " << shorterPath(line).c_str();
@ -160,113 +177,138 @@ void printSourceFileAndLine
}
fileName absolutePath(const char* fn)
// Uses 'which' to find executable on PATH
// - could also iterate through PATH directly
inline Foam::fileName whichPath(const char* fn)
{
fileName fname(fn);
Foam::fileName fname(fn);
if (fname[0] != '/' && fname[0] != '~')
if (!fname.empty() && fname[0] != '/' && fname[0] != '~')
{
string tmp = pOpen("which " + fname);
std::string s = pipeOpen("which " + fname);
if (tmp[0] == '/' || tmp[0] == '~')
if (s[0] == '/' || s[0] == '~')
{
fname = tmp;
fname = s;
}
}
return fname;
}
word demangleSymbol(const char* sn)
{
int st;
char* cxx_sname = abi::__cxa_demangle
(
sn,
nullptr,
0,
&st
);
if (st == 0 && cxx_sname)
{
word demangled(cxx_sname);
free(cxx_sname);
return demangled;
}
return sn;
}
} // End anonymous namespace
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
void Foam::error::safePrintStack(std::ostream& os)
{
// Get raw stack symbols
void *array[100];
size_t size = backtrace(array, 100);
char **strings = backtrace_symbols(array, size);
void *callstack[100];
const int size = backtrace(callstack, 100);
char **strings = backtrace_symbols(callstack, size);
size_t rdelim;
// See if they contain function between () e.g. "(__libc_start_main+0xd0)"
// and see if cplus_demangle can make sense of part before +
for (size_t i = 0; i < size; ++i)
for (int i = 0; i < size; ++i)
{
string msg(strings[i]);
fileName programFile;
word address;
std::string str(strings[i]);
os << '#' << label(i) << '\t' << msg << std::endl;
os << '#' << i << '\t';
// Possibly shorten paths that appear to correspond to OpenFOAM
// locations (platforms).
//
// Eg, "/path/openfoam/platforms/linux64GccDPInt32Opt/lib/libxyz.so"
// --> "platforms/linux64GccDPInt32Opt/lib/libxyz.so"
auto ldelim = str.find('(');
auto beg = str.find("/platforms/");
if (beg == std::string::npos || !beg || beg > ldelim)
{
beg = 0;
}
else
{
++beg;
}
if
(
(ldelim != std::string::npos)
&& (rdelim = str.find('+', ldelim+1)) != std::string::npos
&& (rdelim > ldelim+1)
)
{
// Found function between () e.g. "(__libc_start_main+0xd0)"
// - demangle function name (before the '+' offset)
// - preserve trailing [0xAddr]
os << str.substr(beg, ldelim-beg)
<< ' '
<< demangleSymbol
(
str.substr(ldelim+1, rdelim-ldelim-1).c_str()
);
if ((rdelim = str.find('[', rdelim)) != std::string::npos)
{
os << ' ' << str.substr(rdelim);
}
}
else if (beg)
{
// With shortened path name
os << str.substr(beg);
}
else
{
// No modification to string
os << str;
}
os << std::endl;
}
free(strings);
}
void Foam::error::printStack(Ostream& os)
{
// Get raw stack symbols
const size_t CALLSTACK_SIZE = 128;
void *callstack[100];
const int size = backtrace(callstack, 100);
void *callstack[CALLSTACK_SIZE];
size_t size = backtrace(callstack, CALLSTACK_SIZE);
Dl_info info;
fileName fname;
Dl_info *info = new Dl_info;
fileName fname = "???";
word address;
for (size_t i=0; i<size; ++i)
for (int i = 0; i < size; ++i)
{
int st = dladdr(callstack[i], info);
int st = dladdr(callstack[i], &info);
os << '#' << label(i) << " ";
if (st != 0 && info->dli_fname != nullptr && info->dli_fname[0] != '\0')
os << '#' << i << " ";
if (st != 0 && info.dli_fname != nullptr && *(info.dli_fname))
{
fname = absolutePath(info->dli_fname);
fname = whichPath(info.dli_fname);
os <<
(
(info->dli_sname != nullptr)
? demangleSymbol(info->dli_sname)
: "?"
);
if (info.dli_sname)
{
os << demangleSymbol(info.dli_sname).c_str();
}
else
{
os << '?';
}
}
else
{
os << "?";
fname = "???";
os << '?';
}
printSourceFileAndLine(os, fname, info, callstack[i]);
os << nl;
os << nl;
}
delete info;
}

View File

@ -118,22 +118,22 @@ void Foam::IOstream::print(Ostream& os) const
void Foam::IOstream::print(Ostream& os, const int streamState) const
{
if (streamState == ios_base::goodbit)
if (streamState == std::ios_base::goodbit)
{
os << "ios_base::goodbit set : the last operation on stream succeeded"
<< endl;
}
else if (streamState & ios_base::badbit)
else if (streamState & std::ios_base::badbit)
{
os << "ios_base::badbit set : characters possibly lost"
<< endl;
}
else if (streamState & ios_base::failbit)
else if (streamState & std::ios_base::failbit)
{
os << "ios_base::failbit set : some type of formatting error"
<< endl;
}
else if (streamState & ios_base::eofbit)
else if (streamState & std::ios_base::eofbit)
{
os << "ios_base::eofbit set : at end of stream"
<< endl;

View File

@ -60,9 +60,9 @@ using std::ios_base;
using std::istream;
using std::ostream;
using std::cerr;
using std::cin;
using std::cout;
using std::cerr;
// Additional constructors and methods (as per v2012 and earlier)
#define Foam_IOstream_extras
@ -147,7 +147,7 @@ protected:
//- Set stream state to be good
void setGood() noexcept
{
ioState_ = std::ios_base::iostate(0);
ioState_ = std::ios_base::goodbit;
}
@ -169,7 +169,7 @@ public:
explicit IOstream(IOstreamOption streamOpt = IOstreamOption())
:
IOstreamOption(streamOpt),
ioState_(std::ios_base::iostate(0)),
ioState_(std::ios_base::goodbit),
openClosed_(CLOSED),
sizeofLabel_(static_cast<unsigned char>(sizeof(label))),
sizeofScalar_(static_cast<unsigned char>(sizeof(scalar))),
@ -233,7 +233,7 @@ public:
//- True if next operation might succeed
bool good() const noexcept
{
return ioState_ == 0;
return ioState_ == 0; // == goodbit
}
//- True if end of input seen
@ -331,7 +331,7 @@ public:
// \return the previous value
label lineNumber(const label num) noexcept
{
const label old(lineNumber_);
label old(lineNumber_);
lineNumber_ = num;
return old;
}
@ -440,31 +440,31 @@ inline IOstream& operator<<(IOstream& io, IOstreamManip f)
inline IOstream& dec(IOstream& io)
{
io.setf(ios_base::dec, ios_base::dec|ios_base::hex|ios_base::oct);
io.setf(std::ios_base::dec, std::ios_base::basefield);
return io;
}
inline IOstream& hex(IOstream& io)
{
io.setf(ios_base::hex, ios_base::dec|ios_base::hex|ios_base::oct);
io.setf(std::ios_base::hex, std::ios_base::basefield);
return io;
}
inline IOstream& oct(IOstream& io)
{
io.setf(ios_base::oct, ios_base::dec|ios_base::hex|ios_base::oct);
io.setf(std::ios_base::oct, std::ios_base::basefield);
return io;
}
inline IOstream& fixed(IOstream& io)
{
io.setf(ios_base::fixed, ios_base::floatfield);
io.setf(std::ios_base::fixed, std::ios_base::floatfield);
return io;
}
inline IOstream& scientific(IOstream& io)
{
io.setf(ios_base::scientific, ios_base::floatfield);
io.setf(std::ios_base::scientific, std::ios_base::floatfield);
return io;
}

View File

@ -165,7 +165,10 @@ Foam::word Foam::operator&(const word& a, const word& b)
Foam::word Foam::name(const void* ptr)
{
std::ostringstream buf;
buf << "0x" << std::hex << uintptr_t(ptr);
buf.setf(std::ios_base::hex, std::ios_base::basefield);
buf << "0x"; // Same as setf(std::ios::showbase)
buf << uintptr_t(ptr);
return word(buf.str(), false); // Needs no stripping
}