From ac83b41aafddcc91babb3527d4ac4ba0aa215f25 Mon Sep 17 00:00:00 2001 From: Mark Olesen Date: Wed, 20 Jul 2022 15:26:17 +0200 Subject: [PATCH] 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 --- .../OSspecific/addr2line/addr2line.C | 51 ++-- src/OSspecific/MSwindows/fileStat/fileStat.H | 48 +-- src/OSspecific/POSIX/fileStat/fileStat.H | 48 +-- src/OSspecific/POSIX/printStack/printStack.C | 284 ++++++++++-------- .../db/IOstreams/IOstreams/IOstream.C | 8 +- .../db/IOstreams/IOstreams/IOstream.H | 20 +- src/OpenFOAM/primitives/strings/word/word.C | 5 +- 7 files changed, 260 insertions(+), 204 deletions(-) diff --git a/applications/utilities/miscellaneous/OSspecific/addr2line/addr2line.C b/applications/utilities/miscellaneous/OSspecific/addr2line/addr2line.C index 9d61aef751..6a8345d976 100644 --- a/applications/utilities/miscellaneous/OSspecific/addr2line/addr2line.C +++ b/applications/utilities/miscellaneous/OSspecific/addr2line/addr2line.C @@ -43,7 +43,7 @@ Description static void usage(); static void version(); static std::string getLine(const std::string&, const std::string&); -static std::string pOpen(const std::string&, int line=0); +static std::string pipeOpen(const std::string& cmd, const int lineNum = 0); // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -132,51 +132,50 @@ void version() } -std::string pOpen(const std::string& cmd, int line) +// Read up to and including lineNum from the piped command +// Return the final line read +std::string pipeOpen(const std::string& cmd, int lineNum) { - std::string res; + std::string str; - FILE* cmdPipe = popen(cmd.c_str(), "r"); - if (!cmdPipe) return res; + FILE* handle = popen(cmd.c_str(), "r"); + if (!handle) return str; char* buf = nullptr; + size_t len = 0; + ssize_t nread; - // Read line number of lines - for (int cnt = 0; cnt <= line; ++cnt) + // Read lineNum number of lines + for + ( + int cnt = 0; + cnt <= lineNum && (nread = ::getline(&buf, &len, handle)) >= 0; + ++cnt + ) { - size_t linecap = 0; - ssize_t linelen = ::getline(&buf, &linecap, cmdPipe); - - if (linelen < 0) + if (cnt == lineNum) { - break; - } + // Retain the last line, trimming trailing newline + str.assign(buf); - if (cnt == line) - { - res = std::string(buf); - - // Trim trailing newline - if (res.size()) + if (str.size()) { - res.resize(res.size()-1); + str.resize(str.size()-1); } - break; } } - if (buf) free(buf); + free(buf); + pclose(handle); - pclose(cmdPipe); - - return res; + return str; } std::string getLine(const std::string& filename, const std::string& addr) { std::string line = - pOpen + pipeOpen ( "echo 'image lookup -va " + addr + "'" diff --git a/src/OSspecific/MSwindows/fileStat/fileStat.H b/src/OSspecific/MSwindows/fileStat/fileStat.H index 9912155211..24176aa2d9 100644 --- a/src/OSspecific/MSwindows/fileStat/fileStat.H +++ b/src/OSspecific/MSwindows/fileStat/fileStat.H @@ -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 #include @@ -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; diff --git a/src/OSspecific/POSIX/fileStat/fileStat.H b/src/OSspecific/POSIX/fileStat/fileStat.H index 9912155211..24176aa2d9 100644 --- a/src/OSspecific/POSIX/fileStat/fileStat.H +++ b/src/OSspecific/POSIX/fileStat/fileStat.H @@ -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 #include @@ -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; diff --git a/src/OSspecific/POSIX/printStack/printStack.C b/src/OSspecific/POSIX/printStack/printStack.C index dd72db239c..026e71ee57 100644 --- a/src/OSspecific/POSIX/printStack/printStack.C +++ b/src/OSspecific/POSIX/printStack/printStack.C @@ -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 +#include #include -#include #include +#include -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// * * * * * * * * * * * * * * * 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; idli_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; } diff --git a/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.C b/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.C index 66c6ff87f2..c358ae680b 100644 --- a/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.C +++ b/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.C @@ -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; diff --git a/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.H b/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.H index 7226f7bc60..8ad3282048 100644 --- a/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.H +++ b/src/OpenFOAM/db/IOstreams/IOstreams/IOstream.H @@ -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(sizeof(label))), sizeofScalar_(static_cast(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; } diff --git a/src/OpenFOAM/primitives/strings/word/word.C b/src/OpenFOAM/primitives/strings/word/word.C index 47c9ddd9f7..bd9f5e0fa8 100644 --- a/src/OpenFOAM/primitives/strings/word/word.C +++ b/src/OpenFOAM/primitives/strings/word/word.C @@ -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 }