profile.scm 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734
  1. ; Part of Scheme 48 1.9. See file COPYING for notices and license.
  2. ; Authors: Marcel Turino, Manuel Dietrich, Taylor Campbell
  3. ;;;;;; Rudimentary Scheme48 profiler -*- Scheme -*-
  4. ;;; Taylor Campbell wrote parts of the original code; he has placed them in the public domain.
  5. ;; profiling information for each template
  6. (define-record-type profinfo :profinfo
  7. (really-make-profinfo template callers occurs hist memoryuse min-pc instrumented cycle)
  8. profinfo?
  9. (template profinfo-template) ; scheme code template
  10. (callers profinfo-callers profinfo-set-callers!) ; table of callerinfos
  11. (occurs profinfo-occurs profinfo-set-occurs!)
  12. (hist profinfo-hist profinfo-set-hist!)
  13. (tchild profinfo-tchild profinfo-set-tchild!)
  14. (toporder profinfo-toporder profinfo-set-toporder!)
  15. (dfn profinfo-dfn profinfo-set-dfn!) ; depth-first number
  16. (cycle profinfo-cycle profinfo-set-cycle!)
  17. (memoryuse profinfo-memoryuse profinfo-set-memoryuse!)
  18. (min-pc profinfo-min-pc profinfo-set-min-pc!)
  19. (instrumented profinfo-instrumented? profinfo-set-instrumented?!))
  20. (define (make-profinfo prof-data template)
  21. (let ((pi (really-make-profinfo template
  22. (make-table profinfo-id)
  23. 0 0 0 #f #f #f)))
  24. (table-set! (profile-data-templates prof-data) template pi)
  25. pi))
  26. ;; profiling data for template when being called by CALLER
  27. (define-record-type callerinfo :callerinfo
  28. (make-callerinfo caller calls)
  29. callerinfo?
  30. (caller callerinfo-caller) ; caller profinfo
  31. (calls callerinfo-calls callerinfo-set-calls!) ; number of calls
  32. (tself callerinfo-tself callerinfo-set-tself!) ; time spent in called self
  33. (tchild callerinfo-tchild callerinfo-set-tchild!)) ; time spent in children of called
  34. ;;; Miscellaneous global stuff
  35. (define-record-type profile-data :profile-data
  36. (really-make-profile-data)
  37. (interrupttime profile-data-interrupttime set-profile-data-interrupttime!)
  38. (measure-noninstr? profile-data-measure-noninstr? set-profile-data-measure-noninstr?!)
  39. (starttime profile-data-starttime set-profile-data-starttime!)
  40. (endtime profile-data-endtime set-profile-data-endtime!)
  41. (root profile-data-root set-profile-data-root!)
  42. (cycles profile-data-cycles set-profile-data-cycles!)
  43. (samples profile-data-samples set-profile-data-samples!)
  44. (templates profile-data-templates set-profile-data-templates!)
  45. (memoryuse profile-data-memoryuse set-profile-data-memoryuse!)
  46. (gcruns profile-data-gcruns set-profile-data-gcruns!))
  47. ;; hash function for callers table in profiling information
  48. (define (profinfo-id pi)
  49. ; (template-id (profinfo-template pi)))
  50. 0)
  51. (define (make-empty-profile-data)
  52. (let ((pd (really-make-profile-data)))
  53. (set-profile-data-interrupttime! pd (profiler-default-interrupt-time))
  54. (set-profile-data-measure-noninstr?! pd (profiler-measure-non-instrumented?))
  55. (set-profile-data-memoryuse! pd 0)
  56. (set-profile-data-cycles! pd '())
  57. pd))
  58. (define-record-type cycleinfo :cycleinfo
  59. (make-cycleinfo number members)
  60. cycleinfo?
  61. (number cycleinfo-number) ; consecutive numbering
  62. (members cycleinfo-members cycleinfo-set-members!) ; member profinfos
  63. (tchild cycleinfo-tchild cycleinfo-set-tchild!)
  64. )
  65. ;; represents a stack entry (while profiling)
  66. (define-record-type stackentry :stackentry
  67. (really-make-stackentry cont template calls firstseen seen)
  68. stackentry?
  69. (cont stackentry-cont stackentry-set-cont!) ; scheme continuation
  70. (template stackentry-template stackentry-set-template!) ; scheme code template
  71. (calls stackentry-reccalls stackentry-set-reccalls!) ; recursive calls
  72. (firstseen stackentry-firstseen stackentry-set-firstseen!) ; run-time first seen this entry
  73. (seen stackentry-seen stackentry-set-seen!)) ; seen this time? (boolean)
  74. (define (make-stackentry cont template)
  75. (really-make-stackentry cont template 0 (run-time) #f))
  76. ;;; Global profiling stuff (independent of prof-data)
  77. (define *interrupt-time* #f) ; (theoretical) ms between interrupts
  78. (define *measure-noninstr?* #f)
  79. (define *saved-interrupt-handler* #f) ; non-profiler interrupt handler
  80. (define *profiler-continuation* #f) ; profiler's top continuation
  81. (define *profiler-lock* (make-lock)) ; exclusive lock for interrupt handler
  82. (define *profiler-lastrun* #f) ; run-time of profiler runs
  83. (define *profiler-thisrun* #f)
  84. (define *start-gc-count* 0)
  85. (define *last-gc-count* 0)
  86. (define *cur-gc-count* 0)
  87. (define *last-avail-memory* 0)
  88. (define *cur-avail-memory* 0)
  89. (define interrupt/alarm (enum interrupt alarm))
  90. (define *active-profile-data* #f)
  91. (define *first-call?* #f)
  92. ;;; Sampling interrupt time (with setting)
  93. (define *profiler-default-interrupt-time* 50)
  94. (define (positive-integer? n)
  95. (and (integer? n)
  96. (exact? n)
  97. (positive? n)))
  98. (define (profiler-default-interrupt-time)
  99. *profiler-default-interrupt-time*)
  100. (define (set-profiler-default-interrupt-time! interrupt-time)
  101. (set! *profiler-default-interrupt-time* interrupt-time))
  102. (add-setting 'profiler-interrupt-time positive-integer?
  103. profiler-default-interrupt-time
  104. set-profiler-default-interrupt-time!
  105. "profiler sampling interrupt time in milliseconds")
  106. ;;; Measure-non-instrumented? flag
  107. (define *profiler-measure-non-instrumented?* #t)
  108. (define (profiler-measure-non-instrumented?)
  109. *profiler-measure-non-instrumented?*)
  110. (define (set-profiler-measure-non-instrumented?! do?)
  111. (set! *profiler-measure-non-instrumented?* do?))
  112. (add-setting 'profiler-measure-noninstr #t
  113. profiler-measure-non-instrumented?
  114. set-profiler-measure-non-instrumented?!
  115. "profiler will measure calls to non-instrumented code"
  116. "profiler will only measure calls to instrumented code")
  117. ;;; Miscellaneous global stuff
  118. (define (run-time)
  119. (primitives:time (enum time-option run-time) #f))
  120. ;; debug display
  121. (define (ddisplay x)
  122. ; (display x)
  123. #f)
  124. (define (get-profinfo prof-data stack-entry)
  125. (if stack-entry
  126. (get-profinfo-from-template prof-data (stackentry-template stack-entry))
  127. #f))
  128. (define (profiler-continuation? cont)
  129. (eq? cont *profiler-continuation*))
  130. (define (get-profinfo-from-template prof-data template)
  131. (or (table-ref (profile-data-templates prof-data) template)
  132. (make-profinfo prof-data template)))
  133. (define (get-profinfo prof-data stack-entry)
  134. (if stack-entry
  135. (get-profinfo-from-template prof-data (stackentry-template stack-entry))
  136. #f))
  137. (define (get-template-name-and-modules prof-data template)
  138. (if (eq? template (profile-data-root prof-data))
  139. (cons '<profiler> '())
  140. (let ((ddata (template-debug-data template)))
  141. (if (not (and (debug-data? ddata)
  142. (pair? (debug-data-names ddata))))
  143. (cons (string-append "anonymous"
  144. (if (debug-data? ddata)
  145. (number->string (debug-data-uid ddata))
  146. (if (number? ddata)
  147. (number->string ddata)
  148. "?")))
  149. '())
  150. (let loop ((names (debug-data-names ddata))
  151. (lst '()))
  152. (set! lst (cons (or (car names) "anonymous") lst))
  153. (if (pair? (cdr names))
  154. (loop (cdr names) lst)
  155. (reverse lst)))))))
  156. (define (same-name? a b)
  157. (string=? (if (symbol? a)
  158. (symbol->string a)
  159. a)
  160. (if (symbol? b)
  161. (symbol->string b)
  162. b)))
  163. (define (profile-data-find prof-data names)
  164. (let ((found-lst '()))
  165. (table-walk
  166. (lambda (template pi)
  167. (let loop ((names names)
  168. (tempnames (get-template-name-and-modules prof-data template)))
  169. (if (string? names)
  170. ;; only string given, search match in path
  171. (if (pair? tempnames)
  172. (if (same-name? names (car tempnames))
  173. (set! found-lst (cons pi found-lst))
  174. (loop names (cdr tempnames))))
  175. ;; list of strings given, requires full path matching
  176. (if (not (pair? names))
  177. (set! found-lst (cons pi found-lst))
  178. (if (and (pair? tempnames)
  179. (same-name? (car names) (car tempnames)))
  180. (loop (cdr names) (cdr tempnames)))))))
  181. (profile-data-templates prof-data))
  182. found-lst))
  183. (define (do-for-first-matching fun prof-data names)
  184. (let ((pis (profile-data-find prof-data names)))
  185. (if (pair? pis)
  186. (fun (car pis)))))
  187. ;;; MAIN
  188. (define (profile command . interrupt-time)
  189. (profile-and-display (if (eq? (car command) 'run)
  190. (eval `(LAMBDA () ,(cadr command))
  191. (environment-for-commands))
  192. (lambda () (execute-command command)))
  193. interrupt-time
  194. (current-output-port)))
  195. (define (profile-and-display thunk
  196. interrupt-time
  197. port)
  198. (let ((prof-data (make-empty-profile-data)))
  199. (call-with-values
  200. (lambda ()
  201. (if (null? interrupt-time)
  202. (profile-thunk prof-data thunk)
  203. (profile-thunk prof-data thunk (car interrupt-time))))
  204. (lambda results
  205. (profile-display prof-data port)
  206. (set-command-results! results)))))
  207. (define (profile-thunk prof-data thunk . opt-args)
  208. (if (not (eq? (profile-data-samples prof-data)
  209. (primitives:unspecific)))
  210. (error 'profile-thunk
  211. "a profile-data record can be used only once"))
  212. (set! *interrupt-time* #f)
  213. (set! *measure-noninstr?* (primitives:unspecific))
  214. ;; optional arguments: interrupt-time ...
  215. (case (length opt-args)
  216. ((1) ; interrupt time
  217. (let ((int-time (car opt-args)))
  218. (set! *interrupt-time* int-time)))
  219. ((2) ; interrupt time with non-instr?
  220. (let ((int-time (car opt-args))
  221. (non-instr? (cadr opt-args)))
  222. (set! *interrupt-time* int-time)
  223. (set! *measure-noninstr?* non-instr?)))
  224. )
  225. ;; profile-data interrupt time, if not set
  226. (if (not *interrupt-time*)
  227. (set! *interrupt-time* (profile-data-interrupttime prof-data)))
  228. ;; profile-data measure-noninstr?, if not set
  229. (if (eq? *measure-noninstr?* (primitives:unspecific))
  230. (set! *measure-noninstr?* (profile-data-measure-noninstr? prof-data)))
  231. (if *profiler-continuation*
  232. (error
  233. 'profile-thunk
  234. "profiler can not be running twice at the same time" thunk)
  235. (begin
  236. (set! *active-profile-data* prof-data)
  237. (set! *first-call?* #t)
  238. (set! *last-stack* #f)
  239. (set! *profiler-thisrun* #f)
  240. (set! *profiler-lastrun* #f)
  241. (set! *last-avail-memory* (available-memory))
  242. (set! *start-gc-count* (get-current-gc-count))
  243. (set! *last-gc-count* *start-gc-count*)
  244. (release-lock *profiler-lock*)
  245. ;; init profile-data
  246. (set-profile-data-templates! prof-data (make-table template-id))
  247. (set-profile-data-samples! prof-data 0)
  248. (set-profile-data-starttime! prof-data (run-time))
  249. (set-profile-data-interrupttime! prof-data *interrupt-time*)
  250. ;; this is more flexible than generating a own template
  251. (primitive-cwcc
  252. (lambda (cont)
  253. (set-profile-data-root! prof-data
  254. (continuation-template cont))))
  255. (call-with-values
  256. (lambda ()
  257. (dynamic-wind
  258. (lambda ()
  259. (install-profiler-interrupt-handler)
  260. (start-periodic-interrupts!))
  261. (lambda ()
  262. (primitive-cwcc
  263. (lambda (profiler-cont)
  264. (set! *profiler-continuation* profiler-cont)
  265. (thunk)))) ; run program!
  266. (lambda ()
  267. (set! *profiler-continuation* #f)
  268. (stop-periodic-interrupts!)
  269. (uninstall-profiler-interrupt-handler)
  270. (set-profile-data-endtime! prof-data (run-time))
  271. (set-profile-data-gcruns! prof-data (- (get-current-gc-count) *start-gc-count*))
  272. (post-process-stack! prof-data *last-stack*) ; process the last stack trace
  273. ;; do necessary calculations
  274. (remove-uncalled prof-data)
  275. (depth-numbering prof-data)
  276. (propagate-times prof-data)
  277. (toporder-numbering prof-data)
  278. (set! *active-profile-data* #f))))
  279. (lambda results
  280. (apply values results))))))
  281. ;;; INTERRUPT HANDLING
  282. (define (start-periodic-interrupts!)
  283. (schedule-interrupt *interrupt-time*))
  284. (define (stop-periodic-interrupts!)
  285. (schedule-interrupt 0))
  286. (define (install-profiler-interrupt-handler)
  287. (set! *saved-interrupt-handler* (get-interrupt-handler interrupt/alarm))
  288. (set-interrupt-handler! interrupt/alarm handle-profiler-interrupt))
  289. (define (uninstall-profiler-interrupt-handler)
  290. (let ((handler *saved-interrupt-handler*))
  291. (set! *saved-interrupt-handler* #f)
  292. (set-interrupt-handler! interrupt/alarm handler)))
  293. (define (handle-profiler-interrupt template enabled)
  294. ;; After Scheme48 1.0's architectural changes TEMPLATE argument has
  295. ;; always been just #f.
  296. ;; first thing is getting the continuation, in tail position to prevent
  297. ;; capturing profiler functions
  298. (primitive-cwcc
  299. (lambda (cont)
  300. (if (maybe-obtain-lock *profiler-lock*)
  301. (begin
  302. (*saved-interrupt-handler* template enabled) ; thread system, ...
  303. (if *profiler-continuation* (record-continuation! *active-profile-data* cont))
  304. (release-lock *profiler-lock*)
  305. ;; HACK: To override thread system interrupt scheduling, may cause
  306. ;; extreme performance loss on thread system?
  307. (start-periodic-interrupts!))))))
  308. ;;; DISPLAY DATA
  309. ;; display s right-aligned in field with width w
  310. (define (display-w s w port)
  311. (if (< (string-length s) w)
  312. (begin
  313. (display " " port)
  314. (display-w s (- w 1) port))
  315. (display s port)))
  316. ;; display number right-aligned in field with width w
  317. (define (display-w-nr n w port)
  318. (if n
  319. (display-w (number->string (round n)) w port)
  320. (display-w "?" w port)))
  321. ;; same as above, but do not display 0 values
  322. (define (display-w-nr-nz n w port)
  323. (if (= n 0)
  324. (display-w "" w port)
  325. (display-w-nr n w port)))
  326. (define (display-w-mem n w port)
  327. (if (> n 1000000000)
  328. (display-w (string-append (number->string (round (/ n 1000000))) "M") w port)
  329. (display-w (string-append (number->string (round (/ n 1000))) "k") w port)))
  330. (define (display-sep-nrs nr1 nr2 sep w port)
  331. (display-w
  332. (string-append (number->string nr1) sep (number->string nr2))
  333. w
  334. port))
  335. (define (display-sep-unequal-nrs nr1 nr2 sep w port)
  336. (display-w
  337. (if (= nr1 nr2)
  338. (number->string nr1)
  339. (string-append (number->string nr1) sep (number->string nr2)))
  340. w
  341. port))
  342. (define (display-sep-nz-nrs nr1 nr2 sep w port)
  343. (display-w
  344. (if (> nr2 0)
  345. (string-append (number->string nr1) sep (number->string nr2))
  346. (number->string nr1))
  347. w
  348. port))
  349. ;; Are there no functions for this!?
  350. (define (number-as-percent-string nr)
  351. (if nr
  352. (let* ((expanded (truncate (* 10000 nr)))
  353. (afterdot (round (inexact->exact (modulo expanded 100))))
  354. (full (round (inexact->exact (quotient (- expanded afterdot) 100)))))
  355. (string-append (number->string full)
  356. "."
  357. (number->string afterdot)
  358. "%"))
  359. "?"))
  360. (define (save/ a b)
  361. (if (= b 0)
  362. #f
  363. (/ a b)))
  364. (define (parse-port-arg opt-port)
  365. (if (null? opt-port)
  366. (current-output-port)
  367. (car opt-port)))
  368. (define (has-samples prof-data)
  369. (> (profile-data-samples prof-data) 0))
  370. (define (profile-display prof-data . opt-port)
  371. (let ((port (parse-port-arg opt-port)))
  372. (profile-display-overview prof-data port)
  373. (newline port)
  374. (profile-display-flat prof-data port)
  375. (newline port)
  376. (profile-display-tree prof-data port)))
  377. ;; general profiling data
  378. (define (profile-display-overview prof-data . opt-port)
  379. (let ((port (parse-port-arg opt-port))
  380. (run-time (profile-data-runtime prof-data))
  381. (samples (profile-data-samples prof-data)))
  382. (display "** Samples: " port)
  383. (display samples port)
  384. (if (has-samples prof-data)
  385. (begin
  386. (display " (approx. one per " port)
  387. (display (round (/ run-time samples)) port)
  388. (display "ms)")))
  389. (newline port)
  390. (display "** Interrupt time: " port)
  391. (display *interrupt-time* port)
  392. (display "ms" port)
  393. (newline port)
  394. (display "** Real run time: " port)
  395. (display run-time port)
  396. (display "ms" port)
  397. (newline port)
  398. (if (has-samples prof-data)
  399. (begin
  400. (display "** Total memory: " port)
  401. (display (round (/ (profile-data-memoryuse prof-data) 1000)) port)
  402. (display "k" port)
  403. (newline port)
  404. (display "** GC runs: " port)
  405. (display (profile-data-gcruns prof-data) port)
  406. (newline port)))))
  407. (define (profile-display-flat prof-data . opt-port)
  408. (let ((port (parse-port-arg opt-port)))
  409. (display "** Flat result (times in ms):" port)
  410. (newline port)
  411. (newline port)
  412. ;; gprof:
  413. ;; % cumulative self self total
  414. ;; time seconds seconds calls ms/call ms/call name
  415. (if (has-samples prof-data)
  416. (begin
  417. (display-w "time" 7 port)
  418. (display-w "cumu" 7 port)
  419. (display-w "self" 7 port)
  420. (display-w "mem" 10 port)))
  421. (display-w "calls" 14 port)
  422. (display-w "ms/call" 9 port)
  423. (display-w "name" 7 port)
  424. (newline port)
  425. ;; sort and print
  426. (let ((sorted-templates
  427. (get-sorted-templates prof-data (lambda (pi) (- (profinfo-hist pi))) #t)))
  428. (for-each (lambda (profinfo)
  429. (profile-display-profinfo-flat prof-data profinfo port))
  430. sorted-templates))))
  431. ;; display data "gprof call graph"-like
  432. (define (profile-display-tree prof-data . opt-port)
  433. (let ((port (parse-port-arg opt-port))
  434. (cycles (profile-data-cycles prof-data)))
  435. (display "** Tree result (times in ms):" port)
  436. (newline port)
  437. (newline port)
  438. (display-w "i" 3 port)
  439. (if (has-samples prof-data)
  440. (begin
  441. (display-w "time" 8 port)
  442. (display-w "self" 7 port)
  443. (display-w "child" 7 port)
  444. (display-w "mem" 10 port)))
  445. (display-w "calls" 14 port)
  446. (display-w "name" 7 port)
  447. (newline port)
  448. ;; sort and print
  449. (let ((sorted-templates
  450. (get-sorted-templates prof-data (lambda (pi) (- (profinfo-occurs pi))) #t)))
  451. (for-each (lambda (profinfo)
  452. (profile-display-profinfo-tree prof-data profinfo port))
  453. sorted-templates))
  454. (if cycles
  455. (for-each (lambda (cyc)
  456. (profile-display-cycle-tree prof-data cyc port))
  457. cycles))))
  458. (define (profile-function-calls prof-data names)
  459. (do-for-first-matching profinfo-total-calls prof-data names))
  460. (define (profile-function-reccalls prof-data names)
  461. (do-for-first-matching profinfo-total-reccalls prof-data names))
  462. (define (profile-function-nonreccalls prof-data names)
  463. (do-for-first-matching profinfo-total-nonreccalls prof-data names))
  464. (define (profile-function-occurs prof-data names)
  465. (do-for-first-matching profinfo-occurs prof-data names))
  466. (define (profile-function-hist prof-data names)
  467. (do-for-first-matching profinfo-hist prof-data names))
  468. (define (profile-function-memoryuse prof-data names)
  469. (do-for-first-matching profinfo-memoryuse prof-data names))
  470. (define (profile-function-timeshare prof-data names)
  471. (do-for-first-matching (lambda (pi) (profinfo-timeshare prof-data pi)) prof-data names))
  472. (define (profile-function-time-cumulative prof-data names)
  473. (do-for-first-matching (lambda (pi) (profinfo-total-ms prof-data pi)) prof-data names))
  474. (define (profile-function-time-self prof-data names)
  475. (do-for-first-matching (lambda (pi) (profinfo-self-ms prof-data pi)) prof-data names))
  476. (define (profile-display-function-flat prof-data names . opt-port)
  477. (let ((port (parse-port-arg opt-port))
  478. (pis (profile-data-find prof-data names)))
  479. (for-each (lambda (pi)
  480. (profile-display-profinfo-flat prof-data pi port))
  481. pis)))
  482. (define (profile-display-profinfo-flat prof-data profinfo port)
  483. (let* ((template (profinfo-template profinfo))
  484. (occurs (profinfo-occurs profinfo))
  485. (calls (profinfo-total-calls profinfo))
  486. (reccalls (profinfo-total-reccalls profinfo))
  487. (nonreccalls (profinfo-total-nonreccalls profinfo))
  488. (hist (profinfo-hist profinfo))
  489. (memuse (profinfo-memoryuse profinfo))
  490. (timeshare (profinfo-timeshare prof-data profinfo))
  491. (ttotal (profinfo-total-ms prof-data profinfo))
  492. (tself (profinfo-self-ms prof-data profinfo))
  493. (ms/call (save/ (occurs->ms prof-data occurs) calls)))
  494. (if (not (eq? template (profile-data-root prof-data)))
  495. (begin
  496. (if (has-samples prof-data)
  497. (begin
  498. (display-w (number-as-percent-string timeshare) 7 port)
  499. (display-w-nr ttotal 7 port)
  500. (display-w-nr tself 7 port)
  501. (display-w-mem memuse 10 port)))
  502. (display-sep-nz-nrs nonreccalls reccalls "+" 14 port)
  503. (display-w-nr ms/call 9 port)
  504. (display " " port)
  505. (display-location prof-data template port) ; name
  506. (newline port)
  507. ))))
  508. (define (profile-display-function-cycle prof-data names . opt-port)
  509. (let ((port (parse-port-arg opt-port))
  510. (pis (profile-data-find prof-data names)))
  511. (for-each (lambda (pi)
  512. (let ((ci (profinfo-cycle pi)))
  513. (if ci
  514. (profile-display-cycle-tree prof-data ci port))))
  515. pis)))
  516. (define (profile-display-cycle-tree prof-data cycleinfo port)
  517. (let* ((number (cycleinfo-number cycleinfo))
  518. (members (cycleinfo-members cycleinfo))
  519. (callers (cycleinfo-called-from cycleinfo))
  520. (intcalls (cycleinfo-internal-calls cycleinfo))
  521. (extcalls (cycleinfo-external-calls cycleinfo))
  522. (hist (cycleinfo-hist cycleinfo))
  523. (tchild (cycleinfo-tchild cycleinfo))
  524. (memuse (cycleinfo-memoryuse cycleinfo))
  525. (fromextcalls (sumup-calls-int/ext-cycle cycleinfo #f))
  526. (ttotal (+ hist tchild))
  527. (timeshare (save/ ttotal (profile-data-samples prof-data))))
  528. (display "=============================================" port)
  529. (display "=============================================" port)
  530. (newline port)
  531. ;; print cycle callers
  532. (for-each
  533. (lambda (caller-pi)
  534. (let* ((calls (cycleinfo-calls-from cycleinfo caller-pi))
  535. (share (/ calls fromextcalls))
  536. (tchild (* tchild share))
  537. (memuse (* memuse share)))
  538. (display-w "" 3 port)
  539. (if (has-samples prof-data)
  540. (begin
  541. (display-w "" 8 port)
  542. (display-w-nr (occurs->ms prof-data hist) 7 port)
  543. (display-w-nr (occurs->ms prof-data tchild) 7 port)
  544. (display-w-mem memuse 10 port)))
  545. (display-sep-nz-nrs calls fromextcalls "/" 14 port)
  546. (display " " port)
  547. (display-profinfo-name prof-data caller-pi port)
  548. (newline port)))
  549. callers)
  550. ;; print primary line
  551. (display-w-nr number 3 port)
  552. (if (has-samples prof-data)
  553. (begin
  554. (display-w (number-as-percent-string timeshare) 8 port)
  555. (display-w-nr (occurs->ms prof-data hist) 7 port)
  556. (display-w-nr (occurs->ms prof-data tchild) 7 port)
  557. (display-w-mem memuse 10 port)))
  558. (display-sep-nz-nrs extcalls intcalls "+" 14 port)
  559. (display " " port)
  560. (display "<cycle " port)
  561. (display number port)
  562. (display " as a whole>" port)
  563. (newline port)
  564. ;; print cycle members
  565. (for-each
  566. (lambda (member-pi)
  567. (let* ((intcalls (calls-int/ext-cycle cycleinfo member-pi #t))
  568. (nonreccalls (profinfo-total-nonreccalls member-pi))
  569. (totalmemuse (profinfo-memoryuse member-pi))
  570. (occurs (profinfo-occurs member-pi))
  571. (hist (profinfo-hist member-pi))
  572. (tchild (cycleinfo-tchild-member prof-data cycleinfo member-pi))
  573. (share (/ intcalls nonreccalls))
  574. (memuse (* totalmemuse share)))
  575. (display-w "" 3 port)
  576. (if (has-samples prof-data)
  577. (begin
  578. (display-w "" 8 port)
  579. (display-w-nr (occurs->ms prof-data hist) 7 port)
  580. (display-w-nr (occurs->ms prof-data tchild) 7 port)
  581. (display-w-mem memuse 10 port)))
  582. (display-w-nr intcalls 14 port)
  583. (display " " port)
  584. (display-profinfo-name prof-data member-pi port)
  585. (newline port)))
  586. members)
  587. ;; print functions called out of the cycle
  588. (for-each
  589. (lambda (called-pi)
  590. (let* ((nonreccalls (profinfo-total-nonreccalls called-pi))
  591. (totalmemuse (profinfo-memoryuse called-pi))
  592. (calls (cycleinfo-calls-to cycleinfo called-pi))
  593. (share (/ calls nonreccalls))
  594. (memuse (* totalmemuse share)))
  595. (display-w "" 3 port)
  596. (if (has-samples prof-data)
  597. (begin
  598. (display-w "" 8 port)
  599. (display-w-nr 0 7 port)
  600. (display-w-nr 0 7 port)
  601. (display-w-mem memuse 10 port)))
  602. (display-sep-nrs calls nonreccalls "/" 14 port)
  603. (display " " port)
  604. (display-profinfo-name prof-data called-pi port)
  605. (newline port)))
  606. (cycleinfo-called-externals prof-data cycleinfo))))
  607. (define (profile-display-function-tree prof-data names . opt-port)
  608. (let ((port (parse-port-arg opt-port))
  609. (pis (profile-data-find prof-data names)))
  610. (for-each (lambda (pi)
  611. (profile-display-profinfo-tree prof-data pi port))
  612. pis)))
  613. (define (profile-display-profinfo-tree prof-data primary-pi port)
  614. (let* ((template (profinfo-template primary-pi))
  615. (toporder (profinfo-toporder primary-pi))
  616. (dfn (profinfo-dfn primary-pi))
  617. (callers (profinfo-callers primary-pi))
  618. (occurs (profinfo-occurs primary-pi))
  619. (calls (profinfo-total-calls primary-pi))
  620. (reccalls (profinfo-total-reccalls primary-pi))
  621. (nonreccalls (profinfo-total-nonreccalls primary-pi))
  622. (memuse (profinfo-memoryuse primary-pi))
  623. (upcalls (profinfo-total-upcalls primary-pi))
  624. (hist (profinfo-hist primary-pi))
  625. (tchild (profinfo-tchild primary-pi))
  626. (primary-cyc (profinfo-cycle primary-pi))
  627. (timeshare (save/ occurs (profile-data-samples prof-data)))
  628. (ms/call (save/ (occurs->ms prof-data occurs) calls)))
  629. (display "=============================================" port)
  630. (display "=============================================" port)
  631. (newline port)
  632. ;; print parents
  633. (if (= (table-size callers) 0)
  634. (if (not (eq? template (profile-data-root prof-data)))
  635. (begin (display-w " " 49 port) (display " <spontaneous>" port) (newline)))
  636. (table-walk
  637. (lambda (caller-pi cinfo)
  638. (if (not (eq? caller-pi primary-pi))
  639. (let* ((template (profinfo-template caller-pi))
  640. (dfn (profinfo-dfn caller-pi))
  641. (occurs (profinfo-occurs caller-pi))
  642. (caller-cyc (profinfo-cycle caller-pi))
  643. (calls (callerinfo-calls cinfo))
  644. (share (/ calls upcalls))
  645. (tself-share (* hist share)) ; TODO: correct when recursive function?
  646. (tchild-share (* tchild share))
  647. (memuse-share (* memuse share)))
  648. (display-w "" 3 port)
  649. (if (has-samples prof-data)
  650. (begin
  651. (display-w "" 8 port)
  652. (if (or (not primary-cyc)
  653. (not (eq? caller-cyc primary-cyc)))
  654. (begin
  655. (display-w-nr (occurs->ms prof-data tself-share) 7 port)
  656. (display-w-nr (occurs->ms prof-data tchild-share) 7 port)
  657. (display-w-mem memuse-share 10 port))
  658. (begin
  659. (display-w "" 7 port)
  660. (display-w "" 7 port)
  661. (display-w "" 10 port)))))
  662. (display-sep-nrs calls nonreccalls "/" 14 port)
  663. (display " " port)
  664. (display-profinfo-name prof-data caller-pi port)
  665. (newline port))))
  666. callers))
  667. ;; print primary line
  668. (display-w-nr toporder 3 port)
  669. (if (has-samples prof-data)
  670. (begin
  671. (display-w (number-as-percent-string timeshare) 8 port)
  672. (display-w-nr (occurs->ms prof-data hist) 7 port)
  673. (display-w-nr (occurs->ms prof-data tchild) 7 port)
  674. (display-w-mem memuse 10 port)))
  675. (display-sep-nz-nrs nonreccalls reccalls "+" 14 port)
  676. (display " " port)
  677. (display-profinfo-name prof-data primary-pi port)
  678. (newline port)
  679. ;; print children
  680. (for-each
  681. (lambda (called-pi)
  682. (if (not (eq? called-pi primary-pi))
  683. (let* ((template (profinfo-template called-pi))
  684. (dfn (profinfo-dfn called-pi))
  685. (occurs (profinfo-occurs called-pi))
  686. (calls (number-of-calls primary-pi called-pi))
  687. (nonreccalls (profinfo-total-nonreccalls called-pi))
  688. (upcalls (profinfo-upcalls primary-pi called-pi))
  689. (hist (profinfo-hist called-pi))
  690. (tchild (profinfo-tchild called-pi))
  691. (called-cyc (profinfo-cycle called-pi))
  692. (memuse (profinfo-memoryuse called-pi))
  693. (share (/ calls upcalls))
  694. (tself-share (* hist share)) ; TODO: correct when recursive function?
  695. (tchild-share (* tchild share))
  696. (memuse-share (* memuse share)))
  697. (display-w "" 3 port)
  698. (if (has-samples prof-data)
  699. (begin
  700. (display-w "" 8 port)
  701. (if (or (not called-cyc)
  702. (not (eq? called-cyc primary-cyc)))
  703. (begin
  704. (display-w-nr (occurs->ms prof-data tself-share) 7 port)
  705. (display-w-nr (occurs->ms prof-data tchild-share) 7 port)
  706. (display-w-mem memuse-share 10 port))
  707. (begin
  708. (display-w "" 7 port)
  709. (display-w "" 7 port)
  710. (display-w "" 10 port)))))
  711. (display-sep-nrs calls nonreccalls "/" 14 port)
  712. (display " " port)
  713. (display-profinfo-name prof-data called-pi port)
  714. (newline port))))
  715. (profinfo-calls prof-data primary-pi))))
  716. ;; displays functionname and file of a code template
  717. (define (display-location prof-data template port)
  718. (let loop ((names (get-template-name-and-modules prof-data template)))
  719. (if (string? (car names)) (display "\"" port))
  720. (display (car names) port)
  721. (if (string? (car names)) (display "\"" port))
  722. (if (pair? (cdr names))
  723. (begin (display " in " port)
  724. (loop (cdr names))))))
  725. (define (display-profinfo-name prof-data pi port)
  726. (let* ((template (profinfo-template pi))
  727. (ton (profinfo-toporder pi))
  728. (cyc (profinfo-cycle pi)))
  729. (display-location prof-data template port)
  730. (if cyc
  731. (begin
  732. (display " <cycle " port)
  733. (display (cycleinfo-number cyc))
  734. (display ">" port)))
  735. (display " [" port)
  736. (display ton port)
  737. (display "]" port)))
  738. ;;; useful stuff
  739. (define (memq? x l)
  740. (let loop ((l l))
  741. (cond ((null? l) #f)
  742. ((eq? x (car l)) #t)
  743. (else (loop (cdr l))))))
  744. (define (remove-duplicates list)
  745. (do ((list list (cdr list))
  746. (res '() (if (memq? (car list) res)
  747. res
  748. (cons (car list) res))))
  749. ((null? list)
  750. res)))
  751. ;;; DATA CALCULATION
  752. (define (occurs->ms prof-data occs)
  753. (if (has-samples prof-data)
  754. (round (/ (* occs (profile-data-runtime prof-data))
  755. (profile-data-samples prof-data)))
  756. 0))
  757. (define (profile-data-runtime prof-data)
  758. (let ((st (profile-data-starttime prof-data))
  759. (et (profile-data-endtime prof-data)))
  760. (if (or (eq? st (primitives:unspecific))
  761. (eq? et (primitives:unspecific)))
  762. (primitives:unspecific)
  763. (- et st))))
  764. ;;; cycle stuff
  765. (define (make-new-cycleinfo prof-data)
  766. (let ((new (make-cycleinfo (length (profile-data-cycles prof-data)) '())))
  767. new))
  768. (define (cycleinfo-add prof-data ci)
  769. (if (not (memq? ci (profile-data-cycles prof-data)))
  770. (set-profile-data-cycles! prof-data (cons ci (profile-data-cycles prof-data)))))
  771. (define (cycleinfo-add-member ci member)
  772. (let ((members (cycleinfo-members ci)))
  773. (if (not (memq? member members))
  774. (cycleinfo-set-members! ci (cons member members)))))
  775. ;; is profinfo a member of cycle ci?
  776. (define (cycleinfo-member? ci profinfo)
  777. (memq? profinfo
  778. (cycleinfo-members ci)))
  779. (define (cycleinfo-foreach-member ci f)
  780. (for-each f (cycleinfo-members ci)))
  781. ;; number of calls to function called-pi from cycle or from outside of cycle
  782. (define (calls-int/ext-cycle ci called-pi internal)
  783. (let ((cnt-calls 0)
  784. (caller-list (profinfo-callers called-pi)))
  785. (table-walk (lambda (caller-pi cinfo)
  786. (if (and (eq? (cycleinfo-member? ci caller-pi)
  787. internal)
  788. (not (eq? caller-pi called-pi)))
  789. (set! cnt-calls (+ cnt-calls (callerinfo-calls cinfo)))))
  790. caller-list)
  791. cnt-calls))
  792. ;; sum up internal calls of the cycle or calls from outside into the cycle
  793. (define (sumup-calls-int/ext-cycle ci internal)
  794. (let ((cnt-calls 0))
  795. (cycleinfo-foreach-member
  796. ci
  797. (lambda (member-pi)
  798. (set! cnt-calls (+ cnt-calls (calls-int/ext-cycle ci member-pi internal)))))
  799. cnt-calls))
  800. ;; calls done in the cycle internally
  801. (define (cycleinfo-internal-calls ci)
  802. (sumup-calls-int/ext-cycle ci #t))
  803. ;; calls done from outside into the cycle
  804. (define (cycleinfo-external-calls ci)
  805. (sumup-calls-int/ext-cycle ci #f))
  806. ;; time spent in the functions of the cycle itself
  807. (define (cycleinfo-hist ci)
  808. (let ((tt 0))
  809. (cycleinfo-foreach-member
  810. ci
  811. (lambda (pi)
  812. (set! tt (+ tt (profinfo-hist pi)))))
  813. tt))
  814. (define (cycleinfo-memoryuse ci)
  815. (let ((tt 0))
  816. (cycleinfo-foreach-member
  817. ci
  818. (lambda (pi)
  819. (set! tt (+ tt (profinfo-memoryuse pi)))))
  820. tt))
  821. ;; list of function profinfos the called cycle ci
  822. (define (cycleinfo-called-from ci)
  823. (let ((lst '()))
  824. (cycleinfo-foreach-member
  825. ci
  826. (lambda (member-pi)
  827. (let ((caller-list (profinfo-callers member-pi)))
  828. ;; add share of every function called from this cycle-function to total
  829. (table-walk (lambda (caller-pi cinfo)
  830. (if (and (not (cycleinfo-member? ci caller-pi))
  831. (not (memq? caller-pi lst)))
  832. (set! lst (cons caller-pi lst))))
  833. caller-list))))
  834. lst))
  835. ;; list of function profinfos called from cycle ci
  836. (define (cycleinfo-called-externals prof-data ci)
  837. (let ((lst '()))
  838. (cycleinfo-foreach-member
  839. ci
  840. (lambda (member-pi)
  841. (let ((called-list (profinfo-calls prof-data member-pi)))
  842. ;; add share of every function called from this cycle-function to total
  843. (for-each (lambda (called-pi)
  844. (if (and (not (cycleinfo-member? ci called-pi))
  845. (not (memq? called-pi lst)))
  846. (set! lst (cons called-pi lst))))
  847. called-list))))
  848. lst))
  849. ;; calls from cycle ci to some other function
  850. (define (cycleinfo-calls-to ci called-pi)
  851. (let ((cnt-calls 0))
  852. (cycleinfo-foreach-member
  853. ci
  854. (lambda (member-pi)
  855. (set! cnt-calls (+ cnt-calls
  856. (number-of-calls member-pi called-pi)))))
  857. cnt-calls))
  858. ;; calls to cycle ci from some other function
  859. (define (cycleinfo-calls-from ci caller-pi)
  860. (let ((cnt-calls 0))
  861. (cycleinfo-foreach-member
  862. ci
  863. (lambda (member-pi)
  864. (set! cnt-calls (+ cnt-calls
  865. (number-of-calls caller-pi member-pi)))))
  866. cnt-calls))
  867. ;; time spent in functions outside the cycle called from member-pi
  868. (define (cycleinfo-tchild-member prof-data ci member-pi)
  869. (let ((tt 0)
  870. (called-list (profinfo-calls prof-data member-pi)))
  871. ;; add share of every function called from this cycle-function to total
  872. (for-each (lambda (called-pi)
  873. (if (and (not (eq? called-pi
  874. member-pi))
  875. (not (cycleinfo-member? ci called-pi)))
  876. (let* ((thiscalls (number-of-calls member-pi called-pi))
  877. (totalcalls (profinfo-total-nonreccalls called-pi))
  878. (occs (profinfo-occurs called-pi))
  879. (share (/ (* occs thiscalls)
  880. totalcalls)))
  881. (set! tt (+ tt share)))))
  882. called-list)
  883. tt))
  884. (define (get-callerinfo caller called)
  885. (let* ((caller-list (profinfo-callers called))
  886. (cinfo (table-ref caller-list caller)))
  887. cinfo))
  888. (define (number-of-calls caller called)
  889. (let ((cinfo (get-callerinfo caller called)))
  890. (if cinfo
  891. (callerinfo-calls cinfo)
  892. 0)))
  893. ;; total number of calls from caller to the member or its whole cycle
  894. ;; (without recursive and cyclic)
  895. (define (profinfo-upcalls caller-pi called-pi)
  896. (let* ((cyc-called (profinfo-cycle called-pi))
  897. (nonrec-calls (profinfo-total-nonreccalls called-pi)))
  898. (if cyc-called
  899. (cycleinfo-calls-from cyc-called caller-pi)
  900. nonrec-calls)))
  901. ;; total number of calls from caller to the member or its whole cycle
  902. ;; (without recursive and cyclic)
  903. (define (profinfo-total-upcalls called-pi)
  904. (let* ((cyc-called (profinfo-cycle called-pi))
  905. (nonrec-calls (profinfo-total-nonreccalls called-pi)))
  906. (if cyc-called
  907. (sumup-calls-int/ext-cycle cyc-called #f)
  908. nonrec-calls)))
  909. ;; number of calls from inside of it's own cycle
  910. (define (profinfo-total-cycliccalls pi)
  911. (let ((cyc (profinfo-cycle pi)))
  912. (if cyc
  913. (calls-int/ext-cycle cyc pi #t)
  914. 0)))
  915. (define (profinfo-timeshare prof-data profinfo)
  916. (let ((hist (profinfo-hist profinfo)))
  917. (save/ hist (profile-data-samples prof-data))))
  918. (define (profinfo-total-ms prof-data profinfo)
  919. (let ((occurs (profinfo-occurs profinfo)))
  920. (occurs->ms prof-data occurs)))
  921. (define (profinfo-self-ms prof-data profinfo)
  922. (let ((hist (profinfo-hist profinfo)))
  923. (occurs->ms prof-data hist)))
  924. ;; returns a list of all profinfos the function calls
  925. (define (profinfo-calls prof-data caller-pi)
  926. (let ((lst '()))
  927. (table-walk (lambda (template called-pi)
  928. (if (> (number-of-calls caller-pi called-pi) 0)
  929. (set! lst (cons called-pi lst))))
  930. (profile-data-templates prof-data))
  931. (remove-duplicates lst)))
  932. ;; total non-recursive calls of this function
  933. (define (profinfo-total-nonreccalls pi)
  934. (- (profinfo-total-calls pi)
  935. (profinfo-total-reccalls pi)))
  936. ;; total recursive calls of this function
  937. (define (profinfo-total-reccalls pi)
  938. (let* ((cs (profinfo-callers pi))
  939. (info (table-ref cs pi)))
  940. (if info
  941. (callerinfo-calls info)
  942. 0)))
  943. ;; total number of calls (with recursive)
  944. (define (profinfo-total-calls pi)
  945. (let ((cs (profinfo-callers pi))
  946. (total 0))
  947. (table-walk (lambda (key cinfo)
  948. (set! total (+ total (callerinfo-calls cinfo))))
  949. cs)
  950. total))
  951. (define (get-sorted-templates prof-data property filter-noncalled?)
  952. (let ((lst '()))
  953. (table-walk (lambda (template profinfo)
  954. (if (or (not filter-noncalled?)
  955. (> (profinfo-total-calls profinfo) 0))
  956. (set! lst (cons profinfo lst))))
  957. (profile-data-templates prof-data))
  958. (set! lst (sort-list lst
  959. (lambda (a b)
  960. (< (property a)
  961. (property b)))))
  962. lst))
  963. (define (propagate-time-from-children prof-data caller-pi)
  964. (ddisplay "progating time for ")
  965. (ddisplay (profinfo-template caller-pi))
  966. (ddisplay " from children...\n")
  967. (let ((called-list (profinfo-calls prof-data caller-pi)))
  968. (for-each
  969. (lambda (called-pi)
  970. (let* ((cinfo (get-callerinfo caller-pi called-pi))
  971. (called-cyc (profinfo-cycle called-pi))
  972. (caller-cyc (profinfo-cycle caller-pi))
  973. (calls (callerinfo-calls cinfo))
  974. (share 0)
  975. (childshare 0))
  976. (ddisplay (profinfo-template caller-pi))
  977. (ddisplay " --> ")
  978. (ddisplay (profinfo-template called-pi))
  979. (if (and (not (eq? caller-pi called-pi))
  980. (or (not called-cyc) (not (eq? called-cyc caller-cyc))))
  981. (begin
  982. (let ((ctself
  983. (if called-cyc
  984. (cycleinfo-hist called-cyc)
  985. (profinfo-hist called-pi)))
  986. (ctchild
  987. (if called-cyc
  988. (cycleinfo-tchild called-cyc)
  989. (profinfo-tchild called-pi)))
  990. (nonreccalls
  991. (if called-cyc
  992. (cycleinfo-external-calls called-cyc)
  993. (profinfo-total-nonreccalls called-pi))))
  994. (ddisplay " ctself: ")
  995. (ddisplay ctself)
  996. (ddisplay ", ctchild: ")
  997. (ddisplay ctchild)
  998. (ddisplay ", nrc: ")
  999. (ddisplay nonreccalls)
  1000. (set! share (/ (* ctself calls) nonreccalls))
  1001. (set! childshare (/ (* ctchild calls) nonreccalls))
  1002. )))
  1003. (ddisplay ", calls ")
  1004. (ddisplay (round calls))
  1005. (ddisplay ", share ")
  1006. (ddisplay (round share))
  1007. (ddisplay ", childshare ")
  1008. (ddisplay (round childshare))
  1009. (ddisplay "\n")
  1010. ;; add shares to arc information
  1011. (callerinfo-set-tself! cinfo share)
  1012. (callerinfo-set-tchild! cinfo childshare)
  1013. ;; add everything to child share for parent
  1014. (profinfo-set-tchild! caller-pi
  1015. (+ (profinfo-tchild caller-pi)
  1016. (+ share childshare)))
  1017. (if caller-cyc
  1018. (cycleinfo-set-tchild! caller-cyc
  1019. (+ (cycleinfo-tchild caller-cyc)
  1020. (+ share childshare))))
  1021. ))
  1022. called-list)))
  1023. (define (propagate-times prof-data)
  1024. ;; zero out
  1025. (table-walk (lambda (template profinfo)
  1026. (profinfo-set-tchild! profinfo 0))
  1027. (profile-data-templates prof-data))
  1028. (for-each (lambda (cyc)
  1029. (cycleinfo-set-tchild! cyc 0))
  1030. (profile-data-cycles prof-data))
  1031. (for-each (lambda (template)
  1032. (propagate-time-from-children prof-data template))
  1033. (get-sorted-templates prof-data (lambda (pi) (- (profinfo-dfn pi))) #f)))
  1034. ;;; number function by their depth in the call stack
  1035. (define (profinfo-dfn-set? pi)
  1036. (number? (profinfo-dfn pi)))
  1037. (define (profinfo-dfn-busy? pi)
  1038. (eq? (profinfo-dfn pi) 'busy))
  1039. (define (build-cycle prof-data dfn-stack top-pi)
  1040. ;; is it just a recursive call?
  1041. (if (not (eq? (car dfn-stack) top-pi))
  1042. (begin
  1043. ;; move down the stack till we find ourselves again, adding
  1044. ;; every function to our cycle
  1045. (let ((cyc (make-new-cycleinfo prof-data)))
  1046. (let loop ((stack dfn-stack))
  1047. (let* ((pi (car stack))
  1048. (pi-cyc (profinfo-cycle pi)))
  1049. (cycleinfo-add-member cyc pi)
  1050. ;; if this function is in a cycle already, we all belong to this cycle too
  1051. (if pi-cyc
  1052. (begin
  1053. ;; copy members to this cycle
  1054. (for-each (lambda (memb)
  1055. (cycleinfo-add-member pi-cyc memb))
  1056. (cycleinfo-members cyc))
  1057. (set! cyc pi-cyc)))
  1058. (if (and (not (null? (cdr stack)))
  1059. (not (eq? pi top-pi)))
  1060. (loop (cdr stack)))))
  1061. ;; add cycle globally
  1062. (cycleinfo-add prof-data cyc)
  1063. ;; update cycle information in profinfos
  1064. (for-each (lambda (memb)
  1065. (profinfo-set-cycle! memb cyc))
  1066. (cycleinfo-members cyc))
  1067. ))))
  1068. (define (toporder-numbering prof-data)
  1069. (let ((sorted-templates
  1070. (get-sorted-templates prof-data (lambda (pi) (- (profinfo-occurs pi))) #f))
  1071. (toporder 0))
  1072. (for-each (lambda (profinfo)
  1073. (profinfo-set-toporder! profinfo toporder)
  1074. (set! toporder (+ toporder 1)))
  1075. sorted-templates)))
  1076. (define (remove-uncalled prof-data)
  1077. (let ((tab (profile-data-templates prof-data)))
  1078. (table-walk (lambda (template profinfo)
  1079. (if (and (= (profinfo-total-calls profinfo) 0)
  1080. (not (eq? template (profile-data-root prof-data))))
  1081. (table-set! tab template #f)))
  1082. tab)))
  1083. ;;; numbers all functions by their depth in the call stack
  1084. (define (depth-numbering prof-data)
  1085. (let ((dfn-counter (table-size (profile-data-templates prof-data))))
  1086. (letrec ((depth-number-function
  1087. (lambda (dfn-stack cur-pi)
  1088. ;; already set?
  1089. (if (not (profinfo-dfn-set? cur-pi))
  1090. (begin
  1091. ;; is it busy? must be a cycle
  1092. (if (profinfo-dfn-busy? cur-pi)
  1093. (build-cycle prof-data dfn-stack cur-pi)
  1094. ;; no cycle
  1095. (begin
  1096. ;; pre-visit
  1097. (profinfo-set-dfn! cur-pi 'busy)
  1098. ;; process children
  1099. (for-each (lambda (called-pi)
  1100. (depth-number-function (cons cur-pi dfn-stack)
  1101. called-pi))
  1102. (profinfo-calls prof-data cur-pi))
  1103. (set! dfn-counter (- dfn-counter 1))
  1104. ;; post-visit
  1105. (profinfo-set-dfn! cur-pi dfn-counter)
  1106. )))))))
  1107. ;; zero out
  1108. (table-walk (lambda (template profinfo)
  1109. (profinfo-set-dfn! profinfo 'notset)
  1110. (profinfo-set-cycle! profinfo #f))
  1111. (profile-data-templates prof-data))
  1112. (table-walk (lambda (template profinfo)
  1113. (depth-number-function '() profinfo))
  1114. (profile-data-templates prof-data)))))
  1115. ;; find root and number from there
  1116. ; (if (profile-data-root prof-data)
  1117. ; (let ((root-pi (get-profinfo-from-template prof-data (profile-data-root prof-data))))
  1118. ; (if root-pi
  1119. ; (depth-number-function '() root-pi)))))))
  1120. ;;; RECORDING DATA (while target is running)
  1121. (define *last-stack* #f) ; stack at last interrupt
  1122. (define *cur-stack* #f) ; stack at this interrupt (to be built)
  1123. (define (last-stackentry)
  1124. (if (null? *cur-stack*)
  1125. #f
  1126. (car *cur-stack*)))
  1127. ;; adds one call to the profinfo of CALLED
  1128. (define (profinfo-count-call called caller)
  1129. (if (and called caller)
  1130. (let ((cs (profinfo-callers called)))
  1131. (cond ((table-ref cs caller)
  1132. => (lambda (ci)
  1133. (callerinfo-set-calls! ci (+ 1 (callerinfo-calls ci)))))
  1134. (else
  1135. (table-set! cs caller (make-callerinfo caller 1)))))))
  1136. ;; duplicate from sort/vector-util
  1137. (define (has-element list index)
  1138. (cond
  1139. ((zero? index)
  1140. (if (pair? list)
  1141. (values #t (car list))
  1142. (values #f #f)))
  1143. ((null? list)
  1144. (values #f #f))
  1145. (else
  1146. (has-element (cdr list) (- index 1)))))
  1147. (define (list-ref-or-default list index default)
  1148. (if list
  1149. (call-with-values
  1150. (lambda () (has-element list index))
  1151. (lambda (has? maybe)
  1152. (if has?
  1153. maybe
  1154. default)))
  1155. default))
  1156. (define (set-unseen-all!)
  1157. (and *last-stack*
  1158. (for-each (lambda (se)
  1159. (stackentry-set-seen! se #f))
  1160. *last-stack*)))
  1161. (define (seen? stackentry)
  1162. (and stackentry
  1163. (stackentry-seen stackentry)))
  1164. (define (seen! old-se se)
  1165. (if old-se
  1166. (begin
  1167. (stackentry-set-firstseen! se (stackentry-firstseen old-se))
  1168. (stackentry-set-seen! old-se #t))))
  1169. (define (time-passed se)
  1170. (let* ((firstseen (stackentry-firstseen se))
  1171. (mid (if *profiler-lastrun*
  1172. (- *profiler-thisrun*
  1173. *profiler-lastrun*)
  1174. 0))
  1175. (passed (- *profiler-thisrun*
  1176. firstseen)))
  1177. (- passed (/ mid 2))))
  1178. ;; process the stack entries that have the seen "bit" not set.
  1179. (define (post-process-stack! prof-data call-stack)
  1180. (let ((gone-stackentries '()))
  1181. (if call-stack
  1182. (let loop ((stack call-stack)
  1183. (caller-se #f)
  1184. (seen-templates '()))
  1185. (if (not (null? stack))
  1186. (let* ((called-se (car stack))
  1187. (called-pi (get-profinfo prof-data called-se))
  1188. (template (stackentry-template called-se))
  1189. (reccalls (stackentry-reccalls called-se)))
  1190. (if (and (= reccalls 0)
  1191. (not (memq? template seen-templates)))
  1192. (begin
  1193. ;; record occurrence
  1194. (profinfo-set-occurs! called-pi
  1195. (+ (profinfo-occurs called-pi) 1))))
  1196. ;; if top element, count as running
  1197. (if (null? (cdr stack))
  1198. (profinfo-set-hist! called-pi
  1199. (+ (profinfo-hist called-pi) 1)))
  1200. ;; if gone, record it
  1201. (if (not (stackentry-seen called-se))
  1202. (set! gone-stackentries
  1203. (cons called-se gone-stackentries)))
  1204. (loop (cdr stack)
  1205. called-se
  1206. (cons template seen-templates))))))
  1207. gone-stackentries))
  1208. (define (compare-continuation-args c1 c2)
  1209. (let ((ac (continuation-arg-count c1))
  1210. (ac2 (continuation-arg-count c2)))
  1211. (if (= ac ac2)
  1212. (let loop ((i 1))
  1213. (if (< i ac)
  1214. (if (eq? (continuation-arg c1 i)
  1215. (continuation-arg c2 i))
  1216. (loop (+ i 1))
  1217. #f)
  1218. #t))
  1219. #f)))
  1220. (define (process-stack-traces! prof-data)
  1221. (let ((stat-new-funcs '())
  1222. (stat-gone-funcs '())
  1223. (stat-new-caller #f)
  1224. (stat-top #f))
  1225. ;; go from bottom to top and count calls
  1226. (let loop ((pos 0)
  1227. (stack *cur-stack*)
  1228. (caller-se #f)
  1229. (diff-found #f))
  1230. (if (not (null? stack))
  1231. (let ((new-se (car stack)))
  1232. ;; compare with last stack
  1233. (let ((old-se (list-ref-or-default *last-stack* pos #f))
  1234. (rcdcall #f)
  1235. (old-diff-found diff-found))
  1236. (if (or (not old-se) ; not on old stack
  1237. diff-found)
  1238. (begin
  1239. (set! rcdcall #t)
  1240. (set! diff-found #t))
  1241. (if (not (eq? (stackentry-template old-se) ; other template => other func
  1242. (stackentry-template new-se)))
  1243. (begin
  1244. (set! rcdcall #t)
  1245. (set! diff-found #t))
  1246. ;; same template...
  1247. (let ((old-cont (stackentry-cont old-se))
  1248. (new-cont (stackentry-cont new-se)))
  1249. (if (not (eq? old-cont new-cont)) ; other continuation, something changed
  1250. (begin
  1251. (set! diff-found #t) ; remember change upwards...
  1252. (if (and (eq? (continuation-pc old-cont) ; same pc and arg-count, else
  1253. (continuation-pc new-cont)) ; may be just other place in func
  1254. (eq? (continuation-code old-cont)
  1255. (continuation-code new-cont))
  1256. (compare-continuation-args old-cont new-cont)) ; detects most tailcalls
  1257. (set! rcdcall #t)))))))
  1258. (if (and caller-se
  1259. (not (eq? diff-found
  1260. old-diff-found)))
  1261. (set! stat-new-caller caller-se))
  1262. (if rcdcall
  1263. (begin ; new call to fun
  1264. (set! stat-new-funcs (cons new-se stat-new-funcs))
  1265. (if (and caller-se
  1266. *measure-noninstr?*)
  1267. (record-call! prof-data
  1268. (stackentry-template caller-se) 0
  1269. (stackentry-template new-se) 0
  1270. #f))
  1271. )
  1272. (seen! old-se new-se))
  1273. (loop (+ pos 1)
  1274. (cdr stack)
  1275. new-se
  1276. diff-found)))
  1277. (set! stat-top caller-se)))
  1278. (set! stat-gone-funcs
  1279. (post-process-stack! prof-data *last-stack*))
  1280. (analyze-memory-usage prof-data stat-top stat-new-funcs stat-new-caller stat-gone-funcs)
  1281. ))
  1282. (define (record-template! cont template)
  1283. (if template
  1284. (if (eq? (closure-template profile-count)
  1285. template)
  1286. (begin
  1287. ; (display "hitting profile-count, throwing stack away\n")
  1288. (set! *cur-stack* '()))
  1289. (begin
  1290. (let ((lse (last-stackentry))
  1291. (nse (make-stackentry cont template)))
  1292. (if (and lse
  1293. (eq? (stackentry-template lse)
  1294. template))
  1295. (stackentry-set-reccalls! lse
  1296. (+ 1 (stackentry-reccalls lse))))
  1297. ;; consider recursion (disabled)
  1298. (set! *cur-stack*
  1299. (cons nse *cur-stack*)))))))
  1300. ;; main record function (called from interrupt handler)
  1301. (define (record-continuation! prof-data cont)
  1302. ;; init
  1303. (set! *cur-stack* '())
  1304. (set! *profiler-lastrun* *profiler-thisrun*)
  1305. (set! *profiler-thisrun* (run-time)) ; we cap this here, profiler could run some time
  1306. (set! *cur-avail-memory* (available-memory))
  1307. (set! *cur-gc-count* (get-current-gc-count))
  1308. (set-profile-data-samples! prof-data
  1309. (+ 1 (profile-data-samples prof-data)))
  1310. ;; record the current template
  1311. (record-template! cont (find-template cont))
  1312. ;; decent until we reach our own continuation
  1313. (let loop ((cont (continuation-cont cont)))
  1314. (if (and cont
  1315. (not (profiler-continuation? cont)))
  1316. (let ((parent (continuation-cont cont)))
  1317. (record-template! cont (continuation-template cont))
  1318. (loop parent))))
  1319. ;; record our root template
  1320. (record-template! #f (profile-data-root prof-data))
  1321. ;; process the stack built above
  1322. (if (not (null? *cur-stack*))
  1323. (begin
  1324. (process-stack-traces! prof-data)
  1325. ;; save old stack
  1326. (set! *last-stack* *cur-stack*)
  1327. (set-unseen-all!)))
  1328. ;; save memory status
  1329. (set! *last-avail-memory* (available-memory))
  1330. (set! *last-gc-count* (get-current-gc-count)))
  1331. ;; searchs the (moving?) template in the continuation
  1332. (define (find-template cont)
  1333. (let ((len (primitives:continuation-length cont)))
  1334. (let loop ((i 0))
  1335. (and (< i len)
  1336. (let ((elt (primitives:continuation-ref cont i)))
  1337. (if (template? elt)
  1338. elt
  1339. (loop (+ i 1))))))))
  1340. ;;;;;; HEAP PROFILER
  1341. ;; see commit messages and documentation
  1342. (define (available-memory)
  1343. (primitives:memory-status (enum memory-status-option available) #f))
  1344. (define (get-current-gc-count)
  1345. (primitives:memory-status (enum memory-status-option gc-count) #f))
  1346. (define (gc-running-meanwhile?)
  1347. (> *cur-gc-count* *last-gc-count*))
  1348. (define (analyze-memory-usage prof-data top new caller gone)
  1349. (if (gc-running-meanwhile?)
  1350. (begin
  1351. ;; we need to know the free memory after GC to fix this
  1352. #f)
  1353. (begin
  1354. (let* ((usage (- *last-avail-memory*
  1355. *cur-avail-memory*))
  1356. (cntnew (length new))
  1357. (cntgone (length gone))
  1358. (dotop (and top
  1359. (= cntnew 0)
  1360. (= cntgone 0)))
  1361. (totcnt (+ (if caller 1 0)
  1362. cntnew
  1363. cntgone))
  1364. (avgusage (if (= totcnt 0) 0 (/ usage totcnt)))
  1365. (addmem (lambda (se amount)
  1366. (let ((pi (get-profinfo prof-data se)))
  1367. (profinfo-set-memoryuse!
  1368. pi
  1369. (+ (profinfo-memoryuse pi)
  1370. amount))))))
  1371. (if (> usage 0)
  1372. (begin
  1373. (set-profile-data-memoryuse!
  1374. prof-data
  1375. (+ (profile-data-memoryuse prof-data) usage))
  1376. ;; if the template at the top still the same, add all memory to it
  1377. (if dotop
  1378. (addmem top usage)
  1379. ;; else distribute memory usage to all relevant templates
  1380. (begin
  1381. (if caller (addmem caller avgusage))
  1382. (for-each (lambda (se) (addmem se avgusage)) new)
  1383. (for-each (lambda (se) (addmem se avgusage)) gone)))))))))
  1384. ; (warning
  1385. ; 'profile-analyse-memory-usage
  1386. ; (string-append "usage < 0, somehow memory got free with no GC run: "
  1387. ; (number->string *last-avail-memory*)
  1388. ; " -> "
  1389. ; (number->string *cur-avail-memory*)))
  1390. (define (record-call! prof-data caller-template caller-pc called-template called-pc instrumented?)
  1391. (let* ((caller-profinfo (get-profinfo-from-template
  1392. prof-data
  1393. (if *first-call?*
  1394. (begin
  1395. (set! *first-call?* #f)
  1396. (profile-data-root prof-data))
  1397. caller-template)))
  1398. (called-profinfo (get-profinfo-from-template prof-data called-template))
  1399. (min-pc (profinfo-min-pc called-profinfo))
  1400. (only-instrumented? (profinfo-instrumented? called-profinfo)))
  1401. ;; only count this call, if counted by profile-count or
  1402. ;; by the sampling interrupt if it was not counted by profile-count yet
  1403. (if (or instrumented?
  1404. (not only-instrumented?))
  1405. (begin
  1406. ;; store the program counter of the first call to the function
  1407. (if (not min-pc)
  1408. (begin
  1409. (set! min-pc called-pc)
  1410. (profinfo-set-min-pc! called-profinfo min-pc)
  1411. (if instrumented?
  1412. (profinfo-set-instrumented?! called-profinfo #t))))
  1413. ;; we need to check the program counter, otherwise let-s would be counted
  1414. ;; as a call
  1415. (if (<= called-pc min-pc)
  1416. (profinfo-count-call called-profinfo caller-profinfo))))))
  1417. ;;; called from every profiled function (mcount in gprof),
  1418. ;;; if profiler-instrumentation optimizer enabled for this package
  1419. (define (profile-count)
  1420. (if *active-profile-data*
  1421. (let ((x (primitive-cwcc profile-count-cont)))
  1422. x)))
  1423. (define (profile-count-cont cont)
  1424. (let* ((cont-called (continuation-cont cont))
  1425. (cont-caller (continuation-cont cont-called))
  1426. (template-called (continuation-template cont-called))
  1427. (template-caller (continuation-template cont-caller))
  1428. (pc-called (continuation-pc cont-called))
  1429. (pc-caller (continuation-pc cont-caller)))
  1430. ; (display template-caller)
  1431. ; (display " (")
  1432. ; (display pc-caller)
  1433. ; (display ") calls ")
  1434. ; (display template-called)
  1435. ; (display " (")
  1436. ; (display pc-called)
  1437. ; (display ")\n")
  1438. (record-call! *active-profile-data* template-caller pc-caller template-called pc-called #t)))
  1439. (define (get-profinfo-from-template prof-data template)
  1440. (or (table-ref (profile-data-templates prof-data) template)
  1441. (make-profinfo prof-data template)))
  1442. ;; adds one call to the profinfo of CALLED
  1443. (define (profinfo-count-call called caller)
  1444. (if (and called caller)
  1445. (let ((cs (profinfo-callers called)))
  1446. (cond ((table-ref cs caller)
  1447. => (lambda (ci)
  1448. (callerinfo-set-calls! ci (+ 1 (callerinfo-calls ci)))))
  1449. (else
  1450. (table-set! cs caller (make-callerinfo caller 1)))))))