/
profile.cljc
92 lines (77 loc) · 2.67 KB
/
profile.cljc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
(ns com.wsscode.pathom.profile
(:require
[clojure.core.async :refer [<! go chan put!]]
[#?(:clj com.wsscode.common.async-clj
:cljs com.wsscode.common.async-cljs) :refer [let-chan]]
[com.wsscode.pathom.core :as p]))
(defn- append-at [cur v]
(cond
(map? cur)
(assoc cur ::self v)
:else
v))
(defn current-time-ms []
#?(:clj (System/currentTimeMillis)
:cljs (.getTime (js/Date.))))
(def profile-plugin
{::p/wrap-parser
(fn profile-plugin-wrap-parser [parser]
(fn profile-plugin-wrap-parser-internal [env tx]
(parser (assoc env ::profile* (atom {})) tx)))
::p/wrap-read
(fn profile-plugin-wrap-read [reader]
(fn profile-plugin-wrap-read-internal
[{::keys [profile*] ::p/keys [path] :as env}]
(if (= ::profile (p/key-dispatch env))
@profile*
(let [start-time (current-time-ms)]
(let-chan [res (reader env)]
(try
(swap! profile* update-in path append-at
(- (current-time-ms) start-time))
(catch #?(:clj Throwable :cljs :default) _))
res)))))
::p/wrap-mutate
(fn profile-plugin-wrap-mutate [mutate]
(fn profile-plugin-wrap-mutate-internal
[{::keys [profile*] :as env} k params]
(let [out (mutate env k params)]
(cond-> out
(:action out)
(update :action
(fn [action]
(fn []
(let [start-time (current-time-ms)
res (action)]
(swap! profile* assoc k (- (current-time-ms) start-time))
res))))))))})
;; Helper computing functions
(defn node-name [x]
(cond
(vector? x) (clojure.string/join "_" (map node-name x))
:else (str x)))
(defn node-value [x]
(if (map? x)
(or (::self x) (apply + (map node-value (vals x))))
x))
;; Flame graph conversion
(defn profile->nvc* [m]
(->> m
(into [] (comp (remove #(= ::self (first %)))
(map (fn [[k v]]
(cond-> {:name (node-name k) :value (node-value v)}
(map? v) (assoc :children (profile->nvc* v)))))))))
(defn profile->nvc
"Convert data into format of maps containg the keys:
name: the current attribute name
value: the total time spent on the node + children
children: the children elements (recursive)
This for is suitable for some d3 flamegraph plugins on the browser"
[data]
(let [total (apply + (map node-value (vals data)))]
{:name "Root"
:value total
:children (profile->nvc* data)}))
;; DEPRECATED
; old name, kept for compatibility
(def profile->flame-graph profile->nvc)