Cleaned up a bit, removed todos for OpenThread, avoid TerminateThread
[wine] / dlls / ntdll / debugtools.c
1 /*
2  * Debugging functions
3  *
4  * Copyright 2000 Alexandre Julliard
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <assert.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <ctype.h>
27
28 #include "wine/debug.h"
29 #include "wine/exception.h"
30 #include "thread.h"
31 #include "winbase.h"
32 #include "winnt.h"
33 #include "ntddk.h"
34 #include "wtypes.h"
35 #include "msvcrt/excpt.h"
36
37 WINE_DECLARE_DEBUG_CHANNEL(tid);
38
39 /* ---------------------------------------------------------------------- */
40
41 struct debug_info
42 {
43     char *str_pos;       /* current position in strings buffer */
44     char *out_pos;       /* current position in output buffer */
45     char  strings[1024]; /* buffer for temporary strings */
46     char  output[1024];  /* current output line */
47 };
48
49 static struct debug_info initial_thread_info;  /* debug info for initial thread */
50
51 /* filter for page-fault exceptions */
52 static WINE_EXCEPTION_FILTER(page_fault)
53 {
54     if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
55         return EXCEPTION_EXECUTE_HANDLER;
56     return EXCEPTION_CONTINUE_SEARCH;
57 }
58
59 /* get the debug info pointer for the current thread */
60 static inline struct debug_info *get_info(void)
61 {
62     struct debug_info *info = NtCurrentTeb()->debug_info;
63
64     if (!info) NtCurrentTeb()->debug_info = info = &initial_thread_info;
65     if (!info->str_pos)
66     {
67         info->str_pos = info->strings;
68         info->out_pos = info->output;
69     }
70     return info;
71 }
72
73 /* allocate some tmp space for a string */
74 static void *gimme1(int n)
75 {
76     struct debug_info *info = get_info();
77     char *res = info->str_pos;
78
79     if (res + n >= &info->strings[sizeof(info->strings)]) res = info->strings;
80     info->str_pos = res + n;
81     return res;
82 }
83
84 /* release extra space that we requested in gimme1() */
85 static inline void release( void *ptr )
86 {
87     struct debug_info *info = NtCurrentTeb()->debug_info;
88     info->str_pos = ptr;
89 }
90
91 /* put an ASCII string into the debug buffer */
92 inline static char *put_string_a( const char *src, int n )
93 {
94     char *dst, *res;
95
96     if (n < 0) n = 0;
97     else if (n > 200) n = 200;
98     dst = res = gimme1 (n * 4 + 6);
99     *dst++ = '"';
100     while (n-- > 0 && *src)
101     {
102         unsigned char c = *src++;
103         switch (c)
104         {
105         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
106         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
107         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
108         case '"': *dst++ = '\\'; *dst++ = '"'; break;
109         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
110         default:
111             if (c >= ' ' && c <= 126)
112                 *dst++ = c;
113             else
114             {
115                 *dst++ = '\\';
116                 *dst++ = '0' + ((c >> 6) & 7);
117                 *dst++ = '0' + ((c >> 3) & 7);
118                 *dst++ = '0' + ((c >> 0) & 7);
119             }
120         }
121     }
122     *dst++ = '"';
123     if (*src)
124     {
125         *dst++ = '.';
126         *dst++ = '.';
127         *dst++ = '.';
128     }
129     *dst++ = '\0';
130     release( dst );
131     return res;
132 }
133
134 /* put a Unicode string into the debug buffer */
135 inline static char *put_string_w( const WCHAR *src, int n )
136 {
137     char *dst, *res;
138
139     if (n < 0) n = 0;
140     else if (n > 200) n = 200;
141     dst = res = gimme1 (n * 5 + 7);
142     *dst++ = 'L';
143     *dst++ = '"';
144     while (n-- > 0 && *src)
145     {
146         WCHAR c = *src++;
147         switch (c)
148         {
149         case '\n': *dst++ = '\\'; *dst++ = 'n'; break;
150         case '\r': *dst++ = '\\'; *dst++ = 'r'; break;
151         case '\t': *dst++ = '\\'; *dst++ = 't'; break;
152         case '"': *dst++ = '\\'; *dst++ = '"'; break;
153         case '\\': *dst++ = '\\'; *dst++ = '\\'; break;
154         default:
155             if (c >= ' ' && c <= 126)
156                 *dst++ = c;
157             else
158             {
159                 *dst++ = '\\';
160                 sprintf(dst,"%04x",c);
161                 dst+=4;
162             }
163         }
164     }
165     *dst++ = '"';
166     if (*src)
167     {
168         *dst++ = '.';
169         *dst++ = '.';
170         *dst++ = '.';
171     }
172     *dst++ = '\0';
173     release( dst );
174     return res;
175 }
176
177 /***********************************************************************
178  *              wine_dbgstr_an (NTDLL.@)
179  */
180 const char *wine_dbgstr_an( const char *src, int n )
181 {
182     char *res, *old_pos;
183     struct debug_info *info = get_info();
184
185     if (!HIWORD(src))
186     {
187         if (!src) return "(null)";
188         res = gimme1(6);
189         sprintf(res, "#%04x", LOWORD(src) );
190         return res;
191     }
192     /* save current position to restore it on exception */
193     old_pos = info->str_pos;
194     __TRY
195     {
196         res = put_string_a( src, n );
197     }
198     __EXCEPT(page_fault)
199     {
200         release( old_pos );
201         return "(invalid)";
202     }
203     __ENDTRY
204     return res;
205 }
206
207 /***********************************************************************
208  *              wine_dbgstr_wn (NTDLL.@)
209  */
210 const char *wine_dbgstr_wn( const WCHAR *src, int n )
211 {
212     char *res, *old_pos;
213     struct debug_info *info = get_info();
214
215     if (!HIWORD(src))
216     {
217         if (!src) return "(null)";
218         res = gimme1(6);
219         sprintf(res, "#%04x", LOWORD(src) );
220         return res;
221     }
222
223     /* save current position to restore it on exception */
224     old_pos = info->str_pos;
225     __TRY
226     {
227         res = put_string_w( src, n );
228     }
229     __EXCEPT(page_fault)
230     {
231         release( old_pos );
232         return "(invalid)";
233     }
234     __ENDTRY
235      return res;
236 }
237
238 /***********************************************************************
239  *              wine_dbgstr_guid (NTDLL.@)
240  */
241 const char *wine_dbgstr_guid( const GUID *id )
242 {
243     char *str;
244
245     if (!id) return "(null)";
246     if (!HIWORD(id))
247     {
248         str = gimme1(12);
249         sprintf( str, "<guid-0x%04x>", LOWORD(id) );
250     }
251     else
252     {
253         str = gimme1(40);
254         sprintf( str, "{%08lx-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x}",
255                  id->Data1, id->Data2, id->Data3,
256                  id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
257                  id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
258     }
259     return str;
260 }
261
262 /***********************************************************************
263  *              wine_dbg_vprintf (NTDLL.@)
264  */
265 int wine_dbg_vprintf( const char *format, va_list args )
266 {
267     struct debug_info *info = get_info();
268     char *p;
269
270     int ret = vsnprintf( info->out_pos, sizeof(info->output) - (info->out_pos - info->output),
271                          format, args );
272
273     /* make sure we didn't exceed the buffer length
274      * the two checks are due to glibc changes in vsnprintfs return value
275      * the buffer size can be exceeded in case of a missing \n in
276      * debug output */
277     if ((ret == -1) || (ret >= sizeof(info->output) - (info->out_pos - info->output)))
278     {
279        fprintf( stderr, "wine_dbg_vprintf: debugstr buffer overflow (contents: '%s')\n",
280                 info->output);
281        info->out_pos = info->output;
282        abort();
283     }
284
285     p = strrchr( info->out_pos, '\n' );
286     if (!p) info->out_pos += ret;
287     else
288     {
289         char *pos = info->output;
290         p++;
291         write( 2, pos, p - pos );
292         /* move beginning of next line to start of buffer */
293         while ((*pos = *p++)) pos++;
294         info->out_pos = pos;
295     }
296     return ret;
297 }
298
299 /***********************************************************************
300  *              wine_dbg_printf (NTDLL.@)
301  */
302 int wine_dbg_printf(const char *format, ...)
303 {
304     int ret;
305     va_list valist;
306
307     va_start(valist, format);
308     ret = wine_dbg_vprintf( format, valist );
309     va_end(valist);
310     return ret;
311 }
312
313 /***********************************************************************
314  *              wine_dbg_log (NTDLL.@)
315  */
316 int wine_dbg_log(enum __WINE_DEBUG_CLASS cls, const char *channel,
317                  const char *function, const char *format, ... )
318 {
319     static const char *classes[__WINE_DBCL_COUNT] = { "fixme", "err", "warn", "trace" };
320     va_list valist;
321     int ret = 0;
322
323     va_start(valist, format);
324     if (TRACE_ON(tid))
325         ret = wine_dbg_printf( "%08lx:", (DWORD)NtCurrentTeb()->tid );
326     if (cls < __WINE_DBCL_COUNT)
327         ret += wine_dbg_printf( "%s:%s:%s ", classes[cls], channel + 1, function );
328     if (format)
329         ret += wine_dbg_vprintf( format, valist );
330     va_end(valist);
331     return ret;
332 }