Browse Source

enhance: add atom/volatile mem-leak detect tool

rcmerci 10 tháng trước cách đây
mục cha
commit
391f553c2a

+ 26 - 3
src/main/frontend/components/profiler.cljs

@@ -1,6 +1,7 @@
 (ns frontend.components.profiler
   "Profiler UI"
-  (:require [fipp.edn :as fipp]
+  (:require [clojure.set :as set]
+            [fipp.edn :as fipp]
             [frontend.handler.profiler :as profiler-handler]
             [frontend.util :as util]
             [logseq.shui.ui :as shui]
@@ -8,10 +9,12 @@
 
 (rum/defcs profiler < rum/reactive
   (rum/local nil ::reports)
+  (rum/local nil ::mem-leak-reports)
   (rum/local nil ::register-fn-name)
   [state]
   (let [profiling-fns (keys (rum/react profiler-handler/*fn-symbol->origin-fn))
         *reports (get state ::reports)
+        *mem-leak-reports (get state ::mem-leak-reports)
         *register-fn-name (get state ::register-fn-name)]
     [:div
      [:b "Profiling fns(Only support UI thread now):"]
@@ -38,7 +41,6 @@
                             (when (= v "input fn name here")
                               (set! (.-value (.-target e)) ""))))
         :placeholder "input fn name here"}]]
-     [:hr]
      [:div.flex.gap-2.flex-wrap.items-center.pb-3
       (shui/button
        {:size :sm
@@ -57,4 +59,25 @@
            (-> @*reports
                (update :time-sum update-time-sum)
                (fipp/pprint {:width 20})
-               with-out-str))]])]))
+               with-out-str))]])
+     [:hr]
+     [:b "Atom/Volatile Mem Leak Detect(Only support UI thread now):"]
+     [:div.flex.flex-row.items-center.gap-2
+      (if (= 2 (count (set/difference #{'cljs.core/reset! 'cljs.core/vreset!} (set profiling-fns))))
+        (shui/button
+         {:on-click (fn []
+                      (profiler-handler/mem-leak-detect))}
+         "Start to detect")
+        (shui/button
+         {:size :sm
+          :on-click (fn [_] (reset! *mem-leak-reports (profiler-handler/mem-leak-report)))}
+         (shui/tabler-icon "refresh") "Refresh reports"))]
+     [:pre.select-text
+      (when @*mem-leak-reports
+        (let [ref-hash->ref (:ref-hash->ref @*mem-leak-reports)]
+          (-> @*mem-leak-reports
+              (dissoc :ref-hash->ref)
+              (assoc :ref-hash->take-3-vals (update-vals ref-hash->ref (fn [ref] (take 3 @ref)))
+                     :ref-hash->take-3-watch-keys (update-vals ref-hash->ref (fn [ref] (take 3 (.-watches ^js ref)))))
+              (fipp/pprint {:width 20})
+              with-out-str)))]]))

+ 37 - 14
src/main/frontend/handler/profiler.cljs

@@ -67,6 +67,41 @@
   {:call-count @*fn-symbol->key->call-count
    :time-sum @*fn-symbol->key->time-sum})
 
+(def ^:private *ref-hash->coll-size (volatile! {}))
+(def ^:private *ref-hash->watches-count (volatile! {}))
+(def ^:private *ref-hash->ref (volatile! {}))
+
+(defn mem-leak-detect
+  "Add monitor on Atom/Volatile.
+  Show atoms/volatiles contains huge collections.
+  Show atoms have a huge number of watchers"
+  [& {:keys [data-count-threshold watches-count-threshold]
+      :or {data-count-threshold 5000 watches-count-threshold 1000}}]
+  (register-fn! 'cljs.core/reset!
+                :custom-key-fn (fn [[ref _] newval]
+                                 (let [coll-size (and (coll? newval) (count newval))
+                                       *ref-hash (delay (hash ref))]
+                                   (when (> coll-size data-count-threshold)
+                                     (vswap! *ref-hash->coll-size assoc @*ref-hash coll-size)
+                                     (vswap! *ref-hash->ref assoc @*ref-hash ref))
+                                   (let [watches-count (count (.-watches ref))]
+                                     (when (> watches-count watches-count-threshold)
+                                       (vswap! *ref-hash->watches-count assoc @*ref-hash watches-count)
+                                       (vswap! *ref-hash->ref assoc @*ref-hash ref))))))
+  (register-fn! 'cljs.core/vreset!
+                :custom-key-fn (fn [[ref _] newval]
+                                 (let [coll-size (and (coll? newval) (count newval))
+                                       *ref-hash (delay (hash ref))]
+                                   (when (> coll-size data-count-threshold)
+                                     (vswap! *ref-hash->coll-size assoc @*ref-hash coll-size)
+                                     (vswap! *ref-hash->ref assoc @*ref-hash ref))))))
+
+(defn mem-leak-report
+  []
+  {:ref-hash->coll-size @*ref-hash->coll-size
+   :ref-hash->watches-count @*ref-hash->watches-count
+   :ref-hash->ref @*ref-hash->ref})
+
 (comment
   (register-fn! 'datascript.core/entity)
   (prn :profiling (keys @*fn-symbol->origin-fn))
@@ -83,19 +118,7 @@
 
   (register-fn! 'frontend.handler.profiler/test-fn-to-profile
                 :custom-key-fn (fn [args result] {:a args :r result}))
-  (register-fn! 'cljs.core/reset!
-                :custom-key-fn (fn [args result]
-                                 (when (and (coll? result) (> (count result) 5000))
-                                   (prn :atom-size (count result) (take 3 result))
-                                   (js/console.trace))
-                                 nil))
-  (register-fn! 'cljs.core/vreset!
-                :custom-key-fn (fn [args result]
-                                 (when (and (coll? result) (> (count result) 5000))
-                                   (prn :volatile-size (count result) (take 3 result))
-                                   (js/console.trace))
-                                 nil))
-
-
 
+  (mem-leak-detect)
+  [@*ref-hash->coll-size @*ref-hash->watches-count]
   )