LibreOffice Module desktop (master)  1
crashreport.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
10 #include <desktop/crashreport.hxx>
11 #include <rtl/bootstrap.hxx>
12 #include <osl/file.hxx>
15 #include <unotools/bootstrap.hxx>
17 #include <desktop/minidump.hxx>
18 
19 #include <config_version.h>
20 #include <config_folders.h>
21 
22 #include <string>
23 
24 
25 #if HAVE_FEATURE_BREAKPAD
26 
27 #include <fstream>
28 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
29 #include <client/linux/handler/exception_handler.h>
30 #elif defined _WIN32
31 #if defined __clang__
32 #pragma clang diagnostic push
33 #pragma clang diagnostic ignored "-Wmicrosoft-enum-value"
34 #endif
35 #include <client/windows/handler/exception_handler.h>
36 #if defined __clang__
37 #pragma clang diagnostic pop
38 #endif
39 #include <locale>
40 #include <codecvt>
41 #endif
42 
43 osl::Mutex CrashReporter::maMutex;
44 std::unique_ptr<google_breakpad::ExceptionHandler> CrashReporter::mpExceptionHandler;
45 bool CrashReporter::mbInit = false;
46 CrashReporter::vmaKeyValues CrashReporter::maKeyValues;
47 
48 
49 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
50 static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* /*context*/, bool succeeded)
51 {
52  CrashReporter::addKeyValue("DumpFile", OStringToOUString(descriptor.path(), RTL_TEXTENCODING_UTF8), CrashReporter::Write);
53  SAL_WARN("desktop", "minidump generated: " << descriptor.path());
54 
55  return succeeded;
56 }
57 #elif defined _WIN32
58 static bool dumpCallback(const wchar_t* path, const wchar_t* id,
59  void* /*context*/, EXCEPTION_POINTERS* /*exinfo*/,
60  MDRawAssertionInfo* /*assertion*/,
61  bool succeeded)
62 {
63  // TODO: moggi: can we avoid this conversion
64 #ifdef _MSC_VER
65 #pragma warning (disable: 4996)
66 #endif
67  std::wstring_convert<std::codecvt_utf8<wchar_t>> conv1;
68  std::string aPath = conv1.to_bytes(std::wstring(path)) + conv1.to_bytes(std::wstring(id)) + ".dmp";
69  CrashReporter::addKeyValue("DumpFile", OStringToOUString(aPath.c_str(), RTL_TEXTENCODING_UTF8), CrashReporter::AddItem);
70  CrashReporter::addKeyValue("GDIHandles", OUString::number(::GetGuiResources(::GetCurrentProcess(), GR_GDIOBJECTS)), CrashReporter::Write);
71  SAL_WARN("desktop", "minidump generated: " << aPath);
72  return succeeded;
73 }
74 #endif
75 
76 
77 void CrashReporter::writeToFile(std::ios_base::openmode Openmode)
78 {
79  std::ofstream ini_file(getIniFileName(), Openmode);
80 
81  for (auto& keyValue : maKeyValues)
82  {
83  ini_file << OUStringToOString(keyValue.first, RTL_TEXTENCODING_UTF8) << "=";
84  ini_file << OUStringToOString(keyValue.second, RTL_TEXTENCODING_UTF8) << "\n";
85  }
86 
87  maKeyValues.clear();
88  ini_file.close();
89 }
90 
91 void CrashReporter::addKeyValue(const OUString& rKey, const OUString& rValue, tAddKeyHandling AddKeyHandling)
92 {
93  osl::MutexGuard aGuard(maMutex);
94 
95  if (IsDumpEnable())
96  {
97  if (!rKey.isEmpty())
98  maKeyValues.push_back(mpair(rKey, rValue));
99 
100  if (AddKeyHandling != AddItem)
101  {
102  if (mbInit)
103  writeToFile(std::ios_base::app);
104  else if (AddKeyHandling == Create)
105  writeCommonInfo();
106  }
107  }
108 }
109 
110 void CrashReporter::writeCommonInfo()
111 {
113 
114  const OUString protocol = "https";
115  const OUString url = "crashreport.libreoffice.org";
116  const sal_Int32 port = 443;
117 
118  const ucbhelper::InternetProxyServer proxy_server = proxy_decider.getProxy(protocol, url, port);
119 
120  // save the new Keys
121  vmaKeyValues atlast = maKeyValues;
122  // clear the keys, the following Keys should be at the begin
123  maKeyValues.clear();
124 
125  // limit the amount of code that needs to be executed before the crash reporting
126  addKeyValue("ProductName", "LibreOffice", AddItem);
127  addKeyValue("Version", LIBO_VERSION_DOTTED, AddItem);
129  addKeyValue("URL", protocol + "://" + url + "/submit/", AddItem);
130 
131  if (proxy_server.aName != OUString())
132  {
133  addKeyValue("Proxy", proxy_server.aName + ":" + OUString::number(proxy_server.nPort), AddItem);
134  }
135 
136  // write the new keys at the end
137  maKeyValues.insert(maKeyValues.end(), atlast.begin(), atlast.end());
138 
139  mbInit = true;
140 
141  writeToFile(std::ios_base::trunc);
142 
143  updateMinidumpLocation();
144 }
145 
146 
147 namespace {
148 
149 OUString getCrashDirectory()
150 {
151  OUString aCrashURL;
152  rtl::Bootstrap::get("CrashDirectory", aCrashURL);
153  // Need to convert to URL in case of user-defined path
154  osl::FileBase::getFileURLFromSystemPath(aCrashURL, aCrashURL);
155 
156  if (aCrashURL.isEmpty()) { // Fall back to user profile
157  aCrashURL = "${$BRAND_BASE_DIR/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE("bootstrap") ":UserInstallation}/crash/";
158  rtl::Bootstrap::expandMacros(aCrashURL);
159  }
160 
161  if (!aCrashURL.endsWith("/"))
162  aCrashURL += "/";
163 
164  osl::Directory::create(aCrashURL);
165  OUString aCrashPath;
166  osl::FileBase::getSystemPathFromFileURL(aCrashURL, aCrashPath);
167  return aCrashPath;
168 }
169 
170 }
171 
172 void CrashReporter::updateMinidumpLocation()
173 {
174 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
175  OUString aURL = getCrashDirectory();
176  OString aOStringUrl = OUStringToOString(aURL, RTL_TEXTENCODING_UTF8);
177  google_breakpad::MinidumpDescriptor descriptor(aOStringUrl.getStr());
178  mpExceptionHandler->set_minidump_descriptor(descriptor);
179 #elif defined _WIN32
180  OUString aURL = getCrashDirectory();
181  mpExceptionHandler->set_dump_path(o3tl::toW(aURL.getStr()));
182 #endif
183 }
184 
185 bool CrashReporter::crashReportInfoExists()
186 {
187  static bool first = true;
188  static bool InfoExist = false;
189 
190  if (first)
191  {
192  first = false;
193  InfoExist = crashreport::readConfig(CrashReporter::getIniFileName(), nullptr);
194  }
195 
196  return InfoExist;
197 }
198 
199 bool CrashReporter::readSendConfig(std::string& response)
200 {
201  return crashreport::readConfig(CrashReporter::getIniFileName(), &response);
202 }
203 
204 void CrashReporter::installExceptionHandler()
205 {
206  if (!IsDumpEnable())
207  return;
208 #if defined( UNX ) && !defined MACOSX && !defined IOS && !defined ANDROID
209  google_breakpad::MinidumpDescriptor descriptor("/tmp");
210  mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(descriptor, nullptr, dumpCallback, nullptr, true, -1);
211 #elif defined _WIN32
212  mpExceptionHandler = std::make_unique<google_breakpad::ExceptionHandler>(L".", nullptr, dumpCallback, nullptr, google_breakpad::ExceptionHandler::HANDLER_ALL);
213 #endif
214 }
215 
216 void CrashReporter::removeExceptionHandler()
217 {
218  mpExceptionHandler.reset();
219 }
220 
221 
222 
223 bool CrashReporter::IsDumpEnable()
224 {
225  OUString sToken;
226  OString sEnvVar(std::getenv("CRASH_DUMP_ENABLE"));
227  bool bEnable = true; // default, always on
228  // read configuration item 'CrashDumpEnable' -> bool on/off
229  if (rtl::Bootstrap::get("CrashDumpEnable", sToken) && sEnvVar.isEmpty())
230  {
231  bEnable = sToken.toBoolean();
232  }
233 
234  return bEnable;
235 }
236 
237 
238 std::string CrashReporter::getIniFileName()
239 {
240  OUString url = getCrashDirectory() + "dump.ini";
241  OString aUrl = OUStringToOString(url, RTL_TEXTENCODING_UTF8);
242  std::string aRet(aUrl.getStr());
243  return aRet;
244 }
245 
246 
247 #endif //HAVE_FEATURE_BREAKPAD
248 
249 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
URL aURL
css::uno::Reference< css::deployment::XPackageRegistry > create(css::uno::Reference< css::deployment::XPackageRegistry > const &xRootRegistry, OUString const &context, OUString const &cachePath, css::uno::Reference< css::uno::XComponentContext > const &xComponentContext)
bool readConfig(const std::string &iniPath, std::string *response)
Read+Send, Test and send info from the Dump.ini .
Definition: minidump.cxx:193
#define SAL_CONFIGFILE(name)
OString OUStringToOString(const OUString &str, ConnectionSettings const *settings)
Reference< XComponentContext > getProcessComponentContext()
void SAL_CALL first(const css::awt::SpinEvent &rEvent) override
#define SAL_WARN(area, stream)
static OUString getBuildIdData(OUString const &_sDefault)
static void addKeyValue(SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER const OUString &, SAL_UNUSED_PARAMETER tAddKeyHandling)
Definition: crashreport.hxx:89