1 /** 2 * The amount of virtual and physycal memory used by system or process. 3 * Authors: 4 * $(LINK2 https://github.com/FreeSlave, Roman Chistokhodov). 5 * License: 6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0). 7 * Copyright: 8 * Roman Chistokhodov 2015 9 * 10 * Note: Every function may throw on fail ($(B ErrnoException) on Linux, $(B WindowsException) on Windows). 11 */ 12 13 module resusage.memory; 14 import resusage.common; 15 16 import std.exception; 17 18 ///Get system memory information. 19 @trusted SystemMemInfo systemMemInfo() 20 { 21 SystemMemInfo memInfo; 22 memInfo.update(); 23 return memInfo; 24 } 25 26 ///Get memory info for current process. 27 @trusted ProcessMemInfo processMemInfo() { 28 ProcessMemInfo memInfo; 29 memInfo.initialize(); 30 memInfo.update(); 31 return memInfo; 32 } 33 34 /** 35 * Get memory info for specific process. 36 * Params: 37 * pid = Process ID of specific process. On Windows also can be process handle. 38 */ 39 @trusted ProcessMemInfo processMemInfo(int pid) { 40 ProcessMemInfo memInfo; 41 memInfo.initialize(pid); 42 memInfo.update(); 43 return memInfo; 44 } 45 46 private @nogc @safe double percent(ulong total, ulong part) pure nothrow { 47 if (total) { 48 return part / cast(double)total * 100.0; 49 } 50 return 0.0; 51 } 52 53 version(ResUsageDocs) 54 { 55 ///System-wide memory information. 56 struct SystemMemInfo 57 { 58 ///Total physycal memory in the system, in bytes. 59 @nogc @safe ulong totalRAM() const nothrow; 60 61 ///Amout of physycal memory currently available (free), in bytes. 62 @nogc @safe ulong freeRAM() const nothrow; 63 64 ///Amout of physycal memory currently available (free), in percents of total physycal memory. 65 @nogc @safe double freeRAMPercent() const nothrow; 66 67 ///Amout of physycal memory currently in use, in bytes. 68 @nogc @safe ulong usedRAM() const nothrow; 69 70 ///Amout of physycal memory currently in use, in percents of total physycal memory. 71 @nogc @safe double usedRAMPercent() const nothrow; 72 73 ///Total virtual memory in the system, in bytes. 74 @nogc @safe ulong totalVirtMem() const nothrow; 75 76 ///Amout of virtual memory currently available (free), in bytes. 77 @nogc @safe ulong freeVirtMem() const nothrow; 78 79 ///Amout of virtual memory currently available (free), in percents of total virtual memory. 80 @nogc @safe double freeVirtMemPercent() const nothrow; 81 82 ///Amout of virtual memory currently in use, in bytes. 83 @nogc @safe ulong usedVirtMem() const nothrow; 84 85 ///Amout of virtual memory currently in use, in percents of total virtual memory. 86 @nogc @safe double usedVirtMemPercent() const nothrow; 87 88 ///Actualize values. 89 @trusted void update(); 90 } 91 92 ///Single process memory information. 93 struct ProcessMemInfo 94 { 95 ///Amount of physycal memory (RAM) currently used by single process, in bytes. 96 @nogc @safe ulong usedRAM() const nothrow; 97 98 ///Amount of virtual memory currently used by single process, in bytes. 99 @nogc @safe ulong usedVirtMem() const nothrow; 100 101 ///Actualize values. 102 @trusted void update(); 103 104 ///ID of underlying process. 105 @nogc @safe int processID() const nothrow; 106 107 private: 108 void initialize(); 109 void initialize(int pid); 110 } 111 } else version(Windows) { 112 113 @trusted ProcessMemInfo processMemInfo(HANDLE procHandle) { 114 ProcessMemInfo memInfo; 115 memInfo.initialize(procHandle); 116 memInfo.update(); 117 return memInfo; 118 } 119 120 private { 121 alias ulong DWORDLONG; 122 123 struct MEMORYSTATUSEX { 124 DWORD dwLength; 125 DWORD dwMemoryLoad; 126 DWORDLONG ullTotalPhys; 127 DWORDLONG ullAvailPhys; 128 DWORDLONG ullTotalPageFile; 129 DWORDLONG ullAvailPageFile; 130 DWORDLONG ullTotalVirtual; 131 DWORDLONG ullAvailVirtual; 132 DWORDLONG ullAvailExtendedVirtual; 133 }; 134 135 extern(Windows) BOOL GlobalMemoryStatusEx(MEMORYSTATUSEX* lpBuffer) @system nothrow; 136 137 struct PROCESS_MEMORY_COUNTERS { 138 DWORD cb; 139 DWORD PageFaultCount; 140 SIZE_T PeakWorkingSetSize; 141 SIZE_T WorkingSetSize; 142 SIZE_T QuotaPeakPagedPoolUsage; 143 SIZE_T QuotaPagedPoolUsage; 144 SIZE_T QuotaPeakNonPagedPoolUsage; 145 SIZE_T QuotaNonPagedPoolUsage; 146 SIZE_T PagefileUsage; 147 SIZE_T PeakPagefileUsage; 148 }; 149 150 struct PROCESS_MEMORY_COUNTERS_EX { 151 DWORD cb; 152 DWORD PageFaultCount; 153 SIZE_T PeakWorkingSetSize; 154 SIZE_T WorkingSetSize; 155 SIZE_T QuotaPeakPagedPoolUsage; 156 SIZE_T QuotaPagedPoolUsage; 157 SIZE_T QuotaPeakNonPagedPoolUsage; 158 SIZE_T QuotaNonPagedPoolUsage; 159 SIZE_T PagefileUsage; 160 SIZE_T PeakPagefileUsage; 161 SIZE_T PrivateUsage; 162 }; 163 164 extern(Windows) @nogc BOOL dummy(in HANDLE Process, PROCESS_MEMORY_COUNTERS* ppsmemCounters, DWORD cb) @system nothrow { return 0; } 165 166 alias typeof(&dummy) func_GetProcessMemoryInfo; 167 __gshared func_GetProcessMemoryInfo GetProcessMemoryInfo; 168 __gshared DWORD psApiError; 169 } 170 171 @nogc @trusted bool isPsApiLoaded() { 172 return GetProcessMemoryInfo !is null; 173 } 174 175 shared static this() 176 { 177 HMODULE psApiLib = LoadLibraryA("Psapi"); 178 if (psApiLib) { 179 GetProcessMemoryInfo = cast(func_GetProcessMemoryInfo)GetProcAddress(psApiLib, "GetProcessMemoryInfo"); 180 } 181 182 if (GetProcessMemoryInfo is null) { 183 psApiError = GetLastError(); 184 } 185 } 186 187 struct SystemMemInfo 188 { 189 @nogc @safe ulong totalRAM() const nothrow { 190 return memInfo.ullTotalPhys; 191 } 192 @nogc @safe ulong freeRAM() const nothrow { 193 return memInfo.ullAvailPhys; 194 } 195 @nogc @safe double freeRAMPercent() const nothrow { 196 return percent(totalRAM, freeRAM); 197 } 198 @nogc @safe ulong usedRAM() const nothrow { 199 return memInfo.ullTotalPhys - memInfo.ullAvailPhys; 200 } 201 @nogc @safe double usedRAMPercent() const nothrow { 202 return percent(totalRAM, usedRAM); 203 } 204 205 @nogc @safe ulong totalVirtMem() const nothrow { 206 return memInfo.ullTotalPageFile; 207 } 208 @nogc @safe ulong freeVirtMem() const nothrow { 209 return memInfo.ullAvailPageFile; 210 } 211 @nogc @safe double freeVirtMemPercent() const nothrow { 212 return percent(totalVirtMem, freeVirtMem); 213 } 214 @nogc @safe ulong usedVirtMem() const nothrow { 215 return memInfo.ullTotalPageFile - memInfo.ullAvailPageFile; 216 } 217 @nogc @safe double usedVirtMemPercent() const nothrow { 218 return percent(totalVirtMem, usedVirtMem); 219 } 220 221 @trusted void update() { 222 memInfo.dwLength = MEMORYSTATUSEX.sizeof; 223 wenforce(GlobalMemoryStatusEx(&memInfo), "Could not get memory status"); 224 } 225 private: 226 MEMORYSTATUSEX memInfo; 227 } 228 229 struct ProcessMemInfo 230 { 231 @nogc @safe ulong usedRAM() const nothrow { 232 return pmc.WorkingSetSize; 233 } 234 @nogc @safe ulong usedVirtMem() const nothrow { 235 return pmc.PrivateUsage; 236 } 237 238 @trusted void update() { 239 if (!isPsApiLoaded()) { 240 throw new WindowsException(psApiError, "Psapi.dll is not loaded"); 241 } 242 243 HANDLE handle = openProcess(pid); 244 scope(exit) CloseHandle(handle); 245 246 wenforce(GetProcessMemoryInfo(handle, cast(PROCESS_MEMORY_COUNTERS*)&pmc, pmc.sizeof), "Could not get process memory info"); 247 } 248 249 @nogc @safe int processID() const nothrow { 250 return pid; 251 } 252 253 private: 254 void initialize() { 255 pid = thisProcessID; 256 } 257 258 void initialize(int procId) { 259 pid = procId; 260 } 261 262 void initialize(HANDLE procHandle) { 263 pid = GetProcessId(procHandle); 264 } 265 266 int pid; 267 PROCESS_MEMORY_COUNTERS_EX pmc; 268 } 269 270 } else version(linux) { 271 private { 272 static if (is(typeof({ import core.sys.linux.sys.sysinfo; }))) { 273 import core.sys.linux.sys.sysinfo; 274 } else { 275 pragma(msg, "core.sys.linux.sys.sysinfo not found, fallback will be used."); 276 extern(C) @nogc nothrow: 277 struct sysinfo_ 278 { 279 c_long uptime; /* Seconds since boot */ 280 c_ulong[3] loads; /* 1, 5, and 15 minute load averages */ 281 c_ulong totalram; /* Total usable main memory size */ 282 c_ulong freeram; /* Available memory size */ 283 c_ulong sharedram; /* Amount of shared memory */ 284 c_ulong bufferram; /* Memory used by buffers */ 285 c_ulong totalswap; /* Total swap space size */ 286 c_ulong freeswap; /* swap space still available */ 287 ushort procs; /* Number of current processes */ 288 ushort pad; /* Explicit padding for m68k */ 289 c_ulong totalhigh; /* Total high memory size */ 290 c_ulong freehigh; /* Available high memory size */ 291 uint mem_unit; /* Memory unit size in bytes */ 292 ubyte[20-2 * c_ulong.sizeof - uint.sizeof] _f; /* Padding: libc5 uses this.. */ 293 } 294 int sysinfo(sysinfo_ *info); 295 } 296 } 297 298 private @trusted void memoryUsedHelper(const(char)* proc, ref c_ulong vsize, ref c_long rss) 299 { 300 FILE* f = errnoEnforce(fopen(proc, "r")); 301 scope(exit) fclose(f); 302 errnoEnforce(fscanf(f, 303 "%*d " ~//pid 304 "%*s " ~//comm 305 "%*c " ~//state 306 "%*d " ~//ppid 307 "%*d " ~//pgrp 308 "%*d " ~//session 309 "%*d " ~//tty_nr 310 "%*d " ~//tpgid 311 "%*u " ~//flags 312 "%*lu " ~//minflt 313 "%*lu " ~//cminflt 314 "%*lu " ~//majflt 315 "%*lu " ~//cmajflt 316 "%*lu " ~//utime 317 "%*lu " ~//stime 318 "%*ld " ~//cutime 319 "%*ld " ~//cstime 320 "%*ld " ~//priority 321 "%*ld " ~//nice 322 "%*ld " ~//num_threads 323 "%*ld " ~//itrealvalue 324 "%*llu " ~//starttime 325 "%lu " ~//vsize 326 "%ld ", //rss 327 &vsize, &rss 328 ) == 2); 329 } 330 331 struct SystemMemInfo 332 { 333 @nogc @safe ulong totalRAM() const nothrow { 334 ulong total = memInfo.totalram; 335 total *= memInfo.mem_unit; 336 return total; 337 } 338 @nogc @safe ulong freeRAM() const nothrow { 339 ulong free = memInfo.freeram; 340 free *= memInfo.mem_unit; 341 return free; 342 } 343 @nogc @safe double freeRAMPercent() const nothrow { 344 return percent(totalRAM, freeRAM); 345 } 346 @nogc @safe ulong usedRAM() const nothrow { 347 return totalRAM() - freeRAM(); 348 } 349 @nogc @safe double usedRAMPercent() const nothrow { 350 return percent(totalRAM, usedRAM); 351 } 352 353 @nogc @safe ulong totalVirtMem() const nothrow { 354 ulong total = memInfo.totalram + memInfo.totalswap; 355 total *= memInfo.mem_unit; 356 return total; 357 } 358 @nogc @safe ulong freeVirtMem() const nothrow { 359 ulong free = memInfo.freeram + memInfo.freeswap; 360 free *= memInfo.mem_unit; 361 return free; 362 } 363 @nogc @safe double freeVirtMemPercent() const nothrow { 364 return percent(totalVirtMem, freeVirtMem); 365 } 366 @nogc @safe ulong usedVirtMem() const nothrow { 367 return totalVirtMem() - freeVirtMem(); 368 } 369 @nogc @safe double usedVirtMemPercent() const nothrow { 370 return percent(totalVirtMem, usedVirtMem); 371 } 372 373 @trusted void update() { 374 errnoEnforce(sysinfo(&memInfo) == 0); 375 } 376 private: 377 sysinfo_ memInfo; 378 } 379 380 struct ProcessMemInfo 381 { 382 @nogc @trusted ulong usedRAM() const nothrow { 383 return rss * PAGE_SIZE; 384 } 385 @nogc @safe ulong usedVirtMem() const nothrow { 386 return vsize; 387 } 388 389 @trusted void update() { 390 memoryUsedHelper(proc, vsize, rss); 391 } 392 393 @nogc @safe int processID() const nothrow { 394 return pid; 395 } 396 397 private: 398 void initialize() { 399 pid = thisProcessID; 400 proc = procSelf; 401 } 402 403 void initialize(int procId) { 404 pid = procId; 405 proc = procOfPid(pid); 406 } 407 408 int pid; 409 const(char)* proc; 410 c_ulong vsize; 411 c_long rss; 412 } 413 414 __gshared size_t PAGE_SIZE; 415 416 shared static this() 417 { 418 PAGE_SIZE = sysconf(_SC_PAGESIZE); 419 } 420 421 } else version(FreeBSD) { 422 423 private { 424 import core.sys.posix.fcntl; 425 426 struct kvm_t; 427 428 struct kvm_swap { 429 char[32] ksw_devname; 430 int ksw_used; 431 int ksw_total; 432 int ksw_flags; 433 int ksw_reserved1; 434 int ksw_reserved2; 435 }; 436 437 extern(C) @nogc @system nothrow { 438 int sysctl(const(int)* name, uint namelen, void *oldp, size_t *oldlenp, const(void)* newp, size_t newlen); 439 int sysctlbyname(const(char)* name, void *oldp, size_t *oldlenp, const(void)* newp, size_t newlen); 440 441 kvm_t *kvm_open(const(char)*, const(char)*, const(char)*, int, const(char)*); 442 int kvm_close(kvm_t *); 443 int kvm_getswapinfo(kvm_t*, kvm_swap*, int, int); 444 445 int getpagesize(); 446 } 447 448 } 449 450 struct SystemMemInfo 451 { 452 @nogc @safe ulong totalRAM() const nothrow { 453 return _totalRam; 454 } 455 @nogc @safe ulong freeRAM() const nothrow { 456 return _freeRam; 457 } 458 @nogc @safe double freeRAMPercent() const nothrow { 459 return percent(totalRAM, freeRAM); 460 } 461 @nogc @safe ulong usedRAM() const nothrow { 462 return totalRAM() - freeRAM(); 463 } 464 @nogc @safe double usedRAMPercent() const nothrow { 465 return percent(totalRAM, usedRAM); 466 } 467 468 @nogc @safe ulong totalVirtMem() const nothrow { 469 return _totalVirtMem; 470 } 471 @nogc @safe ulong freeVirtMem() const nothrow { 472 return _freeVirtMem; 473 } 474 @nogc @safe double freeVirtMemPercent() const nothrow { 475 return percent(totalVirtMem, freeVirtMem); 476 } 477 @nogc @safe ulong usedVirtMem() const nothrow { 478 return totalVirtMem() - freeVirtMem(); 479 } 480 @nogc @safe double usedVirtMemPercent() const nothrow { 481 return percent(totalVirtMem, usedVirtMem); 482 } 483 484 @trusted void update() { 485 kvm_t* kvmh = errnoEnforce(kvm_open(null, "/dev/null", "/dev/null", O_RDONLY, "kvm_open")); 486 scope(exit) kvm_close(kvmh); 487 kvm_swap k_swap; 488 489 errnoEnforce(kvm_getswapinfo(kvmh, &k_swap, 1, 0) != -1); 490 491 ulong pageSize = cast(ulong)getpagesize(); 492 493 static @trusted int ctlValueByName(const(char)* name) { 494 int value; 495 size_t len = int.sizeof; 496 errnoEnforce(sysctlbyname(name, &value, &len, null, 0) == 0); 497 return value; 498 } 499 500 int totalPages = ctlValueByName("vm.stats.vm.v_page_count"); 501 int freePages = ctlValueByName("vm.stats.vm.v_free_count"); 502 503 _totalRam = cast(ulong)totalPages * pageSize; 504 _freeRam = cast(ulong)freePages * pageSize; 505 506 _totalVirtMem = cast(ulong)k_swap.ksw_total * pageSize + _totalRam; 507 _freeVirtMem = cast(ulong)(k_swap.ksw_total - k_swap.ksw_used) * pageSize + _freeRam; 508 509 } 510 private: 511 ulong _totalRam; 512 ulong _freeRam; 513 514 ulong _totalVirtMem; 515 ulong _freeVirtMem; 516 } 517 518 struct ProcessMemInfo 519 { 520 @nogc @safe ulong usedRAM() const nothrow { 521 return 0; 522 } 523 @nogc @safe ulong usedVirtMem() const nothrow { 524 return 0; 525 } 526 527 @trusted void update() { 528 529 } 530 531 @nogc @safe int processID() const nothrow { 532 return pid; 533 } 534 535 private: 536 void initialize() { 537 pid = thisProcessID; 538 } 539 540 void initialize(int procId) { 541 pid = procId; 542 } 543 544 int pid; 545 } 546 } else version(OSX) { 547 struct SystemMemInfo 548 { 549 @nogc @safe ulong totalRAM() const nothrow { 550 return 0; 551 } 552 @nogc @safe ulong freeRAM() const nothrow { 553 return 0; 554 } 555 @nogc @safe double freeRAMPercent() const nothrow { 556 return 0; 557 } 558 @nogc @safe ulong usedRAM() const nothrow { 559 return 0; 560 } 561 @nogc @safe double usedRAMPercent() const nothrow { 562 return 0; 563 } 564 565 @nogc @safe ulong totalVirtMem() const nothrow { 566 return 0; 567 } 568 @nogc @safe ulong freeVirtMem() const nothrow { 569 return 0; 570 } 571 @nogc @safe double freeVirtMemPercent() const nothrow { 572 return percent(totalVirtMem, freeVirtMem); 573 } 574 @nogc @safe ulong usedVirtMem() const nothrow { 575 return totalVirtMem() - freeVirtMem(); 576 } 577 @nogc @safe double usedVirtMemPercent() const nothrow { 578 return percent(totalVirtMem, usedVirtMem); 579 } 580 @trusted void update() { 581 } 582 } 583 584 struct ProcessMemInfo 585 { 586 @nogc @safe ulong usedRAM() const nothrow { 587 return 0; 588 } 589 @nogc @safe ulong usedVirtMem() const nothrow { 590 return 0; 591 } 592 593 @trusted void update() { 594 } 595 596 @nogc @safe int processID() const nothrow { 597 return pid; 598 } 599 600 private: 601 void initialize() { 602 } 603 604 void initialize(int procId) { 605 } 606 607 int pid; 608 const(char)* proc; 609 } 610 }