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