Difference between revisions of "Accessing FreeBSD System Information"

From Free Pascal wiki
Jump to navigationJump to search
(→‎Accessing FreeBSD System Information: work in progress 40% done)
m (Fixed typo)
(One intermediate revision by the same user not shown)
Line 1: Line 1:
 
[https://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3 Sysctl] provides an interface that allows you to read and, with appropriate privileges, set many kernel attributes in FreeBSD (the BSDs, macOS and Linux). 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).
 
[https://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3 Sysctl] provides an interface that allows you to read and, with appropriate privileges, set many kernel attributes in FreeBSD (the BSDs, macOS and Linux). 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/freebsd/sysctlh.inc' source file is a partial translation of the FreeBSD '/usr/include/sys/sysctl.h' file. For details of the sysctls available, start a console or X11 xterm and type 'man sysctl'. Typing 'sysctl-a | more' will list all the sysctls.
+
The '/usr/local/share/fpcsrc/rtl/freebsd/sysctlh.inc' source file is a partial translation of the FreeBSD '/usr/include/sys/sysctl.h' file. For details of the sysctls available, start a console or X11 xterm and type 'man sysctl'. Typing 'sysctl -a | more' will list all the sysctls.
  
 
==FPsysctl()==
 
==FPsysctl()==
Line 35: Line 35:
 
           -------------------------------------------------------------------------------
 
           -------------------------------------------------------------------------------
 
           #define CTL_KERN        1        sys/sysctl.h        /* High kernel limits */
 
           #define CTL_KERN        1        sys/sysctl.h        /* High kernel limits */
           #define CTL_VM          2        sys/vmmeter.h      /* Virtual memory */
+
           #define CTL_VM          2        vm/vm_param.h      /* Virtual memory */
 
           #define CTL_VFS          3        sys/mount.h        /* File system */
 
           #define CTL_VFS          3        sys/mount.h        /* File system */
 
           #define CTL_NET          4        sys/socket.h        /* Networking */
 
           #define CTL_NET          4        sys/socket.h        /* Networking */
Line 90: Line 90:
 
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).  
 
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'.
+
There is a possible issue with the above because if you have, for example, a multi-core CPU. One guess. Yes, it will return the total number of cores and not the number of CPUs. This probably does not matter depending on your use case, but you should be aware of it.  
  
However, before we move on, let's look at retrieving a string with FPsysctl() which is a little different than retrieving an integer.
+
Before we move on to look at a second function 'FPsysctlbyname', let's look at retrieving a string with FPsysctl() which is a little different than retrieving an integer.
  
 
<source lang="pascal">
 
<source lang="pascal">
Line 126: Line 126:
 
</source>
 
</source>
  
{{Warning | The rest of this page is my macOS Sysctl article - updating for FreeBSD is in progress}}
+
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 FreeBSD computer this returns the string "Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz".
 
 
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).
 
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).
 
  
 
<source lang="pascal">
 
<source lang="pascal">
Line 138: Line 135:
 
   sysctl;
 
   sysctl;
 
...
 
...
function SwapUsage: String;
+
function GetClockInfo: String;
 
 
 
type
 
type
  swapinfo = record
+
  clockinfo = record
   
+
    hz      : Integer; // clock frequency
end;
+
    tick    : Integer;  // ms per Hz tick
 +
    spare  : Integer;  // unused
 +
    stathz  : Integer;  // statistics clock frequency
 +
    profhz  : Integer;  // profiling clock frequency
 +
  end;
  
 
var
 
var
  mib : array[0..1] of Integer;
+
mib : array[0..1] of Integer;
  status : Integer;
+
status : Integer;
  len : size_t;
+
len : size_t;
  swap : swapinfo;
+
clock : clockinfo;
 
 
 
begin
 
begin
  mib[0] := CTL_VM;
+
mib[0] := CTL_KERN;
  mib[1] := VM_SWAPUSAGE;
+
mib[1] := KERN_CLOCKRATE;
  
  FillChar(swap, sizeof(swap), 0);
+
FillChar(clock, sizeof(clock), 0);
  len := sizeof(swap);
+
len := sizeof(clock);
  
  status := fpSysCtl(PChar(@mib), Length(mib), @swap, @len, Nil, 0);
+
status := fpSysCtl(@mib, Length(mib), @clock, @len, Nil, 0);
  if status <> 0 then RaiseLastOSError;
