;;; -*- Mode:LISP; Package:SYSTEM-INTERNALS; Base:10; Readtable:ZL -*-

;;; Process scheduler meters
(defconst *psm-total-time* 0)
(defconst *psm-total-before-time* 0)
(defconst *psm-total-count* 0)
(defconst *psm-clock-list-total* 0)
(defconst *psm-clock-list-count* 0)
(defconst *psm-wholine-update-total* 0)
(defconst *psm-wholine-update-count* 0)
(defconst *psm-find-process-total* 0)
(defconst *psm-scheduler-idle-total* 0)
(defconst *psm-scheduler-idle-count* 0)

(defun reset-scheduler-meters ()
  (without-interrupts
    (setq *psm-total-time* 0
	  *psm-total-before-time* 0
	  *psm-total-count* 0
	  *psm-clock-list-total* 0
	  *psm-clock-list-count* 0
	  *psm-wholine-update-total* 0
	  *psm-wholine-update-count* 0
	  *psm-find-process-total* 0
	  *psm-scheduler-idle-total* 0
	  *psm-scheduler-idle-count* 0)))

(defun print-scheduler-meters ()
  (without-interrupts
    (format t "~&Meter:~30tTotal~45tCount~60TAverage~%~
             ----------------------------------------------------------------------")
    (format t "~&Clock~30T~D~45T~D~60T~D"
	    *psm-clock-list-total*
	    *psm-clock-list-count*
	    (if (zerop *psm-clock-list-count*)
		0
	      (// *psm-clock-list-total* *psm-clock-list-count* )))
    (format t "~&Wholine~30T~D~45T~D~60T~D"
	    *psm-wholine-update-total*
	    *psm-wholine-update-count*
	    (if (zerop *psm-wholine-update-count*)
		0
	      (// *psm-wholine-update-total* *psm-wholine-update-count* )))
    (format t "~&Idle~30T~D~45T~D~60T~D"
	    *psm-scheduler-idle-total*
	    *psm-scheduler-idle-count*
	    (if (zerop *psm-scheduler-idle-count*)
		0
	      (// *psm-scheduler-idle-total* *psm-scheduler-idle-count* )))
    (format t "~&Total (before proc)~30T~D~45T~D~60T~D"
	    *psm-total-before-time*
	    *psm-total-count*
	    (if (zerop *psm-total-count*)
		0
	      (// *psm-total-before-time* *psm-total-count*)))
    (format t "~&Find process~30T~D~45T~D~60T~D"
	    *psm-find-process-total*
	    *psm-total-count*
	    (if (zerop *psm-total-count*)
		0
	      (// *psm-find-process-total* *psm-total-count*)))
    (format t "~&Total (without proc)~30T~D~45T~D~60T~D"
	    *psm-total-time*
	    *psm-total-count*
	    (if (zerop *psm-total-count*)
		0
	      (// *psm-total-time* *psm-total-count*)))))

(DEFUN PROCESS-SCHEDULER ()
  (WITHOUT-INTERRUPTS
    (reset-scheduler-meters)
    (DO ((THIS-TIME (TIME) (TIME))
	 (LAST-TIME (TIME) THIS-TIME)
	 (DELTA-TIME)
	 (NEXT-WHO-TIME 0)
	 (PROCESS)
	 (START-PAGE-FAULTS)
	 (START-DISK-TIME)
	 (START-TIME)
	 (TIME-USED)
	 (DISK-TIME-USED)
	 (cycle-start-time (%fixnum-microsecond-time) (%fixnum-microsecond-time))
	 (scratch-start-time))
	(())
      (%P-STORE-TAG-AND-POINTER (+ %DISK-RUN-LIGHT 2) 0 0)
      (WHEN (PLUSP (SETQ DELTA-TIME (TIME-DIFFERENCE THIS-TIME LAST-TIME)))
	(WHEN (NOT (NULL CURRENT-PROCESS))
	  (DECF (PROCESS-QUANTUM-REMAINING CURRENT-PROCESS) DELTA-TIME))
	(CATCH 'PROCESS-WAIT-IN-SCHEDULER  ;minor bum to reduce overhead.  None of these frobs
		;will normally PROCESS-WAIT, but in exception circumstances (ie GC), they can.
	  (setq scratch-start-time (%fixnum-microsecond-time))
	  (DOLIST (F CLOCK-FUNCTION-LIST)
	    (FUNCALL F DELTA-TIME))
	  (incf *psm-clock-list-total* (%pointer-difference (%fixnum-microsecond-time) scratch-start-time))
	  (incf *psm-clock-list-count*)
	  (WHEN (MINUSP (DECF NEXT-WHO-TIME DELTA-TIME))
	    (setq scratch-start-time (%fixnum-microsecond-time))
	    (WHEN (FBOUNDP 'TV::WHO-LINE-UPDATE) (TV:WHO-LINE-UPDATE))
	    (SETQ NEXT-WHO-TIME *STATUS-LINE-UPDATE-INTERVAL*)
	    (incf *psm-wholine-update-total* (%pointer-difference (%fixnum-microsecond-time) scratch-start-time))
	    (incf *psm-wholine-update-count*))))
      (setq scratch-start-time (%fixnum-microsecond-time))
      (IF (prog1 (NULL (SETQ PROCESS (SETQ CURRENT-PROCESS (FIND-RUNNABLE-PROCESS))))
		 (incf *psm-find-process-total*
		       (%pointer-difference (%fixnum-microsecond-time) scratch-start-time)))
	  (progn
	    (setq scratch-start-time (%fixnum-microsecond-time))
	    (PROCESS-SCHEDULER-IDLE-FUNCTION)
	    (incf *psm-scheduler-idle-total* (%pointer-difference (%fixnum-microsecond-time) scratch-start-time))
	    (incf *psm-scheduler-idle-count*)
	    (incf *psm-total-time* (%pointer-difference (%fixnum-microsecond-time) cycle-start-time))
	    (incf *psm-total-count*))
	(SETF (PROCESS-WAIT-WHOSTATE PROCESS) NIL)
	(SET-PROCESS-WAIT PROCESS #'TRUE NIL)
	(SETQ START-PAGE-FAULTS (FIXNUM-READ-METER %COUNT-DISK-PAGE-READ-OPERATIONS))
	(SETQ START-DISK-TIME (FIXNUM-READ-METER %DISK-WAIT-TIME))
	(SETQ START-TIME (%FIXNUM-MICROSECOND-TIME))
	(%P-STORE-TAG-AND-POINTER (+ %DISK-RUN-LIGHT 2) -1 -1)
	(LET ((STACK-GROUP (PROCESS-STACK-GROUP PROCESS)))
	  (incf *psm-total-before-time* (%pointer-difference (%fixnum-microsecond-time) cycle-start-time))
	  (IF (TYPEP STACK-GROUP 'STACK-GROUP)
	      (STACK-GROUP-RESUME STACK-GROUP NIL)
	    (CATCH 'PROCESS-WAIT-IN-SCHEDULER
	      (APPLY STACK-GROUP (CDR (PROCESS-INITIAL-FORM PROCESS))))))
	(%P-STORE-TAG-AND-POINTER (+ %DISK-RUN-LIGHT 2) 0 0)
	(INCREMENT-PROCESS-TIME-METER
	  (PROCESS-TOTAL-RUN-TIME PROCESS)
	  (SETQ TIME-USED (%POINTER-DIFFERENCE
			    (%FIXNUM-MICROSECOND-TIME)
			    START-TIME)))
	(INCREMENT-PROCESS-TIME-METER
	  (PROCESS-DISK-WAIT-TIME PROCESS)
	  (SETQ DISK-TIME-USED (%POINTER-DIFFERENCE
				 (FIXNUM-READ-METER %DISK-WAIT-TIME)
				 START-DISK-TIME)))
	(INCF (PROCESS-PAGE-FAULT-COUNT PROCESS)
	      (%POINTER-DIFFERENCE
		(FIXNUM-READ-METER %COUNT-DISK-PAGE-READ-OPERATIONS)
		START-PAGE-FAULTS))
	(LET ((LAST-TIME (PROCESS-LAST-TIME-RUN PROCESS)))
	  (SETF (PROCESS-PERCENT-UTILIZATION PROCESS)
		(WITHOUT-FLOATING-UNDERFLOW-TRAPS
		  (+ (IF (NOT (NULL LAST-TIME))
			 (FIX (* (PROCESS-PERCENT-UTILIZATION PROCESS)
				 (^ *PERCENT-UTILIZATION-DISCOUNT-FACTOR*
				    (TIME-DIFFERENCE THIS-TIME LAST-TIME))))
		       0)
		     ;; Don't use ROUND -- loses before SYS: SYS2; RAT is loaded.
		     ;;  Believe this, FOOO.
		     (TRUNCATE (+ TIME-USED 500.) 1000.))))
	  (SETF (PROCESS-LAST-TIME-RUN PROCESS) THIS-TIME))
	;; Remember current stack group of process last run, so we resume it next time.
	(UNLESS (PROCESS-SIMPLE-P PROCESS)
	  (SETF (PROCESS-STACK-GROUP PROCESS) %CURRENT-STACK-GROUP-PREVIOUS-STACK-GROUP))
	(incf *psm-total-time* (- (%pointer-difference (%fixnum-microsecond-time) cycle-start-time)
				  time-used))
	(incf *psm-total-count*)))))

(defconst *random-special-var-counter* 0)

(defun typical-timing-overhead (&aux scratch-start-time time-start)
  (without-interrupts
    (setq time-start (%fixnum-microsecond-time))
    (setq scratch-start-time (%fixnum-microsecond-time))
    (incf *random-special-var-counter* (- (%fixnum-microsecond-time) scratch-start-time))
    (incf *random-special-var-counter*)
    (%pointer-difference (%fixnum-microsecond-time) time-start)))

(defun time-find-runable-process (&optional (times 100)
				  &aux (total 0)
				  start-time)
  (dotimes (c times (// total times))
    (setq start-time (%fixnum-microsecond-time))
    (find-runnable-process)
    (incf total (- (%fixnum-microsecond-time) start-time))
    (process-allow-schedule)))

(defconst *process-wait-function-snapshot* ())

(defun snapshot-process-wait-functions (&aux return-list)
  (without-interrupts
    (dolist (l active-processes
	       (setq *process-wait-function-snapshot*
		     (reverse return-list)))
      (when (and (car l)
		 (not (memq (second l) (list 'flushed-process #'true))))
	(push (firstn 3 l) return-list)))))

(defun edit-process-wait-functions ()
  (dolist (l *process-wait-function-snapshot*)
    (when (y-or-n-p "Edit ~S : ~S " (car l) (function-name (cadr l)))
      (ed (cadr l)))))
