Skip to content

Commit ee66e8e

Browse files
committed
feat(core): Crash Reporter
1 parent b9df62c commit ee66e8e

File tree

5 files changed

+230
-3
lines changed

5 files changed

+230
-3
lines changed

setup.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ if (-not $env:GITHUB_SHA) {
77
if (!(Test-Path -Path "$PSScriptRoot\build")) {
88
mkdir build
99
Set-Location build
10-
python ../configure.py --enable-optimize --symbol-files -s cs2
10+
python ../configure.py --enable-optimize -s cs2
1111
Set-Location ..
1212
}
1313

setup.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fi
1010
if [ ! -d build ]; then
1111
mkdir build
1212
cd build
13-
CC=gcc CXX=g++ python ../configure.py --enable-optimize --symbol-files -s cs2
13+
CC=gcc CXX=g++ python ../configure.py --enable-optimize -s cs2
1414
cd ..
1515
fi
1616

src/crashreporter/CrashReport.cpp

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
#include "CrashReport.h"
2+
3+
#ifdef _WIN32
4+
bool BeginCrashListener() { return true; }
5+
#else
6+
7+
#include <rapidjson/document.h>
8+
#include <rapidjson/writer.h>
9+
#include <rapidjson/stringbuffer.h>
10+
11+
#include <execinfo.h>
12+
#include <dlfcn.h>
13+
#include <cxxabi.h>
14+
#include <signal.h>
15+
16+
#include <tier0/icommandline.h>
17+
18+
#include <TextTable.h>
19+
#include <sstream>
20+
21+
#include <random>
22+
23+
#include "../files/Files.h"
24+
#include "../common.h"
25+
#include "../entrypoint.h"
26+
27+
std::string get_uuid()
28+
{
29+
static std::random_device dev;
30+
static std::mt19937 rng(dev());
31+
32+
std::uniform_int_distribution<int> dist(0, 15);
33+
34+
const char *v = "0123456789abcdef";
35+
const bool dash[] = {0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0};
36+
37+
std::string res;
38+
for (int i = 0; i < 16; i++)
39+
{
40+
if (dash[i])
41+
res += "-";
42+
res += v[dist(rng)];
43+
res += v[dist(rng)];
44+
}
45+
return res;
46+
}
47+
48+
std::string startup_cmd = "None";
49+
50+
const char *ws = " \t\n\r\f\v";
51+
52+
// trim from end of string (right)
53+
inline std::string &rtrim(std::string &s, const char *t = ws)
54+
{
55+
s.erase(s.find_last_not_of(t) + 1);
56+
return s;
57+
}
58+
59+
// trim from beginning of string (left)
60+
inline std::string &ltrim(std::string &s, const char *t = ws)
61+
{
62+
s.erase(0, s.find_first_not_of(t));
63+
return s;
64+
}
65+
66+
// trim from both ends of string (right then left)
67+
inline std::string &trim(std::string &s, const char *t = ws)
68+
{
69+
return ltrim(rtrim(s, t), t);
70+
}
71+
72+
std::string BacktraceRaw(int skip = 1)
73+
{
74+
void *callstack[128];
75+
const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
76+
char buf[1024];
77+
int nFrames = backtrace(callstack, nMaxFrames);
78+
char **symbols = backtrace_symbols(callstack, nFrames);
79+
80+
std::ostringstream trace_buf;
81+
for (int i = skip; i < nFrames; i++)
82+
{
83+
Dl_info info;
84+
if (dladdr(callstack[i], &info) && info.dli_sname)
85+
{
86+
char *demangled = NULL;
87+
int status = -1;
88+
if (info.dli_sname[0] == '_')
89+
demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
90+
snprintf(buf, sizeof(buf), "%02d. %p %s + %zd\n",
91+
i, int(2 + sizeof(void *) * 2), callstack[i],
92+
status == 0 ? demangled : info.dli_sname == 0 ? symbols[i]
93+
: info.dli_sname,
94+
(char *)callstack[i] - (char *)info.dli_saddr);
95+
free(demangled);
96+
}
97+
else
98+
{
99+
snprintf(buf, sizeof(buf), "%02d. %p %s\n",
100+
i, callstack[i], symbols[i]);
101+
}
102+
trace_buf << buf;
103+
}
104+
free(symbols);
105+
if (nFrames == nMaxFrames)
106+
trace_buf << "[truncated]\n";
107+
return trace_buf.str();
108+
}
109+
110+
TextTable GetBacktrace()
111+
{
112+
TextTable backtraceTable('-', '|', '+');
113+
114+
backtraceTable.add(" ID ");
115+
backtraceTable.add(" File ");
116+
backtraceTable.add(" Function ");
117+
backtraceTable.add(" Offset ");
118+
backtraceTable.add(" Address ");
119+
backtraceTable.endOfRow();
120+
121+
void *callstack[128];
122+
const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
123+
char buf[1024];
124+
int nFrames = backtrace(callstack, nMaxFrames);
125+
char **symbols = backtrace_symbols(callstack, nFrames);
126+
127+
for (int i = 2; i < nFrames; i++)
128+
{
129+
backtraceTable.add(string_format(" %02d. ", i));
130+
131+
std::vector<std::string> symbolExploded = explode(symbols[i], "/");
132+
backtraceTable.add(string_format(" %s ", explode(symbolExploded[symbolExploded.size() - 1], "(")[0].c_str()));
133+
134+
Dl_info info;
135+
if (dladdr(callstack[i], &info) && info.dli_sname)
136+
{
137+
char *demangled = NULL;
138+
int status = -1;
139+
if (info.dli_sname[0] == '_')
140+
demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
141+
142+
std::string funcName = (status == 0 ? demangled : info.dli_sname == 0 ? "-"
143+
: info.dli_sname);
144+
145+
backtraceTable.add(string_format(" %s ", (funcName.size() > 36 ? (funcName.substr(0, 33) + "...") : funcName).c_str()));
146+
free(demangled);
147+
}
148+
else
149+
backtraceTable.add(" - ");
150+
151+
backtraceTable.add(string_format(" %s ", explode(explode(symbolExploded[symbolExploded.size() - 1], "+")[1], ")")[0].c_str()));
152+
backtraceTable.add(string_format(" %p ", callstack[i]));
153+
backtraceTable.endOfRow();
154+
}
155+
free(symbols);
156+
157+
return backtraceTable;
158+
}
159+
160+
void signal_handler(int signumber)
161+
{
162+
try
163+
{
164+
void *tracePointers[20];
165+
size_t count = backtrace(tracePointers, 20);
166+
167+
PLUGIN_PRINTF("Crash Reporter", "A crash has occured and a dump has been created:\n");
168+
std::string backtraceData = BacktraceRaw();
169+
170+
TextTable traceTable = GetBacktrace();
171+
PrintTextTable("Crash Reporter", traceTable);
172+
173+
std::string file_path = string_format("addons/swiftly/dumps/crash.%s.log", get_uuid().c_str());
174+
if (Files::ExistsPath(file_path))
175+
Files::Delete(file_path);
176+
177+
Files::Append(file_path, string_format("================================\nCommand: %s\nMap: %s\n================================\n\n%s", startup_cmd.c_str(), engine->GetServerGlobals() ? engine->GetServerGlobals()->mapname.ToCStr() : "None", backtraceData.c_str()), false);
178+
PLUGIN_PRINTF("Crash Reporter", "A dump log file has been created at: %s\n", file_path.c_str());
179+
}
180+
catch (const std::runtime_error &e)
181+
{
182+
PLUGIN_PRINTF("Crash Reporter", "Error crash handling: %s\n", e.what());
183+
}
184+
185+
exit(EXIT_FAILURE);
186+
}
187+
188+
bool BeginCrashListener()
189+
{
190+
if (!Files::ExistsPath("addons/swiftly/dumps"))
191+
{
192+
if (!Files::CreateDirectory("addons/swiftly/dumps"))
193+
{
194+
PLUGIN_PRINTF("Crash Listener", "Couldn't create dumps folder.\n");
195+
return false;
196+
}
197+
}
198+
199+
::signal(SIGSEGV, &signal_handler);
200+
201+
startup_cmd = CommandLine()->GetCmdLine();
202+
std::vector<std::string> exp = explode(startup_cmd, " ");
203+
std::vector<std::string> exp2;
204+
for (int i = 1; i < exp.size(); i++)
205+
{
206+
std::string str = trim(exp[i]);
207+
if (str.length() == 0)
208+
continue;
209+
if (exp2.size() > 0)
210+
if (ends_with(exp2[exp2.size() - 1], "sv_setsteamaccount") || ends_with(exp2[exp2.size() - 1], "authkey"))
211+
str = "REDACTED";
212+
213+
exp2.push_back(str);
214+
}
215+
startup_cmd = implode(exp2, " ");
216+
217+
return true;
218+
}
219+
220+
#endif

src/crashreporter/CrashReport.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#pragma once
2+
3+
bool BeginCrashListener();

src/entrypoint.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@
77

88
#include <steam/steam_gameserver.h>
99

10+
#include "crashreporter/CrashReport.h"
11+
#include "hooks/NativeHooks.h"
1012
#include "player/PlayerManager.h"
1113
#include "plugins/PluginManager.h"
1214
#include "signatures/Signatures.h"
1315
#include "signatures/Offsets.h"
14-
#include "hooks/NativeHooks.h"
1516

1617
SH_DECL_HOOK3_void(INetworkServerService, StartupServer, SH_NOATTRIB, 0, const GameSessionConfiguration_t &, ISource2WorldSession *, const char *);
1718
SH_DECL_HOOK3_void(IServerGameDLL, GameFrame, SH_NOATTRIB, 0, bool, bool, bool);
@@ -113,6 +114,9 @@ bool Swiftly::Load(PluginId id, ISmmAPI *ismm, char *error, size_t maxlen, bool
113114

114115
ConVar_Register(FCVAR_RELEASE | FCVAR_SERVER_CAN_EXECUTE | FCVAR_CLIENT_CAN_EXECUTE | FCVAR_GAMEDLL);
115116

117+
if (!BeginCrashListener())
118+
PRINTRET("Crash Reporter failed to initialize.\n", false)
119+
116120
g_pluginManager = new PluginManager();
117121
g_Signatures = new Signatures();
118122
g_Offsets = new Offsets();

0 commit comments

Comments
 (0)