+
if status <> 0 then RaiseLastOSError;
  
  Result := 'Swap total: ' + FloatToStr(Round(swap.xsu_total /1024 /1024)) + ' MB'
+
Result := 'Clock freq: ' + IntToStr(clock.hz) + 'Hz' + LineEnding
            + LineEnding + 'Swap used: ' + FloatToStr(Round(swap.xsu_used /1024 /1024)) + ' MB'
+
          + 'Ms per Hz tick: ' + IntToStr(clock.tick) + LineEnding
            + LineEnding + 'Swap free: ' + FloatToStr(Round(swap.xsu_avail /1024 /1024)) + ' MB'
+
          + 'Profiling clock freq: ' + IntToStr(clock.profhz) + ' Hz'
            + LineEnding + 'Swap page size: ' + IntToStr(swap.xsu_pagesize) + ' bytes'
+
          + LineEnding;
            + LineEnding + 'Swap encrypted: ' + SwapEncrypted + LineEnding;
+
end;  
end;
 
 
</source>
 
</source>
  
 
On my computer this returned:
 
On my computer this returned:
  
  Swap total: 1024 MB
+
Clock freq: 100Hz
  Swap used: 191 MB
+
Ms per Hz tick: 10000
  Swap free: 833 MB
+
Profiling clock freq: 8128 Hz
  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:
+
The clockinfo record was pretty easily translated from the C structure found in the system time.h file:
  
 
<source lang="c">
 
<source lang="c">
struct xsw_usage {
+
struct clockinfo {
u_int64_t      xsu_total;
+
        int    hz;             /* clock frequency */
u_int64_t      xsu_avail;
+
        int    tick;           /* micro-seconds per hz tick */
u_int64_t      xsu_used;
+
        int    spare;
u_int32_t      xsu_pagesize;
+
        int    stathz;         /* statistics clock frequency */
boolean_t      xsu_encrypted;
+
        int    profhz;         /* profiling clock frequency */
 
};
 
};
</source>
 
 
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:
 
 
<source lang="c">
 
#define VM_SWAPUSAGE    5              /* total swap usage */
 
 
</source>
 
</source>
  
 
Finally, yes, it is time to move on to the FPsysctlbyname() function.
 
Finally, yes, it is time to move on to the FPsysctlbyname() function.
 
  
 
== FPsysctlbyname ==
 
== FPsysctlbyname ==
Line 206: Line 195:
 
</source>
 
</source>
  
There. Not realy different from the FPsysctl() function. Is that a sigh of relief? So let's look at finally retrieving the number of physical CPU cores.
+
There. Not realy different from the FPsysctl() function. Is that a sigh of relief? So let's look at finally retrieving the number of CPU cores.
  
 
<source lang="pascal">
 
<source lang="pascal">
function NumberOfCores: Integer;
+
function NumberOfCPU: Integer;
  
 
var
 
var
Line 218: Line 207:
 
   len := SizeOf(Result);
 
   len := SizeOf(Result);
  
   status := fpSysCtlByName('hw.physicalcpu', @Result, @len, Nil, 0);
+
   status := fpSysCtlByName('hw.ncpu', @Result, @len, Nil, 0);
 
   if status <> 0 then RaiseLastOSError;
 
   if status <> 0 then RaiseLastOSError;
 
end;
 
end;
 
</source>
 
</source>
  
