The open source OpenXR runtime
1// Copyright 2019-2023, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief Small debug helpers.
6 * @author Jakob Bornecrantz <jakob@collabora.com>
7 * @ingroup aux_util
8 *
9 * Debug get option helpers heavily inspired from mesa ones.
10 */
11
12#include "xrt/xrt_config_os.h"
13
14#include "util/u_debug.h"
15#include "util/u_logging.h"
16
17#include <ctype.h>
18#include <stdio.h>
19#include <stdlib.h>
20#include <string.h>
21
22#ifdef XRT_OS_ANDROID
23#include <sys/system_properties.h>
24#endif
25
26
27DEBUG_GET_ONCE_BOOL_OPTION(print, "XRT_PRINT_OPTIONS", false)
28
29
30/*
31 *
32 * Helpers
33 *
34 */
35
36#if defined XRT_OS_WINDOWS
37
38static const char *
39get_option_raw(char *chars, size_t char_count, const char *name)
40{
41 size_t required_size;
42
43 getenv_s(&required_size, chars, char_count, name);
44 if (required_size == 0) {
45 return NULL;
46 }
47
48 return chars;
49}
50
51#elif defined XRT_OS_ANDROID
52
53struct android_read_arg
54{
55 char *chars;
56 size_t char_count;
57};
58
59static void
60android_on_property_read(void *cookie, const char *name, const char *value, uint32_t serial)
61{
62 struct android_read_arg *a = (struct android_read_arg *)cookie;
63
64 snprintf(a->chars, a->char_count, "%s", value);
65}
66
67static const char *
68get_option_raw(char *chars, size_t char_count, const char *name)
69{
70 char prefixed[1024];
71 snprintf(prefixed, ARRAY_SIZE(prefixed), "debug.xrt.%s", name);
72
73 const struct prop_info *pi = __system_property_find(prefixed);
74 if (pi == NULL) {
75 return NULL;
76 }
77
78 struct android_read_arg a = {.chars = chars, .char_count = char_count};
79 __system_property_read_callback(pi, &android_on_property_read, &a);
80
81 return chars;
82}
83
84#elif defined XRT_OS_LINUX
85
86static const char *
87get_option_raw(char *chars, size_t char_count, const char *name)
88{
89 const char *raw = getenv(name);
90
91 if (raw == NULL) {
92 return NULL;
93 } else {
94 snprintf(chars, char_count, "%s", raw);
95 return chars;
96 }
97}
98
99#else
100#error "Please provide port of this function!"
101#endif
102
103
104static const char *
105level_to_str(enum u_logging_level level)
106{
107 switch (level) {
108 case U_LOGGING_TRACE: return "trace";
109 case U_LOGGING_DEBUG: return "debug";
110 case U_LOGGING_INFO: return "info";
111 case U_LOGGING_WARN: return "warn";
112 case U_LOGGING_ERROR: return "error";
113 default: return "invalid";
114 }
115}
116
117static const char *
118tristate_to_str(enum debug_tristate_option tristate)
119{
120 switch (tristate) {
121 case DEBUG_TRISTATE_OFF: return "OFF";
122 case DEBUG_TRISTATE_AUTO: return "AUTO";
123 case DEBUG_TRISTATE_ON: return "ON";
124 default: return "invalid";
125 }
126}
127
128/*!
129 * This function checks @p str if it matches @p matches, it returns true as long
130 * as the complete @p str is in the starts of @p matches. Empty string does not
131 * match.
132 */
133static bool
134is_str_in_start_of(const char *str, const char *matches)
135{
136 if (str[0] == '\0') {
137 return false;
138 }
139
140 for (int i = 0; str[i] != '\0'; i++) {
141 if (matches[i] == '\0') {
142 return false;
143 }
144 if (matches[i] != tolower(str[i])) {
145 return false;
146 }
147 }
148
149 return true;
150}
151
152
153/*
154 *
155 * 'Exported' conversion functions.
156 *
157 */
158
159bool
160debug_string_to_bool(const char *string)
161{
162 if (string == NULL) {
163 return false;
164 } else if (!strcmp(string, "false")) {
165 return false;
166 } else if (!strcmp(string, "FALSE")) {
167 return false;
168 } else if (!strcmp(string, "off")) {
169 return false;
170 } else if (!strcmp(string, "OFF")) {
171 return false;
172 } else if (!strcmp(string, "no")) {
173 return false;
174 } else if (!strcmp(string, "NO")) {
175 return false;
176 } else if (!strcmp(string, "n")) {
177 return false;
178 } else if (!strcmp(string, "N")) {
179 return false;
180 } else if (!strcmp(string, "f")) {
181 return false;
182 } else if (!strcmp(string, "F")) {
183 return false;
184 } else if (!strcmp(string, "0")) {
185 return false;
186 } else {
187 return true;
188 }
189}
190
191enum debug_tristate_option
192debug_string_to_tristate(const char *string)
193{
194 if (string == NULL) {
195 return DEBUG_TRISTATE_AUTO;
196 } else if (!strcmp(string, "AUTO")) {
197 return DEBUG_TRISTATE_AUTO;
198 } else if (!strcmp(string, "auto")) {
199 return DEBUG_TRISTATE_AUTO;
200 } else if (!strcmp(string, "a")) {
201 return DEBUG_TRISTATE_AUTO;
202 } else if (!strcmp(string, "A")) {
203 return DEBUG_TRISTATE_AUTO;
204 } else {
205 if (debug_string_to_bool(string)) {
206 return DEBUG_TRISTATE_ON;
207 } else {
208 return DEBUG_TRISTATE_OFF;
209 }
210 }
211}
212
213long
214debug_string_to_num(const char *string, long _default)
215{
216 if (string == NULL) {
217 return _default;
218 }
219
220 char *endptr;
221 long ret = strtol(string, &endptr, 0);
222
223 // Restore the default value when no digits were found.
224 if (string == endptr) {
225 return _default;
226 }
227
228 return ret;
229}
230
231float
232debug_string_to_float(const char *string, float _default)
233{
234 if (string == NULL) {
235 return _default;
236 }
237
238 char *endptr;
239 float ret = strtof(string, &endptr);
240
241 // Restore the default value when no digits were found.
242 if (string == endptr) {
243 return _default;
244 }
245
246 return ret;
247}
248
249enum u_logging_level
250debug_string_to_log_level(const char *string, enum u_logging_level _default)
251{
252 if (string == NULL) {
253 return _default;
254 } else if (is_str_in_start_of(string, "trace")) {
255 return U_LOGGING_TRACE;
256 } else if (is_str_in_start_of(string, "debug")) {
257 return U_LOGGING_DEBUG;
258 } else if (is_str_in_start_of(string, "info")) {
259 return U_LOGGING_INFO;
260 } else if (is_str_in_start_of(string, "warn")) {
261 return U_LOGGING_WARN;
262 } else if (is_str_in_start_of(string, "error")) {
263 return U_LOGGING_ERROR;
264 } else {
265 return _default;
266 }
267}
268
269
270/*
271 *
272 * 'Exported' debug value getters.
273 *
274 */
275
276const char *
277debug_get_option(char *chars, size_t char_count, const char *name, const char *_default)
278{
279 const char *raw = get_option_raw(chars, char_count, name);
280 const char *ret = raw;
281
282 if (ret == NULL) {
283 if (_default != NULL) {
284 snprintf(chars, char_count, "%s", _default);
285 ret = chars; // Return a value.
286 }
287 }
288
289 if (debug_get_bool_option_print()) {
290 U_LOG_RAW("%s=%s (%s)", name, ret, raw == NULL ? "nil" : raw);
291 }
292
293 return ret;
294}
295
296bool
297debug_get_bool_option(const char *name, bool _default)
298{
299 char chars[DEBUG_CHAR_STORAGE_SIZE];
300 const char *raw = get_option_raw(chars, ARRAY_SIZE(chars), name);
301
302 bool ret = raw == NULL ? _default : debug_string_to_bool(raw);
303
304 if (debug_get_bool_option_print()) {
305 U_LOG_RAW("%s=%s (%s)", name, ret ? "TRUE" : "FALSE", raw == NULL ? "nil" : raw);
306 }
307
308 return ret;
309}
310
311enum debug_tristate_option
312debug_get_tristate_option(const char *name)
313{
314 char chars[DEBUG_CHAR_STORAGE_SIZE];
315 const char *raw = get_option_raw(chars, ARRAY_SIZE(chars), name);
316
317 enum debug_tristate_option ret = debug_string_to_tristate(raw);
318
319 if (debug_get_bool_option_print()) {
320 const char *pretty_val = tristate_to_str(ret);
321 U_LOG_RAW("%s=%s (%s)", name, pretty_val, raw == NULL ? "nil" : raw);
322 }
323
324 return ret;
325}
326
327long
328debug_get_num_option(const char *name, long _default)
329{
330 char chars[DEBUG_CHAR_STORAGE_SIZE];
331 const char *raw = get_option_raw(chars, ARRAY_SIZE(chars), name);
332
333 long ret = debug_string_to_num(raw, _default);
334
335 if (debug_get_bool_option_print()) {
336 U_LOG_RAW("%s=%li (%s)", name, ret, raw == NULL ? "nil" : raw);
337 }
338
339 return ret;
340}
341
342float
343debug_get_float_option(const char *name, float _default)
344{
345 char chars[DEBUG_CHAR_STORAGE_SIZE];
346 const char *raw = get_option_raw(chars, ARRAY_SIZE(chars), name);
347
348 float ret = debug_string_to_float(raw, _default);
349
350 if (debug_get_bool_option_print()) {
351 U_LOG_RAW("%s=%f (%s)", name, ret, raw == NULL ? "nil" : raw);
352 }
353
354 return ret;
355}
356
357enum u_logging_level
358debug_get_log_option(const char *name, enum u_logging_level _default)
359{
360 char chars[DEBUG_CHAR_STORAGE_SIZE];
361 const char *raw = get_option_raw(chars, ARRAY_SIZE(chars), name);
362
363 enum u_logging_level ret = debug_string_to_log_level(raw, _default);
364
365 if (debug_get_bool_option_print()) {
366 const char *pretty_val = level_to_str(ret);
367 U_LOG_RAW("%s=%s (%s)", name, pretty_val, raw == NULL ? "nil" : raw);
368 }
369
370 return ret;
371}