2018-07-21 02:31:51 +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 .
*
* 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"
2019-07-27 00:04:27 +02:00
# ifndef PLATFORM_ARCH_FOLDER
# define PLATFORM_ARCH_FOLDER ""
# endif
# include <sp_vm_api.h>
2018-07-21 02:31:51 +02:00
# include <IWebternet.h>
# include "MemoryDownloader.h"
# if defined _LINUX
# include "client/linux/handler/exception_handler.h"
# include "common/linux/linux_libc_support.h"
# include "third_party/lss/linux_syscall_support.h"
# include "common/linux/dump_symbols.h"
# include "common/path_helper.h"
# include <signal.h>
# include <dirent.h>
# include <unistd.h>
# 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 ) ;
}
} ;
2019-04-28 21:09:35 +02:00
// Taken from https://hg.mozilla.org/mozilla-central/file/3eb7623b5e63b37823d5e9c562d56e586604c823/build/unix/stdc%2B%2Bcompat/stdc%2B%2Bcompat.cpp
extern " C " void __attribute__ ( ( weak ) ) __cxa_throw_bad_array_new_length ( ) {
2019-07-20 02:51:47 +02:00
abort ( ) ;
2019-04-28 21:09:35 +02:00
}
namespace std {
2019-07-20 02:51:47 +02:00
/* We shouldn't be throwing exceptions at all, but it sadly turns out
we call STL ( inline ) functions that do . */
void __attribute__ ( ( weak ) ) __throw_out_of_range_fmt ( char const * fmt , . . . ) {
va_list ap ;
char buf [ 1024 ] ; // That should be big enough.
va_start ( ap , fmt ) ;
vsnprintf ( buf , sizeof ( buf ) , fmt , ap ) ;
buf [ sizeof ( buf ) - 1 ] = 0 ;
va_end ( ap ) ;
__throw_range_error ( buf ) ;
}
2019-04-28 21:09:35 +02:00
} // namespace std
// Updated versions of the SM ones for C++14
void operator delete ( void * ptr , size_t sz ) {
free ( ptr ) ;
}
void operator delete [ ] ( void * ptr , size_t sz ) {
free ( ptr ) ;
}
2018-07-21 02:31:51 +02:00
# elif defined _WINDOWS
# define _STDINT // ~.~
# include "client/windows/handler/exception_handler.h"
# else
# error Bad platform.
# endif
# 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>
Accelerator g_accelerator ;
SMEXT_LINK ( & g_accelerator ) ;
IWebternet * webternet ;
IGameConfig * gameconfig ;
typedef void ( * GetSpew_t ) ( char * buffer , unsigned int length ) ;
GetSpew_t GetSpew ;
# if defined _WINDOWS
typedef void ( __fastcall * GetSpewFastcall_t ) ( char * buffer , unsigned int length ) ;
GetSpewFastcall_t GetSpewFastcall ;
# endif
char spewBuffer [ 65536 ] ; // Hi.
char crashMap [ 256 ] ;
char crashGamePath [ 512 ] ;
char crashCommandLine [ 1024 ] ;
char crashSourceModPath [ 512 ] ;
char crashGameDirectory [ 256 ] ;
2019-07-27 00:04:27 +02:00
char crashSourceModVersion [ 32 ] ;
2018-07-21 02:31:51 +02:00
char steamInf [ 1024 ] ;
char dumpStoragePath [ 512 ] ;
char logPath [ 512 ] ;
google_breakpad : : ExceptionHandler * handler = NULL ;
# if defined _LINUX
2019-07-20 02:51:47 +02:00
void terminateHandler ( )
{
const char * msg = " missing exception " ;
std : : exception_ptr pEx = std : : current_exception ( ) ;
if ( pEx ) {
try {
std : : rethrow_exception ( pEx ) ;
} catch ( const std : : exception & e ) {
msg = strdup ( e . what ( ) ) ;
} catch ( . . . ) {
msg = " unknown exception " ;
}
}
size_t msgLength = strlen ( msg ) + 2 ;
volatile char * volatile msgForCrashDumps = ( char * ) alloca ( msgLength ) ;
strcpy ( ( char * ) msgForCrashDumps + 1 , msg ) ;
abort ( ) ;
}
2018-07-21 02:31:51 +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 )
{
//printf("Wrote minidump to: %s\n", descriptor.path());
if ( succeeded ) {
sys_write ( STDOUT_FILENO , " Wrote minidump to: " , 19 ) ;
} else {
sys_write ( STDOUT_FILENO , " Failed to write minidump to: " , 29 ) ;
}
sys_write ( STDOUT_FILENO , descriptor . path ( ) , my_strlen ( descriptor . path ( ) ) ) ;
sys_write ( STDOUT_FILENO , " \n " , 1 ) ;
if ( ! succeeded ) {
return succeeded ;
}
my_strlcpy ( dumpStoragePath , descriptor . path ( ) , sizeof ( dumpStoragePath ) ) ;
my_strlcat ( dumpStoragePath , " .txt " , sizeof ( dumpStoragePath ) ) ;
int extra = sys_open ( dumpStoragePath , O_WRONLY | O_CREAT , S_IRUSR | S_IWUSR ) ;
if ( extra = = - 1 ) {
sys_write ( STDOUT_FILENO , " Failed to open metadata file! \n " , 30 ) ;
return succeeded ;
}
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 ) ) ;
2019-07-27 00:04:27 +02:00
if ( crashSourceModVersion [ 0 ] ) {
sys_write ( extra , " \n SourceModVersion= " , 18 ) ;
sys_write ( extra , crashSourceModVersion , my_strlen ( crashSourceModVersion ) ) ;
}
2018-07-21 02:31:51 +02:00
sys_write ( extra , " \n ExtensionVersion= " , 18 ) ;
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 ) ) ;
sys_write ( extra , steamInf , my_strlen ( steamInf ) ) ;
sys_write ( extra , " \n -------- CONFIG END -------- \n " , 30 ) ;
if ( GetSpew ) {
GetSpew ( spewBuffer , sizeof ( spewBuffer ) ) ;
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 ) ;
}
}
sys_close ( extra ) ;
return succeeded ;
}
void OnGameFrame ( bool simulating )
{
2019-07-20 02:51:47 +02:00
std : : set_terminate ( terminateHandler ) ;
2018-07-21 02:31:51 +02:00
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 ] ) ;
}
act . sa_sigaction = SignalHandler ;
act . sa_flags = SA_ONSTACK | SA_SIGINFO ;
for ( int i = 0 ; i < kNumHandledSignals ; + + i ) {
sigaction ( kExceptionSignals [ i ] , & act , NULL ) ;
}
}
# elif defined _WINDOWS
void * vectoredHandler = NULL ;
LONG CALLBACK BreakpadVectoredHandler ( _In_ PEXCEPTION_POINTERS ExceptionInfo )
{
switch ( ExceptionInfo - > ExceptionRecord - > ExceptionCode )
{
case EXCEPTION_ACCESS_VIOLATION :
case EXCEPTION_INVALID_HANDLE :
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED :
case EXCEPTION_DATATYPE_MISALIGNMENT :
case EXCEPTION_ILLEGAL_INSTRUCTION :
case EXCEPTION_INT_DIVIDE_BY_ZERO :
case EXCEPTION_STACK_OVERFLOW :
case 0xC0000409 : // STATUS_STACK_BUFFER_OVERRUN
case 0xC0000374 : // STATUS_HEAP_CORRUPTION
break ;
case 0 : // Valve use this for Sys_Error.
if ( ( ExceptionInfo - > ExceptionRecord - > ExceptionFlags & EXCEPTION_NONCONTINUABLE ) = = 0 )
return EXCEPTION_CONTINUE_SEARCH ;
break ;
default :
return EXCEPTION_CONTINUE_SEARCH ;
}
if ( handler - > WriteMinidumpForException ( ExceptionInfo ) )
{
// Stop the handler thread from deadlocking us.
delete handler ;
// Stop Valve's handler being called.
ExceptionInfo - > ExceptionRecord - > ExceptionCode = EXCEPTION_BREAKPOINT ;
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 )
{
if ( ! succeeded ) {
printf ( " Failed to write minidump to: %ls \\ %ls.dmp \n " , dump_path , minidump_id ) ;
return succeeded ;
}
printf ( " Wrote minidump to: %ls \\ %ls.dmp \n " , dump_path , minidump_id ) ;
sprintf ( dumpStoragePath , " %ls \\ %ls.dmp.txt " , dump_path , minidump_id ) ;
FILE * extra = fopen ( dumpStoragePath , " wb " ) ;
if ( ! extra ) {
printf ( " Failed to open metadata file! \n " ) ;
return succeeded ;
}
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 ) ;
fprintf ( extra , " \n ExtensionVersion=%s " , SM_VERSION ) ;
fprintf ( extra , " \n ExtensionBuild=%s " , SM_BUILD_UNIQUEID ) ;
fprintf ( extra , " %s " , steamInf ) ;
fprintf ( extra , " \n -------- CONFIG END -------- \n " ) ;
if ( GetSpew | | GetSpewFastcall ) {
if ( GetSpew ) {
GetSpew ( spewBuffer , sizeof ( spewBuffer ) ) ;
} else if ( GetSpewFastcall ) {
GetSpewFastcall ( spewBuffer , sizeof ( spewBuffer ) ) ;
}
if ( strlen ( spewBuffer ) > 0 ) {
fprintf ( extra , " -------- CONSOLE HISTORY BEGIN -------- \n %s-------- CONSOLE HISTORY END -------- \n " , spewBuffer ) ;
}
}
fclose ( extra ) ;
return succeeded ;
}
# else
# error Bad platform.
# endif
class ClogInhibitor
{
std : : streambuf * saved_clog = nullptr ;
public :
ClogInhibitor ( ) {
saved_clog = std : : clog . rdbuf ( ) ;
std : : clog . rdbuf ( nullptr ) ;
}
~ ClogInhibitor ( ) {
std : : clog . rdbuf ( saved_clog ) ;
}
} ;
class UploadThread : public IThread
{
2019-01-05 03:02:29 +01:00
FILE * log = nullptr ;
2019-01-06 00:13:18 +01:00
char serverId [ 38 ] = " " ;
2019-01-05 03:02:29 +01:00
2018-07-21 02:31:51 +02:00
void RunThread ( IThreadHandle * pHandle ) {
rootconsole - > ConsolePrint ( " Accelerator upload thread started. " ) ;
2019-01-05 03:02:29 +01:00
log = fopen ( logPath , " a " ) ;
2018-07-21 02:31:51 +02:00
if ( ! log ) {
g_pSM - > LogError ( myself , " Failed to open Accelerator log file: %s " , logPath ) ;
}
2019-01-06 00:13:18 +01:00
char path [ 512 ] ;
g_pSM - > Format ( path , sizeof ( path ) , " %s/server-id.txt " , dumpStoragePath ) ;
FILE * serverIdFile = fopen ( path , " r " ) ;
if ( serverIdFile ) {
fread ( serverId , 1 , sizeof ( serverId ) - 1 , serverIdFile ) ;
if ( ! feof ( serverIdFile ) | | strlen ( serverId ) ! = 36 ) {
serverId [ 0 ] = ' \0 ' ;
}
fclose ( serverIdFile ) ;
}
if ( ! serverId [ 0 ] ) {
serverIdFile = fopen ( path , " w " ) ;
if ( serverIdFile ) {
g_pSM - > Format ( serverId , sizeof ( serverId ) , " %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x " ,
2019-04-28 21:09:35 +02:00
rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , 0x40 | ( ( rand ( ) % 255 ) & 0x0F ) , rand ( ) % 255 ,
0x80 | ( ( rand ( ) % 255 ) & 0x3F ) , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 , rand ( ) % 255 ) ;
2019-01-06 00:13:18 +01:00
fputs ( serverId , serverIdFile ) ;
fclose ( serverIdFile ) ;
}
}
2018-07-21 02:31:51 +02:00
IDirectory * dumps = libsys - > OpenDirectory ( dumpStoragePath ) ;
2019-01-05 03:02:29 +01:00
int skip = 0 ;
2018-07-21 02:31:51 +02:00
int count = 0 ;
int failed = 0 ;
2019-01-05 03:02:29 +01:00
char metapath [ 512 ] ;
2019-01-05 22:35:23 +01:00
char presubmitToken [ 512 ] ;
2018-07-21 02:31:51 +02:00
char response [ 512 ] ;
while ( dumps - > MoreFiles ( ) ) {
if ( ! dumps - > IsEntryFile ( ) ) {
dumps - > NextEntry ( ) ;
continue ;
}
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 ) ;
2019-01-05 03:02:29 +01:00
g_pSM - > Format ( metapath , sizeof ( metapath ) , " %s.txt " , path ) ;
2018-07-21 02:31:51 +02:00
2019-01-05 03:02:29 +01:00
if ( ! libsys - > PathExists ( metapath ) ) {
metapath [ 0 ] = ' \0 ' ;
}
2018-07-21 02:31:51 +02:00
2019-01-05 22:35:23 +01:00
presubmitToken [ 0 ] = ' \0 ' ;
2019-01-14 01:11:10 +01:00
PresubmitResponse presubmitResponse = kPRUploadCrashDumpAndMetadata ;
const char * presubmitOption = g_pSM - > GetCoreConfigValue ( " MinidumpPresubmit " ) ;
bool canPresubmit = ! presubmitOption | | ( tolower ( presubmitOption [ 0 ] ) = = ' y ' | | presubmitOption [ 0 ] = = ' 1 ' ) ;
if ( canPresubmit ) {
presubmitResponse = PresubmitCrashDump ( path , presubmitToken , sizeof ( presubmitToken ) ) ;
}
2018-07-21 02:31:51 +02:00
2019-01-05 22:35:23 +01:00
switch ( presubmitResponse ) {
case kPRLocalError :
2019-01-05 03:02:29 +01:00
failed + + ;
2019-01-05 22:35:23 +01:00
g_pSM - > LogError ( myself , " Accelerator failed to locally process crash dump " ) ;
if ( log ) fprintf ( log , " Failed to locally process crash dump " ) ;
break ;
case kPRRemoteError :
case kPRUploadCrashDumpAndMetadata :
case kPRUploadMetadataOnly :
if ( UploadCrashDump ( ( presubmitResponse = = kPRUploadMetadataOnly ) ? nullptr : path , metapath , presubmitToken , response , sizeof ( response ) ) ) {
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 ) ;
}
break ;
case kPRDontUpload :
skip + + ;
g_pSM - > LogError ( myself , " Accelerator crash dump upload skipped by server " ) ;
if ( log ) fprintf ( log , " Skipped due to server request \n " ) ;
break ;
2018-07-21 02:31:51 +02:00
}
2019-01-05 22:35:23 +01:00
if ( metapath [ 0 ] ) {
2019-01-05 03:02:29 +01:00
unlink ( metapath ) ;
}
unlink ( path ) ;
2019-01-05 22:35:23 +01:00
if ( log ) fflush ( log ) ;
2018-07-21 02:31:51 +02:00
dumps - > NextEntry ( ) ;
}
libsys - > CloseDirectory ( dumps ) ;
if ( log ) {
fclose ( log ) ;
2019-01-05 03:02:29 +01:00
log = nullptr ;
2018-07-21 02:31:51 +02:00
}
2019-01-05 03:02:29 +01:00
rootconsole - > ConsolePrint ( " Accelerator upload thread finished. (%d skipped, %d uploaded, %d failed) " , skip , count , failed ) ;
2018-07-21 02:31:51 +02:00
}
void OnTerminate ( IThreadHandle * pHandle , bool cancel ) {
rootconsole - > ConsolePrint ( " Accelerator upload thread terminated. (canceled = %s) " , ( cancel ? " true " : " false " ) ) ;
}
2018-07-21 15:06:47 +02:00
# if defined _LINUX
2019-01-05 22:35:23 +01:00
bool UploadSymbolFile ( const google_breakpad : : CodeModule * module , const char * presubmitToken ) {
2018-07-21 15:06:47 +02:00
auto debugFile = module - > debug_file ( ) ;
2019-01-12 00:42:34 +01:00
std : : string vdsoOutputPath = " " ;
if ( debugFile = = " linux-gate.so " ) {
2019-01-12 02:33:10 +01:00
FILE * auxvFile = fopen ( " /proc/self/auxv " , " rb " ) ;
if ( auxvFile ) {
char vdsoOutputPathBuffer [ 512 ] ;
g_pSM - > BuildPath ( Path_SM , vdsoOutputPathBuffer , sizeof ( vdsoOutputPathBuffer ) , " data/dumps/linux-gate.so " ) ;
vdsoOutputPath = vdsoOutputPathBuffer ;
while ( ! feof ( auxvFile ) ) {
int auxvEntryId = 0 ;
fread ( & auxvEntryId , sizeof ( auxvEntryId ) , 1 , auxvFile ) ;
long auxvEntryValue = 0 ;
fread ( & auxvEntryValue , sizeof ( auxvEntryValue ) , 1 , auxvFile ) ;
if ( auxvEntryId = = 0 ) break ;
if ( auxvEntryId ! = 33 ) continue ; // AT_SYSINFO_EHDR
Elf32_Ehdr * vdsoHdr = ( Elf32_Ehdr * ) auxvEntryValue ;
auto vdsoSize = vdsoHdr - > e_shoff + ( vdsoHdr - > e_shentsize * vdsoHdr - > e_shnum ) ;
void * vdsoBuffer = malloc ( vdsoSize ) ;
memcpy ( vdsoBuffer , vdsoHdr , vdsoSize ) ;
FILE * vdsoFile = fopen ( vdsoOutputPath . c_str ( ) , " wb " ) ;
if ( vdsoFile ) {
fwrite ( vdsoBuffer , 1 , vdsoSize , vdsoFile ) ;
fclose ( vdsoFile ) ;
debugFile = vdsoOutputPath ;
}
free ( vdsoBuffer ) ;
break ;
2019-01-12 00:42:34 +01:00
}
2019-01-12 02:33:10 +01:00
fclose ( auxvFile ) ;
2019-01-12 00:42:34 +01:00
}
}
2018-07-21 15:06:47 +02:00
if ( debugFile [ 0 ] ! = ' / ' ) {
return false ;
}
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Submitting symbols for %s \n " , debugFile . c_str ( ) ) ;
2018-07-21 15:06:47 +02:00
auto debugFileDir = google_breakpad : : DirName ( debugFile ) ;
std : : vector < string > debug_dirs {
debugFileDir ,
debugFileDir + " /.debug " ,
" /usr/lib/debug " + 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 ) ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Failed to process symbol file \n " ) ;
2018-07-21 15:06:47 +02:00
return false ;
}
}
}
auto output = outputStream . str ( ) ;
// output = output.substr(0, output.find("\n"));
// printf(">>> %s\n", output.c_str());
2019-01-12 00:42:34 +01:00
if ( debugFile = = vdsoOutputPath ) {
unlink ( vdsoOutputPath . c_str ( ) ) ;
}
2018-07-21 15:06:47 +02:00
IWebForm * form = webternet - > CreateForm ( ) ;
2019-01-05 22:35:23 +01:00
const char * minidumpAccount = g_pSM - > GetCoreConfigValue ( " MinidumpAccount " ) ;
if ( minidumpAccount & & minidumpAccount [ 0 ] ) form - > AddString ( " UserID " , minidumpAccount ) ;
form - > AddString ( " ExtensionVersion " , SMEXT_CONF_VERSION ) ;
2019-01-06 00:13:18 +01:00
form - > AddString ( " ServerID " , serverId ) ;
2019-01-05 22:35:23 +01:00
if ( presubmitToken & & presubmitToken [ 0 ] ) {
form - > AddString ( " PresubmitToken " , presubmitToken ) ;
}
2019-07-20 02:51:47 +02:00
2018-07-21 15:06:47 +02:00
form - > AddString ( " symbol_file " , output . c_str ( ) ) ;
MemoryDownloader data ;
IWebTransfer * xfer = webternet - > CreateSession ( ) ;
xfer - > SetFailOnHTTPError ( true ) ;
const char * symbolUrl = g_pSM - > GetCoreConfigValue ( " MinidumpSymbolUrl " ) ;
if ( ! symbolUrl ) symbolUrl = " http://crash.limetech.org/symbols/submit " ;
bool symbolUploaded = xfer - > PostAndDownload ( symbolUrl , form , & data , NULL ) ;
if ( ! symbolUploaded ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Symbol upload failed: %s (%d) \n " , xfer - > LastErrorMessage ( ) , xfer - > LastErrorCode ( ) ) ;
2018-07-21 15:06:47 +02:00
return false ;
}
int responseSize = data . GetSize ( ) ;
char * response = new char [ responseSize + 1 ] ;
strncpy ( response , data . GetBuffer ( ) , responseSize + 1 ) ;
response [ responseSize ] = ' \0 ' ;
while ( responseSize > 0 & & response [ responseSize - 1 ] = = ' \n ' ) {
response [ - - responseSize ] = ' \0 ' ;
}
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Symbol upload complete: %s \n " , response ) ;
2018-07-21 15:06:47 +02:00
delete [ ] response ;
return true ;
}
# endif
2019-01-05 22:35:23 +01:00
bool UploadModuleFile ( const google_breakpad : : CodeModule * module , const char * presubmitToken ) {
2019-01-05 03:02:29 +01:00
const auto & codeFile = module - > code_file ( ) ;
# ifndef WIN32
if ( codeFile [ 0 ] ! = ' / ' ) {
# else
if ( codeFile [ 1 ] ! = ' : ' ) {
# endif
return false ;
}
if ( log ) fprintf ( log , " Submitting binary for %s \n " , codeFile . c_str ( ) ) ;
IWebForm * form = webternet - > CreateForm ( ) ;
2019-01-05 22:35:23 +01:00
const char * minidumpAccount = g_pSM - > GetCoreConfigValue ( " MinidumpAccount " ) ;
if ( minidumpAccount & & minidumpAccount [ 0 ] ) form - > AddString ( " UserID " , minidumpAccount ) ;
form - > AddString ( " ExtensionVersion " , SMEXT_CONF_VERSION ) ;
2019-01-06 00:13:18 +01:00
form - > AddString ( " ServerID " , serverId ) ;
2019-01-05 22:35:23 +01:00
if ( presubmitToken & & presubmitToken [ 0 ] ) {
form - > AddString ( " PresubmitToken " , presubmitToken ) ;
}
2019-01-05 03:02:29 +01:00
form - > AddString ( " debug_identifier " , module - > debug_identifier ( ) . c_str ( ) ) ;
2019-01-05 22:35:23 +01:00
form - > AddString ( " code_identifier " , module - > code_identifier ( ) . c_str ( ) ) ;
2019-01-05 03:02:29 +01:00
form - > AddFile ( " code_file " , codeFile . c_str ( ) ) ;
MemoryDownloader data ;
IWebTransfer * xfer = webternet - > CreateSession ( ) ;
xfer - > SetFailOnHTTPError ( true ) ;
const char * binaryUrl = g_pSM - > GetCoreConfigValue ( " MinidumpBinaryUrl " ) ;
if ( ! binaryUrl ) binaryUrl = " http://crash.limetech.org/binary/submit " ;
bool binaryUploaded = xfer - > PostAndDownload ( binaryUrl , form , & data , NULL ) ;
if ( ! binaryUploaded ) {
if ( log ) fprintf ( log , " Binary upload 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 ' ;
while ( responseSize > 0 & & response [ responseSize - 1 ] = = ' \n ' ) {
response [ - - responseSize ] = ' \0 ' ;
}
if ( log ) fprintf ( log , " Binary upload complete: %s \n " , response ) ;
delete [ ] response ;
return true ;
}
enum ModuleType {
kMTUnknown ,
kMTSystem ,
kMTGame ,
kMTAddon ,
kMTExtension ,
} ;
const char * ModuleTypeCode [ 5 ] = {
" Unknown " ,
" System " ,
" Game " ,
" Addon " ,
" Extension " ,
} ;
# ifndef WIN32
# define PATH_SEP " / "
# else
# define PATH_SEP "\\"
# endif
bool PathPrefixMatches ( const std : : string & prefix , const std : : string & path ) {
# ifndef WIN32
return strncmp ( prefix . c_str ( ) , path . c_str ( ) , prefix . length ( ) ) = = 0 ;
# else
return _strnicmp ( prefix . c_str ( ) , path . c_str ( ) , prefix . length ( ) ) = = 0 ;
# endif
}
struct PathComparator {
struct compare {
bool operator ( ) ( const unsigned char & a , const unsigned char & b ) const {
# ifndef WIN32
return a < b ;
# else
return tolower ( a ) < tolower ( b ) ;
# endif
}
} ;
bool operator ( ) ( const std : : string & a , const std : : string & b ) const {
return ! std : : lexicographical_compare (
a . begin ( ) , a . end ( ) ,
b . begin ( ) , b . end ( ) ,
compare ( ) ) ;
} ;
} ;
std : : map < std : : string , ModuleType , PathComparator > modulePathMap ;
bool InitModuleClassificationMap ( const std : : string & base ) {
if ( ! modulePathMap . empty ( ) ) {
2019-01-05 22:35:23 +01:00
modulePathMap . clear ( ) ;
2019-01-05 03:02:29 +01:00
}
modulePathMap [ base ] = kMTGame ;
modulePathMap [ std : : string ( crashGamePath ) + PATH_SEP " addons " PATH_SEP ] = kMTAddon ;
modulePathMap [ std : : string ( crashSourceModPath ) + PATH_SEP " extensions " PATH_SEP ] = kMTExtension ;
return true ;
}
ModuleType ClassifyModule ( const google_breakpad : : CodeModule * module ) {
if ( modulePathMap . empty ( ) ) {
return kMTUnknown ;
}
const auto & codeFile = module - > code_file ( ) ;
# ifndef WIN32
2019-01-12 00:42:34 +01:00
if ( codeFile = = " linux-gate.so " ) {
return kMTSystem ;
}
2019-01-05 03:02:29 +01:00
if ( codeFile [ 0 ] ! = ' / ' ) {
# else
if ( codeFile [ 1 ] ! = ' : ' ) {
# endif
return kMTUnknown ;
}
for ( decltype ( modulePathMap ) : : const_iterator i = modulePathMap . begin ( ) ; i ! = modulePathMap . end ( ) ; + + i ) {
if ( PathPrefixMatches ( i - > first , codeFile ) ) {
return i - > second ;
}
}
return kMTSystem ;
}
std : : string PathnameStripper_Directory ( const std : : string & path ) {
std : : string : : size_type slash = path . rfind ( ' / ' ) ;
std : : string : : size_type backslash = path . rfind ( ' \\ ' ) ;
std : : string : : size_type file_start = 0 ;
if ( slash ! = std : : string : : npos & & ( backslash = = std : : string : : npos | | slash > backslash ) ) {
file_start = slash + 1 ;
} else if ( backslash ! = string : : npos ) {
file_start = backslash + 1 ;
}
return path . substr ( 0 , file_start ) ;
}
2019-01-05 22:35:23 +01:00
enum PresubmitResponse {
kPRLocalError ,
kPRRemoteError ,
kPRDontUpload ,
kPRUploadCrashDumpAndMetadata ,
kPRUploadMetadataOnly ,
} ;
PresubmitResponse PresubmitCrashDump ( const char * path , char * tokenBuffer , size_t tokenBufferLength ) {
2018-07-21 02:31:51 +02:00
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 ) {
2019-01-05 22:35:23 +01:00
return kPRLocalError ;
2018-07-21 02:31:51 +02:00
}
2019-01-06 15:29:44 +01:00
std : : string os_short = " " ;
std : : string cpu_arch = " " ;
if ( processState . system_info ( ) ) {
os_short = processState . system_info ( ) - > os_short ;
if ( os_short . empty ( ) ) {
os_short = processState . system_info ( ) - > os ;
}
cpu_arch = processState . system_info ( ) - > cpu ;
}
2018-07-21 02:31:51 +02:00
int requestingThread = processState . requesting_thread ( ) ;
if ( requestingThread = = - 1 ) {
requestingThread = 0 ;
}
const google_breakpad : : CallStack * stack = processState . threads ( ) - > at ( requestingThread ) ;
if ( ! stack ) {
2019-01-05 22:35:23 +01:00
return kPRLocalError ;
2018-07-21 02:31:51 +02:00
}
int frameCount = stack - > frames ( ) - > size ( ) ;
2019-01-05 03:02:29 +01:00
if ( frameCount > 1024 ) {
frameCount = 1024 ;
}
2018-07-21 02:31:51 +02:00
std : : ostringstream summaryStream ;
2019-01-06 15:29:44 +01:00
summaryStream < < 2 < < " | " < < processState . time_date_stamp ( ) < < " | " < < os_short < < " | " < < cpu_arch < < " | " < < processState . crashed ( ) < < " | " < < processState . crash_reason ( ) < < " | " < < std : : hex < < processState . crash_address ( ) < < std : : dec < < " | " < < requestingThread ;
2018-07-21 02:31:51 +02:00
std : : map < const google_breakpad : : CodeModule * , unsigned int > moduleMap ;
2019-01-08 22:52:55 +01:00
unsigned int moduleCount = processState . modules ( ) ? processState . modules ( ) - > module_count ( ) : 0 ;
2018-07-21 02:31:51 +02:00
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 " ) ;
2019-01-05 22:35:23 +01:00
if ( minidumpAccount & & minidumpAccount [ 0 ] ) form - > AddString ( " UserID " , minidumpAccount ) ;
2018-07-21 02:31:51 +02:00
2019-01-05 03:02:29 +01:00
form - > AddString ( " ExtensionVersion " , SMEXT_CONF_VERSION ) ;
2019-01-06 00:13:18 +01:00
form - > AddString ( " ServerID " , serverId ) ;
2019-01-05 03:02:29 +01:00
2018-07-21 02:31:51 +02:00
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 ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Presubmit failed: %s (%d) \n " , xfer - > LastErrorMessage ( ) , xfer - > LastErrorCode ( ) ) ;
2019-01-05 22:35:23 +01:00
return kPRRemoteError ;
2018-07-21 02:31:51 +02:00
}
int responseSize = data . GetSize ( ) ;
char * response = new char [ responseSize + 1 ] ;
strncpy ( response , data . GetBuffer ( ) , responseSize + 1 ) ;
response [ responseSize ] = ' \0 ' ;
2018-07-21 15:06:47 +02:00
while ( responseSize > 0 & & response [ responseSize - 1 ] = = ' \n ' ) {
response [ - - responseSize ] = ' \0 ' ;
}
2019-01-05 03:02:29 +01:00
//if (log) fprintf(log, "Presubmit complete: %s\n", response);
2018-07-21 02:31:51 +02:00
if ( responseSize < 2 ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Presubmit response too short \n " ) ;
2018-07-21 02:31:51 +02:00
delete [ ] response ;
2019-01-05 22:35:23 +01:00
return kPRRemoteError ;
2018-07-21 02:31:51 +02:00
}
if ( response [ 0 ] = = ' E ' ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Presubmit error: %s \n " , & response [ 2 ] ) ;
2018-07-21 02:31:51 +02:00
delete [ ] response ;
2019-01-05 22:35:23 +01:00
return kPRRemoteError ;
2018-07-21 02:31:51 +02:00
}
2019-01-05 22:35:23 +01:00
PresubmitResponse presubmitResponse = kPRRemoteError ;
if ( response [ 0 ] = = ' Y ' ) presubmitResponse = kPRUploadCrashDumpAndMetadata ;
else if ( response [ 0 ] = = ' N ' ) presubmitResponse = kPRDontUpload ;
else if ( response [ 0 ] = = ' M ' ) presubmitResponse = kPRUploadMetadataOnly ;
else return kPRRemoteError ;
2018-07-21 02:31:51 +02:00
if ( response [ 1 ] ! = ' | ' ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Response delimiter missing \n " ) ;
2018-07-21 02:31:51 +02:00
delete [ ] response ;
2019-01-05 22:35:23 +01:00
return kPRRemoteError ;
2018-07-21 02:31:51 +02:00
}
unsigned int responseCount = responseSize - 2 ;
2018-07-21 15:06:47 +02:00
if ( responseCount < moduleCount ) {
2019-01-05 03:02:29 +01:00
if ( log ) fprintf ( log , " Response module list doesn't match sent list (%d < %d) \n " , responseCount , moduleCount ) ;
2018-07-21 02:31:51 +02:00
delete [ ] response ;
2019-01-05 22:35:23 +01:00
return presubmitResponse ;
}
// There was a presubmit token included.
if ( tokenBuffer & & responseCount > moduleCount & & response [ 2 + moduleCount ] = = ' | ' ) {
int tokenStart = 2 + moduleCount + 1 ;
int tokenEnd = tokenStart ;
while ( tokenEnd < responseSize & & response [ tokenEnd ] ! = ' | ' ) {
tokenEnd + + ;
}
size_t tokenLength = tokenEnd - tokenStart ;
if ( tokenLength < tokenBufferLength ) {
strncpy ( tokenBuffer , & response [ tokenStart ] , tokenLength ) ;
tokenBuffer [ tokenLength ] = ' \0 ' ;
}
if ( log ) fprintf ( log , " Got a presubmit token from server: %s \n " , tokenBuffer ) ;
2018-07-21 02:31:51 +02:00
}
2019-01-08 22:52:55 +01:00
if ( moduleCount > 0 ) {
auto mainModule = processState . modules ( ) - > GetMainModule ( ) ;
auto executableBaseDir = PathnameStripper_Directory ( mainModule - > code_file ( ) ) ;
InitModuleClassificationMap ( executableBaseDir ) ;
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
// 0 = Disabled
// 1 = System Only
// 2 = System + Game
// 3 = System + Game + Addons
const char * symbolSubmitOptionStr = g_pSM - > GetCoreConfigValue ( " MinidumpSymbolUpload " ) ;
int symbolSubmitOption = symbolSubmitOptionStr ? atoi ( symbolSubmitOptionStr ) : 3 ;
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
const char * binarySubmitOption = g_pSM - > GetCoreConfigValue ( " MinidumpBinaryUpload " ) ;
bool canBinarySubmit = ! binarySubmitOption | | ( tolower ( binarySubmitOption [ 0 ] ) = = ' y ' | | binarySubmitOption [ 0 ] = = ' 1 ' ) ;
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
for ( unsigned int moduleIndex = 0 ; moduleIndex < moduleCount ; + + moduleIndex ) {
bool submitSymbols = false ;
bool submitBinary = ( response [ 2 + moduleIndex ] = = ' U ' ) ;
2019-01-05 22:35:23 +01:00
# if defined _LINUX
2019-01-08 22:52:55 +01:00
submitSymbols = ( response [ 2 + moduleIndex ] = = ' Y ' ) ;
2019-01-05 22:35:23 +01:00
# endif
2019-01-08 22:52:55 +01:00
if ( ! submitSymbols & & ! submitBinary ) {
continue ;
}
2018-07-21 02:31:51 +02:00
2019-01-08 22:52:55 +01:00
auto module = processState . modules ( ) - > GetModuleAtIndex ( moduleIndex ) ;
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
auto moduleType = ClassifyModule ( module ) ;
if ( log ) fprintf ( log , " Classified module %s as %s \n " , module - > code_file ( ) . c_str ( ) , ModuleTypeCode [ moduleType ] ) ;
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
switch ( moduleType ) {
case kMTUnknown :
2019-01-05 03:02:29 +01:00
continue ;
2019-01-08 22:52:55 +01:00
case kMTSystem :
if ( symbolSubmitOption < 1 ) {
continue ;
}
break ;
case kMTGame :
if ( symbolSubmitOption < 2 ) {
continue ;
}
break ;
case kMTAddon :
case kMTExtension :
if ( symbolSubmitOption < 3 ) {
continue ;
}
break ;
}
2019-01-05 03:02:29 +01:00
2019-01-08 22:52:55 +01:00
if ( canBinarySubmit & & submitBinary ) {
UploadModuleFile ( module , tokenBuffer ) ;
}
2019-01-05 03:02:29 +01:00
# if defined _LINUX
2019-01-08 22:52:55 +01:00
if ( submitSymbols ) {
UploadSymbolFile ( module , tokenBuffer ) ;
}
2018-07-21 02:31:51 +02:00
# endif
2019-01-08 22:52:55 +01:00
}
2019-01-05 03:02:29 +01:00
}
2018-07-21 02:31:51 +02:00
delete [ ] response ;
2019-01-05 22:35:23 +01:00
return presubmitResponse ;
2018-07-21 02:31:51 +02:00
}
2019-01-05 22:35:23 +01:00
bool UploadCrashDump ( const char * path , const char * metapath , const char * presubmitToken , char * response , int maxlen ) {
2018-07-21 02:31:51 +02:00
IWebForm * form = webternet - > CreateForm ( ) ;
const char * minidumpAccount = g_pSM - > GetCoreConfigValue ( " MinidumpAccount " ) ;
2019-01-05 22:35:23 +01:00
if ( minidumpAccount & & minidumpAccount [ 0 ] ) form - > AddString ( " UserID " , minidumpAccount ) ;
2018-07-21 02:31:51 +02:00
2019-01-05 22:35:23 +01:00
form - > AddString ( " GameDirectory " , crashGameDirectory ) ;
2018-07-21 02:31:51 +02:00
form - > AddString ( " ExtensionVersion " , SMEXT_CONF_VERSION ) ;
2019-01-06 00:13:18 +01:00
form - > AddString ( " ServerID " , serverId ) ;
2018-07-21 02:31:51 +02:00
2019-01-05 22:35:23 +01:00
if ( presubmitToken & & presubmitToken [ 0 ] ) {
form - > AddString ( " PresubmitToken " , presubmitToken ) ;
}
if ( path & & path [ 0 ] ) {
form - > AddFile ( " upload_file_minidump " , path ) ;
}
2018-07-21 02:31:51 +02:00
2019-01-05 22:35:23 +01:00
if ( metapath & & metapath [ 0 ] ) {
2018-07-21 02:31:51 +02:00
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 ' ;
2018-07-21 15:06:47 +02:00
while ( responseSize > 0 & & response [ responseSize - 1 ] = = ' \n ' ) {
response [ - - responseSize ] = ' \0 ' ;
}
2018-07-21 02:31:51 +02:00
} else {
g_pSM - > Format ( response , maxlen , " %s (%d) " , xfer - > LastErrorMessage ( ) , xfer - > LastErrorCode ( ) ) ;
}
}
return uploaded ;
}
} uploadThread ;
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 ) ( ) ;
}
bool Accelerator : : SDK_OnLoad ( char * error , size_t maxlength , bool late )
{
sharesys - > AddDependency ( myself , " webternet.ext " , true , true ) ;
SM_GET_IFACE ( WEBTERNET , webternet ) ;
g_pSM - > BuildPath ( Path_SM , dumpStoragePath , sizeof ( dumpStoragePath ) , " data/dumps " ) ;
if ( ! libsys - > IsPathDirectory ( dumpStoragePath ) )
{
if ( ! libsys - > CreateFolder ( dumpStoragePath ) )
{
if ( error )
g_pSM - > Format ( error , maxlength , " %s didn't exist and we couldn't create it :( " , dumpStoragePath ) ;
return false ;
}
}
g_pSM - > BuildPath ( Path_SM , logPath , sizeof ( logPath ) , " logs/accelerator.log " ) ;
2019-01-05 03:02:29 +01:00
// Get these early so the upload thread can use them.
strncpy ( crashGamePath , g_pSM - > GetGamePath ( ) , sizeof ( crashGamePath ) - 1 ) ;
strncpy ( crashSourceModPath , g_pSM - > GetSourceModPath ( ) , sizeof ( crashSourceModPath ) - 1 ) ;
strncpy ( crashGameDirectory , g_pSM - > GetGameFolderName ( ) , sizeof ( crashGameDirectory ) - 1 ) ;
2018-07-21 02:31:51 +02:00
threader - > MakeThread ( & uploadThread ) ;
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 ;
# if defined _WINDOWS
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
# endif
) {
smutils - > LogMessage ( myself , " WARNING: Sigscan for GetSpew failed, console output will not be included in crash reports. " ) ;
break ;
}
} while ( false ) ;
# if defined _LINUX
google_breakpad : : MinidumpDescriptor descriptor ( dumpStoragePath ) ;
handler = new google_breakpad : : ExceptionHandler ( descriptor , NULL , dumpCallback , NULL , true , - 1 ) ;
struct sigaction oact ;
sigaction ( SIGSEGV , NULL , & oact ) ;
SignalHandler = oact . sa_sigaction ;
g_pSM - > AddGameFrameHook ( OnGameFrame ) ;
# elif defined _WINDOWS
wchar_t * buf = new wchar_t [ sizeof ( dumpStoragePath ) ] ;
size_t num_chars = mbstowcs ( buf , dumpStoragePath , sizeof ( dumpStoragePath ) ) ;
2019-05-05 16:11:04 +02:00
handler = new google_breakpad : : ExceptionHandler (
std : : wstring ( buf , num_chars ) , NULL , dumpCallback , NULL , google_breakpad : : ExceptionHandler : : HANDLER_ALL ,
2019-05-06 01:20:07 +02:00
static_cast < MINIDUMP_TYPE > ( MiniDumpWithUnloadedModules | MiniDumpWithFullMemoryInfo ) , static_cast < const wchar_t * > ( NULL ) , NULL ) ;
2018-07-21 02:31:51 +02:00
vectoredHandler = AddVectoredExceptionHandler ( 0 , BreakpadVectoredHandler ) ;
delete buf ;
# else
# error Bad platform.
# endif
2019-07-27 00:04:27 +02:00
do {
char spJitPath [ 512 ] ;
g_pSM - > BuildPath ( Path_SM , spJitPath , sizeof ( spJitPath ) , " bin/ " PLATFORM_ARCH_FOLDER " sourcepawn.jit.x86. " PLATFORM_LIB_EXT ) ;
char spJitError [ 255 ] ;
std : : unique_ptr < ILibrary > spJit ( libsys - > OpenLibrary ( spJitPath , spJitError , sizeof ( spJitError ) ) ) ;
if ( ! spJit ) {
smutils - > LogMessage ( myself , " WARNING: Failed to load SourcePawn library %s: %s " , spJitPath , spJitError ) ;
break ;
}
GetSourcePawnFactoryFn factoryFn = ( GetSourcePawnFactoryFn ) spJit - > GetSymbolAddress ( " GetSourcePawnFactory " ) ;
if ( ! factoryFn ) {
smutils - > LogMessage ( myself , " WARNING: SourcePawn library is out of date: No factory function. " ) ;
break ;
}
ISourcePawnFactory * spFactory = factoryFn ( 0x0207 ) ;
if ( ! spFactory ) {
smutils - > LogMessage ( myself , " WARNING: SourcePawn library is out of date: Failed to get version 2.7 " , 0x0207 ) ;
break ;
}
ISourcePawnEnvironment * spEnvironment = spFactory - > CurrentEnvironment ( ) ;
if ( ! spEnvironment ) {
smutils - > LogMessage ( myself , " WARNING: Could not get SourcePawn environment. " ) ;
break ;
}
strncpy ( crashSourceModVersion , spEnvironment - > APIv2 ( ) - > GetVersionString ( ) , sizeof ( crashSourceModVersion ) ) ;
} while ( false ) ;
2019-07-24 01:27:24 +02:00
plsys - > AddPluginsListener ( this ) ;
2018-07-21 02:31:51 +02:00
2019-07-24 01:27:24 +02:00
IPluginIterator * iterator = plsys - > GetPluginIterator ( ) ;
while ( iterator - > MorePlugins ( ) ) {
IPlugin * plugin = iterator - > GetPlugin ( ) ;
if ( plugin - > GetStatus ( ) = = Plugin_Running ) {
this - > OnPluginLoaded ( plugin ) ;
}
iterator - > NextPlugin ( ) ;
2018-07-21 02:31:51 +02:00
}
2019-07-24 01:27:24 +02:00
delete iterator ;
2018-07-21 02:31:51 +02:00
strncpy ( crashCommandLine , GetCmdLine ( ) , sizeof ( crashCommandLine ) - 1 ) ;
char steamInfPath [ 512 ] ;
g_pSM - > BuildPath ( Path_Game , steamInfPath , sizeof ( steamInfPath ) , " steam.inf " ) ;
FILE * steamInfFile = fopen ( steamInfPath , " rb " ) ;
if ( steamInfFile ) {
char steamInfTemp [ 1024 ] = { 0 } ;
fread ( steamInfTemp , sizeof ( char ) , sizeof ( steamInfTemp ) - 1 , steamInfFile ) ;
fclose ( steamInfFile ) ;
unsigned commentChars = 0 ;
unsigned valueChars = 0 ;
unsigned source = 0 ;
strcpy ( steamInf , " \n Steam_ " ) ;
unsigned target = 7 ; // strlen("\nSteam_");
while ( true ) {
if ( steamInfTemp [ source ] = = ' \0 ' ) {
source + + ;
break ;
}
if ( steamInfTemp [ source ] = = ' / ' ) {
source + + ;
commentChars + + ;
continue ;
}
if ( commentChars = = 1 ) {
commentChars = 0 ;
steamInf [ target + + ] = ' / ' ;
valueChars + + ;
}
if ( steamInfTemp [ source ] = = ' \r ' ) {
source + + ;
continue ;
}
if ( steamInfTemp [ source ] = = ' \n ' ) {
commentChars = 0 ;
source + + ;
if ( steamInfTemp [ source ] = = ' \0 ' ) {
break ;
}
if ( valueChars > 0 ) {
valueChars = 0 ;
strcpy ( & steamInf [ target ] , " \n Steam_ " ) ;
target + = 7 ;
}
continue ;
}
if ( commentChars > = 2 ) {
source + + ;
continue ;
}
steamInf [ target + + ] = steamInfTemp [ source + + ] ;
valueChars + + ;
}
}
if ( late ) {
this - > OnCoreMapStart ( NULL , 0 , 0 ) ;
}
return true ;
}
void Accelerator : : SDK_OnUnload ( )
{
2019-07-24 01:27:24 +02:00
plsys - > RemovePluginsListener ( this ) ;
2018-07-21 02:31:51 +02:00
# 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 ) ;
}
2019-07-24 01:27:24 +02:00
/* 010 Editor Template
uint64 headerMagic ;
uint32 version ;
uint32 size ;
uint32 count ;
struct {
uint32 size ;
uint32 context < format = hex > ;
char file [ ] ;
uint32 count ;
struct {
uint32 pcode < format = hex > ;
char name [ ] ;
} functions [ count ] < optimize = false > ;
} plugins [ count ] < optimize = false > ;
uint64 tailMagic ;
*/
unsigned char * serializedPluginContexts = nullptr ;
std : : map < const IPluginContext * , unsigned char * > pluginContextMap ;
void SerializePluginContexts ( )
{
if ( serializedPluginContexts ) {
handler - > UnregisterAppMemory ( serializedPluginContexts ) ;
free ( serializedPluginContexts ) ;
serializedPluginContexts = nullptr ;
}
uint32_t count = pluginContextMap . size ( ) ;
if ( count = = 0 ) {
return ;
}
uint32_t size = 0 ;
size + = sizeof ( uint64_t ) ; // header magic
size + = sizeof ( uint32_t ) ; // version
size + = sizeof ( uint32_t ) ; // size
size + = sizeof ( uint32_t ) ; // count
for ( auto & it : pluginContextMap ) {
unsigned char * buffer = it . second ;
uint32_t bufferSize ;
memcpy ( & bufferSize , buffer , sizeof ( uint32_t ) ) ;
size + = bufferSize ;
}
size + = sizeof ( uint64_t ) ; // tail magic
serializedPluginContexts = ( unsigned char * ) malloc ( size ) ;
handler - > RegisterAppMemory ( serializedPluginContexts , size ) ;
unsigned char * cursor = serializedPluginContexts ;
uint64_t headerMagic = 103582791429521979ULL ;
memcpy ( cursor , & headerMagic , sizeof ( uint64_t ) ) ;
cursor + = sizeof ( uint64_t ) ;
uint32_t version = 1 ;
memcpy ( cursor , & version , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
memcpy ( cursor , & size , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
memcpy ( cursor , & count , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
for ( auto & it : pluginContextMap ) {
unsigned char * buffer = it . second ;
uint32_t bufferSize ;
memcpy ( & bufferSize , buffer , sizeof ( uint32_t ) ) ;
memcpy ( cursor , buffer , bufferSize ) ;
cursor + = bufferSize ;
}
uint64_t tailMagic = 76561197987819599ULL ;
memcpy ( cursor , & tailMagic , sizeof ( uint64_t ) ) ;
cursor + = sizeof ( uint64_t ) ;
}
void Accelerator : : OnPluginLoaded ( IPlugin * plugin )
{
IPluginRuntime * runtime = plugin - > GetRuntime ( ) ;
IPluginContext * context = plugin - > GetBaseContext ( ) ;
if ( ! runtime | | ! context ) {
return ;
}
const char * filename = plugin - > GetFilename ( ) ;
size_t filenameSize = strlen ( filename ) + 1 ;
uint32_t size = 0 ;
size + = sizeof ( uint32_t ) ; // size
size + = sizeof ( void * ) ; // GetBaseContext
size + = filenameSize ;
uint32_t count = runtime - > GetPublicsNum ( ) ;
size + = sizeof ( uint32_t ) ; // count
size + = count * sizeof ( uint32_t ) ; // pubinfo->code_offs
for ( uint32_t i = 0 ; i < count ; + + i ) {
sp_public_t * pubinfo ;
runtime - > GetPublicByIndex ( i , & pubinfo ) ;
size + = strlen ( pubinfo - > name ) + 1 ;
}
unsigned char * buffer = ( unsigned char * ) malloc ( size ) ;
unsigned char * cursor = buffer ;
memcpy ( cursor , & size , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
memcpy ( cursor , & context , sizeof ( void * ) ) ;
cursor + = sizeof ( void * ) ;
memcpy ( cursor , filename , filenameSize ) ;
cursor + = filenameSize ;
memcpy ( cursor , & count , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
for ( uint32_t i = 0 ; i < count ; + + i ) {
sp_public_t * pubinfo ;
runtime - > GetPublicByIndex ( i , & pubinfo ) ;
memcpy ( cursor , & pubinfo - > code_offs , sizeof ( uint32_t ) ) ;
cursor + = sizeof ( uint32_t ) ;
size_t nameSize = strlen ( pubinfo - > name ) + 1 ;
memcpy ( cursor , pubinfo - > name , nameSize ) ;
cursor + = nameSize ;
}
pluginContextMap [ context ] = buffer ;
SerializePluginContexts ( ) ;
}
void Accelerator : : OnPluginUnloaded ( IPlugin * plugin )
{
IPluginContext * context = plugin - > GetBaseContext ( ) ;
if ( ! context ) {
return ;
}
auto it = pluginContextMap . find ( context ) ;
if ( it ! = pluginContextMap . end ( ) ) {
free ( it - > second ) ;
pluginContextMap . erase ( it ) ;
}
SerializePluginContexts ( ) ;
}