sm-ext-accelerator-2023/extension/extension.cpp

896 lines
26 KiB
C++
Raw Normal View History

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"
#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"
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>
#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"
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
#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;
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.
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];
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;
# if 0
struct PluginInfo {
unsigned int serial;
PluginStatus status;
char filename[256];
char name[256];
char author[256];
char description[256];
char version[256];
char url[256];
};
unsigned int plugin_count;
PluginInfo plugins[256];
#endif
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)
{
//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;
}
2016-01-14 12:18:10 +01:00
my_strlcpy(dumpStoragePath, descriptor.path(), sizeof(dumpStoragePath));
my_strlcat(dumpStoragePath, ".txt", sizeof(dumpStoragePath));
2016-01-14 12:18:10 +01:00
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;
}
2016-01-25 16:38:43 +01:00
sys_write(extra, "-------- CONFIG BEGIN --------", 30);
sys_write(extra, "\nMap=", 5);
sys_write(extra, crashMap, my_strlen(crashMap));
sys_write(extra, "\nGamePath=", 10);
sys_write(extra, crashGamePath, my_strlen(crashGamePath));
sys_write(extra, "\nCommandLine=", 13);
sys_write(extra, crashCommandLine, my_strlen(crashCommandLine));
sys_write(extra, "\nSourceModPath=", 15);
sys_write(extra, crashSourceModPath, my_strlen(crashSourceModPath));
sys_write(extra, "\nGameDirectory=", 15);
sys_write(extra, crashGameDirectory, my_strlen(crashGameDirectory));
sys_write(extra, "\nExtensionVersion=", 18);
2016-09-11 13:36:07 +02:00
sys_write(extra, SM_VERSION, my_strlen(SM_VERSION));
sys_write(extra, "\nExtensionBuild=", 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);
if (GetSpew) {
GetSpew(spewBuffer, sizeof(spewBuffer));
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);
}
}
#if 0
char pis[64];
char pds[32];
for (unsigned i = 0; i < plugin_count; ++i) {
PluginInfo *p = &plugins[i];
if (p->serial == 0) continue;
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));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "name=", 5);
sys_write(extra, p->name, my_strlen(p->name));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "author=", 7);
sys_write(extra, p->author, my_strlen(p->author));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "description=", 12);
sys_write(extra, p->description, my_strlen(p->description));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "version=", 8);
sys_write(extra, p->version, my_strlen(p->version));
sys_write(extra, "\n", 1);
sys_write(extra, pis, my_strlen(pis));
sys_write(extra, "url=", 4);
sys_write(extra, p->url, my_strlen(p->url));
sys_write(extra, "\n", 1);
}
#endif
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);
}
}
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:
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, "\nMap=%s", crashMap);
fprintf(extra, "\nGamePath=%s", crashGamePath);
fprintf(extra, "\nCommandLine=%s", crashCommandLine);
fprintf(extra, "\nSourceModPath=%s", crashSourceModPath);
fprintf(extra, "\nGameDirectory=%s", crashGameDirectory);
2016-09-11 13:36:07 +02:00
fprintf(extra, "\nExtensionVersion=%s", SM_VERSION);
fprintf(extra, "\nExtensionBuild=%s", SM_BUILD_UNIQUEID);
2016-08-22 16:06:14 +02:00
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));
}
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
}
fclose(extra);
2016-01-13 17:49:19 +01:00
2013-05-18 01:46:46 +02:00
return succeeded;
}
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
class ClogInhibitor
2013-05-16 08:18:31 +02:00
{
std::streambuf *saved_clog = nullptr;
2013-05-16 08:18:31 +02:00
public:
ClogInhibitor() {
saved_clog = std::clog.rdbuf();
std::clog.rdbuf(nullptr);
2013-05-16 08:18:31 +02:00
}
~ClogInhibitor() {
std::clog.rdbuf(saved_clog);
}
};
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-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;
}
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);
// 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);
}
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
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;
}
} 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))
{
2016-01-14 12:18:10 +01:00
if (!libsys->CreateFolder(dumpStoragePath))
{
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);
return false;
}
}
2016-01-14 12:18:10 +01:00
g_pSM->BuildPath(Path_SM, logPath, sizeof(logPath), "logs/accelerator.log");
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);
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
#if 0
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;
#endif
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) {
char steamInfTemp[1024] = {0};
2016-09-11 13:30:20 +02:00
fread(steamInfTemp, sizeof(char), sizeof(steamInfTemp) - 1, steamInfFile);
2016-09-11 13:30:20 +02:00
fclose(steamInfFile);
2016-08-22 16:06:14 +02:00
unsigned commentChars = 0;
unsigned valueChars = 0;
2016-09-11 13:30:20 +02:00
unsigned source = 0;
strcpy(steamInf, "\nSteam_");
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;
}
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') {
commentChars = 0;
2016-09-11 13:30:20 +02:00
source++;
if (steamInfTemp[source] == '\0') {
break;
}
if (valueChars > 0) {
valueChars = 0;
strcpy(&steamInf[target], "\nSteam_");
target += 7;
}
continue;
}
if (commentChars >= 2) {
source++;
2016-09-11 13:30:20 +02:00
continue;
}
steamInf[target++] = steamInfTemp[source++];
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
}