├── safety-architecture └── tools │ └── callgraph-tool │ ├── tests │ ├── resources │ │ ├── crix-callgraph │ │ │ ├── test-same-funcname.module.h │ │ │ ├── cg-temp │ │ │ │ ├── cg-test-template │ │ │ │ │ ├── core │ │ │ │ │ │ ├── file2.h │ │ │ │ │ │ ├── module.mk │ │ │ │ │ │ ├── file8.c │ │ │ │ │ │ ├── file1.h │ │ │ │ │ │ ├── log.h │ │ │ │ │ │ ├── file4.h │ │ │ │ │ │ ├── file3.h │ │ │ │ │ │ ├── file2.c │ │ │ │ │ │ ├── file5.c │ │ │ │ │ │ ├── file1.c │ │ │ │ │ │ ├── file7.c │ │ │ │ │ │ ├── file6.c │ │ │ │ │ │ ├── file3.c │ │ │ │ │ │ └── file4.c │ │ │ │ │ ├── include │ │ │ │ │ │ ├── file3.h │ │ │ │ │ │ ├── file1.h │ │ │ │ │ │ ├── common.h │ │ │ │ │ │ └── file2.h │ │ │ │ │ ├── init │ │ │ │ │ │ ├── module.mk │ │ │ │ │ │ └── main.c │ │ │ │ │ └── README.md │ │ │ │ └── Makefile │ │ │ ├── test-same-funcname.module.c │ │ │ ├── test-hello.cpp │ │ │ ├── test-asminline.c │ │ │ ├── test-same-funcname.c │ │ │ ├── test-modules.child.h │ │ │ ├── test-cast-fptr.c │ │ │ ├── test-ta-mlta.c │ │ │ ├── test-namespace-1.cpp │ │ │ ├── test-modules.base.cpp │ │ │ ├── test-namespace-2.cpp │ │ │ ├── test-modules.base.h │ │ │ ├── test-opt.c │ │ │ ├── test-cast-struct.c │ │ │ ├── test-mlta-notassigned.c │ │ │ ├── test-modules.child.cpp │ │ │ ├── test-escape.c │ │ │ ├── test-union.c │ │ │ ├── test-sizeof.c │ │ │ ├── test-inheritance-multiple-3.cpp │ │ │ ├── test-inheritance-multiple-1.cpp │ │ │ ├── test-inheritance-multilevel.cpp │ │ │ ├── test-mlta-null.c │ │ │ ├── test-mlta-memcpy.c │ │ │ ├── test-mlta-basic.c │ │ │ ├── test-inline.c │ │ │ ├── test-mlta-assign-value.c │ │ │ ├── test-inheritance-global.cpp │ │ │ ├── test-inheritance-basic.cpp │ │ │ ├── test-mlta-misc.c │ │ │ ├── test-mlta-arr.c │ │ │ ├── test-mlta-confinestore.c │ │ │ ├── generate_expected_output.sh │ │ │ ├── test-namespace-3.cpp │ │ │ ├── test-bitfield.c │ │ │ ├── test-inheritance-multiple-2.cpp │ │ │ ├── test-mlta-x86-init.c │ │ │ ├── generate_bitcodes.sh │ │ │ └── Makefile │ │ ├── find_coverage_gaps │ │ │ └── find_coverage_gaps_test_data.tar.bz2 │ │ ├── find_callchains │ │ │ ├── expect_function_filename_specific.csv │ │ │ ├── expect_recursive_chains.csv │ │ │ ├── chains.csv │ │ │ ├── expect_single_chain_both.csv │ │ │ ├── expect_single_chain_left.csv │ │ │ ├── expect_single_chain_right.csv │ │ │ ├── chain_calls.csv │ │ │ └── chain_calls_dupl.csv │ │ ├── query-callgraph │ │ │ ├── test-duplicate-path.c │ │ │ ├── generate_bitcodes.sh │ │ │ ├── test-chain.c │ │ │ ├── Makefile │ │ │ ├── test-demo.c │ │ │ └── test-cpp-demo.cpp │ │ └── filter │ │ │ ├── expect_one_col_mult_filter.csv │ │ │ ├── expect_mult_col_mult_filter.csv │ │ │ ├── expect_mult_col_one_filter.csv │ │ │ ├── expect_one_col_one_filter.csv │ │ │ └── calls.csv │ ├── test_utils.py │ └── test_filter_callgraph.py │ ├── doc │ ├── chainX.jpg │ ├── long_chain.jpg │ ├── sys_close.png │ ├── test_demo.png │ ├── multi_chain.jpg │ ├── all_callchains.jpg │ ├── related │ │ ├── related.png │ │ └── related.md │ ├── sock_recvmsg_d1.png │ ├── sock_recvmsg_d2.png │ ├── test_cpp_demo.png │ ├── test_crixcg_d1.png │ ├── test_cpp_demo_libc.png │ ├── sock_recvmsg_syscalls.png │ ├── coverage │ │ ├── ksys_mmap_pgoff.png │ │ ├── syzkaller_coverage.md │ │ └── coverage_gaps.md │ ├── sock_recvmsg_d1_inverse.png │ ├── sock_recvmsg_d2_coverage.png │ ├── sock_recvmsg_d2_inverse.png │ ├── test_crixcg_filtered_d1.png │ ├── test_crixcg_filtered_d3.png │ ├── test_cpp_demo_alldemangled.png │ ├── from_sock_recvmsg_to_x64_sys_recv_left.jpg │ ├── licenses │ │ └── license_info.md │ ├── using_custom_llvm.md │ ├── find_callchains_examples.md │ └── simple_c_example.md │ ├── .gitignore │ ├── requirements.txt │ ├── .travis.yml │ ├── CONTRIBUTORS │ ├── AUTHORS │ ├── src │ ├── CMakeLists.txt │ └── lib │ │ ├── CMakeLists.txt │ │ ├── VirtualCallTargets.h │ │ ├── Common.h │ │ ├── Analyzer.h │ │ ├── CallGraph.h │ │ ├── Common.cc │ │ └── Analyzer.cc │ ├── env.sh │ ├── scripts │ ├── format_coverage.py │ ├── clang_format.sh │ ├── grapher.py │ ├── utils.py │ ├── filter_callgraph.py │ └── clang_download.py │ ├── CONTRIBUTING.md │ ├── LICENSES │ └── LicenseRef-LLVM.txt │ ├── Makefile │ └── CODE_OF_CONDUCT.md ├── development-process └── stable-maintenance │ └── regression-analysis │ ├── index.pickle │ ├── index.pickle.license │ ├── tests │ ├── testdata.tar.bz2 │ ├── testdata.tar.bz2.license │ ├── test_badfixupdate_int.py │ ├── test_badfixcommon_int.py │ ├── test_badfixplot_int.py │ └── test_badfixstats_int.py │ ├── requirements-dev.txt │ ├── .gitignore │ ├── requirements.txt │ ├── .coveragerc │ ├── Makefile │ └── badfixfilter.py ├── .reuse └── dep5 ├── .github └── ISSUE_TEMPLATE │ └── nomination.md ├── new-wg-template.md ├── .travis.yml ├── README.md ├── LICENSES └── LicenseRef-LLVM.txt └── meeting-rules.md /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-same-funcname.module.h: -------------------------------------------------------------------------------- 1 | void module_call(); -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/chainX.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/chainX.jpg -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/long_chain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/long_chain.jpg -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sys_close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sys_close.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_demo.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file2.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE2_H 2 | #define FILE2_H 3 | 4 | #endif 5 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/include/file3.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE3_H 2 | #define FILE3_H 3 | 4 | #endif 5 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/multi_chain.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/multi_chain.jpg -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/all_callchains.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/all_callchains.jpg -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/related/related.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/related/related.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d1.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_cpp_demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_cpp_demo.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_crixcg_d1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_crixcg_d1.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_cpp_demo_libc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_cpp_demo_libc.png -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/index.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/development-process/stable-maintenance/regression-analysis/index.pickle -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_syscalls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_syscalls.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/coverage/ksys_mmap_pgoff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/coverage/ksys_mmap_pgoff.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d1_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d1_inverse.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2_coverage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2_coverage.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2_inverse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/sock_recvmsg_d2_inverse.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_crixcg_filtered_d1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_crixcg_filtered_d1.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_crixcg_filtered_d3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_crixcg_filtered_d3.png -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/test_cpp_demo_alldemangled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/test_cpp_demo_alldemangled.png -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/index.pickle.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | 3 | SPDX-License-Identifier: GPL-2.0-only -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/module.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the core 3 | # 4 | 5 | SRC += $(wildcard cg-test-template/core/*.c) 6 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/init/module.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the init 3 | # 4 | 5 | SRC += $(wildcard cg-test-template/init/*.c) 6 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/testdata.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/development-process/stable-maintenance/regression-analysis/tests/testdata.tar.bz2 -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/testdata.tar.bz2.license: -------------------------------------------------------------------------------- 1 | SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | 3 | SPDX-License-Identifier: GPL-2.0-only -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/from_sock_recvmsg_to_x64_sys_recv_left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/doc/from_sock_recvmsg_to_x64_sys_recv_left.jpg -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file8.c: -------------------------------------------------------------------------------- 1 | //TODO: 2 | //implement aliased functions and struct 3 | //#define __alias(symbol) attribute((__alias__(#symbol))) 4 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/include/file1.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE1_H 2 | #define FILE1_H 3 | 4 | struct f1_ops1{ 5 | int param1; 6 | float param2; 7 | int* p; 8 | }; 9 | 10 | #endif 11 | 12 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | pycodestyle 6 | pytest 7 | pytest-cov 8 | reuse 9 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-same-funcname.module.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void say_hello() 4 | { 5 | printf("Hello from module.c\n"); 6 | } 7 | 8 | void module_call() 9 | { 10 | say_hello(); 11 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_coverage_gaps/find_coverage_gaps_test_data.tar.bz2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/elisa-tech/tsc/HEAD/safety-architecture/tools/callgraph-tool/tests/resources/find_coverage_gaps/find_coverage_gaps_test_data.tar.bz2 -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file1.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE1_H 2 | #define FILE1_H 3 | 4 | struct m_ops 5 | { 6 | int rep; 7 | void (*exec1)(void); 8 | int (*exec2)(void); 9 | }; 10 | 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | index.pickle 6 | env/ 7 | venv/ 8 | **/.vscode 9 | __pycache__/ 10 | *.py[cod] 11 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | gitpython 6 | pandas 7 | plotly 8 | tabulate 9 | lifelines 10 | python-dateutil 11 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/.gitignore: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | clang/ 6 | temp/ 7 | env/ 8 | venv/ 9 | build/ 10 | **/.vscode 11 | __pycache__/ 12 | *.py[cod] 13 | *.csv 14 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-hello.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace FOO 4 | { 5 | void say_hello() 6 | { 7 | std::cout << "Hello\n"; 8 | } 9 | } 10 | 11 | int main() 12 | { 13 | FOO::say_hello(); 14 | return 0; 15 | } -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/.coveragerc: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | [run] 6 | branch = True 7 | omit = 8 | */tests/* 9 | */venv/* 10 | 11 | [report] 12 | #fail_under = 80 -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-asminline.c: -------------------------------------------------------------------------------- 1 | // Verify function calls in inline assembly are *not* detected 2 | void callee(void) 3 | { 4 | } 5 | 6 | void inline_asm_caller(void) 7 | { 8 | asm("call callee\n"); 9 | } 10 | 11 | int main() 12 | { 13 | inline_asm_caller(); 14 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-same-funcname.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-same-funcname.module.h" 3 | 4 | static void say_hello() 5 | { 6 | printf("Hello from test-same-funcname.c\n"); 7 | } 8 | 9 | int main() 10 | { 11 | say_hello(); 12 | module_call(); 13 | } 14 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_H 2 | #define COMMON_H 3 | 4 | void f1_main(void); 5 | void f2_main(void); 6 | void f3_main(void); 7 | void f4_main(void); 8 | void f5_main(void); 9 | void f6_main(void); 10 | void f7_main(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/requirements.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | colorlog 6 | graphviz 7 | networkx 8 | pandas 9 | wget 10 | wllvm 11 | distro 12 | 13 | # dev requirements: 14 | pycodestyle 15 | pytest 16 | pytest-cov 17 | reuse 18 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #ifndef NO_LOGGING 5 | #include 6 | 7 | #define log(func) \ 8 | printf("__FUNCTION__ = %s\n", func) 9 | 10 | #else 11 | 12 | #define log(func) \ 13 | 14 | #endif 15 | 16 | #endif 17 | 18 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-modules.child.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_MODULES_CHILD_H 2 | #define TEST_MODULES_CHILD_H 3 | 4 | #include "test-modules.base.h" 5 | 6 | class Child : public Base 7 | { 8 | public: 9 | Child(); 10 | void do_init(); 11 | void do_work(); 12 | void do_final(); 13 | }; 14 | 15 | #endif -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-cast-fptr.c: -------------------------------------------------------------------------------- 1 | // Test casting function pointer 2 | 3 | typedef void (*func_pointer)(int); 4 | 5 | static void icall(int i) { 6 | } 7 | 8 | static void caller(void* fptr) { 9 | func_pointer f = (func_pointer)fptr; 10 | f(0); 11 | } 12 | 13 | void test_main(void){ 14 | caller(icall); 15 | } 16 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | 3 | Files: safety-architecture/tools/callgraph-tool/tests/resources/* 4 | Copyright: 2020 callgraph-tool authors. All rights reserved. 5 | License: Apache-2.0 6 | 7 | Files: safety-architecture/tools/callgraph-tool/doc/* 8 | Copyright: 2020 callgraph-tool authors. All rights reserved. 9 | License: Apache-2.0 10 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/expect_function_filename_specific.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","chain1","34","36","test-chain-alt.c","chain2","29","direct","","","" 3 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/expect_recursive_chains.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","recursive_call","10","20","test-chain.c","recursive_call","10","direct","","","" 3 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-ta-mlta.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello() 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | struct mystruct { 9 | void (*function_pointer)(); 10 | }; 11 | 12 | int main() 13 | { 14 | struct mystruct struct_test; 15 | struct_test.function_pointer = say_hello; 16 | struct_test.function_pointer(); 17 | } 18 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file4.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE4_H 2 | #define FILE4_H 3 | 4 | struct bitfield_ops { 5 | char bit0 : 1; 6 | char bit1 : 1; 7 | char bits2_5 : 4; 8 | char bits6_9 : 4; 9 | void (*up)(int bit_nr, void* ops); 10 | void (*down)(int bit_nr, void* ops); 11 | short mask : 8; 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/.travis.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | language: python 6 | python: 7 | - "3.6" 8 | - "3.7" 9 | - "3.8" 10 | # command to install dependencies 11 | install: 12 | - make install-requirements 13 | # command to run tests 14 | script: 15 | - make pre-push 16 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/CONTRIBUTORS: -------------------------------------------------------------------------------- 1 | 6 | 7 | # This file contains the list of people who contribute (or have contributed) 8 | # to the callgraph tool project. The copyright holder information is kept in 9 | # AUTHORS file. 10 | 11 | Henri Rosten 12 | Marijo Simunovic 13 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/AUTHORS: -------------------------------------------------------------------------------- 1 | 6 | 7 | # This file contains copyright holders of the callgraph-tool repository 8 | # See CONTRIBUTORS file for individual contributors 9 | 10 | Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 11 | Kangjie Lu 12 | Aditya Pakki 13 | Qiushi Wu -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/include/file2.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE2_H 2 | #define FILE2_H 3 | 4 | struct f2_ops{ 5 | int i; 6 | char bits01 : 2; 7 | char bits23 : 2; 8 | char bits47 : 4; 9 | float (*expect)(int* array, int N); 10 | }; 11 | 12 | struct f2_data{ 13 | int N; 14 | int* array; 15 | }; 16 | 17 | typedef struct f2_ops f2_ops_t; 18 | #endif 19 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/init/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "common.h" 3 | 4 | 5 | int main(int argc, char** argv) 6 | { 7 | printf("This is the main entry point\n"); 8 | f1_main(); 9 | f2_main(); 10 | f3_main(); 11 | f4_main(); 12 | f5_main(); 13 | f6_main(); 14 | f7_main(); 15 | 16 | printf("Exit program\n"); 17 | 18 | return 0; 19 | } 20 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/chains.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 3 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 4 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/expect_single_chain_both.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 3 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 4 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/expect_single_chain_left.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 3 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 4 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/expect_single_chain_right.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 3 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 4 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-namespace-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace NS { 4 | class Base { 5 | public: 6 | virtual void base_pure_virtual() = 0; 7 | }; 8 | 9 | class Child : public Base { 10 | public: 11 | void base_pure_virtual() { 12 | std::cout << __PRETTY_FUNCTION__ << "\n"; 13 | } 14 | }; 15 | 16 | Child child; 17 | Base *baseptr = &child; 18 | } 19 | 20 | int main() { 21 | NS::baseptr->base_pure_virtual(); 22 | return 0; 23 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-modules.base.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-modules.base.h" 3 | 4 | using namespace std; 5 | 6 | void Base::run() 7 | { 8 | cout << __PRETTY_FUNCTION__ << "\n"; 9 | // These calls can be to the Base's implementation, 10 | // or to the (any) Child's implementation depending on the 11 | // object through which the call to Base::run() was invoked. 12 | this->do_init(); 13 | this->do_work(); 14 | this->do_final(); 15 | } 16 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-namespace-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace NS { 4 | class Base { 5 | public: 6 | virtual void base_pure_virtual() = 0; 7 | }; 8 | 9 | class Child : public Base { 10 | public: 11 | void base_pure_virtual() { 12 | std::cout << __PRETTY_FUNCTION__ << "\n"; 13 | } 14 | }; 15 | } 16 | 17 | 18 | int main() { 19 | NS::Child child; 20 | NS::Base *baseptr = &child; 21 | baseptr->base_pure_virtual(); 22 | return 0; 23 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-modules.base.h: -------------------------------------------------------------------------------- 1 | #ifndef TEST_MODULES_BASE_H 2 | #define TEST_MODULES_BASE_H 3 | 4 | class Base 5 | { 6 | private: 7 | const char *ID; 8 | 9 | public: 10 | Base(const char *name) : ID(name) {} 11 | // Base's definition in header file 12 | virtual void do_init(){ return; } 13 | virtual void do_work(){ return; } 14 | virtual void do_final(){ return; } 15 | 16 | // Base's definition in cpp-file 17 | virtual void run(); 18 | }; 19 | 20 | #endif -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file3.h: -------------------------------------------------------------------------------- 1 | #ifndef FILE3_H 2 | #define FILE3_H 3 | 4 | //inspired by example from mm/shmem.c 5 | struct page { 6 | int *data; 7 | int size; 8 | }; 9 | 10 | struct file{ 11 | void* file; 12 | int size; 13 | }; 14 | 15 | struct as_operations { 16 | int (*writepage)(struct page *page, void *wbc); 17 | int (*readpage)(struct file *file, struct page *page); 18 | void (*freepage)(struct page *page); 19 | }; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-opt.c: -------------------------------------------------------------------------------- 1 | // When compiled with -O0, empty functions should stay 2 | // in the generated callgraph. 3 | // When compiled with -O1, empty functions are optimized away, 4 | // which should also be visible in the generated callgraph. 5 | // The test in this file is to compile it with both -O0 and -O1 6 | // and verify that the empty function is included only into the 7 | // -O0 build. 8 | 9 | void do_nothing() 10 | { 11 | } 12 | 13 | int main() 14 | { 15 | do_nothing(); 16 | } 17 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-cast-struct.c: -------------------------------------------------------------------------------- 1 | // Test casting struct 2 | struct struct_test { 3 | int i; 4 | void (*function_ptr)(int i); 5 | }; 6 | 7 | static void icall(int i) { 8 | } 9 | 10 | static void caller(void* data) { 11 | struct struct_test* cast_result = (struct struct_test*)data; 12 | cast_result->function_ptr(2); 13 | } 14 | 15 | static struct struct_test self = 16 | { 17 | .i = 1, 18 | .function_ptr = icall 19 | }; 20 | 21 | void test_main(void){ 22 | caller(&self); 23 | } 24 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-notassigned.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello() 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | struct S { 9 | void (*function_pointer)(void); 10 | }; 11 | 12 | int main(void) 13 | { 14 | void (*unrelated_fptr)(void); 15 | unrelated_fptr = say_hello; 16 | struct S s; 17 | // 'unrelated_fptr' should not be a potential call target for 18 | // s.function pointer because it was never assigned to it. 19 | s.function_pointer(); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-modules.child.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "test-modules.child.h" 3 | 4 | using namespace std; 5 | 6 | Child::Child() : Base("Child") {} 7 | 8 | void Child::do_init() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | void Child::do_work() 13 | { 14 | cout << __PRETTY_FUNCTION__ << "\n"; 15 | } 16 | void Child::do_final() 17 | { 18 | cout << __PRETTY_FUNCTION__ << "\n"; 19 | } 20 | 21 | int main() 22 | { 23 | Child c; 24 | c.run(); 25 | return 0; 26 | } -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/nomination.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Nomination 3 | about: Nominate yourself to run for the TSC 4 | title: "[NOMINATION 2026 TSC]: Your name" 5 | labels: "nomination-2026" 6 | assignees: '' 7 | 8 | --- 9 | 10 | # Nomination 🗳️ 11 | 12 | ## Nominee Name 13 | 14 | 15 | ## Short Personal Bio 16 | 17 | 18 | ## Short Personal Pitch 19 | 20 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-escape.c: -------------------------------------------------------------------------------- 1 | // Test type escape 2 | // Inspired by mm/mempool.c 3 | 4 | typedef void (*mempool_alloc_t)(int i); 5 | 6 | typedef struct mempool_s { 7 | mempool_alloc_t alloc; 8 | } mempool_t; 9 | 10 | int mempool_init_node(mempool_t *pool, mempool_alloc_t alloc_fn) 11 | { 12 | pool->alloc = alloc_fn; 13 | pool->alloc(1); 14 | return 0; 15 | } 16 | 17 | void icall(int i) { 18 | } 19 | 20 | int test_main() { 21 | mempool_t pool; 22 | mempool_init_node(&pool, icall); 23 | return 0; 24 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-union.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello() 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | void say_int(int i) 9 | { 10 | printf("Int: %i\n", i); 11 | } 12 | 13 | struct S 14 | { 15 | union 16 | { 17 | void (*fnptr1)(void); 18 | void (*fnptr2)(int); 19 | } fptr; 20 | }; 21 | 22 | int main(void) 23 | { 24 | struct S s; 25 | s.fptr.fnptr1 = say_hello; 26 | s.fptr.fnptr1(); 27 | s.fptr.fnptr2 = say_int; 28 | s.fptr.fnptr2(0); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-duplicate-path.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello() 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | void duplicte_s4(){ 9 | say_hello(); 10 | } 11 | 12 | void duplicate_s3() 13 | { 14 | say_hello(); 15 | duplicte_s4(); 16 | } 17 | 18 | void duplicate_s1() 19 | { 20 | duplicate_s3(); 21 | duplicate_s3(); 22 | } 23 | 24 | void duplicate_s2() 25 | { 26 | duplicate_s3(); 27 | } 28 | 29 | int main() 30 | { 31 | duplicate_s1(); 32 | duplicate_s2(); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | # 3 | # SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | cmake_minimum_required(VERSION 3.5.1) 6 | project(CALLGRAPH) 7 | 8 | message(STATUS "LLVM_MIN_VERSION=${LLVM_MIN_VERSION}") 9 | find_package(LLVM ${LLVM_MIN_VERSION} REQUIRED) 10 | 11 | message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") 12 | message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") 13 | 14 | include_directories(${LLVM_INCLUDE_DIRS}) 15 | add_definitions(${LLVM_DEFINITIONS}) 16 | 17 | add_subdirectory(lib) 18 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file2.c: -------------------------------------------------------------------------------- 1 | #include "file2.h" 2 | #include "log.h" 3 | 4 | struct local_ops { 5 | void (*local_call)(void); 6 | }; 7 | 8 | static void f2_local_function(void) 9 | { 10 | log(__FUNCTION__); 11 | } 12 | 13 | static void f2_local_struct_par(struct local_ops* ops) 14 | { 15 | log(__FUNCTION__); 16 | ops->local_call(); 17 | } 18 | 19 | void f2_main(void){ 20 | struct local_ops local = { 21 | .local_call = f2_local_function 22 | }; 23 | log(__FUNCTION__); 24 | local.local_call(); 25 | f2_local_struct_par(&local); 26 | } 27 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | # 3 | # SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | set (AnalyzerSourceCodes 6 | Common.cc 7 | Analyzer.cc 8 | CallGraph.cc 9 | VirtualCallTargets.cc 10 | ) 11 | 12 | set(CMAKE_MACOSX_RPATH 0) 13 | 14 | # Build executable 15 | set (EXECUTABLE_OUTPUT_PATH ${ANALYZER_BINARY_DIR}) 16 | link_directories (${ANALYZER_BINARY_DIR}/lib) 17 | add_executable(crix-callgraph ${AnalyzerSourceCodes}) 18 | target_link_libraries(crix-callgraph 19 | LLVMAsmParser 20 | LLVMSupport 21 | LLVMCore 22 | LLVMAnalysis 23 | LLVMIRReader 24 | LLVMipo 25 | LLVMPasses 26 | ) 27 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file5.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | struct ops1 { 4 | void (*callback)(void); 5 | int member; 6 | }; 7 | 8 | struct ops2 { 9 | void (*callback)(void); 10 | int member; 11 | }; 12 | 13 | 14 | void f5_cb_impl(void) 15 | { 16 | log(__FUNCTION__); 17 | } 18 | 19 | struct ops1 ops = { 20 | .callback = f5_cb_impl 21 | }; 22 | 23 | 24 | void f5_local_function(void){ 25 | struct ops2 ops = { 26 | .callback = f5_cb_impl 27 | }; 28 | log(__FUNCTION__); 29 | ops.callback(); 30 | 31 | } 32 | 33 | void f5_main(void) 34 | { 35 | ops.callback(); 36 | f5_local_function(); 37 | } 38 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-sizeof.c: -------------------------------------------------------------------------------- 1 | // Test sizeof: sizeof is evaluated at compile time 2 | #include 3 | 4 | static inline int func() { 5 | printf("func()\n"); 6 | return 0; 7 | } 8 | 9 | int main() 10 | { 11 | printf("main, before sizeof()\n"); 12 | // func is an argument to sizeof, which is a compile time operator: 13 | // therefore, the below line doesn't call func() at run time 14 | (void) sizeof( (func()) ); 15 | printf("main, after sizeof()\n"); 16 | func(); 17 | printf("main, after func()\n"); 18 | } 19 | 20 | /* 21 | # Prints: 22 | main, before sizeof() 23 | main, after sizeof() 24 | func() 25 | main, after func() 26 | */ -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-multiple-3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base_1 6 | { 7 | public: 8 | virtual void foo() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | }; 13 | 14 | class Base_2 15 | { 16 | public: 17 | virtual void foo() 18 | { 19 | cout << __PRETTY_FUNCTION__ << "\n"; 20 | } 21 | }; 22 | 23 | class Child : public Base_1, public Base_2 24 | { 25 | public: 26 | void foo() 27 | { 28 | Base_1::foo(); 29 | Base_2::foo(); 30 | cout << __PRETTY_FUNCTION__ << "\n"; 31 | } 32 | }; 33 | 34 | int main() 35 | { 36 | Child c; 37 | c.foo(); 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/generate_bitcodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MYNAME=$(basename $0) 4 | 5 | ################################################################################ 6 | 7 | exit_unless_command_exists () { 8 | if ! [ -x "$(command -v $1)" ]; then 9 | echo "Error: '$1' is not installed" >&2 10 | [ ! -z "$2" ] && echo "$2" 11 | exit 1 12 | fi 13 | } 14 | 15 | ################################################################################ 16 | 17 | scriptdir=$(realpath $(dirname "$0")) 18 | pushd $scriptdir >/dev/null 19 | 20 | exit_unless_command_exists make 21 | make 22 | 23 | popd >/dev/null 24 | 25 | ################################################################################ 26 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file1.c: -------------------------------------------------------------------------------- 1 | #include "file1.h" 2 | #include "log.h" 3 | 4 | // Function is called through m_ops structure member exec1 5 | void m_ifunc1(void) 6 | { 7 | log(__FUNCTION__); 8 | } 9 | 10 | // Function is called through m_ops structure member exec2 11 | int m_ifunc2(void) 12 | { 13 | log(__FUNCTION__); 14 | return 42; 15 | } 16 | 17 | 18 | static const struct m_ops m_exec = { 19 | .exec1 = m_ifunc1, 20 | .exec2 = m_ifunc2 21 | }; 22 | 23 | static void f1_local(const struct m_ops* ops){ 24 | log(__FUNCTION__); 25 | ops->exec1(); 26 | } 27 | 28 | void f1_main(void) 29 | { 30 | log(__FUNCTION__); 31 | f1_local(&m_exec); 32 | m_exec.exec2(); 33 | } 34 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-multiple-1.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base_1 6 | { 7 | public: 8 | void base1_concrete() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | }; 13 | 14 | class Base_2 15 | { 16 | public: 17 | void base2_concrete() 18 | { 19 | cout << __PRETTY_FUNCTION__ << "\n"; 20 | } 21 | }; 22 | 23 | class Child : public Base_1, public Base_2 24 | { 25 | }; 26 | 27 | int main() 28 | { 29 | Child c; 30 | Base_1 *base1ptr = &c; 31 | Base_2 *base2ptr = &c; 32 | 33 | c.base1_concrete(); 34 | base1ptr->base1_concrete(); 35 | 36 | c.base2_concrete(); 37 | base2ptr->base2_concrete(); 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file7.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | #ifndef __cacheline_aligned 4 | #define __cacheline_aligned __attribute__((__aligned__(64))) 5 | #endif 6 | 7 | struct metadata { 8 | int md1; 9 | int md2; 10 | float md3; 11 | double md4; 12 | }; 13 | 14 | struct aligned_ops { 15 | int member1; 16 | struct metadata data __cacheline_aligned; 17 | char member2; 18 | void (*callback)(void); 19 | } __cacheline_aligned; 20 | 21 | 22 | static void f7_cb_implement(void) 23 | { 24 | log(__FUNCTION__); 25 | } 26 | 27 | static const struct aligned_ops aops = { 28 | .callback = f7_cb_implement 29 | }; 30 | 31 | void f7_main(void) 32 | { 33 | log(__FUNCTION__); 34 | aops.callback(); 35 | } 36 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-chain.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef void (*fptr_t)(); 4 | 5 | void say_hello() 6 | { 7 | printf("Hello\n"); 8 | } 9 | 10 | void recursive_call(int i) 11 | { 12 | say_hello(); 13 | i = i - 1; 14 | if (i <= 0) 15 | { 16 | return; 17 | } 18 | else 19 | { 20 | recursive_call(i); 21 | } 22 | } 23 | 24 | void chain3() 25 | { 26 | say_hello(); 27 | } 28 | 29 | void chain2() 30 | { 31 | chain3(); 32 | } 33 | 34 | void chain1() 35 | { 36 | chain2(); 37 | } 38 | 39 | void start_of_longer_call_chain() 40 | { 41 | chain1(); 42 | } 43 | 44 | int main() 45 | { 46 | say_hello(); 47 | fptr_t fptr = say_hello; 48 | fptr(); 49 | recursive_call(5); 50 | start_of_longer_call_chain(); 51 | } 52 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-multilevel.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base_1 6 | { 7 | public: 8 | void base1_concrete() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | virtual void base1_pure_virtual(int i) = 0; 13 | }; 14 | 15 | class Child_1 : public Base_1 16 | { 17 | public: 18 | void child1_concrete() 19 | { 20 | cout << __PRETTY_FUNCTION__ << "\n"; 21 | } 22 | void base1_pure_virtual(int i) 23 | { 24 | cout << __PRETTY_FUNCTION__ << "\n"; 25 | } 26 | }; 27 | 28 | class Child_2 : public Child_1 29 | { 30 | public: 31 | void base1_pure_virtual(int i) 32 | { 33 | cout << __PRETTY_FUNCTION__ << "\n"; 34 | } 35 | }; 36 | 37 | int main() 38 | { 39 | Child_2 c; 40 | c.base1_concrete(); 41 | c.child1_concrete(); 42 | c.base1_pure_virtual(0); 43 | return 0; 44 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file6.c: -------------------------------------------------------------------------------- 1 | #include "log.h" 2 | 3 | // inspired by arch/x86/kernel/apic/vector.c 4 | struct obs_kernel_param { 5 | const char *str; 6 | int (*setup_func)(void); 7 | int early; 8 | }; 9 | 10 | #define __setup_param(str, unique_id, fn, early) \ 11 | static const char __setup_str_##unique_id[] = str; \ 12 | static struct obs_kernel_param __setup_##unique_id \ 13 | = { __setup_str_##unique_id, fn, early }; 14 | 15 | #define __call(unique_id) \ 16 | __setup_##unique_id.setup_func(); 17 | 18 | #define __setup(str, fn) \ 19 | __setup_param(str, fn, fn, 0) 20 | 21 | 22 | static int show_lapic = 1; 23 | 24 | static int setup_show_lapic(void) 25 | { 26 | log(__FUNCTION__); 27 | return 2; 28 | } 29 | 30 | __setup("show_lapic=", setup_show_lapic); 31 | 32 | void f6_main(void) 33 | { 34 | log(__FUNCTION__); 35 | show_lapic = __call(setup_show_lapic); 36 | } 37 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/filter/expect_one_col_mult_filter.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "net/mac80211/trace.h","trace_event_raw_event_drv_add_nan_func","1890","1890","kernel/kcov.c","__sanitizer_cov_trace_const_cmp8","289","direct","include/linux/trace_events.h","611.0","" 3 | "kernel/trace/trace_stat.c","stat_seq_show","214","222","net/ipv4/udp.c","udp4_seq_show","2986","indirect","","","MLTA" 4 | "drivers/pci/pci.c","pci_dev_save_and_disable","4961","4972","arch/x86/pci/fixup.c","sb600_hpet_quirk","527","indirect","","","MLTA" 5 | "drivers/base/regmap/regmap.c","_regmap_raw_write_impl","1466","1573","fs/locks.c","filelock_init","3006","indirect","","","TA" 6 | "kernel/time/alarmtimer.c","alarm_start","356","367","lib/zstd/decompress.c","ZSTD_DStreamOutSize","2278","indirect","","","MLTA" 7 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-null.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void say_hello(void) 5 | { 6 | printf("Hello\n"); 7 | } 8 | 9 | void say_inner1() 10 | { 11 | printf("Inner1\n"); 12 | } 13 | 14 | void say_inner2(void) 15 | { 16 | printf("Inner2\n"); 17 | } 18 | 19 | void say_int(int i) { 20 | printf("Int: %i\n", i); 21 | } 22 | 23 | typedef void (*fptr_t)(); 24 | typedef void (*fptr_t_int)(int); 25 | 26 | struct I { 27 | int i; 28 | int j; 29 | char k; 30 | unsigned p; 31 | fptr_t i_fptr; 32 | fptr_t_int i_fptr_int; 33 | long long a; 34 | }; 35 | 36 | struct S { 37 | int i; 38 | fptr_t s_fptr; 39 | fptr_t_int s_fptr_int; 40 | struct I s_i_inner1; 41 | struct I s_i_inner2; 42 | }; 43 | 44 | struct O { 45 | struct S o_s_inner; 46 | }; 47 | 48 | int main(void) 49 | { 50 | struct O o; 51 | o.o_s_inner.s_i_inner1.i_fptr = NULL; 52 | o.o_s_inner.s_i_inner1.i_fptr(); 53 | } 54 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file3.c: -------------------------------------------------------------------------------- 1 | #include "file3.h" 2 | #include "log.h" 3 | 4 | 5 | static const struct as_operations mem_aops; 6 | 7 | int f3_check_equal(const struct as_operations* rhs) 8 | { 9 | log(__FUNCTION__); 10 | if(rhs == &mem_aops) 11 | return 1; 12 | return 0; 13 | } 14 | 15 | static int f3_wp(struct page *page, void *wbc) 16 | { 17 | log(__FUNCTION__); 18 | return 0; 19 | } 20 | 21 | static int f3_rp(struct file *file, struct page *page) 22 | { 23 | log(__FUNCTION__); 24 | return 0; 25 | } 26 | 27 | static void f3_fp(struct page *page) 28 | { 29 | log(__FUNCTION__); 30 | } 31 | 32 | void f3_main(void) 33 | { 34 | log(__FUNCTION__); 35 | struct as_operations local = { 36 | .freepage = f3_fp 37 | }; 38 | f3_check_equal(&local); 39 | f3_check_equal(&mem_aops); 40 | } 41 | 42 | 43 | static const struct as_operations mem_aops = { 44 | .writepage = f3_wp, 45 | .readpage = f3_rp 46 | }; 47 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-memcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void say_hello(void) 5 | { 6 | printf("Hello\n"); 7 | } 8 | 9 | void say_inner1() 10 | { 11 | printf("Inner1\n"); 12 | } 13 | 14 | void say_inner2(void) 15 | { 16 | printf("Inner2\n"); 17 | } 18 | 19 | void say_int(int i) { 20 | printf("Int: %i\n", i); 21 | } 22 | 23 | typedef void (*fptr_t)(); 24 | typedef void (*fptr_t_int)(int); 25 | 26 | struct I { 27 | int i; 28 | int j; 29 | char k; 30 | unsigned p; 31 | fptr_t i_fptr; 32 | fptr_t_int i_fptr_int; 33 | long long a; 34 | }; 35 | 36 | struct S { 37 | int i; 38 | fptr_t s_fptr; 39 | fptr_t_int s_fptr_int; 40 | struct I s_i_inner1; 41 | struct I s_i_inner2; 42 | }; 43 | 44 | struct O { 45 | struct S o_s_inner; 46 | }; 47 | 48 | int main(void) 49 | { 50 | struct O o; 51 | struct S s; 52 | s.s_i_inner1.i_fptr = say_hello; 53 | memcpy(&o.o_s_inner, &s, sizeof(struct S)); 54 | o.o_s_inner.s_i_inner1.i_fptr(); 55 | } 56 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-basic.c: -------------------------------------------------------------------------------- 1 | // Example from paper: 2 | // https://www-users.cs.umn.edu/~kjlu/papers/mlta.pdf 3 | 4 | #include 5 | #include 6 | 7 | typedef void (*fptr_t)(char *, char *); 8 | 9 | struct A { fptr_t handler; }; 10 | struct B { int i; struct A a; }; // B is an outer layer of A 11 | struct C { char i; int j; struct A a; }; // C is an outer layer of A 12 | 13 | #define MAX_LEN 10 14 | 15 | void copy_with_check(char *dst, char *src) { 16 | if (strlen(src) < MAX_LEN) strcpy(dst, src); 17 | } 18 | 19 | void copy_no_check(char *dst, char *src) { 20 | strcpy(dst, src); 21 | } 22 | 23 | // Store functions with initializers 24 | struct B b = { .a = { .handler = ©_with_check } }; 25 | 26 | // Store function with store instruction 27 | struct C c; 28 | 29 | int main() 30 | { 31 | c.a.handler = ©_no_check; 32 | char buf[MAX_LEN]; 33 | char user_input[2*MAX_LEN]; 34 | (*b.a.handler)(buf, user_input); // safe 35 | (*c.a.handler)(buf, user_input); // buffer overflow !! 36 | } 37 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/filter/expect_mult_col_mult_filter.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "mm/readahead.c","ondemand_readahead","440","491","mm/kasan/generic_report.c","__asan_report_store8_noabort","152","direct","","","" 3 | "kernel/trace/trace_stat.c","stat_seq_show","214","222","net/ipv4/udp.c","udp4_seq_show","2986","indirect","","","MLTA" 4 | "drivers/pci/pci.c","pci_dev_save_and_disable","4961","4972","arch/x86/pci/fixup.c","sb600_hpet_quirk","527","indirect","","","MLTA" 5 | "drivers/base/regmap/regmap.c","_regmap_raw_write_impl","1466","1573","fs/locks.c","filelock_init","3006","indirect","","","TA" 6 | "kernel/time/alarmtimer.c","alarm_start","356","367","lib/zstd/decompress.c","ZSTD_DStreamOutSize","2278","indirect","","","MLTA" 7 | "include/linux/refcount.h","refcount_sub_and_test","264","266","include/asm-generic/atomic-instrumented.h","atomic_fetch_sub_release","218","direct","","","" 8 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inline.c: -------------------------------------------------------------------------------- 1 | // Test inline function call 2 | 3 | void actual_call() 4 | { 5 | } 6 | 7 | // 'inlined_func' will always be inlined. 8 | static inline __attribute__((__always_inline__)) void 9 | inlined_func() 10 | { 11 | actual_call(); 12 | } 13 | 14 | // 'another_inlined_func' will also always be inlined. 15 | static inline __attribute__((__always_inline__)) void 16 | another_inlined_func() 17 | { 18 | } 19 | 20 | int main() 21 | { 22 | // inlined_func() will be inlined here. 23 | // It calls actual_call(), so the output will include a call 24 | // from main.c:27 to actual_call() at main.c:3. 25 | // Field 'callee_inlined_from_line' will be line 11, which is the 26 | // line number of the actual_call() call in the inlined_func() 27 | inlined_func(); 28 | 29 | // antoher_inlined_func() will be inlined here. 30 | // Since there are no function calls from another_inlined_func(), 31 | // the inlined function call will not be recorded - i.e. this 32 | // call will not be visible in the output csv 33 | another_inlined_func(); 34 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CFLAGS = -O0 -g 3 | INC = -I./ 4 | 5 | SHELL := /bin/bash 6 | CLANG_BIN_DIR := /usr/lib/llvm-10/bin 7 | CG_BIN := ../../../build/lib/crix-callgraph 8 | PATH := $(CLANG_BIN_DIR):$(PATH) 9 | 10 | # List of target programs 11 | PROGS = \ 12 | test-chain\ 13 | test-duplicate-path\ 14 | 15 | progs: $(PROGS) 16 | 17 | # Check clang is in PATH 18 | .PHONY: clang-check 19 | clang-check: 20 | @which clang >/dev/null 21 | 22 | cg-bin: 23 | cd ../../../ && make crix-callgraph 24 | 25 | # Build bitcode files from the targets in PROGS 26 | # Run CG_BIN to generate the callgraph for each bitcode target 27 | .PHONY: $(PROGS) 28 | $(PROGS): %: %.c clang-check cg-bin 29 | $(CC) $(CFLAGS) $(INC) -emit-llvm -c -o $@.bc $< 30 | # .bclist is just a list of .bc files that will be analyzed together 31 | echo $@.bc >$@.bclist 32 | $(CG_BIN) -o $@.csv @$@.bclist 2>/dev/null 33 | 34 | clean: 35 | find . -type f -name "*.bc" -delete 36 | find . -type f -name "*.bclist" -delete 37 | find . -type f -name "*.png" -delete 38 | find . -type f -name "*.csv" -delete 39 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c: -------------------------------------------------------------------------------- 1 | // Demonstrate callgraph with a clumsy example 2 | 3 | #include 4 | #include 5 | 6 | #define MAX_INPUT_LEN 10 7 | 8 | typedef void (*fptr_t)(char *); 9 | 10 | struct reader_a 11 | { 12 | fptr_t read; 13 | int other_data; 14 | }; 15 | 16 | struct reader_b 17 | { 18 | fptr_t read; 19 | char *other_data; 20 | }; 21 | 22 | void flush() 23 | { 24 | char c; 25 | while ((c = getchar()) != '\n' && c != EOF) 26 | ; 27 | } 28 | 29 | char *gets(char *); 30 | void read_no_check(char *buffer) 31 | { 32 | printf("Input to %s: ", __func__); 33 | gets(buffer); 34 | } 35 | 36 | void read_with_check(char *buffer) 37 | { 38 | printf("Input to %s: ", __func__); 39 | if (fgets(buffer, MAX_INPUT_LEN, stdin)) 40 | if (!strchr(buffer, '\n')) 41 | flush(); 42 | } 43 | 44 | int main() 45 | { 46 | char input[MAX_INPUT_LEN]; 47 | struct reader_a safe = {.read = read_with_check}; 48 | struct reader_b unsafe = {.read = read_no_check}; 49 | safe.read(input); 50 | unsafe.read(input); 51 | return 0; 52 | } 53 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/README.md: -------------------------------------------------------------------------------- 1 | # Template source code for testing Callgraph tool capabilities 2 | 3 | ## Detecting various call variants (including structs) 4 | 5 | Use cases: 6 | 7 | 1. Static struct defined in a particular translation unit: 8 | -available only in that translation unit (used directly or passed as function paramater) 9 | 10 | 2. global struct available in multiple translation units 11 | 12 | 3. Local struct defined inside of a function 13 | - used in that function 14 | - passed through another function to other parts of the code 15 | 16 | 4. Struct defined trough usage of macros 17 | 18 | 5. Detection of the typedefs usage 19 | 20 | 6. Alias macros 21 | 22 | 7. Struct that use cache alignment (for members, and globally for the whole struct) 23 | 24 | 8. Struct with bitfield members 25 | 26 | 9. Forward declared structs used in other (see "mm/shmem.c:address_space_operations") 27 | 28 | 10. Structs returned from function 29 | 30 | 11. local functions in different files with the same name 31 | 32 | 12. recursive calls (and cyclic calls) 33 | 34 | 13. Two local structs of the same name 35 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-assign-value.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello1() 4 | { 5 | printf("Hello1\n"); 6 | } 7 | void say_hello2() 8 | { 9 | printf("Hello2\n"); 10 | } 11 | void say_hello3() 12 | { 13 | printf("Hello3\n"); 14 | } 15 | 16 | void say_inner1() 17 | { 18 | printf("Inner1\n"); 19 | } 20 | 21 | void say_inner2() 22 | { 23 | printf("Inner2\n"); 24 | } 25 | 26 | typedef void (*fptr_t)(void); 27 | 28 | struct I { 29 | int i; 30 | int j; 31 | char k; 32 | unsigned p; 33 | fptr_t inner_fptr; 34 | long long a; 35 | }; 36 | 37 | struct S { 38 | int i; 39 | fptr_t fptr; 40 | struct I inner1; 41 | struct I inner2; 42 | }; 43 | 44 | struct S s = { 45 | .fptr = say_hello1, 46 | .inner2 = { .inner_fptr = say_inner2 } 47 | }; 48 | 49 | struct S s_array[3] = { 50 | { 51 | .fptr = say_hello2, 52 | } 53 | }; 54 | 55 | struct S empty = {}; 56 | 57 | int main(void) 58 | { 59 | struct S s_new = s; 60 | s_new.inner2.inner_fptr(); // say_inner2 61 | s_array[2] = s; // Type escapes here 62 | s_array[2].fptr(); // say_hello1, say_hello2 63 | } 64 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/cg-test-template/core/file4.c: -------------------------------------------------------------------------------- 1 | #include "file4.h" 2 | #include "log.h" 3 | 4 | 5 | static void activate(int bit_nr, void* ops) 6 | { 7 | struct bitfield_ops* b = (struct bitfield_ops*)ops; 8 | if(bit_nr >= 10) 9 | return; 10 | 11 | b->mask |= 0x1 << bit_nr; 12 | if(bit_nr == 0){ 13 | b->bit0 = 0x1; 14 | }else if(bit_nr == 1){ 15 | b->bit1 = 0x1; 16 | }else{ 17 | bit_nr -= 2; 18 | b->bits2_5 |= 0x1 << bit_nr; 19 | } 20 | } 21 | 22 | static void deactivate(int bit_nr, void* ops) 23 | { 24 | struct bitfield_ops* b = (struct bitfield_ops*)ops; 25 | if(bit_nr >= 10) 26 | return; 27 | 28 | b->mask &= ~(0x1 << bit_nr); 29 | if(bit_nr == 0){ 30 | b->bit0 = 0x0; 31 | }else if(bit_nr == 1){ 32 | b->bit1 = 0x0; 33 | }else{ 34 | bit_nr -= 2; 35 | b->bits2_5 &= ~(0x1 << bit_nr); 36 | } 37 | } 38 | 39 | static struct bitfield_ops self = 40 | { 41 | .bit0 = 1, 42 | .up = activate, 43 | .down = deactivate 44 | }; 45 | 46 | void f4_main(void){ 47 | self.up(2, &self); 48 | self.down(1, &self); 49 | } 50 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-global.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base 6 | { 7 | public: 8 | void base_concrete() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | virtual void base_virtual() 13 | { 14 | cout << __PRETTY_FUNCTION__ << "\n"; 15 | } 16 | virtual void base_pure_virtual(int i) = 0; 17 | }; 18 | 19 | class Child : public Base 20 | { 21 | public: 22 | void base_virtual() 23 | { 24 | cout << __PRETTY_FUNCTION__ << "\n"; 25 | } 26 | 27 | void base_pure_virtual(int i) 28 | { 29 | cout << __PRETTY_FUNCTION__ << "\n"; 30 | } 31 | }; 32 | 33 | void base_concrete() 34 | { 35 | cout << __PRETTY_FUNCTION__ << "\n"; 36 | } 37 | 38 | Child gchild; 39 | Base *baseptr = &gchild; 40 | 41 | int main() 42 | { 43 | base_concrete(); 44 | baseptr->base_concrete(); 45 | 46 | // Below two calls should be to the same target function 47 | baseptr->base_virtual(); 48 | gchild.base_virtual(); 49 | 50 | // Below two calls should be to the same target function 51 | baseptr->base_pure_virtual(0); 52 | gchild.base_pure_virtual(0); 53 | 54 | return 0; 55 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-basic.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base 6 | { 7 | public: 8 | void base_concrete() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | virtual void base_virtual() 13 | { 14 | cout << __PRETTY_FUNCTION__ << "\n"; 15 | } 16 | virtual void base_pure_virtual(int i) = 0; 17 | }; 18 | 19 | class Child : public Base 20 | { 21 | public: 22 | void base_virtual() 23 | { 24 | cout << __PRETTY_FUNCTION__ << "\n"; 25 | } 26 | 27 | void base_pure_virtual(int i) 28 | { 29 | cout << __PRETTY_FUNCTION__ << "\n"; 30 | } 31 | }; 32 | 33 | void base_concrete() 34 | { 35 | cout << __PRETTY_FUNCTION__ << "\n"; 36 | } 37 | 38 | int main() 39 | { 40 | Base *baseptr; 41 | Child c; 42 | baseptr = &c; 43 | 44 | base_concrete(); 45 | baseptr->base_concrete(); 46 | 47 | // Below two calls should be to the same target function 48 | baseptr->base_virtual(); 49 | c.base_virtual(); 50 | 51 | // Below two calls should be to the same target function 52 | baseptr->base_pure_virtual(0); 53 | c.base_pure_virtual(0); 54 | 55 | return 0; 56 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-misc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello() 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | void say_inner1() 9 | { 10 | printf("Inner1\n"); 11 | } 12 | 13 | void say_inner2() 14 | { 15 | printf("Inner2\n"); 16 | } 17 | 18 | typedef void (*fptr_t)(void); 19 | 20 | struct I { 21 | int i; 22 | int j; 23 | char k; 24 | unsigned p; 25 | fptr_t inner_fptr; 26 | long long a; 27 | }; 28 | 29 | struct S { 30 | int i; 31 | fptr_t fptr; 32 | struct I inner1; 33 | struct I inner2; 34 | }; 35 | 36 | struct O { 37 | struct S o_inner; 38 | }; 39 | 40 | struct S s1 = { 41 | .i = 1, 42 | .fptr = say_hello, 43 | .inner1 = { .inner_fptr = say_inner1 }, 44 | .inner2 = { .inner_fptr = say_inner2 } 45 | }; 46 | 47 | struct O o1 = { 48 | .o_inner = { 49 | .inner1 = { .inner_fptr = say_inner1 } 50 | } 51 | }; 52 | 53 | int main(void) 54 | { 55 | struct S s2; 56 | s2.fptr = say_hello; 57 | s2.inner2.inner_fptr = say_inner2; 58 | s1.fptr(); 59 | s1.inner2.inner_fptr(); 60 | struct O o2; 61 | o2.o_inner = s2; 62 | o2.o_inner.inner2.inner_fptr(); 63 | o1.o_inner.inner1.inner_fptr(); 64 | } 65 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/chain_calls.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","say_hello","5","7","","printf","","direct","","","" 3 | "test-chain.c","recursive_call","10","12","test-chain.c","say_hello","5","direct","","","" 4 | "test-chain.c","recursive_call","10","20","test-chain.c","recursive_call","10","direct","","","" 5 | "test-chain.c","chain3","24","26","test-chain.c","say_hello","5","direct","","","" 6 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 7 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 8 | "test-chain.c","start_of_longer_call_chain","39","41","test-chain.c","chain1","34","direct","","","" 9 | "test-chain.c","main","44","46","test-chain.c","say_hello","5","direct","","","" 10 | "test-chain.c","main","44","48","test-chain.c","say_hello","5","indirect","","","MLTA" 11 | "test-chain.c","main","44","49","test-chain.c","recursive_call","10","direct","","","" 12 | "test-chain.c","main","44","50","test-chain.c","start_of_longer_call_chain","39","direct","","","" 13 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-arr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello1() 4 | { 5 | printf("Hello1\n"); 6 | } 7 | void say_hello2() 8 | { 9 | printf("Hello2\n"); 10 | } 11 | void say_hello3() 12 | { 13 | printf("Hello3\n"); 14 | } 15 | 16 | void say_inner1() 17 | { 18 | printf("Inner1\n"); 19 | } 20 | 21 | void say_inner2() 22 | { 23 | printf("Inner2\n"); 24 | } 25 | 26 | typedef void (*fptr_t)(void); 27 | 28 | struct I { 29 | int i; 30 | int j; 31 | char k; 32 | unsigned p; 33 | fptr_t inner_fptr; 34 | long long a; 35 | }; 36 | 37 | struct S { 38 | int i; 39 | fptr_t fptr; 40 | struct I inner1; 41 | struct I inner2; 42 | }; 43 | 44 | struct S s_array[3] = { 45 | { 46 | .fptr = say_hello1, 47 | .inner1 = {.inner_fptr = say_inner1}, 48 | .inner2 = {.inner_fptr = say_inner2} 49 | } 50 | }; 51 | 52 | fptr_t fptr_array[10] = {say_hello1, say_hello2}; 53 | 54 | int main(void) 55 | { 56 | fptr_array[2] = say_hello3; 57 | fptr_array[1](); // say_hello2 58 | fptr_array[2](); // say_hello3 59 | 60 | s_array[0].fptr(); // say_hello1 61 | s_array[0].inner2.inner_fptr(); // say_inner1, say_inner2 62 | } 63 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-cpp-demo.cpp: -------------------------------------------------------------------------------- 1 | // Demonstrate C++ callgraph with a simple example 2 | 3 | #include 4 | 5 | using namespace std; 6 | 7 | //////////////////////////////////////////////////////////////////////////////// 8 | 9 | class Worker 10 | { 11 | public: 12 | virtual void do_init() = 0; 13 | virtual void do_work() = 0; 14 | virtual void run(); 15 | }; 16 | 17 | 18 | void Worker::run() 19 | { 20 | cout << __PRETTY_FUNCTION__ << "\n"; 21 | 22 | // Call implementation from the derived class: 23 | this->do_init(); 24 | this->do_work(); 25 | } 26 | 27 | //////////////////////////////////////////////////////////////////////////////// 28 | 29 | class DummyWorker: public Worker 30 | { 31 | public: 32 | void do_init(); 33 | void do_work(); 34 | }; 35 | 36 | void DummyWorker::do_init() 37 | { 38 | cout << __PRETTY_FUNCTION__ << "\n"; 39 | } 40 | void DummyWorker::do_work() 41 | { 42 | cout << __PRETTY_FUNCTION__ << "\n"; 43 | } 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | int main() 48 | { 49 | DummyWorker w; 50 | w.run(); 51 | return 0; 52 | } 53 | 54 | //////////////////////////////////////////////////////////////////////////////// 55 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/filter/expect_mult_col_one_filter.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "mm/readahead.c","ondemand_readahead","440","491","mm/kasan/generic_report.c","__asan_report_store8_noabort","152","direct","","","" 3 | "drivers/pci/pci.c","pci_dev_save_and_disable","4961","4972","arch/x86/pci/fixup.c","sb600_hpet_quirk","527","indirect","","","MLTA" 4 | "lib/decompress_inflate.c","__gunzip","42","61","drivers/acpi/scan.c","acpi_walk_dep_device_list","2023","indirect","","","MLTA" 5 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 6 | "drivers/base/regmap/regmap.c","_regmap_raw_write_impl","1466","1573","fs/locks.c","filelock_init","3006","indirect","","","TA" 7 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 8 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 9 | "include/linux/refcount.h","refcount_sub_and_test","264","266","include/asm-generic/atomic-instrumented.h","atomic_fetch_sub_release","218","direct","","","" 10 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-confinestore.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void say_hello(void) 4 | { 5 | printf("Hello\n"); 6 | } 7 | 8 | void say_inner1() 9 | { 10 | printf("Inner1\n"); 11 | } 12 | 13 | void say_inner2(void) 14 | { 15 | printf("Inner2\n"); 16 | } 17 | 18 | void say_int(int i) { 19 | printf("Int: %i\n", i); 20 | } 21 | 22 | typedef void (*fptr_t)(); 23 | typedef void (*fptr_t_int)(int); 24 | 25 | struct I { 26 | int i; 27 | int j; 28 | char k; 29 | unsigned p; 30 | fptr_t i_fptr; 31 | fptr_t_int i_fptr_int; 32 | long long a; 33 | }; 34 | 35 | struct S { 36 | int i; 37 | fptr_t s_fptr; 38 | fptr_t_int s_fptr_int; 39 | struct I s_i_inner1; 40 | struct I s_i_inner2; 41 | }; 42 | 43 | struct O { 44 | struct S o_s_inner; 45 | }; 46 | 47 | struct S s1 = { 48 | .i = 1, 49 | .s_fptr = say_hello, 50 | .s_fptr_int = say_int, 51 | .s_i_inner1 = { .i_fptr = say_inner1, .i_fptr_int = say_int }, 52 | }; 53 | 54 | int main(void) 55 | { 56 | struct O o; 57 | o.o_s_inner = s1; 58 | o.o_s_inner.s_i_inner1.i_fptr(); 59 | o.o_s_inner.s_i_inner1.i_fptr_int(3); 60 | struct S s2; 61 | struct S *s = &s2; 62 | s->s_fptr_int(2); 63 | s2.s_i_inner2.i_fptr = say_hello; 64 | s->s_i_inner2.i_fptr(); 65 | } 66 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/find_callchains/chain_calls_dupl.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "test-chain.c","say_hello","5","7","","printf","","direct","","","" 3 | "test-chain.c","recursive_call","10","12","test-chain.c","say_hello","5","direct","","","" 4 | "test-chain.c","recursive_call","10","20","test-chain.c","recursive_call","10","direct","","","" 5 | "test-chain.c","chain3","24","26","test-chain.c","say_hello","5","direct","","","" 6 | "test-chain.c","chain2","29","31","test-chain.c","chain3","24","direct","","","" 7 | "test-chain.c","chain1","34","36","test-chain.c","chain2","29","direct","","","" 8 | "test-chain.c","start_of_longer_call_chain","39","41","test-chain.c","chain1","34","direct","","","" 9 | "test-chain.c","main","44","46","test-chain.c","say_hello","5","direct","","","" 10 | "test-chain.c","main","44","48","test-chain.c","say_hello","5","indirect","","","MLTA" 11 | "test-chain.c","main","44","49","test-chain.c","recursive_call","10","direct","","","" 12 | "test-chain.c","main","44","50","test-chain.c","start_of_longer_call_chain","39","direct","","","" 13 | "test-chain.c","chain1","34","36","test-chain-alt.c","chain2","29","direct","","","" 14 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/generate_expected_output.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MYNAME=$(basename $0) 4 | 5 | ################################################################################ 6 | 7 | exit_unless_command_exists () { 8 | if ! [ -x "$(command -v $1)" ]; then 9 | echo "Error: '$1' is not installed" >&2 10 | [ ! -z "$2" ] && echo "$2" 11 | exit 1 12 | fi 13 | } 14 | 15 | ################################################################################ 16 | 17 | scriptdir=$(realpath $(dirname "$0")) 18 | pushd $scriptdir >/dev/null 19 | 20 | exit_unless_command_exists make 21 | exit_unless_command_exists csvsql 22 | exit_unless_command_exists csvformat 23 | 24 | make expected_calls 25 | 26 | # Remove entries where caller or callee function name begins with '__cxx'. 27 | # These are library functions we don't want to include into the 28 | # expected_calls.csv 29 | csvsql --query \ 30 | "select distinct * from expected_calls where caller_function not like '__cxx%' and callee_function not like '__cxx%'"\ 31 | expected_calls.csv | csvformat -U1 > expected_calls.csv.temp 32 | 33 | mv expected_calls.csv.temp expected_calls.csv 34 | sort expected_calls.csv -o expected_calls.csv 35 | 36 | popd >/dev/null 37 | 38 | ################################################################################ -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-namespace-3.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | namespace NS1 { 6 | namespace NS2 { 7 | 8 | class Base 9 | { 10 | public: 11 | void base_concrete() 12 | { 13 | cout << __PRETTY_FUNCTION__ << "\n"; 14 | } 15 | virtual void base_virtual() 16 | { 17 | cout << __PRETTY_FUNCTION__ << "\n"; 18 | } 19 | virtual void base_pure_virtual(int i) = 0; 20 | }; 21 | 22 | class Child : public Base 23 | { 24 | public: 25 | void base_virtual() 26 | { 27 | cout << __PRETTY_FUNCTION__ << "\n"; 28 | } 29 | 30 | void base_pure_virtual(int i) 31 | { 32 | cout << __PRETTY_FUNCTION__ << "\n"; 33 | } 34 | }; 35 | 36 | void base_concrete() 37 | { 38 | cout << __PRETTY_FUNCTION__ << "\n"; 39 | } 40 | 41 | Child gchild; 42 | Base *baseptr = &gchild; 43 | 44 | } 45 | } 46 | 47 | int main() 48 | { 49 | NS1::NS2::base_concrete(); 50 | NS1::NS2::baseptr->base_concrete(); 51 | 52 | // Below two calls should be to the same target function 53 | NS1::NS2::baseptr->base_virtual(); 54 | NS1::NS2::gchild.base_virtual(); 55 | 56 | // Below two calls should be to the same target function 57 | NS1::NS2::baseptr->base_pure_virtual(0); 58 | NS1::NS2::gchild.base_pure_virtual(0); 59 | 60 | return 0; 61 | } -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-bitfield.c: -------------------------------------------------------------------------------- 1 | // Bitfields can be problematic. In this case, the struct bitfield_ops type in 2 | // the IR will be: { i8, void (i32)*, i8, i8, void (i32)* }. There are two i8 3 | // fields due to the 10-bit mask variable in the struct. 4 | // After bitcast operation, the type will be converted to: 5 | // { i8, void (i32)*, i16, void (i32)* }, merging the two i8 fields into one 6 | // i16 field. This has an impact on the struct field indexes. 7 | // crix-callgraph identifies when a bitcast operation impacts the number 8 | // fields in the structure, and reverts back to matching only the function 9 | // signatures in such cases. Therefore, both 'up' and 'down' are assigned 10 | // two possible indirect call targets: activate() and deactivate() in this 11 | // example. Notice: if the bitfield size is less than or equal to 8 bits, 12 | // the revert does not occur. 13 | 14 | struct bitfield_ops { 15 | char bit0 : 1; 16 | void (*up)(int bit_nr); 17 | short mask : 10; 18 | void (*down)(int bit_nr); 19 | }; 20 | 21 | 22 | static void activate(int bit_nr) 23 | { 24 | } 25 | 26 | static void deactivate(int bit_nr) 27 | { 28 | } 29 | 30 | 31 | static struct bitfield_ops self = 32 | { 33 | .bit0 = 1, 34 | .up = activate, 35 | .down = deactivate, 36 | }; 37 | 38 | void f4_main(void){ 39 | self.up(0); 40 | self.down(0); 41 | } 42 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/filter/expect_one_col_one_filter.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "mm/page-writeback.c","domain_dirty_limits","392","393","kernel/kcov.c","__sanitizer_cov_trace_pc","189","direct","","","" 3 | "mm/page-writeback.c","balance_dirty_pages","1555","1721","kernel/kcov.c","__sanitizer_cov_trace_pc","189","direct","include/linux/spinlock.h","0.0","" 4 | "mm/readahead.c","ondemand_readahead","440","491","mm/kasan/generic_report.c","__asan_report_store8_noabort","152","direct","","","" 5 | "net/mac80211/trace.h","trace_event_raw_event_drv_add_nan_func","1890","1890","kernel/kcov.c","__sanitizer_cov_trace_const_cmp8","289","direct","include/linux/trace_events.h","611.0","" 6 | "include/linux/spinlock.h","spin_lock","352","354","kernel/locking/spinlock.c","_raw_spin_lock","149","direct","","","" 7 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 8 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 9 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 10 | "include/linux/refcount.h","refcount_sub_and_test","264","266","include/asm-generic/atomic-instrumented.h","atomic_fetch_sub_release","218","direct","","","" 11 | -------------------------------------------------------------------------------- /new-wg-template.md: -------------------------------------------------------------------------------- 1 | # New working group proposal template 2 | 3 | New working groups should be proposed on the ELISA Technical Community 4 | [mailing list](https://lists.elisa.tech/g/devel) using the following template: 5 | 6 | Proposed working group name 7 | --------------------------- 8 | 9 | *Name of proposed working group* 10 | 11 | Proposed WG chair 12 | ----------------- 13 | 14 | *Name of proposed chair* 15 | 16 | Proposed meeting schedule 17 | ------------------------- 18 | 19 | *When and how often will the working group meet? Can be planned day of the week 20 | and time of call if known, or frequency (e.g. weekly, bi-weekly, monthly)* 21 | 22 | Proposed mission statement 23 | -------------------------- 24 | 25 | *What is the proposed scope of the workgroup? Will it focus on a specific aspect 26 | of the ELISA mission statement, or a specific industry sector or type of safety 27 | use case for Linux?* 28 | 29 | Rationale 30 | --------- 31 | 32 | *Why is the new working group needed? What is it intended to accomplish that is 33 | not already addressed by one of the existing working groups?* 34 | 35 | Planned activities 36 | ------------------ 37 | 38 | *What type of activities will the working group undertake? What are the expected 39 | results of these activities and how will these be shared and managed?* 40 | 41 | Collaboration 42 | ------------- 43 | 44 | *What is the expected relationship with other working groups? How will the new 45 | WG collaborate with the existing WGs, and how will this be managed?* 46 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/cg-temp/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Makefile for the core 3 | # 4 | # 5 | MODULES := cg-test-template/core cg-test-template/init 6 | #each submodule is also include path 7 | CFLAGS += -Icg-test-template/include 8 | CFLAGS += $(patsubst %,-I%,$(MODULES)) 9 | LIBS := 10 | # add here in submodule makefiles 11 | SRC := 12 | INCL := $(patsubst %,%/module.mk,$(MODULES)) 13 | 14 | # include makefiles from submodules 15 | include $(patsubst %,%/module.mk,$(MODULES)) 16 | 17 | GCC := gcc 18 | LL_CC := clang 19 | BC_CC := clang 20 | CC := gcc 21 | 22 | LL_FLAGS := -g -emit-llvm -S -Xclang -disable-O0-optnone 23 | BC_FLAGS := -g -emit-llvm -c 24 | DEPFLAGS := -MM -MG 25 | CFLAGS += -g -O0 26 | CXXFLAGS += $(CFLAGS) 27 | 28 | OBJ := $(patsubst %.c,%.o,$(filter %.c,$(SRC))) 29 | DEP := $(OBJ:.o=.d) 30 | LLVM := $(OBJ:.o=.ll) 31 | BC := $(OBJ:.o=.bc) 32 | 33 | TARGET = cgtemplate 34 | 35 | $(TARGET): $(OBJ) 36 | $(CC) -o $@ $(OBJ) $(LIBS) 37 | 38 | depend: $(DEP) 39 | llfiles: $(LLVM) 40 | bcfiles: $(BC) 41 | #include C dependencies 42 | -include $(DEP) 43 | #-include $(LLVM) 44 | 45 | # determine include dependencies 46 | %.d: %.c 47 | @$(GCC) $(DEPFLAGS) $< $(CFLAGS) | sed -e 's@^\(.*\)\.o:@\1.d \1.o:@' > $@ 48 | 49 | %.ll: %.c 50 | @$(LL_CC) $(LL_FLAGS) $(CFLAGS) -o $@ $< 51 | 52 | %.bc: %.c 53 | @$(BC_CC) $(BC_FLAGS) $(CFLAGS) -o $@ $< 54 | 55 | 56 | clean: 57 | @rm -rf $(DEP) 58 | @rm -rf $(OBJ) 59 | @rm -rf $(LLVM) 60 | @rm -rf $(BC) 61 | @rm -rf $(TARGET) 62 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import subprocess 8 | import os 9 | import sys 10 | import pytest 11 | import shutil 12 | import csv 13 | from pathlib import Path 14 | import pandas as pd 15 | 16 | ################################################################################ 17 | 18 | 19 | def df_to_string(df): 20 | return \ 21 | "\n" + \ 22 | df.to_string( 23 | max_rows=None, 24 | max_cols=None, 25 | index=False, 26 | justify='left') + \ 27 | "\n" 28 | 29 | 30 | def df_difference(df_left, df_right): 31 | df = df_left.merge( 32 | df_right, 33 | how='outer', 34 | indicator=True, 35 | ) 36 | # Keep only the rows that differ (that are not in both) 37 | df = df[df['_merge'] != 'both'] 38 | # Rename 'left_only' and 'right_only' values in '_merge' column 39 | df['_merge'] = df['_merge'].replace(['left_only'], 'EXPECTED ==> ') 40 | df['_merge'] = df['_merge'].replace(['right_only'], 'RESULT ==> ') 41 | # Re-order columns: last column ('_merge') becomes first 42 | cols = df.columns.tolist() 43 | cols = cols[-1:] + cols[:-1] 44 | df = df[cols] 45 | # Rename '_merge' column to empty string 46 | df = df.rename(columns={"_merge": ""}) 47 | return df 48 | 49 | ################################################################################ 50 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-inheritance-multiple-2.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace std; 4 | 5 | class Base_1 6 | { 7 | public: 8 | void base1_concrete() 9 | { 10 | cout << __PRETTY_FUNCTION__ << "\n"; 11 | } 12 | virtual void base1_virtual() 13 | { 14 | cout << __PRETTY_FUNCTION__ << "\n"; 15 | } 16 | virtual void base1_pure_virtual(int i) = 0; 17 | }; 18 | 19 | class Base_2 20 | { 21 | public: 22 | virtual void base2_virtual() 23 | { 24 | cout << __PRETTY_FUNCTION__ << "\n"; 25 | } 26 | virtual void base2_pure_virtual(int i) = 0; 27 | }; 28 | 29 | class Child : public Base_1, public Base_2 30 | { 31 | public: 32 | void base1_virtual() 33 | { 34 | cout << __PRETTY_FUNCTION__ << "\n"; 35 | } 36 | 37 | void base1_pure_virtual(int i) 38 | { 39 | cout << __PRETTY_FUNCTION__ << "\n"; 40 | } 41 | 42 | void base2_pure_virtual(int i) 43 | { 44 | cout << __PRETTY_FUNCTION__ << "\n"; 45 | } 46 | }; 47 | 48 | int main() 49 | { 50 | Base_1 *base1ptr; 51 | Base_2 *base2ptr; 52 | Child c; 53 | base1ptr = &c; 54 | 55 | base1ptr->base1_concrete(); 56 | 57 | // Below two calls should be to the same target 58 | base1ptr->base1_virtual(); 59 | c.base1_virtual(); 60 | 61 | // Below two calls should be to the same target 62 | base1ptr->base1_pure_virtual(0); 63 | c.base1_pure_virtual(0); 64 | 65 | base2ptr = &c; 66 | // Child's 67 | base2ptr->base2_pure_virtual(0); 68 | // Base2's 69 | base2ptr->base2_virtual(); 70 | 71 | return 0; 72 | } -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | define target_success 6 | @printf "\033[32m==> Target \"$(1)\" passed\033[0m\n\n" 7 | endef 8 | 9 | .DEFAULT_GOAL := help 10 | 11 | TARGET: ## DESCRIPTION 12 | @echo "TARGET is here only to provide the header for 'help'" 13 | 14 | help: ## Show this help message 15 | @grep -E '^[a-zA-Z_-]+:.*?##.*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' 16 | 17 | install-requirements: ## Install all requirements 18 | pip3 install -r requirements.txt --no-cache-dir 19 | pip3 install -r requirements-dev.txt --no-cache-dir 20 | pip3 install --upgrade pytest 21 | $(call target_success,$@) 22 | 23 | pre-push: test style clean ## Run tests, pycodestyle, and reuse-lint 24 | $(call target_success,$@) 25 | 26 | test: ## Run tests 27 | pytest tests/ 28 | $(call target_success,$@) 29 | 30 | style: ## Check with pycodestyle 31 | pycodestyle --max-line-length 110 --exclude='venv/' . 32 | $(call target_success,$@) 33 | 34 | reuse-lint: ## Check with reuse lint 35 | reuse lint 36 | $(call target_success,$@) 37 | 38 | coverage: ## Check test coverage 39 | pytest --cov-report=term --cov=./ --cov-config=.coveragerc tests/ 40 | 41 | clean: clean-pyc clean-test ## Remove all artifacts 42 | $(call target_success,$@) 43 | 44 | clean-test: ## Remove test and coverage artifacts 45 | rm -f .coverage 46 | rm -fr .pytest_cache/ 47 | 48 | clean-pyc: ## Remove Python artifacts 49 | find . -name '*.pyc' -exec rm -f {} + 50 | find . -name '__pycache__' -exec rm -fr {} + 51 | 52 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/test_badfixupdate_int.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-only 5 | 6 | """ 7 | This file contains integration test cases for badfixstats.py 8 | """ 9 | 10 | import subprocess 11 | import os 12 | import sys 13 | import pytest 14 | import re 15 | from pathlib import Path 16 | import shutil 17 | import pandas as pd 18 | 19 | TESTS_DIR = Path(os.path.dirname(os.path.realpath(__file__))) 20 | TEST_DATA_DIR = TESTS_DIR / "testdata" 21 | TEST_DATA_TAR = TESTS_DIR / "testdata.tar.bz2" 22 | BADFIXUPDATE = TESTS_DIR / ".." / "update.py" 23 | 24 | 25 | @pytest.fixture() 26 | def set_up_test_data(): 27 | print("setup") 28 | shutil.rmtree(TEST_DATA_DIR, ignore_errors=True) 29 | assert subprocess.call( 30 | ["tar", "-xjf", TEST_DATA_TAR, "--directory", TESTS_DIR]) == 0 31 | yield "resource" 32 | print("clean up") 33 | shutil.rmtree(TEST_DATA_DIR) 34 | 35 | 36 | def test_help(): 37 | """ 38 | Test help 39 | """ 40 | cmd = [BADFIXUPDATE, "-h"] 41 | assert subprocess.run(cmd).returncode == 0 42 | 43 | 44 | def test_badfixupdate_basic(set_up_test_data): 45 | """ 46 | Test badfixupdate.py runs correctly 47 | """ 48 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 49 | dst = TEST_DATA_DIR / "update_data" 50 | cmd = [BADFIXUPDATE, 51 | "--git-dir", gitdir, 52 | "--dst", dst] 53 | print(cmd) 54 | cwd = TESTS_DIR / ".." 55 | assert subprocess.run( 56 | cmd, 57 | cwd=cwd).returncode == 0 58 | assert (dst / 'README.md').exists() 59 | 60 | 61 | if __name__ == '__main__': 62 | pytest.main([__file__]) 63 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/VirtualCallTargets.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-Apache-2.0-with-LLVM 4 | 5 | #ifndef VIRTUAL_CALL_TARGETS_H 6 | #define VIRTUAL_CALL_TARGETS_H 7 | 8 | #include "llvm/IR/Dominators.h" 9 | #include "llvm/IR/Instructions.h" 10 | #include "llvm/Transforms/IPO/WholeProgramDevirt.h" 11 | 12 | #include 13 | #include 14 | 15 | namespace virtcall { 16 | 17 | using FunctionSet = llvm::SmallPtrSet; 18 | 19 | //////////////////////////////////////////////////////////////////////////////// 20 | 21 | class VirtualCallTargetsResult { 22 | public: 23 | void addVirtualCallCandidates(llvm::CallInst *call, FunctionSet &&candidates); 24 | void addVirtualInvokeCandidates(llvm::InvokeInst *call, 25 | FunctionSet &&candidates); 26 | bool hasVirtualCallCandidates(llvm::Instruction *instr) const; 27 | const FunctionSet &getVirtualCallCandidates(llvm::Instruction *instr) const; 28 | void dump(); 29 | 30 | private: 31 | void addCandidates(llvm::Instruction *instr, FunctionSet &&candidates); 32 | std::unordered_map m_virtualCallCandidates; 33 | FunctionSet m_emptyFunctionSet; 34 | }; // class VirtualCallTargetsResult 35 | 36 | //////////////////////////////////////////////////////////////////////////////// 37 | 38 | class VirtualCallResolver { 39 | 40 | public: 41 | static void ResolveVirtualCalls(llvm::Module &M, 42 | VirtualCallTargetsResult &result); 43 | }; 44 | 45 | //////////////////////////////////////////////////////////////////////////////// 46 | 47 | } // end namespace virtcall 48 | 49 | #endif -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/coverage/syzkaller_coverage.md: -------------------------------------------------------------------------------- 1 | # Syzkaller coverage 2 | 3 | [`syzkaller`](https://github.com/google/syzkaller) is an unsupervized coverage-guided kernel fuzzer. It generates programs (according to specific rules) which are used for testing the Linux kernel and exposing the bugs. This process is guided based on the coverage statistics of the generated programs. 4 | 5 | ## Obtaining syzkaller coverage 6 | 7 | The configuration for the Linux kernel needs to be modified according to the [instructions]([200~https://github.com/google/syzkaller/blob/master/docs/linux/setup_ubuntu-host_qemu-vm_x86-64-kernel.md). This configuration needs to be used when generating the call graph database. 8 | Program counters that were executed during the syzkaller run can be obtained through `/rawcover` http request handler in `syz-manager` web interface. This data can be saved to a file and then converted into syzkaller coverage data using the `syz-cover` tool's `csv` export option. 9 | The resulting data has following format: 10 | ``` 11 | Filename,Function,Covered PCs,Total PCs 12 | /home/user/linux/arch/x86/entry/vsyscall/vsyscall_64.c,addr_to_vsyscall_nr,0,2 13 | /home/user/linux/arch/x86/entry/vsyscall/vsyscall_64.c,emulate_vsyscall,0,50 14 | /home/marijo/linux/arch/x86/entry/vsyscall/vsyscall_64.c,gate_vma_name,0,1 15 | ... 16 | /home/user/linux/sound/sound_core.c,cleanup_soundcore,0,1 17 | /home/user/linux/sound/sound_core.c,init_soundcore,0,4 18 | /home/user/linux/sound/sound_core.c,sound_devnode,0,5 19 | ``` 20 | Details on the file format and the conversion from PC values into coverage database are explained [here](https://github.com/google/syzkaller/blob/master/docs/linux/coverage.md). 21 | This data can be converted into CallGraph tool suitable format using the script [format_coverage.py](../../scripts/format_coverage): 22 | ``` 23 | format_coverage.py 24 | --format syzkaller 25 | --project_root /home/user/ 26 | --coverage syz_coverage.csv 27 | --out callgraph_coverage.csv -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/Common.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | #ifndef COMMON_H 6 | #define COMMON_H 7 | 8 | #include 9 | #include 10 | 11 | using namespace llvm; 12 | using namespace std; 13 | 14 | #define OP llvm::errs() 15 | 16 | #define DEBUG 0 17 | 18 | #define DSTREAM stderr 19 | #define LOG(str) LOG_FMT("%s\n", str) 20 | #define LOG_OBJ(str, obj) \ 21 | do { \ 22 | if (DEBUG) { \ 23 | LOG_FMT("%s ", str); \ 24 | obj->print(OP); \ 25 | OP << "\n"; \ 26 | } \ 27 | } while (0) 28 | #define LOG_FMT(fmt, ...) \ 29 | do { \ 30 | if (DEBUG) \ 31 | fprintf(DSTREAM, "[%s:%d]: " fmt, __func__, __LINE__, __VA_ARGS__); \ 32 | } while (0) 33 | 34 | #define CLR_NRM "\x1B[0m" 35 | #define CLR_YEL "\x1B[1;33m" 36 | #define WARN_FMT(fmt, ...) \ 37 | do { \ 38 | fprintf(DSTREAM, "%s[Warning]%s " fmt, CLR_YEL, CLR_NRM, __VA_ARGS__); \ 39 | } while (0) 40 | 41 | // 42 | // Common functions 43 | // 44 | 45 | size_t funcHash(Function *F, bool withName = true); 46 | size_t callHash(CallBase *CI); 47 | size_t typeHash(Type *Ty); 48 | size_t typeIdxHash(Type *Ty, int Idx = -1); 49 | size_t hashIdxHash(size_t Hs, int Idx = -1); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/Analyzer.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | #ifndef ANALYZER_GLOBAL_H 6 | #define ANALYZER_GLOBAL_H 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include "Common.h" 13 | 14 | enum AnalysisType { mlta_pref, mlta_only, ta_only }; 15 | enum Demangle { demangle_debug_only, demangle_all, demangle_none }; 16 | 17 | // 18 | // typedefs 19 | // 20 | typedef vector> ModuleList; 21 | // The set of all functions. 22 | typedef llvm::SmallPtrSet FuncSet; 23 | // Mapping from function name to function. 24 | typedef unordered_map NameFuncMap; 25 | 26 | struct GlobalContext { 27 | 28 | GlobalContext() {} 29 | 30 | // Map global function name to function. 31 | NameFuncMap GlobalFuncs; 32 | 33 | // Functions whose addresses are taken. 34 | FuncSet AddressTakenFuncs; 35 | 36 | // Unified functions -- no redundant inline functions 37 | DenseMap UnifiedFuncMap; 38 | 39 | // Map function signature to functions 40 | DenseMap sigFuncsMap; 41 | 42 | // Modules. 43 | ModuleList Modules; 44 | 45 | // Command line arguments 46 | AnalysisType analysisType = mlta_pref; 47 | Demangle demangle = demangle_debug_only; 48 | ofstream csvout; 49 | }; 50 | 51 | class IterativeModulePass { 52 | protected: 53 | GlobalContext *Ctx; 54 | const char *ID; 55 | 56 | public: 57 | IterativeModulePass(GlobalContext *Ctx_, const char *ID_) 58 | : Ctx(Ctx_), ID(ID_) {} 59 | 60 | // Run on each module before iterative pass. 61 | virtual bool doInitialization(llvm::Module *M) { return true; } 62 | 63 | // Run on each module after iterative pass. 64 | virtual bool doFinalization(llvm::Module *M) { return true; } 65 | 66 | // Iterative pass. 67 | virtual bool doModulePass(llvm::Module *M) { return false; } 68 | 69 | virtual void run(ModuleList &modules); 70 | }; 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/filter/calls.csv: -------------------------------------------------------------------------------- 1 | "caller_filename","caller_function","caller_def_line","caller_line","callee_filename","callee_function","callee_line","callee_calltype","callee_inlined_from_file","callee_inlined_from_line","indirect_found_with" 2 | "mm/page-writeback.c","domain_dirty_limits","392","393","kernel/kcov.c","__sanitizer_cov_trace_pc","189","direct","","","" 3 | "mm/page-writeback.c","balance_dirty_pages","1555","1721","kernel/kcov.c","__sanitizer_cov_trace_pc","189","direct","include/linux/spinlock.h","0","" 4 | "mm/readahead.c","ondemand_readahead","440","491","mm/kasan/generic_report.c","__asan_report_store8_noabort","152","direct","","","" 5 | "net/mac80211/trace.h","trace_event_raw_event_drv_add_nan_func","1890","1890","kernel/kcov.c","__sanitizer_cov_trace_const_cmp8","289","direct","include/linux/trace_events.h","611","" 6 | "kernel/trace/trace_stat.c","stat_seq_show","214","222","net/ipv4/udp.c","udp4_seq_show","2986","indirect","","","MLTA" 7 | "drivers/pci/pci.c","pci_dev_save_and_disable","4961","4972","arch/x86/pci/fixup.c","sb600_hpet_quirk","527","indirect","","","MLTA" 8 | "lib/decompress_inflate.c","__gunzip","42","61","drivers/acpi/scan.c","acpi_walk_dep_device_list","2023","indirect","","","MLTA" 9 | "include/linux/spinlock.h","spin_lock","352","354","kernel/locking/spinlock.c","_raw_spin_lock","149","direct","","","" 10 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 11 | "drivers/base/regmap/regmap.c","_regmap_raw_write_impl","1466","1573","fs/locks.c","filelock_init","3006","indirect","","","TA" 12 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 13 | "include/linux/log2.h","__ilog2_u64","30","32","arch/x86/include/asm/bitops.h","fls64","366","direct","","","" 14 | "kernel/time/alarmtimer.c","alarm_start","356","367","lib/zstd/decompress.c","ZSTD_DStreamOutSize","2278","indirect","","","MLTA" 15 | "include/linux/refcount.h","refcount_sub_and_test","264","266","include/asm-generic/atomic-instrumented.h","atomic_fetch_sub_release","218","direct","","","" 16 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/CallGraph.h: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | #ifndef CALL_GRAPH_H 6 | #define CALL_GRAPH_H 7 | 8 | #include "Analyzer.h" 9 | #include "VirtualCallTargets.h" 10 | 11 | typedef llvm::SmallVector IndexVector; 12 | 13 | class CallGraphPass : public IterativeModulePass { 14 | 15 | private: 16 | const DataLayout *DL; 17 | // char * or void * 18 | Type *Int8PtrTy; 19 | // long interger type 20 | Type *IntPtrTy; 21 | 22 | static DenseMap typeFuncsMap; 23 | static unordered_map> typeConfineMap; 24 | static unordered_map> typeTransitMap; 25 | 26 | static set typeEscapeSet; 27 | 28 | // Use type-based analysis to find targets of indirect calls 29 | void findCalleesWithType(llvm::CallBase *, FuncSet &); 30 | 31 | bool isCompositeType(Type *Ty); 32 | bool typeConfineInInitializer(User *Ini); 33 | bool typeConfineInStore(Value *, Value *); 34 | bool typeConfineInCast(CastInst *CastI); 35 | void addAddressTakenFunction(Function *F); 36 | void escapeType(Type *Ty, int Idx = -1); 37 | void transitType(Type *ToTy, Type *FromTy, int ToIdx = -1, int FromIdx = -1); 38 | 39 | Type *nextLayerBaseType(Value *V, int &Idx, IndexVector *NextIndex = NULL); 40 | 41 | void funcSetIntersection(FuncSet &FS1, FuncSet &FS2, FuncSet &FS); 42 | bool findCalleesWithMLTA(CallBase *CI, FuncSet &FS); 43 | void printCallGraphHeader(); 44 | void printCallGraphRow(CallBase *, Function *, string, string); 45 | void getVirtualFunctionCandidates(CallBase *CI, 46 | virtcall::VirtualCallTargetsResult &VCT, 47 | FuncSet &FS); 48 | 49 | public: 50 | CallGraphPass(GlobalContext *Ctx_); 51 | 52 | virtual bool doInitialization(llvm::Module *); 53 | virtual bool doFinalization(llvm::Module *); 54 | virtual bool doModulePass(llvm::Module *); 55 | 56 | void resolveVirtualCallTargets(std::string wholeProgramBitcodeFile); 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/test-mlta-x86-init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define __latent_entropy 5 | #define __noinitretpoline 6 | #define __cold __attribute__((__cold__)) 7 | #define __section(S) __attribute__((__section__(#S))) 8 | #define __initdata __section(.init.data) 9 | #define __init __section(.init.text) __cold __latent_entropy __noinitretpoline 10 | 11 | 12 | struct mpc_table { 13 | unsigned int reserved; 14 | }; 15 | 16 | void probe_roms(void){} 17 | 18 | void reserve_standard_io_resources(void){} 19 | 20 | char *e820__memory_setup_default(void) 21 | { 22 | char *ret = ""; 23 | return ret; 24 | } 25 | 26 | void x86_init_uint_noop(unsigned int unused) { } 27 | 28 | void __init default_smp_read_mpc_oem(struct mpc_table *mpc) { } 29 | 30 | static int iommu_init_noop(void) { return 0; } 31 | 32 | #define default_get_smp_config x86_init_uint_noop 33 | 34 | struct x86_init_resources { 35 | void (*probe_roms)(void); 36 | void (*reserve_resources)(void); 37 | char *(*memory_setup)(void); 38 | }; 39 | 40 | struct x86_init_mpparse { 41 | void (*mpc_record)(unsigned int mode); 42 | void (*smp_read_mpc_oem)(struct mpc_table *mpc); 43 | void (*get_smp_config)(unsigned int early); 44 | }; 45 | 46 | struct x86_init_iommu { 47 | int (*iommu_init)(void); 48 | }; 49 | 50 | 51 | struct x86_init_ops { 52 | struct x86_init_resources resources; 53 | struct x86_init_mpparse mpparse; 54 | struct x86_init_iommu iommu; 55 | }; 56 | 57 | struct x86_init_ops x86_init __initdata = { 58 | 59 | .resources = { 60 | .probe_roms = probe_roms, 61 | .reserve_resources = reserve_standard_io_resources, 62 | .memory_setup = e820__memory_setup_default, 63 | }, 64 | 65 | .mpparse = { 66 | .mpc_record = x86_init_uint_noop, 67 | .smp_read_mpc_oem = default_smp_read_mpc_oem, 68 | .get_smp_config = default_get_smp_config, 69 | }, 70 | 71 | .iommu = { 72 | .iommu_init = iommu_init_noop, 73 | }, 74 | }; 75 | 76 | int main(void) 77 | { 78 | struct mpc_table mpc; 79 | x86_init.mpparse.smp_read_mpc_oem(&mpc); 80 | x86_init.mpparse.get_smp_config(0); 81 | } 82 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/licenses/license_info.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # License and Copyright information in the callgraph-tool 8 | 9 | We are doing our best to keep all the licensing and copyright information in place. 10 | Please follow the guidelines bellow for your contributions. The requirement is installation 11 | of `reuse` tool: 12 | ``` 13 | pip install reuse 14 | ``` 15 | 16 | ## Contributions 17 | 18 | All the copyright holders are kept in top-level [AUTHORS](../../AUTHORS) file. The information 19 | about the people (developers) who contribute (or have contributed) the project are kept in 20 | [CONTRIBUTORS](../../CONTRIBUTORS) file. Make sure that you add your name in the first PR to 21 | the tool. 22 | 23 | ## Licenses 24 | We use Apache-2.0 license for all the files in the repository. The exception is `crix` core, 25 | where original LLVM license is kept. This is available under name `LicenseRef-LLVM.txt` in the 26 | [LICENSES](../../LICENSES) directory. 27 | 28 | 29 | ## Guidelines 30 | 31 | If you are modifying the existing file then there is in most cases no need for any action. 32 | When adding the new file it is required to add licensing and copyright information. This can 33 | be either done by simply copying the copyright and license information from other files in the 34 | same format of using the `reuse` tool: 35 | ``` 36 | reuse addheader \ 37 | --copyright "callgraph-tool authors. All rights reserved"\ 38 | --license Apache-2.0 39 | --year \ 40 | --style \ 41 | ``` 42 | 43 | If you are adding multiple files or files that don't have editable header you can license the 44 | entire directory by adding the appropriate entry in ./reuse/dep5 file. For example, the copyright 45 | and license information for entire contents of `doc` directory can be defined as: 46 | ``` 47 | Files: doc/* 48 | Copyright: 2020 callgraph-tool authors. All rights reserved. 49 | License: Apache-2.0 50 | ``` 51 | 52 | Before committing/pushing the file make sure that the reuse conformance is kept with: 53 | ``` 54 | reuse lint 55 | ``` 56 | There should be no warnings or errors in the tool output. 57 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/generate_bitcodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | MYNAME=$(basename $0) 4 | 5 | ################################################################################ 6 | 7 | exit_unless_command_exists () { 8 | if ! [ -x "$(command -v $1)" ]; then 9 | echo "Error: '$1' is not installed" >&2 10 | [ ! -z "$2" ] && echo "$2" 11 | exit 1 12 | fi 13 | } 14 | 15 | ################################################################################ 16 | 17 | scriptdir=$(realpath $(dirname "$0")) 18 | pushd $scriptdir >/dev/null 19 | 20 | exit_unless_command_exists make 21 | exit_unless_command_exists find 22 | exit_unless_command_exists grep 23 | 24 | if [ ! -z "$1" ]; then 25 | # If $1 is given, assume it's either relative or absolute 26 | # path to directory that contains clang binaries somewhere in the 27 | # directory hierarchy 28 | absdir=$(cd $1 2>/dev/null && pwd) 29 | if [ -z $absdir ] || [ ! -d $absdir ]; then 30 | echo "Error: '$1' does not exist." 31 | exit 1 32 | fi 33 | # Find the path to an executable we know should exist in the clang bin 34 | # directory. From the path to executable, find the path to bin/ directory. 35 | CLANG_BIN_DIR=$(find $absdir -name 'clang++' -executable -print -quit 2>/dev/null | grep -oE ".*bin") 36 | if [ -z $CLANG_BIN_DIR ]; then 37 | echo "Error: could not find clang binary directory from '$absdir'" 38 | exit 1 39 | fi 40 | else 41 | CLANG_BIN_DIR=/usr/lib/llvm-10/bin 42 | fi 43 | echo "Using CLANG_BIN_DIR=${CLANG_BIN_DIR}" 44 | if [ ! -d $CLANG_BIN_DIR ]; then 45 | echo "Error: '$CLANG_BIN_DIR' does not exist." 46 | exit 1 47 | fi 48 | 49 | if [ ! -z "$2" ]; then 50 | LLVM_VERSION=$2 51 | else 52 | LLVM_VERSION="10.0.0" 53 | fi 54 | echo "Using LLVM_VERSION=${LLVM_VERSION}" 55 | 56 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION progs 57 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION progs-cxx 58 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION cg-test-template 59 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION test-opt 60 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION test-same-funcname 61 | make CLANG_BIN_DIR=$CLANG_BIN_DIR LLVM_VERSION=$LLVM_VERSION test-modules 62 | 63 | popd >/dev/null 64 | 65 | ################################################################################ 66 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 2 | # 3 | # SPDX-License-Identifier: GPL-2.0-only 4 | 5 | matrix: 6 | include: 7 | 8 | # Sub-dir: development-process/stable-maintenance/regression-analysis/ 9 | # Python version: 3.6 10 | - language: python 11 | python: 12 | - "3.6" 13 | before_install: 14 | - cd development-process/stable-maintenance/regression-analysis/ 15 | install: 16 | - make install-requirements 17 | script: 18 | - make pre-push 19 | 20 | # Sub-dir: development-process/stable-maintenance/regression-analysis/ 21 | # Python version: 3.7 22 | - language: python 23 | python: 24 | - "3.7" 25 | before_install: 26 | - cd development-process/stable-maintenance/regression-analysis/ 27 | install: 28 | - make install-requirements 29 | script: 30 | - make pre-push 31 | 32 | # Sub-dir safety-architecture/tools/callgraph-tool 33 | # Python version: 3.6 34 | - language: python 35 | dist: bionic 36 | addons: 37 | apt: 38 | packages: 39 | - build-essential 40 | - cmake 41 | - libstdc++-8-dev 42 | - clang-10 43 | - python3-clang-10 44 | - llvm-10 45 | - llvm-10-dev 46 | - clang-format-10 47 | - graphviz 48 | python: 49 | - "3.6" 50 | before_install: 51 | - cd safety-architecture/tools/callgraph-tool 52 | install: 53 | - make install-requirements 54 | script: 55 | - make pre-push 56 | 57 | # Sub-dir safety-architecture/tools/callgraph-tool 58 | # Python version: 3.7 59 | - language: python 60 | dist: bionic 61 | addons: 62 | apt: 63 | packages: 64 | - build-essential 65 | - cmake 66 | - libstdc++-8-dev 67 | - clang-10 68 | - python3-clang-10 69 | - llvm-10 70 | - llvm-10-dev 71 | - clang-format-10 72 | - graphviz 73 | python: 74 | - "3.7" 75 | before_install: 76 | - cd safety-architecture/tools/callgraph-tool 77 | install: 78 | - make install-requirements 79 | script: 80 | - make pre-push 81 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/using_custom_llvm.md: -------------------------------------------------------------------------------- 1 | # Crix-callgraph with custom LLVM 2 | 3 | This page documents instructions on how to use crix-callgraph with custom version of LLVM. 4 | 5 | ## Setup 6 | To begin, make sure you have gone through the setup instructions from the main [README](../README.md#getting-started). In this example, we will be using the mainline tree as the target kernel: 7 | ``` 8 | cd ~ # wherever you prefer to clone the kernel tree to 9 | git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 10 | cd linux 11 | git checkout master 12 | ``` 13 | 14 | The below instructions assume you have setup the `$KERNEL` and `$CG_DIR` variables to contain the path to the target kernel tree and the callgraph-tool directories as explained in the main [README](../README.md). 15 | 16 | ## Getting LLVM binaries 17 | We will download pre-built llvm binaries from https://github.com/llvm/llvm-project/releases using the [clang_download.py](../scripts/clang_download.py) helper script. [clang_download.py](../scripts/clang_download.py) currently supports Ubuntu 18.04 and Ubuntu 20.04 host systems. For any other distribution or operating system you might have to manually download the llvm binaries or build llvm from sources. If you prefer building your own llvm binaries from the sources, note that crix-callgraph has been tested with llvm-10 and llvm-11 releases and might not work with newer releases. 18 | ``` 19 | cd $CG_DIR 20 | 21 | # Download pre-built binaries 22 | ./scripts/clang_download.py 23 | 24 | # Now, you can find pre-built binaries from `$CG_DIR/clang/bin/bin` 25 | ``` 26 | ## Building crix-callgraph with custom LLVM 27 | To compile crix-callgraph using the custom version of LLVM, run: 28 | ``` 29 | cd $CG_DIR 30 | 31 | # Make sure correct version of clang is in $PATH. 32 | # Notice the argument '--clangdir ./clang' which indicates the directory that 33 | # contains the clang binaries somewhere in the directory hierarchy 34 | source env.sh --clangdir ./clang 35 | 36 | # Clean 37 | make clean 38 | 39 | # Build crix-callgraph using LLVM libraries from specified 40 | # directory (LLVM_DIR) expecting the specified LLVM version 41 | # (LLVM_VERSION) 42 | make LLVM_DIR=./clang/bin/ LLVM_VERSION=11.0.0 43 | 44 | # Now, you can find the executable in `build/lib/crix-callgraph` 45 | ``` 46 | ## Building kernel with custom LLVM 47 | Make sure the correct version of clang is in the PATH before any other version of clang by setting the environment with `cd $CG_DIR && source env.sh -c ./clang` before building the kernel. Then, follow the instructions e.g. from the main [README](../README.md#generate-bitcode-files-from-the-target-program) to build the kernel and generate bitcode files. 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Purpose 2 | 3 | The mission of the ELISA project is to define and maintain a common set of elements, processes and tools that can be incorporated into Linux-based, safety-critical systems amenable to safety certification. 4 | 5 | # Working Groups 6 | 7 | Several working groups have been formed since the start of the ELISA project. 8 | The working groups maintain their own repositories within the GitHub org for elisa-tech. 9 | Some working groups represent horizontals, such as for tooling, architecture, and features, 10 | while others represent whole verticals like aerospace, automotive, and medical. 11 | 12 | * [Safety Architecture](https://github.com/elisa-tech/Safety_Architecture_WG) 13 | * [Linux Features](https://github.com/elisa-tech/wg-lfscs) 14 | * [Engineering Process](https://github.com/elisa-tech/wg-osep) 15 | * [Tools](https://github.com/elisa-tech/wg-tools) 16 | * [BASIL - requirements](https://github.com/elisa-tech/BASIL) 17 | * [Kernel Static Navigator](https://github.com/elisa-tech/ks-nav) 18 | * [Systems](https://github.com/elisa-tech/wg-systems) 19 | * [Best practices Standard - lighthouse](https://github.com/elisa-tech/lighthouse-oss) 20 | * [Aerospace](https://github.com/elisa-tech/wg-aerospace) 21 | * [Space Grade Linux - SGL](https://github.com/elisa-tech/sig-sgl) 22 | * [Yocto meta layer](https://github.com/elisa-tech/meta-sgl) 23 | * [Automotive - covered in Systems WG](https://github.com/elisa-tech/wg-automotive) 24 | * [Yocto meta layer for AGL IC cluster](https://github.com/elisa-tech/meta-elisa) 25 | * [Medical - paused](https://github.com/elisa-tech/wg-medical-devices) 26 | 27 | More information can also be found at the official website under [Community->Working Groups](https://elisa.tech/community/working-groups/). 28 | 29 | Regular meetings are held and minutes are typically maintained in the working group's repository or GitHub wiki. 30 | The TSC meeting minutes can be found in the [wiki](https://github.com/elisa-tech/tsc/wiki) of this repository. 31 | 32 | To find out when meetings take place, simply check the [public meeting calendar](https://elisa.tech/community/meetings/). 33 | *Note: In rare cases some browsers do not show any meetings. Try another browser or drop a mail on one of the working groups mailing lists* 34 | 35 | The mailing list with community information and meeting invitations can be found [here](https://lists.elisa.tech/). 36 | 37 | # Goals 38 | 39 | Project goals include: 40 | 41 | * A set of elements, processes and tools to support safety certification of Linux-based systems. 42 | * A Linux-based reference system to focus the activities of the project. 43 | * Companies are able to incorporate the output of the project into products. 44 | * The open source community, safety community, regulation authorities, standards bodies and system developers all accept the project work. 45 | 46 | 47 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | ################################################################################ 8 | 9 | MYNAME=$0 10 | 11 | usage () { 12 | echo "Usage: $MYNAME [--clangdir DIR]" 13 | echo "" 14 | echo "Set environment variables for crix-callgraph" 15 | echo "" 16 | } 17 | 18 | ################################################################################ 19 | 20 | if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then 21 | echo "Error: this script needs to be sourced, not executed." 22 | echo "Re-run with: 'source $MYNAME $@'" 23 | exit 1 24 | fi 25 | 26 | ################################################################################ 27 | 28 | # Transform long options to short ones 29 | for arg in "$@"; do 30 | shift 31 | case "$arg" in 32 | "--help") set -- "$@" "-h" ;; 33 | "--clangdir") set -- "$@" "-c" ;; 34 | *) set -- "$@" "$arg" 35 | esac 36 | done 37 | 38 | # Default 39 | clangdir='/usr/lib/llvm-10/bin' 40 | 41 | # Parse short options 42 | OPTIND=1 43 | while getopts "hc:" opt 44 | do 45 | case "$opt" in 46 | "h") usage; return ;; 47 | "c") clangdir=$OPTARG ;; 48 | "?") usage; return ;; 49 | "--"*) usage; return ;; 50 | esac 51 | done 52 | # remove options from positional parameters 53 | shift $(expr $OPTIND - 1) 54 | 55 | ################################################################################ 56 | 57 | # $clangdir now contains either relative or absolute 58 | # path to directory that contains clang binaries somewhere in the 59 | # directory hierarchy 60 | absdir=$(cd $clangdir 2>/dev/null && pwd) 61 | if [ -z $absdir ] || [ ! -d $absdir ]; then 62 | echo "Error: '$clangdir' does not exist." 63 | return 64 | fi 65 | # Find the path to an executable we know should exist in the clang bin 66 | # directory. From the path to executable, find the path to bin/ directory. 67 | CLANG_BIN_DIR=$(find $absdir -name 'clang++' -executable -print -quit 2>/dev/null | grep -oE ".*bin") 68 | if [ -z $CLANG_BIN_DIR ]; then 69 | echo "Error: could not find clang binary directory from '$absdir'" 70 | return 71 | fi 72 | 73 | echo "Using CLANG_BIN_DIR=${CLANG_BIN_DIR}" 74 | 75 | if [ ! -d $CLANG_BIN_DIR ]; then 76 | echo "Error: '$CLANG_BIN_DIR' does not exist." 77 | return 78 | fi 79 | 80 | export LLVM_COMPILER=clang 81 | export WLLVM_OUTPUT_LEVEL=WARNING 82 | export WLLVM_OUTPUT_FILE=/tmp/wrapper.log 83 | PATH=${CLANG_BIN_DIR}:${PATH}:${HOME}/.local/bin/ 84 | # Remove duplicates from the PATH, preserving order 85 | PATH=$(n= IFS=':'; for e in $PATH; do [[ :$n == *:$e:* ]] || n+=$e:; done; echo "${n:0: -1}") 86 | export PATH=${PATH} 87 | 88 | ################################################################################ 89 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/related/related.md: -------------------------------------------------------------------------------- 1 | # Related functions detection 2 | 3 | The script `find_related.py` is used to detect the related function in the call graph tree. There are two ways to use the script based on the `algorithm` argument value. 4 | The first way of usage is to track the common system call ancestors of the two functions: 5 | 6 | ``` 7 | ./find_related.py --calls=callgraph.csv --function1=inet_stream_connect --function2=unix_stream_connect --algorithm=ancestor --out=related.json 8 | ``` 9 | Where the arguments have the following meaning: 10 | * `calls` - callgraph database 11 | * `function1` - name of the first function for which we are looking common ancestor 12 | * `function2` - name of the second function for which we are looking common ancestor 13 | * `algorithm` - determines if we look an ancestor or an offspring 14 | * `out` - name of the output json file 15 | 16 | The result is stored into a JSON file in the form of the entries containing filename, function and the definition line of the resulting functions: 17 | ```json 18 | [ 19 | { 20 | "function": "__sys_connect_file", 21 | "filename": "net/socket.c", 22 | "def_line": "1837.0" 23 | }, 24 | { 25 | "function": "__sys_connect", 26 | "filename": "net/socket.c", 27 | "def_line": "1858.0" 28 | } 29 | ] 30 | ``` 31 | If there is more than one result in the output file this is a valuable input into determining potential other ways to increase the coverage of the code under analysis. 32 | 33 | The second way of usage is to find the common subtree of the two (system) calls. In this concrete example we want to determine the common offsprings of the `sys_connect` and `_sys_connect_file` functions: 34 | ``` 35 | ./find_related.py --calls=sys_connect.csv,sys_connect_file.csv --function1=__sys_connect --function2=__sys_connect_file "--algorithm=offspring --out=related.csv 36 | ``` 37 | Here, arguments have the following meaning: 38 | * `calls` - list of callgraph databases containing subtrees of function1 and function2, respectively 39 | * `function1` - name of the first function for which we are looking common offsprings 40 | * `function2` - name of the second function for which we are looking common offsprings 41 | * `algorithm` - determines if we look an ancestor or an offspring 42 | * `out` - name of the output CSV file 43 | 44 | In this case the scripts also inserts additional dummy input with value `___` (tripple underscore) as a caller to a `function1` and `function2`. This represents a root to a new subtree generated by merging the common offsprings of these two functions. We can utilise the scripy `query_callgraph.py` in order to visualize the results: 45 | ``` 46 | ./query_callgraph.py --csv=related.csv --depth=3 --function=___ --coverage_file=file.cov --out=related.png 47 | ``` 48 | The result of this query is displayed bellow ![related](related.png) 49 | 50 | 51 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/Common.cc: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | #include "llvm/IR/DebugInfoMetadata.h" 6 | #include 7 | 8 | #include "Common.h" 9 | 10 | static string fixHash(string hash) { 11 | // Dirty fix for issue discussed in: TODO 12 | static const regex reg("void\\(\\.\\.\\.\\)"); 13 | return regex_replace(hash, reg, "void()"); 14 | } 15 | 16 | size_t funcHash(Function *F, bool withName) { 17 | 18 | hash str_hash; 19 | string output; 20 | 21 | string sig; 22 | raw_string_ostream rso(sig); 23 | Type *FTy = F->getFunctionType(); 24 | FTy->print(rso); 25 | output = rso.str(); 26 | 27 | if (withName) { 28 | output += F->getName(); 29 | 30 | #if 1 31 | if (!F->hasExternalLinkage()) { 32 | // For file local (static) functions, include the filename into the 33 | // hash, so that it will not collide with possible global function 34 | // with the same name 35 | DISubprogram *SP = F->getSubprogram(); 36 | if (SP) { 37 | output = SP->getFilename().str() + ":" + output; 38 | } 39 | } 40 | #endif 41 | } 42 | string::iterator end_pos = remove(output.begin(), output.end(), ' '); 43 | output.erase(end_pos, output.end()); 44 | output = fixHash(output); 45 | LOG_FMT("hash [%lu] based on string: %s\n", str_hash(output), output.c_str()); 46 | 47 | return str_hash(output); 48 | } 49 | 50 | size_t callHash(CallBase *CS) { 51 | 52 | Function *CF = CS->getCalledFunction(); 53 | 54 | if (CF) 55 | return funcHash(CF); 56 | else { 57 | hash str_hash; 58 | string sig; 59 | raw_string_ostream rso(sig); 60 | Type *FTy = CS->getFunctionType(); 61 | FTy->print(rso); 62 | 63 | string strip_str = rso.str(); 64 | string::iterator end_pos = remove(strip_str.begin(), strip_str.end(), ' '); 65 | strip_str.erase(end_pos, strip_str.end()); 66 | strip_str = fixHash(strip_str); 67 | LOG_FMT("hash [%lu] based on string: %s\n", str_hash(strip_str), 68 | strip_str.c_str()); 69 | return str_hash(strip_str); 70 | } 71 | } 72 | 73 | size_t typeHash(Type *Ty) { 74 | hash str_hash; 75 | string sig; 76 | 77 | raw_string_ostream rso(sig); 78 | Ty->print(rso); 79 | string ty_str = rso.str(); 80 | string::iterator end_pos = remove(ty_str.begin(), ty_str.end(), ' '); 81 | ty_str.erase(end_pos, ty_str.end()); 82 | 83 | LOG_FMT("hash [%lu] based on string: %s\n", str_hash(ty_str), ty_str.c_str()); 84 | return str_hash(ty_str); 85 | } 86 | 87 | size_t hashIdxHash(size_t Hs, int Idx) { 88 | hash str_hash; 89 | LOG_FMT("hash Idx: %d\n", Idx); 90 | LOG_FMT("hash idx hash: %lu\n", (Hs + str_hash(to_string(Idx)))); 91 | return Hs + str_hash(to_string(Idx)); 92 | } 93 | 94 | size_t typeIdxHash(Type *Ty, int Idx) { return hashIdxHash(typeHash(Ty), Idx); } 95 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/format_coverage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import argparse 8 | import csv 9 | import logging 10 | import os 11 | import pandas as pd 12 | 13 | import utils 14 | 15 | _LOGGER = logging.getLogger(utils.LOGGER_NAME) 16 | 17 | ################################################################################ 18 | 19 | 20 | def df_from_csv_file(name): 21 | df = pd.read_csv(name, na_values=[''], keep_default_na=False) 22 | df.reset_index(drop=True, inplace=True) 23 | return df 24 | 25 | 26 | def df_to_csv_file(df, name): 27 | df.to_csv( 28 | path_or_buf=name, 29 | quoting=csv.QUOTE_ALL, 30 | sep=",", index=False, encoding='utf-8') 31 | _LOGGER.info("wrote: %s" % name) 32 | 33 | 34 | def syzkaller_calculate_cov(df_cov, project_root=None): 35 | require_cols = ['filename', 'function', "covered pcs", "total pcs"] 36 | df_cov.columns = df_cov.columns.str.lower() 37 | if not all(x in list(df_cov.columns.values) for x in require_cols): 38 | _LOGGER.error( 39 | "Coverage file missing required headers: %s" % require_cols) 40 | exit(1) 41 | df_cov['percent'] = (df_cov['covered pcs'] * 100) / df_cov['total pcs'] 42 | if project_root: 43 | # TODO: append path separator if does not exist 44 | df_cov['filename'] = df_cov['filename'].str.replace(project_root, "") 45 | return df_cov 46 | 47 | 48 | def getargs(): 49 | desc = "Convert the input coverage file into format suitable for CallGraph tool usage" 50 | epil = "Example: ./%s --format syzkaller "\ 51 | "--coverage coverage.input --out coverage.csv" % \ 52 | os.path.basename(__file__) 53 | 54 | parser = argparse.ArgumentParser(description=desc, epilog=epil) 55 | 56 | help = "Path to source root. Specify if you want paths in coverage data to be stored"\ 57 | " relative to the project root" 58 | parser.add_argument("--project_root", help=help, default="") 59 | help = "Format of the coverage input file" 60 | parser.add_argument("--format", help=help, required=True, choices=["syzkaller"]) 61 | help = "File with coverage data in specified format" 62 | parser.add_argument("--coverage", help=help, required=True) 63 | help = "Output file where data in CallGraph tool format will be exported" 64 | parser.add_argument('--out', help=help, required=True) 65 | help = "Set the verbosity level (e.g. -vv for debug level)" 66 | parser.add_argument( 67 | '-v', '--verbose', help=help, action='count', default=1) 68 | return parser.parse_args() 69 | 70 | 71 | if __name__ == '__main__': 72 | args = getargs() 73 | 74 | utils.exit_unless_accessible(args.coverage) 75 | utils.setup_logging(verbosity=args.verbose) 76 | 77 | if args.format == "syzkaller": 78 | df = df_from_csv_file(args.coverage) 79 | df = syzkaller_calculate_cov(df, args.project_root) 80 | df_to_csv_file(df, args.out) 81 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/clang_format.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | ################################################################################ 8 | 9 | MYNAME=$0 10 | 11 | GREEN='\033[32m' 12 | RED='\033[31m' 13 | YELLOW='\033[33m' 14 | NC='\033[0m' 15 | 16 | ################################################################################ 17 | 18 | usage () { 19 | echo "Usage: $MYNAME [--warn] [--apply] [--style STYLE]" 20 | echo "" 21 | echo "Check/apply clang-format to C++ files in this project" 22 | echo "" 23 | } 24 | 25 | ################################################################################ 26 | 27 | exit_unless_command_exists () { 28 | if ! [ -x "$(command -v $1)" ]; then 29 | echo "Error: $1 is not installed" >&2 30 | exit 1 31 | fi 32 | } 33 | 34 | exit_unless_file_exists () { 35 | if ! [ -f "$1" ]; then 36 | echo "File not found: $1" 37 | exit 1 38 | fi 39 | } 40 | 41 | ################################################################################ 42 | 43 | # Transform long options to short ones 44 | for arg in "$@"; do 45 | shift 46 | case "$arg" in 47 | "--help") set -- "$@" "-h" ;; 48 | "--warn") set -- "$@" "-w" ;; 49 | "--apply") set -- "$@" "-a" ;; 50 | "--style") set -- "$@" "-s" ;; 51 | *) set -- "$@" "$arg" 52 | esac 53 | done 54 | 55 | warn=false; 56 | apply=false 57 | style=none 58 | 59 | # Parse short options 60 | OPTIND=1 61 | while getopts "hwas:" opt 62 | do 63 | case "$opt" in 64 | "h") usage; exit 0 ;; 65 | "w") warn=true ;; 66 | "a") apply=true ;; 67 | "s") style=$OPTARG ;; 68 | "?") usage; exit 0 ;; 69 | "--"*) usage; exit 1 ;; 70 | esac 71 | done 72 | # remove options from positional parameters 73 | shift $(expr $OPTIND - 1) 74 | 75 | exit_unless_command_exists clang-format 76 | exit_unless_command_exists find 77 | 78 | # Default: warn only 79 | if [ "$apply" == "false" ]; then 80 | warn=true 81 | fi 82 | 83 | # Default style is LLVM 84 | # For other pre-configured styles, see: 85 | # https://clang.llvm.org/docs/ClangFormatStyleOptions.html#configurable-format-style-options 86 | # and https://zed0.co.uk/clang-format-configurator/ 87 | if [ "$style" == "none" ]; then 88 | style=LLVM 89 | fi 90 | 91 | CPP_FILES_REGEX='.*\.\(cc\|h\)' 92 | CPP_SRC_DIR='src/' 93 | fail=false 94 | 95 | for cppfile in $(find $CPP_SRC_DIR -regex $CPP_FILES_REGEX); do 96 | diff_ret=$(diff -u $cppfile <(clang-format -style=$style $cppfile) | tee /dev/tty) 97 | if [ ! -z "$diff_ret" ] && [ "$warn" == "true" ]; then 98 | fail=true 99 | fi 100 | if [ "$apply" == "true" ]; then 101 | clang-format -style=$style -i $cppfile 102 | fi 103 | done 104 | 105 | if [ "$fail" == "true" ]; then 106 | printf "${RED}Style check failed${NC}\n" 107 | printf "${YELLOW}Run '$MYNAME --apply' to fix${NC}\n" 108 | exit 1 109 | else 110 | printf "${GREEN}Style check passed${NC}\n" 111 | exit 0 112 | fi 113 | 114 | ################################################################################ 115 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/grapher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import graphviz as gv 8 | import os 9 | from collections import OrderedDict 10 | 11 | 12 | class Grapher(): 13 | 14 | def __init__(self, out): 15 | self.out = out 16 | self.maxdepth = 30 17 | 18 | self.digraph = gv.Digraph(filename=out) 19 | self.digraph.attr('graph', rankdir='LR') 20 | self.digraph.attr('node', shape='box') 21 | self.digraph.attr('node', style='rounded') 22 | self.digraph.attr('node', margin='0.3,0.1') 23 | self.digraph.attr('graph', concentrate='true') 24 | # Key: node name, Value: list of labels associated to node name 25 | self.nodelabels = {} 26 | 27 | # Initial number of entries in the graph 28 | self.initlen = len(self.digraph.body) 29 | 30 | def _add_node(self, function, filename, line): 31 | function = str(function) 32 | filename = str(filename) 33 | line = str(line).split('.')[0] 34 | node_name = "%s_%s" % (filename, function) 35 | # Node name = function, Default label = [] 36 | labels = self.nodelabels.setdefault(node_name, []) 37 | # Add filename as new label 38 | filename = filename if filename else "NaN" 39 | line = line if line else "NaN" 40 | labels.append("%s:%s" % (filename, line)) 41 | # Remove possible duplicate labels, preserving order 42 | labels = list(OrderedDict.fromkeys(labels)) 43 | # Build the html label: function name on the first line followed by 44 | # associated filenames framed with html font-tags 45 | beg = "" 46 | end = "" 47 | label = "<%s
%s%s%s>" % (function, beg, "
".join(labels), end) 48 | fillcolor = '#EEEEEE' 49 | # Add node to the graph 50 | self.digraph.node( 51 | node_name, label, style='rounded,filled', fillcolor=fillcolor) 52 | 53 | def _add_edge(self, row): 54 | edge_style = None 55 | if row.callee_calltype == "indirect": 56 | edge_style = "dashed" 57 | self.digraph.edge( 58 | "%s_%s" % (row.caller_filename, row.caller_function), 59 | "%s_%s" % (row.callee_filename, row.callee_function), 60 | style=edge_style) 61 | 62 | def graph(self, df): 63 | for row in df.itertuples(): 64 | # Add caller node 65 | self._add_node( 66 | row.caller_function, 67 | row.caller_filename, 68 | row.caller_def_line) 69 | # Add callee node 70 | self._add_node( 71 | row.callee_function, 72 | row.callee_filename, 73 | row.callee_line) 74 | # Add edge between the nodes 75 | self._add_edge(row) 76 | 77 | def render(self, filename): 78 | # Render the graph 79 | if len(self.digraph.body) <= self.initlen: 80 | return 81 | fname, extension = os.path.splitext(filename) 82 | ext = extension[1:] 83 | self.digraph.render(filename=fname, format=ext, cleanup=True) 84 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import csv 8 | import os 9 | import sys 10 | import hashlib 11 | import logging 12 | import argparse 13 | import subprocess 14 | 15 | from colorlog import ColoredFormatter, default_log_colors 16 | 17 | ############################################################################### 18 | 19 | LOG_SPAM = logging.DEBUG - 1 20 | LOGGER_NAME = "xcgraph-logger" 21 | _LOGGER = logging.getLogger(LOGGER_NAME) 22 | _FILEDIR = os.path.dirname(os.path.realpath(__file__)) 23 | 24 | ############################################################################### 25 | 26 | 27 | def exit_unless_accessible(filename): 28 | if filename and not os.path.isfile(filename): 29 | _LOGGER.error( 30 | "File not found or no permissions: \"%s\"" % filename) 31 | sys.exit(1) 32 | 33 | ################################################################################ 34 | 35 | 36 | def setup_logging(verbosity=1): 37 | project_logger = logging.getLogger(LOGGER_NAME) 38 | 39 | if verbosity == 0: 40 | level = logging.NOTSET 41 | elif verbosity == 1: 42 | level = logging.INFO 43 | elif verbosity == 2: 44 | level = logging.DEBUG 45 | else: 46 | level = LOG_SPAM 47 | 48 | log_colors = default_log_colors 49 | if level < logging.DEBUG: 50 | logformat = \ 51 | "%(log_color)s%(levelname)-8s%(reset)s "\ 52 | "%(filename)s:%(lineno)d:%(funcName)s(): %(message)s" 53 | else: 54 | logformat = "%(log_color)s%(levelname)-8s%(reset)s %(message)s" 55 | 56 | default_log_colors['ERROR'] = 'bold_red' 57 | default_log_colors['INFO'] = 'fg_bold_white' 58 | default_log_colors['SPAM'] = 'fg_bold_black' 59 | default_log_colors['DEBUG'] = 'fg_white' 60 | formatter = ColoredFormatter(logformat, log_colors=log_colors) 61 | stream = logging.StreamHandler() 62 | stream.setFormatter(formatter) 63 | logging.addLevelName(LOG_SPAM, "SPAM") 64 | project_logger.addHandler(stream) 65 | project_logger.setLevel(level) 66 | 67 | 68 | ################################################################################ 69 | 70 | 71 | def exec_cmd(cmd): 72 | command_str = " ".join(cmd) 73 | _LOGGER.debug("Running: %s" % command_str) 74 | ret = subprocess.run(cmd) 75 | if ret.returncode != 0: 76 | _LOGGER.debug("Command returned error status: %s" % ret.returncode) 77 | _LOGGER.debug("stdout: %s" % ret.stdout) 78 | _LOGGER.debug("stderr: %s" % ret.stderr) 79 | 80 | 81 | ################################################################################ 82 | 83 | 84 | class CsvWriter(): 85 | def __init__(self, filename): 86 | self.filename = filename 87 | self.fp = open(self.filename, 'w') 88 | self.writer = csv.writer(self.fp, delimiter=',', quoting=csv.QUOTE_ALL) 89 | 90 | def write_arr(self, elems): 91 | self.writer.writerow(elems) 92 | 93 | def close(self): 94 | self.fp.close() 95 | _LOGGER.info("Wrote: %s" % self.filename) 96 | 97 | 98 | ################################################################################ 99 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Contributing to Callgraph Tool 8 | We appreciate your input! If you want to contribute to the project send us a pull request following the guidelines in this document. 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | Updating and extending docs as well as adding new tests is a good way to start contributing the project. 13 | You might also want to check the [issue tracker](https://github.com/elisa-tech/workgroups/labels/callgraph-tool) for some known problems that are worth considering. 14 | 15 | ## Pull requests 16 | Pull requests are the best way to get the proposed changes into the codebase (we use [Github Flow](https://guides.github.com/introduction/flow/index.html)). 17 | Follow the checklists bellow: 18 | 19 | 1. Go to project home [page](https://github.com/elisa-tech/workgroups) and click the `Fork` button in the top-right corner. This will create 20 | a `https://github.com/YOUR_GITHUB_USERNAME/workgroups` repository. 21 | 22 | 2. Checkout main repository into your working directory: 23 | 24 | ``` 25 | cd $WORKDIR 26 | git clone https://github.com/elisa-tech/workgroups.git 27 | ``` 28 | 29 | 3. Add the fork as an additional origin: 30 | 31 | ``` 32 | git remote add my-origin https://github.com/YOUR_GITHUB_USERNAME/workgroups.git 33 | git fetch my-origin 34 | git checkout -b dev-branch my-origin/master 35 | ``` 36 | 37 | 4. Develop the feature or fix a bug: 38 | * If you've added code that should be tested, add tests. 39 | * Ensure the test suite passes: check the main makefile target `make pre-push` still passes after your changes 40 | * Ensure that you are in sync with the master branch 41 | * Push the changes in your remote branch 42 | 43 | 5. Go to https://github.com/elisa-tech/workgroups. There should be a green `Compare & pull request` button. Use it to crate a pull request. 44 | 45 | 6. Wait for a review and acknowledge the possible comments. 46 | 47 | ## Commits 48 | 49 | Commit messages should contain of short one-line description, optionally followed by an empty line and more verbose description. All the commits must be 50 | signed. Use `git commit -s -m COMMIT_MESSAGE` to add a signature to your commit. 51 | 52 | ## Report bugs using Github's [issues](https://github.com/elisa-tech/workgroups/issues) 53 | We use GitHub issues to track public bugs. Report a bug by [opening a new issue](https://github.com/elisa-tech/workgroups/issues). 54 | The title of the issue should start with CG: and contain callgraph-tool label. 55 | 56 | ## Write bug reports with detail, background, and sample code 57 | 58 | **Great Bug Reports** tend to have: 59 | 60 | - A quick summary and/or background 61 | - Steps to reproduce 62 | - Be specific! 63 | - Give sample code if you can. 64 | - What you expected would happen 65 | - What actually happens 66 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 67 | 68 | ## Use a Consistent Coding Style 69 | * You can try running `make style` for style unification 70 | 71 | ## License 72 | By contributing, you agree that your contributions will be licensed under Apache 2.0 License. 73 | -------------------------------------------------------------------------------- /LICENSES/LicenseRef-LLVM.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | ============================================================================== 46 | Copyrights and Licenses for Third Party Software Distributed with LLVM: 47 | ============================================================================== 48 | The LLVM software contains code written by third parties. Such software will 49 | have its own individual LICENSE.TXT file in the directory in which it appears. 50 | This file will describe the copyrights, license, and restrictions which apply 51 | to that code. 52 | 53 | The disclaimer of warranty in the University of Illinois Open Source License 54 | applies to all code in the LLVM Distribution, and nothing in any of the 55 | other licenses gives permission to use the names of the LLVM Team or the 56 | University of Illinois to endorse or promote products derived from this 57 | Software. 58 | 59 | The following pieces of software have additional or alternate copyrights, 60 | licenses, and/or restrictions: 61 | 62 | Program Directory 63 | ------- --------- 64 | Autoconf llvm/autoconf 65 | llvm/projects/ModuleMaker/autoconf 66 | llvm/projects/sample/autoconf 67 | CellSPU backend llvm/lib/Target/CellSPU/README.txt 68 | Google Test llvm/utils/unittest/googletest 69 | OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} 70 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/LICENSES/LicenseRef-LLVM.txt: -------------------------------------------------------------------------------- 1 | ============================================================================== 2 | LLVM Release License 3 | ============================================================================== 4 | University of Illinois/NCSA 5 | Open Source License 6 | 7 | Copyright (c) 2003-2010 University of Illinois at Urbana-Champaign. 8 | All rights reserved. 9 | 10 | Developed by: 11 | 12 | LLVM Team 13 | 14 | University of Illinois at Urbana-Champaign 15 | 16 | http://llvm.org 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy of 19 | this software and associated documentation files (the "Software"), to deal with 20 | the Software without restriction, including without limitation the rights to 21 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 22 | of the Software, and to permit persons to whom the Software is furnished to do 23 | so, subject to the following conditions: 24 | 25 | * Redistributions of source code must retain the above copyright notice, 26 | this list of conditions and the following disclaimers. 27 | 28 | * Redistributions in binary form must reproduce the above copyright notice, 29 | this list of conditions and the following disclaimers in the 30 | documentation and/or other materials provided with the distribution. 31 | 32 | * Neither the names of the LLVM Team, University of Illinois at 33 | Urbana-Champaign, nor the names of its contributors may be used to 34 | endorse or promote products derived from this Software without specific 35 | prior written permission. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 39 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE 43 | SOFTWARE. 44 | 45 | ============================================================================== 46 | Copyrights and Licenses for Third Party Software Distributed with LLVM: 47 | ============================================================================== 48 | The LLVM software contains code written by third parties. Such software will 49 | have its own individual LICENSE.TXT file in the directory in which it appears. 50 | This file will describe the copyrights, license, and restrictions which apply 51 | to that code. 52 | 53 | The disclaimer of warranty in the University of Illinois Open Source License 54 | applies to all code in the LLVM Distribution, and nothing in any of the 55 | other licenses gives permission to use the names of the LLVM Team or the 56 | University of Illinois to endorse or promote products derived from this 57 | Software. 58 | 59 | The following pieces of software have additional or alternate copyrights, 60 | licenses, and/or restrictions: 61 | 62 | Program Directory 63 | ------- --------- 64 | Autoconf llvm/autoconf 65 | llvm/projects/ModuleMaker/autoconf 66 | llvm/projects/sample/autoconf 67 | CellSPU backend llvm/lib/Target/CellSPU/README.txt 68 | Google Test llvm/utils/unittest/googletest 69 | OpenBSD regex llvm/lib/Support/{reg*, COPYRIGHT.regex} 70 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/filter_callgraph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import argparse 8 | import csv 9 | import logging 10 | import os 11 | import pandas as pd 12 | 13 | import utils 14 | 15 | _LOGGER = logging.getLogger(utils.LOGGER_NAME) 16 | 17 | ################################################################################ 18 | 19 | 20 | def df_from_csv_file(name): 21 | df = pd.read_csv(name, na_values=[''], keep_default_na=False) 22 | df.reset_index(drop=True, inplace=True) 23 | return df 24 | 25 | 26 | def df_to_csv_file(df, name): 27 | df.to_csv( 28 | path_or_buf=name, 29 | quoting=csv.QUOTE_ALL, 30 | sep=",", index=False, encoding='utf-8') 31 | _LOGGER.info("wrote: %s" % name) 32 | 33 | 34 | def df_regex_filter(df, column, regex): 35 | return df[~df[column].str.contains(regex, regex=True, na=False)] 36 | 37 | 38 | def getargs(): 39 | desc = "Filter the call graph database in CSV format based on the column values" 40 | epil = "Example: ./%s --cols caller_function "\ 41 | "--filters '^__x64_sys_' --calls callgraph.csv" % \ 42 | os.path.basename(__file__) 43 | 44 | parser = argparse.ArgumentParser(description=desc, epilog=epil) 45 | 46 | help = "List of columns to filter. Number of cols must be either one or match the"\ 47 | " number of arguments passed to --filter_regex option. In case that the only one"\ 48 | " value is specified it is broadcasted for all the regex values." 49 | parser.add_argument("--cols", nargs="+", help=help, required=True) 50 | help = "Regular expresion for filtering the rows. Number of arguments must be either one"\ 51 | " or match number of arguments passed to --cols option. In case that only one value"\ 52 | " is specified it is broadcasted for all the columns. " 53 | parser.add_argument("--filters", nargs="+", help=help, required=True) 54 | help = "Set the verbosity level (e.g. -vv for debug level)" 55 | parser.add_argument( 56 | '-v', '--verbose', help=help, action='count', default=1) 57 | help = "Function call database csv file" 58 | parser.add_argument('--calls', help=help, required=True) 59 | help = "The output CSV file. If not specified the resulting file will be stored"\ 60 | "to the same directory where input file resides and will use the name of the"\ 61 | "original file with 'filtered_' prefix" 62 | parser.add_argument('--out', help=help, default="") 63 | return parser.parse_args() 64 | 65 | 66 | if __name__ == '__main__': 67 | args = getargs() 68 | 69 | utils.exit_unless_accessible(args.calls) 70 | utils.setup_logging(verbosity=args.verbose) 71 | 72 | cols, col_cnt = args.cols, len(args.cols) 73 | filters, filters_cnt = args.filters, len(args.filters) 74 | 75 | if filters_cnt == 1: 76 | # broadcast 77 | filters = [filters[0]] * col_cnt 78 | filters_cnt = col_cnt 79 | 80 | if col_cnt == 1: 81 | # broadcast 82 | cols = [cols[0]] * filters_cnt 83 | col_cnt = filters_cnt 84 | 85 | if filters_cnt == col_cnt: 86 | d = zip(cols, filters) 87 | df = df_from_csv_file(args.calls) 88 | for col, regex in d: 89 | df = df_regex_filter(df, col, regex) 90 | 91 | if args.out: 92 | out = args.out 93 | else: 94 | filename = os.path.basename(args.calls) 95 | path = os.path.dirname(args.calls) 96 | out = os.path.join(path, "filtered_" + filename) 97 | 98 | df_to_csv_file(df, out) 99 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/badfixfilter.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-only 5 | 6 | import argparse 7 | import csv 8 | import os 9 | import sys 10 | 11 | import pandas as pd 12 | 13 | ################################################################################ 14 | 15 | 16 | def df_from_csv_file(name): 17 | df = pd.read_csv(name, na_values=[''], keep_default_na=False) 18 | df.reset_index(drop=True, inplace=True) 19 | return df 20 | 21 | 22 | def df_to_csv_file(df, name): 23 | df.to_csv( 24 | path_or_buf=name, 25 | quoting=csv.QUOTE_ALL, 26 | sep=",", index=False, encoding='utf-8') 27 | print("[+] Wrote: %s" % name) 28 | 29 | 30 | def filter_csv(db, column, filter, out): 31 | # Dataframe from db-file 32 | df_db = df_from_csv_file(db) 33 | 34 | # Dataframe from filter 35 | df_filter = pd.read_csv(filter, header=None, names=['filter']) 36 | df_filter.reset_index(drop=True, inplace=True) 37 | df_filter.drop_duplicates(inplace=True) 38 | 39 | # Merge to filter df_db 40 | df = df_db.merge( 41 | df_filter, 42 | how='left', 43 | left_on=[column], 44 | right_on=['filter'], 45 | indicator=True, 46 | ) 47 | 48 | # Only keep the rows that are in both 49 | df = df[df['_merge'] == 'both'] 50 | 51 | # Drop the intermediate columns 'filter' and 'merge' 52 | df.drop(['filter', '_merge'], axis=1, inplace=True) 53 | df_to_csv_file(df, out) 54 | 55 | 56 | def exit_unless_accessible(filename): 57 | if not os.path.isfile(filename): 58 | sys.stderr.write( 59 | "Error: file not found or no permissions: %s\n" % filename) 60 | sys.exit(1) 61 | 62 | 63 | def getargs(): 64 | desc = \ 65 | "Filter data generated with badfixstats.py. "\ 66 | "Given badfix database (--db), "\ 67 | "filter the database by only outputting "\ 68 | "the rows where specified column (--col) values match "\ 69 | "any of the lines in the text file (--filter). "\ 70 | 71 | epil = "Example: ./%s --db badfixdb.csv --col Commit_hexsha "\ 72 | "--filter patchlist.txt" % \ 73 | os.path.basename(__file__) 74 | parser = argparse.ArgumentParser(description=desc, epilog=epil) 75 | 76 | required_named = parser.add_argument_group('required named arguments') 77 | help = "CSV database to be filtered (output from badfixstats.py)" 78 | required_named.add_argument('--db', help=help, required=True) 79 | 80 | help = "CSV database column name" 81 | required_named.add_argument('--col', help=help, required=True) 82 | 83 | help = "Text file specifying the accepted values, one value per line" 84 | required_named.add_argument('--filter', help=help, required=True) 85 | 86 | help = \ 87 | "set the output file name, default is the db-name, prefixed with "\ 88 | " __filtered.csv" 89 | parser.add_argument('--out', nargs='?', help=help, default='') 90 | 91 | return parser.parse_args() 92 | 93 | ################################################################################ 94 | 95 | 96 | if __name__ == "__main__": 97 | args = getargs() 98 | 99 | exit_unless_accessible(args.db) 100 | exit_unless_accessible(args.filter) 101 | 102 | out = args.out 103 | if not out: 104 | out = "%s__filtered.csv" % args.db 105 | 106 | filter_csv(args.db, args.col, args.filter, out) 107 | 108 | ################################################################################ 109 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/scripts/clang_download.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import argparse 8 | import os 9 | import sys 10 | import re 11 | import wget 12 | import platform 13 | import distro 14 | from pathlib import Path 15 | import shutil 16 | import tarfile 17 | import logging 18 | 19 | ################################################################################ 20 | 21 | LLVM_URL = 'https://github.com/llvm/llvm-project/releases/download' 22 | RELEASE = '11.0.0' 23 | 24 | ################################################################################ 25 | 26 | 27 | def get_source_url(): 28 | return "%s/llvmorg-%s/clang-%s.src.tar.xz" % (LLVM_URL, RELEASE, RELEASE) 29 | 30 | 31 | def get_clang_url(): 32 | plat = platform.platform() 33 | if 'x86_64' in platform.platform() and \ 34 | distro.id() == 'ubuntu' and distro.version() == '20.04': 35 | url = "%s/llvmorg-%s/" \ 36 | "clang+llvm-%s-x86_64-linux-gnu-ubuntu-20.04.tar.xz" \ 37 | "" % (LLVM_URL, RELEASE, RELEASE) 38 | return url 39 | elif 'x86_64' in platform.platform() and \ 40 | distro.id() == 'ubuntu' and \ 41 | (distro.version() == '18.04' or distro.version() == '16.04'): 42 | url = "%s/llvmorg-%s/" \ 43 | "clang+llvm-%s-x86_64-linux-gnu-ubuntu-16.04.tar.xz" \ 44 | "" % (LLVM_URL, RELEASE, RELEASE) 45 | return url 46 | # elif 47 | # ... 48 | else: 49 | sys.stderr.write("Error: unknown platfrom \"%s\"\n" % plat) 50 | print("%s" % platform.system()) 51 | sys.exit(1) 52 | 53 | 54 | def rm_r(path): 55 | if os.path.isdir(path) and not os.path.islink(path): 56 | shutil.rmtree(path) 57 | elif os.path.exists(path): 58 | os.remove(path) 59 | 60 | 61 | def prompt_if_exists(dstdir): 62 | if dstdir.exists(): 63 | prompt = \ 64 | "This will remove earlier content from \"%s\". "\ 65 | "Are you sure? (y/N): " % dstdir 66 | if input(prompt) != 'y': 67 | print("Cancelled") 68 | sys.exit(0) 69 | 70 | 71 | def update_clang(output_dir): 72 | # TODO: error checks, check signatures 73 | outpath = Path(output_dir) 74 | prompt_if_exists(outpath) 75 | rm_r(outpath) 76 | outpath.mkdir(parents=True, exist_ok=True) 77 | 78 | url = get_source_url() 79 | print("[+] Downloading: %s\n" % url) 80 | source_tar = wget.download(url, out=str(outpath)) 81 | print("\n") 82 | print("[+] Extracting: %s" % source_tar) 83 | with tarfile.open(source_tar) as f: 84 | f.extractall(str(outpath)) 85 | extdir = Path(Path(source_tar).stem).stem 86 | shutil.move(str(outpath / extdir), str(outpath / "src")) 87 | 88 | url = get_clang_url() 89 | print("[+] Downloading: %s\n" % url) 90 | source_tar = wget.download(url, out=str(outpath)) 91 | print("\n") 92 | print("[+] Extracting: %s" % source_tar) 93 | with tarfile.open(source_tar) as f: 94 | f.extractall(str(outpath)) 95 | extdir = Path(Path(source_tar).stem).stem 96 | shutil.move(str(outpath / extdir), str(outpath / "bin")) 97 | 98 | 99 | def getargs(): 100 | desc = \ 101 | "Download clang release from https://github.com/llvm/llvm-project/ "\ 102 | "and extract to OUTPUT_DIR" 103 | 104 | epil = "Example: ./%s" % \ 105 | os.path.basename(__file__) 106 | parser = argparse.ArgumentParser(description=desc, epilog=epil) 107 | 108 | help = "set the destination folder, defaults to ./clang" 109 | parser.add_argument('--output-dir', nargs='?', 110 | help=help, default='./clang') 111 | 112 | return parser.parse_args() 113 | 114 | ################################################################################ 115 | 116 | 117 | if __name__ == "__main__": 118 | args = getargs() 119 | update_clang(args.output_dir) 120 | 121 | ################################################################################ 122 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/Makefile: -------------------------------------------------------------------------------- 1 | # SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | 5 | SHELL := /bin/bash 6 | 7 | ifeq ($(LLVM_DIR),) 8 | # If LLVM_DIR was not specified outside this makefile (e.g. via 9 | # command line argument) use the following defaults: 10 | LLVM_VERSION := 10.0.0 11 | LLVM_DIR := /usr/lib/llvm-10 12 | LLVM_FIX := sudo apt install clang-10 llvm-10 llvm-10-dev 13 | else 14 | LLVM_FIX := ./scripts/clang_download.py 15 | endif 16 | 17 | ifndef LLVM_VERSION 18 | $(error LLVM_VERSION must be specified if using non-default LLVM_DIR) 19 | endif 20 | 21 | $(info Using LLVM_DIR: '${LLVM_DIR}') 22 | $(info Using LLVM_VERSION: '${LLVM_VERSION}') 23 | 24 | CRIXCG_SRC_DIR := ${CURDIR}/src 25 | CRIXCG_BUILD_DIR := ${CURDIR}/build 26 | PATH := $(LLVM_DIR)/bin:$(PATH) 27 | PYENV_DIR := ${CURDIR}/venv 28 | 29 | UNAME := $(shell uname) 30 | ifeq ($(UNAME), Linux) 31 | NPROC := ${shell nproc} 32 | else 33 | NPROC := ${shell sysctl -n hw.ncpu} 34 | endif 35 | 36 | build_analyzer_func = \ 37 | (mkdir -p ${2} \ 38 | && cd ${2} \ 39 | && PATH=${PATH} \ 40 | LLVM_TOOLS_BINARY_DIR=${LLVM_DIR}/bin \ 41 | LLVM_LIBRARY_DIRS=${LLVM_DIR}/lib \ 42 | LLVM_INCLUDE_DIRS=${LLVM_DIR}/include \ 43 | CC=clang CXX=clang++ \ 44 | cmake ${1} \ 45 | -DCMAKE_BUILD_TYPE=Release \ 46 | -DLLVM_ENABLE_ASSERTIONS=ON \ 47 | -DCMAKE_CXX_FLAGS_RELEASE="-std=c++14 -fno-rtti -fpic -g -Wall" \ 48 | -DLLVM_MIN_VERSION=${LLVM_VERSION} && make -j${NPROC}) 49 | 50 | GREEN := \033[32m 51 | RED := \033[31m 52 | YELLOW := \033[33m 53 | NC := \033[0m 54 | 55 | define target_success 56 | @printf "${GREEN}==> Target \"$(1)\" passed${NC}\n\n" 57 | endef 58 | 59 | .DEFAULT_GOAL := crix-callgraph 60 | 61 | TARGET: ## DESCRIPTION 62 | @echo "TARGET is here only to provide the header for 'help'" 63 | 64 | help: ## Show this help message 65 | @grep -E '^[a-zA-Z_-]+:.*?##.*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' 66 | 67 | llvm-check: ## Check LLVM is available 68 | @if [ ! -d ${LLVM_DIR} ]; then \ 69 | printf "${RED}Error: '${LLVM_DIR}' does not exist${NC}\n"; \ 70 | printf "${YELLOW}Get LLVM (${LLVM_FIX}) and try again${NC}\n"; \ 71 | false; \ 72 | fi 73 | @if [ ! -d ${LLVM_DIR}/lib/cmake/llvm ]; then \ 74 | printf "${RED}Error: '${LLVM_DIR}/lib/cmake/llvm' does not exist${NC}\n"; \ 75 | printf "${YELLOW}Get LLVM (${LLVM_FIX}) and try again${NC}\n"; \ 76 | false; \ 77 | fi 78 | 79 | install-requirements: ## Install python requirements 80 | pip3 install -r requirements.txt --no-cache-dir 81 | $(call target_success,$@) 82 | 83 | install-preprequisites: 84 | sudo apt update 85 | sudo apt install -y -q python3 \ 86 | build-essential cmake \ 87 | clang-10 python3-clang-10 llvm-10 llvm-10-dev clang-format-10 88 | @if [ ! -d ${PYENV_DIR} ]; then \ 89 | printf "${YELLOW}Python env directory does not exist${NC}\n"; \ 90 | printf "${YELLOW}Creating python env direcotry ${PYENV_DIR}{$NC}\n"; \ 91 | fi 92 | python3 -m venv venv 93 | source venv/bin/activate 94 | venv/bin/pip3 install -r requirements.txt --no-cache-dir 95 | $(call target_success,$@) 96 | 97 | pre-push: test style clean ## Run tests, style, and clean 98 | $(call target_success,$@) 99 | 100 | test: crix-callgraph ## Build and run tests 101 | python3 -m pytest -vx tests/ 102 | $(call target_success,$@) 103 | 104 | style-pyc: ## Check with pycodestyle 105 | pycodestyle --max-line-length 95 --exclude='venv/','clang/','temp/' . 106 | $(call target_success,$@) 107 | 108 | style-cpp: ## Check with clang-format 109 | scripts/clang_format.sh --warn --style LLVM 110 | $(call target_success,$@) 111 | 112 | style: style-pyc style-cpp ## Check style 113 | 114 | crix-callgraph: llvm-check ## Build crix-callgraph 115 | $(call build_analyzer_func, ${CRIXCG_SRC_DIR}, ${CRIXCG_BUILD_DIR}) 116 | 117 | clean-pyc: ## Remove Python artifacts 118 | rm -fr .pytest_cache/ 119 | find . -name '*.pyc' -exec rm -f {} + 120 | find . -name '__pycache__' -exec rm -fr {} + 121 | 122 | clean: clean-pyc ## Remove all build artifacts 123 | cd tests/resources/crix-callgraph && make clean 124 | rm -rf ${CRIXCG_BUILD_DIR} 125 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/test_filter_callgraph.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # SPDX-FileCopyrightText: 2020 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 4 | # 5 | # SPDX-License-Identifier: Apache-2.0 6 | 7 | import subprocess 8 | import os 9 | import sys 10 | import pytest 11 | import shutil 12 | import csv 13 | from pathlib import Path 14 | import pandas as pd 15 | import imghdr 16 | import test_utils 17 | 18 | ################################################################################ 19 | 20 | TESTS_DIR = Path(os.path.dirname(os.path.realpath(__file__))) 21 | TEST_RESOURCES_DIR = TESTS_DIR / "resources" / "filter" 22 | TEST_DATA_DIR = TESTS_DIR / "filter_callgraph_test_data" 23 | FILTER_CG = TESTS_DIR / ".." / "scripts" / "filter_callgraph.py" 24 | CALLGRAPH_CSV = TEST_RESOURCES_DIR / "calls.csv" 25 | EXPECT_ONE_COL_ONE_FILTER = TEST_RESOURCES_DIR / "expect_one_col_one_filter.csv" 26 | EXPECT_ONE_COL_MULT_FILTER = TEST_RESOURCES_DIR / "expect_one_col_mult_filter.csv" 27 | EXPECT_MULT_COL_ONE_FILTER = TEST_RESOURCES_DIR / "expect_mult_col_one_filter.csv" 28 | EXPECT_MULT_COL_MULT_FILTER = TEST_RESOURCES_DIR / "expect_mult_col_mult_filter.csv" 29 | 30 | ################################################################################ 31 | 32 | 33 | @pytest.fixture(scope="session", autouse=True) 34 | def set_up_session_test_data(request): 35 | # Run once at the start of test session, before any tests have been run 36 | print("session setup") 37 | request.addfinalizer(clean_up_session_test_data) 38 | assert Path(FILTER_CG).exists() 39 | # Generate target bitcode files 40 | cmd = ["mkdir", "-p", TEST_DATA_DIR] 41 | assert subprocess.run(cmd).returncode == 0 42 | 43 | 44 | def clean_up_session_test_data(): 45 | print("session cleanup") 46 | cmd = ["rm", "-rf", TEST_DATA_DIR] 47 | assert subprocess.run(cmd).returncode == 0 48 | 49 | 50 | @pytest.fixture() 51 | def set_up_test_data(): 52 | print("test setup") 53 | shutil.rmtree(TEST_DATA_DIR, ignore_errors=True) 54 | os.makedirs(TEST_DATA_DIR) 55 | yield "resource" 56 | print("test clean up") 57 | shutil.rmtree(TEST_DATA_DIR) 58 | 59 | 60 | def test_one_col_one_filter(set_up_test_data): 61 | filter_out = TEST_DATA_DIR / "one_col_one_filter_calls.csv" 62 | cmd = [ 63 | FILTER_CG, 64 | "--cols", "indirect_found_with", 65 | "--filters", "TA$", 66 | "--out", filter_out, 67 | "--calls", CALLGRAPH_CSV 68 | ] 69 | assert subprocess.run(cmd).returncode == 0 70 | assert Path(filter_out).exists() 71 | cmd = ["diff", filter_out, EXPECT_ONE_COL_ONE_FILTER] 72 | assert subprocess.run(cmd).returncode == 0 73 | 74 | 75 | def test_one_col_mult_filter(set_up_test_data): 76 | filter_out = TEST_DATA_DIR / "one_col_mult_filter_calls.csv" 77 | cmd = [ 78 | FILTER_CG, 79 | "--cols", "caller_filename", 80 | "--filters", "^mm", "^lib", "^include", 81 | "--out", filter_out, 82 | "--calls", CALLGRAPH_CSV 83 | ] 84 | assert subprocess.run(cmd).returncode == 0 85 | assert Path(filter_out).exists() 86 | cmd = ["diff", filter_out, EXPECT_ONE_COL_MULT_FILTER] 87 | assert subprocess.run(cmd).returncode == 0 88 | 89 | 90 | def test_mult_col_one_filter(set_up_test_data): 91 | filter_out = TEST_DATA_DIR / "mult_col_one_filter_calls.csv" 92 | cmd = [ 93 | FILTER_CG, 94 | "--cols", "caller_filename", "callee_filename", 95 | "--filters", "kernel", 96 | "--out", filter_out, 97 | "--calls", CALLGRAPH_CSV 98 | ] 99 | assert subprocess.run(cmd).returncode == 0 100 | assert Path(filter_out).exists() 101 | cmd = ["diff", filter_out, EXPECT_MULT_COL_ONE_FILTER] 102 | assert subprocess.run(cmd).returncode == 0 103 | 104 | 105 | def test_mult_col_mult_filter(set_up_test_data): 106 | filter_out = TEST_DATA_DIR / "mult_col_mult_filter_calls.csv" 107 | cmd = [ 108 | FILTER_CG, 109 | "--cols", "caller_function", "callee_filename", 110 | "--filters", "^__", "kernel", 111 | "--out", filter_out, 112 | "--calls", CALLGRAPH_CSV 113 | ] 114 | assert subprocess.run(cmd).returncode == 0 115 | assert Path(filter_out).exists() 116 | cmd = ["diff", filter_out, EXPECT_MULT_COL_MULT_FILTER] 117 | assert subprocess.run(cmd).returncode == 0 118 | 119 | 120 | ################################################################################ 121 | -------------------------------------------------------------------------------- /meeting-rules.md: -------------------------------------------------------------------------------- 1 | # ELISA Meeting Guidelines 2 | 3 | These guidelines apply to all working group meetings within the ELISA project to ensure 4 | effective collaboration, transparency, and respect for participants. 5 | 6 | 7 | ## General Meeting Rules 8 | - **Send invitations upfront** 9 | Ensure all participants receive a calendar invitation well in advance of the meeting. 10 | This is guaranteed by the lfx Zoom account for scheduled meetings. 11 | 12 | - **Prepare and share an agenda** 13 | An agenda should be distributed before the meeting and followed during the session. 14 | 15 | - **Create and share meeting minutes** 16 | Document key decisions, action items, and agreements in the meeting minutes and share 17 | them with participants. 18 | 19 | 20 | ## Recording and Transcription Rules 21 | To protect privacy and maintain trust, the following rules apply to recording and 22 | transcription features (e.g., Zoom transcripts): 23 | 24 | - **Default Setting** 25 | - Recording and transcription are **disabled by default**. 26 | 27 | - **Agreement Before Enabling** 28 | - All participants must **explicitly agree** before enabling recording or transcription. 29 | - The agreement must be documented in the **meeting minutes**. 30 | - Participants joining after recording has started are notified by the meeting tool (Zoom) 31 | that a recording is in progress and have the freedom of choice to speak, or not, and 32 | notify whether they agree, or disagree to be recorded. 33 | 34 | - **Usage and Sharing** 35 | - Any recordings or transcripts are for **personal use only** by participants. 36 | - They **must not be shared publicly** or distributed beyond the agreed scope. 37 | - WARNING: Transcripts are still known to be NOT reliable in the identification of the exact words used, 38 | in particular within specific domains like Functional Safety. So, they shall NOT and cannot NOT be trusted 39 | without confirmation of the speaker, to confirm understanding. 40 | 41 | - **Documentation** 42 | - Meeting minutes should clearly state: 43 | - Whether recording or transcription was enabled. 44 | - That all participants agreed to it. 45 | 46 | ### Example for documentation in meeting minutes 47 | 48 | This is just an example and can be adjusted. It is mainly important to document this. 49 | ``` 50 | Recording/Transcription: Enabled 51 | Agreement: All participants agreed before enabling 52 | Purpose: Personal reference only, no sharing 53 | ``` 54 | 55 | **Note:** *Any meeting before September 2025 misses information about recording or 56 | transcript, as the rule was not in place. The rules were jointly discussed and agreed 57 | in the [TSC meeting August 20th 2025](https://github.com/elisa-tech/tsc/wiki/20-Aug-2025#meeting-recordings-and-transcriptions).* 58 | 59 | ## Antitrust policy 60 | 61 | All ELISA meetings follow valid antitrust policies as stated by the Linux Foundation. 62 | [LF Antitrust Policy](https://www.linuxfoundation.org/legal/antitrust-policy) 63 | 64 | 65 | ## Code of Conduct 66 | 67 | The kernel and LF Code of Conduct applies to all communication with this project: 68 | 69 | * https://www.linuxfoundation.org/code-of-conduct/ 70 | * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/code-of-conduct.rst 71 | * https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/Documentation/process/code-of-conduct-interpretation.rst 72 | 73 | ## Copyright and Licensing 74 | 75 | Email communication will be treated as documentation and be received and made available by the Project under the 76 | Creative Commons Attribution 4.0 International License (available at http://creativecommons.org/licenses/by/4.0/). 77 | Please refer to the ELISA Technical Charter section 7 subsection iv. for details. 78 | 79 | This also holds for any content presented in meetings and placed in the Google Drive directories. 80 | 81 | Note: If you disagree with those terms, you need to point out that the provided content is not under those terms, 82 | please let us know beforehand and we will ensure to have clear licensing conditions for all content in this collaboration. 83 | 84 | ## Clarification on Relationship to Employing Companies 85 | The discussions in these meetings are exploratory. The opinions expressed by participants are not necessarily the policy of the companies. 86 | 87 | ## Technical Charter 88 | 89 | ELISA Project contributions and WG discussions are subject to and governed by the 90 | [Technical Charter](https://elisa.tech/wp-content/uploads/sites/75/2020/08/elisa_technical_charter_082620.pdf) -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/test_badfixcommon_int.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-only 5 | 6 | """ 7 | This file contains integration test cases for badfixstats.py 8 | """ 9 | 10 | import subprocess 11 | import os 12 | import sys 13 | import pytest 14 | import re 15 | from pathlib import Path 16 | import shutil 17 | import pandas as pd 18 | 19 | TESTS_DIR = Path(os.path.dirname(os.path.realpath(__file__))) 20 | TEST_DATA_DIR = TESTS_DIR / "testdata" 21 | TEST_DATA_TAR = TESTS_DIR / "testdata.tar.bz2" 22 | BADFIXSTATS = TESTS_DIR / ".." / "badfixstats.py" 23 | BADFIXCOMMON = TESTS_DIR / ".." / "badfixcommon.py" 24 | 25 | 26 | @pytest.fixture() 27 | def set_up_test_data(): 28 | print("setup") 29 | shutil.rmtree(TEST_DATA_DIR, ignore_errors=True) 30 | assert subprocess.call( 31 | ["tar", "-xjf", TEST_DATA_TAR, "--directory", TESTS_DIR]) == 0 32 | yield "resource" 33 | print("clean up") 34 | shutil.rmtree(TEST_DATA_DIR) 35 | 36 | 37 | def df_from_csv_file(name): 38 | df = pd.read_csv(name, na_values=['None'], keep_default_na=True) 39 | df.reset_index(drop=True, inplace=True) 40 | return df 41 | 42 | 43 | def test_help(): 44 | """ 45 | Test help 46 | """ 47 | cmd = [BADFIXCOMMON, "-h"] 48 | assert subprocess.run(cmd).returncode == 0 49 | 50 | 51 | def test_badfixcommon_basic(set_up_test_data): 52 | """ 53 | Test badfixcommon.py runs correctly 54 | """ 55 | 56 | # First, run badfixstats.py to generate two test databases 57 | db1 = TEST_DATA_DIR / "badfixstats_out.csv" 58 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 59 | cmd = [BADFIXSTATS, 60 | "--git-dir", gitdir, 61 | "--out", db1, 62 | "--no-merge-datapoints", 63 | "v4.19^..v4.19.1"] 64 | print(cmd) 65 | assert subprocess.run(cmd).returncode == 0 66 | assert db1.exists() 67 | 68 | db2 = TEST_DATA_DIR / "badfixstats_out.csv" 69 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 70 | cmd = [BADFIXSTATS, 71 | "--git-dir", gitdir, 72 | "--out", db2, 73 | "--no-merge-datapoints", 74 | "v4.19^..v4.19.2"] 75 | print(cmd) 76 | assert subprocess.run(cmd).returncode == 0 77 | assert db2.exists() 78 | 79 | # Then, run the badfixcommon.py using the generated databases 80 | outname = TEST_DATA_DIR / "badfixcommon_out__" 81 | cmd = [BADFIXCOMMON, 82 | "--out", outname, 83 | db1, 84 | db2, 85 | ] 86 | print(cmd) 87 | assert subprocess.run(cmd).returncode == 0 88 | assert Path("%scommon.csv" % outname).exists() 89 | assert Path("%scsv1_unique.csv" % outname).exists() 90 | assert Path("%scsv2_unique.csv" % outname).exists() 91 | assert Path("%smerged.csv" % outname).exists() 92 | 93 | # 'db2' and 'merged' should have the same commits. We simply 94 | # check here that both have the same number of commits 95 | df_db2 = df_from_csv_file(db2) 96 | df_db2 = df_db2[['Commit_hexsha']].drop_duplicates() 97 | df_db2 = df_db2[df_db2['Commit_hexsha'].notnull()] 98 | df_merged = df_from_csv_file(Path("%smerged.csv" % outname)) 99 | df_merged = df_merged[['Commit_hexsha']].drop_duplicates() 100 | df_merged = df_merged[df_merged['Commit_hexsha'].notnull()] 101 | assert df_db2.shape[0] == df_merged.shape[0] 102 | 103 | 104 | def test_badfixcommon_self(set_up_test_data): 105 | """ 106 | Test badfixcommon.py generates expected output when input databases 107 | are the same 108 | """ 109 | 110 | # First, run badfixstats.py to generate a test database 111 | db1 = TEST_DATA_DIR / "badfixstats_out.csv" 112 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 113 | cmd = [BADFIXSTATS, 114 | "--git-dir", gitdir, 115 | "--out", db1, 116 | "--no-merge-datapoints", 117 | "v4.19^..v4.19.1"] 118 | print(cmd) 119 | assert subprocess.run(cmd).returncode == 0 120 | assert db1.exists() 121 | 122 | # Then, run the badfixcommon.py using the generated database 123 | outname = TEST_DATA_DIR / "badfixcommon_out__" 124 | cmd = [BADFIXCOMMON, 125 | "--out", outname, 126 | db1, 127 | db1, 128 | ] 129 | print(cmd) 130 | assert subprocess.run(cmd).returncode == 0 131 | assert Path("%scommon.csv" % outname).exists() 132 | assert Path("%scsv1_unique.csv" % outname).exists() 133 | assert Path("%scsv2_unique.csv" % outname).exists() 134 | assert Path("%smerged.csv" % outname).exists() 135 | 136 | # There should be unique commits in either 137 | df_csv1 = df_from_csv_file(Path("%scsv1_unique.csv" % outname)) 138 | df_csv2 = df_from_csv_file(Path("%scsv2_unique.csv" % outname)) 139 | assert df_csv1.shape[0] == 0 140 | assert df_csv2.shape[0] == 0 141 | 142 | 143 | if __name__ == '__main__': 144 | pytest.main([__file__]) 145 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/test_badfixplot_int.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-only 5 | 6 | """ 7 | This file contains integration test cases for badfixstats.py 8 | """ 9 | 10 | import subprocess 11 | import os 12 | import sys 13 | import pytest 14 | import re 15 | from pathlib import Path 16 | import shutil 17 | import pandas as pd 18 | 19 | TESTS_DIR = Path(os.path.dirname(os.path.realpath(__file__))) 20 | TEST_DATA_DIR = TESTS_DIR / "testdata" 21 | TEST_DATA_TAR = TESTS_DIR / "testdata.tar.bz2" 22 | BADFIXPLOT = TESTS_DIR / ".." / "badfixplot.py" 23 | BADFIXSTATS = TESTS_DIR / ".." / "badfixstats.py" 24 | 25 | 26 | @pytest.fixture() 27 | def set_up_test_data(): 28 | print("setup") 29 | shutil.rmtree(TEST_DATA_DIR, ignore_errors=True) 30 | assert subprocess.call( 31 | ["tar", "-xjf", TEST_DATA_TAR, "--directory", TESTS_DIR]) == 0 32 | yield "resource" 33 | print("clean up") 34 | shutil.rmtree(TEST_DATA_DIR) 35 | 36 | 37 | def test_help(): 38 | """ 39 | Test help 40 | """ 41 | cmd = [BADFIXPLOT, "-h"] 42 | assert subprocess.run(cmd).returncode == 0 43 | 44 | 45 | def test_badfixplot_basic(set_up_test_data): 46 | """ 47 | Test badfixplot.py runs correctly 48 | """ 49 | 50 | # First, run badfixstats.py to generate the database 51 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 52 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 53 | cmd = [BADFIXSTATS, 54 | "--git-dir", gitdir, 55 | "--out", outfile, 56 | "--no-merge-datapoints", 57 | "v4.19^..v4.19.2"] 58 | print(cmd) 59 | assert subprocess.run(cmd).returncode == 0 60 | assert outfile.exists() 61 | 62 | # Then, run the badfixplot.py using the generated database 63 | infile = outfile 64 | outfile = TEST_DATA_DIR / "badfixplot_out" 65 | cmd = [BADFIXPLOT, 66 | "--out", outfile, 67 | infile] 68 | print(cmd) 69 | assert subprocess.run(cmd).returncode == 0 70 | 71 | # Check some html files got generated 72 | assert Path("%s__summary.html" % outfile).exists() 73 | assert Path("%s__absolute_number_of_commits.html" % outfile).exists() 74 | 75 | 76 | def test_badfixplot_num_commits(set_up_test_data): 77 | """ 78 | Test badfixplot.py runs generates expected output 79 | """ 80 | 81 | # First, run badfixstats.py to generate the database 82 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 83 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 84 | cmd = [BADFIXSTATS, 85 | "--git-dir", gitdir, 86 | "--out", outfile, 87 | "--no-merge-datapoints", 88 | "v4.19.1..v4.19.2"] 89 | print(cmd) 90 | assert subprocess.run(cmd).returncode == 0 91 | assert outfile.exists() 92 | 93 | # Then, run the badfixplot.py using the generated database 94 | infile = outfile 95 | outfile = TEST_DATA_DIR / "badfixplot_out" 96 | cmd = [BADFIXPLOT, 97 | "--out", outfile, 98 | "--include_plotlyjs", "cdn", 99 | infile] 100 | print(cmd) 101 | assert subprocess.run(cmd).returncode == 0 102 | 103 | # Check some html files got generated 104 | assert Path("%s__summary.html" % outfile).exists() 105 | assert Path("%s__absolute_number_of_commits.html" % outfile).exists() 106 | 107 | # Sanity check the generated js: 108 | # There should be exactly 362 commits in v4.19.2 109 | absfile = Path("%s__absolute_number_of_commits.html" % outfile) 110 | re_commits = re.compile(r'Commits: (?P\d+)') 111 | commits = None 112 | with open(absfile, 'r') as f: 113 | for line in f: 114 | match = re_commits.search(line) 115 | if match: 116 | commits = match.group('days') 117 | break 118 | assert int(commits) == 362 119 | 120 | 121 | def test_badfixplot_none(set_up_test_data): 122 | """ 123 | Test badfixstats.py with a badfixdb with no badfixes 124 | """ 125 | 126 | # First, run badfixstats.py to generate the database 127 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 128 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 129 | cmd = [BADFIXSTATS, 130 | "--git-dir", gitdir, 131 | "--out", outfile, 132 | "--no-merge-datapoints", 133 | "v4.19^..v4.19"] 134 | print(cmd) 135 | assert subprocess.run(cmd).returncode == 0 136 | assert outfile.exists() 137 | 138 | # Then, run the badfixplot.py using the generated database 139 | infile = outfile 140 | outfile = TEST_DATA_DIR / "badfixplot_out" 141 | cmd = [BADFIXPLOT, 142 | "--out", outfile, 143 | "--include_plotlyjs", "cdn", 144 | infile] 145 | print(cmd) 146 | assert subprocess.run(cmd).returncode == 0 147 | 148 | # Check some html files got generated 149 | assert Path("%s__summary.html" % outfile).exists() 150 | assert Path("%s__absolute_number_of_commits.html" % outfile).exists() 151 | 152 | 153 | if __name__ == '__main__': 154 | pytest.main([__file__]) 155 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/find_callchains_examples.md: -------------------------------------------------------------------------------- 1 | # Instructions for finding call chains in the graph 2 | 3 | This page documents instructions and examples on usage of `find_callchains.py` script. The scripts offers a simple way to query all the call chains between one specific function and a set of a functions defined with the regular expression. 4 | 5 | ``` 6 | ./find_callchains.py --help 7 | usage: find_callchains.py [-h] --calls CALLS --from_function FROM_FUNCTION 8 | --to_function TO_FUNCTION [--out OUT] 9 | [--direction {left,right,both}] [-v] 10 | 11 | Find function call chains reachable from the input function. The end of call 12 | chain is defined with second argument which is a regular expression matching 13 | one or more function names. The output is either the database of detected call 14 | chains in csv format or image of the detected subgraph 15 | 16 | optional arguments: 17 | -h, --help show this help message and exit 18 | --out OUT name of the output file containing detected chains 19 | --direction {left,right,both} 20 | selects search direction. 21 | --cutoff CUTOFF select cutoff length for path search 22 | -v, --verbose set the verbosity level (e.g. -vv for debug level) 23 | 24 | required named arguments: 25 | --calls CALLS function call database csv file 26 | --from_function FROM_FUNCTION 27 | function name from where the search begins (literal 28 | match) 29 | --to_function TO_FUNCTION 30 | function name where call chain ends (regex match) 31 | 32 | Example: ./find_callchains.py --from_function sock_recvmsg --to_function 33 | '^x64_sys'--calls calls.csv 34 | ``` 35 | 36 | 37 | The script exports the results of a user search either to a `CSV` file (if `out` input parameter ends with .csv) or to an image file otherwise. The source function is always a literal match, while the destination function can be defined as regular expression. The search can be performed in both directions, i.e. both in 38 | callers of the source function - `left` search and callees of the source function - `right` search. 39 | 40 | # Usage examples 41 | 42 | 43 | Suppose that we have an example database for a program with a following call graph: 44 | 45 | 46 |

