├── source └── resusage │ ├── package.d │ ├── common.d │ ├── cpu.d │ └── memory.d ├── .gitignore ├── examples ├── totalusage.d ├── processusage.d ├── cpuselfwatcher.d └── cpuwatcher.d ├── dub.json ├── .github └── workflows │ └── ci.yml ├── LICENSE_1_0.txt ├── appveyor.yml └── README.md /source/resusage/package.d: -------------------------------------------------------------------------------- 1 | module resusage; 2 | 3 | public import resusage.memory; 4 | public import resusage.cpu; 5 | 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .dub 2 | docs.json 3 | __dummy.html 4 | *.o 5 | *.obj 6 | *.a 7 | *.lib 8 | *.so 9 | *.dll 10 | *.exe 11 | bin/ 12 | docs/ 13 | dub.selections.json 14 | -------------------------------------------------------------------------------- /examples/totalusage.d: -------------------------------------------------------------------------------- 1 | /+dub.sdl: 2 | name "totalusage" 3 | dependency "resusage" path="../" 4 | +/ 5 | import std.stdio; 6 | import resusage.memory; 7 | 8 | void main() 9 | { 10 | auto memInfo = systemMemInfo(); 11 | 12 | writefln("Total virtual memory: %s bytes", memInfo.totalVirtMem); 13 | writefln("Virtual memory currently in use: %s bytes (%s %%)", memInfo.usedVirtMem, memInfo.usedVirtMemPercent); 14 | writefln("Total physical memory (RAM): %s bytes", memInfo.totalRAM); 15 | writefln("Physical memory (RAM) currently in use: %s bytes (%s %%)", memInfo.usedRAM, memInfo.usedRAMPercent); 16 | } 17 | -------------------------------------------------------------------------------- /examples/processusage.d: -------------------------------------------------------------------------------- 1 | /+dub.sdl: 2 | name "processusage" 3 | dependency "resusage" path="../" 4 | +/ 5 | import std.stdio; 6 | import std.conv; 7 | import resusage.memory; 8 | 9 | void main(string[] args) 10 | { 11 | if (args.length < 2) { 12 | auto memInfo = processMemInfo(); 13 | writeln("Virtual memory used by this process: ", memInfo.usedVirtMem); 14 | writeln("Physical memory used by this process: ", memInfo.usedRAM); 15 | } else { 16 | string pidStr = args[1]; 17 | auto pid = to!int(pidStr); 18 | auto memInfo = processMemInfo(pid); 19 | writeln("Virtual memory used by process: ", memInfo.usedVirtMem); 20 | writeln("Physical memory used by process: ", memInfo.usedRAM); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /dub.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "resusage", 3 | "description" : "Virtual memory, RAM, CPU usage by system or process", 4 | "license" : "BSL-1.0", 5 | "authors" : ["Roman Chistokhodov"], 6 | 7 | "targetName" : "resusage", 8 | "targetPath" : "lib", 9 | "targetType" : "library", 10 | 11 | "libs-freebsd" : ["kvm"], 12 | 13 | "buildTypes" : { 14 | "docs" : { 15 | "buildOptions" : ["syntaxOnly"], 16 | "dflags": ["-c", "-Dddocs"], 17 | "versions" : ["ResUsageDocs"] 18 | }, 19 | "ddox" : { 20 | "buildOptions" : ["syntaxOnly"], 21 | "dflags": ["-c", "-Df__dummy.html", "-Xfdocs.json"], 22 | "versions" : ["ResUsageDocs"] 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: 3 | pull_request: 4 | push: 5 | release: 6 | types: [published] 7 | 8 | jobs: 9 | Build: 10 | if: "!contains(github.event.head_commit.message, '[skip ci]')" 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: [ubuntu-latest, macos-latest, windows-latest] 15 | dc: 16 | - ldc-latest 17 | - dmd-latest 18 | runs-on: ${{ matrix.os }} 19 | steps: 20 | - uses: actions/checkout@v2 21 | with: 22 | submodules: recursive 23 | 24 | - name: Setup D 25 | uses: dlang-community/setup-dlang@v1 26 | with: 27 | compiler: ${{ matrix.dc }} 28 | 29 | - name: Build 30 | run: | 31 | dub build 32 | 33 | - name: Run example 34 | run: | 35 | dub examples/totalusage.d 36 | -------------------------------------------------------------------------------- /examples/cpuselfwatcher.d: -------------------------------------------------------------------------------- 1 | /+dub.sdl: 2 | name "cpuselfwatcher" 3 | dependency "resusage" path="../" 4 | +/ 5 | import std.stdio; 6 | import core.thread; 7 | import core.time; 8 | import std.parallelism; 9 | import std.getopt; 10 | import std.exception; 11 | import resusage.cpu; 12 | 13 | void work() 14 | { 15 | for (size_t i=0; i totalCPUs) { 29 | stderr.writefln("thread count should not exceed total number of CPU (%s)", totalCPUs); 30 | return 1; 31 | } 32 | auto cpuWatcher = new ProcessCPUWatcher(); 33 | typeof(task!work())[] tasks; 34 | for (ushort i = 1; i $null; 34 | popd; 35 | } 36 | elseif($env:DC -eq "ldc"){ 37 | echo "downloading ..."; 38 | if($env:arch -eq "x86"){ 39 | $env:DConf = "m32"; 40 | } 41 | elseif($env:arch -eq "x64"){ 42 | $env:DConf = "m64"; 43 | } 44 | $env:toolchain = "msvc"; 45 | $version = $env:DVersion; 46 | Invoke-WebRequest "https://github.com/ldc-developers/ldc/releases/download/v$($version)/ldc2-$($version)-win64-msvc.zip" -OutFile "c:\ldc.zip"; 47 | echo "finished."; 48 | pushd c:\\; 49 | 7z x ldc.zip > $null; 50 | popd; 51 | } 52 | } 53 | - ps: SetUpDCompiler 54 | - powershell -Command Invoke-WebRequest http://code.dlang.org/files/dub-1.0.0-windows-x86.zip -OutFile dub.zip 55 | - 7z x dub.zip -odub > nul 56 | - set PATH=%CD%\%binpath%;%CD%\dub;%PATH% 57 | - dub --version 58 | 59 | before_build: 60 | - ps: if($env:arch -eq "x86"){ 61 | $env:compilersetupargs = "x86"; 62 | $env:Darch = "x86"; 63 | } 64 | elseif($env:arch -eq "x64"){ 65 | $env:compilersetupargs = "amd64"; 66 | $env:Darch = "x86_64"; 67 | } 68 | - ps : if($env:DC -eq "dmd"){ 69 | $env:PATH += ";C:\dmd2\windows\bin;"; 70 | } 71 | elseif($env:DC -eq "ldc"){ 72 | $version = $env:DVersion; 73 | $env:PATH += ";C:\ldc2-$($version)-win64-msvc\bin"; 74 | $env:DC = "ldc2"; 75 | } 76 | - ps: $env:compilersetup = "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall"; 77 | - '"%compilersetup%" %compilersetupargs%' 78 | 79 | build_script: 80 | - echo dummy build script - dont remove me 81 | 82 | test_script: 83 | - echo %PLATFORM% 84 | - echo %Darch% 85 | - echo %DC% 86 | - echo %PATH% 87 | - '%DC% --version' 88 | - dub test --arch=%Darch% --compiler=%DC% 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Resusage 2 | 3 | Obtaining of virtual memory, RAM and CPU usage by the whole system or by single process. 4 | 5 | [![Build Status](https://github.com/FreeSlave/resusage/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/FreeSlave/resusage/actions/workflows/ci.yml) 6 | [![Windows Build Status](https://ci.appveyor.com/api/projects/status/github/FreeSlave/resusage?branch=master&svg=true)](https://ci.appveyor.com/project/FreeSlave/resusage) 7 | 8 | Currently works on Linux and Windows. 9 | 10 | FreeBSD support is partial - only system-wide memory information and per-process CPU usage can be retrieved now. 11 | 12 | ## Generating documentation 13 | 14 | Ddoc: 15 | 16 | dub build --build=docs 17 | 18 | Ddox: 19 | 20 | dub build --build=ddox 21 | 22 | ## Brief 23 | 24 | ```d 25 | // import module 26 | import resusage.memory; 27 | 28 | // or the whole package 29 | import resusage; 30 | 31 | // get system memory usage 32 | SystemMemInfo sysMemInfo = systemMemInfo(); 33 | 34 | // access properties 35 | sysMemInfo.totalRAM; 36 | sysMemInfo.usedRAM; 37 | sysMemInfo.freeRAM; 38 | 39 | sysMemInfo.totalVirtMem; 40 | sysMemInfo.usedVirtMem; 41 | sysMemInfo.freeVirtMem; 42 | 43 | // actualize values after some amount of time 44 | sysMemInfo.update(); 45 | 46 | // get memory usage of the current process 47 | ProcessMemInfo procMemInfo = processMemInfo(); 48 | 49 | // or pass process ID to get info about specific process 50 | int pid = ...; 51 | ProcessMemInfo procMemInfo = processMemInfo(pid); 52 | 53 | // access properties 54 | procMemInfo.usedVirtMem; 55 | procMemInfo.usedRAM; 56 | 57 | // actualize values after some amount of time 58 | procMemInfo.update(); 59 | 60 | //import module 61 | import resusage.cpu; 62 | 63 | // create watcher to watch system CPU 64 | auto cpuWatcher = new SystemCPUWatcher(); 65 | 66 | // get actual value when needed 67 | double percent = cpuWatcher.current(); 68 | 69 | // create CPU watcher for current process 70 | auto cpuWatcher = new ProcessCPUWatcher(); 71 | 72 | // or for process with given id 73 | int pid = ...; 74 | auto cpuWatcher = new ProcessCPUWatcher(pid); 75 | 76 | // get actual value when needed 77 | double percent = cpuWatcher.current(); 78 | ``` 79 | 80 | ## Examples 81 | 82 | ### [Total usage](examples/totalusage.d) 83 | 84 | Prints total amount of virtual and physical memory (in bytes) and their current usage in the system (in percents). 85 | 86 | dub examples/totalusage.d 87 | 88 | ### [Process usage](examples/processusage.d) 89 | 90 | Prints amount of virtual and physical memory currently used by process, in bytes. 91 | 92 | dub examples/processusage.d `pidof process` 93 | 94 | ### [CPU Watcher](examples/cpuwatcher.d) 95 | 96 | All following examples show CPU time used by a system or by a process in the 0-100% range. 97 | 98 | Watch system CPU time: 99 | 100 | dub examples/cpuwatcher.d 101 | 102 | Watch process CPU time: 103 | 104 | dub examples/cpuwatcher.d `pidof process` 105 | 106 | Spawn process and watch for its CPU time: 107 | 108 | dub examples/cpuwatcher.d --spawn firefox 109 | 110 | Adjust the rate of output: 111 | 112 | dub examples/cpuwatcher.d --rate=1 --spawn firefox 113 | 114 | ### [CPU self watcher](examples/cpuselfwatcher.d) 115 | 116 | Consume CPU time and report CPU usage by this process: 117 | 118 | dub examples/cpuselfwatcher --threads=2 119 | 120 | E.g. if you have 4 cores and run this example with 2 threads it will report 50% CPU time. 121 | 122 | ## Platform notes and implementation details 123 | 124 | ### Windows 125 | 126 | In order to provide some functionality **resusage** dynamically loads the following libraries at startup: 127 | 128 | 1. [Psapi.dll](https://docs.microsoft.com/en-us/windows/win32/psapi/psapi-reference) for [GetProcessMemoryInfo](https://msdn.microsoft.com/en-us/library/windows/desktop/ms683219(v=vs.85).aspx). 129 | 2. [Pdh.dll](https://msdn.microsoft.com/en-us/library/windows/desktop/aa373083(v=vs.85).aspx) to calculate CPU time used by system. 130 | 131 | If Psapi.dll or Pdh.dll could not be loaded, corresponding functions will always throw *WindowsException*. 132 | 133 | ### Linux 134 | 135 | Uses [sysinfo](https://linux.die.net/man/2/sysinfo), [clock_gettime](https://linux.die.net/man/3/clock_gettime) and proc stats. 136 | 137 | ### FreeBSD 138 | 139 | Uses [sysctl](https://www.freebsd.org/cgi/man.cgi?query=sysctl&apropos=0&sektion=3&arch=default&format=html) to get RAM and 140 | [libkvm](https://www.freebsd.org/cgi/man.cgi?query=kvm_open&apropos=0&sektion=3&arch=default&format=html) to get swap memory to calculate virtual memory. 141 | Uses clock_gettime to evaluate CPU usage. 142 | -------------------------------------------------------------------------------- /source/resusage/cpu.d: -------------------------------------------------------------------------------- 1 | /** 2 | * CPU time 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.cpu; 14 | import resusage.common; 15 | 16 | import std.exception; 17 | 18 | ///Base interface for cpu watchers 19 | interface CPUWatcher 20 | { 21 | ///CPU time currently used, in percents. 22 | @safe double current(); 23 | } 24 | 25 | version(Windows) 26 | { 27 | private import core.stdc.string : memcpy; 28 | 29 | private { 30 | alias HANDLE PDH_HQUERY; 31 | alias HANDLE PDH_HCOUNTER; 32 | alias LONG PDH_STATUS; 33 | 34 | struct PDH_FMT_COUNTERVALUE 35 | { 36 | DWORD CStatus; 37 | static union DUMMYUNIONNAME 38 | { 39 | LONG longValue; 40 | double doubleValue; 41 | LONGLONG largeValue; 42 | LPCSTR AnsiStringValue; 43 | LPCWSTR WideStringValue; 44 | } 45 | DUMMYUNIONNAME dummyUnion; 46 | }; 47 | 48 | extern(Windows) @nogc PDH_STATUS openQueryDummy( 49 | const(wchar)* szDataSource, 50 | DWORD_PTR dwUserData, 51 | PDH_HQUERY *phQuery) @system nothrow { return 0; } 52 | 53 | extern(Windows) @nogc PDH_STATUS addCounterDummy( 54 | PDH_HQUERY Query, 55 | const(wchar)* szFullCounterPath, 56 | DWORD_PTR dwUserData, 57 | PDH_HCOUNTER *phCounter) @system nothrow { return 0; } 58 | 59 | extern(Windows) @nogc PDH_STATUS queryDataDummy( 60 | PDH_HQUERY hQuery) @system nothrow { return 0; } 61 | 62 | extern(Windows) @nogc PDH_STATUS formattedValueDummy( 63 | PDH_HCOUNTER hCounter, 64 | DWORD dwFormat, 65 | LPDWORD lpdwType, 66 | PDH_FMT_COUNTERVALUE* pValue) @system nothrow { return 0; } 67 | 68 | alias typeof(&openQueryDummy) func_PdhOpenQuery; 69 | alias typeof(&addCounterDummy) func_PdhAddCounter; 70 | alias typeof(&queryDataDummy) func_PdhCollectQueryData; 71 | alias typeof(&formattedValueDummy) func_PdhGetFormattedCounterValue; 72 | 73 | __gshared func_PdhOpenQuery PdhOpenQuery; 74 | __gshared func_PdhAddCounter PdhAddCounter; 75 | __gshared func_PdhCollectQueryData PdhCollectQueryData; 76 | __gshared func_PdhGetFormattedCounterValue PdhGetFormattedCounterValue; 77 | 78 | __gshared DWORD pdhError; 79 | 80 | enum PDH_FMT_DOUBLE = 0x00000200; 81 | } 82 | 83 | shared static this() 84 | { 85 | debug import std.stdio : writeln; 86 | 87 | HMODULE pdhLib = LoadLibraryA("Pdh"); 88 | if (pdhLib) { 89 | PdhOpenQuery = cast(func_PdhOpenQuery) GetProcAddress(pdhLib, "PdhOpenQueryW"); 90 | PdhAddCounter = cast(func_PdhAddCounter) GetProcAddress(pdhLib, "PdhAddEnglishCounterW"); 91 | 92 | /* 93 | PdhAddEnglishCounterW is defined only since Windows Vista or Windows Server 2008. 94 | Load locale-dependent PdhAddCounter on older Windows versions and hope that user has english locale. 95 | */ 96 | if (PdhAddCounter is null) { 97 | PdhAddCounter = cast(func_PdhAddCounter) GetProcAddress(pdhLib, "PdhAddCounterW"); 98 | debug writeln("Warning: resusage will use PdhAddCounter, because could not find PdhAddEnglishCounter"); 99 | } 100 | 101 | PdhCollectQueryData = cast(func_PdhCollectQueryData) GetProcAddress(pdhLib, "PdhCollectQueryData"); 102 | PdhGetFormattedCounterValue = cast(func_PdhGetFormattedCounterValue) GetProcAddress(pdhLib, "PdhGetFormattedCounterValue"); 103 | } 104 | 105 | if (!isPdhLoaded()) { 106 | pdhError = GetLastError(); 107 | } 108 | } 109 | 110 | private @nogc @trusted bool isPdhLoaded() { 111 | return PdhOpenQuery && PdhAddCounter && PdhCollectQueryData && PdhGetFormattedCounterValue; 112 | } 113 | 114 | private struct PlatformSystemCPUWatcher 115 | { 116 | @trusted void initialize() { 117 | if (!isPdhLoaded()) { 118 | throw new WindowsException(pdhError, "Pdh.dll is not loaded"); 119 | } 120 | 121 | wenforce(PdhOpenQuery(null, 0, &cpuQuery) == ERROR_SUCCESS, "Could not query pdh data"); 122 | wenforce(PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time"w.ptr, 0, &cpuTotal) == ERROR_SUCCESS, "Could not add pdh counter"); 123 | wenforce(PdhCollectQueryData(cpuQuery) == ERROR_SUCCESS, "Could not collect pdh query data"); 124 | } 125 | 126 | @trusted double current() 127 | { 128 | PDH_FMT_COUNTERVALUE counterVal; 129 | wenforce(PdhCollectQueryData(cpuQuery) == ERROR_SUCCESS, "Could not collect pdh query data"); 130 | wenforce(PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, null, &counterVal) == ERROR_SUCCESS, "Could not format pdh counter data"); 131 | return counterVal.dummyUnion.doubleValue; 132 | } 133 | 134 | private: 135 | PDH_HQUERY cpuQuery; 136 | PDH_HCOUNTER cpuTotal; 137 | } 138 | 139 | private @trusted void timesHelper(int pid, ref ULARGE_INTEGER now, ref ULARGE_INTEGER sys, ref ULARGE_INTEGER user) 140 | { 141 | HANDLE handle = openProcess(pid); 142 | scope(exit) CloseHandle(handle); 143 | 144 | DWORD exitCode; 145 | wenforce(GetExitCodeProcess(handle, &exitCode), "Couldn't access process code"); 146 | if (exitCode == STILL_ACTIVE) 147 | { 148 | FILETIME ftime, fsys, fuser; 149 | GetSystemTimeAsFileTime(&ftime); 150 | memcpy(&now, &ftime, FILETIME.sizeof); 151 | 152 | GetProcessTimes(handle, &ftime, &ftime, &fsys, &fuser); 153 | memcpy(&sys, &fsys, FILETIME.sizeof); 154 | memcpy(&user, &fuser, FILETIME.sizeof); 155 | } 156 | else 157 | { 158 | throw new WindowsException(ERROR_SUCCESS, "Process has exited"); 159 | } 160 | } 161 | 162 | private struct PlatformProcessCPUWatcher 163 | { 164 | @trusted void initialize(int procId) { 165 | pid = procId; 166 | timesHelper(pid, lastCPU, lastSysCPU, lastUserCPU); 167 | } 168 | 169 | @trusted void initialize(HANDLE procHandle) { 170 | pid = GetProcessId(procHandle); 171 | timesHelper(pid, lastCPU, lastSysCPU, lastUserCPU); 172 | } 173 | 174 | @trusted void initialize() { 175 | pid = thisProcessID(); 176 | timesHelper(pid, lastCPU, lastSysCPU, lastUserCPU); 177 | } 178 | 179 | @trusted double current() 180 | { 181 | ULARGE_INTEGER now, sys, user; 182 | timesHelper(pid, now, sys, user); 183 | 184 | auto cpuTime = (sys.QuadPart - lastSysCPU.QuadPart) + (user.QuadPart - lastUserCPU.QuadPart); 185 | auto timeDiff = now.QuadPart - lastCPU.QuadPart; 186 | if (timeDiff == 0) // guard against division by zero 187 | return 0; 188 | double percent = cpuTime / cast(double)timeDiff; 189 | 190 | lastCPU = now; 191 | lastUserCPU = user; 192 | lastSysCPU = sys; 193 | 194 | return percent * 100; 195 | } 196 | 197 | @trusted int processID() const nothrow { 198 | return pid; 199 | } 200 | 201 | private: 202 | int pid; 203 | ULARGE_INTEGER lastCPU, lastUserCPU, lastSysCPU; 204 | } 205 | } else version(linux) { 206 | private @trusted void readProcStat(ref ulong totalUser, ref ulong totalUserLow, ref ulong totalSys, ref ulong totalIdle) 207 | { 208 | FILE* f = errnoEnforce(fopen("/proc/stat", "r")); 209 | scope(exit) fclose(f); 210 | errnoEnforce(fscanf(f, "cpu %llu %llu %llu %llu", &totalUser, &totalUserLow, &totalSys, &totalIdle) == 4); 211 | } 212 | 213 | private struct PlatformSystemCPUWatcher 214 | { 215 | @trusted void initialize() { 216 | readProcStat(lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle); 217 | } 218 | 219 | @trusted double current() 220 | { 221 | ulong totalUser, totalUserLow, totalSys, totalIdle; 222 | readProcStat(totalUser, totalUserLow, totalSys, totalIdle); 223 | 224 | double percent; 225 | 226 | if (totalUser < lastTotalUser || totalUserLow < lastTotalUserLow || 227 | totalSys < lastTotalSys || totalIdle < lastTotalIdle){ 228 | //Overflow detection. Just skip this value. 229 | return lastPercent; 230 | } else { 231 | auto total = (totalUser - lastTotalUser) + (totalUserLow - lastTotalUserLow) + (totalSys - lastTotalSys); 232 | percent = total; 233 | total += (totalIdle - lastTotalIdle); 234 | percent /= total; 235 | percent *= 100; 236 | } 237 | 238 | lastTotalUser = totalUser; 239 | lastTotalUserLow = totalUserLow; 240 | lastTotalSys = totalSys; 241 | lastTotalIdle = totalIdle; 242 | 243 | lastPercent = percent; 244 | return percent; 245 | } 246 | 247 | private: 248 | ulong lastTotalUser, lastTotalUserLow, lastTotalSys, lastTotalIdle; 249 | double lastPercent; 250 | } 251 | } else version(FreeBSD) { 252 | private struct PlatformSystemCPUWatcher 253 | { 254 | @trusted void initialize() { 255 | 256 | } 257 | 258 | @trusted double current() 259 | { 260 | return 0.0; 261 | } 262 | 263 | private: 264 | 265 | } 266 | } else version(OSX) { 267 | private struct PlatformSystemCPUWatcher 268 | { 269 | @trusted void initialize() { 270 | 271 | } 272 | 273 | @trusted double current() 274 | { 275 | return 0.0; 276 | } 277 | 278 | private: 279 | 280 | } 281 | 282 | private struct PlatformProcessCPUWatcher 283 | { 284 | @trusted void initialize(int pid) { 285 | _pid = pid; 286 | } 287 | 288 | @trusted void initialize() { 289 | _pid = thisProcessID; 290 | } 291 | 292 | @trusted double current() 293 | { 294 | return 0.0; 295 | } 296 | 297 | @trusted int processID() const nothrow { 298 | return _pid; 299 | } 300 | 301 | private: 302 | int _pid; 303 | } 304 | } 305 | 306 | static if (is(typeof({import core.sys.posix.time : CLOCK_MONOTONIC;}))) 307 | { 308 | private import core.sys.posix.time; 309 | private import core.time; 310 | private import core.sys.posix.unistd; 311 | private import core.stdc.errno; 312 | extern(C) @nogc int clock_getcpuclockid(pid_t pid, clockid_t *clock_id) nothrow; 313 | 314 | private auto getClockTime(clockid_t clockId) 315 | { 316 | timespec spec; 317 | auto result = clock_gettime(clockId, &spec); 318 | if (result != 0) { 319 | if (errno == EINVAL) { 320 | throw new ErrnoException("clock_gettime failed, the watched process probably died"); 321 | } else { 322 | throw new ErrnoException("clock_gettime"); 323 | } 324 | } else { 325 | return dur!"seconds"(spec.tv_sec) + dur!"nsecs"(spec.tv_nsec); 326 | } 327 | } 328 | 329 | private struct PlatformProcessCPUWatcher 330 | { 331 | @trusted void initialize(int pid) { 332 | _pid = pid; 333 | int result = clock_getcpuclockid(pid, &clockId); 334 | if (result == 0) { 335 | lastCPUTime = getClockTime(clockId); 336 | } else { 337 | errno = result; 338 | errnoEnforce(false, "clock_getcpuclockid"); 339 | } 340 | lastTime = getClockTime(CLOCK_MONOTONIC); 341 | } 342 | 343 | @trusted void initialize() { 344 | static if (is(typeof({import core.sys.posix.time : CLOCK_PROCESS_CPUTIME_ID;}))) { 345 | import core.sys.posix.time : CLOCK_PROCESS_CPUTIME_ID; 346 | _pid = getpid(); 347 | clockId = CLOCK_PROCESS_CPUTIME_ID; 348 | lastCPUTime = getClockTime(clockId); 349 | lastTime = getClockTime(CLOCK_MONOTONIC); 350 | } else { 351 | initialize(getpid()); 352 | } 353 | } 354 | 355 | @trusted double current() 356 | { 357 | auto nowTime = getClockTime(CLOCK_MONOTONIC); 358 | auto nowCPUTime = getClockTime(clockId); 359 | double percent; 360 | 361 | if (nowTime < lastTime || nowCPUTime < lastCPUTime) { 362 | //Overflow detection. Just skip this value. 363 | return lastPercent; 364 | } else { 365 | percent = (nowCPUTime - lastCPUTime).total!"hnsecs"; 366 | percent /= (nowTime - lastTime).total!"hnsecs"; 367 | percent *= 100; 368 | } 369 | lastTime = nowTime; 370 | lastCPUTime = nowCPUTime; 371 | lastPercent = percent; 372 | return percent; 373 | } 374 | 375 | @trusted int processID() const nothrow { 376 | return _pid; 377 | } 378 | 379 | private: 380 | int _pid; 381 | double lastPercent; 382 | clockid_t clockId; 383 | Duration lastTime; 384 | Duration lastCPUTime; 385 | } 386 | } 387 | 388 | ///System CPU watcher. 389 | final class SystemCPUWatcher : CPUWatcher 390 | { 391 | /** 392 | * Watch system. 393 | */ 394 | @safe this() { 395 | _watcher.initialize(); 396 | } 397 | 398 | /** 399 | * CPU time used by all processes in the system, in percents. 400 | * This returns the average value amongst all CPUs, thus can't exceed 100. 401 | */ 402 | @safe override double current() { 403 | return _watcher.current(); 404 | } 405 | 406 | private: 407 | PlatformSystemCPUWatcher _watcher; 408 | } 409 | 410 | ///CPU watcher for single process. 411 | final class ProcessCPUWatcher : CPUWatcher 412 | { 413 | /** 414 | * Watch process by id. 415 | * Params: 416 | * pid = ID number of process. Can be process handle on Windows. 417 | */ 418 | @safe this(int pid) { 419 | _watcher.initialize(pid); 420 | } 421 | 422 | version(Windows) { 423 | @safe this(HANDLE procHandle) { 424 | _watcher.initialize(procHandle); 425 | } 426 | } 427 | 428 | ///ditto, but watch this process. 429 | @safe this() { 430 | _watcher.initialize(); 431 | } 432 | /** 433 | * CPU time used by underlying process, in percents between 0 and 100. 434 | */ 435 | @safe override double current() { 436 | import std.parallelism : totalCPUs; 437 | return _watcher.current() / totalCPUs; 438 | } 439 | 440 | /** 441 | * CPU time used by underlying process. The value range depends on CPU count. 442 | * E.g. on the system with 4 CPUs it will be between 0 and 400 (considering all units are available for the process). 443 | */ 444 | @safe double currentTotal() { 445 | return _watcher.current(); 446 | } 447 | 448 | ///The process ID number. 449 | @safe int processID() const nothrow { 450 | return _watcher.processID(); 451 | } 452 | 453 | private: 454 | PlatformProcessCPUWatcher _watcher; 455 | } 456 | -------------------------------------------------------------------------------- /source/resusage/memory.d: -------------------------------------------------------------------------------- 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(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 | } 611 | --------------------------------------------------------------------------------