mem_info.cpp 7.2 KB

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