From 03cfede8f0aa952bde76fa595ca06770cc52e655 Mon Sep 17 00:00:00 2001 From: Robert Pluim Date: Wed, 15 Feb 2023 12:16:11 +0100 Subject: [PATCH 1/6] Improve thing-at-point email detection * lisp/thingatpt.el (thing-at-point-email-regexp): Allow numbers at the start of the user portion, and disallow '.' at the start. Also disallow '.' at the start of the domain portion. * test/lisp/thingatpt-tests.el (thing-at-point-test-data): Add various email tests. Bug#61519 --- lisp/thingatpt.el | 2 +- test/lisp/thingatpt-tests.el | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lisp/thingatpt.el b/lisp/thingatpt.el index 9363a474cb5..f3367290dee 100644 --- a/lisp/thingatpt.el +++ b/lisp/thingatpt.el @@ -645,7 +645,7 @@ back from point." ;; Email addresses (defvar thing-at-point-email-regexp - "?" + "?" "A regular expression probably matching an email address. This does not match the real name portion, only the address, optionally with angle brackets.") diff --git a/test/lisp/thingatpt-tests.el b/test/lisp/thingatpt-tests.el index 0daf27f32ec..7cf41d2817b 100644 --- a/test/lisp/thingatpt-tests.el +++ b/test/lisp/thingatpt-tests.el @@ -72,7 +72,38 @@ ("" 1 url "ftp://ftp.example.net/abc/") ;; UUID, only hex is allowed ("01234567-89ab-cdef-ABCD-EF0123456789" 1 uuid "01234567-89ab-cdef-ABCD-EF0123456789") - ("01234567-89ab-cdef-ABCD-EF012345678G" 1 uuid nil)) + ("01234567-89ab-cdef-ABCD-EF012345678G" 1 uuid nil) + ;; email addresses + ("foo@example.com" 1 email "foo@example.com") + ("f@example.com" 1 email "f@example.com") + ("foo@example.com" 4 email "foo@example.com") + ("foo@example.com" 5 email "foo@example.com") + ("foo@example.com" 15 email "foo@example.com") + ("foo@example.com" 16 email "foo@example.com") + ("" 1 email "") + ("" 4 email "") + ("" 5 email "") + ("" 16 email "") + ("" 17 email "") + ;; email adresses containing numbers + ("foo1@example.com" 1 email "foo1@example.com") + ("1foo@example.com" 1 email "1foo@example.com") + ("11@example.com" 1 email "11@example.com") + ("1@example.com" 1 email "1@example.com") + ;; email adresses user portion containing dots + ("foo.bar@example.com" 1 email "foo.bar@example.com") + (".foobar@example.com" 1 email nil) + (".foobar@example.com" 2 email "foobar@example.com") + ;; email adresses domain portion containing dots and dashes + ("foobar@.example.com" 1 email nil) + ("foobar@-example.com" 1 email "foobar@-example.com") + ;; These are illegal, but thingatpt doesn't yet handle them + ;; ("foo..bar@example.com" 1 email nil) + ;; ("foobar@.example.com" 1 email nil) + ;; ("foobar@example..com" 1 email nil) + ;; ("foobar.@example.com" 1 email nil) + + ) "List of `thing-at-point' tests. Each list element should have the form From d5d2959217f7afc99f2636cafdb8ffe00e14dfae Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 10 Mar 2023 12:22:43 -0500 Subject: [PATCH 2/6] * lisp/progmodes/gud.el (gud-minor-menu-map): Fix thinko in last change --- lisp/progmodes/gud.el | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el index 42d64952d86..d5c8e37a37b 100644 --- a/lisp/progmodes/gud.el +++ b/lisp/progmodes/gud.el @@ -211,7 +211,9 @@ Used to gray out relevant toolbar icons.") ;; We then merge them here into `gud-minor-mode-map'. :parent gud-menu-mode-map "" `(menu-item nil ,gud-text-menu-bar-map - :filter ,(lambda (map) (unless window-system map)))) + ;; Be careful to return an empty keymap rather than nil + ;; so as not to hide the parent's menus. + :filter ,(lambda (map) (if window-system '(keymap) map)))) (easy-menu-define gud-menu-map gud-menu-mode-map "Menu for `gud-mode'." From 9a5f2ac97ecd5f434690f04ed0b6573d2dd58148 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Fri, 10 Mar 2023 14:54:52 -0500 Subject: [PATCH 3/6] src/profiler.c: Keep track of allocations during GC Cargo-cult the `cpu_gc_count` code to `memory_gc_count`. * src/profiler.c (mem_gc_count): New var. (Fprofiler_memory_start): Initialize it. (Fprofiler_memory_log): Increment it. (Fprofiler_memory_log): Use it. --- src/profiler.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/profiler.c b/src/profiler.c index 8247b2e90c6..92d8a0aea1c 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -227,6 +227,9 @@ static Lisp_Object cpu_log; /* Separate counter for the time spent in the GC. */ static EMACS_INT cpu_gc_count; +/* Separate counter for the memory allocations during GC. */ +static EMACS_INT mem_gc_count; + /* The current sampling interval in nanoseconds. */ static EMACS_INT current_sampling_interval; @@ -451,7 +454,10 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */) error ("Memory profiler is already running"); if (NILP (memory_log)) - memory_log = make_log (); + { + mem_gc_count = 0; + memory_log = make_log (); + } profiler_memory_running = true; @@ -495,6 +501,10 @@ Before returning, a new log is allocated for future samples. */) more for our use afterwards since we can't rely on its special pre-allocated keys anymore. So we have to allocate a new one. */ memory_log = profiler_memory_running ? make_log () : Qnil; + Fputhash (make_vector (1, QAutomatic_GC), + make_fixnum (mem_gc_count), + result); + mem_gc_count = 0; return result; } @@ -506,10 +516,19 @@ void malloc_probe (size_t size) { if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ - /* FIXME: We should do something like what we did with `cpu_gc_count`. */ - return; - eassert (HASH_TABLE_P (memory_log)); - record_backtrace (XHASH_TABLE (memory_log), min (size, MOST_POSITIVE_FIXNUM)); + /* Special case the malloc-count inside GC because the hash-table + code is not prepared to be used while the GC is running. + More specifically it uses ASIZE at many places where it does + not expect the ARRAY_MARK_FLAG to be set. We could try and + harden the hash-table code, but it doesn't seem worth the + effort. */ + mem_gc_count = saturated_add (mem_gc_count, 1); + else + { + eassert (HASH_TABLE_P (memory_log)); + record_backtrace (XHASH_TABLE (memory_log), + min (size, MOST_POSITIVE_FIXNUM)); + } } DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0, From f97d4b9e54c7de3f67d78be8c63afcdb6b704531 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 10 Mar 2023 15:05:55 -0500 Subject: [PATCH 4/6] src/profiler.c: Try and fix occasional assertion failures Apparently the (>= match imatch) test fails sometimes in `profiler.el`. Not sure where this comes from, but this patch should remove one possible source. * src/profiler.c (Fprofiler_cpu_log, Fprofiler_memory_log): Change the special Automatic_GC backtrace to make it clear that it's a *sibling* of the call tree (i.e. it's at the (its own) root). (malloc_probe): Obey `size` when incrementing the gc_counter. --- src/profiler.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/profiler.c b/src/profiler.c index 92d8a0aea1c..b96f7211934 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -426,7 +426,7 @@ Before returning, a new log is allocated for future samples. */) more for our use afterwards since we can't rely on its special pre-allocated keys anymore. So we have to allocate a new one. */ cpu_log = profiler_cpu_running ? make_log () : Qnil; - Fputhash (make_vector (1, QAutomatic_GC), + Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), make_fixnum (cpu_gc_count), result); cpu_gc_count = 0; @@ -501,7 +501,7 @@ Before returning, a new log is allocated for future samples. */) more for our use afterwards since we can't rely on its special pre-allocated keys anymore. So we have to allocate a new one. */ memory_log = profiler_memory_running ? make_log () : Qnil; - Fputhash (make_vector (1, QAutomatic_GC), + Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), make_fixnum (mem_gc_count), result); mem_gc_count = 0; @@ -518,11 +518,10 @@ malloc_probe (size_t size) if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ /* Special case the malloc-count inside GC because the hash-table code is not prepared to be used while the GC is running. - More specifically it uses ASIZE at many places where it does - not expect the ARRAY_MARK_FLAG to be set. We could try and - harden the hash-table code, but it doesn't seem worth the - effort. */ - mem_gc_count = saturated_add (mem_gc_count, 1); + E.g. it uses ASIZE at many places where it does not expect + the ARRAY_MARK_FLAG to be set and in anyn case it'd modify the + heap behind the GC's back. */ + mem_gc_count = saturated_add (mem_gc_count, size); else { eassert (HASH_TABLE_P (memory_log)); From 83be04c66ffaec86aee136b9a94979169d1ba68d Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 10 Mar 2023 15:30:20 -0500 Subject: [PATCH 5/6] src/profiler.c: Share more code between CPU and Memory profilers * src/profiler.c (struct profiler_log): New type. (make_log): Use it. (cpu, memory): New vars to replace cpu_log, memory_log, cpu_gc_count, and mem_gc_count. (add_sample): New function, extracted from `handle_profiler_signal`. (handle_profiler_signal, malloc_probe): Use it. (Fprofiler_cpu_start, Fprofiler_memory_start): Adjust call to `make_log`. (export_log): New function, extracted from `Fprofiler_cpu_log`. (Fprofiler_cpu_log, Fprofiler_memory_log): Use it. (syms_of_profiler, syms_of_profiler_for_pdumper): Adjust to new `cpu` and `memory` vars. --- src/profiler.c | 146 +++++++++++++++++++++++-------------------------- 1 file changed, 67 insertions(+), 79 deletions(-) diff --git a/src/profiler.c b/src/profiler.c index b96f7211934..d5a5a2cf5f3 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -49,7 +49,12 @@ static const struct hash_table_test hashtest_profiler = hashfn_profiler, }; -static Lisp_Object +struct profiler_log { + Lisp_Object log; + EMACS_INT gc_count; +}; + +static struct profiler_log make_log (void) { /* We use a standard Elisp hash-table object, but we use it in @@ -60,11 +65,13 @@ make_log (void) = clip_to_bounds (0, profiler_log_size, MOST_POSITIVE_FIXNUM); ptrdiff_t max_stack_depth = clip_to_bounds (0, profiler_max_stack_depth, PTRDIFF_MAX);; - Lisp_Object log = make_hash_table (hashtest_profiler, heap_size, - DEFAULT_REHASH_SIZE, - DEFAULT_REHASH_THRESHOLD, - Qnil, false); - struct Lisp_Hash_Table *h = XHASH_TABLE (log); + struct profiler_log log + = { make_hash_table (hashtest_profiler, heap_size, + DEFAULT_REHASH_SIZE, + DEFAULT_REHASH_THRESHOLD, + Qnil, false), + 0 }; + struct Lisp_Hash_Table *h = XHASH_TABLE (log.log); /* What is special about our hash-tables is that the values are pre-filled with the vectors we'll use as keys. */ @@ -222,13 +229,10 @@ static enum profiler_cpu_running profiler_cpu_running; /* Hash-table log of CPU profiler. */ -static Lisp_Object cpu_log; +static struct profiler_log cpu; -/* Separate counter for the time spent in the GC. */ -static EMACS_INT cpu_gc_count; - -/* Separate counter for the memory allocations during GC. */ -static EMACS_INT mem_gc_count; +/* Hash-table log of Memory profiler. */ +static struct profiler_log memory; /* The current sampling interval in nanoseconds. */ static EMACS_INT current_sampling_interval; @@ -236,32 +240,39 @@ static EMACS_INT current_sampling_interval; /* Signal handler for sampling profiler. */ static void -handle_profiler_signal (int signal) +add_sample (struct profiler_log *log, EMACS_INT count) { - if (EQ (backtrace_top_function (), QAutomatic_GC)) + if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ /* Special case the time-count inside GC because the hash-table code is not prepared to be used while the GC is running. More specifically it uses ASIZE at many places where it does not expect the ARRAY_MARK_FLAG to be set. We could try and harden the hash-table code, but it doesn't seem worth the effort. */ - cpu_gc_count = saturated_add (cpu_gc_count, 1); + log->gc_count = saturated_add (log->gc_count, count); else { - EMACS_INT count = 1; -#if defined HAVE_ITIMERSPEC && defined HAVE_TIMER_GETOVERRUN - if (profiler_timer_ok) - { - int overruns = timer_getoverrun (profiler_timer); - eassert (overruns >= 0); - count += overruns; - } -#endif - eassert (HASH_TABLE_P (cpu_log)); - record_backtrace (XHASH_TABLE (cpu_log), count); + eassert (HASH_TABLE_P (log->log)); + record_backtrace (XHASH_TABLE (log->log), count); } } + +static void +handle_profiler_signal (int signal) +{ + EMACS_INT count = 1; +#if defined HAVE_ITIMERSPEC && defined HAVE_TIMER_GETOVERRUN + if (profiler_timer_ok) + { + int overruns = timer_getoverrun (profiler_timer); + eassert (overruns >= 0); + count += overruns; + } +#endif + add_sample (&cpu, count); +} + static void deliver_profiler_signal (int signal) { @@ -346,11 +357,8 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */) if (profiler_cpu_running) error ("CPU profiler is already running"); - if (NILP (cpu_log)) - { - cpu_gc_count = 0; - cpu_log = make_log (); - } + if (NILP (cpu.log)) + cpu = make_log (); int status = setup_cpu_timer (sampling_interval); if (status < 0) @@ -412,6 +420,21 @@ DEFUN ("profiler-cpu-running-p", return profiler_cpu_running ? Qt : Qnil; } +static Lisp_Object +export_log (struct profiler_log *log) +{ + Lisp_Object result = log->log; + Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), + make_fixnum (log->gc_count), + result); + /* Here we're making the log visible to Elisp, so it's not safe any + more for our use afterwards since we can't rely on its special + pre-allocated keys anymore. So we have to allocate a new one. */ + if (profiler_cpu_running) + *log = make_log (); + return result; +} + DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log, 0, 0, 0, doc: /* Return the current cpu profiler log. @@ -421,16 +444,7 @@ of functions, where the last few elements may be nil. Before returning, a new log is allocated for future samples. */) (void) { - Lisp_Object result = cpu_log; - /* Here we're making the log visible to Elisp, so it's not safe any - more for our use afterwards since we can't rely on its special - pre-allocated keys anymore. So we have to allocate a new one. */ - cpu_log = profiler_cpu_running ? make_log () : Qnil; - Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), - make_fixnum (cpu_gc_count), - result); - cpu_gc_count = 0; - return result; + return (export_log (&cpu)); } #endif /* PROFILER_CPU_SUPPORT */ @@ -439,8 +453,6 @@ Before returning, a new log is allocated for future samples. */) /* True if memory profiler is running. */ bool profiler_memory_running; -static Lisp_Object memory_log; - DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start, 0, 0, 0, doc: /* Start/restart the memory profiler. @@ -453,11 +465,8 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */) if (profiler_memory_running) error ("Memory profiler is already running"); - if (NILP (memory_log)) - { - mem_gc_count = 0; - memory_log = make_log (); - } + if (NILP (memory.log)) + memory = make_log (); profiler_memory_running = true; @@ -496,16 +505,7 @@ of functions, where the last few elements may be nil. Before returning, a new log is allocated for future samples. */) (void) { - Lisp_Object result = memory_log; - /* Here we're making the log visible to Elisp , so it's not safe any - more for our use afterwards since we can't rely on its special - pre-allocated keys anymore. So we have to allocate a new one. */ - memory_log = profiler_memory_running ? make_log () : Qnil; - Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), - make_fixnum (mem_gc_count), - result); - mem_gc_count = 0; - return result; + return (export_log (&memory)); } @@ -515,19 +515,7 @@ Before returning, a new log is allocated for future samples. */) void malloc_probe (size_t size) { - if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ - /* Special case the malloc-count inside GC because the hash-table - code is not prepared to be used while the GC is running. - E.g. it uses ASIZE at many places where it does not expect - the ARRAY_MARK_FLAG to be set and in anyn case it'd modify the - heap behind the GC's back. */ - mem_gc_count = saturated_add (mem_gc_count, size); - else - { - eassert (HASH_TABLE_P (memory_log)); - record_backtrace (XHASH_TABLE (memory_log), - min (size, MOST_POSITIVE_FIXNUM)); - } + add_sample (&memory, min (size, MOST_POSITIVE_FIXNUM)); } DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0, @@ -612,16 +600,16 @@ to make room for new entries. */); #ifdef PROFILER_CPU_SUPPORT profiler_cpu_running = NOT_RUNNING; - cpu_log = Qnil; - staticpro (&cpu_log); + cpu.log = Qnil; + staticpro (&cpu.log); defsubr (&Sprofiler_cpu_start); defsubr (&Sprofiler_cpu_stop); defsubr (&Sprofiler_cpu_running_p); defsubr (&Sprofiler_cpu_log); #endif profiler_memory_running = false; - memory_log = Qnil; - staticpro (&memory_log); + memory.log = Qnil; + staticpro (&memory.log); defsubr (&Sprofiler_memory_start); defsubr (&Sprofiler_memory_stop); defsubr (&Sprofiler_memory_running_p); @@ -636,16 +624,16 @@ syms_of_profiler_for_pdumper (void) if (dumped_with_pdumper_p ()) { #ifdef PROFILER_CPU_SUPPORT - cpu_log = Qnil; + cpu.log = Qnil; #endif - memory_log = Qnil; + memory.log = Qnil; } else { #ifdef PROFILER_CPU_SUPPORT - eassert (NILP (cpu_log)); + eassert (NILP (cpu.log)); #endif - eassert (NILP (memory_log)); + eassert (NILP (memory.log)); } } From d236ab09300070696f21ebfda49678b11c2327eb Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Fri, 10 Mar 2023 15:54:10 -0500 Subject: [PATCH 6/6] src/profiler.c: Keep track of the discarded counts When the table overflows and wh evict entries, keep track of those counts in a global counter so we can see the proportion of samples this represents. * src/profiler.c (struct profiler_log): Add `discarded` field. (evict_lower_half): Change arg to be `struct profiler_log`. Transfer counts to the new `discarded` field. (record_backtrace): Change arg to be `struct profiler_log`. (add_sample): Adjust call accordingly. (export_log): Add `discarded` counts to the result. Onle add the GC and `discarded` counts if they're non-zero. (syms_of_profiler): Define new symbol `Discarded Samples`. --- src/profiler.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/src/profiler.c b/src/profiler.c index d5a5a2cf5f3..6217071ef9c 100644 --- a/src/profiler.c +++ b/src/profiler.c @@ -51,7 +51,8 @@ static const struct hash_table_test hashtest_profiler = struct profiler_log { Lisp_Object log; - EMACS_INT gc_count; + EMACS_INT gc_count; /* Samples taken during GC. */ + EMACS_INT discarded; /* Samples evicted during table overflow. */ }; static struct profiler_log @@ -70,7 +71,7 @@ make_log (void) DEFAULT_REHASH_SIZE, DEFAULT_REHASH_THRESHOLD, Qnil, false), - 0 }; + 0, 0 }; struct Lisp_Hash_Table *h = XHASH_TABLE (log.log); /* What is special about our hash-tables is that the values are pre-filled @@ -123,8 +124,9 @@ static EMACS_INT approximate_median (log_t *log, } } -static void evict_lower_half (log_t *log) +static void evict_lower_half (struct profiler_log *plog) { + log_t *log = XHASH_TABLE (plog->log); ptrdiff_t size = ASIZE (log->key_and_value) / 2; EMACS_INT median = approximate_median (log, 0, size); @@ -134,6 +136,8 @@ static void evict_lower_half (log_t *log) if (XFIXNUM (HASH_VALUE (log, i)) <= median) { Lisp_Object key = HASH_KEY (log, i); + EMACS_INT count = XFIXNUM (HASH_VALUE (log, i)); + plog->discarded = saturated_add (plog->discarded, count); { /* FIXME: we could make this more efficient. */ Lisp_Object tmp; XSET_HASH_TABLE (tmp, log); /* FIXME: Use make_lisp_ptr. */ @@ -155,12 +159,12 @@ static void evict_lower_half (log_t *log) size for memory. */ static void -record_backtrace (log_t *log, EMACS_INT count) +record_backtrace (struct profiler_log *plog, EMACS_INT count) { + eassert (HASH_TABLE_P (plog->log)); + log_t *log = XHASH_TABLE (plog->log); if (log->next_free < 0) - /* FIXME: transfer the evicted counts to a special entry rather - than dropping them on the floor. */ - evict_lower_half (log); + evict_lower_half (plog); ptrdiff_t index = log->next_free; /* Get a "working memory" vector. */ @@ -240,7 +244,7 @@ static EMACS_INT current_sampling_interval; /* Signal handler for sampling profiler. */ static void -add_sample (struct profiler_log *log, EMACS_INT count) +add_sample (struct profiler_log *plog, EMACS_INT count) { if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ /* Special case the time-count inside GC because the hash-table @@ -249,12 +253,9 @@ add_sample (struct profiler_log *log, EMACS_INT count) not expect the ARRAY_MARK_FLAG to be set. We could try and harden the hash-table code, but it doesn't seem worth the effort. */ - log->gc_count = saturated_add (log->gc_count, count); + plog->gc_count = saturated_add (plog->gc_count, count); else - { - eassert (HASH_TABLE_P (log->log)); - record_backtrace (XHASH_TABLE (log->log), count); - } + record_backtrace (plog, count); } @@ -424,9 +425,14 @@ static Lisp_Object export_log (struct profiler_log *log) { Lisp_Object result = log->log; - Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), - make_fixnum (log->gc_count), - result); + if (log->gc_count) + Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil), + make_fixnum (log->gc_count), + result); + if (log->discarded) + Fputhash (CALLN (Fvector, QDiscarded_Samples, Qnil), + make_fixnum (log->discarded), + result); /* Here we're making the log visible to Elisp, so it's not safe any more for our use afterwards since we can't rely on its special pre-allocated keys anymore. So we have to allocate a new one. */ @@ -595,6 +601,7 @@ to make room for new entries. */); profiler_log_size = 10000; DEFSYM (Qprofiler_backtrace_equal, "profiler-backtrace-equal"); + DEFSYM (QDiscarded_Samples, "Discarded Samples"); defsubr (&Sfunction_equal);