Accessing macOS System Information
Sysctl provides an interface that allows you to read and, with appropriate privileges, set many kernel attributes in macOS (and Linux and the BSDs). This provides a wealth of information detailing system hardware and configuration attributes which can be useful when debugging or simply optimising your application (eg to take advantage of threads on multi-core CPU systems).
The '/usr/local/share/fpcsrc/rtl/darwin/sysctlh.inc' source file is a partial translation of the macOS '/usr/include/sys/sysctl.h' file. For details of the sysctls available, start a Terminal and type 'man sysctl'. Typing 'sysctl-a | more' in a Terminal will list all the sysctls.
FPsysctl()
The fpSysCtl() function allows you to retrieve system information. The data returned from the function comprises integers (integer and int64), strings (AnsiStrings) and C structures (records). The function is defined in '/usr/local/share/fpcsrc/rtl/bsd/sysctl.pp' as:
function FPsysctl (Name: pchar; namelen:cuint; oldp:pointer;oldlenp:psize_t; newp:pointer;newlen:size_t):cint; cdecl; external name 'sysctl';
name -- a pointer to a Management Information Base (MIB) array of integers. Each element of this array must contain the correct value to read or write the particular system attribute.
namelen -- the length of the array of integers in name.
Note: if the old attribute value is not required, the next two variables can be set to Nil and 0.
oldp -- a pointer to the buffer into which the value is copied.
oldlenp -- the size of the oldp buffer.
Note: if the new attribute value is not being set, the next two variables should be set to Nil and 0.
newp -- a pointer to the buffer containing the new value to be set.
newlen -- the size of the newp buffer.
The size of the MIB array depends on the data to be read or written. The first element indicates the level of the information and the subsequent elements indicate the value to read. The top level names are:
-------------------------------------------------------------- Name Next level names Description are found in -------------------------------------------------------------- CTL_DEBUG sys/sysctl.h Debugging CTL_VFS sys/mount.h File system CTL_HW sys/sysctl.h Generic CPU, I/O CTL_KERN sys/sysctl.h High kernel limits CTL_MACHDEP sys/sysctl.h Machine dependent CTL_NET sys/socket.h Networking CTL_USER sys/sysctl.h User-level CTL_VM sys/resources.h Virtual memory --------------------------------------------------------------
Let's look at CTL_HW. The subsequent elements are found in the system sysctl.h file and are:
------------------------------------------------------------------------------- Name Value Description ------------------------------------------------------------------------------- #define HW_MACHINE 1 /* string: machine class */ #define HW_MODEL 2 /* string: specific machine model */ #define HW_NCPU 3 /* int: number of cpus */ #define HW_BYTEORDER 4 /* int: machine byte order */ #define HW_PHYSMEM 5 /* int: total memory */ #define HW_USERMEM 6 /* int: non-kernel memory */ #define HW_PAGESIZE 7 /* int: software page size */ #define HW_DISKNAMES 8 /* strings: disk drive names */ #define HW_DISKSTATS 9 /* struct: diskstats[] */ #define HW_EPOCH 10 /* int: 0 for Legacy, else NewWorld */ #define HW_FLOATINGPT 11 /* int: has HW floating point? */ #define HW_MACHINE_ARCH 12 /* string: machine architecture */ #define HW_VECTORUNIT 13 /* int: has HW vector unit? */ #define HW_BUS_FREQ 14 /* int: Bus Frequency */ #define HW_CPU_FREQ 15 /* int: CPU Frequency */ #define HW_CACHELINE 16 /* int: Cache Line Size in Bytes */ #define HW_L1ICACHESIZE 17 /* int: L1 I Cache Size in Bytes */ #define HW_L1DCACHESIZE 18 /* int: L1 D Cache Size in Bytes */ #define HW_L2SETTINGS 19 /* int: L2 Cache Settings */ #define HW_L2CACHESIZE 20 /* int: L2 Cache Size in Bytes */ #define HW_L3SETTINGS 21 /* int: L3 Cache Settings */ #define HW_L3CACHESIZE 22 /* int: L3 Cache Size in Bytes */ #define HW_TB_FREQ 23 /* int: Bus Frequency */ #define HW_MEMSIZE 24 /* uint64_t: physical ram size */ #define HW_AVAILCPU 25 /* int: number of available CPUs */ #define HW_MAXID 26 /* number of valid hw ids */ -------------------------------------------------------------------------------
Armed with this information, we can now find out the number of CPUs:
...
Uses
sysctl;
...
function NumberOfCPU: Integer;
var
mib: array[0..1] of Integer;
status : Integer;
len : size_t;
begin
mib[0] := CTL_HW;
mib[1] := HW_NCPU;
len := SizeOf(Result);
status := fpSysCtl(PChar(@mib), Length(mib), @Result, @len, Nil, 0);
if status <> 0 then RaiseLastOSError;
end;
The above code returns the number of CPUs in an integer; you need to check the C header file to determine what is being returned (an integer, int64, string, struct/record).
There is a possible issue with the above because if you have, for example, an Intel CPU with hyperthreading. One guess. Yes, it will return the total number of threads and not the number of CPUs or even the number of physical cores. HW_NCPU is pretty much deprecated these days as a useful attribute. Instead, we can retrieve the number of CPU packages, physical CPUs (cores) or logical CPUs (the same as HW_NCPU). To do so requires the use of a second function 'FPsysctlbyname'.
However, before we move on, let's look at retrieving a string with FPsysctl() which is a little different tan retrieving an integer.
...
Uses
sysctl;
...
function HWmodel : AnsiString;
var
mib : array[0..1] of Integer;
status : Integer;
len : size_t;
p : PChar;
begin
mib[0] := CTL_HW;
mib[1] := HW_MODEL;
status := fpSysCtl(PChar(@mib), Length(mib), Nil, @len, Nil, 0);
if status <> 0 then RaiseLastOSError;
GetMem(p, len);
try
status := fpSysCtl(PChar(@mib), Length(mib), p, @len, Nil, 0);
if status <> 0 then RaiseLastOSError;
Result := p;
finally
FreeMem(p);
end;
end;
Notice that this time we needed two calls to FPsysctl() - one to find out the size of the buffer required to hold the string value, and the second to actually retrieve the string value into that buffer. On my Apple computer this returns the string "Macmini8,1" - the 2018 Mac mini.
No, we're not quite ready to move on yet... we still need to see how to retrieve information in a C structure (Pascal record).
...
Uses
sysctl;
...
function SwapUsage: String;
type
swapinfo = record
xsu_total : Int64;
xsu_avail : Int64;
xsu_used : Int64;
xsu_pagesize : Integer;
xsu_encrypted: Boolean;
end;
const
VM_SWAPUSAGE = 5;
var
mib : array[0..1] of Integer;
status : Integer;
len : size_t;
swap : swapinfo;
SwapEncrypted: String;
begin
mib[0] := CTL_VM;
mib[1] := VM_SWAPUSAGE;
FillChar(swap, sizeof(swap), 0);
len := sizeof(swap);
status := fpSysCtl(PChar(@mib), Length(mib), @swap, @len, Nil, 0);
if status <> 0 then RaiseLastOSError;
if(swap.xsu_encrypted = true) then
SwapEncrypted := 'Yes' else SwapEncrypted := 'No';
Result := 'Swap total: ' + FloatToStr(Round(swap.xsu_total /1024 /1024)) + ' MB'
+ LineEnding + 'Swap used: ' + FloatToStr(Round(swap.xsu_used /1024 /1024)) + ' MB'
+ LineEnding + 'Swap free: ' + FloatToStr(Round(swap.xsu_avail /1024 /1024)) + ' MB'
+ LineEnding + 'Swap page size: ' + IntToStr(swap.xsu_pagesize) + ' bytes'
+ LineEnding + 'Swap encrypted: ' + SwapEncrypted + LineEnding;
end;
On my computer this returned:
Swap total: 1024 MB Swap used: 191 MB Swap free: 833 MB Swap page size: 4096 bytes Swap encrypted: Yes
The swapinfo record was pretty easily translated from the C structure found in the system sysctl.h file:
struct xsw_usage {
u_int64_t xsu_total;
u_int64_t xsu_avail;
u_int64_t xsu_used;
u_int32_t xsu_pagesize;
boolean_t xsu_encrypted;
};
We also defined the MIB constant VM_SWAPUSAGE by looking at the system sysctl.h file. Why? Because Free Pascal 3.04 was missing this identifier for some reason. So, we looked it up and found:
#define VM_SWAPUSAGE 5 /* total swap usage */
Finally, yes, it is time to move on to the FPsysctlbyname() function.
FPsysctlbyname()
The FPsysctlbyname function is defined in '/usr/local/share/fpcsrc/rtl/bsd/sysctl.pp' as:
function FPsysctlbyname (Name: pchar; oldp:pointer;oldlenp:psize_t; newp:pointer;newlen:size_t):cint; cdecl; external name 'sysctlbyname';
... more content to come ...