info.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  1. #include "info.h"
  2. #include "error.h"
  3. #include "fs.h"
  4. #include <cmath>
  5. #include <cstdlib>
  6. #if defined(_linux_) || defined(_cygwin_)
  7. #include <fcntl.h>
  8. #include <sys/sysinfo.h>
  9. #endif
  10. #if defined(_win_)
  11. #include "winint.h"
  12. #include <stdio.h>
  13. #else
  14. #include <unistd.h>
  15. #endif
  16. #if defined(_bionic_)
  17. // TODO
  18. #elif defined(_cygwin_)
  19. static int getloadavg(double* loadavg, int nelem) {
  20. for (int i = 0; i < nelem; ++i) {
  21. loadavg[i] = 0.0;
  22. }
  23. return nelem;
  24. }
  25. #elif defined(_unix_) || defined(_darwin_)
  26. #include <sys/types.h>
  27. #endif
  28. #if defined(_freebsd_) || defined(_darwin_)
  29. #include <sys/sysctl.h>
  30. #endif
  31. #include <util/string/ascii.h>
  32. #include <util/string/cast.h>
  33. #include <util/string/strip.h>
  34. #include <util/string/split.h>
  35. #include <util/stream/file.h>
  36. #include <util/generic/yexception.h>
  37. #if defined(_linux_)
  38. /*
  39. This function olny works properly if you apply correct setting to your nanny/deploy project
  40. In nanny - Runtime -> Instance spec -> Advanced settings -> Cgroupfs settings: Mount mode = Read only
  41. In deploy - Stage - Edit stage - Box - Cgroupfs settings: Mount mode = Read only
  42. */
  43. static inline double CgroupV1Cpus(const TString& cpuCfsQuotaUsPath, const TString& cfsPeriodUsPath) {
  44. try {
  45. double q = FromString<int32_t>(StripString(TFileInput(cpuCfsQuotaUsPath).ReadAll()));
  46. if (q <= 0) {
  47. return 0;
  48. }
  49. double p = FromString<int32_t>(StripString(TFileInput(cfsPeriodUsPath).ReadAll()));
  50. if (p <= 0) {
  51. return 0;
  52. }
  53. return q / p;
  54. } catch (...) {
  55. return 0;
  56. }
  57. }
  58. /*
  59. In cgroups v2 there isn't a dedicated "cpu" directory under /sys/fs/cgroup,
  60. so the approximation of the number of CPUs may use the cpu.max file.
  61. The format is the following:
  62. $MAX $PERIOD
  63. Which indicates that the group may consume up to $MAX in each $PERIOD duration.
  64. The "max" value could be either the string "max" or a number. In the first case
  65. our approximation doesn't work so we can bail out earlier.
  66. */
  67. static inline double CgroupV2Cpus(const TString& cpuMaxPath) {
  68. try {
  69. TVector<TString> cgroupCpuMax = StringSplitter(TFileInput(cpuMaxPath).ReadAll()).Split(' ').Take(2);
  70. double max = FromString<int32_t>(StripString(cgroupCpuMax[0]));
  71. double period = FromString<int32_t>(StripString(cgroupCpuMax[1]));
  72. if (max <= 0 || period <= 0) {
  73. return 0;
  74. }
  75. return max / period;
  76. } catch (...) {
  77. return 0;
  78. }
  79. }
  80. static inline double CgroupCpus() {
  81. static const TString cpuMaxPath("/sys/fs/cgroup/cpu.max");
  82. static const TString cpuCfsQuotaUsPath("/sys/fs/cgroup/cpu/cpu.cfs_quota_us");
  83. static const TString cfsPeriodUsPath("/sys/fs/cgroup/cpu/cpu.cfs_period_us");
  84. if (NFs::Exists(cpuMaxPath)) {
  85. auto cgroup2Cpus = CgroupV2Cpus(cpuMaxPath);
  86. if (cgroup2Cpus > 0) {
  87. return cgroup2Cpus;
  88. }
  89. }
  90. if (NFs::Exists(cpuCfsQuotaUsPath) && NFs::Exists(cfsPeriodUsPath)) {
  91. auto cgroups1Cpus = CgroupV1Cpus(cpuCfsQuotaUsPath, cfsPeriodUsPath);
  92. if (cgroups1Cpus > 0) {
  93. return cgroups1Cpus;
  94. }
  95. }
  96. return 0;
  97. }
  98. #endif
  99. size_t NSystemInfo::NumberOfMillicores() {
  100. #if defined(_linux_)
  101. return CgroupCpus() * 1000;
  102. #else
  103. // fallback behaviour if cgroupfs is not available
  104. // returns number of millicores which is a multiple of an integer number of cpus
  105. return NSystemInfo::NumberOfCpus() * 1000;
  106. #endif
  107. }
  108. size_t NSystemInfo::NumberOfCpus() {
  109. #if defined(_linux_)
  110. if (auto res = CgroupCpus(); res) {
  111. return Max<ssize_t>(1, std::llround(res));
  112. }
  113. #endif
  114. #if defined(_win_)
  115. SYSTEM_INFO info;
  116. GetSystemInfo(&info);
  117. return info.dwNumberOfProcessors;
  118. #elif defined(_SC_NPROCESSORS_ONLN)
  119. return sysconf(_SC_NPROCESSORS_ONLN);
  120. #elif defined(_linux_)
  121. unsigned ret;
  122. int fd, nread, column;
  123. char buf[512];
  124. static const char matchstr[] = "processor\t:";
  125. fd = open("/proc/cpuinfo", O_RDONLY);
  126. if (fd == -1) {
  127. abort();
  128. }
  129. column = 0;
  130. ret = 0;
  131. while (true) {
  132. nread = read(fd, buf, sizeof(buf));
  133. if (nread <= 0) {
  134. break;
  135. }
  136. for (int i = 0; i < nread; ++i) {
  137. const char ch = buf[i];
  138. if (ch == '\n') {
  139. column = 0;
  140. } else if (column != -1) {
  141. if (AsciiToLower(ch) == matchstr[column]) {
  142. ++column;
  143. if (column == sizeof(matchstr) - 1) {
  144. column = -1;
  145. ++ret;
  146. }
  147. } else {
  148. column = -1;
  149. }
  150. }
  151. }
  152. }
  153. if (ret == 0) {
  154. abort();
  155. }
  156. close(fd);
  157. return ret;
  158. #elif defined(_freebsd_) || defined(_darwin_)
  159. int mib[2];
  160. size_t len;
  161. unsigned ncpus = 1;
  162. mib[0] = CTL_HW;
  163. mib[1] = HW_NCPU;
  164. len = sizeof(ncpus);
  165. if (sysctl(mib, 2, &ncpus, &len, nullptr, 0) == -1) {
  166. abort();
  167. }
  168. return ncpus;
  169. #else
  170. #error todo
  171. #endif
  172. }
  173. size_t NSystemInfo::LoadAverage(double* la, size_t len) {
  174. #if defined(_win_) || defined(_musl_) || defined(_bionic_)
  175. int ret = -1;
  176. #else
  177. for (size_t i = 0; i < len; ++i) {
  178. la[i] = 0;
  179. }
  180. int ret = getloadavg(la, len);
  181. #endif
  182. if (ret < 0) {
  183. for (size_t i = 0; i < len; ++i) {
  184. la[i] = 0;
  185. }
  186. ret = len;
  187. }
  188. return (size_t)ret;
  189. }
  190. static size_t NCpus;
  191. static size_t NMillicores;
  192. size_t NSystemInfo::CachedNumberOfCpus() {
  193. if (!NCpus) {
  194. NCpus = NumberOfCpus();
  195. }
  196. return NCpus;
  197. }
  198. size_t NSystemInfo::CachedNumberOfMillicores() {
  199. if (!NMillicores) {
  200. NMillicores = NumberOfMillicores();
  201. }
  202. return NMillicores;
  203. }
  204. size_t NSystemInfo::GetPageSize() noexcept {
  205. #if defined(_win_)
  206. SYSTEM_INFO sysInfo;
  207. GetSystemInfo(&sysInfo);
  208. return sysInfo.dwPageSize;
  209. #else
  210. return sysconf(_SC_PAGESIZE);
  211. #endif
  212. }
  213. size_t NSystemInfo::TotalMemorySize() {
  214. #if defined(_linux_) && defined(_64_)
  215. try {
  216. auto q = FromString<size_t>(StripString(TFileInput("/sys/fs/cgroup/memory/memory.limit_in_bytes").ReadAll()));
  217. if (q < (((size_t)1) << 60)) {
  218. return q;
  219. }
  220. } catch (...) {
  221. }
  222. #endif
  223. #if defined(_linux_) || defined(_cygwin_)
  224. struct sysinfo info;
  225. sysinfo(&info);
  226. return info.totalram;
  227. #elif defined(_darwin_)
  228. int mib[2];
  229. int64_t memSize;
  230. size_t length;
  231. // Get the Physical memory size
  232. mib[0] = CTL_HW;
  233. mib[1] = HW_MEMSIZE;
  234. length = sizeof(int64_t);
  235. if (sysctl(mib, 2, &memSize, &length, NULL, 0) != 0) {
  236. ythrow yexception() << "sysctl failed: " << LastSystemErrorText();
  237. }
  238. return (size_t)memSize;
  239. #elif defined(_win_)
  240. MEMORYSTATUSEX memoryStatusEx;
  241. memoryStatusEx.dwLength = sizeof(memoryStatusEx);
  242. if (!GlobalMemoryStatusEx(&memoryStatusEx)) {
  243. ythrow yexception() << "GlobalMemoryStatusEx failed: " << LastSystemErrorText();
  244. }
  245. return (size_t)memoryStatusEx.ullTotalPhys;
  246. #else
  247. return 0;
  248. #endif
  249. }
  250. size_t NSystemInfo::MaxOpenFiles() {
  251. #if defined(ANDROID) || defined(__ANDROID__)
  252. return sysconf(_SC_OPEN_MAX);
  253. #elif defined(_win_)
  254. return _getmaxstdio();
  255. #else
  256. return getdtablesize();
  257. #endif
  258. }