/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project .
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License , v . 2 . 0 . If a copy of the MPL was not distributed with this
* file , You can obtain one at http : //mozilla.org/MPL/2.0/.
*/
#include <sal/config.h>
#include <cassert>
#include <cstdlib>
#include <limits>
#include <memory>
#include <mutex>
#include <o3tl/runtimetooustring.hxx>
#include <rtl/ustrbuf.hxx>
#include <rtl/ustring.hxx>
#include <sal/types.h>
#include <sal/log.hxx>
#include <sal/backtrace.hxx>
#include "backtrace.h"
#include <backtraceasstring.hxx>
OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) {
std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth );
return sal::backtrace_to_string( backtrace.get());
}
std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth)
{
assert(maxDepth != 0 );
auto const maxInt = static_cast <unsigned int >(
std::numeric_limits<int >::max());
if (maxDepth > maxInt) {
maxDepth = static_cast <sal_uInt32>(maxInt);
}
auto b1 = new void *[maxDepth];
int n = backtrace(b1, static_cast <int >(maxDepth));
return std::unique_ptr<BacktraceState>(new BacktraceState{ b1, n });
}
#if OSL_DEBUG_LEVEL > 0 && (defined LINUX || defined MACOSX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined (DRAGONFLY))
// The backtrace_symbols() function is unreliable, it requires -rdynamic and even then it cannot resolve names
// of many functions, such as those with hidden ELF visibility. Libunwind doesn't resolve names for me either,
// boost::stacktrace doesn't work properly, the best result I've found is addr2line. Using addr2line is relatively
// slow, but I don't find that to be a big problem for printing of backtraces. Feel free to improve if needed
// (e.g. the calls could be grouped by the binary).
#include <dlfcn.h>
#include <unistd.h>
#include <vector>
#include <osl/process.h>
#include <rtl/strbuf.hxx>
#include <o3tl/lru_map.hxx>
#include "file_url.hxx"
namespace
{
struct FrameData
{
const char * file = nullptr;
void * addr;
ptrdiff_t offset;
OString info;
bool handled = false ;
};
typedef o3tl::lru_map<void *, OString> FrameCache;
std::mutex frameCacheMutex;
FrameCache frameCache( 256 );
void process_file_addr2line( const char * file, std::vector<FrameData>& frameData )
{
if (access( file, R_OK ) != 0 )
return ; // cannot read info from the binary file anyway
OUString binary(u"addr2line" _ustr);
OUString dummy;
#if defined __clang__
// llvm-addr2line is faster than addr2line
if (osl::detail::find_in_PATH(u"llvm-addr2line" _ustr, dummy))
binary = "llvm-addr2line" ;
#endif
if (!osl::detail::find_in_PATH(binary, dummy))
return ; // Will not work, avoid warnings from osl process code.
OUString arg1(u"-Cfe" _ustr);
OUString arg2 = OUString::fromUtf8(file);
std::vector<OUString> addrs;
std::vector<rtl_uString*> args;
args.reserve(frameData.size() + 2 );
args.push_back( arg1.pData );
args.push_back( arg2.pData );
for ( FrameData& frame : frameData )
{
if ( frame.file != nullptr && strcmp( file, frame.file ) == 0 )
{
addrs.push_back("0x" + OUString::number(frame.offset, 16 ));
args.push_back(addrs.back().pData);
frame.handled = true ;
}
}
oslProcess aProcess;
oslFileHandle pOut = nullptr;
oslFileHandle pErr = nullptr;
oslSecurity pSecurity = osl_getCurrentSecurity();
oslProcessError eErr = osl_executeProcess_WithRedirectedIO(
binary.pData, args.data(), args.size(), osl_Process_SEARCHPATH | osl_Process_HIDDEN, pSecurity, nullptr,
nullptr, 0 , &aProcess, nullptr, &pOut, &pErr);
osl_freeSecurityHandle(pSecurity);
if (eErr != osl_Process_E_None)
{
SAL_WARN("sal.osl" , binary << " call to resolve " << file << " symbols failed" );
return ;
}
OStringBuffer outputBuffer;
if (pOut)
{
const sal_uInt64 BUF_SIZE = 1024 ;
char buffer[BUF_SIZE];
while (true )
{
sal_uInt64 bytesRead = 0 ;
while (osl_readFile(pErr, buffer, BUF_SIZE, &bytesRead) == osl_File_E_None
&& bytesRead != 0 )
; // discard possible stderr output
oslFileError err = osl_readFile(pOut, buffer, BUF_SIZE, &bytesRead);
if (bytesRead == 0 && err == osl_File_E_None)
break ;
outputBuffer.append(buffer, bytesRead);
if (err != osl_File_E_None && err != osl_File_E_AGAIN)
break ;
}
osl_closeFile(pOut);
}
if (pErr)
osl_closeFile(pErr);
eErr = osl_joinProcess(aProcess);
osl_freeProcessHandle(aProcess);
OString output = outputBuffer.makeStringAndClear();
std::vector<OString> lines;
sal_Int32 outputPos = 0 ;
while (outputPos < output.getLength())
{
sal_Int32 end1 = output.indexOf('\n' , outputPos);
if (end1 < 0 )
break ;
sal_Int32 end2 = output.indexOf('\n' , end1 + 1 );
if (end2 < 0 )
end2 = output.getLength();
lines.push_back(output.copy( outputPos, end1 - outputPos ));
lines.push_back(output.copy( end1 + 1 , end2 - end1 - 1 ));
outputPos = end2 + 1 ;
}
if (lines.size() != addrs.size() * 2 )
{
SAL_WARN("sal.osl" , "failed to parse " << binary << " call output to resolve " << file << " symbols " );
return ; // addr2line problem?
}
size_t linesPos = 0 ;
for ( FrameData& frame : frameData )
{
if ( frame.file != nullptr && strcmp( file, frame.file ) == 0 )
{
// There should be two lines, first function name and second source file information.
// If each of them starts with ??, it is invalid/unknown.
const OString& function = lines[linesPos];
const OString& source = lines[linesPos+1 ];
linesPos += 2 ;
if (function.isEmpty() || function.startsWith("??" ))
{
// Cache that the address cannot be resolved.
std::lock_guard guard(frameCacheMutex);
frameCache.insert( { frame.addr, "" } );
}
else
{
if ( source.startsWith("??" ))
frame.info = function + " in " + file;
else
frame.info = function + " at " + source;
std::lock_guard guard(frameCacheMutex);
frameCache.insert( { frame.addr, frame.info } );
}
}
}
}
} // namespace
OUString sal::backtrace_to_string(BacktraceState* backtraceState)
{
// Collect frames for each binary and process each binary in one addr2line
// call for better performance.
std::vector< FrameData > frameData;
frameData.resize(backtraceState->nDepth);
for (int i = 0 ; i != backtraceState->nDepth; ++i)
{
Dl_info dli;
void * addr = backtraceState->buffer[i];
std::unique_lock guard(frameCacheMutex);
auto it = frameCache.find(addr);
bool found = it != frameCache.end();
guard.unlock();
if ( found )
{
frameData[ i ].info = it->second;
frameData[ i ].handled = true ;
}
else if (dladdr(addr, &dli) != 0 )
{
if (dli.dli_fname && dli.dli_fbase)
{
frameData[ i ].file = dli.dli_fname;
frameData[ i ].addr = addr;
frameData[ i ].offset = reinterpret_cast <ptrdiff_t>(addr) - reinterpret_cast <ptrdiff_t>(dli.dli_fbase);
}
}
}
for (int i = 0 ; i != backtraceState->nDepth; ++i)
{
if (frameData[ i ].file != nullptr && !frameData[ i ].handled)
process_file_addr2line( frameData[ i ].file, frameData );
}
OUStringBuffer b3;
std::unique_ptr<char *, decltype(free)*> b2{ nullptr, free };
bool fallbackInitDone = false ;
for (int i = 0 ; i != backtraceState->nDepth; ++i)
{
if (i != 0 )
b3.append("\n" );
b3.append( "#" + OUString::number( i ) + " " );
if (!frameData[i].info.isEmpty())
b3.append(o3tl::runtimeToOUString(frameData[i].info.getStr()));
else
{
if (!fallbackInitDone)
{
b2 = std::unique_ptr<char *, decltype(free)*>
{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
fallbackInitDone = true ;
}
if (b2)
b3.append(o3tl::runtimeToOUString(b2.get()[i]));
else
b3.append("??" );
}
}
return b3.makeStringAndClear();
}
#else
OUString sal::backtrace_to_string(BacktraceState* backtraceState)
{
std::unique_ptr<char *, decltype(free)*> b2{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
if (!b2) {
return OUString();
}
OUStringBuffer b3;
for (int i = 0 ; i != backtraceState->nDepth; ++i) {
if (i != 0 ) {
b3.append("\n" );
}
b3.append( "#" + OUString::number( i ) + " " );
b3.append(o3tl::runtimeToOUString(b2.get()[i]));
}
return b3.makeStringAndClear();
}
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Messung V0.5 in Prozent C=93 H=84 G=88
¤ Dauer der Verarbeitung: 0.10 Sekunden
(vorverarbeitet am 2026-09-27)
¤
*© Formatika GbR, Deutschland