1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 #include <sal/config.h>
18 #include <o3tl/runtimetooustring.hxx>
19 #include <rtl/ustrbuf.hxx>
20 #include <rtl/ustring.hxx>
21 #include <sal/types.h>
22 #include <sal/log.hxx>
23 #include <sal/backtrace.hxx>
25 #include "backtrace.h"
26 #include <backtraceasstring.hxx>
28 OUString
osl::detail::backtraceAsString(sal_uInt32 maxDepth
) {
29 std::unique_ptr
<sal::BacktraceState
> backtrace
= sal::backtrace_get( maxDepth
);
30 return sal::backtrace_to_string( backtrace
.get());
33 std::unique_ptr
<sal::BacktraceState
> sal::backtrace_get(sal_uInt32 maxDepth
)
35 assert(maxDepth
!= 0);
36 auto const maxInt
= static_cast<unsigned int>(
37 std::numeric_limits
<int>::max());
38 if (maxDepth
> maxInt
) {
39 maxDepth
= static_cast<sal_uInt32
>(maxInt
);
41 auto b1
= new void *[maxDepth
];
42 int n
= backtrace(b1
, static_cast<int>(maxDepth
));
43 return std::unique_ptr
<BacktraceState
>(new BacktraceState
{ b1
, n
});
46 #if OSL_DEBUG_LEVEL > 0 && (defined LINUX || defined MACOSX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined(DRAGONFLY))
47 // The backtrace_symbols() function is unreliable, it requires -rdynamic and even then it cannot resolve names
48 // of many functions, such as those with hidden ELF visibility. Libunwind doesn't resolve names for me either,
49 // boost::stacktrace doesn't work properly, the best result I've found is addr2line. Using addr2line is relatively
50 // slow, but I don't find that to be a big problem for printing of backtraces. Feel free to improve if needed
51 // (e.g. the calls could be grouped by the binary).
55 #include <osl/process.h>
56 #include <rtl/strbuf.hxx>
57 #include <o3tl/lru_map.hxx>
58 #include "file_url.hxx"
64 const char* file
= nullptr;
71 typedef o3tl::lru_map
<void*, OString
> FrameCache
;
72 std::mutex frameCacheMutex
;
73 FrameCache
frameCache( 256 );
75 void process_file_addr2line( const char* file
, std::vector
<FrameData
>& frameData
)
77 if(access( file
, R_OK
) != 0)
78 return; // cannot read info from the binary file anyway
79 OUString
binary(u
"addr2line"_ustr
);
82 // llvm-addr2line is faster than addr2line
83 if(osl::detail::find_in_PATH(u
"llvm-addr2line"_ustr
, dummy
))
84 binary
= "llvm-addr2line";
86 if(!osl::detail::find_in_PATH(binary
, dummy
))
87 return; // Will not work, avoid warnings from osl process code.
88 OUString
arg1(u
"-Cfe"_ustr
);
89 OUString arg2
= OUString::fromUtf8(file
);
90 std::vector
<OUString
> addrs
;
91 std::vector
<rtl_uString
*> args
;
92 args
.reserve(frameData
.size() + 2);
93 args
.push_back( arg1
.pData
);
94 args
.push_back( arg2
.pData
);
95 for( FrameData
& frame
: frameData
)
97 if( frame
.file
!= nullptr && strcmp( file
, frame
.file
) == 0 )
99 addrs
.push_back("0x" + OUString::number(frame
.offset
, 16));
100 args
.push_back(addrs
.back().pData
);
101 frame
.handled
= true;
106 oslFileHandle pOut
= nullptr;
107 oslFileHandle pErr
= nullptr;
108 oslSecurity pSecurity
= osl_getCurrentSecurity();
109 oslProcessError eErr
= osl_executeProcess_WithRedirectedIO(
110 binary
.pData
, args
.data(), args
.size(), osl_Process_SEARCHPATH
| osl_Process_HIDDEN
, pSecurity
, nullptr,
111 nullptr, 0, &aProcess
, nullptr, &pOut
, &pErr
);
112 osl_freeSecurityHandle(pSecurity
);
114 if (eErr
!= osl_Process_E_None
)
116 SAL_WARN("sal.osl", binary
<< " call to resolve " << file
<< " symbols failed");
120 OStringBuffer outputBuffer
;
123 const sal_uInt64 BUF_SIZE
= 1024;
124 char buffer
[BUF_SIZE
];
127 sal_uInt64 bytesRead
= 0;
128 while(osl_readFile(pErr
, buffer
, BUF_SIZE
, &bytesRead
) == osl_File_E_None
130 ; // discard possible stderr output
131 oslFileError err
= osl_readFile(pOut
, buffer
, BUF_SIZE
, &bytesRead
);
132 if(bytesRead
== 0 && err
== osl_File_E_None
)
134 outputBuffer
.append(buffer
, bytesRead
);
135 if (err
!= osl_File_E_None
&& err
!= osl_File_E_AGAIN
)
142 eErr
= osl_joinProcess(aProcess
);
143 osl_freeProcessHandle(aProcess
);
145 OString output
= outputBuffer
.makeStringAndClear();
146 std::vector
<OString
> lines
;
147 sal_Int32 outputPos
= 0;
148 while(outputPos
< output
.getLength())
150 sal_Int32 end1
= output
.indexOf('\n', outputPos
);
153 sal_Int32 end2
= output
.indexOf('\n', end1
+ 1);
155 end2
= output
.getLength();
156 lines
.push_back(output
.copy( outputPos
, end1
- outputPos
));
157 lines
.push_back(output
.copy( end1
+ 1, end2
- end1
- 1 ));
158 outputPos
= end2
+ 1;
160 if(lines
.size() != addrs
.size() * 2)
162 SAL_WARN("sal.osl", "failed to parse " << binary
<< " call output to resolve " << file
<< " symbols ");
163 return; // addr2line problem?
166 for( FrameData
& frame
: frameData
)
168 if( frame
.file
!= nullptr && strcmp( file
, frame
.file
) == 0 )
170 // There should be two lines, first function name and second source file information.
171 // If each of them starts with ??, it is invalid/unknown.
172 const OString
& function
= lines
[linesPos
];
173 const OString
& source
= lines
[linesPos
+1];
175 if(function
.isEmpty() || function
.startsWith("??"))
177 // Cache that the address cannot be resolved.
178 std::lock_guard
guard(frameCacheMutex
);
179 frameCache
.insert( { frame
.addr
, "" } );
183 if( source
.startsWith("??"))
184 frame
.info
= function
+ " in " + file
;
186 frame
.info
= function
+ " at " + source
;
187 std::lock_guard
guard(frameCacheMutex
);
188 frameCache
.insert( { frame
.addr
, frame
.info
} );
196 OUString
sal::backtrace_to_string(BacktraceState
* backtraceState
)
198 // Collect frames for each binary and process each binary in one addr2line
199 // call for better performance.
200 std::vector
< FrameData
> frameData
;
201 frameData
.resize(backtraceState
->nDepth
);
202 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
205 void* addr
= backtraceState
->buffer
[i
];
206 std::unique_lock
guard(frameCacheMutex
);
207 auto it
= frameCache
.find(addr
);
208 bool found
= it
!= frameCache
.end();
212 frameData
[ i
].info
= it
->second
;
213 frameData
[ i
].handled
= true;
215 else if (dladdr(addr
, &dli
) != 0)
217 if (dli
.dli_fname
&& dli
.dli_fbase
)
219 frameData
[ i
].file
= dli
.dli_fname
;
220 frameData
[ i
].addr
= addr
;
221 frameData
[ i
].offset
= reinterpret_cast<ptrdiff_t>(addr
) - reinterpret_cast<ptrdiff_t>(dli
.dli_fbase
);
225 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
227 if(frameData
[ i
].file
!= nullptr && !frameData
[ i
].handled
)
228 process_file_addr2line( frameData
[ i
].file
, frameData
);
231 std::unique_ptr
<char*, decltype(free
)*> b2
{ nullptr, free
};
232 bool fallbackInitDone
= false;
233 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
237 b3
.append( "#" + OUString::number( i
) + " " );
238 if(!frameData
[i
].info
.isEmpty())
239 b3
.append(o3tl::runtimeToOUString(frameData
[i
].info
.getStr()));
242 if(!fallbackInitDone
)
244 b2
= std::unique_ptr
<char*, decltype(free
)*>
245 {backtrace_symbols(backtraceState
->buffer
, backtraceState
->nDepth
), free
};
246 fallbackInitDone
= true;
249 b3
.append(o3tl::runtimeToOUString(b2
.get()[i
]));
254 return b3
.makeStringAndClear();
259 OUString
sal::backtrace_to_string(BacktraceState
* backtraceState
)
261 std::unique_ptr
<char*, decltype(free
)*> b2
{backtrace_symbols(backtraceState
->buffer
, backtraceState
->nDepth
), free
};
266 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
) {
270 b3
.append( "#" + OUString::number( i
) + " " );
271 b3
.append(o3tl::runtimeToOUString(b2
.get()[i
]));
273 return b3
.makeStringAndClear();
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */