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