]> git.lizzy.rs Git - dragonfireclient.git/blob - src/debug.h
Initially split utility.h to multiple files in util/
[dragonfireclient.git] / src / debug.h
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 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 #ifndef DEBUG_HEADER
21 #define DEBUG_HEADER
22
23 #include <stdio.h>
24 #include <jmutex.h>
25 #include <jmutexautolock.h>
26 #include <iostream>
27 #include "irrlichttypes.h"
28 #include "threads.h"
29 #include "gettime.h"
30 #include "exceptions.h"
31
32 #ifdef _WIN32
33         #define WIN32_LEAN_AND_MEAN
34         #include <windows.h>
35         #ifdef _MSC_VER
36                 #include <eh.h>
37         #endif
38 #else
39 #endif
40
41 // Whether to catch all std::exceptions.
42 // Assert will be called on such an event.
43 // In debug mode, leave these for the debugger and don't catch them.
44 #ifdef NDEBUG
45         #define CATCH_UNHANDLED_EXCEPTIONS 1
46 #else
47         #define CATCH_UNHANDLED_EXCEPTIONS 0
48 #endif
49
50 /*
51         Debug output
52 */
53
54 #define DTIME (getTimestamp()+": ")
55
56 #define DEBUGSTREAM_COUNT 2
57
58 extern FILE *g_debugstreams[DEBUGSTREAM_COUNT];
59
60 extern void debugstreams_init(bool disable_stderr, const char *filename);
61 extern void debugstreams_deinit();
62
63 #define DEBUGPRINT(...)\
64 {\
65         for(int i=0; i<DEBUGSTREAM_COUNT; i++)\
66         {\
67                 if(g_debugstreams[i] != NULL){\
68                         fprintf(g_debugstreams[i], __VA_ARGS__);\
69                         fflush(g_debugstreams[i]);\
70                 }\
71         }\
72 }
73
74 class Debugbuf : public std::streambuf
75 {
76 public:
77         Debugbuf(bool disable_stderr)
78         {
79                 m_disable_stderr = disable_stderr;
80         }
81
82         int overflow(int c)
83         {
84                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
85                 {
86                         if(g_debugstreams[i] == stderr && m_disable_stderr)
87                                 continue;
88                         if(g_debugstreams[i] != NULL)
89                                 (void)fwrite(&c, 1, 1, g_debugstreams[i]);
90                         //TODO: Is this slow?
91                         fflush(g_debugstreams[i]);
92                 }
93                 
94                 return c;
95         }
96         std::streamsize xsputn(const char *s, std::streamsize n)
97         {
98                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
99                 {
100                         if(g_debugstreams[i] == stderr && m_disable_stderr)
101                                 continue;
102                         if(g_debugstreams[i] != NULL)
103                                 (void)fwrite(s, 1, n, g_debugstreams[i]);
104                         //TODO: Is this slow?
105                         fflush(g_debugstreams[i]);
106                 }
107
108                 return n;
109         }
110         
111 private:
112         bool m_disable_stderr;
113 };
114
115 // This is used to redirect output to /dev/null
116 class Nullstream : public std::ostream {
117 public:
118         Nullstream():
119                 std::ostream(0)
120         {
121         }
122 private:
123 };
124
125 extern Debugbuf debugbuf;
126 extern std::ostream dstream;
127 extern std::ostream dstream_no_stderr;
128 extern Nullstream dummyout;
129
130 /*
131         Assert
132 */
133
134 __NORETURN extern void assert_fail(
135                 const char *assertion, const char *file,
136                 unsigned int line, const char *function);
137
138 #define ASSERT(expr)\
139         ((expr)\
140         ? (void)(0)\
141         : assert_fail(#expr, __FILE__, __LINE__, __FUNCTION_NAME))
142
143 #define assert(expr) ASSERT(expr)
144
145 /*
146         DebugStack
147 */
148
149 #define DEBUG_STACK_SIZE 50
150 #define DEBUG_STACK_TEXT_SIZE 300
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
164 extern core::map<threadid_t, DebugStack*> g_debug_stacks;
165 extern JMutex g_debug_stacks_mutex;
166
167 extern void debug_stacks_init();
168 extern void debug_stacks_print_to(std::ostream &os);
169 extern void debug_stacks_print();
170
171 class DebugStacker
172 {
173 public:
174         DebugStacker(const char *text);
175         ~DebugStacker();
176
177 private:
178         DebugStack *m_stack;
179         bool m_overflowed;
180 };
181
182 #define DSTACK(msg)\
183         DebugStacker __debug_stacker(msg);
184
185 #define DSTACKF(...)\
186         char __buf[DEBUG_STACK_TEXT_SIZE];\
187         snprintf(__buf,\
188                         DEBUG_STACK_TEXT_SIZE, __VA_ARGS__);\
189         DebugStacker __debug_stacker(__buf);
190
191 /*
192         Packet counter
193 */
194
195 class PacketCounter
196 {
197 public:
198         PacketCounter()
199         {
200         }
201
202         void add(u16 command)
203         {
204                 core::map<u16, u16>::Node *n = m_packets.find(command);
205                 if(n == NULL)
206                 {
207                         m_packets[command] = 1;
208                 }
209                 else
210                 {
211                         n->setValue(n->getValue()+1);
212                 }
213         }
214
215         void clear()
216         {
217                 for(core::map<u16, u16>::Iterator
218                                 i = m_packets.getIterator();
219                                 i.atEnd() == false; i++)
220                 {
221                         i.getNode()->setValue(0);
222                 }
223         }
224
225         void print(std::ostream &o)
226         {
227                 for(core::map<u16, u16>::Iterator
228                                 i = m_packets.getIterator();
229                                 i.atEnd() == false; i++)
230                 {
231                         o<<"cmd "<<i.getNode()->getKey()
232                                         <<" count "<<i.getNode()->getValue()
233                                         <<std::endl;
234                 }
235         }
236
237 private:
238         // command, count
239         core::map<u16, u16> m_packets;
240 };
241
242 /*
243         These should be put into every thread
244 */
245
246 #if CATCH_UNHANDLED_EXCEPTIONS == 1
247         #define BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER try{
248         #define END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)\
249                 }catch(std::exception &e){\
250                         logstream<<"ERROR: An unhandled exception occurred: "\
251                                         <<e.what()<<std::endl;\
252                         assert(0);\
253                 }
254         #ifdef _WIN32 // Windows
255                 #ifdef _MSC_VER // MSVC
256 void se_trans_func(unsigned int, EXCEPTION_POINTERS*);
257
258 class FatalSystemException : public BaseException
259 {
260 public:
261         FatalSystemException(const char *s):
262                 BaseException(s)
263         {}
264 };
265                         #define BEGIN_DEBUG_EXCEPTION_HANDLER \
266                                 BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER\
267                                 _set_se_translator(se_trans_func);
268
269                         #define END_DEBUG_EXCEPTION_HANDLER(logstream) \
270                                 END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
271                 #else // Probably mingw
272                         #define BEGIN_DEBUG_EXCEPTION_HANDLER\
273                                 BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
274                         #define END_DEBUG_EXCEPTION_HANDLER(logstream)\
275                                 END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
276                 #endif
277         #else // Posix
278                 #define BEGIN_DEBUG_EXCEPTION_HANDLER\
279                         BEGIN_PORTABLE_DEBUG_EXCEPTION_HANDLER
280                 #define END_DEBUG_EXCEPTION_HANDLER(logstream)\
281                         END_PORTABLE_DEBUG_EXCEPTION_HANDLER(logstream)
282         #endif
283 #else
284         // Dummy ones
285         #define BEGIN_DEBUG_EXCEPTION_HANDLER
286         #define END_DEBUG_EXCEPTION_HANDLER(logstream)
287 #endif
288
289 #endif // DEBUG_HEADER
290
291