1 /*
2 debug.cpp
3 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4 */
5
6 /*
7 This file is part of Freeminer.
8
9 Freeminer is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13
14 Freeminer is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with Freeminer. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23
24 #include "porting.h"
25 #include "debug.h"
26 #include "exceptions.h"
27 #include "threads.h"
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <cstring>
31 #include <map>
32 #include "jthread/jmutex.h"
33 #include "jthread/jmutexautolock.h"
34 #include "config.h"
35 /*
36 Debug output
37 */
38
39 #define DEBUGSTREAM_COUNT 2
40
41 FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
42
43 #define DEBUGPRINT(...)\
44 {\
45 for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
46 {\
47 if(g_debugstreams[i] != NULL){\
48 fprintf(g_debugstreams[i], __VA_ARGS__);\
49 fflush(g_debugstreams[i]);\
50 }\
51 }\
52 }
53
debugstreams_init(bool disable_stderr,const char * filename)54 void debugstreams_init(bool disable_stderr, const char *filename)
55 {
56 if(disable_stderr)
57 g_debugstreams[0] = NULL;
58 else
59 g_debugstreams[0] = stderr;
60
61 if(filename)
62 g_debugstreams[1] = fopen(filename, "a");
63
64 if(g_debugstreams[1])
65 {
66 fprintf(g_debugstreams[1], "\n\n-------------\n");
67 fprintf(g_debugstreams[1], " Separator \n");
68 fprintf(g_debugstreams[1], "-------------\n\n");
69 }
70 }
71
debugstreams_deinit()72 void debugstreams_deinit()
73 {
74 if(g_debugstreams[1] != NULL)
75 fclose(g_debugstreams[1]);
76 }
77
78 class Debugbuf : public std::streambuf
79 {
80 public:
Debugbuf(bool disable_stderr)81 Debugbuf(bool disable_stderr)
82 {
83 m_disable_stderr = disable_stderr;
84 }
85
overflow(int c)86 int overflow(int c)
87 {
88 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
89 {
90 if(g_debugstreams[i] == stderr && m_disable_stderr)
91 continue;
92 if(g_debugstreams[i] != NULL)
93 (void)fwrite(&c, 1, 1, g_debugstreams[i]);
94 //TODO: Is this slow?
95 fflush(g_debugstreams[i]);
96 }
97
98 return c;
99 }
xsputn(const char * s,std::streamsize n)100 std::streamsize xsputn(const char *s, std::streamsize n)
101 {
102 #ifdef __ANDROID__
103 __android_log_print(ANDROID_LOG_VERBOSE, PROJECT_NAME, "%s", s);
104 #endif
105 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
106 {
107 if(g_debugstreams[i] == stderr && m_disable_stderr)
108 continue;
109 if(g_debugstreams[i] != NULL)
110 (void)fwrite(s, 1, n, g_debugstreams[i]);
111 //TODO: Is this slow?
112 fflush(g_debugstreams[i]);
113 }
114
115 return n;
116 }
117
118 private:
119 bool m_disable_stderr;
120 };
121
122 Debugbuf debugbuf(false);
123 std::ostream dstream(&debugbuf);
124 Debugbuf debugbuf_no_stderr(true);
125 std::ostream dstream_no_stderr(&debugbuf_no_stderr);
126 Nullstream dummyout;
127
128 /*
129 Assert
130 */
131
assert_fail(const char * assertion,const char * file,unsigned int line,const char * function)132 void assert_fail(const char *assertion, const char *file,
133 unsigned int line, const char *function)
134 {
135 DEBUGPRINT("\nIn thread %lx:\n"
136 "%s:%d: %s: Assertion '%s' failed.\n",
137 (unsigned long)get_current_thread_id(),
138 file, line, function, assertion);
139
140 debug_stacks_print();
141
142 if(g_debugstreams[1])
143 fclose(g_debugstreams[1]);
144
145 abort();
146 }
147
148 /*
149 DebugStack
150 */
151
152 struct DebugStack
153 {
154 DebugStack(threadid_t id);
155 void print(FILE *file, bool everything);
156 void print(std::ostream &os, bool everything);
157
158 threadid_t threadid;
159 char stack[DEBUG_STACK_SIZE][DEBUG_STACK_TEXT_SIZE];
160 int stack_i; // Points to the lowest empty position
161 int stack_max_i; // Highest i that was seen
162 };
163
DebugStack(threadid_t id)164 DebugStack::DebugStack(threadid_t id)
165 {
166 threadid = id;
167 stack_i = 0;
168 stack_max_i = 0;
169 memset(stack, 0, DEBUG_STACK_SIZE*DEBUG_STACK_TEXT_SIZE);
170 }
171
print(FILE * file,bool everything)172 void DebugStack::print(FILE *file, bool everything)
173 {
174 fprintf(file, "DEBUG STACK FOR THREAD %lx:\n",
175 (unsigned long)threadid);
176
177 for(int i=0; i<stack_max_i; i++)
178 {
179 if(i == stack_i && everything == false)
180 break;
181
182 if(i < stack_i)
183 fprintf(file, "#%d %s\n", i, stack[i]);
184 else
185 fprintf(file, "(Leftover data: #%d %s)\n", i, stack[i]);
186 }
187
188 if(stack_i == DEBUG_STACK_SIZE)
189 fprintf(file, "Probably overflown.\n");
190 }
191
print(std::ostream & os,bool everything)192 void DebugStack::print(std::ostream &os, bool everything)
193 {
194 os<<"DEBUG STACK FOR THREAD "<<(unsigned long)threadid<<": "<<std::endl;
195
196 for(int i=0; i<stack_max_i; i++)
197 {
198 if(i == stack_i && everything == false)
199 break;
200
201 if(i < stack_i)
202 os<<"#"<<i<<" "<<stack[i]<<std::endl;
203 else
204 os<<"(Leftover data: #"<<i<<" "<<stack[i]<<")"<<std::endl;
205 }
206
207 if(stack_i == DEBUG_STACK_SIZE)
208 os<<"Probably overflown."<<std::endl;
209 }
210
211 std::map<threadid_t, DebugStack*> g_debug_stacks;
212 JMutex g_debug_stacks_mutex;
213
debug_stacks_init()214 void debug_stacks_init()
215 {
216 }
217
debug_stacks_print_to(std::ostream & os)218 void debug_stacks_print_to(std::ostream &os)
219 {
220 JMutexAutoLock lock(g_debug_stacks_mutex);
221
222 os<<"Debug stacks:"<<std::endl;
223
224 for(std::map<threadid_t, DebugStack*>::iterator
225 i = g_debug_stacks.begin();
226 i != g_debug_stacks.end(); ++i)
227 {
228 i->second->print(os, false);
229 }
230 }
231
debug_stacks_print()232 void debug_stacks_print()
233 {
234 JMutexAutoLock lock(g_debug_stacks_mutex);
235
236 DEBUGPRINT("Debug stacks:\n");
237
238 for(std::map<threadid_t, DebugStack*>::iterator
239 i = g_debug_stacks.begin();
240 i != g_debug_stacks.end(); ++i)
241 {
242 DebugStack *stack = i->second;
243
244 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
245 {
246 if(g_debugstreams[i] != NULL)
247 stack->print(g_debugstreams[i], true);
248 }
249 }
250 }
251
DebugStacker(const char * text)252 DebugStacker::DebugStacker(const char *text)
253 {
254 threadid_t threadid = get_current_thread_id();
255
256 JMutexAutoLock lock(g_debug_stacks_mutex);
257
258 std::map<threadid_t, DebugStack*>::iterator n;
259 n = g_debug_stacks.find(threadid);
260 if(n != g_debug_stacks.end())
261 {
262 m_stack = n->second;
263 }
264 else
265 {
266 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
267 (unsigned int)threadid);*/
268 m_stack = new DebugStack(threadid);
269 g_debug_stacks[threadid] = m_stack;
270 }
271
272 if(m_stack->stack_i >= DEBUG_STACK_SIZE)
273 {
274 m_overflowed = true;
275 }
276 else
277 {
278 m_overflowed = false;
279
280 snprintf(m_stack->stack[m_stack->stack_i],
281 DEBUG_STACK_TEXT_SIZE, "%s", text);
282 m_stack->stack_i++;
283 if(m_stack->stack_i > m_stack->stack_max_i)
284 m_stack->stack_max_i = m_stack->stack_i;
285 }
286 }
287
~DebugStacker()288 DebugStacker::~DebugStacker()
289 {
290 JMutexAutoLock lock(g_debug_stacks_mutex);
291
292 if(m_overflowed == true)
293 return;
294
295 m_stack->stack_i--;
296
297 if(m_stack->stack_i == 0)
298 {
299 threadid_t threadid = m_stack->threadid;
300 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
301 (unsigned int)threadid);*/
302 delete m_stack;
303 g_debug_stacks.erase(threadid);
304 }
305 }
306
307
308 #ifdef _MSC_VER
309 #if CATCH_UNHANDLED_EXCEPTIONS == 1
se_trans_func(unsigned int u,EXCEPTION_POINTERS * pExp)310 void se_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp)
311 {
312 dstream<<"In trans_func.\n";
313 if(u == EXCEPTION_ACCESS_VIOLATION)
314 {
315 PEXCEPTION_RECORD r = pExp->ExceptionRecord;
316 dstream<<"Access violation at "<<r->ExceptionAddress
317 <<" write?="<<r->ExceptionInformation[0]
318 <<" address="<<r->ExceptionInformation[1]
319 <<std::endl;
320 throw FatalSystemException
321 ("Access violation");
322 }
323 if(u == EXCEPTION_STACK_OVERFLOW)
324 {
325 throw FatalSystemException
326 ("Stack overflow");
327 }
328 if(u == EXCEPTION_ILLEGAL_INSTRUCTION)
329 {
330 throw FatalSystemException
331 ("Illegal instruction");
332 }
333 }
334 #endif
335 #endif
336
337
338
339