2014-05-30 08:53:19 +00:00
/*
This file is part of Telegram Desktop ,
2014-12-01 10:47:38 +00:00
the official desktop version of Telegram messaging app , see https : //telegram.org
2014-05-30 08:53:19 +00:00
Telegram Desktop is free software : you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
It is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2015-10-03 13:16:42 +00:00
In addition , as a special exception , the copyright holders give permission
to link the code of portions of this program with the OpenSSL library .
2014-05-30 08:53:19 +00:00
Full license : https : //github.com/telegramdesktop/tdesktop/blob/master/LICENSE
2017-01-11 18:31:31 +00:00
Copyright ( c ) 2014 - 2017 John Preston , https : //desktop.telegram.org
2014-05-30 08:53:19 +00:00
*/
2016-03-24 13:27:34 +00:00
# include "logs.h"
2016-02-01 12:09:23 +00:00
# include <signal.h>
2016-09-29 11:37:16 +00:00
# include <new>
2017-11-30 11:41:15 +00:00
# include <mutex>
2016-09-29 11:37:16 +00:00
2017-03-04 10:23:56 +00:00
# include "platform/platform_specific.h"
2017-02-24 17:15:41 +00:00
# include "mtproto/connection.h"
2014-05-30 08:53:19 +00:00
2016-03-20 09:10:16 +00:00
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-01-31 18:01:43 +00:00
// see https://blog.inventic.eu/2012/08/qt-and-google-breakpad/
# ifdef Q_OS_WIN
2016-02-07 15:38:49 +00:00
# pragma warning(push)
# pragma warning(disable:4091)
2016-01-31 18:01:43 +00:00
# include "client/windows/handler/exception_handler.h"
2016-02-07 15:38:49 +00:00
# pragma warning(pop)
2016-03-20 09:10:16 +00:00
# elif defined Q_OS_MAC // Q_OS_WIN
2016-02-02 11:48:46 +00:00
2016-03-24 13:27:34 +00:00
# include <unistd.h>
2016-02-02 11:48:46 +00:00
# ifdef MAC_USE_BREAKPAD
2016-01-31 18:01:43 +00:00
# include "client/mac/handler/exception_handler.h"
2016-03-20 09:10:16 +00:00
# else // MAC_USE_BREAKPAD
2016-02-02 11:48:46 +00:00
# include "client/crashpad_client.h"
2016-03-20 09:10:16 +00:00
# endif // else for MAC_USE_BREAKPAD
2016-02-02 11:48:46 +00:00
2016-03-20 09:10:16 +00:00
# elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32 // Q_OS_MAC
2016-01-31 18:01:43 +00:00
# include "client/linux/handler/exception_handler.h"
2016-03-20 09:10:16 +00:00
# endif // Q_OS_LINUX64 || Q_OS_LINUX32
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-01-31 18:01:43 +00:00
2016-01-11 15:43:29 +00:00
enum LogDataType {
LogDataMain ,
LogDataDebug ,
LogDataTcp ,
LogDataMtp ,
LogDataCount
} ;
2016-01-17 05:01:14 +00:00
QMutex * _logsMutex ( LogDataType type , bool clear = false ) {
static QMutex * LogsMutexes = 0 ;
if ( clear ) {
delete [ ] LogsMutexes ;
LogsMutexes = 0 ;
} else if ( ! LogsMutexes ) {
2016-01-11 15:43:29 +00:00
LogsMutexes = new QMutex [ LogDataCount ] ;
}
return & LogsMutexes [ type ] ;
}
2016-01-17 05:01:14 +00:00
2016-01-11 15:43:29 +00:00
QString _logsFilePath ( LogDataType type , const QString & postfix = QString ( ) ) {
QString path ( cWorkingDir ( ) ) ;
switch ( type ) {
2016-01-17 05:01:14 +00:00
case LogDataMain : path + = qstr ( " log " ) + postfix + qstr ( " .txt " ) ; break ;
2016-01-11 15:43:29 +00:00
case LogDataDebug : path + = qstr ( " DebugLogs/log " ) + postfix + qstr ( " .txt " ) ; break ;
case LogDataTcp : path + = qstr ( " DebugLogs/tcp " ) + postfix + qstr ( " .txt " ) ; break ;
case LogDataMtp : path + = qstr ( " DebugLogs/mtp " ) + postfix + qstr ( " .txt " ) ; break ;
}
return path ;
}
2014-05-30 08:53:19 +00:00
2016-01-17 05:01:14 +00:00
int32 LogsStartIndexChosen = - 1 ;
QString _logsEntryStart ( ) {
static int32 index = 0 ;
QDateTime tm ( QDateTime : : currentDateTime ( ) ) ;
2017-02-24 17:15:41 +00:00
auto thread = qobject_cast < MTP : : internal : : Thread * > ( QThread : : currentThread ( ) ) ;
auto threadId = thread ? thread - > getThreadIndex ( ) : 0 ;
2016-01-17 05:01:14 +00:00
return QString ( " [%1 %2-%3] " ) . arg ( tm . toString ( " hh:mm:ss.zzz " ) ) . arg ( QString ( " %1 " ) . arg ( threadId , 2 , 10 , QChar ( ' 0 ' ) ) ) . arg ( + + index , 7 , 10 , QChar ( ' 0 ' ) ) ;
}
2016-01-11 15:43:29 +00:00
class LogsDataFields {
public :
2016-01-10 06:05:23 +00:00
2016-01-17 05:01:14 +00:00
LogsDataFields ( ) {
for ( int32 i = 0 ; i < LogDataCount ; + + i ) {
files [ i ] . reset ( new QFile ( ) ) ;
}
2016-01-11 15:43:29 +00:00
}
bool openMain ( ) {
2016-01-17 05:01:14 +00:00
return reopen ( LogDataMain , 0 , qsl ( " start " ) ) ;
}
2016-02-08 10:50:56 +00:00
void closeMain ( ) {
QMutexLocker lock ( _logsMutex ( LogDataMain ) ) ;
if ( files [ LogDataMain ] ) {
streams [ LogDataMain ] . setDevice ( 0 ) ;
files [ LogDataMain ] - > close ( ) ;
}
}
2016-01-17 05:01:14 +00:00
bool instanceChecked ( ) {
2016-01-11 15:43:29 +00:00
return reopen ( LogDataMain , 0 , QString ( ) ) ;
2014-05-30 08:53:19 +00:00
}
2016-01-17 05:01:14 +00:00
QString full ( ) {
if ( ! streams [ LogDataMain ] . device ( ) ) {
return QString ( ) ;
}
QFile out ( files [ LogDataMain ] - > fileName ( ) ) ;
if ( out . open ( QIODevice : : ReadOnly ) ) {
return QString : : fromUtf8 ( out . readAll ( ) ) ;
}
return QString ( ) ;
}
2016-01-11 15:43:29 +00:00
void write ( LogDataType type , const QString & msg ) {
QMutexLocker lock ( _logsMutex ( type ) ) ;
if ( type ! = LogDataMain ) reopenDebug ( ) ;
if ( ! streams [ type ] . device ( ) ) return ;
2014-05-30 08:53:19 +00:00
2016-01-11 15:43:29 +00:00
streams [ type ] < < msg ;
streams [ type ] . flush ( ) ;
2014-05-30 08:53:19 +00:00
}
2016-01-11 15:43:29 +00:00
private :
2016-01-17 05:01:14 +00:00
QSharedPointer < QFile > files [ LogDataCount ] ;
2016-01-11 15:43:29 +00:00
QTextStream streams [ LogDataCount ] ;
2016-01-30 18:24:18 +00:00
int32 part = - 1 ;
2016-01-11 15:43:29 +00:00
bool reopen ( LogDataType type , int32 dayIndex , const QString & postfix ) {
if ( streams [ type ] . device ( ) ) {
if ( type = = LogDataMain ) {
2016-01-17 05:01:14 +00:00
if ( ! postfix . isEmpty ( ) ) {
return true ;
}
} else {
streams [ type ] . setDevice ( 0 ) ;
files [ type ] - > close ( ) ;
2016-01-11 15:43:29 +00:00
}
}
2017-08-31 16:28:58 +00:00
auto mode = QIODevice : : WriteOnly | QIODevice : : Text ;
2016-01-17 05:01:14 +00:00
if ( type = = LogDataMain ) { // we can call LOG() in LogDataMain reopen - mutex not locked
if ( postfix . isEmpty ( ) ) { // instance checked, need to move to log.txt
2017-08-17 09:06:26 +00:00
Assert ( ! files [ type ] - > fileName ( ) . isEmpty ( ) ) ; // one of log_startXX.txt should've been opened already
2016-01-17 05:01:14 +00:00
QSharedPointer < QFile > to ( new QFile ( _logsFilePath ( type , postfix ) ) ) ;
if ( to - > exists ( ) & & ! to - > remove ( ) ) {
LOG ( ( " Could not delete '%1' file to start new logging! " ) . arg ( to - > fileName ( ) ) ) ;
return false ;
}
if ( ! QFile ( files [ type ] - > fileName ( ) ) . copy ( to - > fileName ( ) ) ) { // don't close files[type] yet
LOG ( ( " Could not copy '%1' to '%2' to start new logging! " ) . arg ( files [ type ] - > fileName ( ) ) . arg ( to - > fileName ( ) ) ) ;
return false ;
}
if ( to - > open ( mode | QIODevice : : Append ) ) {
qSwap ( files [ type ] , to ) ;
streams [ type ] . setDevice ( files [ type ] . data ( ) ) ;
streams [ type ] . setCodec ( " UTF-8 " ) ;
LOG ( ( " Moved logging from '%1' to '%2'! " ) . arg ( to - > fileName ( ) ) . arg ( files [ type ] - > fileName ( ) ) ) ;
to - > remove ( ) ;
LogsStartIndexChosen = - 1 ;
QDir working ( cWorkingDir ( ) ) ; // delete all other log_startXX.txt that we can
QStringList oldlogs = working . entryList ( QStringList ( " log_start*.txt " ) , QDir : : Files ) ;
for ( QStringList : : const_iterator i = oldlogs . cbegin ( ) , e = oldlogs . cend ( ) ; i ! = e ; + + i ) {
QString oldlog = cWorkingDir ( ) + * i , oldlogend = i - > mid ( qstr ( " log_start " ) . size ( ) ) ;
if ( oldlogend . size ( ) = = 1 + qstr ( " .txt " ) . size ( ) & & oldlogend . at ( 0 ) . isDigit ( ) & & oldlogend . midRef ( 1 ) = = qstr ( " .txt " ) ) {
bool removed = QFile ( * i ) . remove ( ) ;
LOG ( ( " Old start log '%1' found, deleted: %2 " ) . arg ( * i ) . arg ( Logs : : b ( removed ) ) ) ;
}
}
return true ;
}
LOG ( ( " Could not open '%1' file to start new logging! " ) . arg ( to - > fileName ( ) ) ) ;
return false ;
} else {
bool found = false ;
int32 oldest = - 1 ; // find not existing log_startX.txt or pick the oldest one (by lastModified)
QDateTime oldestLastModified ;
for ( int32 i = 0 ; i < 10 ; + + i ) {
QString trying = _logsFilePath ( type , qsl ( " _start%1 " ) . arg ( i ) ) ;
files [ type ] - > setFileName ( trying ) ;
if ( ! files [ type ] - > exists ( ) ) {
LogsStartIndexChosen = i ;
found = true ;
break ;
}
QDateTime lastModified = QFileInfo ( trying ) . lastModified ( ) ;
if ( oldest < 0 | | lastModified < oldestLastModified ) {
oldestLastModified = lastModified ;
oldest = i ;
}
}
if ( ! found ) {
files [ type ] - > setFileName ( _logsFilePath ( type , qsl ( " _start%1 " ) . arg ( oldest ) ) ) ;
LogsStartIndexChosen = oldest ;
}
}
} else {
files [ type ] - > setFileName ( _logsFilePath ( type , postfix ) ) ;
if ( files [ type ] - > exists ( ) ) {
if ( files [ type ] - > open ( QIODevice : : ReadOnly | QIODevice : : Text ) ) {
if ( QString : : fromUtf8 ( files [ type ] - > readLine ( ) ) . toInt ( ) = = dayIndex ) {
2016-01-11 15:43:29 +00:00
mode | = QIODevice : : Append ;
}
2016-01-17 05:01:14 +00:00
files [ type ] - > close ( ) ;
2016-01-11 15:43:29 +00:00
}
} else {
QDir ( ) . mkdir ( cWorkingDir ( ) + qstr ( " DebugLogs " ) ) ;
}
}
2016-01-17 05:01:14 +00:00
if ( files [ type ] - > open ( mode ) ) {
streams [ type ] . setDevice ( files [ type ] . data ( ) ) ;
2016-01-11 15:43:29 +00:00
streams [ type ] . setCodec ( " UTF-8 " ) ;
if ( type ! = LogDataMain ) {
streams [ type ] < < ( ( mode & QIODevice : : Append ) ? qsl ( " ---------------------------------------------------------------- \n NEW LOGGING INSTANCE STARTED!!! \n ---------------------------------------------------------------- \n " ) : qsl ( " %1 \n " ) . arg ( dayIndex ) ) ;
streams [ type ] . flush ( ) ;
}
return true ;
2016-01-17 05:01:14 +00:00
} else if ( type ! = LogDataMain ) {
LOG ( ( " Could not open debug log '%1'! " ) . arg ( files [ type ] - > fileName ( ) ) ) ;
2016-01-11 15:43:29 +00:00
}
return false ;
2014-05-30 08:53:19 +00:00
}
2016-01-11 15:43:29 +00:00
void reopenDebug ( ) {
time_t t = time ( NULL ) ;
struct tm tm ;
mylocaltime ( & tm , & t ) ;
static const int switchEach = 15 ; // minutes
int32 newPart = ( tm . tm_min + tm . tm_hour * 60 ) / switchEach ;
if ( newPart = = part ) return ;
part = newPart ;
int32 dayIndex = ( tm . tm_year + 1900 ) * 10000 + ( tm . tm_mon + 1 ) * 100 + tm . tm_mday ;
QString postfix = QString ( " _%4_%5 " ) . arg ( ( part * switchEach ) / 60 , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( ( part * switchEach ) % 60 , 2 , 10 , QChar ( ' 0 ' ) ) ;
reopen ( LogDataDebug , dayIndex , postfix ) ;
reopen ( LogDataTcp , dayIndex , postfix ) ;
reopen ( LogDataMtp , dayIndex , postfix ) ;
2014-05-30 08:53:19 +00:00
}
2016-01-11 15:43:29 +00:00
} ;
2014-09-30 14:11:09 +00:00
2016-01-11 15:43:29 +00:00
LogsDataFields * LogsData = 0 ;
2014-09-30 14:11:09 +00:00
2016-01-11 15:43:29 +00:00
typedef QList < QPair < LogDataType , QString > > LogsInMemoryList ;
LogsInMemoryList * LogsInMemory = 0 ;
LogsInMemoryList * DeletedLogsInMemory = SharedMemoryLocation < LogsInMemoryList , 0 > ( ) ;
2016-01-17 05:01:14 +00:00
QString LogsBeforeSingleInstanceChecked ; // LogsInMemory already dumped in LogsData, but LogsData is about to be deleted
2016-01-11 15:43:29 +00:00
void _logsWrite ( LogDataType type , const QString & msg ) {
2016-01-17 05:01:14 +00:00
if ( LogsData & & ( type = = LogDataMain | | LogsStartIndexChosen < 0 ) ) {
2016-01-11 15:43:29 +00:00
if ( type = = LogDataMain | | cDebug ( ) ) {
LogsData - > write ( type , msg ) ;
}
} else if ( LogsInMemory ! = DeletedLogsInMemory ) {
if ( ! LogsInMemory ) {
LogsInMemory = new LogsInMemoryList ;
}
2016-01-17 05:01:14 +00:00
LogsInMemory - > push_back ( qMakePair ( type , msg ) ) ;
} else if ( ! LogsBeforeSingleInstanceChecked . isEmpty ( ) & & type = = LogDataMain ) {
LogsBeforeSingleInstanceChecked + = msg ;
2014-05-30 08:53:19 +00:00
}
}
2016-01-11 15:43:29 +00:00
void _moveOldDataFiles ( const QString & from ) ;
2016-01-31 18:01:43 +00:00
namespace SignalHandlers {
2016-03-20 09:10:16 +00:00
void StartCrashHandler ( ) ;
void FinishCrashHandler ( ) ;
2016-01-31 18:01:43 +00:00
}
2016-01-11 15:43:29 +00:00
namespace Logs {
2016-02-29 16:53:26 +00:00
void start ( ) {
2017-08-17 09:06:26 +00:00
Assert ( LogsData = = 0 ) ;
2016-01-11 15:43:29 +00:00
2016-02-08 14:54:55 +00:00
if ( ! Sandbox : : CheckBetaVersionDir ( ) ) {
2016-01-11 15:43:29 +00:00
return ;
}
2017-12-06 15:07:01 +00:00
auto initialWorkingDir = QDir ( cWorkingDir ( ) ) . absolutePath ( ) + ' / ' ;
auto moveOldDataFrom = QString ( ) ;
auto workingDirChosen = false ;
2016-01-11 15:43:29 +00:00
if ( cBetaVersion ( ) ) {
cSetDebug ( true ) ;
2017-12-06 15:07:01 +00:00
workingDirChosen = true ;
2016-03-20 09:10:16 +00:00
# if defined Q_OS_MAC || defined Q_OS_LINUX
2016-01-11 15:43:29 +00:00
} else {
# ifdef _DEBUG
cForceWorkingDir ( cExeDir ( ) ) ;
2016-03-20 09:10:16 +00:00
# else // _DEBUG
2017-12-07 13:56:36 +00:00
if ( ! cWorkingDir ( ) . isEmpty ( ) ) {
// This value must come only from the "-workdir" argument.
cForceWorkingDir ( cWorkingDir ( ) ) ;
} else {
cForceWorkingDir ( psAppDataPath ( ) ) ;
}
2017-12-06 15:07:01 +00:00
# endif // !_DEBUG
2016-01-11 15:43:29 +00:00
workingDirChosen = true ;
2014-09-30 14:11:09 +00:00
2016-03-20 09:10:16 +00:00
# if defined Q_OS_LINUX && !defined _DEBUG // fix first version
2016-01-21 06:58:58 +00:00
moveOldDataFrom = initialWorkingDir ;
2016-03-20 09:10:16 +00:00
# endif // Q_OS_LINUX && !_DEBUG
2016-01-11 15:43:29 +00:00
2016-04-26 13:00:23 +00:00
# elif defined Q_OS_WINRT // Q_OS_MAC || Q_OS_LINUX
} else {
cForceWorkingDir ( psAppDataPath ( ) ) ;
workingDirChosen = true ;
2017-12-06 15:07:01 +00:00
# elif defined OS_WIN_STORE // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT
2017-02-10 10:10:33 +00:00
# ifdef _DEBUG
cForceWorkingDir ( cExeDir ( ) ) ;
# else // _DEBUG
2017-02-01 10:12:52 +00:00
cForceWorkingDir ( psAppDataPath ( ) ) ;
2017-12-06 15:07:01 +00:00
# endif // !_DEBUG
workingDirChosen = true ;
2017-12-08 08:13:42 +00:00
# elif defined Q_OS_WIN
} else {
if ( ! cWorkingDir ( ) . isEmpty ( ) ) {
// This value must come only from the "-workdir" argument.
cForceWorkingDir ( cWorkingDir ( ) ) ;
workingDirChosen = true ;
}
2017-12-06 15:07:01 +00:00
# endif // Q_OS_MAC || Q_OS_LINUX || Q_OS_WINRT || OS_WIN_STORE
2016-01-11 15:43:29 +00:00
}
2014-09-30 14:11:09 +00:00
2016-01-11 15:43:29 +00:00
LogsData = new LogsDataFields ( ) ;
if ( ! workingDirChosen ) {
2017-12-06 15:07:01 +00:00
cForceWorkingDir ( cExeDir ( ) ) ;
2016-01-11 15:43:29 +00:00
if ( ! LogsData - > openMain ( ) ) {
2017-12-06 15:07:01 +00:00
cForceWorkingDir ( psAppDataPath ( ) ) ;
2016-01-11 15:43:29 +00:00
}
}
cForceWorkingDir ( QDir ( cWorkingDir ( ) ) . absolutePath ( ) + ' / ' ) ;
2017-12-06 15:07:01 +00:00
2016-04-26 13:00:23 +00:00
// WinRT build requires the working dir to stay the same for plugin loading.
# ifndef Q_OS_WINRT
2016-01-11 15:43:29 +00:00
QDir ( ) . setCurrent ( cWorkingDir ( ) ) ;
2016-04-26 13:00:23 +00:00
# endif // !Q_OS_WINRT
2017-12-06 15:07:01 +00:00
2016-01-21 06:58:58 +00:00
QDir ( ) . mkpath ( cWorkingDir ( ) + qstr ( " tdata " ) ) ;
2016-01-11 15:43:29 +00:00
2016-02-08 14:54:55 +00:00
Sandbox : : WorkingDirReady ( ) ;
2016-03-20 09:10:16 +00:00
SignalHandlers : : StartCrashHandler ( ) ;
2016-01-11 15:43:29 +00:00
2016-01-21 06:58:58 +00:00
if ( ! LogsData - > openMain ( ) ) {
delete LogsData ;
LogsData = 0 ;
}
2016-04-27 12:02:17 +00:00
LOG ( ( " Launched version: %1, alpha: %2, beta: %3, debug mode: %4, test dc: %5 " ) . arg ( AppVersion ) . arg ( Logs : : b ( cAlphaVersion ( ) ) ) . arg ( cBetaVersion ( ) ) . arg ( Logs : : b ( cDebug ( ) ) ) . arg ( Logs : : b ( cTestMode ( ) ) ) ) ;
2016-01-11 15:43:29 +00:00
LOG ( ( " Executable dir: %1, name: %2 " ) . arg ( cExeDir ( ) ) . arg ( cExeName ( ) ) ) ;
2016-01-21 06:58:58 +00:00
LOG ( ( " Initial working dir: %1 " ) . arg ( initialWorkingDir ) ) ;
2016-01-11 15:43:29 +00:00
LOG ( ( " Working dir: %1 " ) . arg ( cWorkingDir ( ) ) ) ;
2017-08-15 17:12:57 +00:00
LOG ( ( " Command line: %1 " ) . arg ( cArguments ( ) ) ) ;
2016-01-11 15:43:29 +00:00
2016-01-21 06:58:58 +00:00
if ( ! LogsData ) {
2016-02-04 12:44:39 +00:00
LOG ( ( " FATAL: Could not open '%1' for writing log! " ) . arg ( _logsFilePath ( LogDataMain , qsl ( " _startXX " ) ) ) ) ;
2016-01-11 15:43:29 +00:00
return ;
}
# ifdef Q_OS_WIN
if ( cWorkingDir ( ) = = psAppDataPath ( ) ) { // fix old "Telegram Win (Unofficial)" version
moveOldDataFrom = psAppDataPathOld ( ) ;
}
# endif
if ( ! moveOldDataFrom . isEmpty ( ) ) {
_moveOldDataFiles ( moveOldDataFrom ) ;
}
if ( LogsInMemory ) {
2017-08-17 09:06:26 +00:00
Assert ( LogsInMemory ! = DeletedLogsInMemory ) ;
2016-01-11 15:43:29 +00:00
LogsInMemoryList list = * LogsInMemory ;
for ( LogsInMemoryList : : const_iterator i = list . cbegin ( ) , e = list . cend ( ) ; i ! = e ; + + i ) {
2016-01-17 05:01:14 +00:00
if ( i - > first = = LogDataMain ) {
_logsWrite ( i - > first , i - > second ) ;
}
2016-01-11 15:43:29 +00:00
}
}
2016-01-21 06:58:58 +00:00
LOG ( ( " Logs started " ) ) ;
2014-05-30 08:53:19 +00:00
}
2016-02-29 16:53:26 +00:00
void finish ( ) {
2016-01-11 15:43:29 +00:00
delete LogsData ;
LogsData = 0 ;
2016-01-17 05:01:14 +00:00
if ( LogsInMemory & & LogsInMemory ! = DeletedLogsInMemory ) {
delete LogsInMemory ;
}
LogsInMemory = DeletedLogsInMemory ;
_logsMutex ( LogDataMain , true ) ;
2016-01-31 18:01:43 +00:00
2016-03-20 09:10:16 +00:00
SignalHandlers : : FinishCrashHandler ( ) ;
2016-01-11 15:43:29 +00:00
}
bool started ( ) {
return LogsData ! = 0 ;
}
2014-05-30 08:53:19 +00:00
2016-01-17 05:01:14 +00:00
bool instanceChecked ( ) {
if ( ! LogsData ) return false ;
if ( ! LogsData - > instanceChecked ( ) ) {
LogsBeforeSingleInstanceChecked = Logs : : full ( ) ;
delete LogsData ;
LogsData = 0 ;
2016-02-04 12:44:39 +00:00
LOG ( ( " FATAL: Could not move logging to '%1'! " ) . arg ( _logsFilePath ( LogDataMain ) ) ) ;
2016-01-17 05:01:14 +00:00
return false ;
}
if ( LogsInMemory ) {
2017-08-17 09:06:26 +00:00
Assert ( LogsInMemory ! = DeletedLogsInMemory ) ;
2016-01-17 05:01:14 +00:00
LogsInMemoryList list = * LogsInMemory ;
for ( LogsInMemoryList : : const_iterator i = list . cbegin ( ) , e = list . cend ( ) ; i ! = e ; + + i ) {
if ( i - > first ! = LogDataMain ) {
_logsWrite ( i - > first , i - > second ) ;
}
}
}
if ( LogsInMemory ) {
2017-08-17 09:06:26 +00:00
Assert ( LogsInMemory ! = DeletedLogsInMemory ) ;
2016-01-17 05:01:14 +00:00
delete LogsInMemory ;
}
LogsInMemory = DeletedLogsInMemory ;
DEBUG_LOG ( ( " Debug logs started. " ) ) ;
LogsBeforeSingleInstanceChecked . clear ( ) ;
return true ;
}
void multipleInstances ( ) {
if ( LogsInMemory ) {
2017-08-17 09:06:26 +00:00
Assert ( LogsInMemory ! = DeletedLogsInMemory ) ;
2016-01-17 05:01:14 +00:00
delete LogsInMemory ;
}
LogsInMemory = DeletedLogsInMemory ;
if ( cDebug ( ) ) {
LOG ( ( " WARNING: debug logs are not written in multiple instances mode! " ) ) ;
}
LogsBeforeSingleInstanceChecked . clear ( ) ;
}
2016-02-08 10:50:56 +00:00
void closeMain ( ) {
LOG ( ( " Explicitly closing main log and finishing crash handlers. " ) ) ;
if ( LogsData ) {
LogsData - > closeMain ( ) ;
}
}
2016-01-11 15:43:29 +00:00
void writeMain ( const QString & v ) {
time_t t = time ( NULL ) ;
struct tm tm ;
mylocaltime ( & tm , & t ) ;
2014-09-30 14:11:09 +00:00
2016-01-11 15:43:29 +00:00
QString msg ( QString ( " [%1.%2.%3 %4:%5:%6] %7 \n " ) . arg ( tm . tm_year + 1900 ) . arg ( tm . tm_mon + 1 , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( tm . tm_mday , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( tm . tm_hour , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( tm . tm_min , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( tm . tm_sec , 2 , 10 , QChar ( ' 0 ' ) ) . arg ( v ) ) ;
_logsWrite ( LogDataMain , msg ) ;
2014-09-30 14:11:09 +00:00
2016-01-17 05:01:14 +00:00
QString debugmsg ( QString ( " %1 %2 \n " ) . arg ( _logsEntryStart ( ) ) . arg ( v ) ) ;
2016-01-11 15:43:29 +00:00
_logsWrite ( LogDataDebug , debugmsg ) ;
}
void writeDebug ( const char * file , int32 line , const QString & v ) {
const char * last = strstr ( file , " / " ) , * found = 0 ;
while ( last ) {
found = last ;
last = strstr ( last + 1 , " / " ) ;
}
last = strstr ( file , " \\ " ) ;
while ( last ) {
found = last ;
last = strstr ( last + 1 , " \\ " ) ;
}
if ( found ) {
file = found + 1 ;
}
2016-01-17 05:01:14 +00:00
QString msg ( QString ( " %1 %2 (%3 : %4) \ n " ).arg(_logsEntryStart()).arg(v).arg(file).arg(line)) ;
2016-01-11 15:43:29 +00:00
_logsWrite ( LogDataDebug , msg ) ;
# ifdef Q_OS_WIN
//OutputDebugString(reinterpret_cast<const wchar_t *>(msg.utf16()));
# elif defined Q_OS_MAC
//objc_outputDebugString(msg);
# elif defined Q_OS_LINUX && defined _DEBUG
//std::cout << msg.toUtf8().constData();
# endif
2014-05-30 08:53:19 +00:00
}
2016-01-11 15:43:29 +00:00
void writeTcp ( const QString & v ) {
2016-01-17 05:01:14 +00:00
QString msg ( QString ( " %1 %2 \n " ) . arg ( _logsEntryStart ( ) ) . arg ( v ) ) ;
2016-01-11 15:43:29 +00:00
_logsWrite ( LogDataTcp , msg ) ;
}
2014-05-30 08:53:19 +00:00
2016-01-11 15:43:29 +00:00
void writeMtp ( int32 dc , const QString & v ) {
2016-01-17 05:01:14 +00:00
QString msg ( QString ( " %1 (dc:%2) % 3 \ n " ).arg(_logsEntryStart()).arg(dc).arg(v)) ;
2016-01-11 15:43:29 +00:00
_logsWrite ( LogDataMtp , msg ) ;
}
2014-05-30 08:53:19 +00:00
2016-01-17 05:01:14 +00:00
QString full ( ) {
if ( LogsData ) {
return LogsData - > full ( ) ;
}
if ( ! LogsInMemory | | LogsInMemory = = DeletedLogsInMemory ) {
return LogsBeforeSingleInstanceChecked ;
}
2016-02-08 10:50:56 +00:00
int32 size = LogsBeforeSingleInstanceChecked . size ( ) ;
2016-01-17 05:01:14 +00:00
for ( LogsInMemoryList : : const_iterator i = LogsInMemory - > cbegin ( ) , e = LogsInMemory - > cend ( ) ; i ! = e ; + + i ) {
if ( i - > first = = LogDataMain ) {
size + = i - > second . size ( ) ;
}
}
QString result ;
result . reserve ( size ) ;
2016-02-08 10:50:56 +00:00
if ( ! LogsBeforeSingleInstanceChecked . isEmpty ( ) ) {
result . append ( LogsBeforeSingleInstanceChecked ) ;
}
2016-01-17 05:01:14 +00:00
for ( LogsInMemoryList : : const_iterator i = LogsInMemory - > cbegin ( ) , e = LogsInMemory - > cend ( ) ; i ! = e ; + + i ) {
if ( i - > first = = LogDataMain ) {
result + = i - > second ;
}
}
return result ;
}
2016-01-11 15:43:29 +00:00
QString vector ( const QVector < MTPlong > & ids ) {
if ( ! ids . size ( ) ) return " [] " ;
QString idsStr = QString ( " [%1 " ) . arg ( ids . cbegin ( ) - > v ) ;
for ( QVector < MTPlong > : : const_iterator i = ids . cbegin ( ) + 1 , e = ids . cend ( ) ; i ! = e ; + + i ) {
idsStr + = QString ( " , %2 " ) . arg ( i - > v ) ;
}
return idsStr + " ] " ;
}
2015-06-01 10:58:46 +00:00
2016-01-11 15:43:29 +00:00
QString vector ( const QVector < uint64 > & ids ) {
if ( ! ids . size ( ) ) return " [] " ;
QString idsStr = QString ( " [%1 " ) . arg ( * ids . cbegin ( ) ) ;
for ( QVector < uint64 > : : const_iterator i = ids . cbegin ( ) + 1 , e = ids . cend ( ) ; i ! = e ; + + i ) {
idsStr + = QString ( " , %2 " ) . arg ( * i ) ;
}
return idsStr + " ] " ;
2014-05-30 08:53:19 +00:00
}
}
2016-01-11 15:43:29 +00:00
void _moveOldDataFiles ( const QString & wasDir ) {
2014-09-30 14:11:09 +00:00
QFile data ( wasDir + " data " ) , dataConfig ( wasDir + " data_config " ) , tdataConfig ( wasDir + " tdata/config " ) ;
if ( data . exists ( ) & & dataConfig . exists ( ) & & ! QFileInfo ( cWorkingDir ( ) + " data " ) . exists ( ) & & ! QFileInfo ( cWorkingDir ( ) + " data_config " ) . exists ( ) ) { // move to home dir
LOG ( ( " Copying data to home dir '%1' from '%2' " ) . arg ( cWorkingDir ( ) ) . arg ( wasDir ) ) ;
if ( data . copy ( cWorkingDir ( ) + " data " ) ) {
LOG ( ( " Copied 'data' to home dir " ) ) ;
if ( dataConfig . copy ( cWorkingDir ( ) + " data_config " ) ) {
LOG ( ( " Copied 'data_config' to home dir " ) ) ;
bool tdataGood = true ;
if ( tdataConfig . exists ( ) ) {
tdataGood = false ;
QDir ( ) . mkpath ( cWorkingDir ( ) + " tdata " ) ;
if ( tdataConfig . copy ( cWorkingDir ( ) + " tdata/config " ) ) {
LOG ( ( " Copied 'tdata/config' to home dir " ) ) ;
tdataGood = true ;
} else {
LOG ( ( " Copied 'data' and 'data_config', but could not copy 'tdata/config'! " ) ) ;
}
}
if ( tdataGood ) {
if ( data . remove ( ) ) {
LOG ( ( " Removed 'data' " ) ) ;
} else {
LOG ( ( " Could not remove 'data' " ) ) ;
}
if ( dataConfig . remove ( ) ) {
LOG ( ( " Removed 'data_config' " ) ) ;
} else {
LOG ( ( " Could not remove 'data_config' " ) ) ;
}
if ( ! tdataConfig . exists ( ) | | tdataConfig . remove ( ) ) {
LOG ( ( " Removed 'tdata/config' " ) ) ;
} else {
2016-10-18 07:56:38 +00:00
LOG ( ( " Could not remove 'tdata/config' " ) ) ;
2014-09-30 14:11:09 +00:00
}
QDir ( ) . rmdir ( wasDir + " tdata " ) ;
}
} else {
LOG ( ( " Copied 'data', but could not copy 'data_config'!! " ) ) ;
}
} else {
LOG ( ( " Could not copy 'data'! " ) ) ;
}
}
}
2016-01-21 06:58:58 +00:00
2016-01-31 11:32:29 +00:00
# if defined Q_OS_MAC || defined Q_OS_LINUX32 || defined Q_OS_LINUX64
2016-01-31 18:01:01 +00:00
2016-01-31 11:32:29 +00:00
# include <execinfo.h>
# include <signal.h>
# include <sys/syscall.h>
# ifdef Q_OS_MAC
# include <dlfcn.h>
# endif
# endif
2016-01-21 06:58:58 +00:00
namespace SignalHandlers {
2016-03-31 11:55:25 +00:00
namespace internal {
using Annotations = std : : map < std : : string , std : : string > ;
using AnnotationRefs = std : : map < std : : string , const QString * > ;
Annotations ProcessAnnotations ;
AnnotationRefs ProcessAnnotationRefs ;
2016-03-20 09:10:16 +00:00
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-03-31 11:55:25 +00:00
QString ReportPath ;
FILE * ReportFile = nullptr ;
int ReportFileNo = 0 ;
2016-01-25 10:22:58 +00:00
char LaunchedDateTimeStr [ 32 ] = { 0 } ;
2016-01-30 16:31:10 +00:00
char LaunchedBinaryName [ 256 ] = { 0 } ;
2016-01-21 06:58:58 +00:00
2016-03-31 11:55:25 +00:00
void writeChar ( char ch ) {
fwrite ( & ch , 1 , 1 , ReportFile ) ;
2016-01-21 06:58:58 +00:00
}
2016-03-20 08:16:35 +00:00
template < bool Unsigned , typename Type >
2016-03-31 11:55:25 +00:00
struct writeNumberSignAndRemoveIt {
2016-03-20 08:16:35 +00:00
static void call ( Type & number ) {
if ( number < 0 ) {
2016-03-31 11:55:25 +00:00
writeChar ( ' - ' ) ;
2016-03-20 08:16:35 +00:00
number = - number ;
}
}
} ;
template < typename Type >
2016-03-31 11:55:25 +00:00
struct writeNumberSignAndRemoveIt < true , Type > {
2016-03-20 08:16:35 +00:00
static void call ( Type & number ) {
}
} ;
2016-01-25 10:22:58 +00:00
template < typename Type >
2016-03-31 11:55:25 +00:00
const dump & writeNumber ( const dump & stream , Type number ) {
if ( ! ReportFile ) return stream ;
2016-01-21 06:58:58 +00:00
2016-03-31 11:55:25 +00:00
writeNumberSignAndRemoveIt < ( Type ( - 1 ) > Type ( 0 ) ) , Type > : : call ( number ) ;
2016-01-25 10:22:58 +00:00
Type upper = 1 , prev = number / 10 ;
2016-01-21 06:58:58 +00:00
while ( prev > = upper ) {
upper * = 10 ;
}
while ( upper > 0 ) {
2016-01-25 10:22:58 +00:00
int digit = ( number / upper ) ;
2016-03-31 11:55:25 +00:00
internal : : writeChar ( ' 0 ' + digit ) ;
2016-01-25 10:22:58 +00:00
number - = digit * upper ;
2016-01-21 06:58:58 +00:00
upper / = 10 ;
}
return stream ;
}
2016-03-31 11:55:25 +00:00
} // namespace internal
dump : : ~ dump ( ) {
if ( internal : : ReportFile ) {
fflush ( internal : : ReportFile ) ;
}
}
const dump & operator < < ( const dump & stream , const char * str ) {
if ( ! internal : : ReportFile ) return stream ;
fwrite ( str , 1 , strlen ( str ) , internal : : ReportFile ) ;
return stream ;
}
const dump & operator < < ( const dump & stream , const wchar_t * str ) {
if ( ! internal : : ReportFile ) return stream ;
for ( int i = 0 , l = wcslen ( str ) ; i < l ; + + i ) {
if ( str [ i ] > = 0 & & str [ i ] < 128 ) {
internal : : writeChar ( char ( str [ i ] ) ) ;
} else {
internal : : writeChar ( ' ? ' ) ;
}
}
return stream ;
}
2016-01-25 10:22:58 +00:00
const dump & operator < < ( const dump & stream , int num ) {
2016-03-31 11:55:25 +00:00
return internal : : writeNumber ( stream , num ) ;
2016-01-25 10:22:58 +00:00
}
2016-01-30 18:38:33 +00:00
const dump & operator < < ( const dump & stream , unsigned int num ) {
2016-03-31 11:55:25 +00:00
return internal : : writeNumber ( stream , num ) ;
2016-01-25 10:22:58 +00:00
}
2016-01-30 18:38:33 +00:00
const dump & operator < < ( const dump & stream , unsigned long num ) {
2016-03-31 11:55:25 +00:00
return internal : : writeNumber ( stream , num ) ;
2016-01-30 18:38:33 +00:00
}
const dump & operator < < ( const dump & stream , unsigned long long num ) {
2016-03-31 11:55:25 +00:00
return internal : : writeNumber ( stream , num ) ;
2016-01-25 10:22:58 +00:00
}
2016-01-30 18:24:18 +00:00
const dump & operator < < ( const dump & stream , double num ) {
if ( num < 0 ) {
2016-03-31 11:55:25 +00:00
internal : : writeChar ( ' - ' ) ;
2016-01-30 18:24:18 +00:00
num = - num ;
}
2016-03-31 11:55:25 +00:00
internal : : writeNumber ( stream , uint64 ( floor ( num ) ) ) ;
internal : : writeChar ( ' . ' ) ;
2016-01-30 18:24:18 +00:00
num - = floor ( num ) ;
for ( int i = 0 ; i < 4 ; + + i ) {
num * = 10 ;
int digit = int ( floor ( num ) ) ;
2016-03-31 11:55:25 +00:00
internal : : writeChar ( ' 0 ' + digit ) ;
2016-01-30 18:24:18 +00:00
num - = digit ;
}
return stream ;
}
2016-03-31 11:55:25 +00:00
namespace internal {
2017-03-17 12:05:50 +00:00
using ReservedMemoryChunk = std : : array < gsl : : byte , 1024 * 1024 > ;
std : : unique_ptr < ReservedMemoryChunk > ReservedMemory ;
2016-09-29 11:37:16 +00:00
void InstallOperatorNewHandler ( ) {
2017-03-17 12:05:50 +00:00
ReservedMemory = std : : make_unique < ReservedMemoryChunk > ( ) ;
2017-03-04 08:59:10 +00:00
std : : set_new_handler ( [ ] {
std : : set_new_handler ( nullptr ) ;
2017-03-17 12:05:50 +00:00
ReservedMemory . reset ( ) ;
Unexpected ( " Could not allocate! " ) ;
2017-03-04 08:59:10 +00:00
} ) ;
2016-09-29 11:37:16 +00:00
}
2016-03-31 11:55:25 +00:00
Qt : : HANDLE ReportingThreadId = nullptr ;
bool ReportingHeaderWritten = false ;
QMutex ReportingMutex ;
2016-01-25 10:22:58 +00:00
2016-03-31 11:55:25 +00:00
const char * BreakpadDumpPath = nullptr ;
const wchar_t * BreakpadDumpPathW = nullptr ;
2016-02-01 11:50:07 +00:00
2016-01-31 11:32:29 +00:00
# if defined Q_OS_MAC || defined Q_OS_LINUX32 || defined Q_OS_LINUX64
struct sigaction SIG_def [ 32 ] ;
void Handler ( int signum , siginfo_t * info , void * ucontext ) {
2016-02-01 10:12:37 +00:00
if ( signum > 0 ) {
sigaction ( signum , & SIG_def [ signum ] , 0 ) ;
}
2016-01-31 11:32:29 +00:00
2016-03-20 09:10:16 +00:00
# else // Q_OS_MAC || Q_OS_LINUX32 || Q_OS_LINUX64
2016-01-21 06:58:58 +00:00
void Handler ( int signum ) {
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_MAC || Q_OS_LINUX || Q_OS_LINUX64
2016-01-31 11:32:29 +00:00
2016-01-21 06:58:58 +00:00
const char * name = 0 ;
switch ( signum ) {
case SIGABRT : name = " SIGABRT " ; break ;
case SIGSEGV : name = " SIGSEGV " ; break ;
case SIGILL : name = " SIGILL " ; break ;
case SIGFPE : name = " SIGFPE " ; break ;
# ifndef Q_OS_WIN
case SIGBUS : name = " SIGBUS " ; break ;
case SIGSYS : name = " SIGSYS " ; break ;
2016-03-20 09:10:16 +00:00
# endif // !Q_OS_WIN
2016-01-21 06:58:58 +00:00
}
2016-01-25 10:22:58 +00:00
Qt : : HANDLE thread = QThread : : currentThreadId ( ) ;
2016-03-31 11:55:25 +00:00
if ( thread = = ReportingThreadId ) return ;
QMutexLocker lock ( & ReportingMutex ) ;
ReportingThreadId = thread ;
2016-01-25 10:22:58 +00:00
2016-03-31 11:55:25 +00:00
if ( ! ReportingHeaderWritten ) {
ReportingHeaderWritten = true ;
2016-04-01 07:43:14 +00:00
auto dec2hex = [ ] ( int value ) - > char {
if ( value > = 0 & & value < 10 ) {
return ' 0 ' + value ;
} else if ( value > = 10 & & value < 16 ) {
return ' a ' + ( value - 10 ) ;
}
return ' # ' ;
} ;
2016-01-25 10:22:58 +00:00
2016-03-31 11:55:25 +00:00
for ( const auto & i : ProcessAnnotationRefs ) {
2016-04-01 07:43:14 +00:00
QByteArray utf8 = i . second - > toUtf8 ( ) ;
std : : string wrapped ;
wrapped . reserve ( 4 * utf8 . size ( ) ) ;
for ( auto ch : utf8 ) {
auto uch = static_cast < uchar > ( ch ) ;
wrapped . append ( " \\ x " , 2 ) . append ( 1 , dec2hex ( uch > > 4 ) ) . append ( 1 , dec2hex ( uch & 0x0F ) ) ;
}
ProcessAnnotations [ i . first ] = wrapped ;
2016-03-31 11:55:25 +00:00
}
const Annotations c_ProcessAnnotations ( ProcessAnnotations ) ;
2016-03-14 09:25:48 +00:00
for ( const auto & i : c_ProcessAnnotations ) {
dump ( ) < < i . first . c_str ( ) < < " : " < < i . second . c_str ( ) < < " \n " ;
2016-01-25 10:22:58 +00:00
}
psWriteDump ( ) ;
dump ( ) < < " \n " ;
}
2016-01-21 06:58:58 +00:00
if ( name ) {
2016-01-25 10:22:58 +00:00
dump ( ) < < " Caught signal " < < signum < < " ( " < < name < < " ) in thread " < < uint64 ( thread ) < < " \n " ;
2016-02-01 11:09:11 +00:00
} else if ( signum = = - 1 ) {
dump ( ) < < " Google Breakpad caught a crash, minidump written in thread " < < uint64 ( thread ) < < " \n " ;
2016-02-01 11:50:07 +00:00
if ( BreakpadDumpPath ) {
dump ( ) < < " Minidump: " < < BreakpadDumpPath < < " \n " ;
} else if ( BreakpadDumpPathW ) {
dump ( ) < < " Minidump: " < < BreakpadDumpPathW < < " \n " ;
}
2016-02-01 11:09:11 +00:00
} else {
2016-01-25 10:22:58 +00:00
dump ( ) < < " Caught signal " < < signum < < " in thread " < < uint64 ( thread ) < < " \n " ;
2016-01-21 06:58:58 +00:00
}
2016-01-25 10:22:58 +00:00
2016-02-02 11:58:28 +00:00
// see https://github.com/benbjohnson/bandicoot
2016-01-31 11:32:29 +00:00
# if defined Q_OS_MAC || defined Q_OS_LINUX32 || defined Q_OS_LINUX64
ucontext_t * uc = ( ucontext_t * ) ucontext ;
void * caller = 0 ;
2016-02-01 10:12:37 +00:00
if ( uc ) {
2016-01-31 11:32:29 +00:00
# if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
/* OSX < 10.6 */
# if defined(__x86_64__)
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext - > __ss . __rip ;
2016-01-31 11:32:29 +00:00
# elif defined(__i386__)
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2016-01-31 11:32:29 +00:00
# else
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext - > __ss . __srr0 ;
2016-01-31 11:32:29 +00:00
# endif
# elif defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)
/* OSX >= 10.6 */
# if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__)
caller = ( void * ) uc - > uc_mcontext - > __ss . __rip ;
# else
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext - > __ss . __eip ;
2016-01-31 11:32:29 +00:00
# endif
# elif defined(__linux__)
/* Linux */
# if defined(__i386__)
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext . gregs [ 14 ] ; /* Linux 32 */
2016-01-31 11:32:29 +00:00
# elif defined(__X86_64__) || defined(__x86_64__)
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext . gregs [ 16 ] ; /* Linux 64 */
2016-01-31 11:32:29 +00:00
# elif defined(__ia64__) /* Linux IA64 */
2016-01-31 16:13:51 +00:00
caller = ( void * ) uc - > uc_mcontext . sc_ip ;
2016-01-31 11:32:29 +00:00
# endif
# endif
2016-02-01 10:12:37 +00:00
}
2016-01-31 11:32:29 +00:00
2016-01-31 16:13:51 +00:00
void * addresses [ 132 ] = { 0 } ;
2016-01-31 11:32:29 +00:00
size_t size = backtrace ( addresses , 128 ) ;
/* overwrite sigaction with caller's address */
2016-01-31 16:13:51 +00:00
if ( caller ) {
for ( int i = size ; i > 1 ; - - i ) {
addresses [ i + 3 ] = addresses [ i ] ;
}
addresses [ 2 ] = ( void * ) 0x1 ;
addresses [ 3 ] = caller ;
addresses [ 4 ] = ( void * ) 0x1 ;
}
2016-01-31 11:32:29 +00:00
# ifdef Q_OS_MAC
dump ( ) < < " \n Base image addresses: \n " ;
for ( size_t i = 0 ; i < size ; + + i ) {
Dl_info info ;
dump ( ) < < i < < " " ;
if ( dladdr ( addresses [ i ] , & info ) ) {
dump ( ) < < uint64 ( info . dli_fbase ) < < " ( " < < info . dli_fname < < " ) \n " ;
} else {
dump ( ) < < " _unknown_module_ \n " ;
}
}
2016-03-20 09:10:16 +00:00
# endif // Q_OS_MAC
2016-01-31 11:32:29 +00:00
2016-01-25 10:22:58 +00:00
dump ( ) < < " \n Backtrace: \n " ;
2016-03-31 11:55:25 +00:00
backtrace_symbols_fd ( addresses , size , ReportFileNo ) ;
2016-01-30 18:24:18 +00:00
2016-03-20 09:10:16 +00:00
# else // Q_OS_MAC || Q_OS_LINUX32 || Q_OS_LINUX64
2016-01-31 18:01:43 +00:00
dump ( ) < < " \n Backtrace: \n " ;
2016-01-31 11:32:29 +00:00
psWriteStackTrace ( ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_MAC || Q_OS_LINUX32 || Q_OS_LINUX64
2016-01-31 11:32:29 +00:00
dump ( ) < < " \n " ;
2016-03-31 11:55:25 +00:00
ReportingThreadId = nullptr ;
2016-01-21 06:58:58 +00:00
}
2016-02-02 11:48:46 +00:00
bool SetSignalHandlers = true ;
2016-03-14 09:25:48 +00:00
bool CrashLogged = false ;
2016-02-02 11:48:46 +00:00
# if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
2016-02-01 10:12:37 +00:00
google_breakpad : : ExceptionHandler * BreakpadExceptionHandler = 0 ;
# ifdef Q_OS_WIN
bool DumpCallback ( const wchar_t * _dump_dir , const wchar_t * _minidump_id , void * context , EXCEPTION_POINTERS * exinfo , MDRawAssertionInfo * assertion , bool success )
2016-03-20 09:10:16 +00:00
# elif defined Q_OS_MAC // Q_OS_WIN
2016-02-01 10:12:37 +00:00
bool DumpCallback ( const char * _dump_dir , const char * _minidump_id , void * context , bool success )
2016-03-20 09:10:16 +00:00
# elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32 // Q_OS_MAC
2016-02-01 10:12:37 +00:00
bool DumpCallback ( const google_breakpad : : MinidumpDescriptor & md , void * context , bool success )
2016-03-20 09:10:16 +00:00
# endif // Q_OS_LINUX64 || Q_OS_LINUX32
2016-02-01 10:12:37 +00:00
{
2016-03-14 09:25:48 +00:00
if ( CrashLogged ) return success ;
CrashLogged = true ;
2016-02-01 11:50:07 +00:00
# ifdef Q_OS_WIN
BreakpadDumpPathW = _minidump_id ;
Handler ( - 1 ) ;
2016-03-20 09:10:16 +00:00
# else // Q_OS_WIN
2016-02-01 11:50:07 +00:00
# ifdef Q_OS_MAC
BreakpadDumpPath = _minidump_id ;
2016-03-20 09:10:16 +00:00
# else // Q_OS_MAC
2016-02-01 11:50:07 +00:00
BreakpadDumpPath = md . path ( ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_MAC
2016-02-01 10:12:37 +00:00
Handler ( - 1 , 0 , 0 ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_WIN
2016-02-01 10:12:37 +00:00
return success ;
}
2016-03-20 09:10:16 +00:00
# endif // !Q_OS_MAC || MAC_USE_BREAKPAD
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-02-01 10:12:37 +00:00
2016-03-31 11:55:25 +00:00
} // namespace internal
2016-03-20 09:10:16 +00:00
void StartCrashHandler ( ) {
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-03-31 11:55:25 +00:00
using internal : : ProcessAnnotations ;
2016-02-02 12:50:12 +00:00
ProcessAnnotations [ " Binary " ] = cExeName ( ) . toUtf8 ( ) . constData ( ) ;
ProcessAnnotations [ " ApiId " ] = QString : : number ( ApiId ) . toUtf8 ( ) . constData ( ) ;
2016-04-27 12:02:17 +00:00
ProcessAnnotations [ " Version " ] = ( cBetaVersion ( ) ? qsl ( " %1 beta " ) . arg ( cBetaVersion ( ) ) : ( cAlphaVersion ( ) ? qsl ( " %1 alpha " ) : qsl ( " %1 " ) ) . arg ( AppVersion ) ) . toUtf8 ( ) . constData ( ) ;
2016-02-02 12:50:12 +00:00
ProcessAnnotations [ " Launched " ] = QDateTime : : currentDateTime ( ) . toString ( " dd.MM.yyyy hh:mm:ss " ) . toUtf8 ( ) . constData ( ) ;
ProcessAnnotations [ " Platform " ] = cPlatformString ( ) . toUtf8 ( ) . constData ( ) ;
2016-02-15 15:52:11 +00:00
ProcessAnnotations [ " UserTag " ] = QString : : number ( Sandbox : : UserTag ( ) , 16 ) . toUtf8 ( ) . constData ( ) ;
2016-02-02 11:48:46 +00:00
QString dumpspath = cWorkingDir ( ) + qsl ( " tdata/dumps " ) ;
QDir ( ) . mkpath ( dumpspath ) ;
2016-02-01 10:12:37 +00:00
# ifdef Q_OS_WIN
2016-03-31 11:55:25 +00:00
internal : : BreakpadExceptionHandler = new google_breakpad : : ExceptionHandler (
2016-02-02 11:48:46 +00:00
dumpspath . toStdWString ( ) ,
2016-05-02 13:42:09 +00:00
google_breakpad : : ExceptionHandler : : FilterCallback ( nullptr ) ,
2016-03-31 11:55:25 +00:00
internal : : DumpCallback ,
2016-05-02 13:42:09 +00:00
( void * ) nullptr , // callback_context
google_breakpad : : ExceptionHandler : : HANDLER_ALL ,
MINIDUMP_TYPE ( MiniDumpNormal ) ,
// MINIDUMP_TYPE(MiniDumpWithFullMemory | MiniDumpWithHandleData | MiniDumpWithThreadInfo | MiniDumpWithProcessThreadData | MiniDumpWithFullMemoryInfo | MiniDumpWithUnloadedModules | MiniDumpWithFullAuxiliaryState | MiniDumpIgnoreInaccessibleMemory | MiniDumpWithTokenInformation),
( const wchar_t * ) nullptr , // pipe_name
( const google_breakpad : : CustomClientInfo * ) nullptr
2016-02-01 10:12:37 +00:00
) ;
2016-03-20 09:10:16 +00:00
# elif defined Q_OS_MAC // Q_OS_WIN
2016-02-02 11:48:46 +00:00
# ifdef MAC_USE_BREAKPAD
2016-02-04 12:44:39 +00:00
# ifndef _DEBUG
2016-03-31 11:55:25 +00:00
internal : : BreakpadExceptionHandler = new google_breakpad : : ExceptionHandler (
2016-02-09 14:42:36 +00:00
QFile : : encodeName ( dumpspath ) . toStdString ( ) ,
2016-02-01 10:12:37 +00:00
/*FilterCallback*/ 0 ,
2016-03-31 11:55:25 +00:00
internal : : DumpCallback ,
2016-02-01 10:12:37 +00:00
/*context*/ 0 ,
true ,
0
) ;
2016-03-20 09:10:16 +00:00
# endif // !_DEBUG
2016-03-31 11:55:25 +00:00
internal : : SetSignalHandlers = false ;
2016-03-20 09:10:16 +00:00
# else // MAC_USE_BREAKPAD
2016-02-02 11:48:46 +00:00
crashpad : : CrashpadClient crashpad_client ;
2016-02-02 12:50:12 +00:00
std : : string handler = ( cExeDir ( ) + cExeName ( ) + qsl ( " /Contents/Helpers/crashpad_handler " ) ) . toUtf8 ( ) . constData ( ) ;
2016-02-09 14:42:36 +00:00
std : : string database = QFile : : encodeName ( dumpspath ) . constData ( ) ;
2016-02-02 11:48:46 +00:00
if ( crashpad_client . StartHandler ( base : : FilePath ( handler ) ,
2016-05-02 13:42:09 +00:00
base : : FilePath ( database ) ,
std : : string ( ) ,
ProcessAnnotations ,
std : : vector < std : : string > ( ) ,
false ) ) {
2016-02-02 11:48:46 +00:00
crashpad_client . UseHandler ( ) ;
}
2016-03-20 09:10:16 +00:00
# endif // else for MAC_USE_BREAKPAD
2016-02-01 10:12:37 +00:00
# elif defined Q_OS_LINUX64 || defined Q_OS_LINUX32
2016-03-31 11:55:25 +00:00
internal : : BreakpadExceptionHandler = new google_breakpad : : ExceptionHandler (
2016-02-09 14:42:36 +00:00
google_breakpad : : MinidumpDescriptor ( QFile : : encodeName ( dumpspath ) . toStdString ( ) ) ,
2016-02-01 10:12:37 +00:00
/*FilterCallback*/ 0 ,
2016-03-31 11:55:25 +00:00
internal : : DumpCallback ,
2016-02-01 10:12:37 +00:00
/*context*/ 0 ,
true ,
- 1
) ;
2016-03-20 09:10:16 +00:00
# endif // Q_OS_LINUX64 || Q_OS_LINUX32
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-02-01 10:12:37 +00:00
}
2016-03-20 09:10:16 +00:00
void FinishCrashHandler ( ) {
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-02-02 11:48:46 +00:00
# if !defined Q_OS_MAC || defined MAC_USE_BREAKPAD
2016-03-31 11:55:25 +00:00
if ( internal : : BreakpadExceptionHandler ) {
2016-10-07 16:45:45 +00:00
delete base : : take ( internal : : BreakpadExceptionHandler ) ;
2016-02-01 10:12:37 +00:00
}
2016-03-20 09:10:16 +00:00
# endif // !Q_OS_MAC || MAC_USE_BREAKPAD
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-02-01 10:12:37 +00:00
}
2016-01-21 06:58:58 +00:00
Status start ( ) {
2016-03-20 09:10:16 +00:00
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-03-31 11:55:25 +00:00
using internal : : ReportPath ;
ReportPath = cWorkingDir ( ) + qsl ( " tdata/working " ) ;
2016-03-20 09:10:16 +00:00
2016-02-09 16:05:08 +00:00
# ifdef Q_OS_WIN
2016-03-20 08:16:35 +00:00
FILE * f = nullptr ;
2016-03-31 11:55:25 +00:00
if ( _wfopen_s ( & f , ReportPath . toStdWString ( ) . c_str ( ) , L " rb " ) ! = 0 ) {
2016-03-20 08:16:35 +00:00
f = nullptr ;
} else {
2016-03-20 09:10:16 +00:00
# else // !Q_OS_WIN
2016-03-31 11:55:25 +00:00
if ( FILE * f = fopen ( QFile : : encodeName ( ReportPath ) . constData ( ) , " rb " ) ) {
2016-03-20 09:10:16 +00:00
# endif // else for !Q_OS_WIN
2016-01-21 06:58:58 +00:00
QByteArray lastdump ;
2016-03-11 19:10:56 +00:00
char buffer [ 256 * 1024 ] = { 0 } ;
int32 read = fread ( buffer , 1 , 256 * 1024 , f ) ;
if ( read > 0 ) {
2016-01-21 06:58:58 +00:00
lastdump . append ( buffer , read ) ;
}
fclose ( f ) ;
2016-02-08 14:54:55 +00:00
Sandbox : : SetLastCrashDump ( lastdump ) ;
2016-01-21 06:58:58 +00:00
2016-03-31 11:55:25 +00:00
LOG ( ( " Opened '%1' for reading, the previous Telegram Desktop launch was not finished properly :( Crash log size: %2 " ) . arg ( ReportPath ) . arg ( lastdump . size ( ) ) ) ;
2016-01-21 06:58:58 +00:00
return LastCrashed ;
}
2016-03-20 09:10:16 +00:00
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-01-21 06:58:58 +00:00
return restart ( ) ;
}
Status restart ( ) {
2016-03-20 09:10:16 +00:00
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-03-31 11:55:25 +00:00
if ( internal : : ReportFile ) {
2016-01-30 16:31:10 +00:00
return Started ;
}
2016-02-09 16:05:08 +00:00
# ifdef Q_OS_WIN
2016-03-31 11:55:25 +00:00
if ( _wfopen_s ( & internal : : ReportFile , internal : : ReportPath . toStdWString ( ) . c_str ( ) , L " wb " ) ! = 0 ) {
internal : : ReportFile = nullptr ;
2016-03-20 08:16:35 +00:00
}
2016-03-20 09:10:16 +00:00
# else // Q_OS_WIN
2016-03-31 14:12:30 +00:00
internal : : ReportFile = fopen ( QFile : : encodeName ( internal : : ReportPath ) . constData ( ) , " wb " ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_WIN
2016-03-31 11:55:25 +00:00
if ( internal : : ReportFile ) {
2016-03-20 08:16:35 +00:00
# ifdef Q_OS_WIN
2016-03-31 11:55:25 +00:00
internal : : ReportFileNo = _fileno ( internal : : ReportFile ) ;
2016-03-20 09:10:16 +00:00
# else // Q_OS_WIN
2016-03-31 14:12:30 +00:00
internal : : ReportFileNo = fileno ( internal : : ReportFile ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_WIN
2016-03-31 11:55:25 +00:00
if ( internal : : SetSignalHandlers ) {
2016-01-31 11:32:29 +00:00
# ifndef Q_OS_WIN
2016-02-02 11:48:46 +00:00
struct sigaction sigact ;
2016-03-31 11:55:25 +00:00
sigact . sa_sigaction = SignalHandlers : : internal : : Handler ;
2016-02-02 11:48:46 +00:00
sigemptyset ( & sigact . sa_mask ) ;
sigact . sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO ;
2016-03-31 14:12:30 +00:00
sigaction ( SIGABRT , & sigact , & internal : : SIG_def [ SIGABRT ] ) ;
sigaction ( SIGSEGV , & sigact , & internal : : SIG_def [ SIGSEGV ] ) ;
sigaction ( SIGILL , & sigact , & internal : : SIG_def [ SIGILL ] ) ;
sigaction ( SIGFPE , & sigact , & internal : : SIG_def [ SIGFPE ] ) ;
sigaction ( SIGBUS , & sigact , & internal : : SIG_def [ SIGBUS ] ) ;
sigaction ( SIGSYS , & sigact , & internal : : SIG_def [ SIGSYS ] ) ;
2016-03-20 09:10:16 +00:00
# else // !Q_OS_WIN
2016-03-31 11:55:25 +00:00
signal ( SIGABRT , SignalHandlers : : internal : : Handler ) ;
signal ( SIGSEGV , SignalHandlers : : internal : : Handler ) ;
signal ( SIGILL , SignalHandlers : : internal : : Handler ) ;
signal ( SIGFPE , SignalHandlers : : internal : : Handler ) ;
2016-03-20 09:10:16 +00:00
# endif // else for !Q_OS_WIN
2016-02-02 11:48:46 +00:00
}
2016-09-29 11:37:16 +00:00
SignalHandlers : : internal : : InstallOperatorNewHandler ( ) ;
2016-01-21 06:58:58 +00:00
return Started ;
}
2016-03-31 11:55:25 +00:00
LOG ( ( " FATAL: Could not open '%1' for writing! " ) . arg ( internal : : ReportPath ) ) ;
2016-01-21 06:58:58 +00:00
return CantOpen ;
2016-03-20 09:10:16 +00:00
# else // !TDESKTOP_DISABLE_CRASH_REPORTS
return Started ;
# endif // else for !TDESKTOP_DISABLE_CRASH_REPORTS
2016-01-21 06:58:58 +00:00
}
void finish ( ) {
2016-03-20 09:10:16 +00:00
# ifndef TDESKTOP_DISABLE_CRASH_REPORTS
2016-03-20 15:08:13 +00:00
FinishCrashHandler ( ) ;
2016-03-31 11:55:25 +00:00
if ( internal : : ReportFile ) {
fclose ( internal : : ReportFile ) ;
internal : : ReportFile = nullptr ;
2016-03-11 20:07:13 +00:00
2016-02-09 16:05:08 +00:00
# ifdef Q_OS_WIN
2016-03-31 11:55:25 +00:00
_wunlink ( internal : : ReportPath . toStdWString ( ) . c_str ( ) ) ;
2016-03-20 09:10:16 +00:00
# else // Q_OS_WIN
2016-03-31 11:55:25 +00:00
unlink ( internal : : ReportPath . toUtf8 ( ) . constData ( ) ) ;
2016-03-20 09:10:16 +00:00
# endif // else for Q_OS_WIN
2016-01-21 06:58:58 +00:00
}
2016-03-20 09:10:16 +00:00
# endif // !TDESKTOP_DISABLE_CRASH_REPORTS
2016-01-21 06:58:58 +00:00
}
2016-03-31 11:55:25 +00:00
void setCrashAnnotation ( const std : : string & key , const QString & value ) {
2017-12-08 08:57:43 +00:00
static QMutex mutex ;
QMutexLocker lock ( & mutex ) ;
2016-03-31 20:36:46 +00:00
if ( ! value . trimmed ( ) . isEmpty ( ) ) {
2016-03-31 11:55:25 +00:00
internal : : ProcessAnnotations [ key ] = value . toUtf8 ( ) . constData ( ) ;
2016-03-31 20:36:46 +00:00
} else {
internal : : ProcessAnnotations . erase ( key ) ;
2016-03-14 09:25:48 +00:00
}
}
2016-03-31 11:55:25 +00:00
void setCrashAnnotationRef ( const std : : string & key , const QString * valuePtr ) {
if ( valuePtr ) {
internal : : ProcessAnnotationRefs [ key ] = valuePtr ;
2016-03-31 20:36:46 +00:00
} else {
internal : : ProcessAnnotationRefs . erase ( key ) ;
2016-03-31 11:55:25 +00:00
}
2016-03-15 12:18:12 +00:00
}
2016-01-21 06:58:58 +00:00
}