doc updates; CMake works reasonably well now.
[oweals/minetest.git] / src / debug.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
14
15 You should have received a copy of the GNU 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 "debug.h"
22 #include <stdio.h>
23 #include <stdlib.h>
24
25 /*
26         Debug output
27 */
28
29 FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
30
31 void debugstreams_init(bool disable_stderr, const char *filename)
32 {
33         if(disable_stderr)
34                 g_debugstreams[0] = NULL;
35
36         if(filename)
37                 g_debugstreams[1] = fopen(filename, "a");
38                 
39         if(g_debugstreams[1])
40         {
41                 fprintf(g_debugstreams[1], "\n\n-------------\n");
42                 fprintf(g_debugstreams[1],     "  Separator  \n");
43                 fprintf(g_debugstreams[1],     "-------------\n\n");
44         }
45 }
46
47 void debugstreams_deinit()
48 {
49         if(g_debugstreams[1] != NULL)
50                 fclose(g_debugstreams[1]);
51 }
52
53 Debugbuf debugbuf(false);
54 std::ostream dstream(&debugbuf);
55 Debugbuf debugbuf_no_stderr(true);
56 std::ostream dstream_no_stderr(&debugbuf_no_stderr);
57 Nullstream dummyout;
58
59 /*
60         Assert
61 */
62
63 void assert_fail(const char *assertion, const char *file,
64                 unsigned int line, const char *function)
65 {
66         DEBUGPRINT("\nIn thread %x:\n"
67                         "%s:%d: %s: Assertion '%s' failed.\n",
68                         (unsigned int)get_current_thread_id(),
69                         file, line, function, assertion);
70         
71         debug_stacks_print();
72
73         if(g_debugstreams[1])
74                 fclose(g_debugstreams[1]);
75
76         abort();
77 }
78
79 /*
80         DebugStack
81 */
82
83 DebugStack::DebugStack(threadid_t id)
84 {
85         threadid = id;
86         stack_i = 0;
87         stack_max_i = 0;
88 }
89
90 void DebugStack::print(FILE *file, bool everything)
91 {
92         fprintf(file, "DEBUG STACK FOR THREAD %x:\n",
93                         (unsigned int)threadid);
94
95         for(int i=0; i<stack_max_i; i++)
96         {
97                 if(i == stack_i && everything == false)
98                         continue;
99
100                 if(i < stack_i)
101                         fprintf(file, "#%d  %s\n", i, stack[i]);
102                 else
103                         fprintf(file, "(Leftover data: #%d  %s)\n", i, stack[i]);
104         }
105
106         if(stack_i == DEBUG_STACK_SIZE)
107                 fprintf(file, "Probably overflown.\n");
108 }
109
110
111 core::map<threadid_t, DebugStack*> g_debug_stacks;
112 JMutex g_debug_stacks_mutex;
113
114 void debug_stacks_init()
115 {
116         g_debug_stacks_mutex.Init();
117 }
118
119 void debug_stacks_print()
120 {
121         JMutexAutoLock lock(g_debug_stacks_mutex);
122
123         DEBUGPRINT("Debug stacks:\n");
124
125         for(core::map<threadid_t, DebugStack*>::Iterator
126                         i = g_debug_stacks.getIterator();
127                         i.atEnd() == false; i++)
128         {
129                 DebugStack *stack = i.getNode()->getValue();
130
131                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
132                 {
133                         if(g_debugstreams[i] != NULL)
134                                 stack->print(g_debugstreams[i], true);
135                 }
136         }
137 }
138
139 DebugStacker::DebugStacker(const char *text)
140 {
141         threadid_t threadid = get_current_thread_id();
142
143         JMutexAutoLock lock(g_debug_stacks_mutex);
144
145         core::map<threadid_t, DebugStack*>::Node *n;
146         n = g_debug_stacks.find(threadid);
147         if(n != NULL)
148         {
149                 m_stack = n->getValue();
150         }
151         else
152         {
153                 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
154                                 (unsigned int)threadid);*/
155                 m_stack = new DebugStack(threadid);
156                 g_debug_stacks.insert(threadid, m_stack);
157         }
158
159         if(m_stack->stack_i >= DEBUG_STACK_SIZE)
160         {
161                 m_overflowed = true;
162         }
163         else
164         {
165                 m_overflowed = false;
166
167                 snprintf(m_stack->stack[m_stack->stack_i],
168                                 DEBUG_STACK_TEXT_SIZE, "%s", text);
169                 m_stack->stack_i++;
170                 if(m_stack->stack_i > m_stack->stack_max_i)
171                         m_stack->stack_max_i = m_stack->stack_i;
172         }
173 }
174
175 DebugStacker::~DebugStacker()
176 {
177         JMutexAutoLock lock(g_debug_stacks_mutex);
178         
179         if(m_overflowed == true)
180                 return;
181         
182         m_stack->stack_i--;
183
184         if(m_stack->stack_i == 0)
185         {
186                 threadid_t threadid = m_stack->threadid;
187                 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
188                                 (unsigned int)threadid);*/
189                 delete m_stack;
190                 g_debug_stacks.remove(threadid);
191         }
192 }
193
194
195 #ifdef _WIN32
196 void se_trans_func(unsigned int u, EXCEPTION_POINTERS* pExp)
197 {
198         dstream<<"In trans_func.\n";
199         if(u == EXCEPTION_ACCESS_VIOLATION)
200         {
201                 PEXCEPTION_RECORD r = pExp->ExceptionRecord;
202                 dstream<<"Access violation at "<<r->ExceptionAddress
203                                 <<" write?="<<r->ExceptionInformation[0]
204                                 <<" address="<<r->ExceptionInformation[1]
205                                 <<std::endl;
206                 throw FatalSystemException
207                 ("Access violation");
208         }
209         if(u == EXCEPTION_STACK_OVERFLOW)
210         {
211                 throw FatalSystemException
212                 ("Stack overflow");
213         }
214         if(u == EXCEPTION_ILLEGAL_INSTRUCTION)
215         {
216                 throw FatalSystemException
217                 ("Illegal instruction");
218         }
219 }
220 #endif
221
222
223