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 <rim (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 (" ================================\n Command: %s\n Map: %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
0 commit comments