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