On my 2018 Mac mini this returns the correct number of cores: 6. Where do you find the special incantation 'hw.physicalcpu' ? Yes, in the system sysctl.h file. Here's a brief excerpt of some of the hardware selectors:
+
On my FreeBSD computer this returns the number of cores: 6. Where do you find the special incantation 'hw.ncpu' ? In the manual pages for the sysctl command (''man 3 sysctl'' [Library functions section] and ''man 8 sysctl'' [System manager's section]). Here's a brief excerpt of the hardware selectors:
 +
 
 +
    Parameters that are byte counts are 64 bit numbers. All other byte parameters are 32 bit numbers.
  
  These are the support HW selectors for sysctlbyname.  Parameters that are byte counts or frequencies are 64 bit numbers.
+
    --------------------------------------------------------------------------------------------
  All other parameters are 32 bit numbers.
+
    Descriptor                  Type        Changeable    Description
 
+
    --------------------------------------------------------------------------------------------
  hw.memsize                - The number of bytes of physical memory in the system.
+
    hw.machine                  string        no          Machine class
  hw.ncpu                  - The maximum number of processors that could be available this boot.
+
    hw.model                    string       no          Machine model
                              Use this value for sizing of static per processor arrays; i.e. processor load statistics.
+
    hw.ncpu                    integer      no          Number of cpus
  hw.activecpu              - The number of processors currently available for executing threads.
+
    hw.byteorder                integer      no          Byteorder (4321 or 1234)
                              Use this number to determine the number threads to create in SMP aware applications.
+
    hw.physmem                  integer      no          Bytes of physical memory
                              This number can change when power management modes are changed.
+
    hw.usermem                  integer      no           Bytes of non-kernel memory
  hw.physicalcpu            - The number of physical processors available in the current power management mode.
+
    hw.pagesize                integer       no          Software page size
  hw.physicalcpu_max       - The maximum number of physical processors that could be available this boot
+
    hw.floatingpoint            integer       no          Non-zero if hardware floating point
  hw.logicalcpu            - The number of logical processors available in the current power management mode.
+
    hw.machine_arch            string        no           Machine dependent architecture
  hw.logicalcpu_max        - The maximum number of logical processors that could be available this boot
+
    hw.realmem                  integer       no          Bytes of real memory
  hw.tbfrequency            - This gives the time base frequency used by the OS and is the basis of all timing services.
+
    --------------------------------------------------------------------------------------------
                              In general is is better to use mach's or higher level timing services, but this value
 
                              is needed to convert the PPC Time Base registers to real time.
 
  hw.cpufrequency           - These values provide the current, min and max cpu frequency.  The min and max are for
 
  hw.cpufrequency_max       - all power management modes.  The current frequency is the max frequency in the current mode.
 
  hw.cpufrequency_min       - All frequencies are in Hz.
 
  hw.busfrequency           - These values provide the current, min and max bus frequency.  The min and max are for
 
  hw.busfrequency_max       - all power management modes.  The current frequency is the max frequency in the current mode.
 
  hw.busfrequency_min      - All frequencies are in Hz.
 
  hw.cputype                - These values provide the mach-o cpu type and subtype.  A complete list is in <mach/machine.h>
 
  hw.cpusubtype            - These values should be used to determine what processor family the running cpu is from so that
 
                              the best binary can be chosen, or the best dynamic code generated.  They should not be used
 
                              to determine if a given processor feature is available.
 
  hw.cputhreadtype          - This value will be present if the processor supports threads.  Like hw.cpusubtype this selector
 
                              should not be used to infer features, and only used to name the processors thread architecture.
 
                              The values are defined in <mach/machine.h>
 
  hw.byteorder              - Gives the byte order of the processor.  4321 for big endian, 1234 for little.
 
  hw.pagesize              - Gives the size in bytes of the pages used by the processor and VM system.
 
  hw.cachelinesize          - Gives the size in bytes of the processor's cache lines.
 
                              This value should be use to control the strides of loops that use cache control instructions
 
                              like dcbz, dcbt or dcbst.
 
  hw.l1dcachesize          - These values provide the size in bytes of the L1, L2 and L3 caches.  If a cache is not present
 
  hw.l1icachesize          - then the selector will return and error.
 
  hw.l2cachesize            -
 
  hw.l3cachesize            -
 
  hw.packages              - Gives the number of processor packages.
 
  
If you prefer, you can instead use the sysctl command line utility in a Terminal to list all the hw selectors with "sysctl hw.".
+
If you prefer, you can also use the sysctl command line utility in a console or X11 xterm to list all the hw selectors with "sysctl hw.".
  
 
Retrieving a string value, rather than an integer, is again reminiscent of FPsysctl(). Two calls to FPsysctlbyname() are needed: (1) to find out the size of the buffer to hold the string to be returned and (2) to return the string value into that buffer. Don't forget to allocate memory for the buffer before the second call!
 
Retrieving a string value, rather than an integer, is again reminiscent of FPsysctl(). Two calls to FPsysctlbyname() are needed: (1) to find out the size of the buffer to hold the string to be returned and (2) to return the string value into that buffer. Don't forget to allocate memory for the buffer before the second call!
  
 
<source lang="pascal">
 
<source lang="pascal">
function BrandOfCPU: AnsiString;
+
function GetCPUarchitecture: AnsiString;
 
var
 
var
 
   status : Integer;
 
   status : Integer;
Line 276: Line 242:
 
   p : PChar;
 
   p : PChar;
 
begin
 
begin
   status := fpSysCtlByName('machdep.cpu.brand_string', Nil, @len, Nil, 0);
+
   status := fpSysCtlByName('hw.machine_arch', Nil, @len, Nil, 0);
 
   if status <> 0 then RaiseLastOSError;
 
   if status <> 0 then RaiseLastOSError;
  
Line 282: Line 248:
  
 
   try
 
   try
     status := fpSysCtlByName('machdep.cpu.brand_string', p, @len, Nil, 0);
+
     status := fpSysCtlByName('hw.machine_arch', p, @len, Nil, 0);
 
     if status <> 0 then RaiseLastOSError;
 
     if status <> 0 then RaiseLastOSError;
 
     Result := p;
 
     Result := p;
Line 291: Line 257:
 
</source>
 
</source>
  
On my computer, this returns the very informative string: Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz. Oh, you noticed, I changed the top level identifier from hw to machdep (machine dependent). Again, consult sysctl.h or use the sysctl command line utility in a Terminal.
+
On my FreeBSD computer, this returns the string: amd64.
  
 
== Last words ==
 
== Last words ==
Line 297: Line 263:
 
For the optimised, performance fiends out there, the man page for these functions notes that the FPsysctl() function runs in about a    third the time as the same request made via the FPsysctlbyname() function.
 
For the optimised, performance fiends out there, the man page for these functions notes that the FPsysctl() function runs in about a    third the time as the same request made via the FPsysctlbyname() function.
  
It should be noted that many of the sysctl attributes are read-only, but there are also quite a few that can be changed. I've left changing a sysctl attribute as an exercise for you dear reader. It should be simple if you study the definitions of the function parameters, paying particular attention to newp and newlen.
+
It should be noted that many of the sysctl attributes are read-only, but there are also quite a few that can be changed. I've left changing a sysctl attribute as an exercise for you dear reader. It should be simple if you study the definitions of the function parameters, paying particular attention to newp and newlen and the manual page for sysctl (man 3 sysctl).
  
  

Revision as of 10:22, 21 July 2019

Sysctl provides an interface that allows you to read and, with appropriate privileges, set many kernel attributes in FreeBSD (the BSDs, macOS and Linux). 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/freebsd/sysctlh.inc' source file is a partial translation of the FreeBSD '/usr/include/sys/sysctl.h' file. For details of the sysctls available, start a console or X11 xterm and type 'man sysctl'. Typing 'sysctl -a | more' 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                     Value    Next level names    Description
                                            are found in
          -------------------------------------------------------------------------------
          #define CTL_KERN         1        sys/sysctl.h        /* High kernel limits */
          #define CTL_VM           2        vm/vm_param.h       /* Virtual memory */
          #define CTL_VFS          3        sys/mount.h         /* File system */
          #define CTL_NET          4        sys/socket.h        /* Networking */
          #define CTL_DEBUG        5        sys/sysctl.h        /* Debugging */
          #define CTL_HW           6        sys/sysctl.h        /* Generic CPU, I/O */
          #define CTL_MACHDEP      7        sys/sysctl.h        /* Machine dependent */
          #define CTL_USER         8        sys/sysctl.h        /* User-level */
          #define CTL_P1003_1B     9        sys/sysctl.h        /* POSIX 1003.1B */
          -------------------------------------------------------------------------------

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: byte order (4321 or 1234) */
          #define HW_PHYSMEM       5              /* int: total memory in bytes */
          #define HW_USERMEM       6              /* int: memory in bytes not wired */
          #define HW_PAGESIZE      7              /* int: software page size */
          #define HW_DISKNAMES     8              /* strings: disk drive names */
          #define HW_DISKSTATS     9              /* struct: diskstats[] */
          #define HW_FLOATINGPT   10              /* int: has HW floating point? */
          #define HW_MACHINE_ARCH 11              /* string: machine architecture */
          #define HW_REALMEM      12              /* int: 'real' memory */
          -------------------------------------------------------------------------------

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, a multi-core CPU. One guess. Yes, it will return the total number of cores and not the number of CPUs. This probably does not matter depending on your use case, but you should be aware of it.

Before we move on to look at a second function 'FPsysctlbyname', let's look at retrieving a string with FPsysctl() which is a little different than 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 FreeBSD computer this returns the string "Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz".

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 GetClockInfo: String;
type
 clockinfo = record
    hz      : Integer;  // clock frequency
    tick    : Integer;  // ms per Hz tick
    spare   : Integer;  // unused
    stathz  : Integer;  // statistics clock frequency
    profhz  : Integer;  // profiling clock frequency
  end;

var
 mib : array[0..1] of Integer;
 status : Integer;
 len : size_t;
 clock : clockinfo;
begin
 mib[0] := CTL_KERN;
 mib[1] := KERN_CLOCKRATE;

 FillChar(clock, sizeof(clock), 0);
 len := sizeof(clock);

 status := fpSysCtl(@mib, Length(mib), @clock, @len, Nil, 0);
 if status <> 0 then RaiseLastOSError;

 Result := 'Clock freq: ' + IntToStr(clock.hz) + 'Hz' + LineEnding
           + 'Ms per Hz tick: ' + IntToStr(clock.tick) + LineEnding
           + 'Profiling clock freq: ' + IntToStr(clock.profhz) + ' Hz'
           + LineEnding;
end;

On my computer this returned:

Clock freq: 100Hz
Ms per Hz tick: 10000
Profiling clock freq: 8128 Hz

The clockinfo record was pretty easily translated from the C structure found in the system time.h file:

struct clockinfo {
        int     hz;             /* clock frequency */
        int     tick;           /* micro-seconds per hz tick */
        int     spare;
        int     stathz;         /* statistics clock frequency */
        int     profhz;         /* profiling clock frequency */
};

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';

There. Not realy different from the FPsysctl() function. Is that a sigh of relief? So let's look at finally retrieving the number of CPU cores.

function NumberOfCPU: Integer;

var
  status : Integer;
  len : size_t;

begin
  len := SizeOf(Result);

  status := fpSysCtlByName('hw.ncpu', @Result, @len, Nil, 0);
  if status <> 0 then RaiseLastOSError;
end;

On my FreeBSD computer this returns the number of cores: 6. Where do you find the special incantation 'hw.ncpu' ? In the manual pages for the sysctl command (man 3 sysctl [Library functions section] and man 8 sysctl [System manager's section]). Here's a brief excerpt of the hardware selectors:

    Parameters that are byte counts are 64 bit numbers. All other byte parameters are 32 bit numbers.
    --------------------------------------------------------------------------------------------
    Descriptor                  Type         Changeable    Description
    --------------------------------------------------------------------------------------------
    hw.machine                  string        no           Machine class
    hw.model                    string        no           Machine model
    hw.ncpu                     integer       no           Number of cpus
    hw.byteorder                integer       no           Byteorder (4321 or 1234)
    hw.physmem                  integer       no           Bytes of physical memory
    hw.usermem                  integer       no           Bytes of non-kernel memory
    hw.pagesize                 integer       no           Software page size 
    hw.floatingpoint            integer       no           Non-zero if hardware floating point
    hw.machine_arch             string        no           Machine dependent architecture
    hw.realmem                  integer       no           Bytes of real memory
    --------------------------------------------------------------------------------------------

If you prefer, you can also use the sysctl command line utility in a console or X11 xterm to list all the hw selectors with "sysctl hw.".

Retrieving a string value, rather than an integer, is again reminiscent of FPsysctl(). Two calls to FPsysctlbyname() are needed: (1) to find out the size of the buffer to hold the string to be returned and (2) to return the string value into that buffer. Don't forget to allocate memory for the buffer before the second call!

function GetCPUarchitecture: AnsiString;
var
  status : Integer;
  len : size_t;
  p : PChar;
begin
  status := fpSysCtlByName('hw.machine_arch', Nil, @len, Nil, 0);
  if status <> 0 then RaiseLastOSError;

  GetMem(p, len);

  try
    status := fpSysCtlByName('hw.machine_arch', p, @len, Nil, 0);
    if status <> 0 then RaiseLastOSError;
    Result := p;
  finally
    FreeMem(p);
  end;
end;

On my FreeBSD computer, this returns the string: amd64.

Last words

For the optimised, performance fiends out there, the man page for these functions notes that the FPsysctl() function runs in about a third the time as the same request made via the FPsysctlbyname() function.

It should be noted that many of the sysctl attributes are read-only, but there are also quite a few that can be changed. I've left changing a sysctl attribute as an exercise for you dear reader. It should be simple if you study the definitions of the function parameters, paying particular attention to newp and newlen and the manual page for sysctl (man 3 sysctl).