mem_info.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. #include "mem_info.h"
  2. #include <util/generic/strbuf.h>
  3. #include <util/generic/utility.h>
  4. #include <util/generic/yexception.h>
  5. #include <util/stream/file.h>
  6. #include <util/string/cast.h>
  7. #include <util/string/builder.h>
  8. #include "error.h"
  9. #include "info.h"
  10. #if defined(_unix_)
  11. #include <errno.h>
  12. #include <unistd.h>
  13. #if defined(_freebsd_)
  14. #include <sys/sysctl.h>
  15. #include <sys/types.h>
  16. #include <sys/user.h>
  17. #elif defined(_darwin_) && !defined(_arm_) && !defined(__IOS__)
  18. #include <libproc.h>
  19. #elif defined(__MACH__) && defined(__APPLE__)
  20. #include <mach/mach.h>
  21. #endif
  22. #elif defined(_win_)
  23. #include <Windows.h>
  24. #include <util/generic/ptr.h>
  25. using NTSTATUS = LONG;
  26. #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
  27. #define STATUS_BUFFER_TOO_SMALL 0xC0000023
  28. typedef struct _UNICODE_STRING {
  29. USHORT Length;
  30. USHORT MaximumLength;
  31. PWSTR Buffer;
  32. } UNICODE_STRING, *PUNICODE_STRING;
  33. typedef struct _CLIENT_ID {
  34. HANDLE UniqueProcess;
  35. HANDLE UniqueThread;
  36. } CLIENT_ID, *PCLIENT_ID;
  37. using KWAIT_REASON = ULONG;
  38. typedef struct _SYSTEM_THREAD_INFORMATION {
  39. LARGE_INTEGER KernelTime;
  40. LARGE_INTEGER UserTime;
  41. LARGE_INTEGER CreateTime;
  42. ULONG WaitTime;
  43. PVOID StartAddress;
  44. CLIENT_ID ClientId;
  45. LONG Priority;
  46. LONG BasePriority;
  47. ULONG ContextSwitches;
  48. ULONG ThreadState;
  49. KWAIT_REASON WaitReason;
  50. } SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION;
  51. typedef struct _SYSTEM_PROCESS_INFORMATION {
  52. ULONG NextEntryOffset;
  53. ULONG NumberOfThreads;
  54. LARGE_INTEGER SpareLi1;
  55. LARGE_INTEGER SpareLi2;
  56. LARGE_INTEGER SpareLi3;
  57. LARGE_INTEGER CreateTime;
  58. LARGE_INTEGER UserTime;
  59. LARGE_INTEGER KernelTime;
  60. UNICODE_STRING ImageName;
  61. LONG BasePriority;
  62. HANDLE UniqueProcessId;
  63. HANDLE InheritedFromUniqueProcessId;
  64. ULONG HandleCount;
  65. ULONG SessionId;
  66. ULONG_PTR PageDirectoryBase;
  67. SIZE_T PeakVirtualSize;
  68. SIZE_T VirtualSize;
  69. DWORD PageFaultCount;
  70. SIZE_T PeakWorkingSetSize;
  71. SIZE_T WorkingSetSize;
  72. SIZE_T QuotaPeakPagedPoolUsage;
  73. SIZE_T QuotaPagedPoolUsage;
  74. SIZE_T QuotaPeakNonPagedPoolUsage;
  75. SIZE_T QuotaNonPagedPoolUsage;
  76. SIZE_T PagefileUsage;
  77. SIZE_T PeakPagefileUsage;
  78. SIZE_T PrivatePageCount;
  79. LARGE_INTEGER ReadOperationCount;
  80. LARGE_INTEGER WriteOperationCount;
  81. LARGE_INTEGER OtherOperationCount;
  82. LARGE_INTEGER ReadTransferCount;
  83. LARGE_INTEGER WriteTransferCount;
  84. LARGE_INTEGER OtherTransferCount;
  85. SYSTEM_THREAD_INFORMATION Threads[1];
  86. } SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
  87. typedef enum _SYSTEM_INFORMATION_CLASS {
  88. SystemBasicInformation = 0,
  89. SystemProcessInformation = 5,
  90. } SYSTEM_INFORMATION_CLASS;
  91. #else
  92. #endif
  93. namespace NMemInfo {
  94. TMemInfo GetMemInfo(pid_t pid) {
  95. TMemInfo result;
  96. #if defined(_unix_)
  97. #if defined(_linux_) || defined(_freebsd_) || defined(_cygwin_)
  98. const ui32 pagesize = NSystemInfo::GetPageSize();
  99. #endif
  100. #if defined(_linux_) || defined(_cygwin_)
  101. TString path;
  102. if (!pid) {
  103. path = "/proc/self/statm";
  104. } else {
  105. path = TStringBuilder() << TStringBuf("/proc/") << pid << TStringBuf("/statm");
  106. }
  107. const TString stats = TUnbufferedFileInput(path).ReadAll();
  108. TStringBuf statsiter(stats);
  109. result.VMS = FromString<ui64>(statsiter.NextTok(' ')) * pagesize;
  110. result.RSS = FromString<ui64>(statsiter.NextTok(' ')) * pagesize;
  111. #if defined(_cygwin_)
  112. //cygwin not very accurate
  113. result.VMS = Max(result.VMS, result.RSS);
  114. #endif
  115. #elif defined(_freebsd_)
  116. int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid};
  117. size_t size = sizeof(struct kinfo_proc);
  118. struct kinfo_proc proc;
  119. Zero(proc);
  120. errno = 0;
  121. if (sysctl((int*)mib, 4, &proc, &size, nullptr, 0) == -1) {
  122. int err = errno;
  123. TString errtxt = LastSystemErrorText(err);
  124. ythrow yexception() << "sysctl({CTL_KERN,KERN_PROC,KERN_PROC_PID,pid},4,proc,&size,NULL,0) returned -1, errno: " << err << " (" << errtxt << ")" << Endl;
  125. }
  126. result.VMS = proc.ki_size;
  127. result.RSS = proc.ki_rssize * pagesize;
  128. #elif defined(_darwin_) && !defined(_arm_) && !defined(__IOS__)
  129. if (!pid) {
  130. pid = getpid();
  131. }
  132. struct proc_taskinfo taskInfo;
  133. const int r = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo));
  134. if (r != sizeof(taskInfo)) {
  135. int err = errno;
  136. TString errtxt = LastSystemErrorText(err);
  137. ythrow yexception() << "proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &taskInfo, sizeof(taskInfo)) returned " << r << ", errno: " << err << " (" << errtxt << ")" << Endl;
  138. }
  139. result.VMS = taskInfo.pti_virtual_size;
  140. result.RSS = taskInfo.pti_resident_size;
  141. #elif defined(__MACH__) && defined(__APPLE__)
  142. Y_UNUSED(pid);
  143. struct mach_task_basic_info taskInfo;
  144. mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
  145. const int r = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&taskInfo, &infoCount);
  146. if (r != KERN_SUCCESS) {
  147. int err = errno;
  148. TString errtxt = LastSystemErrorText(err);
  149. ythrow yexception() << "task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) returned" << r << ", errno: " << err << " (" << errtxt << ")" << Endl;
  150. }
  151. result.VMS = taskInfo.virtual_size;
  152. result.RSS = taskInfo.resident_size;
  153. #elif defined(_arm_)
  154. Y_UNUSED(pid);
  155. ythrow yexception() << "arm is not supported";
  156. #endif
  157. #elif defined(_win_)
  158. if (!pid) {
  159. pid = GetCurrentProcessId();
  160. }
  161. NTSTATUS status;
  162. TArrayHolder<char> buffer;
  163. ULONG bufferSize;
  164. // Query data for all processes and threads in the system.
  165. // This is probably an overkill if the target process is normal not-privileged one,
  166. // but allows to obtain information even about system processes that are not open-able directly.
  167. typedef NTSTATUS(_stdcall * NTQSI_PROC)(SYSTEM_INFORMATION_CLASS, PVOID, ULONG, PULONG);
  168. NTQSI_PROC NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress(GetModuleHandle(TEXT("ntdll.dll")), "NtQuerySystemInformation");
  169. bufferSize = 0x4000;
  170. for (;;) {
  171. buffer.Reset(new char[bufferSize]);
  172. status = NtQuerySystemInformation(SystemProcessInformation, buffer.Get(), bufferSize, &bufferSize);
  173. if (!status) {
  174. break;
  175. }
  176. if (status != STATUS_BUFFER_TOO_SMALL && status != STATUS_INFO_LENGTH_MISMATCH) {
  177. ythrow yexception() << "NtQuerySystemInformation failed with status code " << status;
  178. }
  179. }
  180. SYSTEM_PROCESS_INFORMATION* process = (SYSTEM_PROCESS_INFORMATION*)buffer.Get();
  181. while (process->UniqueProcessId != (HANDLE)(size_t)(pid)) {
  182. if (!process->NextEntryOffset) {
  183. ythrow yexception() << "GetMemInfo: invalid PID";
  184. }
  185. process = (SYSTEM_PROCESS_INFORMATION*)((char*)process + process->NextEntryOffset);
  186. }
  187. result.VMS = process->VirtualSize;
  188. result.RSS = process->WorkingSetSize;
  189. #endif
  190. return result;
  191. }
  192. }