Initial files
[oweals/minetest.git] / src / debug.cpp
1 /*
2 (c) 2010 Perttu Ahola <celeron55@gmail.com>
3 */
4
5 #include "debug.h"
6 #include <stdio.h>
7 #include <stdlib.h>
8
9 #ifdef _WIN32
10         #define WIN32_LEAN_AND_MEAN
11         #include <windows.h>
12         #define sleep_ms(x) Sleep(x)
13 #else
14         #include <unistd.h>
15         #define sleep_ms(x) usleep(x*1000)
16 #endif
17
18 /*
19         Debug output
20 */
21
22 FILE *g_debugstreams[DEBUGSTREAM_COUNT] = {stderr, NULL};
23
24 void debugstreams_init(bool disable_stderr, const char *filename)
25 {
26         if(disable_stderr)
27                 g_debugstreams[0] = NULL;
28
29         if(filename)
30                 g_debugstreams[1] = fopen(filename, "a");
31                 
32         if(g_debugstreams[1])
33         {
34                 fprintf(g_debugstreams[1], "\n\n-------------\n");
35                 fprintf(g_debugstreams[1],     "  Separator  \n");
36                 fprintf(g_debugstreams[1],     "-------------\n\n");
37         }
38 }
39
40 void debugstreams_deinit()
41 {
42         if(g_debugstreams[1] != NULL)
43                 fclose(g_debugstreams[1]);
44 }
45
46 Debugbuf debugbuf(false);
47 std::ostream dstream(&debugbuf);
48 Debugbuf debugbuf_no_stderr(true);
49 std::ostream dstream_no_stderr(&debugbuf_no_stderr);
50 Nullstream dummyout;
51
52 /*
53         Assert
54 */
55
56 void assert_fail(const char *assertion, const char *file,
57                 unsigned int line, const char *function)
58 {
59         DEBUGPRINT("\nIn thread %x:\n"
60                         "%s:%d: %s: Assertion '%s' failed.\n",
61                         (unsigned int)get_current_thread_id(),
62                         file, line, function, assertion);
63         
64         debug_stacks_print();
65
66         if(g_debugstreams[1])
67                 fclose(g_debugstreams[1]);
68
69         //sleep_ms(3000);
70
71         abort();
72 }
73
74 /*
75         DebugStack
76 */
77
78 DebugStack::DebugStack(threadid_t id)
79 {
80         threadid = id;
81         stack_i = 0;
82         stack_max_i = 0;
83 }
84
85 void DebugStack::print(FILE *file, bool everything)
86 {
87         fprintf(file, "BEGIN STACK: Debug stack for thread %x:\n",
88                         (unsigned int)threadid);
89
90         for(int i=0; i<stack_max_i; i++)
91         {
92                 if(i == stack_i && everything == false)
93                         continue;
94
95                 if(everything == true && i == stack_i)
96                         fprintf(file, "END OF STACK.\n"
97                                         "! Continuing beyond stack end:\n");
98
99                 fprintf(file, "#%d  %s\n", i, stack[i]);
100         }
101
102         if(stack_i == DEBUG_STACK_SIZE)
103                 fprintf(file, "Probably overflown.\n");
104 }
105
106
107 core::map<threadid_t, DebugStack*> g_debug_stacks;
108 JMutex g_debug_stacks_mutex;
109
110 void debug_stacks_init()
111 {
112         g_debug_stacks_mutex.Init();
113 }
114
115 void debug_stacks_print()
116 {
117         JMutexAutoLock lock(g_debug_stacks_mutex);
118
119         DEBUGPRINT("Debug stacks:\n");
120
121         for(core::map<threadid_t, DebugStack*>::Iterator
122                         i = g_debug_stacks.getIterator();
123                         i.atEnd() == false; i++)
124         {
125                 DebugStack *stack = i.getNode()->getValue();
126
127                 for(int i=0; i<DEBUGSTREAM_COUNT; i++)
128                 {
129                         if(g_debugstreams[i] != NULL)
130                                 stack->print(g_debugstreams[i], true);
131                 }
132         }
133 }
134
135 DebugStacker::DebugStacker(const char *text)
136 {
137         threadid_t threadid = get_current_thread_id();
138
139         JMutexAutoLock lock(g_debug_stacks_mutex);
140
141         core::map<threadid_t, DebugStack*>::Node *n;
142         n = g_debug_stacks.find(threadid);
143         if(n != NULL)
144         {
145                 m_stack = n->getValue();
146         }
147         else
148         {
149                 /*DEBUGPRINT("Creating new debug stack for thread %x\n",
150                                 (unsigned int)threadid);*/
151                 m_stack = new DebugStack(threadid);
152                 g_debug_stacks.insert(threadid, m_stack);
153         }
154
155         if(m_stack->stack_i >= DEBUG_STACK_SIZE)
156         {
157                 m_overflowed = true;
158         }
159         else
160         {
161                 m_overflowed = false;
162
163                 snprintf(m_stack->stack[m_stack->stack_i],
164                                 DEBUG_STACK_TEXT_SIZE, "%s", text);
165                 m_stack->stack_i++;
166                 if(m_stack->stack_i > m_stack->stack_max_i)
167                         m_stack->stack_max_i = m_stack->stack_i;
168         }
169 }
170
171 DebugStacker::~DebugStacker()
172 {
173         JMutexAutoLock lock(g_debug_stacks_mutex);
174         
175         if(m_overflowed == true)
176                 return;
177         
178         m_stack->stack_i--;
179
180         if(m_stack->stack_i == 0)
181         {
182                 threadid_t threadid = m_stack->threadid;
183                 /*DEBUGPRINT("Deleting debug stack for thread %x\n",
184                                 (unsigned int)threadid);*/
185                 delete m_stack;
186                 g_debug_stacks.remove(threadid);
187         }
188 }
189