The following artificial testcase for -fanalyzer seems to take at least several minutes; perhaps much more: #define DOUBLE_FREE() \ do { \ void *p = __builtin_malloc (1024); \ __builtin_free (p); \ __builtin_free (p); \ } while (0) #define DOUBLE_FREE_x_10() \ do { \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ DOUBLE_FREE(); \ } while (0) #define DOUBLE_FREE_x_100() \ do { \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ DOUBLE_FREE_x_10(); \ } while (0) #define DOUBLE_FREE_x_1000() \ do { \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ DOUBLE_FREE_x_100(); \ } while (0) void test_1 (void) { DOUBLE_FREE_x_1000 (); } Breaking into it shows that it seems to be spending the bulk of its time exploring paths to determine if they are feasible (despite the fact that there's no control flow at all): (gdb) bt #0 0x0000000000f22750 in hash_table<hash_map<ana::region const*, ana::binding_cluster*, simple_hashmap_traits<default_hash_traits<ana::region const*>, ana::binding_cluster*> >::hash_entry, false, xcallocator>::find_slot_with_hash (this=this@entry=0x7fffffffc768, comparable=@0x7fffffffc5f8: 0x292b5c0, hash=5396152, insert=insert@entry=INSERT) at ../../src/gcc/hash-traits.h:186 #1 0x0000000000f1b976 in hash_map<ana::region const*, ana::binding_cluster*, simple_hashmap_traits<default_hash_traits<ana::region const*>, ana::binding_cluster*> >::put (v=<optimized out>, k=@0x7fffffffc5f8: 0x292b5c0, this=0x7fffffffc768) at ../../src/gcc/hash-traits.h:162 #2 ana::store::store (this=this@entry=0x7fffffffc768, other=...) at ../../src/gcc/analyzer/store.cc:2046 #3 0x0000000000eeaecf in ana::region_model::region_model (this=this@entry=0x7fffffffc760, other=...) at ../../src/gcc/analyzer/region-model.cc:260 #4 0x0000000000eccf71 in ana::feasibility_state::feasibility_state (this=0x7fffffffc760, other=...) at ../../src/gcc/analyzer/engine.cc:4478 #5 0x00000000018a51f0 in ana::epath_finder::process_worklist_item (this=<optimized out>, worklist=0x7fffffffc950, tg=..., fg=0x7fffffffc8a0, target_enode=0x2bdcf60, diag_idx=305, out_best_path=0x7fffffffc858) at ../../src/gcc/analyzer/feasible-graph.h:96 #6 0x00000000018a603c in ana::epath_finder::explore_feasible_paths (this=0x7fffffffcb90, target_enode=0x2bdcf60, desc=0x1a64f09 "double_free", diag_idx=305) at ../../src/gcc/analyzer/diagnostic-manager.cc:414 #7 0x00000000018a6787 in ana::saved_diagnostic::calc_best_epath (this=0x2bddbf0, pf=0x7fffffffcb90) at ../../src/gcc/analyzer/diagnostic-manager.cc:736 #8 0x00000000018aaece in ana::dedupe_winners::add (this=this@entry=0x7fffffffcba0, logger=0x0, pf=pf@entry=0x7fffffffcb90, sd=0x2bddbf0) at ../../src/gcc/analyzer/diagnostic-manager.cc:1065 #9 0x00000000018a7ece in ana::diagnostic_manager::emit_saved_diagnostics (this=0x7fffffffcea0, eg=...) at ../../src/gcc/analyzer/analyzer-logging.h:150 #10 0x0000000000ed79ab in ana::impl_run_checkers (logger=logger@entry=0x0) at ../../src/gcc/analyzer/exploded-graph.h:857 #11 0x0000000000ed8804 in ana::run_checkers () at ../../src/gcc/analyzer/analyzer-logging.h:150
Also takes a long time with -Wno-analyzer-double-free; perhaps we ought to reject saved_diagnostics that will ultimately not be emitted.
I suspect that this issue is due to building a feasible_graph per saved diagnostic, thus leading to an O(N^2) where as the function gets bigger, each individual diagnostic requires more work. Perhaps fixable by amortizing the work, by sharing one feasible_graph for all diagnostics in the diagnostic_manager.
The master branch has been updated by David Malcolm <dmalcolm@gcc.gnu.org>: https://gcc.gnu.org/g:7fd6e36ea9aa8575841ff1da08b4aebc0298abe2 commit r12-7677-g7fd6e36ea9aa8575841ff1da08b4aebc0298abe2 Author: David Malcolm <dmalcolm@redhat.com> Date: Wed Mar 16 10:54:44 2022 -0400 analyzer: early rejection of disabled warnings [PR104955] Avoid generating execution paths for warnings that are ultimately rejected due to -Wno-analyzer-* flags. This improves the test case from taking at least several minutes (before I killed it) to taking under a second. This doesn't fix the slowdown seen in PR analyzer/104955 with large numbers of warnings when the warnings are still enabled. gcc/analyzer/ChangeLog: PR analyzer/104955 * diagnostic-manager.cc (get_emission_location): New. (diagnostic_manager::diagnostic_manager): Initialize m_num_disabled_diagnostics. (diagnostic_manager::add_diagnostic): Reject diagnostics that will eventually be rejected due to being disabled. (diagnostic_manager::emit_saved_diagnostics): Log the number of disabled diagnostics. (diagnostic_manager::emit_saved_diagnostic): Split out logic for determining emission location to get_emission_location. * diagnostic-manager.h (diagnostic_manager::m_num_disabled_diagnostics): New field. * engine.cc (stale_jmp_buf::get_controlling_option): New. (stale_jmp_buf::emit): Use it. * pending-diagnostic.h (pending_diagnostic::get_controlling_option): New vfunc. * region-model.cc (poisoned_value_diagnostic::get_controlling_option): New. (poisoned_value_diagnostic::emit): Use it. (shift_count_negative_diagnostic::get_controlling_option): New. (shift_count_negative_diagnostic::emit): Use it. (shift_count_overflow_diagnostic::get_controlling_option): New. (shift_count_overflow_diagnostic::emit): Use it. (dump_path_diagnostic::get_controlling_option): New. (dump_path_diagnostic::emit): Use it. (write_to_const_diagnostic::get_controlling_option): New. (write_to_const_diagnostic::emit): Use it. (write_to_string_literal_diagnostic::get_controlling_option): New. (write_to_string_literal_diagnostic::emit): Use it. * sm-file.cc (double_fclose::get_controlling_option): New. (double_fclose::emit): Use it. (file_leak::get_controlling_option): New. (file_leak::emit): Use it. * sm-malloc.cc (mismatching_deallocation::get_controlling_option): New. (mismatching_deallocation::emit): Use it. (double_free::get_controlling_option): New. (double_free::emit): Use it. (possible_null_deref::get_controlling_option): New. (possible_null_deref::emit): Use it. (possible_null_arg::get_controlling_option): New. (possible_null_arg::emit): Use it. (null_deref::get_controlling_option): New. (null_deref::emit): Use it. (null_arg::get_controlling_option): New. (null_arg::emit): Use it. (use_after_free::get_controlling_option): New. (use_after_free::emit): Use it. (malloc_leak::get_controlling_option): New. (malloc_leak::emit): Use it. (free_of_non_heap::get_controlling_option): New. (free_of_non_heap::emit): Use it. * sm-pattern-test.cc (pattern_match::get_controlling_option): New. (pattern_match::emit): Use it. * sm-sensitive.cc (exposure_through_output_file::get_controlling_option): New. (exposure_through_output_file::emit): Use it. * sm-signal.cc (signal_unsafe_call::get_controlling_option): New. (signal_unsafe_call::emit): Use it. * sm-taint.cc (tainted_array_index::get_controlling_option): New. (tainted_array_index::emit): Use it. (tainted_offset::get_controlling_option): New. (tainted_offset::emit): Use it. (tainted_size::get_controlling_option): New. (tainted_size::emit): Use it. (tainted_divisor::get_controlling_option): New. (tainted_divisor::emit): Use it. (tainted_allocation_size::get_controlling_option): New. (tainted_allocation_size::emit): Use it. gcc/testsuite/ChangeLog: * gcc.dg/analyzer/many-disabled-diagnostics.c: New test. * gcc.dg/plugin/analyzer_gil_plugin.c (gil_diagnostic::get_controlling_option): New. (double_save_thread::emit): Use it. (fncall_without_gil::emit): Likewise. (pyobject_usage_without_gil::emit): Likewise. Signed-off-by: David Malcolm <dmalcolm@redhat.com>