#ifndef _WIN32 #include "OfflineSymbolResolver.h" #include #include #include #include #include #include #include std::string ExecShellCommand( const char* cmd ) { std::array buffer; std::string result; std::unique_ptr pipe(popen(cmd, "r"), pclose); if( !pipe ) { return ""; } while( fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr ) { result += buffer.data(); } return result; } class SymbolResolver { public: SymbolResolver() { std::stringstream result( ExecShellCommand("which addr2line") ); std::getline(result, m_addr2LinePath); if( !m_addr2LinePath.length() ) { std::cerr << "'addr2line' was not found in the system, please installed it" << std::endl; } else { std::cout << "Using 'addr2line' found at: '" << m_addr2LinePath.c_str() << "'" << std::endl; } } static void escapeShellParam(std::string const& s, std::string& out) { out.reserve( s.size() + 2 ); out.push_back( '"' ); for( unsigned char c : s ) { if( ' ' <= c and c <= '~' and c != '\\' and c != '"' ) { out.push_back( c ); } else { out.push_back( '\\' ); switch( c ) { case '"': out.push_back( '"' ); break; case '\\': out.push_back( '\\' ); break; case '\t': out.push_back( 't' ); break; case '\r': out.push_back( 'r' ); break; case '\n': out.push_back( 'n' ); break; default: char const* const hexdig = "0123456789ABCDEF"; out.push_back( 'x' ); out.push_back( hexdig[c >> 4] ); out.push_back( hexdig[c & 0xF] ); } } } out.push_back( '"' ); } bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList, SymbolEntryList& resolvedEntries ) { if( !m_addr2LinePath.length() ) return false; std:: string escapedPath; escapeShellParam( imagePath, escapedPath ); size_t entryIdx = 0; while( entryIdx < inputEntryList.size() ) { const size_t startIdx = entryIdx; const size_t batchEndIdx = std::min( inputEntryList.size(), startIdx + (size_t)1024 ); printf( "Resolving symbols [%zu-%zu]\n", startIdx, batchEndIdx ); // generate a single addr2line cmd line for all addresses in one invocation std::stringstream ss; ss << m_addr2LinePath << " -C -f -e " << escapedPath << " -a "; for( ; entryIdx < batchEndIdx; entryIdx++ ) { const FrameEntry& entry = inputEntryList[entryIdx]; ss << " 0x" << std::hex << entry.symbolOffset; } std::string resultStr = ExecShellCommand( ss.str().c_str() ); std::stringstream result( resultStr ); //printf("executing: '%s' got '%s'\n", ss.str().c_str(), result.str().c_str()); // The output is 2 lines per entry with the following contents: // hex_address_of_symbol // symbol_name // file:line for( size_t i = startIdx ;i < batchEndIdx; i++ ) { const FrameEntry& inputEntry = inputEntryList[i]; SymbolEntry newEntry; std::string addr; std::getline( result, addr ); std::getline( result, newEntry.name ); if( newEntry.name == "??" ) { newEntry.name = "[unknown] + " + std::to_string( inputEntry.symbolOffset ); } std::string fileLine; std::getline( result, fileLine ); if( fileLine != "??:?" ) { size_t pos = fileLine.find_last_of( ':' ); if( pos != std::string::npos ) { newEntry.file = fileLine.substr( 0, pos ); std::string lineStr = fileLine.substr( pos + 1 ); char* after = nullptr; newEntry.line = strtol( lineStr.c_str(), &after, 10 ); } } resolvedEntries.push_back( std::move( newEntry ) ); } } return true; } private: std::string m_addr2LinePath; }; bool ResolveSymbols( const std::string& imagePath, const FrameEntryList& inputEntryList, SymbolEntryList& resolvedEntries ) { static SymbolResolver symbolResolver; return symbolResolver.ResolveSymbols( imagePath, inputEntryList, resolvedEntries ); } #endif // #ifndef _WIN32