GNU complexity is a command line tool that computes a complexity measure of C source code, similar to pmccabe, but with a different method of calculating results with short functions scoring lower than pmccabe and highly nested functionality can score considerably higher. It can be useful to locate suspicious areas in unfamiliar code, get an idea of the efforts required to either understand the code or test it, or self-assess your own code.

Bruce Korb, the maintainer, has just released version 1.5 with some bug fixes, so I’ve given it a quick try.

We’ll need to get the code, build and install it first:



wget ftp://ftp.gnu.org/gnu/complexity/complexity-1.5.tar.gz tar xvf complexity-1.5.tar.gz cd complexity-1.5 ./configure make -j8 sudo make install 1 2 3 4 5 6 wget ftp : // ftp .gnu .org / gnu / complexity / complexity - 1.5.tar.gz tar xvf complexity - 1.5.tar.gz cd complexity - 1.5 . / configure make - j8 sudo make install



The user’s manual provides some insights and an example, which I’ve used against a directory in Linux source code:



cd ~/edev/linux/arch/arm64/kernel complexity --histogram --score --thresh=3 `ls *.c` procedure fpsimd_pm_init in fpsimd.c ended before final close bracket procedure fpsimd_hotplug_init in fpsimd.c ended before final close bracket Complexity Scores Score | ln-ct | nc-lns| file-name(line): proc-name 3 28 17 perf_callchain.c(111): perf_callchain_user 3 20 18 insn.c(337): aarch64_insn_decode_immediate 3 21 19 io.c(27): __memcpy_fromio 3 21 19 io.c(56): __memcpy_toio 3 30 19 cpufeature.c(860): verify_local_cpu_capabilities 3 21 20 module.c(74): reloc_data 3 24 20 setup.c(199): request_standard_resources 3 27 20 alternative.c(89): __apply_alternatives 3 29 20 cpu_ops.c(53): cpu_read_enable_method 3 29 24 insn.c(362): aarch64_insn_encode_immediate 3 31 24 ptrace.c(76): ptrace_hbptriggered 3 54 24 kgdb.c(159): kgdb_arch_handle_exception 3 28 25 armv8_deprecated.c(145): update_insn_emulation_mode 3 32 25 debug-monitors.c(334): aarch32_break_handler 3 35 26 smp.c(413): acpi_map_gic_cpu_interface 3 42 26 process.c(249): copy_thread 3 58 27 perf_event.c(572): armv8pmu_handle_irq 3 32 28 hw_breakpoint.c(544): toggle_bp_registers 3 34 28 insn.c(1019): aarch64_insn_gen_data3 3 34 29 insn.c(519): aarch64_insn_gen_comp_branch_imm 3 56 29 setup.c(121): smp_build_mpidr_hash 3 35 30 insn.c(397): aarch64_insn_encode_register 3 42 31 signal.c(167): setup_sigframe 3 37 33 hw_breakpoint.c(349): arch_bp_generic_fields 3 49 34 setup.c(232): relocate_initrd 3 41 35 insn.c(708): aarch64_insn_gen_add_sub_imm 3 63 38 setup.c(292): setup_arch 3 109 57 cpufeature.c(469): update_cpu_features 4 46 26 smp.c(477): of_parse_and_init_cpus 4 38 31 traps.c(335): call_undef_hook 4 36 32 insn.c(924): aarch64_insn_gen_data1 4 49 33 armv8_deprecated.c(464): cp15barrier_handler 4 71 34 stacktrace.c(41): unwind_frame 4 42 35 ptrace.c(437): hw_break_set 4 42 37 insn.c(968): aarch64_insn_gen_data2 4 49 37 smp.c(708): handle_IPI 4 58 37 hw_breakpoint.c(231): hw_breakpoint_control 4 47 40 insn.c(757): aarch64_insn_gen_bitfield 4 54 44 ptrace.c(376): hw_break_get 4 60 44 armv8_deprecated.c(378): swp_handler 5 28 27 hw_breakpoint.c(198): hw_breakpoint_slot_setup 5 45 35 ptrace.c(672): compat_gpr_get 5 50 36 hw_breakpoint.c(764): reinstall_suspended_bps 5 59 37 hw_breakpoint.c(476): arch_validate_hwbkpt_settings 5 48 42 insn.c(811): aarch64_insn_gen_movewide 5 49 42 insn.c(868): aarch64_insn_gen_add_sub_shifted_reg 5 60 44 psci.c(36): cpu_psci_cpu_init_idle 5 49 46 insn.c(277): aarch64_get_imm_shift_mask 5 95 89 asm-offsets.c(34): main 6 34 32 cpufeature.c(792): __raw_read_system_reg 6 67 37 signal.c(332): do_signal 6 43 39 topology.c(50): parse_core 6 54 41 ptrace.c(724): compat_gpr_set 6 74 44 traps.c(147): dump_backtrace 6 55 49 insn.c(646): aarch64_insn_gen_load_store_pair 6 76 49 hw_breakpoint.c(393): arch_build_bp_info 6 71 52 hw_breakpoint.c(584): breakpoint_handler 6 61 54 insn.c(1062): aarch64_insn_gen_logical_shifted_reg 6 73 59 ptrace.c(1120): compat_arch_ptrace 8 50 30 cpuinfo.c(102): c_show 8 64 50 topology.c(97): parse_cluster 8 97 65 hw_breakpoint.c(660): watchpoint_handler 9 44 32 traps.c(59): dump_mem 11 71 55 signal32.c(129): copy_siginfo_to_user32 31 198 172 module.c(184): apply_relocate_add Complexity Histogram Score-Range Lin-Ct 0-9 2206 ************************************************************ 10-19 55 * 20-29 0 30-39 172 ***** Scored procedure ct: 65 Non-comment line ct: 2433 Average line score: 7 25%-ile score: 3 (75% in higher score procs) 50%-ile score: 5 (half in higher score procs) 75%-ile score: 6 (25% in higher score procs) Highest score: 31 (apply_relocate_add() in module.c) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 cd ~ / edev / linux / arch / arm64 / kernel complexity -- histogram -- score -- thresh = 3 ` ls * .c ` procedure fpsimd_pm_init in fpsimd .c ended before final close bracket procedure fpsimd_hotplug_init in fpsimd .c ended before final close bracket Complexity Scores Score | ln - ct | nc - lns | file - name ( line ) : proc - name 3 28 17 perf_callchain .c ( 111 ) : perf_callchain_user 3 20 18 insn .c ( 337 ) : aarch64_insn_decode_immediate 3 21 19 io .c ( 27 ) : __memcpy_fromio 3 21 19 io .c ( 56 ) : __memcpy_toio 3 30 19 cpufeature .c ( 860 ) : verify_local_cpu_capabilities 3 21 20 module .c ( 74 ) : reloc_data 3 24 20 setup .c ( 199 ) : request_standard_resources 3 27 20 alternative .c ( 89 ) : __apply_alternatives 3 29 20 cpu_ops .c ( 53 ) : cpu_read_enable_method 3 29 24 insn .c ( 362 ) : aarch64_insn_encode_immediate 3 31 24 ptrace .c ( 76 ) : ptrace_hbptriggered 3 54 24 kgdb .c ( 159 ) : kgdb_arch_handle_exception 3 28 25 armv8_deprecated .c ( 145 ) : update_insn_emulation_mode 3 32 25 debug - monitors .c ( 334 ) : aarch32_break_handler 3 35 26 smp .c ( 413 ) : acpi_map_gic_cpu_interface 3 42 26 process .c ( 249 ) : copy_thread 3 58 27 perf_event .c ( 572 ) : armv8pmu_handle_irq 3 32 28 hw_breakpoint .c ( 544 ) : toggle_bp_registers 3 34 28 insn .c ( 1019 ) : aarch64_insn_gen_data3 3 34 29 insn .c ( 519 ) : aarch64_insn_gen_comp_branch_imm 3 56 29 setup .c ( 121 ) : smp_build_mpidr_hash 3 35 30 insn .c ( 397 ) : aarch64_insn_encode_register 3 42 31 signal .c ( 167 ) : setup_sigframe 3 37 33 hw_breakpoint .c ( 349 ) : arch_bp_generic_fields 3 49 34 setup .c ( 232 ) : relocate_initrd 3 41 35 insn .c ( 708 ) : aarch64_insn_gen_add_sub_imm 3 63 38 setup .c ( 292 ) : setup_arch 3 109 57 cpufeature .c ( 469 ) : update_cpu_features 4 46 26 smp .c ( 477 ) : of_parse_and_init_cpus 4 38 31 traps .c ( 335 ) : call_undef_hook 4 36 32 insn .c ( 924 ) : aarch64_insn_gen_data1 4 49 33 armv8_deprecated .c ( 464 ) : cp15barrier_handler 4 71 34 stacktrace .c ( 41 ) : unwind_frame 4 42 35 ptrace .c ( 437 ) : hw_break_set 4 42 37 insn .c ( 968 ) : aarch64_insn_gen_data2 4 49 37 smp .c ( 708 ) : handle_IPI 4 58 37 hw_breakpoint .c ( 231 ) : hw_breakpoint_control 4 47 40 insn .c ( 757 ) : aarch64_insn_gen_bitfield 4 54 44 ptrace .c ( 376 ) : hw_break_get 4 60 44 armv8_deprecated .c ( 378 ) : swp_handler 5 28 27 hw_breakpoint .c ( 198 ) : hw_breakpoint_slot_setup 5 45 35 ptrace .c ( 672 ) : compat_gpr_get 5 50 36 hw_breakpoint .c ( 764 ) : reinstall_suspended_bps 5 59 37 hw_breakpoint .c ( 476 ) : arch_validate_hwbkpt_settings 5 48 42 insn .c ( 811 ) : aarch64_insn_gen_movewide 5 49 42 insn .c ( 868 ) : aarch64_insn_gen_add_sub_shifted_reg 5 60 44 psci .c ( 36 ) : cpu_psci_cpu_init_idle 5 49 46 insn .c ( 277 ) : aarch64_get_imm_shift_mask 5 95 89 asm - offsets .c ( 34 ) : main 6 34 32 cpufeature .c ( 792 ) : __raw_read_system_reg 6 67 37 signal .c ( 332 ) : do_signal 6 43 39 topology .c ( 50 ) : parse_core 6 54 41 ptrace .c ( 724 ) : compat_gpr_set 6 74 44 traps .c ( 147 ) : dump_backtrace 6 55 49 insn .c ( 646 ) : aarch64_insn_gen_load_store_pair 6 76 49 hw_breakpoint .c ( 393 ) : arch_build_bp_info 6 71 52 hw_breakpoint .c ( 584 ) : breakpoint_handler 6 61 54 insn .c ( 1062 ) : aarch64_insn_gen_logical_shifted_reg 6 73 59 ptrace .c ( 1120 ) : compat_arch_ptrace 8 50 30 cpuinfo .c ( 102 ) : c_show 8 64 50 topology .c ( 97 ) : parse_cluster 8 97 65 hw_breakpoint .c ( 660 ) : watchpoint_handler 9 44 32 traps .c ( 59 ) : dump_mem 11 71 55 signal32 .c ( 129 ) : copy_siginfo_to_user32 31 198 172 module .c ( 184 ) : apply_relocate_add Complexity Histogram Score - Range Lin - Ct 0 - 9 2206 ************************************************************ 10 - 19 55 * 20 - 29 0 30 - 39 172 ***** Scored procedure ct : 65 Non - comment line ct : 2433 Average line score : 7 25 % - ile score : 3 ( 75 % in higher score procs ) 50 % - ile score : 5 ( half in higher score procs ) 75 % - ile score : 6 ( 25 % in higher score procs ) Highest score : 31 ( apply_relocate_add ( ) in module .c )



The resulting table shows six information per line: the computed score, the number of lines between the opening and closing curly braces (ln-ct), the number of non-comment, non-blank lines found there (nc-lns), the name of the source file, the line number of the opening curly brace, and the name of the procedure. The higher the score the more complex is the procedure, and thres parameter is used to remove the lowest complexity function from the list. The ranking goes as follows:

‘0-9’ – Easily maintained code.

‘10-19’ – Maintained with little trouble.

‘20-29’ – Maintained with some effort.

‘30-39’ – Difficult to maintain code.

‘40-49’ – Hard to maintain code.

‘50-99’ – Unmaintainable code.

‘100-199’ – Crazy making difficult code.

‘200+’ – I only wish I were kidding.

Have fun.