2013-05-16 08:18:31 +02:00
/*
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
* Accelerator Extension
* Copyright ( C ) 2011 Asher Baker ( asherkin ) . All rights reserved .
* = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
*
* This program is free software ; you can redistribute it and / or modify it under
* the terms of the GNU General Public License , version 3.0 , as published by the
* Free Software Foundation .
2016-08-22 16:06:14 +02:00
*
2013-05-16 08:18:31 +02:00
* This program 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 .
*
* You should have received a copy of the GNU General Public License along with
* this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include "extension.h"
# include <IWebternet.h>
# include "MemoryDownloader.h"
2013-05-18 01:46:46 +02:00
# if defined _LINUX
2013-05-16 08:18:31 +02:00
# include "client/linux/handler/exception_handler.h"
2015-10-23 13:10:09 +02:00
# include "common/linux/linux_libc_support.h"
2015-10-22 18:18:17 +02:00
# include "third_party/lss/linux_syscall_support.h"
2018-07-21 02:29:45 +02:00
# include "common/linux/dump_symbols.h"
# include "common/path_helper.h"
2013-05-16 08:18:31 +02:00
# include <signal.h>
2016-08-22 16:06:14 +02:00
# include <dirent.h>
2013-05-16 08:18:31 +02:00
# include <unistd.h>
2018-07-21 02:29:45 +02:00
# include <paths.h>
class StderrInhibitor
{
FILE * saved_stderr = nullptr ;
public :
StderrInhibitor ( ) {
saved_stderr = fdopen ( dup ( fileno ( stderr ) ) , " w " ) ;
if ( freopen ( _PATH_DEVNULL , " w " , stderr ) ) {
// If it fails, not a lot we can (or should) do.
// Add this brace section to silence gcc warnings.
}
}
~ StderrInhibitor ( ) {
fflush ( stderr ) ;
dup2 ( fileno ( saved_stderr ) , fileno ( stderr ) ) ;
fclose ( saved_stderr ) ;
}
} ;
2013-05-18 01:46:46 +02:00
# elif defined _WINDOWS
# define _STDINT // ~.~
# include "client/windows/handler/exception_handler.h"
2018-07-21 02:29:45 +02:00
2014-11-08 02:45:19 +01:00
# else
# error Bad platform.
2013-05-18 01:46:46 +02:00
# endif
2013-05-16 08:18:31 +02:00
2018-07-21 02:29:45 +02:00
# include <google_breakpad/processor/minidump.h>
# include <google_breakpad/processor/minidump_processor.h>
# include <google_breakpad/processor/process_state.h>
# include <google_breakpad/processor/call_stack.h>
# include <google_breakpad/processor/stack_frame.h>
# include <processor/pathname_stripper.h>
# include <sstream>
# include <streambuf>
2013-05-16 08:18:31 +02:00
Accelerator g_accelerator ;
SMEXT_LINK ( & g_accelerator ) ;
IWebternet * webternet ;
2016-01-13 17:25:03 +01:00
IGameConfig * gameconfig ;
typedef void ( * GetSpew_t ) ( char * buffer , unsigned int length ) ;
GetSpew_t GetSpew ;
2017-09-12 22:10:10 +02:00
# if defined _WINDOWS
2017-09-13 13:23:25 +02:00
typedef void ( __fastcall * GetSpewFastcall_t ) ( char * buffer , unsigned int length ) ;
GetSpewFastcall_t GetSpewFastcall ;
2017-09-12 22:10:10 +02:00
# endif
2016-01-13 17:25:03 +01:00
char spewBuffer [ 65536 ] ; // Hi.
2013-05-16 08:18:31 +02:00
2016-01-25 16:38:43 +01:00
char crashMap [ 256 ] ;
char crashGamePath [ 512 ] ;
char crashCommandLine [ 1024 ] ;
char crashSourceModPath [ 512 ] ;
char crashGameDirectory [ 256 ] ;
2018-01-07 23:20:55 +01:00
char steamInf [ 1024 ] ;
2016-01-25 16:38:43 +01:00
2016-01-14 12:18:10 +01:00
char dumpStoragePath [ 512 ] ;
char logPath [ 512 ] ;
2013-05-16 08:18:31 +02:00
google_breakpad : : ExceptionHandler * handler = NULL ;
2016-01-14 17:53:16 +01:00
# if 0
2015-10-22 18:18:17 +02:00
struct PluginInfo {
2015-10-23 13:10:09 +02:00
unsigned int serial ;
PluginStatus status ;
char filename [ 256 ] ;
2015-10-22 18:18:17 +02:00
char name [ 256 ] ;
char author [ 256 ] ;
char description [ 256 ] ;
char version [ 256 ] ;
char url [ 256 ] ;
} ;
unsigned int plugin_count ;
PluginInfo plugins [ 256 ] ;
2016-01-14 17:53:16 +01:00
# endif
2015-10-22 18:18:17 +02:00
2013-05-18 01:46:46 +02:00
# if defined _LINUX
2013-05-16 08:18:31 +02:00
void ( * SignalHandler ) ( int , siginfo_t * , void * ) ;
const int kExceptionSignals [ ] = {
SIGSEGV , SIGABRT , SIGFPE , SIGILL , SIGBUS
} ;
const int kNumHandledSignals = sizeof ( kExceptionSignals ) / sizeof ( kExceptionSignals [ 0 ] ) ;
static bool dumpCallback ( const google_breakpad : : MinidumpDescriptor & descriptor , void * context , bool succeeded )
{
2015-10-22 18:18:17 +02:00
//printf("Wrote minidump to: %s\n", descriptor.path());
2016-01-13 17:25:03 +01:00
if ( succeeded ) {
sys_write ( STDOUT_FILENO , " Wrote minidump to: " , 19 ) ;
} else {
sys_write ( STDOUT_FILENO , " Failed to write minidump to: " , 29 ) ;
}
2015-10-23 13:10:09 +02:00
sys_write ( STDOUT_FILENO , descriptor . path ( ) , my_strlen ( descriptor . path ( ) ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( STDOUT_FILENO , " \n " , 1 ) ;
2016-01-13 17:25:03 +01:00
if ( ! succeeded ) {
return succeeded ;
}
2016-01-14 12:18:10 +01:00
my_strlcpy ( dumpStoragePath , descriptor . path ( ) , sizeof ( dumpStoragePath ) ) ;
my_strlcat ( dumpStoragePath , " .txt " , sizeof ( dumpStoragePath ) ) ;
2015-10-22 18:18:17 +02:00
2016-01-14 12:18:10 +01:00
int extra = sys_open ( dumpStoragePath , O_WRONLY | O_CREAT , S_IRUSR | S_IWUSR ) ;
2015-10-22 18:18:17 +02:00
if ( extra = = - 1 ) {
sys_write ( STDOUT_FILENO , " Failed to open metadata file! \n " , 30 ) ;
return succeeded ;
}
2016-01-25 16:38:43 +01:00
sys_write ( extra , " -------- CONFIG BEGIN -------- " , 30 ) ;
sys_write ( extra , " \n Map= " , 5 ) ;
sys_write ( extra , crashMap , my_strlen ( crashMap ) ) ;
sys_write ( extra , " \n GamePath= " , 10 ) ;
sys_write ( extra , crashGamePath , my_strlen ( crashGamePath ) ) ;
sys_write ( extra , " \n CommandLine= " , 13 ) ;
sys_write ( extra , crashCommandLine , my_strlen ( crashCommandLine ) ) ;
sys_write ( extra , " \n SourceModPath= " , 15 ) ;
sys_write ( extra , crashSourceModPath , my_strlen ( crashSourceModPath ) ) ;
sys_write ( extra , " \n GameDirectory= " , 15 ) ;
sys_write ( extra , crashGameDirectory , my_strlen ( crashGameDirectory ) ) ;
sys_write ( extra , " \n ExtensionVersion= " , 18 ) ;
2016-09-11 13:36:07 +02:00
sys_write ( extra , SM_VERSION , my_strlen ( SM_VERSION ) ) ;
sys_write ( extra , " \n ExtensionBuild= " , 16 ) ;
sys_write ( extra , SM_BUILD_UNIQUEID , my_strlen ( SM_BUILD_UNIQUEID ) ) ;
2016-08-22 16:06:14 +02:00
sys_write ( extra , steamInf , my_strlen ( steamInf ) ) ;
2016-01-25 16:38:43 +01:00
sys_write ( extra , " \n -------- CONFIG END -------- \n " , 30 ) ;
2016-01-13 17:25:03 +01:00
if ( GetSpew ) {
GetSpew ( spewBuffer , sizeof ( spewBuffer ) ) ;
2017-09-13 13:23:25 +02:00
2016-08-22 16:06:14 +02:00
if ( my_strlen ( spewBuffer ) > 0 ) {
sys_write ( extra , " -------- CONSOLE HISTORY BEGIN -------- \n " , 40 ) ;
sys_write ( extra , spewBuffer , my_strlen ( spewBuffer ) ) ;
sys_write ( extra , " -------- CONSOLE HISTORY END -------- \n " , 38 ) ;
}
2016-01-13 17:25:03 +01:00
}
2016-01-14 17:53:16 +01:00
#if 0
2015-10-23 13:10:09 +02:00
char pis [ 64 ] ;
char pds [ 32 ] ;
2015-10-22 18:18:17 +02:00
for ( unsigned i = 0 ; i < plugin_count ; + + i ) {
PluginInfo * p = & plugins [ i ] ;
if ( p - > serial = = 0 ) continue ;
2015-10-23 13:10:09 +02:00
my_uitos ( pds , i , my_uint_len ( i ) ) ;
pds [ my_uint_len ( i ) ] = ' \0 ' ;
my_strlcpy ( pis , " plugin[ " , sizeof ( pis ) ) ;
my_strlcat ( pis , pds , sizeof ( pis ) ) ;
my_strlcat ( pis , " ]. " , sizeof ( pis ) ) ;
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " filename= " , 9 ) ;
sys_write ( extra , p - > filename , my_strlen ( p - > filename ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
2015-10-23 13:10:09 +02:00
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " name= " , 5 ) ;
sys_write ( extra , p - > name , my_strlen ( p - > name ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
2015-10-23 13:10:09 +02:00
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " author= " , 7 ) ;
sys_write ( extra , p - > author , my_strlen ( p - > author ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
2015-10-23 13:10:09 +02:00
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " description= " , 12 ) ;
sys_write ( extra , p - > description , my_strlen ( p - > description ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
2015-10-23 13:10:09 +02:00
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " version= " , 8 ) ;
sys_write ( extra , p - > version , my_strlen ( p - > version ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
2015-10-23 13:10:09 +02:00
sys_write ( extra , pis , my_strlen ( pis ) ) ;
sys_write ( extra , " url= " , 4 ) ;
sys_write ( extra , p - > url , my_strlen ( p - > url ) ) ;
2015-10-22 18:18:17 +02:00
sys_write ( extra , " \n " , 1 ) ;
}
2016-01-14 17:53:16 +01:00
# endif
2015-10-22 18:18:17 +02:00
sys_close ( extra ) ;
2013-05-16 08:18:31 +02:00
return succeeded ;
}
void OnGameFrame ( bool simulating )
{
bool weHaveBeenFuckedOver = false ;
struct sigaction oact ;
for ( int i = 0 ; i < kNumHandledSignals ; + + i ) {
sigaction ( kExceptionSignals [ i ] , NULL , & oact ) ;
if ( oact . sa_sigaction ! = SignalHandler ) {
weHaveBeenFuckedOver = true ;
break ;
}
}
if ( ! weHaveBeenFuckedOver ) {
return ;
}
struct sigaction act ;
memset ( & act , 0 , sizeof ( act ) ) ;
sigemptyset ( & act . sa_mask ) ;
for ( int i = 0 ; i < kNumHandledSignals ; + + i ) {
sigaddset ( & act . sa_mask , kExceptionSignals [ i ] ) ;
}
2016-08-22 16:06:14 +02:00
2013-05-16 08:18:31 +02:00
act . sa_sigaction = SignalHandler ;
act . sa_flags = SA_ONSTACK | SA_SIGINFO ;
for ( int i = 0 ; i < kNumHandledSignals ; + + i ) {
sigaction ( kExceptionSignals [ i ] , & act , NULL ) ;
}
}
2018-07-21 02:29:45 +02:00
2013-05-18 01:46:46 +02:00
# elif defined _WINDOWS
2014-11-08 03:00:40 +01:00
void * vectoredHandler = NULL ;
2013-05-18 01:46:46 +02:00
LONG CALLBACK BreakpadVectoredHandler ( _In_ PEXCEPTION_POINTERS ExceptionInfo )
{
2013-05-19 04:28:09 +02:00
switch ( ExceptionInfo - > ExceptionRecord - > ExceptionCode )
2013-05-18 01:46:46 +02:00
{
2013-05-19 04:28:09 +02:00
case EXCEPTION_ACCESS_VIOLATION :
2015-02-14 04:36:17 +01:00
case EXCEPTION_INVALID_HANDLE :
2013-05-19 04:28:09 +02:00
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED :
case EXCEPTION_DATATYPE_MISALIGNMENT :
case EXCEPTION_ILLEGAL_INSTRUCTION :
2014-10-31 23:42:09 +01:00
case EXCEPTION_INT_DIVIDE_BY_ZERO :
2013-05-19 04:28:09 +02:00
case EXCEPTION_STACK_OVERFLOW :
2015-02-14 16:58:07 +01:00
case 0xC0000409 : // STATUS_STACK_BUFFER_OVERRUN
case 0xC0000374 : // STATUS_HEAP_CORRUPTION
2013-05-19 04:28:09 +02:00
break ;
2014-10-31 23:42:09 +01:00
case 0 : // Valve use this for Sys_Error.
if ( ( ExceptionInfo - > ExceptionRecord - > ExceptionFlags & EXCEPTION_NONCONTINUABLE ) = = 0 )
return EXCEPTION_CONTINUE_SEARCH ;
break ;
2013-05-19 04:28:09 +02:00
default :
return EXCEPTION_CONTINUE_SEARCH ;
2013-05-18 01:46:46 +02:00
}
2016-08-22 16:06:14 +02:00
2013-05-18 01:46:46 +02:00
if ( handler - > WriteMinidumpForException ( ExceptionInfo ) )
{
// Stop the handler thread from deadlocking us.
delete handler ;
2016-08-22 16:06:14 +02:00
2013-05-18 01:46:46 +02:00
// Stop Valve's handler being called.
ExceptionInfo - > ExceptionRecord - > ExceptionCode = EXCEPTION_BREAKPOINT ;
2016-08-22 16:06:14 +02:00
2013-05-18 01:46:46 +02:00
return EXCEPTION_EXECUTE_HANDLER ;
} else {
return EXCEPTION_CONTINUE_SEARCH ;
}
}
static bool dumpCallback ( const wchar_t * dump_path ,
const wchar_t * minidump_id ,
void * context ,
EXCEPTION_POINTERS * exinfo ,
MDRawAssertionInfo * assertion ,
bool succeeded )
{
2016-01-13 17:49:19 +01:00
if ( ! succeeded ) {
printf ( " Failed to write minidump to: %ls \\ %ls.dmp \n " , dump_path , minidump_id ) ;
return succeeded ;
}
2013-05-18 01:46:46 +02:00
printf ( " Wrote minidump to: %ls \\ %ls.dmp \n " , dump_path , minidump_id ) ;
2016-01-13 17:49:19 +01:00
2016-01-14 15:18:01 +01:00
sprintf ( dumpStoragePath , " %ls \\ %ls.dmp.txt " , dump_path , minidump_id ) ;
2016-01-13 17:49:19 +01:00
2016-01-14 14:10:57 +01:00
FILE * extra = fopen ( dumpStoragePath , " wb " ) ;
2016-01-13 17:49:19 +01:00
if ( ! extra ) {
printf ( " Failed to open metadata file! \n " ) ;
return succeeded ;
}
2016-08-22 16:06:14 +02:00
fprintf ( extra , " -------- CONFIG BEGIN -------- " ) ;
fprintf ( extra , " \n Map=%s " , crashMap ) ;
fprintf ( extra , " \n GamePath=%s " , crashGamePath ) ;
fprintf ( extra , " \n CommandLine=%s " , crashCommandLine ) ;
fprintf ( extra , " \n SourceModPath=%s " , crashSourceModPath ) ;
fprintf ( extra , " \n GameDirectory=%s " , crashGameDirectory ) ;
2016-09-11 13:36:07 +02:00
fprintf ( extra , " \n ExtensionVersion=%s " , SM_VERSION ) ;
fprintf ( extra , " \n ExtensionBuild=%s " , SM_BUILD_UNIQUEID ) ;
2016-08-22 16:06:14 +02:00
fprintf ( extra , " %s " , steamInf ) ;
fprintf ( extra , " \n -------- CONFIG END -------- \n " ) ;
2017-09-13 13:23:25 +02:00
if ( GetSpew | | GetSpewFastcall ) {
if ( GetSpew ) {
2017-09-12 22:10:10 +02:00
GetSpew ( spewBuffer , sizeof ( spewBuffer ) ) ;
2017-09-13 13:23:25 +02:00
} else if ( GetSpewFastcall ) {
GetSpewFastcall ( spewBuffer , sizeof ( spewBuffer ) ) ;
}
2017-09-12 22:10:10 +02:00
2016-08-22 16:06:14 +02:00
if ( strlen ( spewBuffer ) > 0 ) {
fprintf ( extra , " -------- CONSOLE HISTORY BEGIN -------- \n %s-------- CONSOLE HISTORY END -------- \n " , spewBuffer ) ;
}
2016-01-13 17:49:19 +01:00
}
2017-09-13 13:23:25 +02:00
fclose ( extra ) ;
2016-01-13 17:49:19 +01:00
2013-05-18 01:46:46 +02:00
return succeeded ;
}
2018-07-21 02:29:45 +02:00
2014-11-08 02:45:19 +01:00
# else
# error Bad platform.
2013-05-18 01:46:46 +02:00
# endif
2013-05-16 08:18:31 +02:00
2018-07-21 02:29:45 +02:00
class ClogInhibitor
2013-05-16 08:18:31 +02:00
{
2018-07-21 02:29:45 +02:00
std : : streambuf * saved_clog = nullptr ;
2013-05-16 08:18:31 +02:00
2018-07-21 02:29:45 +02:00
public :
ClogInhibitor ( ) {
saved_clog = std : : clog . rdbuf ( ) ;
std : : clog . rdbuf ( nullptr ) ;
2013-05-16 08:18:31 +02:00
}
2015-10-23 13:11:16 +02:00
2018-07-21 02:29:45 +02:00
~ ClogInhibitor ( ) {
std : : clog . rdbuf ( saved_clog ) ;
2015-10-23 13:11:16 +02:00
}
2018-07-21 02:29:45 +02:00
} ;
2013-05-16 08:18:31 +02:00
2016-01-14 12:18:10 +01:00
class UploadThread : public IThread
2013-05-16 08:18:31 +02:00
{
2016-01-14 12:18:10 +01:00
void RunThread ( IThreadHandle * pHandle ) {
rootconsole - > ConsolePrint ( " Accelerator upload thread started. " ) ;
2016-01-13 18:37:20 +01:00
2016-01-14 12:18:10 +01:00
FILE * log = fopen ( logPath , " a " ) ;
if ( ! log ) {
g_pSM - > LogError ( myself , " Failed to open Accelerator log file: %s " , logPath ) ;
}
2013-05-16 08:18:31 +02:00
2016-01-14 12:18:10 +01:00
IDirectory * dumps = libsys - > OpenDirectory ( dumpStoragePath ) ;
2013-05-16 08:18:31 +02:00
2016-01-14 12:18:10 +01:00
int count = 0 ;
int failed = 0 ;
char path [ 512 ] ;
char response [ 512 ] ;
while ( dumps - > MoreFiles ( ) ) {
if ( ! dumps - > IsEntryFile ( ) ) {
dumps - > NextEntry ( ) ;
continue ;
}
2015-10-22 18:18:17 +02:00
2016-01-14 12:18:10 +01:00
const char * name = dumps - > GetEntryName ( ) ;
int namelen = strlen ( name ) ;
if ( namelen < 4 | | strcmp ( & name [ namelen - 4 ] , " .dmp " ) ! = 0 ) {
dumps - > NextEntry ( ) ;
continue ;
}
g_pSM - > Format ( path , sizeof ( path ) , " %s/%s " , dumpStoragePath , name ) ;
2018-07-21 02:29:45 +02:00
// TODO: Check the return value.
bool wantsUpload = PresubmitCrashDump ( path ) ;
2016-01-14 12:18:10 +01:00
bool uploaded = UploadAndDeleteCrashDump ( path , response , sizeof ( response ) ) ;
if ( uploaded ) {
count + + ;
g_pSM - > LogError ( myself , " Accelerator uploaded crash dump: %s " , response ) ;
if ( log ) fprintf ( log , " Uploaded crash dump: %s \n " , response ) ;
} else {
failed + + ;
g_pSM - > LogError ( myself , " Accelerator failed to upload crash dump: %s " , response ) ;
if ( log ) fprintf ( log , " Failed to upload crash dump: %s \n " , response ) ;
}
2015-10-22 18:18:17 +02:00
dumps - > NextEntry ( ) ;
}
2016-01-14 12:18:10 +01:00
libsys - > CloseDirectory ( dumps ) ;
if ( log ) {
fclose ( log ) ;
2014-11-08 02:45:19 +01:00
}
2013-05-18 01:46:46 +02:00
2016-01-14 12:18:10 +01:00
rootconsole - > ConsolePrint ( " Accelerator upload thread finished. (%d uploaded, %d failed) " , count , failed ) ;
2013-05-16 08:18:31 +02:00
}
2016-01-14 12:18:10 +01:00
void OnTerminate ( IThreadHandle * pHandle , bool cancel ) {
rootconsole - > ConsolePrint ( " Accelerator upload thread terminated. (canceled = %s) " , ( cancel ? " true " : " false " ) ) ;
2014-11-08 02:45:19 +01:00
}
2013-05-16 08:18:31 +02:00
2018-07-21 02:29:45 +02:00
bool PresubmitCrashDump ( const char * path ) {
google_breakpad : : ProcessState processState ;
google_breakpad : : ProcessResult processResult ;
google_breakpad : : MinidumpProcessor minidumpProcessor ( nullptr , nullptr ) ;
{
ClogInhibitor clogInhibitor ;
processResult = minidumpProcessor . Process ( path , & processState ) ;
}
if ( processResult ! = google_breakpad : : PROCESS_OK ) {
return false ;
}
int requestingThread = processState . requesting_thread ( ) ;
if ( requestingThread = = - 1 ) {
requestingThread = 0 ;
}
const google_breakpad : : CallStack * stack = processState . threads ( ) - > at ( requestingThread ) ;
if ( ! stack ) {
return false ;
}
int frameCount = stack - > frames ( ) - > size ( ) ;
/*if (frameCount > 10) {
frameCount = 10 ;
} */
std : : ostringstream summaryStream ;
summaryStream < < 1 < < " | " < < processState . crashed ( ) < < " | " < < processState . crash_reason ( ) < < " | " < < std : : hex < < processState . crash_address ( ) < < std : : dec < < " | " < < requestingThread ;
std : : map < const google_breakpad : : CodeModule * , unsigned int > moduleMap ;
unsigned int moduleCount = processState . modules ( ) - > module_count ( ) ;
for ( unsigned int moduleIndex = 0 ; moduleIndex < moduleCount ; + + moduleIndex ) {
auto module = processState . modules ( ) - > GetModuleAtIndex ( moduleIndex ) ;
moduleMap [ module ] = moduleIndex ;
auto debugFile = google_breakpad : : PathnameStripper : : File ( module - > debug_file ( ) ) ;
auto debugIdentifier = module - > debug_identifier ( ) ;
summaryStream < < " |M| " < < debugFile < < " | " < < debugIdentifier ;
}
for ( int frameIndex = 0 ; frameIndex < frameCount ; + + frameIndex ) {
auto frame = stack - > frames ( ) - > at ( frameIndex ) ;
int moduleIndex = - 1 ;
auto moduleOffset = frame - > ReturnAddress ( ) ;
if ( frame - > module ) {
moduleIndex = moduleMap [ frame - > module ] ;
moduleOffset - = frame - > module - > base_address ( ) ;
}
summaryStream < < " |F| " < < moduleIndex < < " | " < < std : : hex < < moduleOffset < < std : : dec ;
}
auto summaryLine = summaryStream . str ( ) ;
// printf("%s\n", summaryLine.c_str());
IWebForm * form = webternet - > CreateForm ( ) ;
const char * minidumpAccount = g_pSM - > GetCoreConfigValue ( " MinidumpAccount " ) ;
if ( minidumpAccount ) form - > AddString ( " UserID " , minidumpAccount ) ;
form - > AddString ( " CrashSignature " , summaryLine . c_str ( ) ) ;
MemoryDownloader data ;
IWebTransfer * xfer = webternet - > CreateSession ( ) ;
xfer - > SetFailOnHTTPError ( true ) ;
const char * minidumpUrl = g_pSM - > GetCoreConfigValue ( " MinidumpUrl " ) ;
if ( ! minidumpUrl ) minidumpUrl = " http://crash.limetech.org/submit " ;
bool uploaded = xfer - > PostAndDownload ( minidumpUrl , form , & data , NULL ) ;
if ( ! uploaded ) {
printf ( " >>> Presubmit failed: %s (%d) \n " , xfer - > LastErrorMessage ( ) , xfer - > LastErrorCode ( ) ) ;
return false ;
}
int responseSize = data . GetSize ( ) ;
char * response = new char [ responseSize + 1 ] ;
strncpy ( response , data . GetBuffer ( ) , responseSize + 1 ) ;
response [ responseSize ] = ' \0 ' ;
printf ( " >>> Presubmit complete: %s \n " , response ) ;
if ( responseSize < 2 ) {
printf ( " >>> Response too short \n " ) ;
delete [ ] response ;
return false ;
}
if ( response [ 0 ] = = ' E ' ) {
printf ( " >>> Presubmit error: %s \n " , & response [ 2 ] ) ;
delete [ ] response ;
return false ;
}
bool submitCrash = ( response [ 0 ] = = ' Y ' ) ;
if ( response [ 1 ] ! = ' | ' ) {
printf ( " >>> Response delimiter missing \n " ) ;
delete [ ] response ;
return false ;
}
unsigned int responseCount = responseSize - 2 ;
if ( responseCount ! = moduleCount ) {
printf ( " >>> Response module list doesn't match sent list (%d != %d) \n " , responseCount , moduleCount ) ;
delete [ ] response ;
return false ;
}
# if defined _LINUX
for ( unsigned int moduleIndex = 0 ; moduleIndex < moduleCount ; + + moduleIndex ) {
bool submitModule = ( response [ 2 + moduleIndex ] = = ' Y ' ) ;
if ( ! submitModule ) {
continue ;
}
auto module = processState . modules ( ) - > GetModuleAtIndex ( moduleIndex ) ;
auto debugFile = module - > debug_file ( ) ;
if ( debugFile [ 0 ] ! = ' / ' ) {
continue ;
}
printf ( " >>> Submitting %s \n " , debugFile . c_str ( ) ) ;
auto debugFileDir = google_breakpad : : DirName ( debugFile ) ;
std : : vector < string > debug_dirs {
debugFileDir ,
} ;
std : : ostringstream outputStream ;
google_breakpad : : DumpOptions options ( ALL_SYMBOL_DATA , true ) ;
{
StderrInhibitor stdrrInhibitor ;
if ( ! WriteSymbolFile ( debugFile , debug_dirs , options , outputStream ) ) {
outputStream . str ( " " ) ;
outputStream . clear ( ) ;
// Try again without debug dirs.
if ( ! WriteSymbolFile ( debugFile , { } , options , outputStream ) ) {
// TODO: Something.
continue ;
}
}
}
auto output = outputStream . str ( ) ;
// output = output.substr(0, output.find("\n"));
// printf(">>> %s\n", output.c_str());
IWebForm * symbolForm = webternet - > CreateForm ( ) ;
symbolForm - > AddString ( " symbol_file " , output . c_str ( ) ) ;
MemoryDownloader symbolData ;
IWebTransfer * symbolXfer = webternet - > CreateSession ( ) ;
xfer - > SetFailOnHTTPError ( true ) ;
const char * symbolUrl = g_pSM - > GetCoreConfigValue ( " MinidumpSymbolUrl " ) ;
if ( ! symbolUrl ) symbolUrl = " http://crash.limetech.org/symbols/submit " ;
bool symbolUploaded = symbolXfer - > PostAndDownload ( symbolUrl , symbolForm , & symbolData , NULL ) ;
if ( ! symbolUploaded ) {
printf ( " >>> Symbol upload failed: %s (%d) \n " , symbolXfer - > LastErrorMessage ( ) , symbolXfer - > LastErrorCode ( ) ) ;
continue ;
}
int symbolResponseSize = symbolData . GetSize ( ) ;
char * symbolResponse = new char [ symbolResponseSize + 1 ] ;
strncpy ( symbolResponse , symbolData . GetBuffer ( ) , symbolResponseSize + 1 ) ;
do {
symbolResponse [ symbolResponseSize ] = ' \0 ' ;
} while ( symbolResponse [ - - symbolResponseSize ] = = ' \n ' ) ;
printf ( " >>> Symbol upload complete: %s \n " , symbolResponse ) ;
delete [ ] symbolResponse ;
}
# else
printf ( " >>> Symbol submission not available on this platform \n " ) ;
# endif
delete [ ] response ;
return submitCrash ;
}
bool UploadAndDeleteCrashDump ( const char * path , char * response , int maxlen ) {
IWebForm * form = webternet - > CreateForm ( ) ;
const char * minidumpAccount = g_pSM - > GetCoreConfigValue ( " MinidumpAccount " ) ;
if ( minidumpAccount ) form - > AddString ( " UserID " , minidumpAccount ) ;
form - > AddString ( " GameDirectory " , g_pSM - > GetGameFolderName ( ) ) ;
form - > AddString ( " ExtensionVersion " , SMEXT_CONF_VERSION ) ;
form - > AddFile ( " upload_file_minidump " , path ) ;
char metapath [ 512 ] ;
g_pSM - > Format ( metapath , sizeof ( metapath ) , " %s.txt " , path ) ;
if ( libsys - > PathExists ( metapath ) ) {
form - > AddFile ( " upload_file_metadata " , metapath ) ;
}
MemoryDownloader data ;
IWebTransfer * xfer = webternet - > CreateSession ( ) ;
xfer - > SetFailOnHTTPError ( true ) ;
const char * minidumpUrl = g_pSM - > GetCoreConfigValue ( " MinidumpUrl " ) ;
if ( ! minidumpUrl ) minidumpUrl = " http://crash.limetech.org/submit " ;
bool uploaded = xfer - > PostAndDownload ( minidumpUrl , form , & data , NULL ) ;
if ( response ) {
if ( uploaded ) {
int responseSize = data . GetSize ( ) ;
if ( responseSize > = maxlen ) responseSize = maxlen - 1 ;
strncpy ( response , data . GetBuffer ( ) , responseSize ) ;
response [ responseSize ] = ' \0 ' ;
} else {
g_pSM - > Format ( response , maxlen , " %s (%d) " , xfer - > LastErrorMessage ( ) , xfer - > LastErrorCode ( ) ) ;
}
}
if ( libsys - > PathExists ( metapath ) ) {
unlink ( metapath ) ;
}
unlink ( path ) ;
return uploaded ;
}
2016-01-13 18:37:20 +01:00
} uploadThread ;
2016-09-11 13:25:06 +02:00
class VFuncEmptyClass { } ;
const char * GetCmdLine ( )
{
static int getCmdLineOffset = 0 ;
if ( getCmdLineOffset = = 0 ) {
if ( ! gameconfig | | ! gameconfig - > GetOffset ( " GetCmdLine " , & getCmdLineOffset ) ) {
return " " ;
}
if ( getCmdLineOffset = = 0 ) {
return " " ;
}
}
void * cmdline = gamehelpers - > GetValveCommandLine ( ) ;
void * * vtable = * ( void * * * ) cmdline ;
void * vfunc = vtable [ getCmdLineOffset ] ;
union {
const char * ( VFuncEmptyClass : : * mfpnew ) ( ) ;
# ifndef WIN32
struct {
void * addr ;
intptr_t adjustor ;
} s ;
} u ;
u . s . addr = vfunc ;
u . s . adjustor = 0 ;
# else
void * addr ;
} u ;
u . addr = vfunc ;
# endif
return ( const char * ) ( reinterpret_cast < VFuncEmptyClass * > ( cmdline ) - > * u . mfpnew ) ( ) ;
}
2013-05-16 08:18:31 +02:00
bool Accelerator : : SDK_OnLoad ( char * error , size_t maxlength , bool late )
{
sharesys - > AddDependency ( myself , " webternet.ext " , true , true ) ;
SM_GET_IFACE ( WEBTERNET , webternet ) ;
2016-01-14 12:18:10 +01:00
g_pSM - > BuildPath ( Path_SM , dumpStoragePath , sizeof ( dumpStoragePath ) , " data/dumps " ) ;
2013-05-16 08:18:31 +02:00
2016-01-14 12:18:10 +01:00
if ( ! libsys - > IsPathDirectory ( dumpStoragePath ) )
2013-05-17 15:48:52 +02:00
{
2016-01-14 12:18:10 +01:00
if ( ! libsys - > CreateFolder ( dumpStoragePath ) )
2013-05-17 15:48:52 +02:00
{
if ( error )
2016-01-14 12:18:10 +01:00
g_pSM - > Format ( error , maxlength , " %s didn't exist and we couldn't create it :( " , dumpStoragePath ) ;
2013-05-17 15:48:52 +02:00
return false ;
}
}
2016-01-14 12:18:10 +01:00
g_pSM - > BuildPath ( Path_SM , logPath , sizeof ( logPath ) , " logs/accelerator.log " ) ;
2016-01-13 18:37:20 +01:00
threader - > MakeThread ( & uploadThread ) ;
2017-09-13 13:23:25 +02:00
do {
char gameconfigError [ 256 ] ;
if ( ! gameconfs - > LoadGameConfigFile ( " accelerator.games " , & gameconfig , gameconfigError , sizeof ( gameconfigError ) ) ) {
smutils - > LogMessage ( myself , " WARNING: Failed to load gamedata file, console output and command line will not be included in crash reports: %s " , gameconfigError ) ;
break ;
}
bool useFastcall = false ;
2016-01-13 17:25:03 +01:00
2017-09-12 22:10:10 +02:00
# if defined _WINDOWS
2017-09-13 13:23:25 +02:00
const char * fastcall = gameconfig - > GetKeyValue ( " UseFastcall " ) ;
if ( fastcall & & strcmp ( fastcall , " yes " ) = = 0 ) {
useFastcall = true ;
}
if ( useFastcall & & ! gameconfig - > GetMemSig ( " GetSpewFastcall " , ( void * * ) & GetSpewFastcall ) ) {
smutils - > LogMessage ( myself , " WARNING: GetSpewFastcall not found in gamedata, console output will not be included in crash reports. " ) ;
break ;
}
# endif
if ( ! useFastcall & & ! gameconfig - > GetMemSig ( " GetSpew " , ( void * * ) & GetSpew ) ) {
smutils - > LogMessage ( myself , " WARNING: GetSpew not found in gamedata, console output will not be included in crash reports. " ) ;
break ;
}
if ( ! GetSpew
# if defined _WINDOWS
& & ! GetSpewFastcall
2017-09-12 22:10:10 +02:00
# endif
2017-09-13 13:23:25 +02:00
) {
smutils - > LogMessage ( myself , " WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports. " ) ;
break ;
}
} while ( false ) ;
2017-09-12 22:10:10 +02:00
2013-05-18 01:46:46 +02:00
# if defined _LINUX
2016-01-14 12:18:10 +01:00
google_breakpad : : MinidumpDescriptor descriptor ( dumpStoragePath ) ;
2013-05-16 08:18:31 +02:00
handler = new google_breakpad : : ExceptionHandler ( descriptor , NULL , dumpCallback , NULL , true , - 1 ) ;
struct sigaction oact ;
sigaction ( SIGSEGV , NULL , & oact ) ;
SignalHandler = oact . sa_sigaction ;
2016-08-22 16:06:14 +02:00
2013-05-16 08:18:31 +02:00
g_pSM - > AddGameFrameHook ( OnGameFrame ) ;
2013-05-18 01:46:46 +02:00
# elif defined _WINDOWS
2016-01-14 12:18:10 +01:00
wchar_t * buf = new wchar_t [ sizeof ( dumpStoragePath ) ] ;
size_t num_chars = mbstowcs ( buf , dumpStoragePath , sizeof ( dumpStoragePath ) ) ;
2013-05-18 01:46:46 +02:00
handler = new google_breakpad : : ExceptionHandler ( std : : wstring ( buf , num_chars ) , NULL , dumpCallback , NULL , google_breakpad : : ExceptionHandler : : HANDLER_ALL ) ;
2014-11-08 03:00:40 +01:00
vectoredHandler = AddVectoredExceptionHandler ( 0 , BreakpadVectoredHandler ) ;
2016-08-22 16:06:14 +02:00
2013-05-18 01:46:46 +02:00
delete buf ;
2014-11-08 02:45:19 +01:00
# else
# error Bad platform.
2013-05-18 01:46:46 +02:00
# endif
2016-01-14 17:53:16 +01:00
#if 0
2015-10-22 18:18:17 +02:00
IPluginIterator * i = plsys - > GetPluginIterator ( ) ;
while ( i - > MorePlugins ( ) ) {
IPlugin * p = i - > GetPlugin ( ) ;
const sm_plugininfo_t * pmi = p - > GetPublicInfo ( ) ;
PluginInfo * pi = & plugins [ plugin_count + + ] ;
pi - > serial = p - > GetSerial ( ) ;
pi - > status = p - > GetStatus ( ) ;
strncpy ( pi - > filename , p - > GetFilename ( ) , sizeof ( pi - > filename ) - 1 ) ;
strncpy ( pi - > name , pmi - > name , sizeof ( pi - > name ) - 1 ) ;
strncpy ( pi - > author , pmi - > author , sizeof ( pi - > author ) - 1 ) ;
strncpy ( pi - > description , pmi - > description , sizeof ( pi - > description ) - 1 ) ;
strncpy ( pi - > version , pmi - > version , sizeof ( pi - > version ) - 1 ) ;
strncpy ( pi - > url , pmi - > url , sizeof ( pi - > url ) - 1 ) ;
i - > NextPlugin ( ) ;
}
delete i ;
2016-01-14 17:53:16 +01:00
# endif
2015-10-22 18:18:17 +02:00
2016-01-25 16:38:43 +01:00
strncpy ( crashGamePath , g_pSM - > GetGamePath ( ) , sizeof ( crashGamePath ) - 1 ) ;
strncpy ( crashCommandLine , GetCmdLine ( ) , sizeof ( crashCommandLine ) - 1 ) ;
strncpy ( crashSourceModPath , g_pSM - > GetSourceModPath ( ) , sizeof ( crashSourceModPath ) - 1 ) ;
strncpy ( crashGameDirectory , g_pSM - > GetGameFolderName ( ) , sizeof ( crashGameDirectory ) - 1 ) ;
2016-08-22 16:06:14 +02:00
char steamInfPath [ 512 ] ;
g_pSM - > BuildPath ( Path_Game , steamInfPath , sizeof ( steamInfPath ) , " steam.inf " ) ;
2016-09-11 13:30:20 +02:00
FILE * steamInfFile = fopen ( steamInfPath , " rb " ) ;
if ( steamInfFile ) {
2018-01-07 23:20:55 +01:00
char steamInfTemp [ 1024 ] = { 0 } ;
2016-09-11 13:30:20 +02:00
fread ( steamInfTemp , sizeof ( char ) , sizeof ( steamInfTemp ) - 1 , steamInfFile ) ;
2016-08-23 11:37:41 +02:00
2016-09-11 13:30:20 +02:00
fclose ( steamInfFile ) ;
2016-08-22 16:06:14 +02:00
2018-01-07 23:20:55 +01:00
unsigned commentChars = 0 ;
unsigned valueChars = 0 ;
2016-09-11 13:30:20 +02:00
unsigned source = 0 ;
strcpy ( steamInf , " \n Steam_ " ) ;
unsigned target = 7 ; // strlen("\nSteam_");
while ( true ) {
2016-08-22 16:06:14 +02:00
if ( steamInfTemp [ source ] = = ' \0 ' ) {
2016-09-11 13:30:20 +02:00
source + + ;
2016-08-22 16:06:14 +02:00
break ;
}
2018-01-07 23:20:55 +01:00
if ( steamInfTemp [ source ] = = ' / ' ) {
source + + ;
commentChars + + ;
continue ;
}
if ( commentChars = = 1 ) {
commentChars = 0 ;
steamInf [ target + + ] = ' / ' ;
valueChars + + ;
}
2016-09-11 13:30:20 +02:00
if ( steamInfTemp [ source ] = = ' \r ' ) {
source + + ;
continue ;
}
if ( steamInfTemp [ source ] = = ' \n ' ) {
2018-01-07 23:20:55 +01:00
commentChars = 0 ;
2016-09-11 13:30:20 +02:00
source + + ;
if ( steamInfTemp [ source ] = = ' \0 ' ) {
break ;
}
2018-01-07 23:20:55 +01:00
if ( valueChars > 0 ) {
valueChars = 0 ;
strcpy ( & steamInf [ target ] , " \n Steam_ " ) ;
target + = 7 ;
}
continue ;
}
if ( commentChars > = 2 ) {
source + + ;
2016-09-11 13:30:20 +02:00
continue ;
}
steamInf [ target + + ] = steamInfTemp [ source + + ] ;
2018-01-07 23:20:55 +01:00
valueChars + + ;
2016-08-22 16:06:14 +02:00
}
}
2016-09-11 13:25:06 +02:00
if ( late ) {
this - > OnCoreMapStart ( NULL , 0 , 0 ) ;
}
return true ;
}
void Accelerator : : SDK_OnUnload ( )
{
# if defined _LINUX
g_pSM - > RemoveGameFrameHook ( OnGameFrame ) ;
# elif defined _WINDOWS
if ( vectoredHandler ) {
RemoveVectoredExceptionHandler ( vectoredHandler ) ;
}
# else
# error Bad platform.
# endif
delete handler ;
}
void Accelerator : : OnCoreMapStart ( edict_t * pEdictList , int edictCount , int clientMax )
{
strncpy ( crashMap , gamehelpers - > GetCurrentMap ( ) , sizeof ( crashMap ) - 1 ) ;
2016-08-22 16:06:14 +02:00
}