Sign in Join
Wiki copytemporary

Accessing FreeBSD System Information: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11[Sysctl](https://www.freebsd.org/cgi/man.cgi?query=sysctl&sektion=3) 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).
22
33The '/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 available sysctls and their values.
44
55## FPsysctl()
66
77The 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:
88
99``` pascal
1010function FPsysctl (Name: pchar; namelen:cuint; oldp:pointer;oldlenp:psize_t; newp:pointer;newlen:size_t):cint; cdecl; external name 'sysctl';
1111```
1212
1313*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.
1414
1515*namelen* -- the length of the array of integers in name.
1616
1717Note: if the old attribute value is not required, the next two variables can be set to Nil and 0.
1818
1919*oldp* -- a pointer to the buffer into which the value is copied.
2020
2121*oldlenp* -- the size of the oldp buffer.
2222
2323Note: if the new attribute value is not being set, the next two variables should be set to Nil and 0.
2424
2525*newp* -- a pointer to the buffer containing the new value to be set.
2626
2727*newlen* -- the size of the newp buffer.
2828
2929The 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:
3030
31-`   -------------------------------------------------------------------------------`\
32-`   Name                     Value    Next level names    Description`\
33-`                                     are found in`\
34-`   -------------------------------------------------------------------------------`\
35-`   #define CTL_KERN         1        sys/sysctl.h        /* High kernel limits */`\
36-`   #define CTL_VM           2        vm/vm_param.h       /* Virtual memory */`\
37-`   #define CTL_VFS          3        sys/mount.h         /* File system */`\
38-`   #define CTL_NET          4        sys/socket.h        /* Networking */`\
39-`   #define CTL_DEBUG        5        sys/sysctl.h        /* Debugging */`\
40-`   #define CTL_HW           6        sys/sysctl.h        /* Generic CPU, I/O */`\
41-`   #define CTL_MACHDEP      7        sys/sysctl.h        /* Machine dependent */`\
42-`   #define CTL_USER         8        sys/sysctl.h        /* User-level */`\
43-`   #define CTL_P1003_1B     9        sys/sysctl.h        /* POSIX 1003.1B */`\
44-`   -------------------------------------------------------------------------------`
31+```
32+ -------------------------------------------------------------------------------
33+ Name Value Next level names Description
34+ are found in
35+ -------------------------------------------------------------------------------
36+ #define CTL_KERN 1 sys/sysctl.h /* High kernel limits */
37+ #define CTL_VM 2 vm/vm_param.h /* Virtual memory */
38+ #define CTL_VFS 3 sys/mount.h /* File system */
39+ #define CTL_NET 4 sys/socket.h /* Networking */
40+ #define CTL_DEBUG 5 sys/sysctl.h /* Debugging */
41+ #define CTL_HW 6 sys/sysctl.h /* Generic CPU, I/O */
42+ #define CTL_MACHDEP 7 sys/sysctl.h /* Machine dependent */
43+ #define CTL_USER 8 sys/sysctl.h /* User-level */
44+ #define CTL_P1003_1B 9 sys/sysctl.h /* POSIX 1003.1B */
45+ -------------------------------------------------------------------------------
46+```
4547
4648Let's look at CTL_HW level of information. The subsequent elements are found in the system sysctl.h file and are:
4749
48-`   -------------------------------------------------------------------------------          `\
49-`   Name                     Value          Description`\
50-`   -------------------------------------------------------------------------------          `\
51-`   #define HW_MACHINE       1              /* string: machine class */`\
52-`   #define HW_MODEL         2              /* string: specific machine model */`\
53-`   #define HW_NCPU          3              /* int: number of cpus */`\
54-`   #define HW_BYTEORDER     4              /* int: byte order (4321 or 1234) */`\
55-`   #define HW_PHYSMEM       5              /* int: total memory in bytes */`\
56-`   #define HW_USERMEM       6              /* int: memory in bytes not wired */`\
57-`   #define HW_PAGESIZE      7              /* int: software page size */`\
58-`   #define HW_DISKNAMES     8              /* strings: disk drive names */`\
59-`   #define HW_DISKSTATS     9              /* struct: diskstats[] */`\
60-`   #define HW_FLOATINGPT   10              /* int: has HW floating point? */`\
61-`   #define HW_MACHINE_ARCH 11              /* string: machine architecture */`\
62-`   #define HW_REALMEM      12              /* int: 'real' memory */`\
63-`   -------------------------------------------------------------------------------`
50+```
51+ -------------------------------------------------------------------------------
52+ Name Value Description
53+ -------------------------------------------------------------------------------
54+ #define HW_MACHINE 1 /* string: machine class */
55+ #define HW_MODEL 2 /* string: specific machine model */
56+ #define HW_NCPU 3 /* int: number of cpus */
57+ #define HW_BYTEORDER 4 /* int: byte order (4321 or 1234) */
58+ #define HW_PHYSMEM 5 /* int: total memory in bytes */
59+ #define HW_USERMEM 6 /* int: memory in bytes not wired */
60+ #define HW_PAGESIZE 7 /* int: software page size */
61+ #define HW_DISKNAMES 8 /* strings: disk drive names */
62+ #define HW_DISKSTATS 9 /* struct: diskstats[] */
63+ #define HW_FLOATINGPT 10 /* int: has HW floating point? */
64+ #define HW_MACHINE_ARCH 11 /* string: machine architecture */
65+ #define HW_REALMEM 12 /* int: 'real' memory */
66+ -------------------------------------------------------------------------------
67+```
6468
6569### Example: Number of CPUs
6670
6771Armed with this information, we can now find out the number of CPUs:
6872
6973``` pascal
7074{$mode objfpc}
7175...
7276
7377Uses
7478 unix,
7579 sysctl, // fpSysCtl
7680 sysutils; // RaiseLastOSError
7781...
7882
7983function NumberOfCPU: Integer;
8084
8185var
8286 mib: array[0..1] of Integer;
8387 status : Integer;
8488 len : size_t;
8589
8690begin
8791 mib[0] := CTL_HW;
8892 mib[1] := HW_NCPU;
8993
9094 len := SizeOf(Result);
9195 status := fpSysCtl(PChar(@mib), Length(mib), @Result, @len, Nil, 0);
9296 if status <> 0 then RaiseLastOSError;
9397end;
9498```
9599
96100The 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).
97101
98102There 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, if your microprocessor supports hyperthreading, the number of cores plus the number of hardware threads -- not the number of discrete CPUs. This may not matter depending on your use case, but you should be aware of it.
99103
100104### Example: CPU model
101105
102106Before 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.
103107
104108``` pascal
105109{$mode objfpc}
106110...
107111
108112Uses
109113 unix,
110114 sysctl, // fpSysCtl
111115 sysutils; // RaiseLastOSError
112116...
113117
114118function HWmodel : AnsiString;
115119
116120var
117121 mib : array[0..1] of Integer;
118122 status : Integer;
119123 len : size_t;
120124 p : PChar;
121125
122126begin
123127 mib[0] := CTL_HW;
124128 mib[1] := HW_MODEL;
125129
126130 status := fpSysCtl(PChar(@mib), Length(mib), Nil, @len, Nil, 0);
127131 if status <> 0 then RaiseLastOSError;
128132
129133 GetMem(p, len);
130134
131135 try
132136 status := fpSysCtl(PChar(@mib), Length(mib), p, @len, Nil, 0);
133137 if status <> 0 then RaiseLastOSError;
134138 Result := p;
135139 finally
136140 FreeMem(p);
137141 end;
138142end;
139143```
140144
141145Notice that this time we needed two calls to FPsysctl() - one to find out the size of the buffer required to hold the string value to be returned, and the second to actually retrieve the string value into that buffer. On my FreeBSD Apple Mac mini computer this returns the string "Intel(R) Core(TM) i7-8700B CPU @ 3.20GHz".
142146
143147### Example: Clock info
144148
145149No, we're not quite ready to move on yet... we still need to see how to retrieve information in a C structure (Pascal record).
146150
147151``` pascal
148152{$mode objfpc}
149153...
150154
151155Uses
152156 unix,
153157 sysctl, // fpSysCtl
154158 sysutils; // RaiseLastOSError
155159...
156160
157161function GetClockInfo: String;
158162
159163type
160164 clockinfo = record
161165 hz : Integer; // clock frequency
162166 tick : Integer; // ms per Hz tick
163167 spare : Integer; // unused
164168 stathz : Integer; // statistics clock frequency
165169 profhz : Integer; // profiling clock frequency
166170 end;
167171
168172var
169173 mib : array[0..1] of Integer;
170174 status : Integer;
171175 len : size_t;
172176 clock : clockinfo;
173177begin
174178 mib[0] := CTL_KERN;
175179 mib[1] := KERN_CLOCKRATE;
176180 clock := Default(clockinfo);
177181
178182 FillChar(clock, sizeof(clock), 0);
179183 len := sizeof(clock);
180184
181185 status := fpSysCtl(@mib, Length(mib), @clock, @len, Nil, 0);
182186 if status <> 0 then RaiseLastOSError;
183187
184188 Result := 'Clock freq: ' + IntToStr(clock.hz) + 'Hz' + LineEnding
185189 + 'Ms per Hz tick: ' + IntToStr(clock.tick) + LineEnding
186190 + 'Profiling clock freq: ' + IntToStr(clock.profhz) + ' Hz'
187191 + LineEnding;
188192end;
189193```
190194
191195On my computer this returned:
192196
193-`Clock freq: 100Hz`\
194-`Ms per Hz tick: 10000`\
195-`Profiling clock freq: 8128 Hz`
197+```
198+Clock freq: 100Hz
199+Ms per Hz tick: 10000
200+Profiling clock freq: 8128 Hz
201+```
196202
197203The clockinfo record was pretty easily translated into a Pascal record from the C structure found in the system time.h file:
198204
199205``` c
200206struct clockinfo {
201207 int hz; /* clock frequency */
202208 int tick; /* micro-seconds per hz tick */
203209 int spare;
204210 int stathz; /* statistics clock frequency */
205211 int profhz; /* profiling clock frequency */
206212};
207213```
208214
209215### Example: Real executable path
210216
211217First some background. Many Pascal programmers are in love with the `ExtractFilePath(ParamStr(0))` function to find the path of an executable. **Just don't do it!** Paramstr(0) must only be used on DOS and Windows (and maybe OS/2). On all other platforms the results are unpredictable and it will not work as you expect (in the best case it will never work; in the worst case it will only fail under certain circumstances, resulting in hard-to-find bugs). For the gory details, see [this thread's posts by Jonas](https://lists.freepascal.org/pipermail/fpc-pascal/2009-February/020211.html).
212218
213219Instead, sysctl to the rescue.
214220
215221``` pascal
216222{$mode objfpc}
217223
218224Uses
219225 unix,
220226 sysctl, // fpSysCtl
221227 sysutils; // RaiseLastOSError
222228
223229function realPath: string;
224230
225231var
226232 mib: array[0..3] of Integer;
227233 status : Integer;
228234 len : size_t;
229235
230236begin
231237 mib[0] := CTL_KERN;
232238 mib[1] := KERN_PROC;
233239 mib[2] := KERN_PROC_PATHNAME;
234240 mib[3] := -1;
235241
236242 len := SizeOf(Result);
237243 status := fpSysCtl(PChar(@mib), Length(mib), @Result, @len, Nil, 0);
238244 if status <> 0 then RaiseLastOSError;
239245end;
240246
241247begin
242248 WriteLn(realPath);
243249end.
244250```
245251
246252Finally, yes, it is time to move on to the FPsysctlbyname() function.
247253
248254## FPsysctlbyname
249255
250256The FPsysctlbyname function is defined in '/usr/local/share/fpcsrc/rtl/bsd/sysctl.pp' as:
251257
252258``` pascal
253259function FPsysctlbyname (Name: pchar; oldp:pointer;oldlenp:psize_t; newp:pointer;newlen:size_t):cint; cdecl; external name 'sysctlbyname';
254260```
255261
256262There. Not really very different from the FPsysctl() function. Is that a sigh of relief?
257263
258264### Example: Number of CPU cores
259265
260266So let's look at finally retrieving the number of CPU cores.
261267
262268``` pascal
263269{$mode objfpc}
264270...
265271
266272Uses
267273 unix,
268274 sysctl, // fpSysCtlByName
269275 sysutils; // RaiseLastOSError
270276...
271277
272278function NumberOfCPU: Integer;
273279
274280var
275281 status : Integer;
276282 len : size_t;
277283
278284begin
279285 len := SizeOf(Result);
280286
281287 status := fpSysCtlByName('hw.ncpu', @Result, @len, Nil, 0);
282288 if status <> 0 then RaiseLastOSError;
283289end;
284290```
285291
286292On 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:
287293
288-`    Parameters that are byte counts are 64 bit numbers. All other byte parameters are 32 bit numbers.`
294+```
295+ Parameters that are byte counts are 64 bit numbers. All other byte parameters are 32 bit numbers.
296+```
289297
290-`    --------------------------------------------------------------------------------------------`\
291-`    Descriptor                  Type         Changeable    Description`\
292-`    --------------------------------------------------------------------------------------------`\
293-`    hw.machine                  string        no           Machine class`\
294-`    hw.model                    string        no           Machine model`\
295-`    hw.ncpu                     integer       no           Number of cpus`\
296-`    hw.byteorder                integer       no           Byteorder (4321 or 1234)`\
297-`    hw.physmem                  integer       no           Bytes of physical memory`\
298-`    hw.usermem                  integer       no           Bytes of non-kernel memory`\
299-`    hw.pagesize                 integer       no           Software page size`\
300-`    hw.floatingpoint            integer       no           Non-zero if hardware floating point`\
301-`    hw.machine_arch             string        no           Machine dependent architecture`\
302-`    hw.realmem                  integer       no           Bytes of real memory`\
303-`    --------------------------------------------------------------------------------------------`
298+``` pascal
299+ --------------------------------------------------------------------------------------------
300+ Descriptor Type Changeable Description
301+ --------------------------------------------------------------------------------------------
302+ hw.machine string no Machine class
303+ hw.model string no Machine model
304+ hw.ncpu integer no Number of cpus
305+ hw.byteorder integer no Byteorder (4321 or 1234)
306+ hw.physmem integer no Bytes of physical memory
307+ hw.usermem integer no Bytes of non-kernel memory
308+ hw.pagesize integer no Software page size
309+ hw.floatingpoint integer no Non-zero if hardware floating point
310+ hw.machine_arch string no Machine dependent architecture
311+ hw.realmem integer no Bytes of real memory
312+ --------------------------------------------------------------------------------------------
313+```
304314
305315If 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.".
306316
307317### Example: CPU architecture
308318
309319Retrieving 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!
310320
311321``` pascal
312322{$mode objfpc}
313323...
314324
315325Uses
316326 unix,
317327 sysctl, // fpSysCtlByName
318328 sysutils; // RaiseLastOSError
319329...
320330
321331function GetCPUarchitecture: AnsiString;
322332
323333var
324334 status : Integer;
325335 len : size_t;
326336 p : PChar;
327337begin
328338 status := fpSysCtlByName('hw.machine_arch', Nil, @len, Nil, 0);
329339 if status <> 0 then RaiseLastOSError;
330340
331341 GetMem(p, len);
332342
333343 try
334344 status := fpSysCtlByName('hw.machine_arch', p, @len, Nil, 0);
335345 if status <> 0 then RaiseLastOSError;
336346 Result := p;
337347 finally
338348 FreeMem(p);
339349 end;
340350end;
341351```
342352
343353On my FreeBSD computer, this returns the string: amd64.
344354
345355### Example: CPU clock speed
346356
347357``` pascal
348358{$mode objfpc}
349359...
350360
351361Uses
352362 unix,
353363 sysctl, // fpSysCtlByName
354364 sysutils; // RaiseLastOSError
355365...
356366
357367function GetCPUClockRate: integer;
358368
359369var
360370 status : Integer;
361371 len : size_t;
362372
363373begin
364374 len := SizeOf(Result);
365375 status := fpSysCtlByName('hw.clockrate', @Result, @len, Nil, 0);
366376 if status <> 0 then RaiseLastOSError;
367377end;
368378```
369379
370380For an Intel(R) Core(TM) i7-2620M CPU @ 2.70GHz, this returns 2.693 (GHz).
371381
372382## Last words
373383
374384For 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.
375385
376386It 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).
377387
378388## External links
379389
380390- [sysctlbyname improved](https://gitlab.com/alfix/sysctlbyname-improved) - A new sysctl internal object to convert a sysctl name to the corresponding OID and to improve sysctlbyname(3).
381391
382392---
383393*From the Free Pascal wiki, page "Accessing FreeBSD System Information", as it stood on 2021-03-09 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history