;;; -*- Mode:LISP; Package:(SPY GLOBAL); Fonts:(MEDFNB); Base:10 -*- ;;; Program to see what is going on in some other process. ;;; 10/12/84 23:26:38 -George Carrette ;;; Provided Functions: ;;; (SPY:PICK-A-PROCESS) ;;; (SPY:STACK-SPY ) ;;; (SPY:QUANTUM &optional ) ;;; Copyright LISP Machine, Inc. 1984 ;;; See filename "Copyright" for ;;; licensing and release information. (defun pick-a-process () "Puts up a menu to choose from the active proceeses" (let ((alist)(value)) (dolist (p si:active-processes) (if (car p) (push (list (si:process-name (car p)) :value (car p)) alist))) (do () ((setq value (tv:menu-choose alist " pick a process")) value)))) (defun quantum (&optional process quantum scheduler-rate) "Reads or sets the quantum of the process P. Also sets the scheduler rate." (let ((p (get-process (or process current-process)))) (cond ((null quantum) (list (format nil "For ~A in 60'ths of a second:" (si:process-name p)) :quantum (si:process-quantum p) :clock-rate (read-meter 'si:%tv-clock-rate))) ('else (send p :set-quantum quantum) (when scheduler-rate (write-meter 'si:%tv-clock-rate (if (numberp scheduler-rate) scheduler-rate quantum))) (quantum p))))) (defun get-process (x) (cond ((typep x 'si:process) x) ('else (or (send x :Send-if-handles :process) (ferror Nil "can't get a process from ~S" x))))) (defun get-stack-group (x &aux temp) "Returns two values, a stack group and a process, if any" (cond ((typep x :stack-group) x) ((setq temp (send x :send-if-handles :process)) (values (send temp :stack-group) temp)) ((typep x 'si:process) (values (send x :stack-group) x)) (t (or (send x :send-if-handles :stack-group) (ferror nil "Can't get a stack group from ~S" x))))) (defvar *terse? t) (defun sg-not-in-schedulerp (sg) (not (eq (si:rp-function-word (si:sg-regular-pdl sg) (sys:SG-AP SG)) sys:scheduler-stack-group))) (defun wait-for-sg-not-in-scheduler (sg) (process-wait "spy" #'sg-not-in-schedulerp sg)) (defun wait-for-process-run (proc lt) (process-wait "*spy*" #'(lambda (p x) (not (eq (si:process-last-time-run p) x))) proc lt)) (defun stack-spy (x &optional &key (terse t) sb-hack) "Spy on stack group or process X. Prints a line of info everytime it is seen that the state of X changes. SB-Hack controls use of extra sequence breaks." (let ((SG)(proc)(*terse? terse)(old-sb) (stream (make-char-punting-stream standard-output))) (multiple-value (sg proc) (get-stack-group x)) (unwind-protect (progn (and sb-hack (setq old-sb (si:sb-on))) (si:sb-on (if (eq sb-hack t) '(:call :unibus :chaos :clock) (union '(:clock) sb-hack))) (cond ((null proc) (do () (nil) (wait-for-sg-not-in-scheduler sg) (stack-print-1 sg stream) (process-sleep 1. "spy sleep"))) ('else (do ((lt)) (nil) (setq lt (si:process-last-time-run proc)) (stack-print-1 sg stream) (wait-for-process-run proc lt))))) (and sb-hack (si:sb-on old-sb))))) (defvar *wholine* nil) (defvar *background-spy* nil) (defun start-background-spy () (or *wholine* (setq *wholine* (make-char-punting-stream tv:who-line-documentation-window))) (or *background-spy* (setq *background-spy* (make-process "background spy"))) (process-disable *background-spy*)) (defun stop-background-spy () (if *background-spy* (process-disable *background-spy*))) (defun background-spy-case-sg (sg) (do () (nil) (wait-for-sg-not-in-scheduler sg) (send *wholine* :clear-screen) (stack-print-1 sg *wholine*) (process-sleep 1. "spy sleep"))) (defun background-spy-case-proc (sg proc) (do ((lt)) (nil) (setq lt (si:process-last-time-run proc)) (when (or (not *terse?) (sg-not-in-schedulerp sg)) (send *wholine* :clear-screen) (stack-print-1 sg *wholine*)) (wait-for-process-run proc lt))) (defun background-spy (x) "Spy on stack group or process X. Prints a line of info everytime it is seen that the state of X changes." (start-background-spy) (multiple-value-bind (sg proc) (get-stack-group x) (cond ((null proc) (send *background-spy* :preset 'background-spy-case-sg sg)) ('else (send *background-spy* :preset 'background-spy-case-proc sg proc))) (process-enable *background-spy*))) (defvar *print-methods* :operation) (defun stack-print-1 (sg punting-stream &aux eh:EH-ERROR eh:CURRENT-FRAME eh:ERROR-LOCUS-FRAME eh:INNERMOST-VISIBLE-FRAME eh:INNERMOST-FRAME-IS-INTERESTING) "Print one line of information about the stack group to the punting-stream" ;; &aux variables are for simulating the environment inside ;; the error handler, which is usually the only thing to look at ;; stacks. Many useful fuction in EH can be called. (SETQ eh:INNERMOST-VISIBLE-FRAME (sys:SG-AP SG)) (setq eh:current-frame eh:innermost-visible-frame) (send punting-stream :fresh-line) (call-on-char-punting-stream 0 punting-stream #'(lambda (stream sg) (map-n-frames #'(lambda (number sg frame stream) number (let* ((f (si:rp-function-word (si:sg-regular-pdl sg) frame)) (name (function-name f))) (cond ((atom name) (princ name stream)) ('else (selectq *print-methods* (:operation (princ (caddr name) stream)) (t (princ name stream))))) (princ " " stream) ;; kludge (or (null name) (and *terse? (eq f sys:scheduler-stack-group))))) sg eh:current-frame () stream)) punting-stream sg)) (defvar *map-n-frames-active-only? t) (defun next-frame (stack-group frame) (if *map-n-frames-active-only? (eh:sg-next-active stack-group frame) (eh:sg-next-open stack-group frame))) (defun map-n-frames (f stack-group start-frame n &rest l) "Call F on n frames in the stack group on arguments: frame-number, stack-group, frame, &rest l. Return if F returns T." (do ((j 0 (1+ j)) (f-val) ;; using EH:SG-NEXT-OPEN gives an error on about the last frame. ;; but in use, the FRAME-to-STOP-ON might save us. (frame start-frame (next-frame stack-group frame))) ((or (null frame) (and n (> j n)))) (if (setq f-val (lexpr-funcall f j stack-group frame l)) (return f-val)))) (defvar *char-punting-limit*) (defvar *char-punting-count*) (defvar *char-punting-output*) (defun char-punting-stream-closure-function (operation &optional arg1 &Rest args) (selectq operation (:which-operations '(:tyo :reset :count :clear-screen)) ((:tyo) (cond ((> (setq *char-punting-count* (1+ *char-punting-count*)) *char-punting-limit*) (princ " etc ..." *char-punting-output*) (*throw 'char-punt *char-punting-limit*)) ('else (send *char-punting-output* operation arg1)))) ((:reset) (setq *char-punting-count* (or arg1 0))) ((:count) *char-punting-count*) ((:fresh-line) (setq *char-punting-count* 0) (send *char-punting-output* :fresh-line)) ((:clear-screen) (char-punting-stream-closure-function :reset) (send *char-punting-output* :clear-screen)) (t (stream-default-handler 'char-punting-stream-closure-function operation arg1 args)))) (defun make-char-punting-stream (*char-punting-output* &optional (*char-punting-limit* (- (send *char-punting-output* :size-in-characters) 10)) &aux (*char-punting-count* 0)) (closure '(*char-punting-output* *char-punting-limit* *char-punting-count*) 'char-punting-stream-closure-function)) (defun call-on-char-punting-stream (sofar stream f &rest l) (send stream :reset sofar) (*catch 'char-punt (lexpr-funcall f l)) (send stream :count)) (defun prin1-puntable (sofar object stream) (call-on-char-punting-stream sofar stream #'prin1 object stream)) (defun princ-puntable (sofar object stream) (call-on-char-punting-stream sofar stream #'princ object stream)) (defun terpri-puntable (stream) (call-on-char-punting-stream 0 stream #'terpri stream)) (defun format-puntable (stream string &rest l) (lexpr-funcall #'call-on-char-punting-stream 0 stream #'format stream string l))