| 1: | | ;; statprof.scm -- A statistical profiler for Gambit-C 4.0 |
| 2: | | |
| 3: | | ;; See the README file for license and usage information. |
| 4: | | |
| 5: | | ;; $Id: statprof.scm,v 1.9 2005/03/14 07:35:49 guillaume Exp $ |
| 6: | | |
| 7: | | |
| 8: | | ;; ---------------------------------------------------------------------------- |
| 9: | | ;; Profiling & interruption handling |
| 10: | | |
| 11: | | (define *buckets* #f) |
| 12: | | (define *total* 0) |
| 13: | | |
| 14: | | (define (profile-start!) |
| 15: | | (set! *buckets* '()) |
| 16: | | (set! *total* 0) |
| 17: | | (##interrupt-vector-set! 1 profile-heartbeat!)) |
| 18: | | |
| 19: | | (define (profile-stop!) |
| 20: | | (##interrupt-vector-set! 1 ##thread-heartbeat!)) |
| 21: | | |
| 22: | | ;; (define (identify-continuation cont) ; first version |
| 23: | | ;; (or (##continuation-creator cont) |
| 24: | | ;; 'unknown)) |
| 25: | | |
| 26: | | (define (identify-continuation cont) ; second version |
| 27: | | (let ((locat (##continuation-locat cont))) |
| 28: | | (if locat |
| 29: | | (let* ((container (##locat-container locat)) |
| 30: | | (file (##container->file container))) |
| 31: | | (if file |
| 32: | | (let* ((filepos (##position->filepos (##locat-position locat))) |
| 33: | | (line (##fixnum.+ (##filepos-line filepos) 1)) |
| 34: | 0% | (col (##fixnum.+ (##filepos-col filepos) 1))) |
| 35: | | (list file line col)) |
| 36: | | 'unknown)) |
| 37: | | 'unknown))) |
| 38: | | |
| 39: | | (define (profile-heartbeat!) |
| 40: | 0% | (##continuation-capture |
| 41: | 0% | (lambda (cont) |
| 42: | 0% | (##thread-heartbeat!) |
| 43: | 0% | (let ((id (identify-continuation cont))) |
| 44: | 0% | (if (not (eq? id 'unknown)) |
| 45: | | (let ((bucket (assoc (car id) *buckets*))) |
| 46: | | (set! *total* (+ *total* 1)) |
| 47: | | (if (not bucket) |
| 48: | | (begin |
| 49: | | (set! *buckets* (cons |
| 50: | | (cons (car id) |
| 51: | | ;; fixme: arbitrary hard limit |
| 52: | | ;; on the length of source |
| 53: | | ;; files |
| 54: | | (make-vector 5000 0)) |
| 55: | | *buckets*)) |
| 56: | | (set! bucket (car *buckets*)))) |
| 57: | | |
| 58: | 0% | (vector-set! (cdr bucket) |
| 59: | | (cadr id) |
| 60: | | (+ (vector-ref (cdr bucket) |
| 61: | | (cadr id)) |
| 62: | | 1)))))))) |
| 63: | | |
| 64: | | |
| 65: | | ;; ---------------------------------------------------------------------------- |
| 66: | | ;; Text formatting |
| 67: | | |
| 68: | | (define (pad-left s l c) |
| 69: | | (let loop ((s (string->list s))) |
| 70: | | (if (< (length s) l) |
| 71: | | (loop (cons c s)) |
| 72: | | (list->string s)))) |
| 73: | | |
| 74: | | |
| 75: | | ;; ---------------------------------------------------------------------------- |
| 76: | | ;; Palette generation & color formatting |
| 77: | | |
| 78: | | (define (gradient from to step) |
| 79: | | (let ((inc (map (lambda (x) (/ x step)) |
| 80: | | (map - to from)))) |
| 81: | | |
| 82: | | (let loop ((i 0) |
| 83: | | (acc '())) |
| 84: | | (if (= i step) |
| 85: | | (reverse acc) |
| 86: | | (loop (+ i 1) |
| 87: | | (cons (map |
| 88: | | (lambda (x o) |
| 89: | | (round (+ x (* i o)))) |
| 90: | | from |
| 91: | | inc) |
| 92: | | acc)))))) |
| 93: | | |
| 94: | | (define (as-rgb col) |
| 95: | | (apply string-append |
| 96: | | (map |
| 97: | | (lambda (x) |
| 98: | | (pad-left (number->string x 16) 2 #\0)) |
| 99: | | col))) |
| 100: | | |
| 101: | | (define palette |
| 102: | | (list->vector |
| 103: | | (cons '(255 255 255) |
| 104: | | (gradient '(127 127 255) |
| 105: | | '(255 127 127) |
| 106: | | 16)))) |
| 107: | | |
| 108: | | |
| 109: | | ;; ---------------------------------------------------------------------------- |
| 110: | | ;; Functions to generate the report |
| 111: | | |
| 112: | | (define (write-profile-report profile-name) |
| 113: | | |
| 114: | | (define (iota1 n) |
| 115: | | (let loop ((n n) |
| 116: | | (l '())) |
| 117: | | (if (>= n 1) |
| 118: | | (loop (- n 1) (cons n l)) |
| 119: | | l))) |
| 120: | | |
| 121: | | (define directory-name (string-append (current-directory) |
| 122: | | profile-name |
| 123: | | "/")) |
| 124: | | (with-exception-catcher |
| 125: | | (lambda (e) |
| 126: | | ;; ignore the exception, it probably means that the directory |
| 127: | | ;; already existed. If there's another problem it will be |
| 128: | | ;; signaled later. |
| 129: | | #f) |
| 130: | | (lambda () |
| 131: | | (create-directory (list path: directory-name |
| 132: | | permissions: #o755)))) |
| 133: | | |
| 134: | | (let ((max-intensity |
| 135: | | (apply max |
| 136: | | (map |
| 137: | | (lambda (data) |
| 138: | | (apply max |
| 139: | | (vector->list data))) |
| 140: | | (map cdr *buckets*))))) |
| 141: | | |
| 142: | | (map |
| 143: | | (lambda (bucket) |
| 144: | | (let ((file (car bucket)) |
| 145: | | (data (cdr bucket))) |
| 146: | | |
| 147: | | (define (get-color n) |
| 148: | | (let ((i (vector-ref data n))) |
| 149: | | (if (= i 0) |
| 150: | | (as-rgb (vector-ref palette 0)) |
| 151: | | (let ((x (* (/ (log (+ 1. i)) |
| 152: | | (ceiling (log max-intensity))) |
| 153: | | (- (vector-length palette) 1)))) |
| 154: | | (as-rgb (vector-ref palette |
| 155: | | (inexact->exact (ceiling x)))))))) |
| 156: | | |
| 157: | | (with-output-to-file (string-append |
| 158: | | directory-name |
| 159: | | (path-strip-directory file) |
| 160: | | ".html") |
| 161: | | (let ((lines (call-with-input-file file |
| 162: | | (lambda (p) (read-all p read-line))))) |
| 163: | | (lambda () |
| 164: | | (display |
| 165: | | (sexp->html |
| 166: | | `(html |
| 167: | | (body |
| 168: | | (table |
| 169: | | cellspacing: 0 |
| 170: | | cellpadding: 0 |
| 171: | | border: 0 |
| 172: | | style: "font-size: 12px;" |
| 173: | | ,@(map |
| 174: | | (lambda (line line#) |
| 175: | | `(tr |
| 176: | | (td ,(string-append |
| 177: | | (number->string line#) |
| 178: | | ": ")) |
| 179: | | ;; (td |
| 180: | | ;; align: center |
| 181: | | ;; ,(let ((n (vector-ref data line#))) |
| 182: | | ;; (if (= n 0) |
| 183: | | ;; "" |
| 184: | | ;; (string-append "[" |
| 185: | | ;; (number->string n) |
| 186: | | ;; "/" |
| 187: | | ;; (number->string *total*) |
| 188: | | ;; "]")))) |
| 189: | | |
| 190: | | (td |
| 191: | | align: center |
| 192: | | ,(let ((n (vector-ref data line#))) |
| 193: | | (if (= n 0) |
| 194: | | "" |
| 195: | | (string-append |
| 196: | | (number->string |
| 197: | | (round% (/ n *total*))) |
| 198: | | "% ")))) |
| 199: | | |
| 200: | | (td (pre style: ,(string-append |
| 201: | | "background-color:#" |
| 202: | | (get-color line#)) |
| 203: | | ,line)))) |
| 204: | | lines |
| 205: | | (iota1 (length lines))))))))))))) |
| 206: | | |
| 207: | | *buckets*)) |
| 208: | | |
| 209: | | (with-output-to-file (string-append directory-name "index.html") |
| 210: | | (lambda () |
| 211: | | (display |
| 212: | | (sexp->html |
| 213: | | `(html |
| 214: | | (body |
| 215: | | ,@(map (lambda (bucket) |
| 216: | | (let ((file-path (string-append |
| 217: | | directory-name |
| 218: | | (path-strip-directory (car bucket)) |
| 219: | | ".html"))) |
| 220: | | `(p (a href: ,file-path ,file-path) |
| 221: | | " [" |
| 222: | | ,(round% |
| 223: | | (/ (apply + (vector->list (cdr bucket))) |
| 224: | | *total*)) |
| 225: | | " %]"))) |
| 226: | | *buckets*)))))))) |
| 227: | | |
| 228: | | (define (round% n) |
| 229: | | (/ (round |
| 230: | | (* 10000 n)) |
| 231: | | 100.)) |
| 232: | | |
| 233: | | |
| 234: | | ;; ---------------------------------------------------------------------------- |
| 235: | | ;; Included file "html.scm" |
| 236: | | ;; ---------------------------------------------------------------------------- |
| 237: | | |
| 238: | | ;; html.scm -- A simple html generator for Gambit-C 4.0 |
| 239: | | |
| 240: | | ;; Written by Guillaume Germain (germaing@iro.umontreal.ca) |
| 241: | | ;; This code is released in the public domain. |
| 242: | | |
| 243: | | |
| 244: | | (define (stringify x) |
| 245: | | (with-output-to-string "" |
| 246: | | (lambda () |
| 247: | | (display x)))) |
| 248: | | |
| 249: | | (define (to-escaped-string x) |
| 250: | | (stringify |
| 251: | | (map (lambda (c) |
| 252: | | (case c |
| 253: | | ((#\<) "<") |
| 254: | | ((#\>) ">") |
| 255: | | ((#\&) "&") |
| 256: | | (else c))) |
| 257: | | (string->list |
| 258: | | (stringify x))))) |
| 259: | | |
| 260: | | ;; Quick and dirty conversion of s-expressions to html |
| 261: | | (define (sexp->html exp) |
| 262: | | |
| 263: | | ;; write the opening tag |
| 264: | | (define (open-tag exp) |
| 265: | | (cond |
| 266: | | ;; null tag isn't valid |
| 267: | | ((null? exp) |
| 268: | | (error "null tag")) |
| 269: | | |
| 270: | | ;; a tag must be a list beginning with a symbol |
| 271: | | ((and (pair? exp) |
| 272: | | (symbol? (car exp))) |
| 273: | | (list "<" |
| 274: | | (car exp) |
| 275: | | " " |
| 276: | | (maybe-args (car exp) (cdr exp)))) |
| 277: | | |
| 278: | | (else |
| 279: | | (error "invalid tag" exp)))) |
| 280: | | |
| 281: | | ;; take care of the keywords / arguments |
| 282: | | (define (maybe-args tag exp) |
| 283: | | |
| 284: | | (cond |
| 285: | | ;; does the rest of the list begins with a keyword |
| 286: | | ((and (pair? exp) |
| 287: | | (keyword? (car exp))) |
| 288: | | |
| 289: | | ;; does the keyword has an associated value? |
| 290: | | (if (or (null? (cdr exp)) |
| 291: | | (keyword? (cadr exp))) |
| 292: | | ;; no, we don't put an associated value |
| 293: | | (list (keyword->string (car exp)) |
| 294: | | " " |
| 295: | | (maybe-args tag (cdr exp))) |
| 296: | | ;; yes, we take the next element in the list as the value |
| 297: | | (list (keyword->string (car exp)) |
| 298: | | "=\"" |
| 299: | | (cadr exp) |
| 300: | | "\" " |
| 301: | | (maybe-args tag (cddr exp))))) |
| 302: | | |
| 303: | | ;; must just be some content |
| 304: | | (else |
| 305: | | (content tag exp)))) |
| 306: | | |
| 307: | | ;; handle the content of the tag and closing it |
| 308: | | (define (content tag exp) |
| 309: | | (cond |
| 310: | | ;; no content... |
| 311: | | ((null? exp) |
| 312: | | ;;(list "></" tag ">")) ; close as "<br></br>" |
| 313: | | (list "/>")) ; close as "<br/>" |
| 314: | | |
| 315: | | ;; write the content, handle tags inside |
| 316: | | ((pair? exp) |
| 317: | | (list ">" |
| 318: | | (map (lambda (e) |
| 319: | | (if (pair? e) |
| 320: | | (open-tag e) |
| 321: | | (to-escaped-string e))) |
| 322: | | exp) |
| 323: | | "</" |
| 324: | | tag |
| 325: | | ">")) |
| 326: | | |
| 327: | | ;; non-null terminated list? |
| 328: | | (else |
| 329: | | (error "strange content...")))) |
| 330: | | |
| 331: | | ;; we rely on Gambit's flattening of list when printed with DISPLAY |
| 332: | | (with-output-to-string "" |
| 333: | | (lambda () |
| 334: | | (display (open-tag exp))))) |