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

Generated at Sat Mar 8 2014 15:12:40 for Orfeo Toolbox with doxygen 1.8.3.1