47 | 48 | ## Search for callers 49 | 50 | Say we are interested if the `say_hello` function is in callee list of `start_of_longer_call_chain` function. We can issue the following call to generate the path between those two functions if it exists: 51 | ``` 52 | ./find_callchains.py --from_function say_hello --to_function start_of_longer_call_chain --direction left --out long_chain.jpg --calls calls.csv 53 | ``` 54 | 55 | The result of the query is in the following image: 56 | 57 | 58 |

59 | 60 | ## Search for callees 61 | 62 | We can also search in forward direction to find out the paths from a caller `main` to a specific function `say_hello` we are interested in: 63 | ``` 64 | ./find_callchains.py --from_function main --to_function say_hello --direction right --out multi_chain.jpg --calls calls.csv 65 | ``` 66 | The command output is shown in: 67 | 68 | 69 |

70 | 71 | ## Search for both callers and callees 72 | 73 | If we want to find out if any of the `chainX` functions, where `X` is a single-digit number, is linked either as a caller or callee to `chain2` function we can issue the following query: 74 | ``` 75 | ./find_callchains.py --from_function chain2 --to_function chain[0-9] --direction both --out chainX.jpg --calls calls.csv 76 | ``` 77 | This query results in a following image for a given database: 78 | 79 | 80 |

81 | 82 | ## Larger example 83 | The examples given so far are intentionally contrived to illustrate the capabilities of the script. These results could have easily been read out from the image of the whole program graph. The usefulness of the script can be demonstrated on a more complicated codebase, such as that of a Linux kernel. Say we want to find out the receive system calls that use `sock_recvmsg` function. We can answer this question by issuing the following query: 84 | ``` 85 | ./find_callshains --calls calls.csv --from_function sock_recvmsg --to_function ^__x64_sys_recv --direction=left --cutoff=12 --out=from_sock_recvmsg_to_x64_sys_recv_left.jpg 86 | 87 | ``` 88 | Note that the database of kernel source is huge and analyzing the paths may take some time. In order to improve the performance, the optional `--cutoff` parameter can be used to limit the length of the paths that we are interested in. 89 | 90 | The output is: 91 | 92 |

