Coverage report: /home/ellis/comp/core/std/sys.lisp
Kind | Covered | All | % |
expression | 12 | 476 | 2.5 |
branch | 1 | 36 | 2.8 |
Key
Not instrumented
Conditionalized out
Executed
Not executed
Both branches taken
One branch taken
Neither branch taken
1
;;; std/sys.lisp --- Lisp System Utilities
9
;; (reexport-from :sb-introspect
10
;; :include '(:function-lambda-list :lambda-list-keywords :lambda-parameters-limit
11
;; :method-combination-lambda-list :deftype-lambda-list
12
;; :primitive-object-size :allocation-information
14
;; :who-specializes-directly :who-specializes-generally
15
;; :find-function-callees :find-function-callers))
18
;; sb-sys:*linkage-info* *machine-version* *runtime-dlhandle* *periodic-polling-function*
19
;; *periodic-polling-period* io-timeout nlx-protect serve-event os-deinit os-exit with-deadline dlopen-or-lose deallocate-system-memory
21
(defvar *interactive* t
22
"When non-nil (the default) specifies that this is an interactive REPL session
23
and we may query the user for input.")
25
(define-symbol-macro .i sb-ext:*inspected*)
28
"Return the standard list of system hooks."
30
:init sb-ext:*init-hooks*
31
:after-gc sb-ext:*after-gc-hooks*
32
:save sb-ext:*save-hooks*
33
:exit sb-ext:*exit-hooks*))
35
(defparameter *default-arena-size* (* 10 1024 1024 1024)
36
"The default size of freshly allocated arenas.")
38
(defparameter *default-heap-size* (ash 1 16)
39
"The default system heap size.")
41
(defun current-lisp-implementation ()
42
"Return the current lisp implemenation as a list: (TYPE VERSION FEATURES)"
44
(lisp-implementation-type)
45
(lisp-implementation-version)
48
(defun current-machine ()
49
"Return the current machine spec as a list: (HOST TYPE VERSION)"
55
(defun list-package-symbols (&optional (pkg *package*))
56
"List all external symbols of PKG."
57
(loop for s being the external-symbol of pkg
60
(defun list-all-symbols ()
61
"List all symbols found in this iamge."
63
(dolist (p (list-all-packages) r)
64
(appendf r (list-package-symbols p)))))
66
(defun package-symbols (&optional (package *package*) test)
67
"List the symbols of PACKAGE which satisfy TEST if present."
69
(do-external-symbols (symbol package)
71
(when (funcall test symbol)
72
(push symbol symbols))
73
(push symbol symbols)))
76
(defun package-symbol-names (&optional (package *package*) test)
77
"List the symbol names of PACKAGE which satisfy test if present."
78
(sort (mapcar (lambda (x) (string-downcase (symbol-name x)))
79
(package-symbols package test))
82
(defmacro do-internal-symbols ((var package) &body forms)
83
"Bind VAR to each internal symbol of PACKAGE in turn and evaluating FORMS for each."
84
(std/sym:with-gensyms (state)
85
`(do-symbols (,var ,package)
86
(multiple-value-bind (,var ,state)
87
(find-symbol (symbol-name ,var) ,package)
88
(when (eq ,state :internal)
91
(defun standard-symbol-names (&optional test)
92
"List the ANSI standard list of symbols which satisfy TEST if present."
93
(package-symbol-names :common-lisp test))
95
(defun handle-serious-condition (condition)
96
"Handle a fatal CONDITION. Depending on whether *INTERACTIVE* is set, enter
100
(invoke-debugger condition))
103
(let ((out (make-synonym-stream '*error-output*)))
104
(format out "~&Fatal condition:~%~A~%" condition)
105
(sb-debug:print-backtrace :stream out))
107
(format t "~A" condition)
108
(sb-ext:quit :unix-status 99))))))
111
(defvar *core-image-revived-p* nil
112
"Set to T when the current image has been revived.")
113
(defvar *core-image-revive-hooks* nil
114
"List of hooks to be evaluated when an image is revived.")
115
(defvar *core-image-entry-point* nil
116
"Entrypoint associated with this core image."))
118
(defun revive-image (&key (interactive *interactive*)
119
(hooks *core-image-revive-hooks*)
120
(entry-point *core-image-entry-point*)
121
(if-already-revived '(cerror "Revive image anyway")))
122
"Like UIOP:RESTORE-IMAGE but without a prelude."
123
(when *core-image-revived-p*
124
(if if-already-revived
125
(funcall if-already-revived "Image already ~:[being ~;~]revived"
126
(eq *core-image-revived-p* t))
127
(return-from revive-image)))
128
(handler-bind ((serious-condition #'handle-serious-condition))
129
(setf *interactive* interactive)
130
(setf *core-image-revive-hook* hooks)
131
(setf *core-image-revived-p* :in-progress)
132
(dolist (f *core-image-revive-hooks*)
134
(setf *core-image-revived-p* t)
135
(let ((results (multiple-value-list
137
(funcall entry-point)
140
(values-list results)
141
(sb-ext:exit :code (if (first results) 0 1))))))
143
;; HACK 2025-06-10: this attempts to modify read-only memory - can we arrange
144
;; for the read-only mem to be replaced on save?
146
;; Remove all symbols from all packages, storing them in weak pointers,
147
;; then collect garbage, and re-intern all symbols that survived GC.
148
;; Any symbol satisfying PREDICATE will be strongly referenced during GC
149
;; so that it doesn't disappear, regardless of whether it appeared unused.
150
(in-package :sb-impl)
151
(defun shake-packages (predicate &key print verbose query)
153
(declare (function predicate))
155
(flet ((weaken (table accessibility)
156
(let ((cells (symtbl-cells table))
160
(if (funcall predicate x accessibility)
161
(push x result) ; keep a strong reference to this symbol
162
(push (cons (string x) (make-weak-pointer x)) result))))
164
(resize-symbol-table table 0 'intern)
166
(dolist (package (list-all-packages))
167
;; Never discard standard symbols
168
(unless (eq package sb-int:*cl-package*)
169
(push (list* (weaken (package-internal-symbols package) :internal)
170
(weaken (package-external-symbols package) :external)
175
(sb-ext:search-roots query :criterion :static))
177
(flet ((reintern (symbols table package access)
178
(declare (ignore package))
179
(dolist (item symbols)
181
(add-symbol table item 'intern)
182
(let ((symbol (weak-pointer-value (cdr item))))
184
(add-symbol table symbol 'intern))
187
(format t " (~a)~A~%" access (car item)))
188
(incf n-dropped))))))))
189
(loop for (internals externals . package) in list
191
(format t "~&Package ~A~%" package))
192
(reintern internals (package-internal-symbols package)
194
(reintern externals (package-external-symbols package)
197
(format t "~&Dropped ~D symbols~%" n-dropped))
200
(in-package :std/sys)
203
(defun save-lisp-tree-shake-and-die (path &rest args)
204
"A naive tree-shaker for lisp."
205
;; https://gist.github.com/burtonsamograd/f08f561264ff94391300
207
do (sb-ext:gc :full t))
208
(apply #'sb-ext:save-lisp-and-die path args))
210
(defparameter *gc-logfile* #P"gc.log")
212
(defun enable-gc-logfile (&optional (file *gc-logfile*))
213
"Enable the system *GC-LOGFILE*."
214
(setf (sb-ext:gc-logfile) file))
216
(defun forget-shared-object (name)
217
"Forget the shared object specified by NAME."
218
(setf (sb-alien::shared-object-dont-save
219
(find name sb-sys:*shared-objects*
220
:key 'sb-alien::shared-object-namestring
221
:test 'string-equal))
224
(defun forget-shared-objects (&optional (objects sb-sys:*shared-objects*))
225
"Set the DONT-SAVE slot of all objects in SB-SYS:*SHARED-OBJECTS* to T."
226
(mapcar (lambda (obj) (setf (sb-alien::shared-object-dont-save obj) t)) objects))
228
(defun save-shared-objects (objects)
229
"Set the DONT-SAVE slot of OBJECTS to T."
230
(mapcar (lambda (obj) (setf (sb-alien::shared-object-dont-save obj) nil)) objects))
232
(defun compile-lisp (name &key force save make package compression verbose version callable-exports executable (toplevel #'sb-impl::toplevel-init) forget save-runtime-options root-structures (purify t))
233
"Process NAME and keyword arguments then pass options to the underlying build
234
system - eventually terminating on SAVE-LISP-AND-DIE."
235
(pkg:with-package (or package *package*)
236
(asdf:compile-system name :force force :verbose verbose :version version)
238
(apply 'asdf:make name (unless (eq t make) make)))
240
(forget-shared-objects (unless (eq t forget) forget)))
242
(when (probe-file save)
244
(sb-ext:save-lisp-and-die save :executable executable
246
:callable-exports callable-exports
247
:save-runtime-options save-runtime-options
248
:root-structures root-structures
250
:compression compression))))
252
(defmacro without-fp-traps (() &body body)
253
"Eval BODY with float traps disabled - sometimes necessary when working with
255
`(sb-int:with-float-traps-masked (:invalid :divide-by-zero)
259
;; https://www.intel.com/content/dam/develop/public/us/en/documents/10tb-24-breakthrough-aes-performance-with-intel-aes-new-instructions-final-secure.pdf
261
;; ncycles=(tscend-tscstart/i)
263
;; based on sb-simd-internals and https://kurohuku.blogspot.com/2009/11/sbclcpuid.html
265
;; also see https://github.com/jdmccalpin/low-overhead-timers/blob/master/low_overhead_timers.c
267
(defun cpuid (eax &optional (ecx 0))
268
"Call the CPUID instruction with supplied 32-bit values for EAX and ECX
269
regs. Returns 4 values containing the regs RAX RBX RCX and RDX respectively."
270
(declare ((unsigned-byte 32) eax ecx))
271
(sb-vm::%cpu-identification eax ecx))
273
(defun word-byte-list (n)
274
"Decompose a 32-bit integer N into 4 octets."
275
(declare ((unsigned-byte 32) n))
280
(ldb (byte 8 24) n)))
282
(macrolet ((%with-cpuid (n &body body)
283
`(multiple-value-bind (a b c d) (cpuid ,n)
286
"Return the vendor of the host CPU."
292
(mapcar #'code-char (word-byte-list n)))
295
;; this is the same as MACHINE-VERSION
297
"Return the brand of the host CPU."
298
(with-output-to-string (s)
299
(dolist (n '#.(mapcar #'(lambda (x)
300
(coerce x '(unsigned-byte 32)))
301
(list #x80000002 #x80000003 #x80000004)))
302
(declare ((unsigned-byte 32) n))
304
(dolist (word (list a b c d))
305
(dolist (code (word-byte-list word))
307
(write-char (code-char code) s)))))))))
310
(declaim (ftype (function () boolean) transaction-supported-p lock-elision-supported-p))
312
(defun lock-elision-supported-p ()
313
"Test for HLE, i.e. hardware lock elision.
314
HLE is supported if (cpuid 7) returns ebx with bit 4 set. If a processor does
315
not support HLE, it will ignore the new assembler instruction prefixes
316
XACQUIRE and XRELEASE.
318
As of June 2013, the only x86-64 CPUs supporting HLE are: * Intel Core i5 4570
319
* Intel Core i5 4670 * Intel Core i7 4770 Beware: at the time of writing all
320
the known K models, as for example Intel Core i7 4770K, do **NOT** support
323
(let ((max-cpuid (cpuid 0)))
324
(when (>= max-cpuid 7)
325
(let ((ebx (nth-value 1 (cpuid 7))))
326
(not (zerop (logand ebx #x10)))))))
328
(defun transaction-supported-p ()
329
"Test for RTM, i.e. hardware memory transactions.
330
RTM is supported if (cpuid 7) returns ebx with bit 11 set. If a processor
331
does not support HLE, trying to execute the new assembler instructions XBEGIN,
332
XEND, XABORT and XTEST will generate faults.
334
As of June 2013, the only x86-64 CPUs supporting RTM are:
339
Beware: at the time of writing all the known K models, as for example Intel
340
Core i7 4770K, do **NOT** support RTM."
341
(let ((max-cpuid (cpuid 0)))
342
(when (>= max-cpuid 7)
343
(let ((ebx (nth-value 1 (cpuid 7))))
344
(not (zerop (logand ebx #x800)))))))
346
(defparameter %little-endian nil
347
"An internal flag which indicates the host is little-endian, in the event that
348
we can't determine endianness at compile-time.")
350
(defun little-endian-p ()
351
"Return T if the current platform is little-endian else NIL."
352
#+(or :x86 :x86-64 :little-endian) t
353
#+(or :PPC :POWERPC :big-endian) nil
354
#-(or :x86 :x86-64 :little-endian :ppc :powerpc :big-endian)
358
"Return T on a 64-bit platform else NIL."
361
"Return T on a 64-bit platform else NIL."
364
(defun register-project-directory (path &optional (asdf t))
365
"Add PATH to QL:*LOCAL-PROJECT-DIRECTORIES* and ASDF:*CENTRAL-REGISTRY* (as
366
long as ASDF is non-nil)."
367
#+quicklisp (pushnew path ql:*local-project-directories*)
368
(when asdf (pushnew path asdf:*central-registry*)))
371
(definline get-real-time-seconds ()
372
"Call GET-INTERNAL-REAL-TIME and convert the result to seconds."
373
(/ (get-internal-real-time) internal-time-units-per-second))
375
(defun %time-remaining (start timeout)
376
"Check the current time to see if TIMEOUT seconds have elapsed since START."
377
(- timeout (- (get-real-time-seconds) start)))
379
(defmacro! with-countdown (o!time &body body)
380
"Eval BODY with an implicit timeout TIME."
381
(with-gensyms (start)
382
`(let ((,start (get-real-time-seconds)))
383
(flet ((time-remaining () (std/sys::%time-remaining ,start ,g!time)))
384
(declare (inline time-remaining))
387
;;; Logical Pathnames
388
(defun logical-host-names ()
389
"Print a list of currently available logical hosts."
390
(map 'list (lambda (x) (slot-value x 'sb-impl::name)) *logical-hosts*))
392
(defmacro define-logical-pathname (host path &rest translations)
393
"Define a new LOGICAL-PATHNAME associated with HOST and defaulting to
394
PATH. TRANSLATIONS is a list of (MATCH TRANSLATION) pairs."
397
(append `((,(format nil "~A" host) ,path)) translations)))
398
`(setf (logical-pathname-translations ,host)
399
;; eval second element only
400
',(mapcar (lambda (x)
401
(setf (cadr x) (eval (cadr x)))
405
(define-logical-pathname "STASH" "/opt/stash/"
406
("**;*.*.*" "/opt/stash/**/*.*"))
407
(define-logical-pathname "STORE" "/opt/store/"
408
("**;*.*.*" "/opt/store/**/*.*"))
409
(define-logical-pathname "SCRATCH" "/opt/scratch/"
410
("**;*.*.*" "/opt/scratch/**/*.*"))
411
;; redefine the sys table
412
(define-logical-pathname "SYS" "/usr/local/lib/sbcl/"
413
("SRC;**;*.*.*" #P"/usr/local/src/sbcl/src/**/*.*")
415
#P"/usr/local/src/sbcl/contrib/**/*.*")
417
(translate-logical-pathname "STASH:OUTPUT;sbcl;**;*.*.*"))
418
("TMP;**;*.*.*" "/tmp/**/*.*"))
421
;; https://stackoverflow.com/questions/69974963/object-memory-layout-in-common-lisp#70019565
422
(defun hexdump-object (obj)
423
"Try to hexdump an object, including immediate objects. All the
424
work is done by sb-vm:hexdump in the interesting cases."
426
(error "not a 64-bit SBCL")
427
(let* ((address/thing (sb-kernel:get-lisp-obj-address obj))
428
(tags (ldb (byte 4 0) address/thing)))
429
(format t "~&lowtags: ~12T~4,'0b~%" tags)
431
((zerop (ldb (byte 1 0) tags))
432
(format t "~&fixnum:~12T~16,'0x = ~S~%" address/thing obj))
433
((= (ldb (byte 2 0) tags) #b01)
434
(format t "~&immediate:~12T~16,'0x = ~S~%" address/thing obj))
435
((= (ldb (byte 2 0) tags) #b11) ;must be true
436
(format t "~&~A:~12T~16,'0x : ~16,'0x~%"
437
(case (ldb (byte 2 2) tags)
442
address/thing (dpb #b0000 (byte 4 0) address/thing))
443
;; this tells you at least something (and really annoyingly
444
;; does not pad addresses on the left)
447
(t (error "mutant"))))