]> git.eshelyaron.com Git - emacs.git/commitdiff
Fix segfault in profiler-cpu-log and profiler-memory-log (bug#78763)
authorZach Shaftel <zach@shaf.tel>
Wed, 11 Jun 2025 19:37:31 +0000 (15:37 -0400)
committerEshel Yaron <me@eshelyaron.com>
Wed, 18 Jun 2025 08:13:41 +0000 (10:13 +0200)
* src/profiler.c (export_log): Check if a log has been allocated first,
and return nil if it hasn't.
(Fprofiler_cpu_log, Fprofiler_memory_log): Doc fix.
* test/src/profiler-tests.el (profiler-tests-cpu-profiler)
(profiler-tests-memory-profiler): New tests.

(cherry picked from commit 009cdc8ae09ef060c030feac06578a4ba37cd8c5)

src/profiler.c
test/src/profiler-tests.el [new file with mode: 0644]

index 12d75012c7958c2511cb62a64eefefd3131dcdd4..f421eb52b31b95da37c365656d514fff45d92e5f 100644 (file)
@@ -534,7 +534,11 @@ DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log,
 The log is a hash-table mapping backtraces to counters which represent
 the amount of time spent at those points.  Every backtrace is a vector
 of functions, where the last few elements may be nil.
-Before returning, a new log is allocated for future samples.  */)
+
+If the profiler has not run since the last invocation of
+`profiler-cpu-log' (or was never run at all), return nil.  If the
+profiler is currently running, allocate a new log for future samples
+before returning.  */)
   (void)
 {
   /* Temporarily stop profiling to avoid it interfering with our data
@@ -556,6 +560,7 @@ Before returning, a new log is allocated for future samples.  */)
 static Lisp_Object
 export_log (struct profiler_log *plog)
 {
+  if (!plog->log) return Qnil;
   log_t *log = plog->log;
   /* The returned hash table uses `equal' as key equivalence predicate
      which is more discriminating than the `function-equal' used by
@@ -639,7 +644,11 @@ DEFUN ("profiler-memory-log",
 The log is a hash-table mapping backtraces to counters which represent
 the amount of memory allocated at those points.  Every backtrace is a vector
 of functions, where the last few elements may be nil.
-Before returning, a new log is allocated for future samples.  */)
+
+If the profiler has not run since the last invocation of
+`profiler-memory-log' (or was never run at all), return nil.  If the
+profiler is currently running, allocate a new log for future samples
+before returning.  */)
   (void)
 {
   bool prof_mem = profiler_memory_running;
diff --git a/test/src/profiler-tests.el b/test/src/profiler-tests.el
new file mode 100644 (file)
index 0000000..fe50de7
--- /dev/null
@@ -0,0 +1,58 @@
+;;; profiler-tests.el --- tests for src/profiler.c  -*- lexical-binding:t -*-
+
+;; Copyright (C) 2025 Free Software Foundation, Inc.
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'ert)
+
+(ert-deftest profiler-tests-memory-profiler ()
+  (let ((was-running (profiler-memory-running-p)))
+    ;; do this first in case the profiler was already running
+    (should (eq was-running (profiler-memory-stop)))
+    (profiler-memory-start)
+    (should-error (profiler-memory-start))
+    (should (profiler-memory-running-p))
+    (should (hash-table-p (profiler-memory-log)))
+    ;; `profiler-memory-log' shouldn't terminate profiling.
+    (should (profiler-memory-running-p))
+    (profiler-memory-stop)
+    (profiler-memory-log)               ;flush the log
+    (should-not (profiler-memory-log))
+    (when was-running (profiler-memory-start))))
+
+(defconst profiler-tests-cpu-sampling-interval 1000000)
+
+(ert-deftest profiler-tests-cpu-profiler ()
+  (skip-unless (fboundp 'profiler-cpu-start))
+  (let ((was-running (profiler-cpu-running-p)))
+    (should (eq was-running (profiler-cpu-stop)))
+    (profiler-cpu-start profiler-tests-cpu-sampling-interval)
+    (should-error (profiler-cpu-start profiler-tests-cpu-sampling-interval))
+    (should (hash-table-p (profiler-cpu-log)))
+    (should (profiler-cpu-running-p))
+    (profiler-cpu-stop)
+    (profiler-cpu-log)
+    (should-not (profiler-cpu-log))
+    (when was-running
+      (profiler-cpu-start (or (bound-and-true-p profiler-sampling-interval)
+                              profiler-tests-cpu-sampling-interval)))))
+
+;;; profiler-tests.el ends here