93 | 94 | # Resolving duplicate function names 95 | If there are multiple functions in the input database that have the same name but are located in different files it is possible to specify the one that we are interested in using `filename:function` format. For example, if we have function `algo_implementation` in both `algo_slow.c` and `algo_fast.c` we can query the callers of the function using following query: 96 | ``` 97 | ./find_callchains.py --calls=calls.csv --to_function=algo_caller_regex --from_function=algo_fast.c:algo_implementation --direction=left --out=say_hello.jpg 98 | ``` 99 | -------------------------------------------------------------------------------- /development-process/stable-maintenance/regression-analysis/tests/test_badfixstats_int.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # SPDX-FileCopyrightText: 2019 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) 3 | # 4 | # SPDX-License-Identifier: GPL-2.0-only 5 | 6 | """ 7 | This file contains integration test cases for badfixstats.py 8 | """ 9 | 10 | import subprocess 11 | import os 12 | import sys 13 | import pytest 14 | import re 15 | from pathlib import Path 16 | import shutil 17 | import pandas as pd 18 | import hashlib 19 | 20 | TESTS_DIR = Path(os.path.dirname(os.path.realpath(__file__))) 21 | TEST_DATA_DIR = TESTS_DIR / "testdata" 22 | TEST_DATA_TAR = TESTS_DIR / "testdata.tar.bz2" 23 | BADFIXSTATS = TESTS_DIR / ".." / "badfixstats.py" 24 | 25 | 26 | @pytest.fixture() 27 | def set_up_test_data(): 28 | print("setup") 29 | shutil.rmtree(TEST_DATA_DIR, ignore_errors=True) 30 | assert subprocess.call( 31 | ["tar", "-xjf", TEST_DATA_TAR, "--directory", TESTS_DIR]) == 0 32 | yield "resource" 33 | print("clean up") 34 | shutil.rmtree(TEST_DATA_DIR) 35 | 36 | 37 | def df_from_csv_file(name): 38 | df = pd.read_csv(name, na_values=['None'], keep_default_na=True) 39 | df.reset_index(drop=True, inplace=True) 40 | return df 41 | 42 | 43 | def test_help(): 44 | """ 45 | Test help 46 | """ 47 | cmd = [BADFIXSTATS, "-h"] 48 | assert subprocess.run(cmd).returncode == 0 49 | 50 | 51 | def test_badfixstats_basic(set_up_test_data): 52 | """ 53 | Test badfixstats.py runs correctly 54 | """ 55 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 56 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 57 | indexfile = TEST_DATA_DIR / "index.pickle" 58 | cmd = [BADFIXSTATS, 59 | "--git-dir", gitdir, 60 | "--out", outfile, 61 | "--index-file", indexfile, 62 | "v4.19^..v4.19.2"] 63 | print(cmd) 64 | assert subprocess.run(cmd).returncode == 0 65 | assert outfile.exists() 66 | 67 | # There should be exactly 3 badfixes in the specified range 68 | df = df_from_csv_file(outfile) 69 | df = df[['Badfix_hexsha']].drop_duplicates() 70 | df = df[df['Badfix_hexsha'].notnull()] 71 | assert df.shape[0] == 3 72 | 73 | 74 | def test_badfixstats_no_badfixes(set_up_test_data): 75 | """ 76 | Test badfixstats.py on a rev range with no badfixes 77 | """ 78 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 79 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 80 | indexfile = TEST_DATA_DIR / "index.pickle" 81 | cmd = [BADFIXSTATS, 82 | "--git-dir", gitdir, 83 | "--out", outfile, 84 | "--index-file", indexfile, 85 | "v4.19^..v4.19"] 86 | print(cmd) 87 | assert subprocess.run(cmd).returncode == 0 88 | assert outfile.exists() 89 | 90 | # There should be no badfixes in the specified range 91 | df = df_from_csv_file(outfile) 92 | df = df[['Badfix_hexsha']].drop_duplicates() 93 | df = df[df['Badfix_hexsha'].notnull()] 94 | assert df.shape[0] == 0 95 | 96 | 97 | def test_badfixstats_no_merge_datapoints(set_up_test_data): 98 | """ 99 | Test badfixstats.py with --no-merge-datapoints 100 | """ 101 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 102 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 103 | indexfile = TEST_DATA_DIR / "index.pickle" 104 | cmd = [BADFIXSTATS, 105 | "--git-dir", gitdir, 106 | "--out", outfile, 107 | "--index-file", indexfile, 108 | "--no-merge-datapoints", 109 | "v4.19^..v4.19.2"] 110 | print(cmd) 111 | assert subprocess.run(cmd).returncode == 0 112 | assert outfile.exists() 113 | 114 | # There should be exactly 3 badfixes in the specified range 115 | df = df_from_csv_file(outfile) 116 | df = df[['Badfix_hexsha']].drop_duplicates() 117 | df = df[df['Badfix_hexsha'].notnull()] 118 | assert df.shape[0] == 3 119 | 120 | 121 | def sha256sum(filename): 122 | with open(filename, "rb") as f: 123 | return hashlib.sha256(f.read()).hexdigest() 124 | 125 | 126 | def test_badfixstats_index_constancy(set_up_test_data): 127 | """ 128 | Test badfixstats.py index-file does not update unless new commits emerge 129 | """ 130 | outfile = TEST_DATA_DIR / "badfixstats_out.csv" 131 | gitdir = TEST_DATA_DIR / "stable_4.19.2" 132 | indexfile = TEST_DATA_DIR / "index.pickle" 133 | 134 | # Run badfixstats the first time 135 | cmd = [BADFIXSTATS, 136 | "--git-dir", gitdir, 137 | "--out", outfile, 138 | "--index-file", indexfile, 139 | "v4.19^..v4.19.1"] 140 | print(cmd) 141 | assert subprocess.run(cmd).returncode == 0 142 | assert indexfile.exists() 143 | hex1 = sha256sum(indexfile) 144 | assert hex1 != "" 145 | 146 | # Run it the second time with the same rev specifier 147 | cmd = [BADFIXSTATS, 148 | "--git-dir", gitdir, 149 | "--out", outfile, 150 | "--index-file", indexfile, 151 | "v4.19^..v4.19.1"] 152 | print(cmd) 153 | assert subprocess.run(cmd).returncode == 0 154 | assert indexfile.exists() 155 | hex2 = sha256sum(indexfile) 156 | assert hex2 != "" 157 | 158 | # index.pickle content should not have changed between the two runs 159 | assert hex1 == hex2 160 | 161 | 162 | if __name__ == '__main__': 163 | pytest.main([__file__]) 164 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/simple_c_example.md: -------------------------------------------------------------------------------- 1 | # Using callgraph with simple C example 2 | 3 | This page shows an example of using callgraph with a simple C example program. 4 | 5 | Table of Contents 6 | ================= 7 | 8 | * [Setup](#setup) 9 | * [Example program](#example-program) 10 | * [Compiling target program to bitcode](#compiling-target-program-to-bitcode) 11 | * [Generating callgraph database with crix-callgraph](#generating-callgraph-database-with-crix-callgraph) 12 | * [Visualizing callgraph database](#visualizing-callgraph-database) 13 | 14 | ## Setup 15 | To begin, make sure you have gone through the following setup instructions from the main [README](../README.md): 16 | - [Getting started](../README.md#getting-started) (no kernel sources are needed for this simple example) 17 | - [Build the crix-callgraph tool](../README.md#build-the-crix-callgraph-tool) 18 | 19 | ## Example program 20 | We use the following [demo program](../tests/resources/query-callgraph/test-demo.c) as an example target program: 21 | ``` 22 | // Demonstrate callgraph with a clumsy example 23 | 24 | #include 25 | #include 26 | 27 | #define MAX_INPUT_LEN 10 28 | 29 | typedef void (*fptr_t)(char *); 30 | 31 | struct reader_a 32 | { 33 | fptr_t read; 34 | int other_data; 35 | }; 36 | 37 | struct reader_b 38 | { 39 | fptr_t read; 40 | char *other_data; 41 | }; 42 | 43 | void flush() 44 | { 45 | char c; 46 | while ((c = getchar()) != '\n' && c != EOF) 47 | ; 48 | } 49 | 50 | char *gets(char *); 51 | void read_no_check(char *buffer) 52 | { 53 | printf("Input to %s: ", __func__); 54 | gets(buffer); 55 | } 56 | 57 | void read_with_check(char *buffer) 58 | { 59 | printf("Input to %s: ", __func__); 60 | if (fgets(buffer, MAX_INPUT_LEN, stdin)) 61 | if (!strchr(buffer, '\n')) 62 | flush(); 63 | } 64 | 65 | int main() 66 | { 67 | char input[MAX_INPUT_LEN]; 68 | struct reader_a safe = {.read = read_with_check}; 69 | struct reader_b unsafe = {.read = read_no_check}; 70 | safe.read(input); 71 | unsafe.read(input); 72 | return 0; 73 | } 74 | ``` 75 | 76 | ## Compiling target program to bitcode 77 | ``` 78 | # We assume $CG_DIR variable contains the path to callgraph directory 79 | # Add correct version of clang to PATH 80 | source $CG_DIR/env.sh 81 | 82 | # Compile test-demo.c to bitcode, including debug info 83 | cd $CG_DIR/tests/resources/query-callgraph; \ 84 | clang -O0 -g -emit-llvm -c -o test-demo.bc test-demo.c 85 | ``` 86 | 87 | ## Generating callgraph database with crix-callgraph 88 | ``` 89 | # Compile crix-callgraph if it wasn't compiled yet 90 | cd $CG_DIR && make 91 | 92 | # Generate callgraph based on the test-demo.bc 93 | cd $CG_DIR/tests/resources/query-callgraph; \ 94 | $CG_DIR/build/lib/crix-callgraph test-demo.bc -o callgraph_test_demo.csv 95 | 96 | # Now, you can find the callgraph database in `callgraph_test_demo.csv` 97 | ``` 98 | 99 | ## Visualizing callgraph database 100 | 101 | To visualize the functions called by function `main` run the following command: 102 | ``` 103 | # --csv callgraph_test_demo.csv: use the file callgraph_test_demo.csv as callgraph database file 104 | # --function main: start from the target function 'main' (exact match) 105 | # --depth 3: include the function calls at depth 3 from 'main' 106 | # --edge_labels: add caller source line numbers to the graph 107 | # --colorize 'gets': colorize graph node if function name matches the given regular expression 108 | # --out test_demo.png: output png-image with filename 'test_demo.png' 109 | 110 | cd $CG_DIR/tests/resources/query-callgraph; \ 111 | $CG_DIR/scripts/query_callgraph.py --csv callgraph_test_demo.csv --function main --depth 3 \ 112 | --edge_labels --colorize 'gets' --out test_demo.png 113 | ``` 114 | Output: 115 | 116 | 117 |

