;;; -*- Mode: Lisp; Package: SI; Readtable: ZL; Base: 10. -*- ;;; ;;; Region hacking stuff ;;; ;;; -dg 5/11/86 ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Region allocation recording utilities ;;; ;;; Region map record is 256 x 2 art-q entries. ;;; n = region number ;;; index[n,0] = area-number for the region ;;; index[n,1] = sorted list of quantum numbers ;;; index[0,0] = sorted list of free quantums ;;; index[0,1] = random ;;; array-leader[0] = free quantum list ;;; array-leader[1] = copy of address space map this all came from ;;; array-leader[2] = fixnum microsecond time when address space map copied ;;; array-leader[3] = universal time when address space map copied ;;; need to dump area info with records (defun make-region-map-record (&optional (record (make-array '(256 2) :type 'art-q :leader-length 4)) (addr-map-copy (make-array (length #'address-space-map) :type 'art-8b))) (let ((zero-region-kludge t)) (without-interrupts (copy-array-contents #'address-space-map addr-map-copy) (setf (array-leader record 0) nil (array-leader record 1) addr-map-copy (array-leader record 2) (%microsecond-time) (array-leader record 3) (time:get-universal-time))) (dotimes (c 2048) (let ((region (aref addr-map-copy c))) (cond (zero-region-kludge (push c (aref record region 1)) (unless (zerop region) (setq zero-region-kludge nil))) ((zerop region) (push c (array-leader record 0))) (t (push c (aref record region 1)))))) (setf (array-leader record 0) (sort (array-leader record 0) '<)) (dotimes (idx 256) (when (setf (aref record idx 1) (sort (aref record idx 1) '<)) (setf (aref record idx 0) (si:%region-area idx)))) record)) (defun print-region-record-by-region (record) (dotimes (c 256) (when (aref record c 1) (format t "~&~D~5T~A~35T:~A" c (si:area-name (si:%region-area c)) (aref record c 1))))) (defconst *cached-real-area-list* ()) (defun get-real-current-area-list () "This eliminated duplicate area numbers (synonyms)." (or *cached-real-area-list* (let ((collect)) (dolist (area (current-area-list) (setq *cached-real-area-list* (reverse collect))) (unless (mem #'(lambda (a b) (= (si:eval-special-ok a) (si:eval-special-ok b))) area collect) (push area collect)))))) (defun find-area-regions-in-record (area-number record &aux return-list) (dotimes (c 256 (reverse return-list)) (when (and (aref record c 1) (= (aref record c 0) area-number)) (push c return-list)))) (defun print-region-record-by-area (record &aux temp) (dolist (c (get-real-current-area-list)) (when (setq temp (find-area-regions-in-record (si:eval-special-ok c) record)) (format t "~&~A:" c) (dolist (region temp) (when (aref record region 1) (format t "~&~10TRegion ~D~20T: ~A~%" region (aref record region 1))))))) (defvar *region-map-record-list* ()) (defun snapshot-address-space-allocation () (push (make-region-map-record) *region-map-record-list*)) (defconst *region-map-record-file-default* "dj:dg.region-records;") (defun make-map-record-filename (&key (host si:local-host) (default *region-map-record-file-default*)) (let ((default (fs:parse-pathname default))) (send default :new-pathname :name (string-append (send (si:parse-host host) :short-name) "-region-map-record") :canonical-type :qfasl :version :newest))) (defun dump-region-map-records (&key (host si:local-host) (file (make-map-record-filename :host host))) (compiler:fasd-symbol-value file '*region-map-record-list*)) (defun restore-region-map-records (&key (host si:local-host) (file (make-map-record-filename :host host))) (load file)) (defun analyze-region-record-changes (&optional (list *region-map-record-list*)) (format t "~&The First record start time: ~\time\" (array-leader (car (last list)) 3)) (let* ((holes (find-holes-in-quantum-map (array-leader (car (last list)) 1))) (quantums (apply '+ (mapcar 'length holes)))) (format t "~&~5THoles in quantum map: ~D holes; ~d quantums; ~D words." (length holes) quantums (* quantums 64 256))) (do* ((l (reverse list) (cdr l)) (pos 2 (add1 pos)) (record-1 (car l) (car l)) (record-2 (cadr l) (cadr l))) ((null record-2) (format t "~&End of records.")) (format t "~&~%The ~:R record start time: ~\time\" pos (array-leader record-2 3)) (if (equal (listarray (array-leader record-1 1)) (listarray (array-leader record-2 1))) (format t "~&~5TNo changes.") (format t "~&~5TArea changes:~%") (dolist (area (get-real-current-area-list)) (let ((a1 (find-area-regions-in-record (si:eval-special-ok area) record-1)) (a2 (find-area-regions-in-record (si:eval-special-ok area) record-2))) (unless (equal a1 a2) (let ((removed (set-difference a1 a2)) (added (set-difference a2 a1))) (when added (format t "~&~10TArea (~A) had regions added: ~A" area added)) (when removed (format t "~&~10TArea (~A) has regions removed: ~A" area removed)))))) (format t "~&~5TRegion changes:~%") (dotimes (region 256) (let ((r1 (aref record-1 region 1)) (r2 (aref record-2 region 1))) (cond ((equal r1 r2)) ((null r2) (format t "~&~10TRegion ~D freed: ~A" region r1)) ((null r1) (format t "~&~10TRegion ~D allocated: ~A" region r2)) (t (format t "~&~10TRegion ~D modified:~%~ ~20TPrevious Quantums: ~A~%~20TCurrent Quantums: ~A" region r1 r2))))) (let* ((holes (find-holes-in-quantum-map (array-leader record-2 1))) (quantums (apply '+ (mapcar 'length holes)))) (format t "~&~5THoles in quantum map: ~D holes; ~d quantums; ~D words." (length holes) quantums (* quantums 64 256)))))) (defun nth-region-record (number &optional (list *region-map-record-list*)) (car (nthcdr (- (length list) (add1 number)) list))) (defun find-holes-in-quantum-map (quantum-map) (do ((count 31 (add1 count)) counting return-list) ((= count 2048) (reverse return-list)) (cond ((zerop (aref quantum-map count)) (push count counting)) (counting (push (reverse counting) return-list) (setq counting nil))))) (defun analyze-holes-in-region-records (&key show-holes (list *region-map-record-list*)) (do* ((l (reverse list) (cdr l)) (record (car l) (car l)) (count 1 (add1 count)) holes quantums) ((null record) (format t "~&End of data.~%")) (setq holes (find-holes-in-quantum-map (array-leader record 1)) quantums (apply '+ (mapcar 'length holes))) (format t "~&The ~:R record has ~D holes; ~D quantums; ~D words.~%" count (length holes) quantums (* quantums 64 256)) (when show-holes (dolist (hole holes) (format t "~&~10T~A~%" hole))))) (defun decode-volatility-constraint (constraint) (etypecase constraint ((member :any) 'last-gc))) ;;; this is more difficult to process-wait for... do it later. ; ((integer 0 3) `(and last-gc (= last-gc ,constraint))) ; (cons `((and last-gc ; (,(case (car constraint) ; (:higher '<) ; (:lower '>) ; (:equal '=)) ; last-gc ; ,(cadr constraint))))))) (defvar *constraint-function-args* '(start-time gc-generation snapshot-count)) (defun decode-activation-constraint (constraint) (check-type constraint (or cons null)) (if (null constraint) #'ignore (let ((*standard-output* 'si:null-stream)) (compile-lambda `(lambda ,*constraint-function-args* ,(decode-activation-constraint-internal constraint)))))) (defun decode-activation-constraint-internal (constraint) (case (car constraint) (:funcall `(funcall ,(cadr constraint) ,(cddr constraint))) (:eval (cadr constraint)) (:or `(or . ,(mapcar 'decode-activation-constraint-internal (cdr constraint)))) (:and `(and . ,(mapcar 'decode-activation-constraint-internal (cdr constraint)))) ; (:elapsed-time `(> (time:get-universal-time) (+ start-time ,(cadr constraint)))) (:after-gc `(//= gc-generation (symeval-globally 'gc:%gc-generation-number))) (:count `(= snapshot-count ,(cadr constraint))))) (defvar *address-space-watch-process-control* ()) (defvar *address-space-watch-snapshot-notify* ()) (defvar *address-space-watch-process* ()) (defun address-space-watch-process (duration interval &rest args) (unwind-protect (apply 'address-space-watch duration interval args) (setq *address-space-watch-process* nil))) (defun address-space-watch (duration interval &key (wipe-out-current-list t) (save-after-finish t) save-after-each-snapshot (save-filename (make-map-record-filename :host si:local-host))) (when wipe-out-current-list (setq *region-map-record-list* nil)) (let ((*time-start* (time:get-universal-time)) (*snapshot-count* 0) (*gc-generation-number* gc:%gc-generation-number) (finish-test (decode-activation-constraint duration)) (snapshot-test (decode-activation-constraint interval)) run-reason) (declare (special *time-start* *snapshot-count* *gc-generation-number*)) (tv:notify nil "Address space watch process started ~\time\" *time-start*) (process-wait "1st snapshot wait" #'(lambda () (null gc:*gc-status*))) (snapshot-address-space-allocation) (tv:notify nil "Address space watch: Made initial snapshot.") (do-forever (process-wait "Wait for event" (closure '(*snapshot-count* *time-start* *gc-generation-number*) #'(lambda () (setq run-reason (cond (*address-space-watch-process-control* :control) ((funcall finish-test *time-start* *gc-generation-number* *snapshot-count*) :finish) ((funcall snapshot-test *time-start* *gc-generation-number* *snapshot-count*) :snapshot)))))) (selectq-every run-reason (:control (selectq-every *address-space-watch-process-control* (nil) (:wait (process-wait "Control wait" (closure '(*address-space-watch-process-control*) #'(lambda () (null *address-space-watch-process-control*))))) (:arrest (send si:current-process :arrest)) ((:save :finish-and-save) (dump-region-map-records :file save-filename)) ((:finish :finish-and-save) (return-from address-space-watch nil))) (setq *address-space-watch-process-control* nil)) (:finish (when save-after-finish (dump-region-map-records :file save-filename)) (return-from address-space-watch nil)) (:snapshot (process-wait "Await flip done" #'(lambda () (null gc:*gc-status*))) (gc:without-flipping (when (si:eval-special-ok snapshot-test) ;in case of flip when gc-type constraint (gc:without-scavenging (snapshot-address-space-allocation) (incf *snapshot-count*) (when *address-space-watch-snapshot-notify* (tv:notify nil "Address space watch: Snapshot taken at ~\time\" (time:get-universal-time))) (when save-after-each-snapshot (dump-region-map-records :file save-filename))) (setq *gc-generation-number* gc:%gc-generation-number)))))))) (defun start-address-space-watch (duration interval &rest args) (setq *address-space-watch-process-control* nil) (if (null *address-space-watch-process*) (setq *address-space-watch-process* (apply 'process-run-function "Address space watch" 'address-space-watch-process duration interval args)) (tv:beep) (format t "Address space watch process already exists: ~S" *address-space-watch-process*))) (defun finish-address-space-watch (&optional (save t)) (when *address-space-watch-process* (setq *address-space-watch-process-control* (if save :finish-and-save :finish)) (process-wait "Wait for finish" #'(lambda () (null *address-space-watch-process*)))))