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

    Match rbspy native Ruby frames in source
---
 flamegraph.el            | 38 +++++++++++++++++++++++++++-----------
 test/flamegraph-tests.el | 45 +++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 70 insertions(+), 13 deletions(-)

diff --git a/flamegraph.el b/flamegraph.el
index ec05fa1e1b..1fb1ec9c9f 100644
--- a/flamegraph.el
+++ b/flamegraph.el
@@ -555,11 +555,29 @@ necessarily its definition."
                      (flamegraph--entry-name entry))))))))
 
 (defun flamegraph--frame-display-name (entry)
-  "Return ENTRY's name with any embedded FILE:LINE location stripped."
-  (if (and (stringp entry)
-           (string-match "\\([^ ():]*\\.[^ ():]+\\):\\([0-9]+\\)" entry))
-      (string-trim-right (substring entry 0 (match-beginning 0)) "[ (:-]+")
-    (flamegraph--entry-name entry)))
+  "Return ENTRY's name with embedded source location text stripped."
+  (cond
+   ((and (stringp entry)
+         (string-match "\\([^ ():]*\\.[^ ():]+\\):\\([0-9]+\\)" entry))
+    (string-trim-right (substring entry 0 (match-beginning 0)) "[ (:-]+"))
+   ((and (stringp entry)
+         (string-match "\\`\\(.+\\) - (unknown)\\'" entry))
+    (match-string 1 entry))
+   (t (flamegraph--entry-name entry))))
+
+(defun flamegraph--frame-search-name (entry)
+  "Return ENTRY's name for matching against source text.
+This can differ from `flamegraph--frame-display-name' when a profiler
+adds metadata around the source spelling of the frame."
+  (cond
+   ;; profiler.el: anonymous function objects are written as lambda forms.
+   ((flamegraph--el-anonymous-function-p entry) "lambda")
+   ;; rbspy: Ruby native frames include implementation/source metadata.
+   ((and (stringp entry)
+         (string-match
+          "\\`\\(.+?\\) \\[c function\\] - (unknown)\\'" entry))
+    (match-string 1 entry))
+   (t (flamegraph--frame-display-name entry))))
 
 (defvar-keymap flamegraph--snippet-line-map
   :doc "Keymap active on source snippet lines."
@@ -717,8 +735,8 @@ symbol boundaries in the target buffer's syntax."
   (and (stringp name) (not (string-empty-p name))
        (not (string-match-p "[][[:space:]();,]" name))))
 
-(defun flamegraph--anonymous-function-p (entry)
-  "Non-nil if ENTRY is a function object with no source-searchable name."
+(defun flamegraph--el-anonymous-function-p (entry)
+  "Non-nil if ENTRY is an anonymous Elisp function object."
   (and (not (symbolp entry)) (functionp entry)))
 
 (defun flamegraph--collect-nested-call-sites (node beg end &optional shown)
@@ -759,10 +777,8 @@ callees the snippet omits."
            (let ((any nil))
              (dolist (k (profiler-calltree-children n))
                (let* ((entry (profiler-calltree-entry k))
-                      (anonymous (flamegraph--anonymous-function-p entry))
-                      (name (if anonymous
-                                "lambda"
-                              (flamegraph--frame-display-name entry)))
+                      (anonymous (flamegraph--el-anonymous-function-p entry))
+                      (name (flamegraph--frame-search-name entry))
                       (weight (/ (float (profiler-calltree-count k)) total)))
                  (cond
                   ((flamegraph--skip-through-p entry)
diff --git a/test/flamegraph-tests.el b/test/flamegraph-tests.el
index c187a81bba..b89c7eb1ae 100644
--- a/test/flamegraph-tests.el
+++ b/test/flamegraph-tests.el
@@ -45,7 +45,7 @@
   (should (null (flamegraph--entry-location "[unknown]")))
   (should (null (flamegraph--entry-location ""))))
 
-;;; Display name strips the embedded location
+;;; Display/search names strip profiler metadata for different uses
 
 (ert-deftest flamegraph-test-frame-display-name-strips-location ()
   (should (equal (flamegraph--frame-display-name
@@ -54,12 +54,33 @@
   (should (equal (flamegraph--frame-display-name "work (app/main.py:20)")
                  "work"))
   (should (equal (flamegraph--frame-display-name "block in foo - 
lib/foo.rb:12")
-                 "block in foo")))
+                 "block in foo"))
+  (should (equal (flamegraph--frame-display-name
+                  "select [c function] - (unknown)")
+                 "select [c function]")))
 
 (ert-deftest flamegraph-test-frame-display-name-pass-through ()
   (should (equal (flamegraph--frame-display-name "emacs") "emacs"))
   (should (equal (flamegraph--frame-display-name "[unknown]") "[unknown]")))
 
+(ert-deftest flamegraph-test-frame-search-name-rbspy-native ()
+  "Ruby native frames from rbspy search by method name."
+  (should (equal (flamegraph--frame-search-name
+                  "gsub! [c function] - (unknown)")
+                 "gsub!"))
+  (should (equal (flamegraph--frame-search-name
+                  "any? [c function] - (unknown)")
+                 "any?"))
+  (should (equal (flamegraph--frame-search-name
+                  "block in foo - lib/foo.rb:12")
+                 "block in foo")))
+
+(ert-deftest flamegraph-test-frame-search-name-el-anonymous ()
+  "Anonymous Elisp function objects search as lambda."
+  (should (equal (flamegraph--frame-search-name
+                  (make-byte-code 257 "\300\207" [] 2))
+                 "lambda")))
+
 ;;; skip-through-p / callee-name-acceptable-p
 
 (ert-deftest flamegraph-test-skip-through-p ()
@@ -127,6 +148,26 @@
   (mapcar (lambda (r) (buffer-substring-no-properties (car r) (cadr r)))
           regions))
 
+(ert-deftest flamegraph-test-walker-rbspy-native-frame-search-name ()
+  "Ruby native frames are matched by method name in source."
+  (with-temp-buffer
+    (ruby-mode)
+    (insert "def outer
+  name.gsub!(/x/, y)
+  items.any? { |item| item.ok? }
+end")
+    (let* ((gsub (profiler-make-calltree
+                  :entry "gsub! [c function] - (unknown)" :count 10))
+           (any (profiler-make-calltree
+                 :entry "any? [c function] - (unknown)" :count 5))
+           (outer (profiler-make-calltree
+                   :entry "outer - lib/foo.rb:1" :count 15
+                   :children (list gsub any)))
+           (regions (flamegraph--collect-nested-call-sites
+                     outer (point-min) (point-max))))
+      (should (equal (flamegraph-test--region-texts regions)
+                     '("gsub!" "any?"))))))
+
 (ert-deftest flamegraph-test-walker-strict-nesting ()
   "Real-function chains narrow strictly: a stray sibling is not matched.
 For `(foo (bar (baz)))' with a separate `(bar 'unrelated)' elsewhere,

Reply via email to