118 | 119 | The output graph shows that function `main` is defined in file test-demo.c on line [44](https://github.com/elisa-tech/workgroups/blob/dc07cf1474f6c693f7087723f53e22c88a259d93/safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c#L44). It calls two functions: `read_with_check` and `read_no_check`. The calls to these functions takes place from test-demo.c on lines [49](https://github.com/elisa-tech/workgroups/blob/dc07cf1474f6c693f7087723f53e22c88a259d93/safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c#L49) and [50](https://github.com/elisa-tech/workgroups/blob/dc07cf1474f6c693f7087723f53e22c88a259d93/safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c#L50). The dashed lines indicate indirect function calls: both calls happen through a function pointer. The two called functions are defined in test-demo.c:[36](https://github.com/elisa-tech/workgroups/blob/dc07cf1474f6c693f7087723f53e22c88a259d93/safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c#L36) and test-demo.c:[30](https://github.com/elisa-tech/workgroups/blob/dc07cf1474f6c693f7087723f53e22c88a259d93/safety-architecture/tools/callgraph-tool/tests/resources/query-callgraph/test-demo.c#L30) respectively. Notice the node labels refer each function's definition, not declaration location. We colorized nodes where the function name matches regular expression 'gets': there's one such function, which is called from function `read_no_check`. Notice the filename and line number information is missing from the C library functions (strchr, fgets, printf, gets): these are external functions, for which the callgraph does not include any other information except the function name. 120 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/tests/resources/crix-callgraph/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | CFLAGS = -O0 -g 3 | INC = -I./ 4 | 5 | CXX = clang++ 6 | CXXFLAGS = -O0 -g -flto -fwhole-program-vtables -fvisibility=hidden 7 | 8 | # = means expand whenever used 9 | # := means expand once 10 | SHELL := /bin/bash 11 | CLANG_BIN_DIR := /usr/lib/llvm-10/bin 12 | CG_BIN := ../../../build/lib/crix-callgraph 13 | PATH := $(CLANG_BIN_DIR):$(PATH) 14 | LLVM_VERSION := 10.0.0 15 | 16 | # List of target c programs 17 | PROGS = \ 18 | test-mlta-basic\ 19 | test-mlta-notassigned\ 20 | test-mlta-arr\ 21 | test-mlta-assign-value\ 22 | test-mlta-confinestore\ 23 | test-mlta-memcpy\ 24 | test-mlta-null\ 25 | test-mlta-x86-init\ 26 | test-mlta-misc\ 27 | test-inline\ 28 | test-asminline\ 29 | test-cast-struct\ 30 | test-cast-fptr\ 31 | test-bitfield\ 32 | test-escape\ 33 | test-sizeof\ 34 | test-ta-mlta\ 35 | test-union\ 36 | 37 | # List of target c++ programs 38 | # TODO: remove test-virtual-func 39 | PROGS_CXX = \ 40 | test-hello\ 41 | test-inheritance-basic\ 42 | test-inheritance-global\ 43 | test-inheritance-multilevel\ 44 | test-inheritance-multiple-1\ 45 | test-inheritance-multiple-2\ 46 | test-inheritance-multiple-3\ 47 | test-namespace-1\ 48 | test-namespace-2\ 49 | test-namespace-3\ 50 | 51 | define target_success 52 | @printf "\033[32m==> Target \"$(1)\" passed\033[0m\n\n" 53 | endef 54 | 55 | progs: $(PROGS) 56 | 57 | progs-cxx: $(PROGS_CXX) 58 | 59 | # Check clang is in PATH 60 | .PHONY: clang-check 61 | clang-check: 62 | @which clang >/dev/null 63 | @which clang++ >/dev/null 64 | @which llvm-link >/dev/null 65 | 66 | cg-bin: 67 | cd ../../../ && make LLVM_DIR=$(CLANG_BIN_DIR)/../ LLVM_VERSION=$(LLVM_VERSION) crix-callgraph 68 | 69 | # Build bitcode files from the targets in PROGS 70 | # Run CG_BIN to generate the callgraph for each bitcode target 71 | .PHONY: $(PROGS) 72 | $(PROGS): %: %.c clang-check cg-bin 73 | $(CC) $(CFLAGS) $(INC) -emit-llvm -c -o $@.bc $< 74 | # To build .ll file: 75 | # $(CC) $(CFLAGS) $(INC) -emit-llvm -S -o $@.ll $< 76 | # .bclist is just a list of .bc files that will be analyzed together 77 | echo $@.bc >$@.bclist 78 | $(CG_BIN) -o $@.csv @$@.bclist 2>/dev/null 79 | 80 | # Build bitcode files from the targets in PROGS_CXX 81 | # Run CG_BIN to generate the callgraph for each bitcode target 82 | .PHONY: $(PROGS_CXX) 83 | $(PROGS_CXX): %: %.cpp clang-check cg-bin 84 | # Uses GCC's libstdc++ standard library 85 | # By default clang uses GCC's C++ standard library, libstdc++: 86 | $(CXX) $(CXXFLAGS) $(INC) -emit-llvm -c -o $@.bc $< 87 | # To build .ll file: 88 | $(CXX) $(CXXFLAGS) $(INC) -emit-llvm -S -o $@.ll $< 89 | # To use LLVM C++ standard library instead, try: 90 | # $(CXX) $(CXXFLAGS) -stdlib=libc++ $(INC) -emit-llvm -c -o $@.bc $< 91 | $(CG_BIN) -o $@.csv -cpp_linked_bitcode $@.bc $@.bc 2>/dev/null 92 | 93 | # Build bitcode files from the separate sub-project under cg-temp/ 94 | # Run CG_BIN to generate the callgraph 95 | .PHONY: cg-test-template 96 | cg-test-template: cg-bin 97 | cd cg-temp && make bcfiles && find ~+ -type f -name \*.bc > ../$@.bclist 98 | $(CG_BIN) -o $@.csv @$@.bclist 2>/dev/null 99 | $(call target_success,$@) 100 | 101 | .PHONY: test-opt 102 | test-opt: clang-check cg-bin 103 | # Build with -O0 104 | $(CC) -O0 -g $(INC) -emit-llvm -c -o $@-O0.bc $@.c 105 | echo $@-O0.bc >$@.bclist 106 | # Build with -O1 107 | $(CC) -O1 -g $(INC) -emit-llvm -c -o $@-O1.bc $@.c 108 | echo $@-O1.bc >>$@.bclist 109 | # -O1 generates nothing, but that's the point of this test 110 | $(CG_BIN) -o $@.csv @$@.bclist 2>/dev/null 111 | $(call target_success,$@) 112 | 113 | test-same-funcname: clang-check cg-bin 114 | $(CC) $(CFLAGS) $(INC) -emit-llvm -c test-same-funcname.c -o $@.bc 115 | $(CC) $(CFLAGS) $(INC) -emit-llvm -c test-same-funcname.module.c -o module.bc 116 | # To test the binary: 117 | # $(CC) $(CFLAGS) $(INC) test-same-funcname.c test-same-funcname.module.c -o test-same-funcname 118 | echo $@.bc >$@.bclist 119 | echo module.bc >>$@.bclist 120 | $(CG_BIN) -o $@.csv @$@.bclist 2>/dev/null 121 | $(call target_success,$@) 122 | 123 | test-modules: clang-check cg-bin 124 | $(CXX) $(CXXFLAGS) $(INC) -emit-llvm -c test-modules.base.cpp -o $@.bc 125 | $(CXX) $(CXXFLAGS) $(INC) -emit-llvm -c test-modules.child.cpp -o test-modules.child.bc 126 | # To test the binary: 127 | # $(CXX) $(CXXFLAGS) $(INC) test-modules.base.cpp test-modules.child.cpp -o test-modules-cpp 128 | echo $@.bc >$@.bclist 129 | echo test-modules.child.bc >>$@.bclist 130 | llvm-link test-modules.child.bc $@.bc > $@.linked.bc 131 | # To emit .ll file from the linked module 132 | # llvm-dis $@.linked.bc 133 | $(CG_BIN) -o $@.csv -cpp_linked_bitcode $@.linked.bc @$@.bclist 2>/dev/null 134 | $(call target_success,$@) 135 | 136 | # Generate expected_calls.csv, which 137 | # will be used as a baseline to compare the results against 138 | # when running the tests 139 | expected_calls: progs progs-cxx cg-test-template test-opt test-same-funcname test-modules 140 | # Remove earlier expected_calls.csv 141 | rm -f $@.csv 142 | # Merge all csv files generated by prerequisite targets, keeping 143 | # the csv header only from the first csv file 144 | awk 'FNR==1 && NR!=1{next;}{print}' *.csv > $@.csv 145 | make clean 146 | @printf "\nWrote: '$@.csv'\n" 147 | $(call target_success,$@) 148 | 149 | # Clean everything except expected_calls.csv 150 | clean: 151 | cd cg-temp && make clean 152 | find . -type f -name "*.bc" -delete 153 | find . -type f -name "*.ll" -delete 154 | find . -type f -name "*.bclist" -delete 155 | find . -type f -name "*.csv" -and ! -name "expected_calls.csv" -delete 156 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | # Contributor Covenant Code of Conduct 3 | 4 | ## Our Pledge 5 | 6 | We as members, contributors, and leaders pledge to make participation in our 7 | community a harassment-free experience for everyone, regardless of age, body 8 | size, visible or invisible disability, ethnicity, sex characteristics, gender 9 | identity and expression, level of experience, education, socio-economic status, 10 | nationality, personal appearance, race, religion, or sexual identity 11 | and orientation. 12 | 13 | We pledge to act and interact in ways that contribute to an open, welcoming, 14 | diverse, inclusive, and healthy community. 15 | 16 | ## Our Standards 17 | 18 | Examples of behavior that contributes to a positive environment for our 19 | community include: 20 | 21 | * Demonstrating empathy and kindness toward other people 22 | * Being respectful of differing opinions, viewpoints, and experiences 23 | * Giving and gracefully accepting constructive feedback 24 | * Accepting responsibility and apologizing to those affected by our mistakes, 25 | and learning from the experience 26 | * Focusing on what is best not just for us as individuals, but for the 27 | overall community 28 | 29 | Examples of unacceptable behavior include: 30 | 31 | * The use of sexualized language or imagery, and sexual attention or 32 | advances of any kind 33 | * Trolling, insulting or derogatory comments, and personal or political attacks 34 | * Public or private harassment 35 | * Publishing others' private information, such as a physical or email 36 | address, without their explicit permission 37 | * Other conduct which could reasonably be considered inappropriate in a 38 | professional setting 39 | 40 | ## Enforcement Responsibilities 41 | 42 | Community leaders are responsible for clarifying and enforcing our standards of 43 | acceptable behavior and will take appropriate and fair corrective action in 44 | response to any behavior that they deem inappropriate, threatening, offensive, 45 | or harmful. 46 | 47 | Community leaders have the right and responsibility to remove, edit, or reject 48 | comments, commits, code, wiki edits, issues, and other contributions that are 49 | not aligned to this Code of Conduct, and will communicate reasons for moderation 50 | decisions when appropriate. 51 | 52 | ## Scope 53 | 54 | This Code of Conduct applies within all community spaces, and also applies when 55 | an individual is officially representing the community in public spaces. 56 | Examples of representing our community include using an official e-mail address, 57 | posting via an official social media account, or acting as an appointed 58 | representative at an online or offline event. 59 | 60 | ## Enforcement 61 | 62 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 63 | reported to the community leaders responsible for enforcement at 64 | callgraph-tool@elisa-tech.com. 65 | All complaints will be reviewed and investigated promptly and fairly. 66 | 67 | All community leaders are obligated to respect the privacy and security of the 68 | reporter of any incident. 69 | 70 | ## Enforcement Guidelines 71 | 72 | Community leaders will follow these Community Impact Guidelines in determining 73 | the consequences for any action they deem in violation of this Code of Conduct: 74 | 75 | ### 1. Correction 76 | 77 | **Community Impact**: Use of inappropriate language or other behavior deemed 78 | unprofessional or unwelcome in the community. 79 | 80 | **Consequence**: A private, written warning from community leaders, providing 81 | clarity around the nature of the violation and an explanation of why the 82 | behavior was inappropriate. A public apology may be requested. 83 | 84 | ### 2. Warning 85 | 86 | **Community Impact**: A violation through a single incident or series 87 | of actions. 88 | 89 | **Consequence**: A warning with consequences for continued behavior. No 90 | interaction with the people involved, including unsolicited interaction with 91 | those enforcing the Code of Conduct, for a specified period of time. This 92 | includes avoiding interactions in community spaces as well as external channels 93 | like social media. Violating these terms may lead to a temporary or 94 | permanent ban. 95 | 96 | ### 3. Temporary Ban 97 | 98 | **Community Impact**: A serious violation of community standards, including 99 | sustained inappropriate behavior. 100 | 101 | **Consequence**: A temporary ban from any sort of interaction or public 102 | communication with the community for a specified period of time. No public or 103 | private interaction with the people involved, including unsolicited interaction 104 | with those enforcing the Code of Conduct, is allowed during this period. 105 | Violating these terms may lead to a permanent ban. 106 | 107 | ### 4. Permanent Ban 108 | 109 | **Community Impact**: Demonstrating a pattern of violation of community 110 | standards, including sustained inappropriate behavior, harassment of an 111 | individual, or aggression toward or disparagement of classes of individuals. 112 | 113 | **Consequence**: A permanent ban from any sort of public interaction within 114 | the community. 115 | 116 | ## Attribution 117 | 118 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 119 | version 2.0, available at 120 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 121 | 122 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 123 | enforcement ladder](https://github.com/mozilla/diversity). 124 | 125 | [homepage]: https://www.contributor-covenant.org 126 | 127 | For answers to common questions about this code of conduct, see the FAQ at 128 | https://www.contributor-covenant.org/faq. Translations are available at 129 | https://www.contributor-covenant.org/translations. 130 | 131 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/src/lib/Analyzer.cc: -------------------------------------------------------------------------------- 1 | // SPDX-FileCopyrightText: 2020 callgraph-tool authors. All rights reserved 2 | // 3 | // SPDX-License-Identifier: LicenseRef-LLVM 4 | 5 | //===-- Analyzer.cc - the kernel-analysis framework--------------===// 6 | // 7 | // This file implements the analysis framework. It calls the pass for 8 | // building call-graph and the pass for finding security checks. 9 | // 10 | // ===-----------------------------------------------------------===// 11 | 12 | #include "llvm/Bitcode/BitcodeReader.h" 13 | #include "llvm/IRReader/IRReader.h" 14 | #include "llvm/Support/CommandLine.h" 15 | #include "llvm/Support/PrettyStackTrace.h" 16 | #include "llvm/Support/Signals.h" 17 | 18 | #include "CallGraph.h" 19 | #include "Common.h" 20 | #include "VirtualCallTargets.h" 21 | 22 | using namespace llvm; 23 | using namespace std; 24 | using namespace virtcall; 25 | 26 | // Command line parameters. 27 | cl::list InputFilenames(cl::Positional, cl::OneOrMore, 28 | cl::desc("")); 29 | 30 | cl::OptionCategory CallgraphCategory("Callgraph Options"); 31 | 32 | cl::opt optOutFilename( 33 | "o", cl::desc("Specify output CSV filename (default='callgraph.csv)"), 34 | cl::value_desc("filename"), cl::init("callgraph.csv"), 35 | cl::cat(CallgraphCategory)); 36 | 37 | cl::opt optAnalysisType( 38 | cl::desc("Resolve indirect call targets with:"), 39 | cl::values( 40 | clEnumVal(mlta_pref, 41 | "Prefer MLTA, fallback to TA if MLTA failed (default)"), 42 | clEnumVal(mlta_only, "Find targets of indirect calls based on MLTA"), 43 | clEnumVal( 44 | ta_only, 45 | "Find targets of indirect calls based on type analysis (TA)")), 46 | cl::cat(CallgraphCategory)); 47 | 48 | cl::opt optDemangle( 49 | cl::desc("Demangle C++ function names:"), 50 | cl::values(clEnumVal(demangle_debug_only, 51 | "Demangle function names that are " 52 | "associated with debug info (default)"), 53 | clEnumVal(demangle_all, "Demangle all function names"), 54 | clEnumVal(demangle_none, "Don't demangle function names")), 55 | cl::cat(CallgraphCategory)); 56 | 57 | cl::opt optCppLinkedBitcode( 58 | "cpp_linked_bitcode", 59 | cl::desc( 60 | "Specify whole-program bitcode file for C++ virtual call resolution"), 61 | cl::value_desc("filename"), cl::init(""), cl::cat(CallgraphCategory)); 62 | 63 | GlobalContext GlobalCtx; 64 | 65 | void IterativeModulePass::run(ModuleList &modules) { 66 | 67 | ModuleList::iterator i, e; 68 | OP << "[" << ID << "] Initializing " << modules.size() << " modules "; 69 | bool again = true; 70 | while (again) { 71 | again = false; 72 | for (i = modules.begin(), e = modules.end(); i != e; ++i) { 73 | if (DEBUG) 74 | OP << "\n"; 75 | again |= doInitialization(i->first); 76 | OP << "."; 77 | } 78 | } 79 | OP << "\n"; 80 | 81 | unsigned iter = 0, changed = 1; 82 | while (changed) { 83 | ++iter; 84 | changed = 0; 85 | unsigned counter_modules = 0; 86 | unsigned total_modules = modules.size(); 87 | for (i = modules.begin(), e = modules.end(); i != e; ++i) { 88 | OP << "[" << ID << " / " << iter << "] "; 89 | OP << "[" << ++counter_modules << " / " << total_modules << "] "; 90 | OP << "[" << i->second << "]"; 91 | if (DEBUG) 92 | OP << "\n"; 93 | 94 | bool ret = doModulePass(i->first); 95 | if (ret) { 96 | ++changed; 97 | OP << "\t [CHANGED]\n"; 98 | } else 99 | OP << "\n"; 100 | } 101 | OP << "[" << ID << "] Updated in " << changed << " modules.\n"; 102 | } 103 | 104 | OP << "[" << ID << "] Postprocessing ...\n"; 105 | again = true; 106 | while (again) { 107 | again = false; 108 | for (i = modules.begin(), e = modules.end(); i != e; ++i) { 109 | // TODO: Dump the results. 110 | again |= doFinalization(i->first); 111 | } 112 | } 113 | 114 | OP << "[" << ID << "] Done!\n\n"; 115 | } 116 | 117 | int main(int argc, char **argv) { 118 | 119 | // Print a stack trace if we signal out. 120 | sys::PrintStackTraceOnErrorSignal(argv[0]); 121 | PrettyStackTraceProgram X(argc, argv); 122 | 123 | llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. 124 | 125 | cl::HideUnrelatedOptions(CallgraphCategory); 126 | cl::ParseCommandLineOptions( 127 | argc, argv, 128 | "\n\n" 129 | " Generate precise global callgraph given input bitcode files\n\n" 130 | "EXAMPLES:\n\n" 131 | " - Generate callgraph given input file '/path/to/foo.bc', write output " 132 | "to default output file 'callgraph.csv':\n" 133 | " crix-callgraph /path/to/foo.bc" 134 | "\n\n" 135 | " - Generate callgraph given two input files '/path/to/foo.bc' and " 136 | "'/path/to/bar.bc', write output " 137 | "to 'foobar.csv':\n" 138 | " crix-callgraph /path/to/foo.bc /path/to/bar.bc -o foobar.csv" 139 | "\n\n" 140 | " - Generate callgraph given a text file with a list of input files " 141 | "'/path/to/foobar.txt' containing one bitcode input file per line, write " 142 | "output " 143 | "to 'foobar.csv':\n" 144 | " crix-callgraph @/path/to/foobar.txt -o foobar.csv" 145 | "\n\n" 146 | 147 | ); 148 | 149 | const string yellow("\033[1;33m"); 150 | const string reset("\033[0m"); 151 | GlobalCtx.analysisType = optAnalysisType; 152 | GlobalCtx.demangle = optDemangle; 153 | GlobalCtx.csvout.open(optOutFilename); 154 | SMDiagnostic Err; 155 | 156 | // Loading modules 157 | OP << "Total " << InputFilenames.size() << " file(s)\n"; 158 | 159 | for (unsigned i = 0; i < InputFilenames.size(); ++i) { 160 | 161 | LLVMContext *LLVMCtx = new LLVMContext(); 162 | unique_ptr M = parseIRFile(InputFilenames[i], Err, *LLVMCtx); 163 | 164 | if (M == NULL) { 165 | WARN_FMT("Error loading file: '%s'\n", InputFilenames[i].c_str()); 166 | continue; 167 | } else if (M->getNamedMetadata("llvm.dbg.cu") == NULL) { 168 | WARN_FMT("Debug info missing: '%s'\n", M->getName().str().c_str()); 169 | } 170 | 171 | Module *Module = M.release(); 172 | StringRef MName = StringRef(strdup(InputFilenames[i].data())); 173 | GlobalCtx.Modules.push_back(make_pair(Module, MName)); 174 | } 175 | 176 | // Build global callgraph 177 | CallGraphPass CGPass(&GlobalCtx); 178 | CGPass.run(GlobalCtx.Modules); 179 | if (!optCppLinkedBitcode.empty()) { 180 | CGPass.resolveVirtualCallTargets(optCppLinkedBitcode); 181 | } 182 | OP << "[Wrote: " << optOutFilename << "]\n"; 183 | 184 | return 0; 185 | } 186 | -------------------------------------------------------------------------------- /safety-architecture/tools/callgraph-tool/doc/coverage/coverage_gaps.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Coverage Gaps 8 | Having the target system callgraph and the function-local code coverage for each function exercised during a test run, we can combine the information to find coverage gaps. 9 | 10 | Coverage gaps are callgraph subtrees where the potential for code coverage improvement is greatest based on the function coverage and the callgraph subtree size rooted with the specific function call. This instruction shows how [find_coverage_gaps.py](../../scripts/find_coverage_gaps.py) can be used to identify such coverage gaps. 11 | 12 | ## Getting Started 13 | You need a callgraph database in csv format. Follow the instructions from [README.md](../../README.md) to generate the callgraph for your target system. 14 | 15 | You also need the code coverage information. The expected format is a CSV file, with at least the following headers: `Filename,Function,Percent`. Look up the [document](syzkaller_coverage.md) for an example of how to generate such coverage information from a syzkaller test run. 16 | 17 | ## Finding Coverage Gaps 18 | For the sake of example, let's assume we have the following callgraph and the coverage information as shown in the graph (the multiple paths in the image are collated using `merge_edges` for better visibility): 19 | 20 | 21 | To find functions where the potential for code coverage improvement is greatest, run [find_coverage_gaps.py](../../find_coverage_gaps.py) as follows: 22 | ``` 23 | ./find_coverage_gaps.py \ 24 | --calls target_callgraph.csv \ 25 | --coverage target_coverage.csv \ 26 | --caller_function_regex '^ksys_mmap_pgoff$' \ 27 | --maxdepth 2 \ 28 | --out ksys_mmap_pgoff_cov.csv 29 | ``` 30 | 31 | The above command runs [find_coverage_gaps.py](../../scripts/find_coverage_gaps.py) with the specified callgraph database and coverage information (`--calls` and `--coverage`). It finds functions where the function name matches regular expression `^ksys_mmap_pgoff$` and follows each call chain starting from the matching functions. For each call chain, it calculates the coverage gap for all functions in the chain following the caller-callee relations to a point where caller reaches at most `--maxdepth 2` depth. The resulting output is stored in `ksys_mmap_pgoff_cov.csv`. 32 | 33 | In the following section, we use the `csvsql` and `csvlook` from the [csvkit](https://csvkit.readthedocs.io/en/latest/index.html) suite to view and query the output data. 34 | 35 | In this simple example, the output contains only the excerpt caller-callee pairs as shown in the below table: 36 | ``` 37 | # Use csvsql to output only the specified columns to make the output more readable 38 | # Sort by callee_coverage_gap descending to make the largest coverage gaps appear at the top 39 | # Use csvlook to render the output as ascii table: 40 | 41 | csvsql --query \ 42 | "select \ 43 | caller_function,callee_function,callee_coverage,callee_coverage_gap,call_stack \ 44 | from ksys_mmap_pgoff_cov \ 45 | order by callee_coverage_gap desc" \ 46 | ksys_mmap_pgoff_cov.csv | csvlook 47 | 48 | | caller_function | callee_function | callee_coverage_gap | callee_coverage | callee_subtree_size | 49 | | ---------------------------- | -------------------------------- | ------------------- | --------------- | ------------------- | 50 | | ksys_mmap_pgoff | hugetlb_file_setup | 148,667… | 66,667… | 446 | 51 | | hugetlb_file_setup | hugetlb_reserve_pages | 90,632… | 26,316… | 123 | 52 | | vm_mmap_pgoff | do_mmap | 75,286… | 55,714… | 170 | 53 | | vm_mmap_pgoff | do_mmap | 75,286… | 55,714… | 170 | 54 | | vm_mmap_pgoff | do_mmap | 75,286… | 55,714… | 170 | 55 | | hugetlb_file_setup | iput | 40,912… | 61,765… | 107 | 56 | | hugetlb_file_setup | hugetlbfs_get_inode | 26,400… | 40,000… | 44 | 57 | | hugetlb_file_setup | user_shm_lock | 20,000… | 0,000… | 20 | 58 | | vm_mmap_pgoff | __mm_populate | 19,111… | 55,556… | 43 | 59 | | hugetlb_file_setup | user_shm_unlock | 4,000… | 0,000… | 4 | 60 | | hugetlb_file_setup | alloc_file_pseudo | 3,833… | 83,333… | 23 | 61 | ... 62 | | ksys_mmap_pgoff | __sanitizer_cov_trace_pc | 0,000… | 0,000… | 0 | 63 | | fput | __sanitizer_cov_trace_pc | 0,000… | 0,000… | 0 | 64 | ``` 65 | 66 | These caller-callee pairs are the cases where the callee coverage is not 100% in the example graph. Since the output is ordered descending by callee_coverage_gap, the topmost entry (`ksys_mmap_pgoff ==> hugetlb_file_setup`) indicates the callee where the potential for code coverage improvement is greatest. The value of the callee_coverage_gap is based on the coverage, as well as the subtree size rooted to the specific callee function considering the call chain at most `--maxdepth 2` callers deep. 67 | 68 | For the entry lower in the table (`hugetlb_file_setup ==> usr_shm_unlock`) callee_coverage_gap is much smaller even though the coverage is worse (0%) than the coverage for the first entry (66.667%). The reason is that `__audit_mmap_fd` is at the end of the call chain so possible coverage improvement would not lead to coverage improvements in any new subtrees. Conversely, the first entry callee for the first entry on the above table is associated to subtree with many nodes. Therefore, the impact of possible coverage improvement for callee `hugetlb_file_setup` is potentially bigger considering the overall coverage. 69 | There are many entries to `__sanitizer_cov_trace_pc`. These are the calls inserted by the compiler in order to be able to track the coverage but they are not relevant in discussion for increasing the overall coverage. 70 | 71 | The output [CSV](ksys_mmap_pgoff_cov.csv) also contains the full call stack starting from the function that initially matched the regular expression `^ksys_mmap_pgoff$`, making it easier to navigate the call chain considered in the calculation. We do not show it here for better readability. 72 | --------------------------------------------------------------------------------