# HG changeset patch
# User Lars Kindermann <lars.kindermann@reglos.de>
# Date 1588443490 -7200
#      Sat May 02 20:18:10 2020 +0200
# Node ID 048933becd8c5546da04af59a7259fb722503184
# Parent  b42fb8061fb9d89bf6b4fba1afab36c167545eab
Add "memory" function (patch #9924).

* scripts/miscellaneous/memory.m: Add function that returns memory information
on Linux and Windows platforms.

* scripts/miscellaneous/module.mk: Include new file in build system.

* scripts/help/__unimplementd__.m: Remove from list.

* doc/interpreter/system.txi: Include function documentation in manual.

diff -r b42fb8061fb9 -r 048933becd8c doc/interpreter/system.txi
--- a/doc/interpreter/system.txi	Wed Apr 22 16:04:48 2020 +0200
+++ b/doc/interpreter/system.txi	Sat May 02 20:18:10 2020 +0200
@@ -554,6 +554,8 @@
 
 @DOCSTRING(license)
 
+@DOCSTRING(memory)
+
 @DOCSTRING(getrusage)
 
 @DOCSTRING(winqueryreg)
diff -r b42fb8061fb9 -r 048933becd8c scripts/help/__unimplemented__.m
--- a/scripts/help/__unimplemented__.m	Wed Apr 22 16:04:48 2020 +0200
+++ b/scripts/help/__unimplemented__.m	Sat May 02 20:18:10 2020 +0200
@@ -981,7 +981,6 @@
   "memmapfile",
   "memoize",
   "MemoizedFunction",
-  "memory",
   "mergecats",
   "meta.abstractDetails",
   "meta.class.fromName",
diff -r b42fb8061fb9 -r 048933becd8c scripts/miscellaneous/memory.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/miscellaneous/memory.m	Sat May 02 20:18:10 2020 +0200
@@ -0,0 +1,218 @@
+########################################################################
+##
+## Copyright (C) 2020 The Octave Project Developers
+##
+## See the file COPYRIGHT.md in the top-level directory of this
+## distribution or <https://octave.org/copyright/>.
+##
+## This file is part of Octave.
+##
+## Octave is free software: you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation, either version 3 of the License, or
+## (at your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+## GNU General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <https://www.gnu.org/licenses/>.
+##
+########################################################################
+
+## -*- texinfo -*-
+## @deftypefn  {} {} memory ()
+## @deftypefnx {} {@var{userinfo} =} memory ()
+## @deftypefnx {} {[@var{userinfo}, @var{systeminfo}] =} memory ()
+##
+## Display or return information about memory usage by Octave.
+##
+## @example
+## @group
+## memory ()
+##    @result{} System    RAM: 3934008 KiB,  swap: 4087804 KiB
+##       Octave    RAM:  170596 KiB,  virt: 1347944 KiB
+##       Free      RAM: 1954940 KiB,  swap: 4087804 KiB
+##       Available RAM: 2451948 KiB, total: 6042744 KiB
+##
+## [userinfo, systeminfo] = memory ()
+##    @result{} userinfo =
+##      scalar structure containing the fields:
+##      MaxPossibleArrayBytes = 6.1622e+09
+##      MemAvailableAllArrays = 6.1622e+09
+##      MemUsedMATLAB = 1.3825e+09
+##      ram_available_all_arrays = 2.4883e+09
+##      mem_used_octave = 1.3825e+09
+##      ram_used_octave = 1.7824e+08
+##
+##    systeminfo =
+##      scalar structure containing the fields:
+##      PhysicalMemory =
+##        scalar structure containing the fields:
+##          Total = 4.0284e+09
+##          Available = 2.4954e+09
+##      SystemMemory =
+##        scalar structure containing the fields:
+##          Total = 8.2143e+09
+##          Available = 6.6813e+09
+##      VirtualAddressSpace =
+##        scalar structure containing the fields:
+##          Total = 2.8147e+14
+##          Available = 2.8147e+14
+## @end group
+## @end example
+##
+## This function is currently implemented for Linux and Windows only.
+##
+## @seealso{computer, getpid, getrusage, nproc, uname}
+## @end deftypefn
+
+function [userdata, systemdata] = memory ()
+
+  if (! isunix () && ! ispc ())
+    if (nargout > 0)
+      error ('memory: function not yet implemented for this architecture');
+    else
+      warning ('memory: function not yet implemented for this architecture');
+    endif
+    return;
+  endif
+
+  kiB = 1024;
+  [architecture, bits] = computer ();
+
+  if (isunix ())
+    ## Read the /proc/pid/statm pseudofile
+    ## see https://linuxwiki.de/proc/pid#A.2Fproc.2Fpid.2Fstatm
+    ## It contains 7 numbers in total:
+    ##   1st: total memory used by the process
+    ##   2nd: currently used RAM
+    ## Values are in pages of size 4096 Bytes
+
+    f = fopen ('/proc/self/statm');
+    buffer = textscan (f);
+    fclose (f);
+    statm = buffer{:};
+    UsedRAM = statm(2) * 4096;
+    UsedVirtual = statm(1) * 4096;
+
+    ## Read the proc/self/status pseudofile
+    ## see https://linuxwiki.de/proc/pid#A.2Fproc.2Fpid.2Fstatus
+    ## It contains a variable number of lines with name-value pairs.
+
+    f = fopen ('/proc/self/status');
+    buffer = textscan (f, '%s %s', 'delimiter', ':\n');
+    fclose (f);
+    for i = 1:size (buffer{1}, 1)
+      status.(buffer{1}{i}) = buffer{2}{i};
+      status_numeric.(buffer{1}{i}) = textscan (buffer{2}{i}){:};
+    endfor
+
+    ## Read the /proc/meminfo pseudofile
+    ## see https://linuxwiki.de/proc/meminfo
+    ## It contains a variable number of lines with name-value pairs.
+
+    f = fopen ('/proc/meminfo');
+    buffer = textscan (f, '%s %s', 'delimiter', ':\n');
+    fclose (f);
+    for i = 1:size (buffer{1}, 1)
+      meminfo.(buffer{1}{i}) = buffer{2}{i};
+      meminfo_numeric.(buffer{1}{i}) = textscan (buffer{2}{i}){:};
+    endfor
+
+    ## FIXME: Query the actual size of the user address space,
+    ## e.g. with getrlimit (RLIMIT_AS, rlp)
+    if (log2 (bits) > 32)
+      ## 64bit platform
+      address_space = 2^48;  # 256 TiB
+    else
+      ## 32bit platform
+      address_space =  3 * 2^30;  # 3 GiB
+    endif
+
+    total_ram = meminfo_numeric.MemTotal * kiB;
+    total_swap = meminfo_numeric.SwapTotal * kiB;
+    free_ram = meminfo_numeric.MemFree * kiB;
+    if isfield (meminfo_numeric, "MemAvailable")
+      available_ram = meminfo_numeric.MemAvailable * kiB;
+    else
+      available_ram = free_ram;
+    endif
+    free_swap = meminfo_numeric.SwapFree * kiB;
+    used_ram = status_numeric.VmRSS * kiB;
+    used_virtual = status_numeric.VmSize * kiB;
+    # used_shared = status_numeric.RssShmem * kiB;
+    avail_virtual = address_space - used_virtual
+
+  elseif (ispc ())
+    [proc, sys] = __wmemory__ ();
+
+    total_ram = sys.TotalPhys;
+    total_swap = sys.TotalPageFile;
+    available_ram = sys.AvailPhys;
+    free_swap = sys.AvailPageFile;
+    used_ram = proc.WorkingSetSize;
+    used_virtual = proc.PagefileUsage;
+    avail_virtual = sys.AvailVirtual;
+    address_space = sys.TotalVirtual;
+
+  endif
+
+  available = min (available_ram + free_swap, avail_virtual);
+  ram_available = min (available_ram, avail_virtual);
+
+  ## FIXME: On 32bit systems, the largest possible array is limited by the
+  ## largest contiguous block in memory.
+  user.MaxPossibleArrayBytes = available;
+  user.MemAvailableAllArrays = available;
+  user.ram_available_all_arrays = ram_available; # Octave only = top Mem Avail on 64 Bit
+  user.MemUsedMATLAB = used_virtual;  # For Compatibility
+  user.mem_used_octave = used_virtual;  # Octave only = top VIRT
+  user.ram_used_octave = used_ram;  # Octave only = top RES
+  # user.shm_used_octave = used_shared;  # Octave only = top SHR
+
+  syst.PhysicalMemory.Available = available_ram;
+  syst.PhysicalMemory.Total = total_ram;
+  syst.SystemMemory.Available = available_ram + free_swap;
+  syst.SystemMemory.Total = total_ram + total_swap;  #octave only
+  syst.VirtualAddressSpace.Available = avail_virtual;
+  syst.VirtualAddressSpace.Total = address_space;
+
+
+  if (nargout)
+    userdata = user;
+    systemdata = syst;
+  else
+    unitsize = kiB;
+    unitname = 'kiB';
+    disp (sprintf ('Octave is running on %s', architecture))
+    disp (sprintf ('System    RAM: %9.0f %s,  swap: %9.0f %s',
+                   round (syst.PhysicalMemory.Total / unitsize), unitname,
+                   round (total_swap / unitsize), unitname ))
+    disp (sprintf ('Octave    RAM: %9.0f %s,  virt: %9.0f %s',
+                   round (user.ram_used_octave / unitsize), unitname,
+                   round (user.mem_used_octave / unitsize), unitname))
+    if (isunix ())
+      ## The concept of free vs. available RAM doesn't seem to exist on Windows
+      disp (sprintf ('Free      RAM: %9.0f %s,  swap: %9.0f %s',
+                     round (free_ram / unitsize), unitname,
+                     round (free_swap / unitsize), unitname))
+    endif
+    disp (sprintf ('Available RAM: %9.0f %s, total: %9.0f %s',
+                   round (user.ram_available_all_arrays / unitsize), unitname,
+                   round (user.MemAvailableAllArrays / unitsize), unitname))
+  endif
+
+endfunction
+
+
+%!test
+%! [user syst] = memory ();
+%! assert (!isunix || user.MemUsedOctave > 0);
+%! assert (!isunix || user.RamUsedOctave <= user.MemUsedOctave);
+%! assert (!isunix || user.MemUsedOctave < syst.SystemMemory.Total);
+%! assert (!isunix || user.MemAvailableAllArrays <= syst.SystemMemory.Available);
+
diff -r b42fb8061fb9 -r 048933becd8c scripts/miscellaneous/module.mk
--- a/scripts/miscellaneous/module.mk	Wed Apr 22 16:04:48 2020 +0200
+++ b/scripts/miscellaneous/module.mk	Sat May 02 20:18:10 2020 +0200
@@ -45,6 +45,7 @@
   %reldir%/loadobj.m \
   %reldir%/ls.m \
   %reldir%/ls_command.m \
+  %reldir%/memory.m \
   %reldir%/menu.m \
   %reldir%/methods.m \
   %reldir%/mex.m \
