11(ns  clj-async-profiler.ui 
22  (:require  [clj-async-profiler.core :as  core]
33            [clj-async-profiler.wwws :as  wwws :refer  [redirect respond]]
4+             [clj-async-profiler.render :as  render]
5+             [clj-async-profiler.results :as  results]
46            [clojure.java.io :as  io]
57            [clojure.string :as  str])
6-   (:import  java.io.File))
8+   (:import  java.io.File
9+            java.util.Date
10+            java.time.format.DateTimeFormatter
11+            java.time.ZoneId))
712
813(def  ^:private ui-state  (atom  {}))
914
15+ (defmacro  ^:private forj  {:style/indent  1 } [bindings body]
16+   `(str/join (for  ~bindings ~body)))
17+ 
1018(defn-  profiler-controls  []
1119  (let  [^String status-msg (core/status )]
1220    (if  (.contains  status-msg " is running"  )
1624      (format  " <form action=\" /start-profiler\" >Event: 
1725               <select name=\" event\" >%s</select>  
1826               <input type=\" submit\"  value=\" Start profiler\" /></form>"  
19-               (str/join 
20-                (for  [ev (core/list-event-types  {:silent?  true })]
21-                  (format  " <option value=%s>%s</option>"   (name  ev) (name  ev))))))))
22- 
23- (defn-  main-page 
24-   [root files]
25-   (let  [{:keys  [show-raw-files]} @ui-state]
26-    (format 
27-     " <html><head>
28- <title>clj-async-profiler</title> 
29- <link rel='icon' href='favicon.png' type='image/x-icon'/> 
30- <style> 
31- body { 
32- margin: 1em auto; 
33- max-width: 40em; 
34- font: 1.1em/1.5 sans-serif; 
35- } 
36- a { text-decoration: none; } 
37- </style></head><body><div><big>clj-async-profiler</big> 
38- <small style=\" float: right\" ><a href=\" /toggle-show-raw\" >%s</a> | <a href=\" /clear-results\" >Clear all results</a></small></div><hr> 
39- <small>%s</small> 
40- <hr><ul>%s</ul><hr></body></html>"  
41-     (if  show-raw-files " Hide raw files"   " Show raw files"  )
42-     (profiler-controls )
43-     (->>  (for  [^File f files
44-                :let  [fname (.getName  f)
45-                      sz (.length  f)]]
46-            (format  " <li><a href='%s'>%s</a> <small><font color=\" gray\" >(%s%s)</font></small></li>" 
47-                    (str  root fname)
48-                    fname
49-                    (if-let  [{:keys  [samples]} (@@#'core/flamegraph-file->metadata  f)]
50-                      (if  (<  samples 1000 )
51-                        (str  samples "  samples, "  )
52-                        (format  " %.1fk samples, "   (/  samples 1e3 )))
53-                      " "  )
54-                    (if  (<  sz 1024 ) (str  sz "  b"  ) (format  " %.1f Kb"   (/  sz 1024.0 )))))
55-          str/join))))
27+               (forj  [ev (remove  #{:ClassName.methodName }
28+                                 (core/list-event-types  {:silent?  true }))]
29+                 (format  " <option value=%s>%s</option>"   (name  ev) (name  ev)))))))
30+ 
31+ (def  ^:private ^DateTimeFormatter date-formatter  (DateTimeFormatter/ofPattern  " MMM d, yyyy"  ))
32+ (def  ^:private ^DateTimeFormatter time-formatter  (DateTimeFormatter/ofPattern  " HH:mm"  ))
33+ 
34+ (defn-  date->local-date  [^Date date]
35+   (.format  date-formatter (.atZone  (.toInstant  date) (ZoneId/systemDefault ))))
36+ 
37+ (defn-  date->local-time  [^Date date]
38+   (.format  time-formatter (.atZone  (.toInstant  date) (ZoneId/systemDefault ))))
39+ 
40+ (defn-  row  [tag tr-classes & cells]
41+   (let  [tag (name  tag)
42+         tds (forj  [c cells]
43+               (format  " <%s>%s</%s>"   tag (or  c " "  ) tag))]
44+     (format  " <tr class='%s'>%s</tr>"   tr-classes tds)))
45+ 
46+ (defn-  format-size  [sz samples]
47+   (let  [sz (if  (<  sz 1024 ) (str  sz "  b"  ) (format  " %.1f Kb"   (/  sz 1024.0 )))
48+         samples (cond  (nil?  samples) nil 
49+                       (<  samples 1000 ) (str  " <br>"   samples "  samples"  )
50+                       :else  (format  " <br>%.1fk samples"   (/  samples 1e3 )))]
51+     (cond->  sz samples (str  samples))))
52+ 
53+ (defn-  file-table  [root files]
54+   (let  [files (sort-by  #(.lastModified  ^File %) > files) ; ; Newer on top
55+         parsed-files (for  [^File f files
56+                            :let  [info (results/parse-results-filename  (.getName  f))]]
57+                        [f (update  info :date  #(or  % (Date.  (.lastModified  f))))])
58+         grouped (group-by  #(date->local-date  (:date  (second  %))) parsed-files)]
59+     (format 
60+      " <table><thead>%s</thead><tbody>%s</tbody></table>" 
61+      (row  :th  " heading" 
62+           " Date/time"   " ID"   " File"   " Event"   " Size"   " Extras"   " Actions"  )
63+      (forj  [[local-date day-files] grouped]
64+        (str  (row  :td  " heading" 
65+                  (str  " "   local-date " "  ) " "   " "   " "   " "   " "   " "  )
66+             (forj  [[^File f {:keys  [id date kind event]}] day-files
67+                    :let  [filename (.getName  f)
68+                          ^File stacks (results/find-stacks-file-by-flamegraph-file  f)
69+                          {:keys  [id1 id2 ^File stacks1 ^File stacks2]}
70+                          (@results/file->metadata  f)]]
71+               (row  :td  " data" 
72+                    (date->local-time  date)
73+                    (when  (and  id stacks
74+                               (=  (:stacks-file  (results/find-profile  id))
75+                                  stacks))
76+                      (format  " %02d"   id))
77+                    (format  " <a href='%s'>%s</a>" 
78+                            (str  root filename)
79+                            (or  kind filename))
80+                    (some->  event name)
81+                    (str  " <small>"   (format-size  (.length  f) (:samples  (@results/file->metadata  f))) " </small>"  )
82+                    (cond  id1 (format  " <a href='%s'>%02d</a> vs <a href='%s'>%02d</a>" 
83+                                      (str  root (.getName  ^File stacks1))
84+                                      id1
85+                                      (str  root (.getName  ^File stacks2))
86+                                      id2)
87+                          stacks (format  " <a href='%s'>raw</a>" 
88+                                         (str  root (.getName  stacks))))
89+                    " "  )))))))
90+ 
91+ (defn-  main-page  [root files]
92+   (->>  {:profiler-controls  (profiler-controls )
93+         :file-table  (file-table  root files)}
94+        (render/render-template  (slurp  (io/resource  " ui/index.html"  )))))
5695
5796(defn-  handler  [{:keys  [path params]} base]
5897  (try 
5998    (let  [files (->>  (.listFiles  (io/file  base))
6099                     (remove  #(.isDirectory  ^File %))
61100                     sort)]
62-       (cond  (=  path " /toggle-show-raw"  )
63-             (do  (swap!  ui-state update :show-raw-files  not)
64-                 (redirect  " /"  ))
65- 
66-             (=  path " /start-profiler"  )
101+       (cond  (=  path " /start-profiler"  )
67102            (do  (core/start  {:event  (keyword  (params  " event"  ))})
68103                (redirect  " /"  ))
69104
@@ -76,10 +111,7 @@ a { text-decoration: none; }
76111                (redirect  " /"  ))
77112
78113            (=  path " /"  )
79-             (let  [files (if  (:show-raw-files  @ui-state)
80-                           files
81-                           (remove  #(=  (wwws/get-extension  (str  %)) " txt"  ) files))]
82-               {:body  (main-page  path files)})
114+             {:body  (main-page  path files)}
83115
84116            (=  path " /favicon.png"  )
85117            {:body  (io/resource  " favicon.png"  )
0 commit comments