diff options
| author | Stefan Monnier | 2023-03-10 15:30:20 -0500 |
|---|---|---|
| committer | Stefan Monnier | 2023-03-10 15:30:20 -0500 |
| commit | 83be04c66ffaec86aee136b9a94979169d1ba68d (patch) | |
| tree | f39bc07c848c5fe9fba781023e924742b5f31fa7 /src | |
| parent | f97d4b9e54c7de3f67d78be8c63afcdb6b704531 (diff) | |
| download | emacs-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.c | 144 |
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 | ||
| 52 | static Lisp_Object | 52 | struct profiler_log { |
| 53 | Lisp_Object log; | ||
| 54 | EMACS_INT gc_count; | ||
| 55 | }; | ||
| 56 | |||
| 57 | static struct profiler_log | ||
| 53 | make_log (void) | 58 | make_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. */ |
| 225 | static Lisp_Object cpu_log; | 232 | static struct profiler_log cpu; |
| 226 | 233 | ||
| 227 | /* Separate counter for the time spent in the GC. */ | 234 | /* Hash-table log of Memory profiler. */ |
| 228 | static EMACS_INT cpu_gc_count; | 235 | static struct profiler_log memory; |
| 229 | |||
| 230 | /* Separate counter for the memory allocations during GC. */ | ||
| 231 | static EMACS_INT mem_gc_count; | ||
| 232 | 236 | ||
| 233 | /* The current sampling interval in nanoseconds. */ | 237 | /* The current sampling interval in nanoseconds. */ |
| 234 | static EMACS_INT current_sampling_interval; | 238 | static 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 | ||
| 238 | static void | 242 | static void |
| 239 | handle_profiler_signal (int signal) | 243 | add_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 | |||
| 261 | static void | ||
| 262 | handle_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 | ||
| 265 | static void | 276 | static 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 | ||
| 423 | static Lisp_Object | ||
| 424 | export_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 | |||
| 415 | DEFUN ("profiler-cpu-log", Fprofiler_cpu_log, Sprofiler_cpu_log, | 438 | DEFUN ("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. | |||
| 421 | Before returning, a new log is allocated for future samples. */) | 444 | Before 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. */ |
| 440 | bool profiler_memory_running; | 454 | bool profiler_memory_running; |
| 441 | 455 | ||
| 442 | static Lisp_Object memory_log; | ||
| 443 | |||
| 444 | DEFUN ("profiler-memory-start", Fprofiler_memory_start, Sprofiler_memory_start, | 456 | DEFUN ("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. | |||
| 496 | Before returning, a new log is allocated for future samples. */) | 505 | Before 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. */) | |||
| 515 | void | 515 | void |
| 516 | malloc_probe (size_t size) | 516 | malloc_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 | ||
| 533 | DEFUN ("function-equal", Ffunction_equal, Sfunction_equal, 2, 2, 0, | 521 | DEFUN ("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 | } |