aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorStefan Monnier2023-03-10 15:30:20 -0500
committerStefan Monnier2023-03-10 15:30:20 -0500
commit83be04c66ffaec86aee136b9a94979169d1ba68d (patch)
treef39bc07c848c5fe9fba781023e924742b5f31fa7 /src
parentf97d4b9e54c7de3f67d78be8c63afcdb6b704531 (diff)
downloademacs-83be04c66ffaec86aee136b9a94979169d1ba68d.tar.gz
emacs-83be04c66ffaec86aee136b9a94979169d1ba68d.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/profiler.c144
1 files changed, 66 insertions, 78 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 =
49 hashfn_profiler, 49 hashfn_profiler,
50 }; 50 };
51 51
52static Lisp_Object 52struct profiler_log {
53 Lisp_Object log;
54 EMACS_INT gc_count;
55};
56
57static struct profiler_log
53make_log (void) 58make_log (void)
54{ 59{
55 /* We use a standard Elisp hash-table object, but we use it in 60 /* We use a standard Elisp hash-table object, but we use it in
@@ -60,11 +65,13 @@ make_log (void)
60 = clip_to_bounds (0, profiler_log_size, MOST_POSITIVE_FIXNUM); 65 = clip_to_bounds (0, profiler_log_size, MOST_POSITIVE_FIXNUM);
61 ptrdiff_t max_stack_depth 66 ptrdiff_t max_stack_depth
62 = clip_to_bounds (0, profiler_max_stack_depth, PTRDIFF_MAX);; 67 = clip_to_bounds (0, profiler_max_stack_depth, PTRDIFF_MAX);;
63 Lisp_Object log = make_hash_table (hashtest_profiler, heap_size, 68 struct profiler_log log
64 DEFAULT_REHASH_SIZE, 69 = { make_hash_table (hashtest_profiler, heap_size,
65 DEFAULT_REHASH_THRESHOLD, 70 DEFAULT_REHASH_SIZE,
66 Qnil, false); 71 DEFAULT_REHASH_THRESHOLD,
67 struct Lisp_Hash_Table *h = XHASH_TABLE (log); 72 Qnil, false),
73 0 };
74 struct Lisp_Hash_Table *h = XHASH_TABLE (log.log);
68 75
69 /* What is special about our hash-tables is that the values are pre-filled 76 /* What is special about our hash-tables is that the values are pre-filled
70 with the vectors we'll use as keys. */ 77 with the vectors we'll use as keys. */
@@ -222,13 +229,10 @@ static enum profiler_cpu_running
222 profiler_cpu_running; 229 profiler_cpu_running;
223 230
224/* Hash-table log of CPU profiler. */ 231/* Hash-table log of CPU profiler. */
225static Lisp_Object cpu_log; 232static struct profiler_log cpu;
226 233
227/* Separate counter for the time spent in the GC. */ 234/* Hash-table log of Memory profiler. */
228static EMACS_INT cpu_gc_count; 235static struct profiler_log memory;
229
230/* Separate counter for the memory allocations during GC. */
231static EMACS_INT mem_gc_count;
232 236
233/* The current sampling interval in nanoseconds. */ 237/* The current sampling interval in nanoseconds. */
234static EMACS_INT current_sampling_interval; 238static EMACS_INT current_sampling_interval;
@@ -236,30 +240,37 @@ static EMACS_INT current_sampling_interval;
236/* Signal handler for sampling profiler. */ 240/* Signal handler for sampling profiler. */
237 241
238static void 242static void
239handle_profiler_signal (int signal) 243add_sample (struct profiler_log *log, EMACS_INT count)
240{ 244{
241 if (EQ (backtrace_top_function (), QAutomatic_GC)) 245 if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */
242 /* Special case the time-count inside GC because the hash-table 246 /* Special case the time-count inside GC because the hash-table
243 code is not prepared to be used while the GC is running. 247 code is not prepared to be used while the GC is running.
244 More specifically it uses ASIZE at many places where it does 248 More specifically it uses ASIZE at many places where it does
245 not expect the ARRAY_MARK_FLAG to be set. We could try and 249 not expect the ARRAY_MARK_FLAG to be set. We could try and
246 harden the hash-table code, but it doesn't seem worth the 250 harden the hash-table code, but it doesn't seem worth the
247 effort. */ 251 effort. */
248 cpu_gc_count = saturated_add (cpu_gc_count, 1); 252 log->gc_count = saturated_add (log->gc_count, count);
249 else 253 else
250 { 254 {
251 EMACS_INT count = 1; 255 eassert (HASH_TABLE_P (log->log));
256 record_backtrace (XHASH_TABLE (log->log), count);
257 }
258}
259
260
261static void
262handle_profiler_signal (int signal)
263{
264 EMACS_INT count = 1;
252#if defined HAVE_ITIMERSPEC && defined HAVE_TIMER_GETOVERRUN 265#if defined HAVE_ITIMERSPEC && defined HAVE_TIMER_GETOVERRUN
253 if (profiler_timer_ok) 266 if (profiler_timer_ok)
254 { 267 {
255 int overruns = timer_getoverrun (profiler_timer); 268 int overruns = timer_getoverrun (profiler_timer);
256 eassert (overruns >= 0); 269 eassert (overruns >= 0);
257 count += overruns; 270 count += overruns;
258 }
259#endif
260 eassert (HASH_TABLE_P (cpu_log));
261 record_backtrace (XHASH_TABLE (cpu_log), count);
262 } 271 }
272#endif
273 add_sample (&cpu, count);
263} 274}
264 275
265static void 276static void
@@ -346,11 +357,8 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */)
346 if (profiler_cpu_running) 357 if (profiler_cpu_running)
347 error ("CPU profiler is already running"); 358 error ("CPU profiler is already running");
348 359
349 if (NILP (cpu_log)) 360 if (NILP (cpu.log))
350 { 361 cpu = make_log ();
351 cpu_gc_count = 0;
352 cpu_log = make_log ();
353 }
354 362
355 int status = setup_cpu_timer (sampling_interval); 363 int status = setup_cpu_timer (sampling_interval);
356 if (status < 0) 364 if (status < 0)
@@ -412,6 +420,21 @@ DEFUN ("profiler-cpu-running-p",
412 return profiler_cpu_running ? Qt : Qnil; 420 return profiler_cpu_running ? Qt : Qnil;
413} 421}
414 422
423static Lisp_Object
424export_log (struct profiler_log *log)
425{
426 Lisp_Object result = log->log;
427 Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil),
428 make_fixnum (log->gc_count),
429 result);
430 /* Here we're making the log visible to Elisp, so it's not safe any
431 more for our use afterwards since we can't rely on its special
432 pre-allocated keys anymore. So we have to allocate a new one. */
433 if (profiler_cpu_running)
434 *log = make_log ();
435 return result;
436}
437
415DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log, 438DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log,
416 0, 0, 0, 439 0, 0, 0,
417 doc: /* Return the current cpu profiler log. 440 doc: /* Return the current cpu profiler log.
@@ -421,16 +444,7 @@ of functions, where the last few elements may be nil.
421Before returning, a new log is allocated for future samples. */) 444Before returning, a new log is allocated for future samples. */)
422 (void) 445 (void)
423{ 446{
424 Lisp_Object result = cpu_log; 447 return (export_log (&cpu));
425 /* Here we're making the log visible to Elisp, so it's not safe any
426 more for our use afterwards since we can't rely on its special
427 pre-allocated keys anymore. So we have to allocate a new one. */
428 cpu_log = profiler_cpu_running ? make_log () : Qnil;
429 Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil),
430 make_fixnum (cpu_gc_count),
431 result);
432 cpu_gc_count = 0;
433 return result;
434} 448}
435#endif /* PROFILER_CPU_SUPPORT */ 449#endif /* PROFILER_CPU_SUPPORT */
436 450
@@ -439,8 +453,6 @@ Before returning, a new log is allocated for future samples. */)
439/* True if memory profiler is running. */ 453/* True if memory profiler is running. */
440bool profiler_memory_running; 454bool profiler_memory_running;
441 455
442static Lisp_Object memory_log;
443
444DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start, 456DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start,
445 0, 0, 0, 457 0, 0, 0,
446 doc: /* Start/restart the memory profiler. 458 doc: /* Start/restart the memory profiler.
@@ -453,11 +465,8 @@ See also `profiler-log-size' and `profiler-max-stack-depth'. */)
453 if (profiler_memory_running) 465 if (profiler_memory_running)
454 error ("Memory profiler is already running"); 466 error ("Memory profiler is already running");
455 467
456 if (NILP (memory_log)) 468 if (NILP (memory.log))
457 { 469 memory = make_log ();
458 mem_gc_count = 0;
459 memory_log = make_log ();
460 }
461 470
462 profiler_memory_running = true; 471 profiler_memory_running = true;
463 472
@@ -496,16 +505,7 @@ of functions, where the last few elements may be nil.
496Before returning, a new log is allocated for future samples. */) 505Before returning, a new log is allocated for future samples. */)
497 (void) 506 (void)
498{ 507{
499 Lisp_Object result = memory_log; 508 return (export_log (&memory));
500 /* Here we're making the log visible to Elisp , so it's not safe any
501 more for our use afterwards since we can't rely on its special
502 pre-allocated keys anymore. So we have to allocate a new one. */
503 memory_log = profiler_memory_running ? make_log () : Qnil;
504 Fputhash (CALLN (Fvector, QAutomatic_GC, Qnil),
505 make_fixnum (mem_gc_count),
506 result);
507 mem_gc_count = 0;
508 return result;
509} 509}
510 510
511 511
@@ -515,19 +515,7 @@ Before returning, a new log is allocated for future samples. */)
515void 515void
516malloc_probe (size_t size) 516malloc_probe (size_t size)
517{ 517{
518 if (EQ (backtrace_top_function (), QAutomatic_GC)) /* bug#60237 */ 518 add_sample (&memory, min (size, MOST_POSITIVE_FIXNUM));
519 /* Special case the malloc-count inside GC because the hash-table
520 code is not prepared to be used while the GC is running.
521 E.g. it uses ASIZE at many places where it does not expect
522 the ARRAY_MARK_FLAG to be set and in anyn case it'd modify the
523 heap behind the GC's back. */
524 mem_gc_count = saturated_add (mem_gc_count, size);
525 else
526 {
527 eassert (HASH_TABLE_P (memory_log));
528 record_backtrace (XHASH_TABLE (memory_log),
529 min (size, MOST_POSITIVE_FIXNUM));
530 }
531} 519}
532 520
533DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0, 521DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0,
@@ -612,16 +600,16 @@ to make room for new entries. */);
612 600
613#ifdef PROFILER_CPU_SUPPORT 601#ifdef PROFILER_CPU_SUPPORT
614 profiler_cpu_running = NOT_RUNNING; 602 profiler_cpu_running = NOT_RUNNING;
615 cpu_log = Qnil; 603 cpu.log = Qnil;
616 staticpro (&cpu_log); 604 staticpro (&cpu.log);
617 defsubr (&Sprofiler_cpu_start); 605 defsubr (&Sprofiler_cpu_start);
618 defsubr (&Sprofiler_cpu_stop); 606 defsubr (&Sprofiler_cpu_stop);
619 defsubr (&Sprofiler_cpu_running_p); 607 defsubr (&Sprofiler_cpu_running_p);
620 defsubr (&Sprofiler_cpu_log); 608 defsubr (&Sprofiler_cpu_log);
621#endif 609#endif
622 profiler_memory_running = false; 610 profiler_memory_running = false;
623 memory_log = Qnil; 611 memory.log = Qnil;
624 staticpro (&memory_log); 612 staticpro (&memory.log);
625 defsubr (&Sprofiler_memory_start); 613 defsubr (&Sprofiler_memory_start);
626 defsubr (&Sprofiler_memory_stop); 614 defsubr (&Sprofiler_memory_stop);
627 defsubr (&Sprofiler_memory_running_p); 615 defsubr (&Sprofiler_memory_running_p);
@@ -636,16 +624,16 @@ syms_of_profiler_for_pdumper (void)
636 if (dumped_with_pdumper_p ()) 624 if (dumped_with_pdumper_p ())
637 { 625 {
638#ifdef PROFILER_CPU_SUPPORT 626#ifdef PROFILER_CPU_SUPPORT
639 cpu_log = Qnil; 627 cpu.log = Qnil;
640#endif 628#endif
641 memory_log = Qnil; 629 memory.log = Qnil;
642 } 630 }
643 else 631 else
644 { 632 {
645#ifdef PROFILER_CPU_SUPPORT 633#ifdef PROFILER_CPU_SUPPORT
646 eassert (NILP (cpu_log)); 634 eassert (NILP (cpu.log));
647#endif 635#endif
648 eassert (NILP (memory_log)); 636 eassert (NILP (memory.log));
649 } 637 }
650 638
651} 639}