216 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Scheme
		
	
	
	
			
		
		
	
	
			216 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Scheme
		
	
	
	
| (define-structure servlet servlet-interface
 | |
|   (open scheme-with-scsh
 | |
| 	servlets
 | |
| 	servlet-handler/admin
 | |
| 	profiling
 | |
| 	handle-fatal-error
 | |
| 	httpd-responses
 | |
| 	(subset srfi-1 (iota))
 | |
| 	define-record-types
 | |
| 	locks
 | |
| 	let-opt
 | |
| 	)
 | |
|   (begin
 | |
| 
 | |
|     ;; This uses the filesystem heavily to not influence the
 | |
|     ;; profiling. Note to get the resulting picture, gnuplot must be
 | |
|     ;; installed.
 | |
| 
 | |
|     (define-record-type state :state
 | |
|       (make-state file-name file-names-to-delete counter)
 | |
|       state?
 | |
|       (file-name state:file-name set-state:file-name)
 | |
|       (file-names-to-delete state:file-names-to-delete set-state:file-names-to-delete)
 | |
|       (counter state:counter set-state:counter))
 | |
|     
 | |
|     (define (state-file-name)
 | |
|       (state:file-name (get-servlet-data)))
 | |
|     (define (state-file-names-to-delete)
 | |
|       (state:file-names-to-delete (get-servlet-data)))
 | |
|     (define (state-counter)
 | |
|       (state:counter (get-servlet-data)))
 | |
|     
 | |
|     ;; Leave this global. Server is running on a single system.
 | |
|     (define gnuplot "/usr/bin/gnuplot")
 | |
|     (define lock (make-lock))
 | |
| 
 | |
|     (define (profile req . maybe-update-text)
 | |
|       (let* ((update-text (:optional maybe-update-text ""))
 | |
| 	     (input-field (make-text-input-field gnuplot '(@ (size 20))))
 | |
| 	     (change-button (make-submit-button "Change"))
 | |
| 	     (new-profile-address (make-address))
 | |
| 	     (result-address (make-address))
 | |
| 	     (reset-address (make-address))
 | |
| 	     (return-address (make-address))
 | |
| 	     (reset-return-address (make-address))
 | |
| 	     (req 
 | |
| 	      (send-html/suspend
 | |
| 	       (lambda (new-url)
 | |
| 		 `(html 
 | |
| 		   (title "Servlet Administration -- Profiling")
 | |
| 		   (body (h1 "Serlvet Administration")
 | |
| 			 (h2 "Profiling")
 | |
| 			 (font (@ (color "red")) ,update-text)
 | |
| 			 (p "Currently, there are " ,(state-counter) " profiles saved.")
 | |
| 			 (ul
 | |
| 			  (li (URL ,(new-profile-address new-url)
 | |
| 				   "Create new profile")
 | |
| 			  (li (URL ,(result-address new-url)
 | |
| 				   "Show profile results")
 | |
| 			      (br)
 | |
| 			      (servlet-form
 | |
| 			       ,new-url
 | |
| 			       POST
 | |
| 			       (p "This uses " (var "gnuplot") " that is searched at "
 | |
| 				  ,input-field ,change-button)))
 | |
| 			  (li (URL ,(reset-address new-url)
 | |
| 				   "Delete files and reset profile state."))))
 | |
| 			 (hr)
 | |
| 			 (URL ,(return-address new-url) 
 | |
| 			      "Return to administration menu leaving files and state untouched.")
 | |
| 			 (br)
 | |
| 			 (URL ,(reset-return-address new-url)
 | |
| 			      "Return to administration menu removing files and reseting state.")
 | |
| 			 (br)
 | |
| 			 (URL "/" "Return to main menu."))))))
 | |
| 	     (bindings (get-bindings req)))
 | |
| 	(cond
 | |
| 	 ((returned-via? new-profile-address bindings)
 | |
| 	  (new-profile req))
 | |
| 	 ((returned-via? result-address bindings)
 | |
| 	  (if (zero? (state-counter))
 | |
| 	      (profile req "Now profiles created, currently. Select 'Create new profile' to create one.")
 | |
| 	      (result req)))
 | |
| 	 ((returned-via? reset-address bindings)
 | |
| 	  (reset req))
 | |
| 	 ((returned-via? return-address bindings)
 | |
| 	  (return-to-main-page req))
 | |
| 	 ((returned-via? reset-return-address bindings)
 | |
| 	  (reset-and-return-to-main-page req))
 | |
| 	 (else
 | |
| 	  (let ((new-gnuplot-location (input-field-value input-field bindings)))
 | |
| 	    (if (and new-gnuplot-location
 | |
| 		     (file-executable? new-gnuplot-location))
 | |
| 		(begin
 | |
| 		  (set! gnuplot new-gnuplot-location)
 | |
| 		  (profile req (format #f "Gnuplot is now searched at ~a." gnuplot)))
 | |
| 		(profile req "Please enter a file name of an existing executable.")))))))
 | |
| 
 | |
|     (define (new-profile req)
 | |
|       (let ((state (get-servlet-data)))
 | |
| 	(format #t "profiling...~%")
 | |
| 	(obtain-lock lock)
 | |
| 	(profile-space (state:file-name state))
 | |
| 	(release-lock lock)
 | |
| 	(format #t "profile recorded in ~s~%" (state:file-name state))
 | |
| 	(set-state:counter state (+ 1 (state:counter state)))
 | |
| 	(profile req (format #f "Profile #~a generated" (state:counter state)))))
 | |
| 
 | |
|     (define (result req)
 | |
|       (let ((results (profile-results (state-file-name)))
 | |
| 	    (gnuplot-data-file-name (create-temp-file "servlet-profiling.data"))
 | |
| 	    (picture-file (string-append (create-temp-file 
 | |
| 					  "../img/servlet-profiling.picture")
 | |
| 					 ".pbm"))
 | |
| 	    (get-total-bytes (lambda (space-info)
 | |
| 			       (total-bytes (space-info-total space-info))))
 | |
| 	    (return-address (make-address))
 | |
| 	    (reset-return-address (make-address)))
 | |
| 	(write-gnuplot-data-file gnuplot-data-file-name 
 | |
| 				 get-total-bytes
 | |
| 				 results)
 | |
| 	(let ((status 
 | |
| 	       (run (,gnuplot -)
 | |
| 		    (<< ,(format #f "set terminal pbm color
 | |
| set output '~a'
 | |
| set size 0.7,0.7
 | |
| plot '~a' title 'Servlet Profiling ~a' with lines"
 | |
| 				 picture-file
 | |
| 				 gnuplot-data-file-name
 | |
| 				 (format-date "~c" (date))
 | |
| 				 )))))
 | |
| 	  (delete-file gnuplot-data-file-name)
 | |
| 	  (add-file-name-to-delete! picture-file)
 | |
| 	  (let* ((req (show-results status picture-file get-total-bytes results 
 | |
| 				    return-address reset-return-address))
 | |
| 		 (bindings (get-bindings req)))
 | |
| 	    (cond
 | |
| 	     ((returned-via? return-address bindings)
 | |
| 	      (profile req "You may continue to make profiles."))
 | |
| 	     ((returned-via? reset-return-address bindings)
 | |
| 	      (reset-and-return-to-main-page req))
 | |
| 	     (else
 | |
| 	      (error "unexpected return address")))))))
 | |
|     
 | |
|     (define (show-results status picture-file get-total-bytes results
 | |
| 			  return-address reset-return-address)
 | |
|       (send-html/suspend
 | |
|        (lambda (new-url)
 | |
| 	 `(html 
 | |
| 	   (title "Servlet Administration -- Profiling Results")
 | |
| 	   (body
 | |
| 	    (h1 "Servlet-Administration")
 | |
| 	    (h2 "Profiling Results")
 | |
| 	    (h3 "Picture")
 | |
| 	    ,(if (zero? status)
 | |
| 		 `(image (@ (src ,picture-file)))
 | |
| 		 `(p "An error occured while generating the profiling results picture."
 | |
| 		     (br)
 | |
| 		     "Are you sure, you have " (q "gnuplot") 
 | |
| 		     " installed at " (q ,gnuplot) "?"))
 | |
| 	    (hr)
 | |
| 	    (h3 "Data")
 | |
| 	    (table
 | |
| 	     (tr (th "#") (th "Total bytes occupied"))
 | |
| 	     ,@(map (lambda (num space-info)
 | |
| 		      `(tr (td ,(+ 1 num))
 | |
| 			   (td ,(get-total-bytes space-info))))
 | |
| 		    (iota (length results))
 | |
| 		    results))
 | |
| 	    (hr)
 | |
| 	    (p (URL ,(return-address new-url) "Return to previous page") (br)
 | |
| 	       (URL ,(reset-return-address new-url) 
 | |
| 		    "Delete files, reset state and return to main menu.")))))))
 | |
| 
 | |
|     (define (reset req)
 | |
|       (reset-profiling-state!)
 | |
|       (profile req "Profiling state reseted."))
 | |
| 
 | |
|     (define (add-file-name-to-delete! file-name)
 | |
|       (let ((state (get-servlet-data)))
 | |
| 	(set-state:file-names-to-delete 
 | |
| 	 state 
 | |
| 	 (cons file-name
 | |
| 	       (state:file-names-to-delete state)))))
 | |
| 
 | |
|     (define (delete-files state)
 | |
|       (let ((file-names-to-delete (state:file-names-to-delete state)))
 | |
| 	(if file-names-to-delete
 | |
| 	    (for-each delete-filesys-object file-names-to-delete))))
 | |
| 
 | |
|     (define (reset-profiling-state!)
 | |
|       (let ((state (get-servlet-data)))
 | |
| 	(set-state:counter state 0)
 | |
| 	(delete-files state)
 | |
| 	(set-state:file-name state 
 | |
| 			     (absolute-file-name (create-temp-file "servlet-profiling")))
 | |
| 	(set-state:file-names-to-delete state
 | |
| 					(list (state:file-name state)))))
 | |
|     
 | |
|     (define (reset-and-return-to-main-page req)
 | |
|       ;; Overhead included :-|
 | |
|       (reset-profiling-state!)
 | |
|       (delete-files (get-servlet-data))
 | |
|       (return-to-main-page req))
 | |
| 
 | |
|     (define (return-to-main-page req)
 | |
|       (send/finish (make-error-response (status-code moved-perm) req
 | |
| 					"admin.scm" "admin.scm")))
 | |
| 
 | |
|     (define (main req)
 | |
|       ;; We'll fill this out soon.
 | |
|       (set-servlet-data! (make-state #f #f 0))
 | |
|       (reset-profiling-state!)
 | |
|       (profile req))
 | |
| 
 | |
|     )) |