1 /**************************************************************************
2  *
3  * Copyright 2008 VMware, Inc.
4  * Copyright (c) 2008 VMware, Inc.
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a
8  * copy of this software and associated documentation files (the
9  * "Software"), to deal in the Software without restriction, including
10  * without limitation the rights to use, copy, modify, merge, publish,
11  * distribute, sub license, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to
13  * the following conditions:
14  *
15  * The above copyright notice and this permission notice (including the
16  * next paragraph) shall be included in all copies or substantial portions
17  * of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
23  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  *
27  **************************************************************************/
28 
29 #include "util/u_atomic.h"
30 #include "util/u_debug.h"
31 #include "util/u_string.h"
32 #include "util/u_math.h"
33 #include <inttypes.h>
34 
35 #include <stdio.h>
36 #include <limits.h> /* CHAR_BIT */
37 #include <ctype.h> /* isalnum */
38 
39 #ifdef _WIN32
40 #include <windows.h>
41 #include <stdlib.h>
42 #endif
43 
44 
45 void
_debug_vprintf(const char * format,va_list ap)46 _debug_vprintf(const char *format, va_list ap)
47 {
48    static char buf[4096] = {'\0'};
49 #if DETECT_OS_WINDOWS
50    /* We buffer until we find a newline. */
51    size_t len = strlen(buf);
52    int ret = vsnprintf(buf + len, sizeof(buf) - len, format, ap);
53    if (ret > (int)(sizeof(buf) - len - 1) || strchr(buf + len, '\n')) {
54       os_log_message(buf);
55       buf[0] = '\0';
56    }
57 #else
58    vsnprintf(buf, sizeof(buf), format, ap);
59    os_log_message(buf);
60 #endif
61 }
62 
63 
64 void
_util_debug_message(struct util_debug_callback * cb,unsigned * id,enum util_debug_type type,const char * fmt,...)65 _util_debug_message(struct util_debug_callback *cb,
66                     unsigned *id,
67                     enum util_debug_type type,
68                     const char *fmt, ...)
69 {
70    if (!cb || !cb->debug_message)
71       return;
72    va_list args;
73    va_start(args, fmt);
74    cb->debug_message(cb->data, id, type, fmt, args);
75    va_end(args);
76 }
77 
78 
79 #ifdef _WIN32
80 void
debug_disable_win32_error_dialogs(void)81 debug_disable_win32_error_dialogs(void)
82 {
83    /* When Windows' error message boxes are disabled for this process (as is
84     * typically the case when running tests in an automated fashion) we disable
85     * CRT message boxes too.
86     */
87    UINT uMode = SetErrorMode(0);
88    SetErrorMode(uMode);
89 #ifndef _GAMING_XBOX
90    if (uMode & SEM_FAILCRITICALERRORS) {
91       /* Disable assertion failure message box.
92        * http://msdn.microsoft.com/en-us/library/sas1dkb2.aspx
93        */
94       _set_error_mode(_OUT_TO_STDERR);
95 #ifdef _MSC_VER
96       /* Disable abort message box.
97        * http://msdn.microsoft.com/en-us/library/e631wekh.aspx
98        */
99       _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT);
100 #endif
101    }
102 #endif /* _GAMING_XBOX */
103 }
104 #endif /* _WIN32 */
105 
106 bool
debug_parse_bool_option(const char * str,bool dfault)107 debug_parse_bool_option(const char *str, bool dfault)
108 {
109    bool result;
110 
111    if (str == NULL)
112       result = dfault;
113    else if (!strcmp(str, "0"))
114       result = false;
115    else if (!strcasecmp(str, "n"))
116       result = false;
117    else if (!strcasecmp(str, "no"))
118       result = false;
119    else if (!strcasecmp(str, "f"))
120       result = false;
121    else if (!strcasecmp(str, "false"))
122       result = false;
123    else if (!strcmp(str, "1"))
124       result = true;
125    else if (!strcasecmp(str, "y"))
126       result = true;
127    else if (!strcasecmp(str, "yes"))
128       result = true;
129    else if (!strcasecmp(str, "t"))
130       result = true;
131    else if (!strcasecmp(str, "true"))
132       result = true;
133    else
134       result = dfault;
135    return result;
136 }
137 
138 static bool
debug_get_option_should_print(void)139 debug_get_option_should_print(void)
140 {
141    static bool initialized = false;
142    static bool value = false;
143 
144    if (unlikely(!p_atomic_read_relaxed(&initialized))) {
145       bool parsed_value = debug_parse_bool_option(os_get_option("GALLIUM_PRINT_OPTIONS"), false);
146       p_atomic_set(&value, parsed_value);
147       p_atomic_set(&initialized, true);
148    }
149 
150    /* We do not print value of GALLIUM_PRINT_OPTIONS intentionally. */
151    return value;
152 }
153 
154 
155 const char *
debug_get_option(const char * name,const char * dfault)156 debug_get_option(const char *name, const char *dfault)
157 {
158    const char *result;
159 
160    result = os_get_option(name);
161    if (!result)
162       result = dfault;
163 
164    if (debug_get_option_should_print())
165       debug_printf("%s: %s = %s\n", __func__, name,
166                    result ? result : "(null)");
167 
168    return result;
169 }
170 
171 
172 const char *
debug_get_option_cached(const char * name,const char * dfault)173 debug_get_option_cached(const char *name, const char *dfault)
174 {
175    const char *result;
176 
177    result = os_get_option_cached(name);
178    if (!result)
179       result = dfault;
180 
181    if (debug_get_option_should_print())
182       debug_printf("%s: %s = %s\n", __FUNCTION__, name,
183                    result ? result : "(null)");
184 
185    return result;
186 }
187 
188 
189 /**
190  * Reads an environment variable and interprets its value as a boolean.
191  * Recognizes 0/n/no/f/false case insensitive as false.
192  * Recognizes 1/y/yes/t/true case insensitive as true.
193  * Other values result in the default value.
194  */
195 bool
debug_get_bool_option(const char * name,bool dfault)196 debug_get_bool_option(const char *name, bool dfault)
197 {
198    bool result = debug_parse_bool_option(os_get_option(name), dfault);
199    if (debug_get_option_should_print())
200       debug_printf("%s: %s = %s\n", __func__, name,
201                    result ? "TRUE" : "FALSE");
202 
203    return result;
204 }
205 
206 
207 int64_t
debug_parse_num_option(const char * str,int64_t dfault)208 debug_parse_num_option(const char *str, int64_t dfault)
209 {
210    int64_t result;
211    if (!str) {
212       result = dfault;
213    } else {
214       char *endptr;
215 
216       result = strtoll(str, &endptr, 0);
217       if (str == endptr) {
218          /* Restore the default value when no digits were found. */
219          result = dfault;
220       }
221    }
222    return result;
223 }
224 
225 int64_t
debug_get_num_option(const char * name,int64_t dfault)226 debug_get_num_option(const char *name, int64_t dfault)
227 {
228    int64_t result = debug_parse_num_option(os_get_option(name), dfault);
229 
230    if (debug_get_option_should_print())
231       debug_printf("%s: %s = %"PRId64"\n", __func__, name, result);
232 
233    return result;
234 }
235 
236 void
debug_get_version_option(const char * name,unsigned * major,unsigned * minor)237 debug_get_version_option(const char *name, unsigned *major, unsigned *minor)
238 {
239    const char *str;
240 
241    str = os_get_option(name);
242    if (str) {
243       unsigned v_maj, v_min;
244       int n;
245 
246       n = sscanf(str, "%u.%u", &v_maj, &v_min);
247       if (n != 2) {
248          debug_printf("Illegal version specified for %s : %s\n", name, str);
249          return;
250       }
251       *major = v_maj;
252       *minor = v_min;
253    }
254 
255    if (debug_get_option_should_print())
256       debug_printf("%s: %s = %u.%u\n", __func__, name, *major, *minor);
257 
258    return;
259 }
260 
261 static bool
str_has_option(const char * str,const char * name)262 str_has_option(const char *str, const char *name)
263 {
264    /* Empty string. */
265    if (!*str) {
266       return false;
267    }
268 
269    /* OPTION=all */
270    if (!strcmp(str, "all")) {
271       return true;
272    }
273 
274    /* Find 'name' in 'str' surrounded by non-alphanumeric characters. */
275    {
276       const char *start = str;
277       unsigned name_len = strlen(name);
278 
279       /* 'start' is the beginning of the currently-parsed word,
280        * we increment 'str' each iteration.
281        * if we find either the end of string or a non-alphanumeric character,
282        * we compare 'start' up to 'str-1' with 'name'. */
283 
284       while (1) {
285          if (!*str || !(isalnum(*str) || *str == '_')) {
286             if (str-start == name_len &&
287                 !memcmp(start, name, name_len)) {
288                return true;
289             }
290 
291             if (!*str) {
292                return false;
293             }
294 
295             start = str+1;
296          }
297 
298          str++;
299       }
300    }
301 
302    return false;
303 }
304 
305 
306 uint64_t
debug_parse_flags_option(const char * name,const char * str,const struct debug_named_value * flags,uint64_t dfault)307 debug_parse_flags_option(const char *name,
308                          const char *str,
309                          const struct debug_named_value *flags,
310                          uint64_t dfault)
311 {
312    uint64_t result;
313    const struct debug_named_value *orig = flags;
314    unsigned namealign = 0;
315 
316    if (!str)
317       result = dfault;
318    else if (!strcmp(str, "help")) {
319       result = dfault;
320       _debug_printf("%s: help for %s:\n", __func__, name);
321       for (; flags->name; ++flags)
322          namealign = MAX2(namealign, strlen(flags->name));
323       for (flags = orig; flags->name; ++flags)
324          _debug_printf("| %*s [0x%0*"PRIx64"]%s%s\n", namealign, flags->name,
325                       (int)sizeof(uint64_t)*CHAR_BIT/4, flags->value,
326                       flags->desc ? " " : "", flags->desc ? flags->desc : "");
327    }
328    else {
329       result = 0;
330       while (flags->name) {
331          if (str_has_option(str, flags->name))
332             result |= flags->value;
333          ++flags;
334       }
335    }
336 
337    return result;
338 }
339 
340 uint64_t
debug_get_flags_option(const char * name,const struct debug_named_value * flags,uint64_t dfault)341 debug_get_flags_option(const char *name,
342                        const struct debug_named_value *flags,
343                        uint64_t dfault)
344 {
345    const char *str = os_get_option(name);
346    uint64_t result = debug_parse_flags_option(name, str, flags, dfault);
347 
348    if (debug_get_option_should_print()) {
349       if (str) {
350          debug_printf("%s: %s = 0x%"PRIx64" (%s)\n",
351                       __func__, name, result, str);
352       } else {
353          debug_printf("%s: %s = 0x%"PRIx64"\n", __func__, name, result);
354       }
355    }
356 
357    return result;
358 }
359 
360 
361 const char *
debug_dump_enum(const struct debug_named_value * names,uint64_t value)362 debug_dump_enum(const struct debug_named_value *names,
363                 uint64_t value)
364 {
365    static char rest[64];
366 
367    while (names->name) {
368       if (names->value == value)
369          return names->name;
370       ++names;
371    }
372 
373    snprintf(rest, sizeof(rest), "0x%08"PRIx64, value);
374    return rest;
375 }
376 
377 
378 const char *
debug_dump_flags(const struct debug_named_value * names,uint64_t value)379 debug_dump_flags(const struct debug_named_value *names, uint64_t value)
380 {
381    static char output[4096];
382    static char rest[256];
383    int first = 1;
384 
385    output[0] = '\0';
386 
387    while (names->name) {
388       if ((names->value & value) == names->value) {
389          if (!first)
390             strncat(output, "|", sizeof(output) - strlen(output) - 1);
391          else
392             first = 0;
393          strncat(output, names->name, sizeof(output) - strlen(output) - 1);
394          output[sizeof(output) - 1] = '\0';
395          value &= ~names->value;
396       }
397       ++names;
398    }
399 
400    if (value) {
401       if (!first)
402          strncat(output, "|", sizeof(output) - strlen(output) - 1);
403       else
404          first = 0;
405 
406       snprintf(rest, sizeof(rest), "0x%08"PRIx64, value);
407       strncat(output, rest, sizeof(output) - strlen(output) - 1);
408       output[sizeof(output) - 1] = '\0';
409    }
410 
411    if (first)
412       return "0";
413 
414    return output;
415 }
416 
417 
418 uint64_t
parse_debug_string(const char * debug,const struct debug_control * control)419 parse_debug_string(const char *debug,
420                    const struct debug_control *control)
421 {
422    uint64_t flag = 0;
423 
424    if (debug != NULL) {
425       for (; control->string != NULL; control++) {
426          if (!strcmp(debug, "all")) {
427             flag |= control->flag;
428 
429          } else {
430             const char *s = debug;
431             unsigned n;
432 
433             for (; n = strcspn(s, ", "), *s; s += MAX2(1, n)) {
434                if (strlen(control->string) == n &&
435                    !strncmp(control->string, s, n))
436                   flag |= control->flag;
437             }
438          }
439       }
440    }
441 
442    return flag;
443 }
444 
445 
446 uint64_t
parse_enable_string(const char * debug,uint64_t default_value,const struct debug_control * control)447 parse_enable_string(const char *debug,
448                     uint64_t default_value,
449                     const struct debug_control *control)
450 {
451    uint64_t flag = default_value;
452 
453    if (debug != NULL) {
454       for (; control->string != NULL; control++) {
455          if (!strcmp(debug, "all")) {
456             flag |= control->flag;
457 
458          } else {
459             const char *s = debug;
460             unsigned n;
461 
462             for (; n = strcspn(s, ", "), *s; s += MAX2(1, n)) {
463                bool enable;
464                if (s[0] == '+') {
465                   enable = true;
466                   s++; n--;
467                } else if (s[0] == '-') {
468                   enable = false;
469                   s++; n--;
470                } else {
471                   enable = true;
472                }
473                if (strlen(control->string) == n &&
474                    !strncmp(control->string, s, n)) {
475                   if (enable)
476                      flag |= control->flag;
477                   else
478                      flag &= ~control->flag;
479                }
480             }
481          }
482       }
483    }
484 
485    return flag;
486 }
487 
488 
489 bool
comma_separated_list_contains(const char * list,const char * s)490 comma_separated_list_contains(const char *list, const char *s)
491 {
492    assert(list);
493    const size_t len = strlen(s);
494 
495    for (unsigned n; n = strcspn(list, ","), *list; list += MAX2(1, n)) {
496       if (n == len && !strncmp(list, s, n))
497          return true;
498    }
499 
500    return false;
501 }
502