"Fossies" - the Fresh Open Source Software Archive

Member "tdesktop-2.6.1/Telegram/SourceFiles/core/crash_reports.cpp" (24 Feb 2021, 16959 Bytes) of package /linux/misc/tdesktop-2.6.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "crash_reports.cpp" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2.5.7_vs_2.5.8.

    1 /*
    2 This file is part of Telegram Desktop,
    3 the official desktop application for the Telegram messaging service.
    4 
    5 For license and copyright information please follow this link:
    6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
    7 */
    8 #include "core/crash_reports.h"
    9 
   10 #include "platform/platform_specific.h"
   11 #include "base/platform/base_platform_info.h"
   12 #include "core/launcher.h"
   13 
   14 #include <signal.h>
   15 #include <new>
   16 #include <mutex>
   17 
   18 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
   19 
   20 // see https://blog.inventic.eu/2012/08/qt-and-google-breakpad/
   21 #ifdef Q_OS_WIN
   22 
   23 #pragma warning(push)
   24 #pragma warning(disable:4091)
   25 #include "client/windows/handler/exception_handler.h"
   26 #pragma warning(pop)
   27 
   28 #elif defined Q_OS_MAC // Q_OS_WIN
   29 
   30 #include <execinfo.h>
   31 #include <signal.h>
   32 #include <sys/syscall.h>
   33 #include <dlfcn.h>
   34 #include <unistd.h>
   35 
   36 #ifdef MAC_USE_BREAKPAD
   37 #include "client/mac/handler/exception_handler.h"
   38 #else // MAC_USE_BREAKPAD
   39 #include "client/crashpad_client.h"
   40 #endif // else for MAC_USE_BREAKPAD
   41 
   42 #elif defined Q_OS_UNIX // Q_OS_MAC
   43 
   44 #include <execinfo.h>
   45 #include <signal.h>
   46 #include <sys/syscall.h>
   47 
   48 #include "client/linux/handler/exception_handler.h"
   49 
   50 #endif // Q_OS_UNIX
   51 
   52 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
   53 
   54 namespace CrashReports {
   55 namespace {
   56 
   57 using Annotations = std::map<std::string, std::string>;
   58 using AnnotationRefs = std::map<std::string, const QString*>;
   59 
   60 Annotations ProcessAnnotations;
   61 AnnotationRefs ProcessAnnotationRefs;
   62 
   63 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
   64 
   65 QString ReportPath;
   66 FILE *ReportFile = nullptr;
   67 int ReportFileNo = 0;
   68 
   69 void SafeWriteChar(char ch) {
   70     fwrite(&ch, 1, 1, ReportFile);
   71 }
   72 
   73 template <bool Unsigned, typename Type>
   74 struct writeNumberSignAndRemoveIt {
   75     static void call(Type &number) {
   76         if (number < 0) {
   77             SafeWriteChar('-');
   78             number = -number;
   79         }
   80     }
   81 };
   82 template <typename Type>
   83 struct writeNumberSignAndRemoveIt<true, Type> {
   84     static void call(Type &number) {
   85     }
   86 };
   87 
   88 template <typename Type>
   89 const dump &SafeWriteNumber(const dump &stream, Type number) {
   90     if (!ReportFile) return stream;
   91 
   92     writeNumberSignAndRemoveIt<(Type(-1) > Type(0)), Type>::call(number);
   93     Type upper = 1, prev = number / 10;
   94     while (prev >= upper) {
   95         upper *= 10;
   96     }
   97     while (upper > 0) {
   98         int digit = (number / upper);
   99         SafeWriteChar('0' + digit);
  100         number -= digit * upper;
  101         upper /= 10;
  102     }
  103     return stream;
  104 }
  105 
  106 using ReservedMemoryChunk = std::array<gsl::byte, 1024 * 1024>;
  107 std::unique_ptr<ReservedMemoryChunk> ReservedMemory;
  108 
  109 void InstallOperatorNewHandler() {
  110     ReservedMemory = std::make_unique<ReservedMemoryChunk>();
  111     std::set_new_handler([] {
  112         std::set_new_handler(nullptr);
  113         ReservedMemory.reset();
  114         Unexpected("Could not allocate!");
  115     });
  116 }
  117 
  118 void InstallQtMessageHandler() {
  119     static QtMessageHandler original = nullptr;
  120     original = qInstallMessageHandler([](
  121             QtMsgType type,
  122             const QMessageLogContext &context,
  123             const QString &message) {
  124         if (original) {
  125             original(type, context, message);
  126         }
  127         if (type == QtFatalMsg) {
  128             CrashReports::SetAnnotation("QtFatal", message);
  129             Unexpected("Qt FATAL message was generated!");
  130         }
  131     });
  132 }
  133 
  134 Qt::HANDLE ReportingThreadId = nullptr;
  135 bool ReportingHeaderWritten = false;
  136 QMutex ReportingMutex;
  137 
  138 const char *BreakpadDumpPath = nullptr;
  139 const wchar_t *BreakpadDumpPathW = nullptr;
  140 
  141 #ifdef Q_OS_UNIX
  142 struct sigaction SIG_def[32];
  143 
  144 void SignalHandler(int signum, siginfo_t *info, void *ucontext) {
  145     if (signum > 0) {
  146         sigaction(signum, &SIG_def[signum], 0);
  147     }
  148 
  149 #else // Q_OS_UNIX
  150 void SignalHandler(int signum) {
  151 #endif // else for Q_OS_UNIX
  152 
  153     const char* name = 0;
  154     switch (signum) {
  155     case SIGABRT: name = "SIGABRT"; break;
  156     case SIGSEGV: name = "SIGSEGV"; break;
  157     case SIGILL: name = "SIGILL"; break;
  158     case SIGFPE: name = "SIGFPE"; break;
  159 #ifndef Q_OS_WIN
  160     case SIGBUS: name = "SIGBUS"; break;
  161     case SIGSYS: name = "SIGSYS"; break;
  162 #endif // !Q_OS_WIN
  163     }
  164 
  165     Qt::HANDLE thread = QThread::currentThreadId();
  166     if (thread == ReportingThreadId) return;
  167 
  168     QMutexLocker lock(&ReportingMutex);
  169     ReportingThreadId = thread;
  170 
  171     if (!ReportingHeaderWritten) {
  172         ReportingHeaderWritten = true;
  173         auto dec2hex = [](int value) -> char {
  174             if (value >= 0 && value < 10) {
  175                 return '0' + value;
  176             } else if (value >= 10 && value < 16) {
  177                 return 'a' + (value - 10);
  178             }
  179             return '#';
  180         };
  181 
  182         for (const auto &i : ProcessAnnotationRefs) {
  183             QByteArray utf8 = i.second->toUtf8();
  184             std::string wrapped;
  185             wrapped.reserve(4 * utf8.size());
  186             for (auto ch : utf8) {
  187                 auto uch = static_cast<uchar>(ch);
  188                 wrapped.append("\\x", 2).append(1, dec2hex(uch >> 4)).append(1, dec2hex(uch & 0x0F));
  189             }
  190             ProcessAnnotations[i.first] = wrapped;
  191         }
  192 
  193         for (const auto &i : ProcessAnnotations) {
  194             dump() << i.first.c_str() << ": " << i.second.c_str() << "\n";
  195         }
  196         psWriteDump();
  197         dump() << "\n";
  198     }
  199     if (name) {
  200         dump() << "Caught signal " << signum << " (" << name << ") in thread " << uint64(thread) << "\n";
  201     } else if (signum == -1) {
  202         dump() << "Google Breakpad caught a crash, minidump written in thread " << uint64(thread) << "\n";
  203         if (BreakpadDumpPath) {
  204             dump() << "Minidump: " << BreakpadDumpPath << "\n";
  205         } else if (BreakpadDumpPathW) {
  206             dump() << "Minidump: " << BreakpadDumpPathW << "\n";
  207         }
  208     } else {
  209         dump() << "Caught signal " << signum << " in thread " << uint64(thread) << "\n";
  210     }
  211 
  212     // see https://github.com/benbjohnson/bandicoot
  213 #ifdef Q_OS_UNIX
  214     ucontext_t *uc = (ucontext_t*)ucontext;
  215 
  216     void *caller = 0;
  217     if (uc) {
  218 #if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
  219         /* OSX < 10.6 */
  220 #if defined(__x86_64__)
  221         caller = (void*)uc->uc_mcontext->__ss.__rip;
  222 #elif defined(__i386__)
  223         caller = (void*)uc->uc_mcontext->__ss.__eip;
  224 #else
  225         caller = (void*)uc->uc_mcontext->__ss.__srr0;
  226 #endif
  227 #elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
  228         /* OSX >= 10.6 */
  229 #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
  230         caller = (void*)uc->uc_mcontext->__ss.__rip;
  231 #else
  232         caller = (void*)uc->uc_mcontext->__ss.__eip;
  233 #endif
  234 #elif defined(__linux__)
  235         /* Linux */
  236 #if defined(__i386__)
  237         caller = (void*)uc->uc_mcontext.gregs[14]; /* Linux 32 */
  238 #elif defined(__X86_64__) || defined(__x86_64__)
  239         caller = (void*)uc->uc_mcontext.gregs[16]; /* Linux 64 */
  240 #elif defined(__ia64__) /* Linux IA64 */
  241         caller = (void*)uc->uc_mcontext.sc_ip;
  242 #endif
  243 
  244 #endif
  245     }
  246 
  247     void *addresses[132] = { 0 };
  248     size_t size = backtrace(addresses, 128);
  249 
  250     /* overwrite sigaction with caller's address */
  251     if (caller) {
  252         for (int i = size; i > 1; --i) {
  253             addresses[i + 3] = addresses[i];
  254         }
  255         addresses[2] = (void*)0x1;
  256         addresses[3] = caller;
  257         addresses[4] = (void*)0x1;
  258     }
  259 
  260 #ifdef Q_OS_MAC
  261     dump() << "\nBase image addresses:\n";
  262     for (size_t i = 0; i < size; ++i) {
  263         Dl_info info;
  264         dump() << i << " ";
  265         if (dladdr(addresses[i], &info)) {
  266             dump() << uint64(info.dli_fbase) << " (" << info.dli_fname << ")\n";
  267         } else {
  268             dump() << "_unknown_module_\n";
  269         }
  270     }
  271 #endif // Q_OS_MAC
  272 
  273     dump() << "\nBacktrace:\n";
  274 
  275     backtrace_symbols_fd(addresses, size, ReportFileNo);
  276 
  277 #else // Q_OS_UNIX
  278     dump() << "\nBacktrace omitted.\n";
  279 #endif // else for Q_OS_UNIX
  280 
  281     dump() << "\n";
  282 
  283     ReportingThreadId = nullptr;
  284 }
  285 
  286 bool SetSignalHandlers = true;
  287 bool CrashLogged = false;
  288 #if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
  289 google_breakpad::ExceptionHandler* BreakpadExceptionHandler = 0;
  290 
  291 #ifdef Q_OS_WIN
  292 bool DumpCallback(const wchar_t* _dump_dir, const wchar_t* _minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool success)
  293 #elif defined Q_OS_MAC // Q_OS_WIN
  294 bool DumpCallback(const char* _dump_dir, const char* _minidump_id, void *context, bool success)
  295 #elif defined Q_OS_UNIX // Q_OS_MAC
  296 bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success)
  297 #endif // Q_OS_UNIX
  298 {
  299     if (CrashLogged) return success;
  300     CrashLogged = true;
  301 
  302 #ifdef Q_OS_WIN
  303     BreakpadDumpPathW = _minidump_id;
  304     SignalHandler(-1);
  305 #else // Q_OS_WIN
  306 
  307 #ifdef Q_OS_MAC
  308     BreakpadDumpPath = _minidump_id;
  309 #else // Q_OS_MAC
  310     BreakpadDumpPath = md.path();
  311 #endif // else for Q_OS_MAC
  312     SignalHandler(-1, 0, 0);
  313 #endif // else for Q_OS_WIN
  314     return success;
  315 }
  316 #endif // !Q_OS_MAC || MAC_USE_BREAKPAD
  317 
  318 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  319 
  320 } // namespace
  321 
  322 QString PlatformString() {
  323     if (Platform::IsWindowsStoreBuild()) {
  324         return Platform::IsWindows64Bit()
  325             ? qsl("WinStore64Bit")
  326             : qsl("WinStore32Bit");
  327     } else if (Platform::IsWindows32Bit()) {
  328         return qsl("Windows32Bit");
  329     } else if (Platform::IsWindows64Bit()) {
  330         return qsl("Windows64Bit");
  331     } else if (Platform::IsMacStoreBuild()) {
  332         return qsl("MacAppStore");
  333     } else if (Platform::IsOSXBuild()) {
  334         return qsl("OSX");
  335     } else if (Platform::IsMac()) {
  336         return qsl("MacOS");
  337     } else if (Platform::IsLinux32Bit()) {
  338         return qsl("Linux32Bit");
  339     } else if (Platform::IsLinux64Bit()) {
  340         return qsl("Linux64bit");
  341     }
  342     Unexpected("Platform in CrashReports::PlatformString.");
  343 }
  344 
  345 void StartCatching(not_null<Core::Launcher*> launcher) {
  346 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  347     ProcessAnnotations["Binary"] = cExeName().toUtf8().constData();
  348     ProcessAnnotations["ApiId"] = QString::number(ApiId).toUtf8().constData();
  349     ProcessAnnotations["Version"] = (cAlphaVersion() ? qsl("%1 alpha").arg(cAlphaVersion()) : (AppBetaVersion ? qsl("%1 beta") : qsl("%1")).arg(AppVersion)).toUtf8().constData();
  350     ProcessAnnotations["Launched"] = QDateTime::currentDateTime().toString("dd.MM.yyyy hh:mm:ss").toUtf8().constData();
  351     ProcessAnnotations["Platform"] = PlatformString().toUtf8().constData();
  352     ProcessAnnotations["UserTag"] = QString::number(launcher->installationTag(), 16).toUtf8().constData();
  353 
  354     QString dumpspath = cWorkingDir() + qsl("tdata/dumps");
  355     QDir().mkpath(dumpspath);
  356 
  357 #ifdef Q_OS_WIN
  358     BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
  359         dumpspath.toStdWString(),
  360         google_breakpad::ExceptionHandler::FilterCallback(nullptr),
  361         DumpCallback,
  362         (void*)nullptr, // callback_context
  363         google_breakpad::ExceptionHandler::HANDLER_ALL,
  364         MINIDUMP_TYPE(MiniDumpNormal),
  365         // MINIDUMP_TYPE(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules | MiniDumpWithFullAuxiliaryState | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation),
  366         (const wchar_t*)nullptr, // pipe_name
  367         (const google_breakpad::CustomClientInfo*)nullptr
  368     );
  369 #elif defined Q_OS_MAC // Q_OS_WIN
  370 
  371 #ifdef MAC_USE_BREAKPAD
  372 #ifndef _DEBUG
  373     BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
  374         QFile::encodeName(dumpspath).toStdString(),
  375         /*FilterCallback*/ 0,
  376         DumpCallback,
  377         /*context*/ 0,
  378         true,
  379         0
  380     );
  381 #endif // !_DEBUG
  382     SetSignalHandlers = false;
  383 #else // MAC_USE_BREAKPAD
  384     crashpad::CrashpadClient crashpad_client;
  385     std::string handler = (cExeDir() + cExeName() + qsl("/Contents/Helpers/crashpad_handler")).toUtf8().constData();
  386     std::string database = QFile::encodeName(dumpspath).constData();
  387     if (crashpad_client.StartHandler(
  388             base::FilePath(handler),
  389             base::FilePath(database),
  390             std::string(),
  391             ProcessAnnotations,
  392             std::vector<std::string>(),
  393             false)) {
  394         crashpad_client.UseHandler();
  395     }
  396 #endif // else for MAC_USE_BREAKPAD
  397 #elif defined Q_OS_UNIX
  398     BreakpadExceptionHandler = new google_breakpad::ExceptionHandler(
  399         google_breakpad::MinidumpDescriptor(QFile::encodeName(dumpspath).toStdString()),
  400         /*FilterCallback*/ 0,
  401         DumpCallback,
  402         /*context*/ 0,
  403         true,
  404         -1
  405     );
  406 #endif // Q_OS_UNIX
  407 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  408 }
  409 
  410 void FinishCatching() {
  411 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  412 #if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
  413 
  414     delete base::take(BreakpadExceptionHandler);
  415 
  416 #endif // !Q_OS_MAC || MAC_USE_BREAKPAD
  417 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  418 }
  419 
  420 StartResult Start() {
  421 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  422     ReportPath = cWorkingDir() + qsl("tdata/working");
  423 
  424 #ifdef Q_OS_WIN
  425     FILE *f = nullptr;
  426     if (_wfopen_s(&f, ReportPath.toStdWString().c_str(), L"rb") != 0) {
  427         f = nullptr;
  428     } else {
  429 #else // !Q_OS_WIN
  430     if (FILE *f = fopen(QFile::encodeName(ReportPath).constData(), "rb")) {
  431 #endif // else for !Q_OS_WIN
  432         QByteArray lastdump;
  433         char buffer[256 * 1024] = { 0 };
  434         int32 read = fread(buffer, 1, 256 * 1024, f);
  435         if (read > 0) {
  436             lastdump.append(buffer, read);
  437         }
  438         fclose(f);
  439 
  440         LOG(("Opened '%1' for reading, the previous "
  441             "Telegram Desktop launch was not finished properly :( "
  442             "Crash log size: %2").arg(ReportPath).arg(lastdump.size()));
  443 
  444         return lastdump;
  445     }
  446 
  447 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  448     return Restart();
  449 }
  450 
  451 Status Restart() {
  452 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  453     if (ReportFile) {
  454         return Started;
  455     }
  456 
  457 #ifdef Q_OS_WIN
  458     if (_wfopen_s(&ReportFile, ReportPath.toStdWString().c_str(), L"wb") != 0) {
  459         ReportFile = nullptr;
  460     }
  461 #else // Q_OS_WIN
  462     ReportFile = fopen(QFile::encodeName(ReportPath).constData(), "wb");
  463 #endif // else for Q_OS_WIN
  464     if (ReportFile) {
  465 #ifdef Q_OS_WIN
  466         ReportFileNo = _fileno(ReportFile);
  467 #else // Q_OS_WIN
  468         ReportFileNo = fileno(ReportFile);
  469 #endif // else for Q_OS_WIN
  470         if (SetSignalHandlers) {
  471 #ifndef Q_OS_WIN
  472             struct sigaction sigact;
  473 
  474             sigact.sa_sigaction = SignalHandler;
  475             sigemptyset(&sigact.sa_mask);
  476             sigact.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO;
  477 
  478             sigaction(SIGABRT, &sigact, &SIG_def[SIGABRT]);
  479             sigaction(SIGSEGV, &sigact, &SIG_def[SIGSEGV]);
  480             sigaction(SIGILL, &sigact, &SIG_def[SIGILL]);
  481             sigaction(SIGFPE, &sigact, &SIG_def[SIGFPE]);
  482             sigaction(SIGBUS, &sigact, &SIG_def[SIGBUS]);
  483             sigaction(SIGSYS, &sigact, &SIG_def[SIGSYS]);
  484 #else // !Q_OS_WIN
  485             signal(SIGABRT, SignalHandler);
  486             signal(SIGSEGV, SignalHandler);
  487             signal(SIGILL, SignalHandler);
  488             signal(SIGFPE, SignalHandler);
  489 #endif // else for !Q_OS_WIN
  490         }
  491 
  492         InstallOperatorNewHandler();
  493         InstallQtMessageHandler();
  494 
  495         return Started;
  496     }
  497 
  498     LOG(("FATAL: Could not open '%1' for writing!").arg(ReportPath));
  499 
  500     return CantOpen;
  501 #else // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  502     return Started;
  503 #endif // else for !DESKTOP_APP_DISABLE_CRASH_REPORTS
  504 }
  505 
  506 void Finish() {
  507 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  508     FinishCatching();
  509 
  510     if (ReportFile) {
  511         fclose(ReportFile);
  512         ReportFile = nullptr;
  513 
  514 #ifdef Q_OS_WIN
  515         _wunlink(ReportPath.toStdWString().c_str());
  516 #else // Q_OS_WIN
  517         unlink(ReportPath.toUtf8().constData());
  518 #endif // else for Q_OS_WIN
  519     }
  520 #endif // !DESKTOP_APP_DISABLE_CRASH_REPORTS
  521 }
  522 
  523 void SetAnnotation(const std::string &key, const QString &value) {
  524     static QMutex mutex;
  525     QMutexLocker lock(&mutex);
  526 
  527     if (!value.trimmed().isEmpty()) {
  528         ProcessAnnotations[key] = value.toUtf8().constData();
  529     } else {
  530         ProcessAnnotations.erase(key);
  531     }
  532 }
  533 
  534 void SetAnnotationHex(const std::string &key, const QString &value) {
  535     if (value.isEmpty()) {
  536         return SetAnnotation(key, value);
  537     }
  538     const auto utf = value.toUtf8();
  539     auto buffer = std::string();
  540     buffer.reserve(4 * utf.size());
  541     const auto hexDigit = [](std::uint8_t value) {
  542         if (value >= 10) {
  543             return 'A' + (value - 10);
  544         }
  545         return '0' + value;
  546     };
  547     const auto appendHex = [&](std::uint8_t value) {
  548         buffer.push_back('\\');
  549         buffer.push_back('x');
  550         buffer.push_back(hexDigit(value / 16));
  551         buffer.push_back(hexDigit(value % 16));
  552     };
  553     for (const auto ch : utf) {
  554         appendHex(ch);
  555     }
  556     ProcessAnnotations[key] = std::move(buffer);
  557 }
  558 
  559 void SetAnnotationRef(const std::string &key, const QString *valuePtr) {
  560     static QMutex mutex;
  561     QMutexLocker lock(&mutex);
  562 
  563     if (valuePtr) {
  564         ProcessAnnotationRefs[key] = valuePtr;
  565     } else {
  566         ProcessAnnotationRefs.erase(key);
  567     }
  568 }
  569 
  570 #ifndef DESKTOP_APP_DISABLE_CRASH_REPORTS
  571 
  572 dump::~dump() {
  573     if (ReportFile) {
  574         fflush(ReportFile);
  575     }
  576 }
  577 
  578 const dump &operator<<(const dump &stream, const char *str) {
  579     if (!ReportFile) return stream;
  580 
  581     fwrite(str, 1, strlen(str), ReportFile);
  582     return stream;
  583 }
  584 
  585 const dump &operator<<(const dump &stream, const wchar_t *str) {
  586     if (!ReportFile) return stream;
  587 
  588     for (int i = 0, l = wcslen(str); i < l; ++i) {
  589         if (
  590 #if !defined(__WCHAR_UNSIGNED__)
  591             str[i] >= 0 &&
  592 #endif
  593             str[i] < 128) {
  594             SafeWriteChar(char(str[i]));
  595         } else {
  596             SafeWriteChar('?');
  597         }
  598     }
  599     return stream;
  600 }
  601 
  602 const dump &operator<<(const dump &stream, int num) {
  603     return SafeWriteNumber(stream, num);
  604 }
  605 
  606 const dump &operator<<(const dump &stream, unsigned int num) {
  607     return SafeWriteNumber(stream, num);
  608 }
  609 
  610 const dump &operator<<(const dump &stream, unsigned long num) {
  611     return SafeWriteNumber(stream, num);
  612 }
  613 
  614 const dump &operator<<(const dump &stream, unsigned long long num) {
  615     return SafeWriteNumber(stream, num);
  616 }
  617 
  618 const dump &operator<<(const dump &stream, double num) {
  619     if (num < 0) {
  620         SafeWriteChar('-');
  621         num = -num;
  622     }
  623     SafeWriteNumber(stream, uint64(floor(num)));
  624     SafeWriteChar('.');
  625     num -= floor(num);
  626     for (int i = 0; i < 4; ++i) {
  627         num *= 10;
  628         int digit = int(floor(num));
  629         SafeWriteChar('0' + digit);
  630         num -= digit;
  631     }
  632     return stream;
  633 }
  634 
  635 #endif // DESKTOP_APP_DISABLE_CRASH_REPORTS
  636 
  637 } // namespace CrashReports