meminfo.c 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. /* Copyright (c) 2003-2004, Roger Dingledine
  2. * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
  3. * Copyright (c) 2007-2018, The Tor Project, Inc. */
  4. /* See LICENSE for licensing information */
  5. /**
  6. * \file meminfo.c
  7. *
  8. * \brief Functions to query total memory, and access meta-information about
  9. * the allocator.
  10. **/
  11. #include "lib/meminfo/meminfo.h"
  12. #include "lib/cc/compat_compiler.h"
  13. #include "lib/cc/torint.h"
  14. #include "lib/fs/files.h"
  15. #include "lib/log/log.h"
  16. #include "lib/malloc/malloc.h"
  17. #ifdef HAVE_SYS_SYSCTL_H
  18. #include <sys/sysctl.h>
  19. #endif
  20. #ifdef HAVE_FCNTL_H
  21. #include <fcntl.h>
  22. #endif
  23. #ifdef HAVE_MALLOC_H
  24. #include <malloc.h>
  25. #endif
  26. #ifdef HAVE_UNISTD_H
  27. #include <unistd.h>
  28. #endif
  29. #ifdef _WIN32
  30. #include <windows.h>
  31. #endif
  32. #include <string.h>
  33. DISABLE_GCC_WARNING(aggregate-return)
  34. /** Call the platform malloc info function, and dump the results to the log at
  35. * level <b>severity</b>. If no such function exists, do nothing. */
  36. void
  37. tor_log_mallinfo(int severity)
  38. {
  39. #ifdef HAVE_MALLINFO
  40. struct mallinfo mi;
  41. memset(&mi, 0, sizeof(mi));
  42. mi = mallinfo();
  43. tor_log(severity, LD_MM,
  44. "mallinfo() said: arena=%d, ordblks=%d, smblks=%d, hblks=%d, "
  45. "hblkhd=%d, usmblks=%d, fsmblks=%d, uordblks=%d, fordblks=%d, "
  46. "keepcost=%d",
  47. mi.arena, mi.ordblks, mi.smblks, mi.hblks,
  48. mi.hblkhd, mi.usmblks, mi.fsmblks, mi.uordblks, mi.fordblks,
  49. mi.keepcost);
  50. #else /* !(defined(HAVE_MALLINFO)) */
  51. (void)severity;
  52. #endif /* defined(HAVE_MALLINFO) */
  53. }
  54. ENABLE_GCC_WARNING(aggregate-return)
  55. #if defined(HW_PHYSMEM64)
  56. /* This appears to be an OpenBSD thing */
  57. #define INT64_HW_MEM HW_PHYSMEM64
  58. #elif defined(HW_MEMSIZE)
  59. /* OSX defines this one */
  60. #define INT64_HW_MEM HW_MEMSIZE
  61. #endif /* defined(HW_PHYSMEM64) || ... */
  62. /**
  63. * Helper: try to detect the total system memory, and return it. On failure,
  64. * return 0.
  65. */
  66. static uint64_t
  67. get_total_system_memory_impl(void)
  68. {
  69. #if defined(__linux__)
  70. /* On linux, sysctl is deprecated. Because proc is so awesome that you
  71. * shouldn't _want_ to write portable code, I guess? */
  72. unsigned long long result=0;
  73. int fd = -1;
  74. char *s = NULL;
  75. const char *cp;
  76. size_t file_size=0;
  77. if (-1 == (fd = tor_open_cloexec("/proc/meminfo",O_RDONLY,0)))
  78. return 0;
  79. s = read_file_to_str_until_eof(fd, 65536, &file_size);
  80. if (!s)
  81. goto err;
  82. cp = strstr(s, "MemTotal:");
  83. if (!cp)
  84. goto err;
  85. /* Use the system sscanf so that space will match a wider number of space */
  86. if (sscanf(cp, "MemTotal: %llu kB\n", &result) != 1)
  87. goto err;
  88. close(fd);
  89. tor_free(s);
  90. return result * 1024;
  91. /* LCOV_EXCL_START Can't reach this unless proc is broken. */
  92. err:
  93. tor_free(s);
  94. close(fd);
  95. return 0;
  96. /* LCOV_EXCL_STOP */
  97. #elif defined (_WIN32)
  98. /* Windows has MEMORYSTATUSEX; pretty straightforward. */
  99. MEMORYSTATUSEX ms;
  100. memset(&ms, 0, sizeof(ms));
  101. ms.dwLength = sizeof(ms);
  102. if (! GlobalMemoryStatusEx(&ms))
  103. return 0;
  104. return ms.ullTotalPhys;
  105. #elif defined(HAVE_SYSCTL) && defined(INT64_HW_MEM)
  106. /* On many systems, HW_PYHSMEM is clipped to 32 bits; let's use a better
  107. * variant if we know about it. */
  108. uint64_t memsize = 0;
  109. size_t len = sizeof(memsize);
  110. int mib[2] = {CTL_HW, INT64_HW_MEM};
  111. if (sysctl(mib,2,&memsize,&len,NULL,0))
  112. return 0;
  113. return memsize;
  114. #elif defined(HAVE_SYSCTL) && defined(HW_PHYSMEM)
  115. /* On some systems (like FreeBSD I hope) you can use a size_t with
  116. * HW_PHYSMEM. */
  117. size_t memsize=0;
  118. size_t len = sizeof(memsize);
  119. int mib[2] = {CTL_HW, HW_USERMEM};
  120. if (sysctl(mib,2,&memsize,&len,NULL,0))
  121. return 0;
  122. return memsize;
  123. #else
  124. /* I have no clue. */
  125. return 0;
  126. #endif /* defined(__linux__) || ... */
  127. }
  128. /**
  129. * Try to find out how much physical memory the system has. On success,
  130. * return 0 and set *<b>mem_out</b> to that value. On failure, return -1.
  131. */
  132. MOCK_IMPL(int,
  133. get_total_system_memory, (size_t *mem_out))
  134. {
  135. static size_t mem_cached=0;
  136. uint64_t m = get_total_system_memory_impl();
  137. if (0 == m) {
  138. /* LCOV_EXCL_START -- can't make this happen without mocking. */
  139. /* We couldn't find our memory total */
  140. if (0 == mem_cached) {
  141. /* We have no cached value either */
  142. *mem_out = 0;
  143. return -1;
  144. }
  145. *mem_out = mem_cached;
  146. return 0;
  147. /* LCOV_EXCL_STOP */
  148. }
  149. #if SIZE_MAX != UINT64_MAX
  150. if (m > SIZE_MAX) {
  151. /* I think this could happen if we're a 32-bit Tor running on a 64-bit
  152. * system: we could have more system memory than would fit in a
  153. * size_t. */
  154. m = SIZE_MAX;
  155. }
  156. #endif /* SIZE_MAX != UINT64_MAX */
  157. *mem_out = mem_cached = (size_t) m;
  158. return 0;
  159. }