• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**************************************************************************
2  *
3  * Copyright 2008-2010 VMware, Inc.
4  * All Rights Reserved.
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a
7  * copy of this software and associated documentation files (the
8  * "Software"), to deal in the Software without restriction, including
9  * without limitation the rights to use, copy, modify, merge, publish,
10  * distribute, sub license, and/or sell copies of the Software, and to
11  * permit persons to whom the Software is furnished to do so, subject to
12  * the following conditions:
13  *
14  * The above copyright notice and this permission notice (including the
15  * next paragraph) shall be included in all copies or substantial portions
16  * of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
19  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
21  * IN NO EVENT SHALL VMWARE AND/OR ITS SUPPLIERS BE LIABLE FOR
22  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
23  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
24  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25  *
26  **************************************************************************/
27 
28 
29 #include "hash_table.h"
30 #include "macros.h"
31 #include "os_misc.h"
32 #include "os_file.h"
33 #include "ralloc.h"
34 #include "simple_mtx.h"
35 
36 #include <stdarg.h>
37 
38 
39 #if DETECT_OS_WINDOWS
40 
41 #include <windows.h>
42 #include <stdio.h>
43 #include <stdlib.h>
44 
45 #else
46 
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <inttypes.h>
51 
52 #endif
53 
54 
55 #if DETECT_OS_ANDROID
56 #  define LOG_TAG "MESA"
57 #  include <unistd.h>
58 #  include <log/log.h>
59 #  include <cutils/properties.h>
60 #elif DETECT_OS_LINUX || DETECT_OS_CYGWIN || DETECT_OS_SOLARIS || DETECT_OS_HURD || DETECT_OS_MANAGARM
61 #  include <unistd.h>
62 #elif DETECT_OS_OPENBSD || DETECT_OS_FREEBSD
63 #  include <sys/resource.h>
64 #  include <sys/sysctl.h>
65 #elif DETECT_OS_APPLE || DETECT_OS_BSD
66 #  include <sys/sysctl.h>
67 #  if DETECT_OS_APPLE
68 #    include <mach/mach_host.h>
69 #    include <mach/vm_param.h>
70 #    include <mach/vm_statistics.h>
71 #   endif
72 #elif DETECT_OS_HAIKU
73 #  include <kernel/OS.h>
74 #elif DETECT_OS_WINDOWS
75 #  include <windows.h>
76 #elif DETECT_OS_FUCHSIA
77 #include <unistd.h>
78 #include <zircon/syscalls.h>
79 #else
80 #error unexpected platform in os_sysinfo.c
81 #endif
82 
83 
84 void
os_log_message(const char * message)85 os_log_message(const char *message)
86 {
87    /* If the GALLIUM_LOG_FILE environment variable is set to a valid filename,
88     * write all messages to that file.
89     */
90    static FILE *fout = NULL;
91 
92    if (!fout) {
93 #if MESA_DEBUG
94       /* one-time init */
95       const char *filename = os_get_option("GALLIUM_LOG_FILE");
96       if (filename) {
97          if (strcmp(filename, "stdout") == 0) {
98             fout = stdout;
99          } else {
100             const char *mode = "w";
101             if (filename[0] == '+') {
102                /* If the filename is prefixed with '+' then open the file for
103                 * appending instead of normal writing.
104                 */
105                mode = "a";
106             filename++; /* skip the '+' */
107             }
108             fout = fopen(filename, mode);
109          }
110       }
111 #endif
112       if (!fout)
113          fout = stderr;
114    }
115 
116 #if DETECT_OS_WINDOWS
117    OutputDebugStringA(message);
118 #if !defined(_GAMING_XBOX)
119    if(GetConsoleWindow() && !IsDebuggerPresent()) {
120       fflush(stdout);
121       fputs(message, fout);
122       fflush(fout);
123    }
124    else if (fout != stderr) {
125       fputs(message, fout);
126       fflush(fout);
127    }
128 #endif
129 #else /* !DETECT_OS_WINDOWS */
130    fflush(stdout);
131    fputs(message, fout);
132    fflush(fout);
133 #  if DETECT_OS_ANDROID
134    LOG_PRI(ANDROID_LOG_ERROR, LOG_TAG, "%s", message);
135 #  endif
136 #endif
137 }
138 
139 #if DETECT_OS_ANDROID
140 #  include <ctype.h>
141 #  include "c11/threads.h"
142 
143 /**
144  * Get an option value from android's property system, as a fallback to
145  * getenv() (which is generally less useful on android due to processes
146  * typically being forked from the zygote.
147  *
148  * The option name used for getenv is translated into a property name
149  * by:
150  *
151  *  1) convert to lowercase
152  *  2) replace '_' with '.'
153  *  3) replace "MESA_" or prepend with "mesa."
154  *  4) look for "debug.mesa." prefix
155  *  5) look for "vendor.mesa." prefix
156  *  6) look for "mesa." prefix
157  *
158  * For example:
159  *  - MESA_EXTENSION_OVERRIDE -> mesa.extension.override
160  *  - GALLIUM_HUD -> mesa.gallium.hud
161  *
162  */
163 static char *
os_get_android_option(const char * name)164 os_get_android_option(const char *name)
165 {
166    static thread_local char os_android_option_value[PROPERTY_VALUE_MAX];
167    char key[PROPERTY_KEY_MAX];
168    char *p = key, *end = key + PROPERTY_KEY_MAX;
169    /* add "mesa." prefix if necessary: */
170    if (strstr(name, "MESA_") != name)
171       p += strlcpy(p, "mesa.", end - p);
172    p += strlcpy(p, name, end - p);
173    for (int i = 0; key[i]; i++) {
174       if (key[i] == '_') {
175          key[i] = '.';
176       } else {
177          key[i] = tolower(key[i]);
178       }
179    }
180 
181    /* prefixes to search sorted by preference */
182    const char *prefices[] = { "debug.", "vendor.", "" };
183    char full_key[PROPERTY_KEY_MAX];
184    int len = 0;
185    for (int i = 0; i < ARRAY_SIZE(prefices); i++) {
186       strlcpy(full_key, prefices[i], PROPERTY_KEY_MAX);
187       strlcat(full_key, key, PROPERTY_KEY_MAX);
188       len = property_get(full_key, os_android_option_value, NULL);
189       if (len > 0)
190          return os_android_option_value;
191    }
192    return NULL;
193 }
194 #endif
195 
196 #if DETECT_OS_WINDOWS
197 
198 /* getenv doesn't necessarily reflect changes to the environment
199  * that have been made during the process lifetime, if either the
200  * setter uses a different CRT (e.g. due to static linking) or the
201  * setter used the Win32 API directly. */
202 const char *
os_get_option(const char * name)203 os_get_option(const char *name)
204 {
205    static thread_local char value[_MAX_ENV];
206    DWORD size = GetEnvironmentVariableA(name, value, _MAX_ENV);
207    return (size > 0 && size < _MAX_ENV) ? value : NULL;
208 }
209 
210 #else
211 
212 const char *
os_get_option(const char * name)213 os_get_option(const char *name)
214 {
215    const char *opt = getenv(name);
216 #if DETECT_OS_ANDROID
217    if (!opt) {
218       opt = os_get_android_option(name);
219    }
220 #endif
221    return opt;
222 }
223 
224 #endif
225 
226 static struct hash_table *options_tbl;
227 static bool options_tbl_exited = false;
228 static simple_mtx_t options_tbl_mtx = SIMPLE_MTX_INITIALIZER;
229 
230 /**
231  * NOTE: The strings that allocated with ralloc_strdup(options_tbl, ...)
232  * are freed by _mesa_hash_table_destroy automatically
233  */
234 static void
options_tbl_fini(void)235 options_tbl_fini(void)
236 {
237    simple_mtx_lock(&options_tbl_mtx);
238    _mesa_hash_table_destroy(options_tbl, NULL);
239    options_tbl = NULL;
240    options_tbl_exited = true;
241    simple_mtx_unlock(&options_tbl_mtx);
242 }
243 
244 const char *
os_get_option_cached(const char * name)245 os_get_option_cached(const char *name)
246 {
247    const char *opt = NULL;
248    simple_mtx_lock(&options_tbl_mtx);
249    if (options_tbl_exited) {
250       opt = os_get_option(name);
251       goto exit_mutex;
252    }
253 
254    if (!options_tbl) {
255       options_tbl = _mesa_hash_table_create(NULL, _mesa_hash_string,
256             _mesa_key_string_equal);
257       if (options_tbl == NULL) {
258          goto exit_mutex;
259       }
260       atexit(options_tbl_fini);
261    }
262    struct hash_entry *entry = _mesa_hash_table_search(options_tbl, name);
263    if (entry) {
264       opt = entry->data;
265       goto exit_mutex;
266    }
267 
268    char *name_dup = ralloc_strdup(options_tbl, name);
269    if (name_dup == NULL) {
270       goto exit_mutex;
271    }
272    opt = ralloc_strdup(options_tbl, os_get_option(name));
273    _mesa_hash_table_insert(options_tbl, name_dup, (void *)opt);
274 exit_mutex:
275    simple_mtx_unlock(&options_tbl_mtx);
276    return opt;
277 }
278 
279 /**
280  * Return the size of the total physical memory.
281  * \param size returns the size of the total physical memory
282  * \return true for success, or false on failure
283  */
284 bool
os_get_total_physical_memory(uint64_t * size)285 os_get_total_physical_memory(uint64_t *size)
286 {
287 #if DETECT_OS_LINUX || DETECT_OS_CYGWIN || DETECT_OS_SOLARIS || DETECT_OS_HURD || DETECT_OS_MANAGARM
288    const long phys_pages = sysconf(_SC_PHYS_PAGES);
289    const long page_size = sysconf(_SC_PAGE_SIZE);
290 
291    if (phys_pages <= 0 || page_size <= 0)
292       return false;
293 
294    *size = (uint64_t)phys_pages * (uint64_t)page_size;
295    return true;
296 #elif DETECT_OS_APPLE || DETECT_OS_BSD
297    size_t len = sizeof(*size);
298    int mib[2];
299 
300    mib[0] = CTL_HW;
301 #if DETECT_OS_APPLE
302    mib[1] = HW_MEMSIZE;
303 #elif DETECT_OS_NETBSD || DETECT_OS_OPENBSD
304    mib[1] = HW_PHYSMEM64;
305 #elif DETECT_OS_FREEBSD
306    mib[1] = HW_REALMEM;
307 #elif DETECT_OS_DRAGONFLY
308    mib[1] = HW_PHYSMEM;
309 #else
310 #error Unsupported *BSD
311 #endif
312 
313    return (sysctl(mib, 2, size, &len, NULL, 0) == 0);
314 #elif DETECT_OS_HAIKU
315    system_info info;
316    status_t ret;
317 
318    ret = get_system_info(&info);
319    if (ret != B_OK || info.max_pages <= 0)
320       return false;
321 
322    *size = (uint64_t)info.max_pages * (uint64_t)B_PAGE_SIZE;
323    return true;
324 #elif DETECT_OS_WINDOWS
325    MEMORYSTATUSEX status;
326    BOOL ret;
327 
328    status.dwLength = sizeof(status);
329    ret = GlobalMemoryStatusEx(&status);
330    *size = status.ullTotalPhys;
331    return (ret == true);
332 #elif DETECT_OS_FUCHSIA
333    *size = zx_system_get_physmem();
334    return true;
335 #else
336 #error unexpected platform in os_misc.c
337    return false;
338 #endif
339 }
340 
341 bool
os_get_available_system_memory(uint64_t * size)342 os_get_available_system_memory(uint64_t *size)
343 {
344 #if DETECT_OS_LINUX
345    char *meminfo = os_read_file("/proc/meminfo", NULL);
346    if (!meminfo)
347       return false;
348 
349    char *str = strstr(meminfo, "MemAvailable:");
350    if (!str) {
351       free(meminfo);
352       return false;
353    }
354 
355    uint64_t kb_mem_available;
356    if (sscanf(str, "MemAvailable: %" PRIu64, &kb_mem_available) == 1) {
357       free(meminfo);
358       *size = kb_mem_available << 10;
359       return true;
360    }
361 
362    free(meminfo);
363    return false;
364 #elif DETECT_OS_OPENBSD || DETECT_OS_FREEBSD
365    struct rlimit rl;
366 #if DETECT_OS_OPENBSD
367    int mib[] = { CTL_HW, HW_USERMEM64 };
368 #elif DETECT_OS_FREEBSD
369    int mib[] = { CTL_HW, HW_USERMEM };
370 #endif
371    int64_t mem_available;
372    size_t len = sizeof(mem_available);
373 
374    /* physmem - wired */
375    if (sysctl(mib, 2, &mem_available, &len, NULL, 0) == -1)
376       return false;
377 
378    /* static login.conf limit */
379    if (getrlimit(RLIMIT_DATA, &rl) == -1)
380       return false;
381 
382    *size = MIN2(mem_available, rl.rlim_cur);
383    return true;
384 #elif DETECT_OS_WINDOWS
385    MEMORYSTATUSEX status;
386    BOOL ret;
387 
388    status.dwLength = sizeof(status);
389    ret = GlobalMemoryStatusEx(&status);
390    *size = status.ullAvailPhys;
391    return (ret == true);
392 #elif DETECT_OS_APPLE
393    vm_statistics64_data_t vm_stats;
394    mach_msg_type_number_t count = HOST_VM_INFO64_COUNT;
395    if (host_statistics64(mach_host_self(), HOST_VM_INFO,
396          (host_info64_t)&vm_stats, &count) != KERN_SUCCESS) {
397       return false;
398    }
399 
400    *size = ((uint64_t)vm_stats.free_count + (uint64_t)vm_stats.inactive_count) * PAGE_SIZE;
401    return true;
402 #else
403    return false;
404 #endif
405 }
406 
407 /**
408  * Return the size of a page
409  * \param size returns the size of a page
410  * \return true for success, or false on failure
411  */
412 bool
os_get_page_size(uint64_t * size)413 os_get_page_size(uint64_t *size)
414 {
415 #if DETECT_OS_POSIX_LITE && !DETECT_OS_APPLE && !DETECT_OS_HAIKU
416    const long page_size = sysconf(_SC_PAGE_SIZE);
417 
418    if (page_size <= 0)
419       return false;
420 
421    *size = (uint64_t)page_size;
422    return true;
423 #elif DETECT_OS_HAIKU
424    *size = (uint64_t)B_PAGE_SIZE;
425    return true;
426 #elif DETECT_OS_WINDOWS
427    SYSTEM_INFO SysInfo;
428 
429    GetSystemInfo(&SysInfo);
430    *size = SysInfo.dwPageSize;
431    return true;
432 #elif DETECT_OS_APPLE
433    *size = PAGE_SIZE;
434    return true;
435 #else
436 #error unexpected platform in os_sysinfo.c
437    return false;
438 #endif
439 }
440