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