dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.26k stars 4.73k forks source link

Implementing GlobalMemoryStatusEx using kstat_open(3) on Solaris #34996

Closed am11 closed 4 years ago

am11 commented 4 years ago

Information about swap memory is not available using sysinfo(2), although it exists on Solaris to provide other information. Therefore, to implement GlobalMemoryStatusEx, it seems like kstat_open(3) and friends are most suitable options.

As per the discussion in this thread, the system counters provided by kstat are mainly "sum total of the value sampled every second since the system was booted", so it requires a bit of computation. Here is a self-contained repro: https://pastebin.com/raw/VxVpwJhC. The result on SmartOS x64 looks like this:

# using vagrant box: https://app.vagrantup.com/secretescapes/boxes/smartos-base64

$ curl https://pastebin.com/raw/VxVpwJhC -o kstat_repro.c
$ gcc -lkstat kstat_repro.c
$ ./a.out
swap_free with updates: 1908323026 # this is most likely sufficient for our usecase?
swap_free with ltime:   1908260647 # xor this?

At this point, df -b reports:

$ df -b
Filesystem              avail
/devices/ramdisk:a      25637
/devices                    0
/dev                        0
ctfs                        0
proc                        0
mnttab                      0
swap                  1760148
objfs                       0
bootfs                      0
sharefs                     0
/devices/pseudo/lofi@1:disk   105976
/usr/lib/libc/libc_hwcap1.so.1   105976
fd                          0
zones                49571816
zones/archive        49571816
zones/cores/global   10407792
zones/var            49571816
zones/config         49571816
zones/opt            49571816
zones/usbkey         49571816
/usbkey/ssh          49571816
swap                  1760148
swap                  1760148
/usbkey/etc/opasswd  49571816
/usbkey/etc/passwd   49571816
/usbkey/etc/user_attr 49571816
/usbkey/etc/group    49571816
/usbkey/etc/shadow   49571816

There is also this blog article which sheds light on kstat and sample program iterates over the linked list to gather all the raw data:

$ curl -O http://iks.cs.ovgu.de/~elkner/tmp/kstat.c
$ gcc -lkstat kstat.c
$ ./a.out 2>&1 | grep -B3 swap
ks_name=kstat_headers  ks_class=kstat  ks_instance=0  ks_module=unix  ks_ndata=946  ks_data_size=174064  ks_type=RAW
        reading kstats
        rcw_vminfo freemem= 0 , swap_resv= 0, swap_alloc=8676587605896200192, swap_avail=0 swap_free=0
--
        contains_search=0   (4 ulong)
ks_name=sysinfo  ks_class=misc  ks_instance=0  ks_module=unix  ks_ndata=24  ks_data_size=24  ks_type=RAW
        reading kstats
        rcw_vminfo freemem= 279104156263557 , swap_resv= 35467358894075094, swap_alloc=197568605324, swap_avail=0 swap_free=32
ks_name=vminfo  ks_class=vm  ks_instance=0  ks_module=unix  ks_ndata=48  ks_data_size=48  ks_type=RAW
        reading kstats
        rcw_vminfo freemem= 78191973109 , swap_resv= 34743278835, swap_alloc=28486459870, swap_avail=692705930984 swap_free=698962749949
--
        duplicate_buffers_found=0   (2)
ks_name=var  ks_class=misc  ks_instance=0  ks_module=unix  ks_ndata=60  ks_data_size=60  ks_type=RAW
        reading kstats
        rcw_vminfo freemem= 100 , swap_resv= 13078175419370, swap_alloc=425201762414, swap_avail=13078175416320 swap_free=4393751544832

The interesting part for global memory status is (under ks_name=vminfo ks_class=vm):

rcw_vminfo freemem= 78191973109 , swap_resv= 34743278835, swap_alloc=28486459870, swap_avail=692705930984 swap_free=698962749949

which I am having trouble mapping to ullTotalPageFile and ullAvailPageFile. Would it be correct to compute as:

// instantaneous_swap_XX is computed value for this instant since the system booted
// i.e. (vminfo.swap_XX * PAGESIZE) / sysinfo.updates

ullTotalPageFile = instantaneous_swap_alloc + instantaneous_swap_avail
ullAvailPageFile = instantaneous_swap_free

cc @janvorli

Dotnet-GitSync-Bot commented 4 years ago

I couldn't figure out the best area label to add to this issue. Please help me learn by adding exactly one area label.

janvorli commented 4 years ago

There is an exact description of the meaning of the returned structure members on Windows: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex It says:

ullTotalPageFile The current committed memory limit for the system or the current process, whichever is smaller, in bytes. To get the system-wide committed memory limit, call GetPerformanceInfo.

ullAvailPageFile The maximum amount of memory the current process can commit, in bytes. This value is equal to or smaller than the system-wide available commit value. To calculate the system-wide available commit value, call GetPerformanceInfo and subtract the value of CommitTotal from the value of CommitLimit.

I am not sure how that maps to the values you got since I don't know what they really mean.

am11 commented 4 years ago

It looks like we do not need kstat for this, but instead swapctl(2). The following program:

#include <inttypes.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/swap.h>

int main(int argc, char **argv)
{
    struct anoninfo ai;
    if (swapctl(SC_AINFO, &ai) != -1)
    {
        int pagesize = getpagesize();
        printf("free:\t%" PRIu64 "\ntotal:\t%" PRIu64 "\n", ai.ani_free * pagesize, ai.ani_max * pagesize);
    }
    return 0;
}

on SmartOS x64 prints:

free:   1833984000
total:  1911136256

Btw, is our usage of sysinfo.totalswap and sysinfo.freeswap correct in case of Linux? I ran the following program on Ubuntu 18.04 x64:

#include <inttypes.h>
#include <stdio.h>
#include <sys/sysinfo.h>

int main(int argc, char **argv)
{
    struct sysinfo info;
    if (sysinfo(&info) == 0)
    {
        printf("total %" PRIu64 "\nfree: %" PRIu64 "\n", info.totalswap, info.freeswap);
    }
    return 0;
}

and it prints 0:

total 0
free: 0
janvorli commented 4 years ago

Do you have swap enabled on your Ubuntu? The zeros seems to indicate you don't.

am11 commented 4 years ago

Thank you, that was the reason. After creating 1Gb swapfile and enabling it on Ubuntu, I get:

$ free -m
              total        used        free      shared  buff/cache   available
Mem:           7953        2340         280          85        5332        5220
Swap:          1023           0        1023

$ ./a.out
total 1073737728
free: 1073737728

I will use swapctl(2) for SunOS and continue the port.