diff --git a/profile-doc/profile/scribblings/renderers.scrbl b/profile-doc/profile/scribblings/renderers.scrbl index 8f242c5..56fb9bb 100644 --- a/profile-doc/profile/scribblings/renderers.scrbl +++ b/profile-doc/profile/scribblings/renderers.scrbl @@ -23,6 +23,7 @@ function that consumes a @racket[profile] instance. See the @defproc[(render [profile-data profile?] + [order (or/c 'topological 'self 'total) 'topological] [#:truncate-source truncate-source exact-nonnegative-integer? 50] [#:hide-self hide-self% (between/c 0 1) 1/100] [#:hide-subs hide-subs% (between/c 0 1) 2/100]) @@ -32,7 +33,7 @@ Prints the given @racket[profile] results as a textual table. The printout begins with general information about the profile, followed by a table with an entry for each node in the call graph. -The entries are displayed in a topological order (roughly, since the +The entries are displayed in a topological order by default (roughly, since the graph can have cycles). This means that it is usually easy to find the callers and callees of a function in its close environment. @@ -105,6 +106,13 @@ The function has a few keyword arguments to customize its output: conditions is to avoid having ``dangling references'' to hidden nodes.} +@item{The @racket[order] argument determines the order in which entries + appear in the output. If @racket[order] is @racket['topological] (the default), + entries are sorted topologically, grouping callers and callees close together. + If @racket[order] is @racket['self], entries are sorted by how often + they appear at the top of a stack snapshot. If @racket[order] is @racket['total], + entries are sorted by how often they appear anywhere in a stack snapshot.} + ]} @@ -115,6 +123,7 @@ The function has a few keyword arguments to customize its output: @defproc[(render [profile-data profile?] + [order (or/c 'topological 'self 'total) 'topological] [#:hide-self hide-self% (between/c 0 1) 1/100] [#:hide-subs hide-subs% (between/c 0 1) 2/100]) void?]{ @@ -128,4 +137,4 @@ of the Graphviz tools to render. Nodes are colored according to their `self' percentages, and edges. The keyword arguments control hiding nodes in the same way as with the -textual renderer.} +textual renderer. The @racket[order] argument is ignored.} diff --git a/profile-doc/profile/scribblings/toplevel.scrbl b/profile-doc/profile/scribblings/toplevel.scrbl index 797a9e5..4dd70f4 100644 --- a/profile-doc/profile/scribblings/toplevel.scrbl +++ b/profile-doc/profile/scribblings/toplevel.scrbl @@ -30,12 +30,15 @@ intended as a convenient tool for profiling code. [#:delay delay (>=/c 0.0) 0.05] [#:repeat iterations exact-nonnegative-integer? 1] [#:threads threads? any/c #f] - [#:render renderer (profile? . -> . any/c) text:render] + [#:render renderer (-> profile? (or/c 'topological 'self 'total) any/c) text:render] [#:periodic-renderer periodic-renderer (or/c #f (list/c (>=/c 0.0) - (profile? . -> . any/c))) + (-> profile? + (or/c 'topological 'self 'total) + any/c))) #f] - [#:use-errortrace? use-errortrace? any/c #f]) + [#:use-errortrace? use-errortrace? any/c #f] + [#:order order (or/c 'topological 'self 'total) 'topological]) any/c]{ Executes the given @racket[thunk] and collect profiling data during @@ -88,6 +91,12 @@ Keyword arguments can customize the profiling: using @racket[errortrace-compile-handler], and the profiled program must be run using @commandline{racket -l errortrace -t program.rkt} Removing compiled files (with extension @tt{.zo}) is sufficient to enable this.} + +@item{The @racket[order] value is passed to the @racket[renderer] to control the + order of its output. By default, entries in the profile are sorted + topologically, but they can also be sorted by the time an entry is on top of + the stack (@racket['self]) or appears anywhere on the stack (@racket['total]). + Some renderers may ignore this option.} ]} @defform[(profile expr keyword-arguments ...)]{ diff --git a/profile-lib/main.rkt b/profile-lib/main.rkt index c2992e0..d6661fc 100644 --- a/profile-lib/main.rkt +++ b/profile-lib/main.rkt @@ -12,7 +12,15 @@ #:threads [threads? #f] #:render [renderer text:render] #:periodic-renderer [periodic-renderer #f] - #:use-errortrace? [et? #f]) + #:use-errortrace? [et? #f] + #:order [order 'topological]) + (unless (member order '(topological self total)) + (raise-argument-error + 'profile-thunk "(or/c 'topological 'self 'total)" order)) + (define (call-renderer renderer profile) + (if (procedure-arity-includes? renderer 2) + (renderer profile order) + (renderer profile))) (define cust (and threads? (make-custodian (current-custodian)))) (define sampler (create-sampler (if threads? (list cust (current-thread)) @@ -25,7 +33,7 @@ [renderer (cadr periodic-renderer)]) (define (loop) (sleep delay) - (renderer (analyze-samples (sampler 'get-snapshots))) + (call-renderer renderer (analyze-samples (sampler 'get-snapshots))) (loop)) (thread loop)))) (define (run) (for/last ([i (in-range rpt)]) (thunk))) @@ -38,7 +46,7 @@ (run))) (when periodic-thread (kill-thread periodic-thread)) (sampler 'stop) - (renderer (analyze-samples (sampler 'get-snapshots))))) + (call-renderer renderer (analyze-samples (sampler 'get-snapshots))))) (define-syntax (profile stx) (syntax-case stx () diff --git a/profile-lib/raco.rkt b/profile-lib/raco.rkt index c501cc9..daa578b 100644 --- a/profile-lib/raco.rkt +++ b/profile-lib/raco.rkt @@ -3,7 +3,8 @@ (require racket/cmdline raco/command-name errortrace/errortrace-lib - "main.rkt" "raco-utils.rkt") + "main.rkt" "raco-utils.rkt" + (prefix-in text: "render-text.rkt")) ;; raco profile ;; profile the main submodule (if there is one), or the top-level module @@ -12,6 +13,7 @@ (define iterations #f) (define threads? #f) (define use-errortrace? #f) +(define order 'topological) (define file (command-line #:program (short-program+command-name) #:once-each @@ -33,6 +35,16 @@ [("--use-errortrace") "Use errortrace mode" (set! use-errortrace? #t)] + #:once-any + [("--topological") + "Order functions topologically (the default)" + (set! order 'topological)] + [("--self") + "Order functions by self time" + (set! order 'self)] + [("--total") + "Order functions by total time" + (set! order 'total)] #:args (filename) filename)) @@ -49,20 +61,24 @@ (profile-thunk t #:delay delay #:repeat iterations + #:order order #:threads threads? #:use-errortrace? use-errortrace?)] [delay (profile-thunk t #:delay delay + #:order order #:threads threads? #:use-errortrace? use-errortrace?)] [iterations (profile-thunk t #:repeat iterations + #:order order #:threads threads? #:use-errortrace? use-errortrace?)] [else (profile-thunk t + #:order order #:threads threads? #:use-errortrace? use-errortrace?)])) diff --git a/profile-lib/render-graphviz.rkt b/profile-lib/render-graphviz.rkt index 2a09359..1f7d67c 100644 --- a/profile-lib/render-graphviz.rkt +++ b/profile-lib/render-graphviz.rkt @@ -5,6 +5,7 @@ (require "analyzer.rkt" "utils.rkt") (define (render profile + [order 'topological] #:hide-self [hide-self% 1/100] #:hide-subs [hide-subs% 2/100]) (define *-node (profile-*-node profile)) diff --git a/profile-lib/render-text.rkt b/profile-lib/render-text.rkt index abbc6af..d4171da 100644 --- a/profile-lib/render-text.rkt +++ b/profile-lib/render-text.rkt @@ -41,9 +41,13 @@ [else (error 'internal-error "poof")]))))) (define (render profile + [order 'topological] #:truncate-source [truncate-source 50] #:hide-self [hide-self% 1/100] #:hide-subs [hide-subs% 2/100]) + (unless (member order '(topological self total)) + (raise-argument-error 'render "(or/c 'topological 'self 'total)" order)) + (define key (if (eq? order 'total) node-total node-self)) (define (show . xs) (let loop ([x xs]) (cond [(or (not x) (null? x) (void? x)) (void)] @@ -58,7 +62,10 @@ (define threads+times (profile-thread-times profile)) (define *-node (profile-*-node profile)) (define hidden (get-hidden profile hide-self% hide-subs%)) - (define nodes (remq* hidden (profile-nodes profile))) + (define nodes (let ([incnodes (remq* hidden (profile-nodes profile))]) + (if (eq? order 'topological) + incnodes + (sort incnodes > #:key key)))) (define node-> (let ([t (make-hasheq)]) (for ([node (in-list nodes)] [idx (in-naturals 1)])