branch: elpa/flamegraph
commit d2a06fe3f900d1ee8231b4d535115ebbcc5ee757
Author: Dmitry Gutov <[email protected]>
Commit: Dmitry Gutov <[email protected]>

    Collapse self-recursion in the Calls tree
    
    A function called once in source but recursing in the profile showed as a
    chain (e.g. file-truename nested six deep under find-file-noselect): after
    matching a call, the walker searched its enclosing form including the head
    symbol, so each recursive child re-matched the same occurrence.
    
    Search a matched call's arguments from past the head symbol, so a
    recursive callee no longer re-anchors on its own name.
---
 flamegraph.el            | 11 +++++++----
 test/flamegraph-tests.el | 23 +++++++++++++++++++++++
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/flamegraph.el b/flamegraph.el
index 78024ac578..94508c8d47 100644
--- a/flamegraph.el
+++ b/flamegraph.el
@@ -746,10 +746,13 @@ tree may show cold callees the snippet omits."
                                (push (list mb me weight) regions))
                              (when-let* ((sub 
(flamegraph--enclosing-form-region
                                                mb)))
-                               ;; Clamp to REGION so a stray enclosing form
-                               ;; can never widen the search.
-                               (walk k (cons (max (car sub) (car region))
-                                             (min (cdr sub) (cdr region)))
+                               ;; Search K's own calls in this call's
+                               ;; arguments: from past the matched head to the
+                               ;; form end, clamped to REGION so a stray
+                               ;; enclosing form cannot widen it.  Starting at
+                               ;; the head's end keeps a recursive callee from
+                               ;; re-matching its own name.
+                               (walk k (cons me (min (cdr sub) (cdr region)))
                                      nil)))))
                        (when (or found concealed-ok)
                          (when shown (puthash k t shown))
diff --git a/test/flamegraph-tests.el b/test/flamegraph-tests.el
index 74bab16492..4410fee4e7 100644
--- a/test/flamegraph-tests.el
+++ b/test/flamegraph-tests.el
@@ -131,6 +131,29 @@ stray `bar' is outside the foo-form's region."
       (should (equal (flamegraph-test--region-texts regions)
                      '("foo" "bar" "baz"))))))
 
+(ert-deftest flamegraph-test-walker-self-recursion-not-renested ()
+  "A function called once in source but recursing deeply in the profile
+is shown once, not as a chain: searching a matched call's arguments must
+not re-match the call's own head symbol."
+  (flamegraph-test--with-elisp-source
+      "(defun outer ()
+  (rec x))"
+    (let* ((rec3 (profiler-make-calltree :entry 'rec :count 1))
+           (rec2 (profiler-make-calltree :entry 'rec :count 2
+                                         :children (list rec3)))
+           (rec1 (profiler-make-calltree :entry 'rec :count 3
+                                         :children (list rec2)))
+           (outer (profiler-make-calltree :entry 'outer :count 3
+                                          :children (list rec1)))
+           (shown (make-hash-table :test 'eq))
+           (regions (flamegraph--collect-nested-call-sites
+                     outer (point-min) (point-max) shown)))
+      ;; Only the one written `(rec x)' is a call site.
+      (should (equal (flamegraph-test--region-texts regions) '("rec")))
+      (should (gethash rec1 shown))
+      (should-not (gethash rec2 shown))
+      (should-not (gethash rec3 shown)))))
+
 (ert-deftest flamegraph-test-walker-macro-expansion-transparent ()
   "Skip-through frames absent from source must not stop the descent.
 The profiler records post-expansion frames (here `while'/`let' from

Reply via email to