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
20 #if defined( WIN32 ) || defined( _WIN32 )
21 #include <windows.h>
22 #if defined( SUPPORT_PSAPI )
23 #include <psapi.h>
24 #endif
25 #endif // defined(WIN32) || defined(_WIN32)
26
27 #if defined( __SUNPRO_CC ) || defined ( __sun__ )
28 #include <unistd.h>
29 #include <cstdio>
30 #include <string>
31 #include <sstream>
32 #endif // !defined(__SUNPRO_CC) && !defined (__sun__)
33
34 #if !defined( WIN32 ) && !defined( _WIN32 )
35 #include <sys/resource.h> // getrusage()
36 #if defined( ITK_HAS_MALLINFO )
37 #include <malloc.h> // mallinfo()
38 #endif // ITK_HAS_MALLINFO
39 #endif // !defined(WIN32) && !defined(_WIN32)
40
41 #if defined( __OpenBSD__ )
42 #include <cstdlib>
43 #endif
44
45 #ifdef __linux__
46 #include <fstream>
47 #include <unistd.h>
48 #endif
49
50 #ifdef __APPLE__
51 #include <sys/sysctl.h>
52 #include <mach/mach.h>
53 #include <cstdint>
54 #include <unistd.h>
55 #endif
56
57 namespace itk
58 {
59 MemoryUsageObserverBase::~MemoryUsageObserverBase() = default;
60
61 #if defined( WIN32 ) || defined( _WIN32 )
62
63 /** ---- Windows Memory Usage Observer ---- */
64
WindowsMemoryUsageObserver()65 WindowsMemoryUsageObserver::WindowsMemoryUsageObserver()
66 {
67 #if defined( SUPPORT_TOOLHELP32 )
68 m_hNTLib = ::LoadLibraryA("ntdll.dll");
69 if ( m_hNTLib )
70 {
71 // load the support function from the kernel
72 ZwQuerySystemInformation = ( PZwQuerySystemInformation ) ::GetProcAddress(m_hNTLib,
73 "ZwQuerySystemInformation");
74 }
75 #endif
76 }
77
~WindowsMemoryUsageObserver()78 WindowsMemoryUsageObserver::~WindowsMemoryUsageObserver()
79 {
80 #if defined ( SUPPORT_TOOLHELP32 )
81 if ( m_hNTLib )
82 {
83 FreeLibrary(m_hNTLib);
84 }
85 #endif
86 }
87
88 #if defined( SUPPORT_TOOLHELP32 )
89
90 #define STATUS_INFO_LENGTH_MISMATCH ( (NTSTATUS)0xC0000004L )
91
92 using KPRIORITY = LONG;
93 #define SystemProcessesAndThreadsInformation 5
94
95 typedef struct _CLIENT_ID {
96 DWORD UniqueProcess;
97 DWORD UniqueThread;
98 } CLIENT_ID;
99
100 typedef struct _UNICODE_STRING {
101 USHORT Length;
102 USHORT MaximumLength;
103 PWSTR Buffer;
104 } UNICODE_STRING;
105
106 typedef struct _VM_COUNTERS {
107 #ifdef _WIN64
108 // the following was inferred by painful reverse engineering
109 SIZE_T PeakVirtualSize; // not actually
110 SIZE_T PageFaultCount;
111 SIZE_T PeakWorkingSetSize;
112 SIZE_T WorkingSetSize;
113 SIZE_T QuotaPeakPagedPoolUsage;
114 SIZE_T QuotaPagedPoolUsage;
115 SIZE_T QuotaPeakNonPagedPoolUsage;
116 SIZE_T QuotaNonPagedPoolUsage;
117 SIZE_T PagefileUsage;
118 SIZE_T PeakPagefileUsage;
119 SIZE_T VirtualSize; // not actually
120 #else
121 SIZE_T PeakVirtualSize;
122 SIZE_T VirtualSize;
123 ULONG PageFaultCount;
124 SIZE_T PeakWorkingSetSize;
125 SIZE_T WorkingSetSize;
126 SIZE_T QuotaPeakPagedPoolUsage;
127 SIZE_T QuotaPagedPoolUsage;
128 SIZE_T QuotaPeakNonPagedPoolUsage;
129 SIZE_T QuotaNonPagedPoolUsage;
130 SIZE_T PagefileUsage;
131 SIZE_T PeakPagefileUsage;
132 #endif
133 } VM_COUNTERS;
134
135 typedef struct _SYSTEM_THREADS {
136 LARGE_INTEGER KernelTime;
137 LARGE_INTEGER UserTime;
138 LARGE_INTEGER CreateTime;
139 ULONG WaitTime;
140 PVOID StartAddress;
141 CLIENT_ID ClientId;
142 KPRIORITY Priority;
143 KPRIORITY BasePriority;
144 ULONG ContextSwitchCount;
145 LONG State;
146 LONG WaitReason;
147 } SYSTEM_THREADS, *PSYSTEM_THREADS;
148
149 typedef struct _SYSTEM_PROCESSES { // Information Class 5
150 ULONG NextEntryDelta;
151 ULONG MaximumNumberOfThreads;
152 ULONG Reserved1[6];
153 LARGE_INTEGER CreateTime;
154 LARGE_INTEGER UserTime;
155 LARGE_INTEGER KernelTime;
156 UNICODE_STRING ProcessName;
157 KPRIORITY BasePriority;
158 #ifdef _WIN64
159 ULONG pad1;
160 ULONG ProcessId;
161 ULONG pad2;
162 ULONG InheritedFromProcessId;
163 ULONG pad3;
164 ULONG pad4;
165 ULONG pad5;
166 #else
167 ULONG ProcessId;
168 ULONG InheritedFromProcessId;
169 #endif
170 ULONG HandleCount;
171 ULONG Reserved2[2];
172 VM_COUNTERS VmCounters;
173 #if defined( _WIN64 ) || _WIN32_WINNT >= 0x500
174 IO_COUNTERS IoCounters;
175 #endif
176 SYSTEM_THREADS Threads[1];
177 } SYSTEM_PROCESSES, *PSYSTEM_PROCESSES;
178 #endif
179
180 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()181 WindowsMemoryUsageObserver::GetMemoryUsage()
182 {
183 MemoryLoadType mem = 0;
184
185 #if defined( SUPPORT_PSAPI )
186 DWORD pid = GetCurrentProcessId();
187 PROCESS_MEMORY_COUNTERS memoryCounters;
188
189 HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION
190 | PROCESS_VM_READ,
191 FALSE, pid);
192
193 if ( nullptr == hProcess )
194 {
195 // Can't determine memory usage.
196 return 0;
197 }
198
199 GetProcessMemoryInfo( hProcess, &memoryCounters, sizeof( memoryCounters ) );
200
201 mem = static_cast< MemoryLoadType >(
202 static_cast< double >( memoryCounters.PagefileUsage )
203 / 1024.0 );
204 #elif defined( SUPPORT_TOOLHELP32 )
205
206 /* Retrieve memory usage using Windows Native API. For more information,
207 * read the book "Windows NT 2000 Native API Reference"
208 */
209
210 if ( !m_hNTLib )
211 {
212 itkGenericExceptionMacro(<< "Can't find ntdll.dll. "
213 << "You should probably disable SUPPORT_TOOLHELP32");
214 }
215 // the ntdll.dll library could not have been opened (file not found?)
216 if ( !ZwQuerySystemInformation )
217 {
218 itkGenericExceptionMacro(<< "The file ntdll.dll is not supported. "
219 << "You should probably disable SUPPORT_TOOLHELP32");
220 }
221
222 DWORD pid = GetCurrentProcessId();
223 ULONG n = 50;
224 PSYSTEM_PROCESSES sp = new SYSTEM_PROCESSES[n];
225 // as we can't know how many processes are running, we loop and test a new size
226 // every time.
227 while ( ZwQuerySystemInformation(SystemProcessesAndThreadsInformation,
228 sp, n * sizeof *sp, 0)
229 == STATUS_INFO_LENGTH_MISMATCH )
230 {
231 delete[] sp;
232 n = n * 2;
233 sp = new SYSTEM_PROCESSES[n];
234 }
235 bool done = false;
236 for ( PSYSTEM_PROCESSES spp = sp;
237 !done;
238 spp = PSYSTEM_PROCESSES(PCHAR(spp) + spp->NextEntryDelta) )
239 {
240 // only the current process is interesting here
241 if ( spp->ProcessId == pid )
242 {
243 mem = static_cast< MemoryLoadType >(
244 static_cast< double >( spp->VmCounters.PagefileUsage - sizeof( *sp ) ) / 1024 );
245 break;
246 }
247 done = ( spp->NextEntryDelta == 0 );
248 }
249 delete[] sp;
250
251 #else
252
253 /* This solution is not optimal as it returns the system memory usage
254 * instead of the process memory usage.
255 */
256
257 MEMORYSTATUSEX statex;
258
259 statex.dwLength = sizeof( statex );
260
261 GlobalMemoryStatusEx (&statex);
262
263 mem = static_cast< MemoryLoadType >(
264 static_cast< double >( statex.ullTotalPhys - statex.ullAvailPhys ) / 1024 );
265 #endif
266 return mem;
267 }
268
269 #endif // WIN32
270
271 #if defined(__linux__)
272
273 /** ---- Linux Memory Usage Observer ---- */
274
~LinuxMemoryUsageObserver()275 LinuxMemoryUsageObserver::~LinuxMemoryUsageObserver()
276 {}
277
278 /** Get Memory Usage - Linux version.
279 * Reference for method used:
280 * http://stackoverflow.com/questions/669438/how-to-get-memory-usage-at-run-time-in-c
281 */
282 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()283 LinuxMemoryUsageObserver::GetMemoryUsage()
284 {
285 std::ifstream procstats("/proc/self/stat",std::ios_base::in);
286 // dummy vars for leading entries in stat that we don't care about
287 //
288 std::string pid, comm, state, ppid, pgrp, session, tty_nr;
289 std::string tpgid, flags, minflt, cminflt, majflt, cmajflt;
290 std::string utime, stime, cutime, cstime, priority, nice;
291 std::string O, itrealvalue, starttime;
292
293 // the two fields we want
294 //
295 unsigned long vsize;
296 long rss;
297
298 procstats >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
299 >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
300 >> utime >> stime >> cutime >> cstime >> priority >> nice
301 >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest
302
303 procstats.close();
304
305 long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
306 // vm_usage = vsize / 1024.0;
307 return rss * page_size_kb;
308 }
309
310 #endif // linux
311
312 #if defined(__APPLE__)
313
314 /** ---- Mac OS X Memory Usage Observer ---- */
315
316 MacOSXMemoryUsageObserver::~MacOSXMemoryUsageObserver() = default;
317
318 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()319 MacOSXMemoryUsageObserver::GetMemoryUsage()
320 {
321 //
322 // this method comes from
323 // http://stackoverflow.com/questions/5839626/how-is-top-able-to-see-memory-usage
324 task_t targetTask = mach_task_self();
325 struct task_basic_info ti;
326 mach_msg_type_number_t count = TASK_BASIC_INFO_64_COUNT;
327 kern_return_t kr =
328 task_info(targetTask, TASK_BASIC_INFO_64,
329 (task_info_t) &ti, &count);
330 if (kr != KERN_SUCCESS)
331 {
332 return 0;
333 }
334
335 // On Mac OS X, the resident_size is in bytes, not pages!
336 // (This differs from the GNU Mach kernel)
337 return ti.resident_size / 1024;
338 }
339
340 #endif // Mac OS X
341
342 #if defined( __SUNPRO_CC ) || defined ( __sun__ )
343
344 /** ---- Sun Solaris Memory Usage Observer ---- */
345
~SunSolarisMemoryUsageObserver()346 SunSolarisMemoryUsageObserver::~SunSolarisMemoryUsageObserver()
347 {}
348
349 /** On Sun Solaris machines, the system call pmap returns information on process.
350 * Calling "pmap PID", the output shall be like the following:
351 * 102905: *my_app*
352 * 00010000 192K r-x-- /usr/bin/my_app
353 * 00042000 40K rwx-- [ heap ]
354 * FF180000 664K r-x-- /usr/lib/libc.so.1
355 * FF236000 24K rwx-- /usr/lib/libc.so.1
356 * FF23C000 8K rwx-- /usr/lib/libc.so.1
357 * FF250000 8K rwx-- [ anon ]
358 * ... ... ... ...
359 * FF3F6000 8K rwx-- /usr/lib/ld.so.1
360 * FFBFC000 16K rw--- [ stack ]
361 * total 1880K
362 */
363 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()364 SunSolarisMemoryUsageObserver::GetMemoryUsage()
365 {
366 MemoryLoadType mem = 0;
367 int pid = getpid();
368
369 FILE * fp = nullptr;
370 std::stringstream command;
371
372 command << "pmap " << pid << std::endl;
373
374 if ( ( fp = popen(command.str().c_str(), "r") ) == nullptr )
375 {
376 itkGenericExceptionMacro(<< "Error using pmap. Can execute pmap command");
377 }
378 char remaining[256];
379 int pmappid = -1;
380 fscanf(fp, "%d:%s", &pmappid, remaining);
381 //the first word shall be the process ID
382 if ( pmappid != pid )
383 {
384 itkGenericExceptionMacro(<< "Error using pmap. 1st line output shall be PID: name");
385 }
386 bool heapNotFound = true;
387 char address[64], perms[32];
388 int memUsage = 0;
389 std::string mapping;
390 while ( heapNotFound )
391 {
392 if ( fscanf(fp, "%s %dK %s", address, &memUsage, perms) != 3 )
393 {
394 break;
395 }
396 if ( fgets(remaining, 256, fp) != nullptr )
397 {
398 mapping = remaining;
399 if ( mapping.find("[ heap ]", 0) != std::string::npos )
400 {
401 mem = memUsage;
402 heapNotFound = false;
403 break;
404 }
405 // if no [ heap ] token is defined, accumulate all the [ xxx ] tokens
406 else if ( mapping.find("[ ", 0) != std::string::npos
407 && mapping.find(" ]", 0) != std::string::npos )
408 {
409 mem += memUsage;
410 }
411 }
412 else
413 {
414 if ( ferror (fp) )
415 {
416 itkGenericExceptionMacro(<< "Error using pmap. Corrupted pmap output");
417 }
418 }
419 }
420 if ( pclose(fp) == -1 )
421 {
422 itkGenericExceptionMacro(<< "Error using pmap. Can't close pmap output file.");
423 }
424 return mem;
425 }
426
427 #endif //defined(__SUNPRO_CC) || defined (__sun__)
428
429 #if !defined( WIN32 ) && !defined( _WIN32 ) || defined( __OpenBSD__ )
430
431 /** ---- SysResource Memory Usage Observer ---- */
432
433 SysResourceMemoryUsageObserver::~SysResourceMemoryUsageObserver() = default;
434
435 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()436 SysResourceMemoryUsageObserver::GetMemoryUsage()
437 {
438 // Maybe use getrusage() ??
439 rusage resourceInfo;
440
441 const int who = RUSAGE_SELF;
442 if ( getrusage(who, &resourceInfo) == 0 )
443 {
444 return static_cast<MemoryUsageObserverBase::MemoryLoadType> (resourceInfo.ru_ixrss);
445 }
446
447 return 0;
448 }
449
450 #if defined( ITK_HAS_MALLINFO )
451
452 /** ---- Mallinfo Memory Usage Observer ---- */
453
~MallinfoMemoryUsageObserver()454 MallinfoMemoryUsageObserver::~MallinfoMemoryUsageObserver()
455 {}
456
457 MemoryUsageObserverBase::MemoryLoadType
GetMemoryUsage()458 MallinfoMemoryUsageObserver::GetMemoryUsage()
459 {
460 struct mallinfo minfo = mallinfo();
461
462 MemoryLoadType mem = static_cast< MemoryLoadType >(
463 static_cast< double >( minfo.uordblks ) / 1024.0 );
464
465 return mem;
466 }
467
468 #endif // ITK_HAS_MALLINFO
469
470 #endif // Unix and Mac Platforms !defined(WIN32) && !defined(_WIN32)
471
472
473 //Destructor for MemoryUsageObserver
474 MemoryUsageObserver::~MemoryUsageObserver()= default;
475 } //end namespace itk
476