stronger typing for SwClient::GetRegisteredIn" and fix SwIterator cast
[LibreOffice.git] / sal / osl / unx / backtraceapi.cxx
blob1a39af42afe8bc3cdf5797547cc6a9bd49e357f2
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/.
8 */
10 #include <sal/config.h>
12 #include <cassert>
13 #include <cstdlib>
14 #include <limits>
15 #include <memory>
16 #include <mutex>
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).
52 #include <dlfcn.h>
53 #include <unistd.h>
54 #include <vector>
55 #include <osl/process.h>
56 #include <rtl/strbuf.hxx>
57 #include <o3tl/lru_map.hxx>
58 #include "file_url.hxx"
60 namespace
62 struct FrameData
64 const char* file = nullptr;
65 void* addr;
66 ptrdiff_t offset;
67 OString info;
68 bool handled = false;
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);
80 OUString dummy;
81 #if defined __clang__
82 // llvm-addr2line is faster than addr2line
83 if(osl::detail::find_in_PATH(u"llvm-addr2line"_ustr, dummy))
84 binary = "llvm-addr2line";
85 #endif
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;
105 oslProcess aProcess;
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");
117 return;
120 OStringBuffer outputBuffer;
121 if (pOut)
123 const sal_uInt64 BUF_SIZE = 1024;
124 char buffer[BUF_SIZE];
125 while (true)
127 sal_uInt64 bytesRead = 0;
128 while(osl_readFile(pErr, buffer, BUF_SIZE, &bytesRead) == osl_File_E_None
129 && bytesRead != 0)
130 ; // discard possible stderr output
131 oslFileError err = osl_readFile(pOut, buffer, BUF_SIZE, &bytesRead);
132 if(bytesRead == 0 && err == osl_File_E_None)
133 break;
134 outputBuffer.append(buffer, bytesRead);
135 if (err != osl_File_E_None && err != osl_File_E_AGAIN)
136 break;
138 osl_closeFile(pOut);
140 if(pErr)
141 osl_closeFile(pErr);
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);
151 if(end1 < 0)
152 break;
153 sal_Int32 end2 = output.indexOf('\n', end1 + 1);
154 if(end2 < 0)
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?
165 size_t linesPos = 0;
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];
174 linesPos += 2;
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, "" } );
181 else
183 if( source.startsWith("??"))
184 frame.info = function + " in " + file;
185 else
186 frame.info = function + " at " + source;
187 std::lock_guard guard(frameCacheMutex);
188 frameCache.insert( { frame.addr, frame.info } );
194 } // namespace
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)
204 Dl_info dli;
205 void* addr = backtraceState->buffer[i];
206 std::unique_lock guard(frameCacheMutex);
207 auto it = frameCache.find(addr);
208 bool found = it != frameCache.end();
209 guard.unlock();
210 if( found )
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 );
230 OUStringBuffer b3;
231 std::unique_ptr<char*, decltype(free)*> b2{ nullptr, free };
232 bool fallbackInitDone = false;
233 for (int i = 0; i != backtraceState->nDepth; ++i)
235 if (i != 0)
236 b3.append("\n");
237 b3.append( "#" + OUString::number( i ) + " " );
238 if(!frameData[i].info.isEmpty())
239 b3.append(o3tl::runtimeToOUString(frameData[i].info.getStr()));
240 else
242 if(!fallbackInitDone)
244 b2 = std::unique_ptr<char*, decltype(free)*>
245 {backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
246 fallbackInitDone = true;
248 if(b2)
249 b3.append(o3tl::runtimeToOUString(b2.get()[i]));
250 else
251 b3.append("??");
254 return b3.makeStringAndClear();
257 #else
259 OUString sal::backtrace_to_string(BacktraceState* backtraceState)
261 std::unique_ptr<char*, decltype(free)*> b2{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
262 if (!b2) {
263 return OUString();
265 OUStringBuffer b3;
266 for (int i = 0; i != backtraceState->nDepth; ++i) {
267 if (i != 0) {
268 b3.append("\n");
270 b3.append( "#" + OUString::number( i ) + " " );
271 b3.append(o3tl::runtimeToOUString(b2.get()[i]));
273 return b3.makeStringAndClear();
276 #endif
278 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */