diff --git a/.github/workflows/CI-unixish.yml b/.github/workflows/CI-unixish.yml index 38c6138f6143..d67331a06eba 100644 --- a/.github/workflows/CI-unixish.yml +++ b/.github/workflows/CI-unixish.yml @@ -421,7 +421,7 @@ jobs: - name: Test Signalhandler run: | cmake -S . -B cmake.output.signal -G "Unix Makefiles" -DBUILD_TESTS=On - cmake --build cmake.output.signal --target test-signalhandler -- -j$(nproc) + cmake --build cmake.output.signal --target test-signalhandler test-stacktrace -- -j$(nproc) cp cmake.output.signal/bin/test-s* . python3 -m pytest -Werror --strict-markers -vv test/signal/test-*.py diff --git a/test/signal/CMakeLists.txt b/test/signal/CMakeLists.txt index 30dc1e861f01..da1ba56b3881 100644 --- a/test/signal/CMakeLists.txt +++ b/test/signal/CMakeLists.txt @@ -7,4 +7,14 @@ target_include_directories(test-signalhandler PRIVATE ${PROJECT_SOURCE_DIR}/cli target_compile_options_safe(test-signalhandler -Wno-missing-declarations) target_compile_options_safe(test-signalhandler -Wno-missing-prototypes) # required for backtrace() to produce function names -target_link_options(test-signalhandler PRIVATE -rdynamic) \ No newline at end of file +target_link_options(test-signalhandler PRIVATE -rdynamic) + +add_executable(test-stacktrace + test-stacktrace.cpp + ${PROJECT_SOURCE_DIR}/cli/stacktrace.cpp) +target_include_directories(test-stacktrace PRIVATE ${PROJECT_SOURCE_DIR}/cli ${PROJECT_SOURCE_DIR}/lib) +# names for static functions are omitted from trace +target_compile_options_safe(test-stacktrace -Wno-missing-declarations) +target_compile_options_safe(test-stacktrace -Wno-missing-prototypes) +# required for backtrace() to produce function names +target_link_options(test-stacktrace PRIVATE -rdynamic) \ No newline at end of file diff --git a/test/signal/test-stacktrace.cpp b/test/signal/test-stacktrace.cpp new file mode 100644 index 000000000000..f3b276f7c041 --- /dev/null +++ b/test/signal/test-stacktrace.cpp @@ -0,0 +1,41 @@ +/* + * Cppcheck - A tool for static C/C++ code analysis + * Copyright (C) 2007-2024 Cppcheck team. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "stacktrace.h" + +// static functions are omitted from trace + +void my_func_2() +{ + print_stacktrace(stdout, 0, true, -1); +} + +void my_func() +{ + my_func_2(); +} + +int main(int argc, const char * const argv[]) +{ + if (argc != 2) + return 1; + + my_func(); + + return 0; +} diff --git a/test/signal/test-stacktrace.py b/test/signal/test-stacktrace.py new file mode 100644 index 000000000000..8f9e57632075 --- /dev/null +++ b/test/signal/test-stacktrace.py @@ -0,0 +1,38 @@ +import subprocess +import os +import sys + +def _lookup_cppcheck_exe(exe_name): + # path the script is located in + script_path = os.path.dirname(os.path.realpath(__file__)) + + if sys.platform == "win32": + exe_name += ".exe" + + for base in (script_path + '/../../', './'): + for path in ('', 'bin/', 'bin/debug/'): + exe_path = base + path + exe_name + if os.path.isfile(exe_path): + print("using '{}'".format(exe_path)) + return exe_path + + return None + +def _call_process(): + exe = _lookup_cppcheck_exe('test-stacktrace') + if exe is None: + raise Exception('executable not found') + p = subprocess.Popen([exe], stdout=subprocess.PIPE, stderr=subprocess.PIPE) + comm = p.communicate() + stdout = comm[0].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + stderr = comm[1].decode(encoding='utf-8', errors='ignore').replace('\r\n', '\n') + return p.returncode, stdout, stderr + + +def test_stack(): + exitcode, stdout, stderr = _call_process() + assert stderr == '' + lines = stdout.splitlines() + assert lines[0] == 'Callstack:' + assert lines[1].endswith('my_func_2()') + assert lines[2].endswith('my_func()')