tdf#154546 skip dispatch when presenter controller is not set
[LibreOffice.git] / sal / osl / unx / backtraceapi.cxx
bloba4cec46ca95bfb768c57442b48663b3f771801fd
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 <osl/mutex.hxx>
58 #include <o3tl/lru_map.hxx>
59 #include "file_url.hxx"
61 namespace
63 struct FrameData
65 const char* file = nullptr;
66 void* addr;
67 ptrdiff_t offset;
68 OString info;
69 bool handled = false;
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");
81 OUString dummy;
82 #if defined __clang__
83 // llvm-addr2line is faster than addr2line
84 if(osl::detail::find_in_PATH("llvm-addr2line", dummy))
85 binary = "llvm-addr2line";
86 #endif
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;
106 oslProcess aProcess;
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");
118 return;
121 OStringBuffer outputBuffer;
122 if (pOut)
124 const sal_uInt64 BUF_SIZE = 1024;
125 char buffer[BUF_SIZE];
126 while (true)
128 sal_uInt64 bytesRead = 0;
129 while(osl_readFile(pErr, buffer, BUF_SIZE, &bytesRead) == osl_File_E_None
130 && bytesRead != 0)
131 ; // discard possible stderr output
132 oslFileError err = osl_readFile(pOut, buffer, BUF_SIZE, &bytesRead);
133 if(bytesRead == 0 && err == osl_File_E_None)
134 break;
135 outputBuffer.append(buffer, bytesRead);
136 if (err != osl_File_E_None && err != osl_File_E_AGAIN)
137 break;
139 osl_closeFile(pOut);
141 if(pErr)
142 osl_closeFile(pErr);
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);
152 if(end1 < 0)
153 break;
154 sal_Int32 end2 = output.indexOf('\n', end1 + 1);
155 if(end2 < 0)
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?
166 size_t linesPos = 0;
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];
175 linesPos += 2;
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, "" } );
182 else
184 if( source.startsWith("??"))
185 frame.info = function + " in " + file;
186 else
187 frame.info = function + " at " + source;
188 std::lock_guard guard(frameCacheMutex);
189 frameCache.insert( { frame.addr, frame.info } );
195 } // namespace
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)
205 Dl_info dli;
206 void* addr = backtraceState->buffer[i];
207 std::unique_lock guard(frameCacheMutex);
208 auto it = frameCache.find(addr);
209 bool found = it != frameCache.end();
210 guard.unlock();
211 if( found )
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 );
231 OUStringBuffer b3;
232 std::unique_ptr<char*, decltype(free)*> b2{ nullptr, free };
233 bool fallbackInitDone = false;
234 for (int i = 0; i != backtraceState->nDepth; ++i)
236 if (i != 0)
237 b3.append("\n");
238 b3.append( "#" + OUString::number( i ) + " " );
239 if(!frameData[i].info.isEmpty())
240 b3.append(o3tl::runtimeToOUString(frameData[i].info.getStr()));
241 else
243 if(!fallbackInitDone)
245 b2 = std::unique_ptr<char*, decltype(free)*>
246 {backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
247 fallbackInitDone = true;
249 if(b2)
250 b3.append(o3tl::runtimeToOUString(b2.get()[i]));
251 else
252 b3.append("??");
255 return b3.makeStringAndClear();
258 #else
260 OUString sal::backtrace_to_string(BacktraceState* backtraceState)
262 std::unique_ptr<char*, decltype(free)*> b2{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
263 if (!b2) {
264 return OUString();
266 OUStringBuffer b3;
267 for (int i = 0; i != backtraceState->nDepth; ++i) {
268 if (i != 0) {
269 b3.append("\n");
271 b3.append( "#" + OUString::number( i ) + " " );
272 b3.append(o3tl::runtimeToOUString(b2.get()[i]));
274 return b3.makeStringAndClear();
277 #endif
279 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */