PageRenderTime 66ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/Code/Common/itkMemoryUsageObserver.cxx

https://github.com/luisibanez/ITK
C++ | 437 lines | 315 code | 68 blank | 54 comment | 51 complexity | 7a089555e0f3164e3586593a846422b8 MD5 | raw file
  1. /*=========================================================================
  2. *
  3. * Copyright Insight Software Consortium
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0.txt
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. *=========================================================================*/
  18. #include "itkMemoryUsageObserver.h"
  19. #if defined( WIN32 ) || defined( _WIN32 )
  20. #include <windows.h>
  21. #if defined( SUPPORT_PSAPI )
  22. #include <psapi.h>
  23. #endif
  24. #endif // defined(WIN32) || defined(_WIN32)
  25. #ifdef linux
  26. #include "itkSmapsFileParser.h"
  27. #endif // linux
  28. #if defined( __APPLE__ ) && MAC_OS_X_VERSION >= MAC_OS_X_VERSION_10_2
  29. #include "itkSmapsFileParser.h"
  30. #endif // Mac OS X
  31. #if defined( __SUNPRO_CC ) || defined ( __sun__ )
  32. #include <unistd.h>
  33. #include <stdio.h>
  34. #include <string>
  35. #include <sstream>
  36. #endif // !defined(__SUNPRO_CC) && !defined (__sun__)
  37. #if !defined( WIN32 ) && !defined( _WIN32 )
  38. #include <sys/resource.h> // getrusage()
  39. #if !defined( __APPLE__ ) && !defined( __SUNPRO_CC ) && !defined ( __sun__ ) && !defined( __FreeBSD__ ) \
  40. && !defined( __OpenBSD__ )
  41. #include <malloc.h> // mallinfo()
  42. #endif // !defined(__APPLE__) && !defined(__SUNPRO_CC) && !defined (__sun__)
  43. #endif // !defined(WIN32) && !defined(_WIN32)
  44. #if defined( __OpenBSD__ )
  45. #include <stdlib.h>
  46. #endif
  47. namespace itk
  48. {
  49. MemoryUsageObserverBase::~MemoryUsageObserverBase()
  50. {}
  51. #if defined( WIN32 ) || defined( _WIN32 )
  52. /** ---- Windows Memory Usage Observer ---- */
  53. WindowsMemoryUsageObserver::WindowsMemoryUsageObserver()
  54. {
  55. #if defined( SUPPORT_TOOLHELP32 )
  56. m_hNTLib = ::LoadLibraryA("ntdll.dll");
  57. if ( m_hNTLib )
  58. {
  59. // load the support function from the kernel
  60. ZwQuerySystemInformation = ( PZwQuerySystemInformation ) ::GetProcAddress(m_hNTLib,
  61. "ZwQuerySystemInformation");
  62. }
  63. #endif
  64. }
  65. WindowsMemoryUsageObserver::~WindowsMemoryUsageObserver()
  66. {
  67. #if defined ( SUPPORT_TOOLHELP32 )
  68. if ( m_hNTLib )
  69. {
  70. FreeLibrary(m_hNTLib);
  71. }
  72. #endif
  73. }
  74. #if defined( SUPPORT_TOOLHELP32 )
  75. #define STATUS_INFO_LENGTH_MISMATCH ( (NTSTATUS)0xC0000004L )
  76. typedef LONG KPRIORITY;
  77. #define SystemProcessesAndThreadsInformation 5
  78. typedef struct _CLIENT_ID {
  79. DWORD UniqueProcess;
  80. DWORD UniqueThread;
  81. } CLIENT_ID;
  82. typedef struct _UNICODE_STRING {
  83. USHORT Length;
  84. USHORT MaximumLength;
  85. PWSTR Buffer;
  86. } UNICODE_STRING;
  87. typedef struct _VM_COUNTERS {
  88. #ifdef _WIN64
  89. // the following was inferred by painful reverse engineering
  90. SIZE_T PeakVirtualSize; // not actually
  91. SIZE_T PageFaultCount;
  92. SIZE_T PeakWorkingSetSize;
  93. SIZE_T WorkingSetSize;
  94. SIZE_T QuotaPeakPagedPoolUsage;
  95. SIZE_T QuotaPagedPoolUsage;
  96. SIZE_T QuotaPeakNonPagedPoolUsage;
  97. SIZE_T QuotaNonPagedPoolUsage;
  98. SIZE_T PagefileUsage;
  99. SIZE_T PeakPagefileUsage;
  100. SIZE_T VirtualSize; // not actually
  101. #else
  102. SIZE_T PeakVirtualSize;
  103. SIZE_T VirtualSize;
  104. ULONG PageFaultCount;
  105. SIZE_T PeakWorkingSetSize;
  106. SIZE_T WorkingSetSize;
  107. SIZE_T QuotaPeakPagedPoolUsage;
  108. SIZE_T QuotaPagedPoolUsage;
  109. SIZE_T QuotaPeakNonPagedPoolUsage;
  110. SIZE_T QuotaNonPagedPoolUsage;
  111. SIZE_T PagefileUsage;
  112. SIZE_T PeakPagefileUsage;
  113. #endif
  114. } VM_COUNTERS;
  115. typedef struct _SYSTEM_THREADS {
  116. LARGE_INTEGER KernelTime;
  117. LARGE_INTEGER UserTime;
  118. LARGE_INTEGER CreateTime;
  119. ULONG WaitTime;
  120. PVOID StartAddress;
  121. CLIENT_ID ClientId;
  122. KPRIORITY Priority;
  123. KPRIORITY BasePriority;
  124. ULONG ContextSwitchCount;
  125. LONG State;
  126. LONG WaitReason;
  127. } SYSTEM_THREADS, *PSYSTEM_THREADS;
  128. typedef struct _SYSTEM_PROCESSES { // Information Class 5
  129. ULONG NextEntryDelta;
  130. ULONG ThreadCount;
  131. ULONG Reserved1[6];
  132. LARGE_INTEGER CreateTime;
  133. LARGE_INTEGER UserTime;
  134. LARGE_INTEGER KernelTime;
  135. UNICODE_STRING ProcessName;
  136. KPRIORITY BasePriority;
  137. #ifdef _WIN64
  138. ULONG pad1;
  139. ULONG ProcessId;
  140. ULONG pad2;
  141. ULONG InheritedFromProcessId;
  142. ULONG pad3;
  143. ULONG pad4;
  144. ULONG pad5;
  145. #else
  146. ULONG ProcessId;
  147. ULONG InheritedFromProcessId;
  148. #endif
  149. ULONG HandleCount;
  150. ULONG Reserved2[2];
  151. VM_COUNTERS VmCounters;
  152. #if defined( _WIN64 ) || _WIN32_WINNT >= 0x500
  153. IO_COUNTERS IoCounters;
  154. #endif
  155. SYSTEM_THREADS Threads[1];
  156. } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;
  157. #endif
  158. MemoryUsageObserverBase::MemoryLoadType
  159. WindowsMemoryUsageObserver::GetMemoryUsage()
  160. {
  161. MemoryLoadType mem = 0;
  162. #if defined( SUPPORT_PSAPI )
  163. DWORD pid = GetCurrentProcessId();
  164. PROCESS_MEMORY_COUNTERS memoryCounters;
  165. HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION
  166. | PROCESS_VM_READ,
  167. FALSE, pid);
  168. if ( NULL == hProcess )
  169. {
  170. // Can't determine memory usage.
  171. return 0;
  172. }
  173. GetProcessMemoryInfo( hProcess, &memoryCounters, sizeof( memoryCounters ) );
  174. mem = static_cast< MemoryLoadType >(
  175. static_cast< double >( memoryCounters.PagefileUsage )
  176. / 1024.0 );
  177. #elif defined( SUPPORT_TOOLHELP32 )
  178. /* Retrieve memory usage using Windows Native API. For more information,
  179. * read the book "Windows NT 2000 Native API Reference"
  180. */
  181. if ( !m_hNTLib )
  182. {
  183. itkGenericExceptionMacro(<< "Can't find ntdll.dll. "
  184. << "You should probably disable SUPPORT_TOOLHELP32");
  185. }
  186. // the ntdll.dll library could not have been opened (file not found?)
  187. if ( !ZwQuerySystemInformation )
  188. {
  189. itkGenericExceptionMacro(<< "The file ntdll.dll is not supported. "
  190. << "You should probably disable SUPPORT_TOOLHELP32");
  191. return mem;
  192. }
  193. DWORD pid = GetCurrentProcessId();
  194. ULONG n = 50;
  195. PSYSTEM_PROCESSES sp = new SYSTEM_PROCESSES[n];
  196. // as we can't know how many processes running, we loop and test a new size
  197. // everytime.
  198. while ( ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,
  199. sp, n * sizeof *sp, 0)
  200. == STATUS_INFO_LENGTH_MISMATCH )
  201. {
  202. delete[] sp;
  203. n = n * 2;
  204. sp = new SYSTEM_PROCESSES[n];
  205. }
  206. bool done = false;
  207. for ( PSYSTEM_PROCESSES spp = sp;
  208. !done;
  209. spp = PSYSTEM_PROCESSES(PCHAR(spp) + spp->NextEntryDelta) )
  210. {
  211. // only the current process is interesting here
  212. if ( spp->ProcessId == pid )
  213. {
  214. mem = static_cast< MemoryLoadType >(
  215. static_cast< double >( spp->VmCounters.PagefileUsage - sizeof( *sp ) ) / 1024 );
  216. break;
  217. }
  218. done = ( spp->NextEntryDelta == 0 );
  219. }
  220. delete[] sp;
  221. #else
  222. /* This solution is not optimal as it returns the system memory usage
  223. * instead of the process memory usage.
  224. */
  225. MEMORYSTATUSEX statex;
  226. statex.dwLength = sizeof( statex );
  227. GlobalMemoryStatusEx (&statex);
  228. mem = static_cast< MemoryLoadType >(
  229. static_cast< double >( statex.ullTotalPhys - statex.ullAvailPhys ) / 1024 );
  230. #endif
  231. return mem;
  232. }
  233. #endif // WIN32
  234. #if linux
  235. /** ---- Linux Memory Usage Observer ---- */
  236. LinuxMemoryUsageObserver::~LinuxMemoryUsageObserver()
  237. {}
  238. MemoryUsageObserverBase::MemoryLoadType
  239. LinuxMemoryUsageObserver::GetMemoryUsage()
  240. {
  241. SmapsFileParser< SmapsData_2_6 > m_ParseSmaps;
  242. m_ParseSmaps.ReadFile();
  243. return m_ParseSmaps.GetHeapUsage() + m_ParseSmaps.GetStackUsage();
  244. }
  245. #endif // linux
  246. #if defined( __APPLE__ ) && MAC_OS_X_VERSION >= MAC_OS_X_VERSION_10_2
  247. /** ---- Mac OS X Memory Usage Observer ---- */
  248. MacOSXMemoryUsageObserver::~MacOSXMemoryUsageObserver()
  249. {}
  250. MemoryUsageObserverBase::MemoryLoadType
  251. MacOSXMemoryUsageObserver::GetMemoryUsage()
  252. {
  253. VMMapFileParser< VMMapData_10_2 > m_ParseVMMmap;
  254. m_ParseVMMmap.ReadFile();
  255. return m_ParseVMMmap.GetHeapUsage() + m_ParseVMMmap.GetStackUsage();
  256. }
  257. #endif // Mac OS X
  258. #if defined( __SUNPRO_CC ) || defined ( __sun__ )
  259. /** ---- Sun Solaris Memory Usage Observer ---- */
  260. SunSolarisMemoryUsageObserver::~SunSolarisMemoryUsageObserver()
  261. {}
  262. /** On Sun Solaris machines, the system call pmap returns information on process.
  263. * calling "pmap PID", the output shall be like the following:
  264. * 102905: *my_app*
  265. * 00010000 192K r-x-- /usr/bin/my_app
  266. * 00042000 40K rwx-- [ heap ]
  267. * FF180000 664K r-x-- /usr/lib/libc.so.1
  268. * FF236000 24K rwx-- /usr/lib/libc.so.1
  269. * FF23C000 8K rwx-- /usr/lib/libc.so.1
  270. * FF250000 8K rwx-- [ anon ]
  271. * ... ... ... ...
  272. * FF3F6000 8K rwx-- /usr/lib/ld.so.1
  273. * FFBFC000 16K rw--- [ stack ]
  274. * total 1880K
  275. */
  276. MemoryUsageObserverBase::MemoryLoadType
  277. SunSolarisMemoryUsageObserver::GetMemoryUsage()
  278. {
  279. MemoryLoadType mem = 0;
  280. int pid = getpid();
  281. FILE * fp = NULL;
  282. std::stringstream command;
  283. command << "pmap " << pid << std::endl;
  284. if ( ( fp = popen(command.str().c_str(), "r") ) == NULL )
  285. {
  286. itkGenericExceptionMacro(<< "Error using pmap. Can execute pmap command");
  287. }
  288. char remaining[256];
  289. int pmappid = -1;
  290. fscanf(fp, "%d:%s", &pmappid, remaining);
  291. //the first word shall be the process ID
  292. if ( pmappid != pid )
  293. {
  294. itkGenericExceptionMacro(<< "Error using pmap. 1st line output shall be PID: name");
  295. }
  296. bool heapNotFound = true;
  297. char address[64], perms[32];
  298. int memUsage = 0;
  299. std::string mapping;
  300. while ( heapNotFound )
  301. {
  302. if ( fscanf(fp, "%s %dK %s", address, &memUsage, perms) != 3 )
  303. {
  304. break;
  305. }
  306. if ( fgets(remaining, 256, fp) != NULL )
  307. {
  308. mapping = remaining;
  309. if ( mapping.find("[ heap ]", 0) != std::string::npos )
  310. {
  311. mem = memUsage;
  312. heapNotFound = false;
  313. break;
  314. }
  315. // if no [ heap ] token is defined, accumulate all the [ xxx ] tokens
  316. else if ( mapping.find("[ ", 0) != std::string::npos
  317. && mapping.find(" ]", 0) != std::string::npos )
  318. {
  319. mem += memUsage;
  320. }
  321. }
  322. else
  323. {
  324. if ( ferror (fp) )
  325. {
  326. itkGenericExceptionMacro(<< "Error using pmap. Corrupted pmap output");
  327. }
  328. }
  329. }
  330. if ( pclose(fp) == -1 )
  331. {
  332. itkGenericExceptionMacro(<< "Error using pmap. Can't close pmap output file.");
  333. }
  334. return mem;
  335. }
  336. #endif //defined(__SUNPRO_CC) || defined (__sun__)
  337. #if !defined( WIN32 ) && !defined( _WIN32 ) || defined( __OpenBSD__ )
  338. /** ---- SysResource Memory Usage Observer ---- */
  339. SysResourceMemoryUsageObserver::~SysResourceMemoryUsageObserver()
  340. {}
  341. MemoryUsageObserverBase::MemoryLoadType
  342. SysResourceMemoryUsageObserver::GetMemoryUsage()
  343. {
  344. // Maybe use getrusage() ??
  345. rusage resourceInfo;
  346. int who = RUSAGE_SELF;
  347. if ( getrusage(who, &resourceInfo) == 0 )
  348. {
  349. return resourceInfo.ru_ixrss;
  350. }
  351. return 0;
  352. }
  353. #if !defined( __APPLE__ ) && !defined( __SUNPRO_CC ) && !defined ( __sun__ ) && !defined( __FreeBSD__ ) \
  354. && !defined( __OpenBSD__ )
  355. /** ---- Mallinfo Memory Usage Observer ---- */
  356. MallinfoMemoryUsageObserver::~MallinfoMemoryUsageObserver()
  357. {}
  358. MemoryUsageObserverBase::MemoryLoadType
  359. MallinfoMemoryUsageObserver::GetMemoryUsage()
  360. {
  361. struct mallinfo minfo = mallinfo();
  362. MemoryLoadType mem = static_cast< MemoryLoadType >(
  363. static_cast< double >( minfo.uordblks ) / 1024.0 );
  364. return mem;
  365. }
  366. #endif // !defined(__APPLE__) && !defined(__SUNPRO_CC) && !defined (__sun__) &&
  367. // !defined(__FreeBSD__) && !defined(__OpenBSD__)
  368. #endif // Unix and Mac Platforms !defined(WIN32) && !defined(_WIN32)
  369. } //end namespace itk