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 <osl/mutex.hxx>
58 #include <o3tl/lru_map.hxx>
59 #include "file_url.hxx"
65 const char* file
= nullptr;
72 typedef o3tl::lru_map
<void*, OString
> FrameCache
;
73 std::mutex frameCacheMutex
;
74 FrameCache
frameCache( 256 );
76 void process_file_addr2line( const char* file
, std::vector
<FrameData
>& frameData
)
78 if(access( file
, R_OK
) != 0)
79 return; // cannot read info from the binary file anyway
80 OUString
binary("addr2line");
83 // llvm-addr2line is faster than addr2line
84 if(osl::detail::find_in_PATH("llvm-addr2line", dummy
))
85 binary
= "llvm-addr2line";
87 if(!osl::detail::find_in_PATH(binary
, dummy
))
88 return; // Will not work, avoid warnings from osl process code.
89 OUString
arg1("-Cfe");
90 OUString arg2
= OUString::fromUtf8(file
);
91 std::vector
<OUString
> addrs
;
92 std::vector
<rtl_uString
*> args
;
93 args
.reserve(frameData
.size() + 2);
94 args
.push_back( arg1
.pData
);
95 args
.push_back( arg2
.pData
);
96 for( FrameData
& frame
: frameData
)
98 if( frame
.file
!= nullptr && strcmp( file
, frame
.file
) == 0 )
100 addrs
.push_back("0x" + OUString::number(frame
.offset
, 16));
101 args
.push_back(addrs
.back().pData
);
102 frame
.handled
= true;
107 oslFileHandle pOut
= nullptr;
108 oslFileHandle pErr
= nullptr;
109 oslSecurity pSecurity
= osl_getCurrentSecurity();
110 oslProcessError eErr
= osl_executeProcess_WithRedirectedIO(
111 binary
.pData
, args
.data(), args
.size(), osl_Process_SEARCHPATH
| osl_Process_HIDDEN
, pSecurity
, nullptr,
112 nullptr, 0, &aProcess
, nullptr, &pOut
, &pErr
);
113 osl_freeSecurityHandle(pSecurity
);
115 if (eErr
!= osl_Process_E_None
)
117 SAL_WARN("sal.osl", binary
<< " call to resolve " << file
<< " symbols failed");
121 OStringBuffer outputBuffer
;
124 const sal_uInt64 BUF_SIZE
= 1024;
125 char buffer
[BUF_SIZE
];
128 sal_uInt64 bytesRead
= 0;
129 while(osl_readFile(pErr
, buffer
, BUF_SIZE
, &bytesRead
) == osl_File_E_None
131 ; // discard possible stderr output
132 oslFileError err
= osl_readFile(pOut
, buffer
, BUF_SIZE
, &bytesRead
);
133 if(bytesRead
== 0 && err
== osl_File_E_None
)
135 outputBuffer
.append(buffer
, bytesRead
);
136 if (err
!= osl_File_E_None
&& err
!= osl_File_E_AGAIN
)
143 eErr
= osl_joinProcess(aProcess
);
144 osl_freeProcessHandle(aProcess
);
146 OString output
= outputBuffer
.makeStringAndClear();
147 std::vector
<OString
> lines
;
148 sal_Int32 outputPos
= 0;
149 while(outputPos
< output
.getLength())
151 sal_Int32 end1
= output
.indexOf('\n', outputPos
);
154 sal_Int32 end2
= output
.indexOf('\n', end1
+ 1);
156 end2
= output
.getLength();
157 lines
.push_back(output
.copy( outputPos
, end1
- outputPos
));
158 lines
.push_back(output
.copy( end1
+ 1, end2
- end1
- 1 ));
159 outputPos
= end2
+ 1;
161 if(lines
.size() != addrs
.size() * 2)
163 SAL_WARN("sal.osl", "failed to parse " << binary
<< " call output to resolve " << file
<< " symbols ");
164 return; // addr2line problem?
167 for( FrameData
& frame
: frameData
)
169 if( frame
.file
!= nullptr && strcmp( file
, frame
.file
) == 0 )
171 // There should be two lines, first function name and second source file information.
172 // If each of them starts with ??, it is invalid/unknown.
173 OString function
= lines
[linesPos
];
174 OString source
= lines
[linesPos
+1];
176 if(function
.isEmpty() || function
.startsWith("??"))
178 // Cache that the address cannot be resolved.
179 std::lock_guard
guard(frameCacheMutex
);
180 frameCache
.insert( { frame
.addr
, "" } );
184 if( source
.startsWith("??"))
185 frame
.info
= function
+ " in " + file
;
187 frame
.info
= function
+ " at " + source
;
188 std::lock_guard
guard(frameCacheMutex
);
189 frameCache
.insert( { frame
.addr
, frame
.info
} );
197 OUString
sal::backtrace_to_string(BacktraceState
* backtraceState
)
199 // Collect frames for each binary and process each binary in one addr2line
200 // call for better performance.
201 std::vector
< FrameData
> frameData
;
202 frameData
.resize(backtraceState
->nDepth
);
203 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
206 void* addr
= backtraceState
->buffer
[i
];
207 std::unique_lock
guard(frameCacheMutex
);
208 auto it
= frameCache
.find(addr
);
209 bool found
= it
!= frameCache
.end();
213 frameData
[ i
].info
= it
->second
;
214 frameData
[ i
].handled
= true;
216 else if (dladdr(addr
, &dli
) != 0)
218 if (dli
.dli_fname
&& dli
.dli_fbase
)
220 frameData
[ i
].file
= dli
.dli_fname
;
221 frameData
[ i
].addr
= addr
;
222 frameData
[ i
].offset
= reinterpret_cast<ptrdiff_t>(addr
) - reinterpret_cast<ptrdiff_t>(dli
.dli_fbase
);
226 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
228 if(frameData
[ i
].file
!= nullptr && !frameData
[ i
].handled
)
229 process_file_addr2line( frameData
[ i
].file
, frameData
);
232 std::unique_ptr
<char*, decltype(free
)*> b2
{ nullptr, free
};
233 bool fallbackInitDone
= false;
234 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
)
238 b3
.append( "#" + OUString::number( i
) + " " );
239 if(!frameData
[i
].info
.isEmpty())
240 b3
.append(o3tl::runtimeToOUString(frameData
[i
].info
.getStr()));
243 if(!fallbackInitDone
)
245 b2
= std::unique_ptr
<char*, decltype(free
)*>
246 {backtrace_symbols(backtraceState
->buffer
, backtraceState
->nDepth
), free
};
247 fallbackInitDone
= true;
250 b3
.append(o3tl::runtimeToOUString(b2
.get()[i
]));
255 return b3
.makeStringAndClear();
260 OUString
sal::backtrace_to_string(BacktraceState
* backtraceState
)
262 std::unique_ptr
<char*, decltype(free
)*> b2
{backtrace_symbols(backtraceState
->buffer
, backtraceState
->nDepth
), free
};
267 for (int i
= 0; i
!= backtraceState
->nDepth
; ++i
) {
271 b3
.append( "#" + OUString::number( i
) + " " );
272 b3
.append(o3tl::runtimeToOUString(b2
.get()[i
]));
274 return b3
.makeStringAndClear();
279 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */