;;; -*- Mode:LISP; Package:TCP; Base:10; Readtable:CL -*- #| Copyright LISP Machine, Inc. 1987 See filename "Copyright.Text" for licensing and release information. |# (defvar *tcp-test-socket-list* nil) (defvar *tcp-test-process-count* 0) (defun multi-test (processes connections-per-process &rest arguments &key (quantum 60) &allow-other-keys) (setq *tcp-test-socket-list* nil) (setq *tcp-test-process-count* 0) (let ((process-list nil)) (unwind-protect (progn (dotimes (i processes) (let ((process (make-process (format nil "TCP TEST ~D" i) :quantum quantum :warm-boot-action 'si:process-warm-boot-reset :arrest-reasons '(:not-running)))) (send process :preset #'(lambda (number) (apply #'tcp:test :print-statistics-p nil :connection-number number :connections connections-per-process arguments)) (* i connections-per-process)) (send process :reset) (send process :run-reason :enable) (incf *tcp-test-process-count*) (push process process-list))) (mapcar #'(lambda (x) (send x :revoke-arrest-reason :not-running)) process-list) (process-wait "Completion" #'(lambda () (zerop *tcp-test-process-count*)))) (mapcar #'(lambda (x) (send x :revoke-arrest-reason :not-running) (send x :kill)) process-list) (when *tcp-test-socket-list* (print-statistics (apply #'append *tcp-test-socket-list*))))) t) (defun test (&key total-sends send-size sends-out receive-size receives-out (check-data-p nil) (connections 1) (open :both) (sender :init) (receiver :listen) auto-push (optimistic t) listen-port init-port host (send-gauges nil) (receive-gauges nil) (print-statistics-p t) (connection-number 0) &allow-other-keys &aux listen-gauges init-gauges sender-sockets receiver-sockets (my-address (parse-internet-address si:local-host))) (declare (special total-sends send-size sends-out receive-size receives-out check-data-p)) (check-type open (member :init :listen :both)) (setq listen-gauges (and (member open '(:listen :both)) (append (if (member sender '(:listen :both)) send-gauges) (if (member receiver '(:listen :both)) receive-gauges)))) (setq init-gauges (and (member open '(:init :both)) (append (if (member sender '(:init :both)) send-gauges) (if (member receiver '(:init :both)) receive-gauges)))) (setq host (parse-internet-address host)) (case open (:listen (check-type listen-port (or null (unsigned-byte 16))) (if host (check-type init-port (unsigned-byte 16)) (check-type init-port null)) (check-type sender (member nil :listen)) (check-type receiver (member nil :listen))) (:init (check-type init-port (or null (unsigned-byte 16))) (check-type listen-port (unsigned-byte 16)) (unless host (setq host my-address)) (check-type sender (member nil :init)) (check-type receiver (member nil :init))) (:both (check-type init-port (or null (unsigned-byte 16))) (check-type listen-port (or null (unsigned-byte 16))) (unless host (setq host my-address)) (check-type sender (member nil :listen :init :both)) (check-type receiver (member nil :listen :init :both)))) (when sender (unless total-sends (setq total-sends 100)) (unless sends-out (setq sends-out 1)) (check-type total-sends (and integer (satisfies plusp))) (assert (or (null send-size) (and (consp send-size) (integerp (first send-size)) (plusp (first send-size))) (and (integerp send-size) (plusp send-size))) (send-size) "Send-size, ~A, is not a positive integer, a list containing a positive integer, or NIL" send-size) (check-type sends-out (and integer (satisfies plusp)))) (when receiver (unless receives-out (setq receives-out 1)) (assert (or (null receive-size) (and (consp receive-size) (integerp (first receive-size)) (plusp (first receive-size))) (and (integerp receive-size) (plusp receive-size))) (receive-size) "Receive-size, ~A, is not a positive integer, a list containing a positive integer, or NIL" receive-size) (check-type receives-out (and integer (satisfies plusp)))) (dotimes (i connections) (let (listen-socket listen-keywords init-socket init-keywords) (when (member open '(:listen :both)) (setq listen-keywords (append `(:open :send-timeout ,(* 60 60) :gauge-name ,(format nil "LSTN ~D" (+ connection-number i)) :auto-push ,auto-push :optimistic ,optimistic) (if listen-port `(:local-port ,listen-port)) (if init-port `(:remote-port ,init-port :remote-address ,host)) (if listen-gauges `(:initial-gauges ,listen-gauges)))) (setq listen-socket (make-tcp-socket :keyword (format nil "LSTN ~D" (+ connection-number i)))) (setq listen-port (apply listen-socket listen-keywords)) (when (member sender '(:listen :both)) (push listen-socket sender-sockets)) (when (member receiver '(:listen :both)) (push listen-socket receiver-sockets))) (when (member open '(:init :both)) (setq init-keywords (append `(:open :send-timeout ,(* 60 60) :gauge-name ,(format nil "INIT ~D" (+ connection-number i)) :remote-port ,listen-port :remote-address ,host :active t :auto-push ,auto-push :optimistic ,optimistic) (if init-port `(:local-port ,init-port)) (if init-gauges `(:initial-gauges ,init-gauges)))) (setq init-socket (make-tcp-socket :keyword (format nil "INIT ~D" (+ connection-number i)))) (apply init-socket init-keywords)) (when (member sender '(:init :both)) (push init-socket sender-sockets)) (when (member receiver '(:init :both)) (push init-socket receiver-sockets)) (incf listen-port))) (let ((final-sockets nil)) (unwind-protect (setq final-sockets (handle-sockets sender-sockets receiver-sockets print-statistics-p (eql my-address host))) (if print-statistics-p (setq *tcp-test-socket-list* final-sockets) (without-interrupts (push final-sockets *tcp-test-socket-list*) (decf *tcp-test-process-count*))))) t) (defstruct (socket-statistics (:conc-name "SS-")) (open-reply-time nil) (close-reply-time nil) (closed nil) (total-sends 0) (sends-out 0) (last-send-byte 0) (total-bytes-sent 0) (total-receives 0) (receives-out 0) (last-receive-byte 0) (total-bytes-received 0)) (defun handle-sockets (send-list receive-list print-statistics-p loopback-p &aux (socket-fifo (make-fifo)) socket-list opens-out) (declare (special total-sends send-size sends-out receive-size receives-out check-data-p)) (declare (special socket-list)) ;;Build a fifo containing one copy of each socket (dolist (socket send-list) (move-to-end-of-fifo socket socket-fifo)) (dolist (socket receive-list) (move-to-end-of-fifo socket socket-fifo)) (setq opens-out (length (fifo-as-list socket-fifo))) (setq socket-list (nreverse (mapcar #'(lambda (x) (cons x (make-socket-statistics))) (fifo-as-list socket-fifo)))) (unwind-protect (do () ((fifo-empty-p socket-fifo)) (let ((socket nil) ;The socket we are currently looking at (elt nil)) ;The read-data element (process-wait "Data" #'(lambda () (or socket (setq socket (multiple-connection-listen (fifo-as-list socket-fifo)))))) ;;Found a socket; look at another one first next time (move-to-end-of-fifo socket socket-fifo) (setq elt (send socket :read-data)) (case (first elt) (:open (handle-open-reply socket) (decf opens-out) (when (zerop opens-out) (startup-io receive-list send-list))) (:data (handle-read-reply socket elt)) (:write-reply (handle-write-reply socket elt)) (:closing (handle-closing socket elt) (unless (member socket send-list) (send socket :close :normal))) ((:reset :closed) (let ((stats (cdr (assoc socket socket-list :test #'eq))) (now (zl:time))) (unless (ss-open-reply-time stats) (setf (ss-open-reply-time stats) now)) (setf (ss-close-reply-time stats) now) (remove-from-fifo socket socket-fifo))) (:timeout (if loopback-p (send socket :reset-timeout) (let ((stats (cdr (assoc socket socket-list :test #'eq))) (now (zl:time))) (cond ((ss-open-reply-time stats) (setf (ss-closed stats) t) (send socket :abort)) (t (setf (ss-open-reply-time stats) now) (setf (ss-close-reply-time stats) now) (remove-from-fifo socket socket-fifo) (decf opens-out) (when (zerop opens-out) (startup-io receive-list send-list)))))))))) (multiple-connection-kill-gauges (mapcar #'car socket-list)) (let ((now (zl:time))) (dolist (socket (fifo-as-list socket-fifo)) (let ((stats (cdr (assoc socket socket-list :test #'eq)))) (unless (ss-open-reply-time stats) (setf (ss-open-reply-time stats) now)) (setf (ss-close-reply-time stats) now) (send socket :abort)))) (when print-statistics-p (print-statistics socket-list))) socket-list) (defun handle-open-reply (socket) (declare (special socket-list)) (let ((stats (cdr (assoc socket socket-list :test #'eq)))) (setf (ss-open-reply-time stats) (zl:time)))) (defun startup-io (receive-list send-list) (declare (special total-sends send-size sends-out receive-size receives-out check-data-p)) (declare (special socket-list)) (dolist (socket receive-list) (let ((stats (cdr (assoc socket socket-list :test #'eq))) (size)) (multiple-value-bind (default-receive-size ignore) (send socket :mss) (setq size (cond ((null receive-size) default-receive-size) ((consp receive-size) (* (car receive-size) default-receive-size)) (t receive-size)))) (dotimes (i receives-out) (send socket :receive (make-array size :element-type '(unsigned-byte 8) :fill-pointer 0)) (incf (ss-receives-out stats))))) (dolist (socket send-list) (let ((stats (cdr (assoc socket socket-list :test #'eq))) (size)) (multiple-value-bind (ignore default-send-size) (send socket :mss) (setq size (cond ((null send-size) default-send-size) ((consp send-size) (* (car send-size) default-send-size)) (t send-size)))) (dotimes (i (min sends-out total-sends)) (let ((buffer (make-array size :element-type '(unsigned-byte 8) :fill-pointer size))) (when check-data-p (do ((i 0 (1+ i)) (byte (ss-last-send-byte stats) (1+ byte))) ((= i size) (setf (ss-last-send-byte stats) (logand byte #xff))) (setf (aref buffer i) byte))) (send socket :write-data buffer)) (incf (ss-sends-out stats)))))) (defun handle-read-reply (socket elt) (declare (special total-sends send-size sends-out receive-size receives-out check-data-p)) (declare (special socket-list)) (let* ((stats (cdr (assoc socket socket-list :test #'eq))) (buffer (second elt)) (size (length buffer))) (decf (ss-receives-out stats)) (incf (ss-total-receives stats)) (incf (ss-total-bytes-received stats) size) (when check-data-p (do ((i 0 (1+ i)) (byte (ss-last-receive-byte stats) (logand (1+ byte) #xff))) ((= i size) (setf (ss-last-receive-byte stats) (logand byte #xff))) (unless (= (aref buffer i) byte) (error "data error!")))) (unless (or (ss-closed stats) (eq (third elt) :eof)) (send socket :receive buffer) (incf (ss-receives-out stats))))) (defun handle-write-reply (socket elt) (declare (special total-sends send-size sends-out receive-size receives-out check-data-p)) (declare (special socket-list)) (let* ((stats (cdr (assoc socket socket-list :test #'eq))) (buffer (second elt)) (count (1+ (ss-total-sends stats)))) (decf (ss-sends-out stats)) (incf (ss-total-sends stats)) (incf (ss-total-bytes-sent stats) (length buffer)) (cond ((ss-closed stats)) ((<= count (- total-sends sends-out)) (when check-data-p (do ((i 0 (1+ i)) (size (length buffer)) (byte (ss-last-send-byte stats) (1+ byte))) ((= i size) (setf (ss-last-send-byte stats) (logand byte #xff))) (setf (aref buffer i) byte))) (send socket :write-data buffer) (incf (ss-sends-out stats))) ((< count total-sends)) ((= count total-sends) (send socket :close :normal)) (t (error "too many sends?"))))) (defun handle-closing (socket elt) (declare (special socket-list)) (let* ((stats (cdr (assoc socket socket-list :test #'eq)))) (decf (ss-receives-out stats) (length (second elt))))) (defun print-statistics (socket-list) (let ((total-bytes-sent 0) (total-sends 0) (total-bytes-received 0) (total-receives 0) (earliest-start nil) (latest-end nil)) (dolist (x socket-list) (let* ((socket (car x)) (stats (cdr x)) (interval (/ (float (time-difference (ss-close-reply-time stats) (ss-open-reply-time stats))) 60.0)) (sends (ss-total-sends stats)) (receives (ss-total-receives stats)) (bytes-sent (ss-total-bytes-sent stats)) (bytes-received (ss-total-bytes-received stats))) (when (or (null earliest-start) (time-lessp (ss-open-reply-time stats) earliest-start)) (setq earliest-start (ss-open-reply-time stats))) (when (or (null latest-end) (time-lessp latest-end (ss-close-reply-time stats))) (setq latest-end (ss-close-reply-time stats))) (incf total-sends sends) (incf total-receives receives) (incf total-bytes-sent bytes-sent) (incf total-bytes-received bytes-received) (format t "~& ~A: ~10T~,2F seconds;~27T~9D bytes sent in ~4D requests: ~,2F Bytes/Second (~,4F kbs)" (tcp-user-gauge-name socket) interval bytes-sent sends (if (zerop interval) "****" (/ bytes-sent interval)) (if (zerop interval) "****" (/ (* bytes-sent 8.0) interval 1000.0))) (format t "~&~27T~9D bytes rcvd in ~4D requests: ~,2F Bytes/Second (~,4F kbs)" bytes-received receives (if (zerop interval) "****" (/ bytes-received interval)) (if (zerop interval) "****" (/ (* bytes-received 8.0) interval 1000.0))))) (let ((max-interval (/ (float (time-difference latest-end earliest-start)) 60.0))) (format t "~&~%~A: ~10T~,2F seconds;~27T~9D bytes sent in ~4D requests: ~,2F Bytes/Second (~,4F kbs)" "TOTALS" max-interval total-bytes-sent total-sends (if (zerop max-interval) "****" (/ total-bytes-sent max-interval)) (if (zerop max-interval) "****" (/ (* total-bytes-sent 8.0) max-interval 1000.0))) (format t "~&~27T~9D bytes rcvd in ~4D requests: ~,2F Bytes/Second (~,4F kbs)" total-bytes-received total-receives (if (zerop max-interval) "****" (/ total-bytes-received max-interval)) (if (zerop max-interval) "****" (/ (* total-bytes-received 8.0) max-interval 1000.0))))))