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