├── cppguard_tests_dyn_lib ├── tests_dyn_lib.def ├── tests_dyn_lib_api.h ├── test_in_c_file_dyn_lib.h ├── test_in_cpp_file_dyn_lib.hpp ├── tests_dyn_lib_api.c ├── critical_section_linux.cpp ├── critical_section_linux.hpp ├── deadlock_monitor_api.cpp ├── CMakeLists.txt ├── test_in_cpp_file_dyn_lib.cpp ├── test_in_c_file_dyn_lib.c └── dl_main.cpp ├── cppguard_tests_guarded_dyn_lib ├── tests_dyn_lib.def ├── CMakeLists.txt └── dl_main.cpp ├── cppguard ├── dll_exports │ ├── cppguard_exports.def │ └── deadlock_monitor_api.cpp ├── helper │ ├── spinlock.hpp │ ├── options_parser.hpp │ ├── cs_mutex.cpp │ ├── timer.hpp │ ├── attributes.hpp │ ├── cs_mutex.hpp │ ├── run_time_options.hpp │ ├── one_shot_timer.hpp │ ├── low_level_alloc.h │ ├── one_shot_timer.cpp │ ├── options_parser.cpp │ └── debugbreak.h ├── output │ ├── log.hpp │ ├── log_helper.hpp │ └── log.cpp ├── thread_history │ ├── thread_history_helper.hpp │ └── thread_history_helper.cpp ├── stacktrace │ ├── stacktrace.h │ └── stacktrace.cc ├── fmt │ ├── format.cc │ └── fmt │ │ ├── ostream.h │ │ └── args.h ├── synch_locks_held.hpp ├── thread_watchdog │ ├── thread_watchdog.hpp │ └── thread_watchdog.cpp ├── deadlock_monitor.hpp ├── include │ └── cppguard.h ├── cppguard_rc.in ├── api_monitors │ ├── mutex_monitor.cpp │ └── critical_section_monitor.cpp ├── dl_main.cpp ├── graphcycles │ └── graphcycles.h └── CMakeLists.txt ├── docs ├── Third_party_software.md ├── Options.md └── Quickstart.md ├── quickstart_example ├── CMakeLists.txt └── main.cpp ├── NOTICE ├── cppguard_tests_static_lib ├── test_in_cpp_file_shared_lib.hpp ├── test_in_c_file_shared_lib.h ├── CMakeLists.txt ├── test_in_c_file_shared_lib.c └── test_in_cpp_file_shared_lib.cpp ├── cppguard_tests ├── utils │ ├── random_numbers.hpp │ ├── cs.hpp │ ├── signal.hpp │ ├── random_numbers.cpp │ ├── cs.cpp │ ├── spinlock.hpp │ └── signal.cpp ├── critical_section_linux.hpp ├── test_c_file.h ├── test_c_file.c ├── CMakeLists.txt ├── main.cpp ├── test_std_recursive_mutex_good.cpp ├── test_thread_watchdog_good.cpp ├── test_std_recursive_mutex_bad.cpp └── test_thread_watchdog_bad.cpp ├── cppguard_tests_exe_used_guarded_dll ├── CMakeLists.txt └── main.cpp ├── CMakeLists.txt ├── README.md └── .gitignore /cppguard_tests_dyn_lib/tests_dyn_lib.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | run_c_test 3 | run_cpp_test -------------------------------------------------------------------------------- /cppguard_tests_guarded_dyn_lib/tests_dyn_lib.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | good_test 3 | bad_test 4 | -------------------------------------------------------------------------------- /cppguard/dll_exports/cppguard_exports.def: -------------------------------------------------------------------------------- 1 | EXPORTS 2 | cppguard_set_options 3 | cppguard_increase_ignore_counter 4 | cppguard_mtx_lock 5 | cppguard_mtx_unlock 6 | cppguard_mtx_trylock 7 | cppguard_init_in_situ 8 | cppguard_mtx_destroy_in_situ 9 | cppguard_cnd_wait 10 | cppguard_cnd_timedwait 11 | cppguard_initialize_critical_section 12 | cppguard_enter_critical_section 13 | cppguard_leave_critical_section 14 | cppguard_tryenter_critical_section 15 | cppguard_delete_critical_section -------------------------------------------------------------------------------- /docs/Third_party_software.md: -------------------------------------------------------------------------------- 1 | # Used Third-party software 2 | 3 | * fmtlib: https://github.com/fmtlib/fmt (MIT license) 4 | * pystring: https://github.com/imageworks/pystring (BSD 3-Clause "New" or "Revised" License) 5 | * debugbreak: https://github.com/scottt/debugbreak/blob/master (BSD 2-Clause "Simplified" License) 6 | * https://github.com/github/gitignore (Creative Commons Zero v1.0 Universal) 7 | * doctest: https://github.com/doctest/doctest (MIT License) 8 | * argh: https://github.com/adishavit/argh -------------------------------------------------------------------------------- /quickstart_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #CppGuard quickstart_example 2 | 3 | cmake_minimum_required(VERSION 3.15) 4 | project(quickstart_example) 5 | 6 | add_executable(quickstart_example main.cpp) 7 | 8 | #add this line to all targets you want to test 9 | target_link_libraries(quickstart_example cppguard::cppguard cppguard::auto_include) 10 | 11 | #Optimization: 12 | #if the target is static library use:target_link_libraries(quickstart_example cppguard::auto_include) 13 | #if the target is a exe or dll which does not contain any locks use: target_link_libraries(quickstart_example cppguard::cppguard) 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /cppguard/helper/spinlock.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | //from https://rigtorp.se/spinlock/ 5 | struct spinlock_t { 6 | std::atomic lock_ { false }; 7 | 8 | void lock() noexcept { 9 | for (;;) { 10 | // Optimistically assume the lock is free on the first try 11 | if (!lock_.exchange(true, std::memory_order_acquire)) { 12 | return; 13 | } 14 | // Wait for lock to be released without generating cache misses 15 | while (lock_.load(std::memory_order_relaxed)) { 16 | // Issue X86 PAUSE or ARM YIELD instruction to reduce contention between 17 | // hyper-threads 18 | #ifdef _WIN32 19 | _mm_pause(); 20 | #else 21 | __builtin_ia32_pause(); 22 | #endif 23 | } 24 | } 25 | } 26 | 27 | void unlock() noexcept { 28 | lock_.store(false, std::memory_order_release); 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | CppGuard 2 | Copyright 2023 Jochen Baier 3 | 4 | 5 | This files contains code derived from 'Abseil - C++ Common Libraries' 6 | https://github.com/abseil/abseil-cpp, Copyright 2017 The Abseil Authors 7 | * graphcycles.cc/graphcycles.h 8 | * low_level_alloc.cc/low_level_alloc.h 9 | * stacktrace.cc/stacktrace.h 10 | * thread_history_helper.cpp/thread_history_helper.hpp 11 | 12 | This software uses: 13 | * fmtlib: https://github.com/fmtlib/fmt (MIT license) 14 | * pystring: https://github.com/imageworks/pystring (BSD 3-Clause "New" or "Revised" License) 15 | * debugbreak: https://github.com/scottt/debugbreak/blob/master (BSD 2-Clause "Simplified" License) 16 | * https://github.com/github/gitignore (Creative Commons Zero v1.0 Universal) 17 | * doctest: https://github.com/doctest/doctest (MIT License) 18 | * argh: https://github.com/adishavit/argh 19 | 20 | 21 | -------------------------------------------------------------------------------- /cppguard_tests_static_lib/test_in_cpp_file_shared_lib.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | void test_cpp_file_in_shared_lib_thread_sanitzer_example(); 21 | -------------------------------------------------------------------------------- /cppguard_tests/utils/random_numbers.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | namespace random_helper 21 | { 22 | int thread_safe_uniform_int(const int& p_min, const int& p_max); 23 | } 24 | 25 | -------------------------------------------------------------------------------- /cppguard/helper/options_parser.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include "run_time_options.hpp" 21 | 22 | void parse_options(const char* p_options_ptr, runtime_options_t& p_runtime_options); 23 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/tests_dyn_lib_api.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | void run_c_test(); 25 | void run_cpp_test(); 26 | 27 | #ifdef __cplusplus 28 | } 29 | #endif 30 | 31 | 32 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/test_in_c_file_dyn_lib.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | void test_c_file_in_dyn_lib_thread_sanitzer_example(); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /cppguard/output/log.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include "fmt/format.h" 21 | 22 | struct runtime_options_t; 23 | 24 | namespace logger 25 | { 26 | void log_msg(fmt::memory_buffer& p_log_buffer, const runtime_options_t& p_runtime_options); 27 | } 28 | -------------------------------------------------------------------------------- /cppguard_tests_static_lib/test_in_c_file_shared_lib.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | void test_c_file_in_shared_lib_thread_sanitzer_example(); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/test_in_cpp_file_dyn_lib.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #ifdef __cplusplus 21 | extern "C" { 22 | #endif 23 | 24 | void test_cpp_file_in_dyn_lib_thread_sanitzer_example(); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | -------------------------------------------------------------------------------- /cppguard_tests_exe_used_guarded_dll/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.15) 19 | project(cppguard_tests_exe_used_guarded_dll) 20 | 21 | add_executable(cppguard_tests_exe_used_guarded_dll main.cpp) 22 | 23 | target_link_libraries(cppguard_tests_exe_used_guarded_dll cppguard_tests_guarded_dyn_lib) 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /cppguard_tests/utils/cs.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #ifdef _WIN32 23 | #include 24 | #else 25 | #include "critical_section_linux.hpp" 26 | #endif 27 | 28 | struct cs_maker_t 29 | { 30 | CRITICAL_SECTION* make_scoped(); 31 | ~cs_maker_t(); 32 | std::vector m_cs_list; 33 | }; 34 | -------------------------------------------------------------------------------- /cppguard_tests/utils/signal.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | namespace utils 24 | { 25 | class event_t 26 | { 27 | public: 28 | std::cv_status wait_with_timeout(const int& p_milliseconds); 29 | std::cv_status wait(); 30 | void notify_one(); 31 | 32 | private: 33 | std::mutex m_signal_mutex; 34 | std::condition_variable m_signal_condition; 35 | bool m_notified=false; 36 | }; 37 | 38 | }; -------------------------------------------------------------------------------- /cppguard/thread_history/thread_history_helper.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | struct SynchLocksHeld; 23 | struct runtime_options_t; 24 | 25 | namespace thread_history_helper 26 | { 27 | bool update_lock_history_after_lock(const void* mu, const uint64_t& id, SynchLocksHeld& held_locks, const runtime_options_t& p_runtime_options); 28 | void update_lock_history_in_lock(void* mu, const uint64_t& id, SynchLocksHeld& held_locks); 29 | } 30 | -------------------------------------------------------------------------------- /cppguard_tests_guarded_dyn_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.15) 19 | 20 | set(source_list 21 | dl_main.cpp 22 | ) 23 | 24 | if (WIN32) 25 | set(source_list ${source_list} 26 | tests_dyn_lib.def 27 | ) 28 | endif() 29 | 30 | 31 | add_library(cppguard_tests_guarded_dyn_lib SHARED ${source_list}) 32 | 33 | if (WIN32) 34 | target_link_libraries(cppguard_tests_guarded_dyn_lib cppguard::cppguard cppguard::auto_include) 35 | endif() 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /cppguard/stacktrace/stacktrace.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include "attributes.hpp" 21 | #include "fmt/format.h" 22 | 23 | TM_ATTRIBUTE_NOINLINE int GetStackFrames(void** result, int p_max_depth, int p_skip_count); 24 | void InitializeSymbolizer(); 25 | void printf_call_stack_symbol_infos(void** pcs, int n, fmt::memory_buffer& p_callstack_string); 26 | TM_ATTRIBUTE_NOINLINE void save_current_callstack_to_string(fmt::memory_buffer& p_callstack_string, const int& p_skip_count); 27 | -------------------------------------------------------------------------------- /cppguard/helper/cs_mutex.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "cs_mutex.hpp" 23 | 24 | cs_mutex_t::cs_mutex_t() 25 | { 26 | #ifdef _WIN32 27 | InitializeCriticalSection(&m_mutex); 28 | #endif 29 | } 30 | 31 | cs_mutex_t::~cs_mutex_t() 32 | { 33 | #ifdef _WIN32 34 | DeleteCriticalSection(&m_mutex); 35 | #endif 36 | } 37 | 38 | -------------------------------------------------------------------------------- /cppguard/helper/timer.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | #include 20 | 21 | #ifdef _WIN32 22 | #include 23 | #else 24 | #include 25 | #endif 26 | 27 | namespace timer 28 | { 29 | inline uint64_t get_ms_tick_counts() 30 | { 31 | #ifdef _WIN32 32 | return GetTickCount64(); //faster then chrono::steady_clock on Windows 33 | #else 34 | return std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count(); 35 | #endif 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/tests_dyn_lib_api.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_in_cpp_file_dyn_lib.hpp" 23 | #include "test_in_c_file_dyn_lib.h" 24 | 25 | void run_c_test() 26 | { 27 | test_c_file_in_dyn_lib_thread_sanitzer_example(); 28 | } 29 | 30 | void run_cpp_test() 31 | { 32 | test_cpp_file_in_dyn_lib_thread_sanitzer_example(); 33 | } 34 | -------------------------------------------------------------------------------- /cppguard_tests_static_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.10) 19 | 20 | set(_source_list 21 | test_in_cpp_file_shared_lib.cpp 22 | test_in_cpp_file_shared_lib.hpp 23 | test_in_c_file_shared_lib.h 24 | test_in_c_file_shared_lib.c 25 | ) 26 | 27 | add_library(cppguard_tests_static_lib ${_source_list}) 28 | 29 | if (WIN32) 30 | target_link_libraries(cppguard_tests_static_lib cppguard::auto_include) 31 | endif() 32 | 33 | #target_compile_options(cppguard_tests_static_lib PRIVATE -fsanitize=thread) 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /cppguard/helper/attributes.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #if defined(_WIN32) 23 | #define TM_ATTRIBUTE_NOINLINE __declspec(noinline) 24 | #define TM_ATTRIBUTE_VISIBILITY 25 | #else 26 | #define TM_ATTRIBUTE_NOINLINE __attribute__((noinline)) 27 | #define TM_ATTRIBUTE_VISIBILITY __attribute__((visibility("default"))) 28 | #endif 29 | 30 | template 31 | constexpr std::size_t mt_countof(T const (&)[N]) noexcept 32 | { 33 | return N; 34 | } 35 | 36 | #define UNUSED(x) (void)(x); 37 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/critical_section_linux.cpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #include 19 | #include "critical_section_linux.hpp" 20 | 21 | void InitializeCriticalSection(CRITICAL_SECTION* p_critical_section) 22 | { 23 | pthread_mutexattr_t Attr; 24 | pthread_mutexattr_init(&Attr); 25 | pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE); 26 | pthread_mutex_init(p_critical_section, &Attr); 27 | 28 | } 29 | 30 | void DeleteCriticalSection(CRITICAL_SECTION* p_critical_section) 31 | { 32 | assert(p_critical_section); 33 | pthread_mutex_destroy(p_critical_section); 34 | } 35 | -------------------------------------------------------------------------------- /cppguard/fmt/format.cc: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ 2 | // 3 | // Copyright (c) 2012 - 2016, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #include "fmt/format-inl.h" 9 | 10 | FMT_BEGIN_NAMESPACE 11 | namespace detail 12 | { 13 | 14 | template FMT_API auto dragonbox::to_decimal(float x) noexcept 15 | -> dragonbox::decimal_fp; 16 | template FMT_API auto dragonbox::to_decimal(double x) noexcept 17 | -> dragonbox::decimal_fp; 18 | 19 | #ifndef FMT_STATIC_THOUSANDS_SEPARATOR 20 | template FMT_API locale_ref::locale_ref(const std::locale& loc); 21 | template FMT_API auto locale_ref::get() const->std::locale; 22 | #endif 23 | 24 | // Explicit instantiations for char. 25 | 26 | template FMT_API auto thousands_sep_impl(locale_ref) 27 | -> thousands_sep_result; 28 | template FMT_API auto decimal_point_impl(locale_ref) -> char; 29 | 30 | template FMT_API void buffer::append(const char*, const char*); 31 | 32 | template FMT_API void vformat_to(buffer&, string_view, 33 | typename vformat_args<>::type, locale_ref); 34 | 35 | // Explicit instantiations for wchar_t. 36 | 37 | template FMT_API auto thousands_sep_impl(locale_ref) 38 | -> thousands_sep_result; 39 | template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; 40 | 41 | template FMT_API void buffer::append(const wchar_t*, const wchar_t*); 42 | 43 | } // namespace detail 44 | FMT_END_NAMESPACE 45 | -------------------------------------------------------------------------------- /cppguard_tests/utils/random_numbers.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "random_numbers.hpp" 23 | #include 24 | 25 | namespace random_helper 26 | { 27 | int thread_safe_uniform_int(const int& p_min, const int& p_max) 28 | { 29 | static thread_local std::random_device rd; 30 | static thread_local std::mt19937 generator(rd()); 31 | std::uniform_int_distribution distribution(p_min, p_max); 32 | const int n=distribution(generator); 33 | return n; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cppguard/helper/cs_mutex.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #ifdef _WIN32 21 | #include 22 | #else 23 | #include 24 | #endif 25 | 26 | struct cs_mutex_t 27 | { 28 | cs_mutex_t(); 29 | ~cs_mutex_t(); 30 | 31 | void lock() noexcept 32 | { 33 | #ifdef _WIN32 34 | EnterCriticalSection(&m_mutex); 35 | #else 36 | m_mutex.lock(); 37 | #endif 38 | } 39 | 40 | void unlock() noexcept 41 | { 42 | #ifdef _WIN32 43 | LeaveCriticalSection(&m_mutex); 44 | #else 45 | m_mutex.unlock(); 46 | #endif 47 | } 48 | 49 | #ifdef _WIN32 50 | CRITICAL_SECTION m_mutex; 51 | #else 52 | std::mutex m_mutex; 53 | #endif 54 | }; 55 | -------------------------------------------------------------------------------- /cppguard_tests/critical_section_linux.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include "pthread.h" 21 | 22 | #ifdef __cplusplus 23 | extern "C" { // only need to export C interface if 24 | // used by C++ source code 25 | #endif 26 | 27 | 28 | typedef pthread_mutex_t CRITICAL_SECTION; 29 | 30 | void cppguard_initialize_critical_section(void* lpCriticalSection); 31 | void cppguard_delete_critical_section(void* lpCriticalSection); 32 | void cppguard_enter_critical_section(void* lpCriticalSection); 33 | int cppguard_tryenter_critical_section(void* lpCriticalSection); 34 | void cppguard_leave_critical_section(void* lpCriticalSection); 35 | 36 | #ifdef __cplusplus 37 | } 38 | #endif 39 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.15) 19 | 20 | project(cppguard-distribution) 21 | 22 | set(CMAKE_CXX_STANDARD 14) 23 | 24 | if (WIN32) 25 | set (CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 26 | set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) 27 | endif() 28 | 29 | include(CTest) 30 | 31 | add_subdirectory(cppguard) 32 | add_subdirectory(cppguard_tests) 33 | 34 | if (WIN32) 35 | add_subdirectory(quickstart_example) 36 | add_subdirectory(cppguard_tests_static_lib) 37 | add_subdirectory(cppguard_tests_dyn_lib) 38 | add_subdirectory(cppguard_tests_exe_used_guarded_dll) 39 | add_subdirectory(cppguard_tests_guarded_dyn_lib) 40 | endif() 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/critical_section_linux.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | 21 | #include "pthread.h" 22 | 23 | #ifdef __cplusplus 24 | extern "C" { // only need to export C interface if 25 | // used by C++ source code 26 | #endif 27 | 28 | 29 | typedef pthread_mutex_t CRITICAL_SECTION; 30 | 31 | void cppguard_enter_critical_section(void* lpCriticalSection); 32 | int cppguard_tryenter_critical_section(void* lpCriticalSection); 33 | void cppguard_leave_critical_section(void* lpCriticalSection); 34 | 35 | void InitializeCriticalSection(CRITICAL_SECTION* p_critical_section); 36 | void DeleteCriticalSection(CRITICAL_SECTION* p_critical_section); 37 | 38 | #ifdef __cplusplus 39 | } 40 | #endif 41 | -------------------------------------------------------------------------------- /cppguard_tests/utils/cs.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "cs.hpp" 23 | 24 | #ifdef __linux__ 25 | #include "cppguard.h" 26 | #include "critical_section_linux.hpp" 27 | #endif 28 | 29 | CRITICAL_SECTION* cs_maker_t::make_scoped() 30 | { 31 | CRITICAL_SECTION* cs=new CRITICAL_SECTION; 32 | InitializeCriticalSection(cs); 33 | m_cs_list.push_back(cs); 34 | 35 | return cs; 36 | } 37 | 38 | cs_maker_t::~cs_maker_t() 39 | { 40 | for(const auto& cs_p:m_cs_list) 41 | { 42 | DeleteCriticalSection(cs_p); 43 | delete cs_p; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/deadlock_monitor_api.cpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #include "cppguard.h" 19 | #include 20 | #include 21 | 22 | #include 23 | #include "options_parser.hpp" 24 | #include 25 | 26 | 27 | extern runtime_options_t g_runtime_options; 28 | 29 | extern deadlock_monitor_t* g_deadlock_monitor; 30 | 31 | int dlm_deadlock_monitor_set_options(const char* const p_options) 32 | { 33 | if (p_options==nullptr) 34 | { 35 | return false; 36 | } 37 | 38 | return parse_options(p_options, g_runtime_options); 39 | } 40 | 41 | void dlm_deadlock_monitor_increase_ignore_counter(const unsigned int p_lock_counter, const unsigned int p_unlocks_counter) 42 | { 43 | assert(g_deadlock_monitor); 44 | g_deadlock_monitor->increase_ignore_counter(p_lock_counter, p_unlocks_counter); 45 | } 46 | 47 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.10) 19 | 20 | set(source_list 21 | dl_main.cpp 22 | test_in_cpp_file_dyn_lib.hpp 23 | tests_dyn_lib_api.c 24 | test_in_c_file_dyn_lib.c 25 | test_in_c_file_dyn_lib.h 26 | test_in_cpp_file_dyn_lib.cpp 27 | ) 28 | 29 | if (WIN32) 30 | set(source_list ${source_list} 31 | tests_dyn_lib.def 32 | ) 33 | endif() 34 | 35 | if (UNIX) 36 | set(source_list ${source_list} 37 | critical_section_linux.hpp 38 | critical_section_linux.cpp 39 | ) 40 | endif() 41 | 42 | add_library(cppguard_tests_dyn_lib SHARED ${source_list}) 43 | 44 | 45 | if (WIN32) 46 | target_link_libraries(cppguard_tests_dyn_lib cppguard::cppguard cppguard::auto_include) 47 | endif() 48 | 49 | #target_compile_options(tests_dyn_lib PRIVATE -fsanitize=thread) 50 | #target_link_options(tests_dyn_lib PRIVATE -fsanitize=thread) 51 | 52 | 53 | 54 | 55 | 56 | 57 | -------------------------------------------------------------------------------- /cppguard/helper/run_time_options.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | enum class log_type_t 24 | { 25 | disabled, 26 | to_stdout, 27 | to_stderr, 28 | to_file 29 | }; 30 | 31 | struct runtime_options_t 32 | { 33 | std::string m_log_file; 34 | std::atomic_uint8_t m_log_type{static_cast(log_type_t::to_stdout)}; 35 | std::atomic_bool m_use_outputdebugstring{true}; 36 | 37 | std::atomic_bool m_halt_on_error{false}; 38 | std::atomic_bool m_override_process_exit_code_on_error{false}; 39 | std::atomic_bool m_thread_watchdog_enabled{true}; 40 | std::atomic_uint32_t m_thread_watchdog_max_duration_sec{30}; 41 | std::atomic_int m_exit_code={66}; 42 | }; 43 | 44 | struct statistics_t 45 | { 46 | std::atomic_uint64_t m_initial_critial_section_counter{0}; 47 | std::atomic_uint64_t m_delete_critial_section_counter{0}; 48 | }; 49 | -------------------------------------------------------------------------------- /cppguard_tests/utils/spinlock.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #include 19 | #include 20 | 21 | //from https://rigtorp.se/spinlock/ 22 | struct spinlock_t { 23 | std::atomic lock_{ false }; 24 | 25 | void lock() noexcept { 26 | for (;;) { 27 | // Optimistically assume the lock is free on the first try 28 | if (!lock_.exchange(true, std::memory_order_acquire)) { 29 | return; 30 | } 31 | // Wait for lock to be released without generating cache misses 32 | while (lock_.load(std::memory_order_relaxed)) { 33 | // Issue X86 PAUSE or ARM YIELD instruction to reduce contention between 34 | // hyper-threads 35 | #ifdef _WIN32 36 | _mm_pause(); 37 | #else 38 | __builtin_ia32_pause(); 39 | #endif 40 | } 41 | } 42 | } 43 | 44 | void unlock() noexcept { 45 | lock_.store(false, std::memory_order_release); 46 | } 47 | }; 48 | -------------------------------------------------------------------------------- /cppguard/synch_locks_held.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | // Return an invalid graph id that will never be assigned by GraphCycles. 27 | inline uint64_t InvalidGraphId() { 28 | return 0; 29 | } 30 | 31 | struct lock_t 32 | { 33 | void* mu{ nullptr }; // lock acquired 34 | int32_t count{ 0 }; // times acquired 35 | uint64_t id{0}; // deadlock_graph id of acquired lock 36 | }; 37 | 38 | struct SynchLocksHeld 39 | { 40 | SynchLocksHeld(): 41 | id(std::this_thread::get_id()) 42 | { 43 | } 44 | 45 | std::thread::id id; 46 | int n{ 0 }; // number of valid entries in locks[] 47 | std::array locks{}; 48 | std::atomic_uint64_t m_lock_start_time_point{0}; 49 | }; 50 | 51 | typedef std::shared_ptr SynchLocksHeldPtr; 52 | 53 | 54 | -------------------------------------------------------------------------------- /cppguard_tests/test_c_file.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | #pragma once 18 | 19 | #ifdef _WIN32 20 | #include 21 | #endif 22 | 23 | #pragma once 24 | #ifdef __cplusplus 25 | extern "C" { 26 | #endif 27 | 28 | void test_c_file_thread_sanitzer_example(); 29 | 30 | inline void test_in_header() 31 | { 32 | CRITICAL_SECTION mu1, mu2; 33 | InitializeCriticalSection(&mu1); 34 | InitializeCriticalSection(&mu2); 35 | 36 | // mu1 => mu2 37 | EnterCriticalSection(&mu1); 38 | EnterCriticalSection(&mu2); 39 | LeaveCriticalSection(&mu2); 40 | LeaveCriticalSection(&mu1); 41 | 42 | // mu2 => mu1 43 | EnterCriticalSection(&mu2); 44 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 45 | 46 | //REQUIRE(false); 47 | 48 | LeaveCriticalSection(&mu1); 49 | LeaveCriticalSection(&mu2); 50 | 51 | DeleteCriticalSection(&mu1); 52 | DeleteCriticalSection(&mu2); 53 | } 54 | 55 | #ifdef __cplusplus 56 | } 57 | #endif 58 | 59 | -------------------------------------------------------------------------------- /cppguard_tests_exe_used_guarded_dll/main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "argh.h" 23 | 24 | #include 25 | 26 | void good_test(); 27 | void bad_test(); 28 | 29 | int main(int, char* argv[]) 30 | { 31 | argh::parser cmdl(argv); 32 | 33 | bool run_good_test=false; 34 | if(cmdl[{ "-g", "--good" }]) 35 | { 36 | run_good_test=true; 37 | } 38 | 39 | bool run_bad_test=false; 40 | if(cmdl[{ "-b", "--bad" }]) 41 | { 42 | run_bad_test=true; 43 | } 44 | 45 | 46 | if(run_good_test) 47 | { 48 | std::thread t([&]() 49 | { 50 | for(size_t i=0; i<1000; i++) 51 | { 52 | good_test(); 53 | } 54 | }); 55 | 56 | t.join(); 57 | } 58 | 59 | if (run_bad_test) 60 | { 61 | bad_test(); 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | -------------------------------------------------------------------------------- /cppguard/helper/one_shot_timer.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #ifdef __linux__ 23 | #include 24 | #include 25 | #include "cs_mutex.hpp" 26 | #else 27 | #include 28 | #endif 29 | 30 | class one_shot_timer_callback_t 31 | { 32 | public: 33 | virtual ~one_shot_timer_callback_t()=default; 34 | virtual void one_shot_timer_callback()=0; 35 | }; 36 | 37 | class one_shot_timer_t 38 | { 39 | public: 40 | one_shot_timer_t(one_shot_timer_callback_t& p_one_shot_timer_callback); 41 | ~one_shot_timer_t(); 42 | void start(const uint8_t p_duration_sec); 43 | void timer_expired() const; 44 | 45 | private: 46 | one_shot_timer_callback_t& m_one_shot_timer_callback; 47 | 48 | #ifdef _WIN32 49 | HANDLE hTimer=nullptr; 50 | HANDLE hTimerQueue=nullptr; 51 | #else 52 | void loop(); 53 | cs_mutex_t m_mutex; 54 | std::atomic_bool m_shutdown{false}; 55 | std::chrono::steady_clock::time_point m_start_time_point{}; 56 | std::thread m_loop_thread; 57 | #endif 58 | }; 59 | 60 | -------------------------------------------------------------------------------- /cppguard_tests/test_c_file.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_c_file.h" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #endif 27 | 28 | #ifdef __linux__ 29 | #include "cppguard.h" 30 | #include "critical_section_linux.hpp" 31 | #endif 32 | 33 | void test_c_file_thread_sanitzer_example() 34 | { 35 | CRITICAL_SECTION mu1, mu2; 36 | InitializeCriticalSection(&mu1); 37 | InitializeCriticalSection(&mu2); 38 | 39 | // mu1 => mu2 40 | EnterCriticalSection(&mu1); 41 | EnterCriticalSection(&mu2); 42 | LeaveCriticalSection(&mu2); 43 | LeaveCriticalSection(&mu1); 44 | 45 | // mu2 => mu1 46 | EnterCriticalSection(&mu2); 47 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 48 | 49 | //REQUIRE(false); 50 | 51 | LeaveCriticalSection(&mu1); 52 | LeaveCriticalSection(&mu2); 53 | 54 | DeleteCriticalSection(&mu1); 55 | DeleteCriticalSection(&mu2); 56 | } 57 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/test_in_cpp_file_dyn_lib.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_in_cpp_file_dyn_lib.hpp" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #endif 27 | 28 | #ifdef __linux__ 29 | #include "../cppguard/include/cppguard.h" 30 | #include "critical_section_linux.hpp" 31 | #endif 32 | 33 | void test_cpp_file_in_dyn_lib_thread_sanitzer_example() 34 | { 35 | CRITICAL_SECTION mu1, mu2; 36 | InitializeCriticalSection(&mu1); 37 | InitializeCriticalSection(&mu2); 38 | 39 | // mu1 => mu2 40 | EnterCriticalSection(&mu1); 41 | EnterCriticalSection(&mu2); 42 | LeaveCriticalSection(&mu2); 43 | LeaveCriticalSection(&mu1); 44 | 45 | // mu2 => mu1 46 | EnterCriticalSection(&mu2); 47 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 48 | 49 | //REQUIRE(false); 50 | 51 | LeaveCriticalSection(&mu1); 52 | LeaveCriticalSection(&mu2); 53 | 54 | DeleteCriticalSection(&mu1); 55 | DeleteCriticalSection(&mu2); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /cppguard_tests_static_lib/test_in_c_file_shared_lib.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_in_c_file_shared_lib.h" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #endif 27 | 28 | #ifdef __linux__ 29 | #include "../cppguard/include/cppguard.h" 30 | #include "../tests/critical_section_linux.hpp" 31 | #endif 32 | 33 | void test_c_file_in_shared_lib_thread_sanitzer_example() 34 | { 35 | CRITICAL_SECTION mu1, mu2; 36 | InitializeCriticalSection(&mu1); 37 | InitializeCriticalSection(&mu2); 38 | 39 | // mu1 => mu2 40 | EnterCriticalSection(&mu1); 41 | EnterCriticalSection(&mu2); 42 | LeaveCriticalSection(&mu2); 43 | LeaveCriticalSection(&mu1); 44 | 45 | // mu2 => mu1 46 | EnterCriticalSection(&mu2); 47 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 48 | 49 | //REQUIRE(false); 50 | 51 | LeaveCriticalSection(&mu1); 52 | LeaveCriticalSection(&mu2); 53 | 54 | DeleteCriticalSection(&mu1); 55 | DeleteCriticalSection(&mu2); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /cppguard_tests_static_lib/test_in_cpp_file_shared_lib.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_in_cpp_file_shared_lib.hpp" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #endif 27 | 28 | #ifdef __linux__ 29 | #include "../cppguard/include/cppguard.h" 30 | #include "../tests/critical_section_linux.hpp" 31 | #endif 32 | 33 | void test_cpp_file_in_shared_lib_thread_sanitzer_example() 34 | { 35 | CRITICAL_SECTION mu1, mu2; 36 | InitializeCriticalSection(&mu1); 37 | InitializeCriticalSection(&mu2); 38 | 39 | // mu1 => mu2 40 | EnterCriticalSection(&mu1); 41 | EnterCriticalSection(&mu2); 42 | LeaveCriticalSection(&mu2); 43 | LeaveCriticalSection(&mu1); 44 | 45 | // mu2 => mu1 46 | EnterCriticalSection(&mu2); 47 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 48 | 49 | //REQUIRE(false); 50 | 51 | LeaveCriticalSection(&mu1); 52 | LeaveCriticalSection(&mu2); 53 | 54 | DeleteCriticalSection(&mu1); 55 | DeleteCriticalSection(&mu2); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/test_in_c_file_dyn_lib.c: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "test_in_c_file_dyn_lib.h" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #endif 27 | 28 | #ifdef __linux__ 29 | #include "../cppguard/include/cppguard.h" 30 | #include "critical_section_linux.hpp" 31 | #endif 32 | 33 | #ifdef __cplusplus 34 | extern "C" { 35 | #endif 36 | 37 | void test_c_file_in_dyn_lib_thread_sanitzer_example() 38 | { 39 | CRITICAL_SECTION mu1, mu2; 40 | InitializeCriticalSection(&mu1); 41 | InitializeCriticalSection(&mu2); 42 | 43 | // mu1 => mu2 44 | EnterCriticalSection(&mu1); 45 | EnterCriticalSection(&mu2); 46 | LeaveCriticalSection(&mu2); 47 | LeaveCriticalSection(&mu1); 48 | 49 | // mu2 => mu1 50 | EnterCriticalSection(&mu2); 51 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 52 | 53 | //REQUIRE(false); 54 | 55 | LeaveCriticalSection(&mu1); 56 | LeaveCriticalSection(&mu2); 57 | 58 | DeleteCriticalSection(&mu1); 59 | DeleteCriticalSection(&mu2); 60 | 61 | } 62 | 63 | #ifdef __cplusplus 64 | } 65 | #endif 66 | 67 | -------------------------------------------------------------------------------- /cppguard_tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | set(source_list 19 | main.cpp 20 | utils/signal.cpp 21 | utils/signal.hpp 22 | utils/cs.cpp 23 | utils/cs.hpp 24 | utils/random_numbers.cpp 25 | utils/random_numbers.hpp 26 | test_critical_section_lock_order_inversion.cpp 27 | test_critical_section_lock_order_ok_or_disabled.cpp 28 | test_thread_watchdog_bad.cpp 29 | test_thread_watchdog_good.cpp 30 | ctests.cmake 31 | ) 32 | 33 | if (WIN32) 34 | set(source_list ${source_list} 35 | test_runtime_options.cpp 36 | test_std_mutex_good.cpp 37 | test_std_mutex_bad.cpp 38 | test_std_recursive_mutex_good.cpp 39 | test_std_recursive_mutex_bad.cpp 40 | test_c_file.c 41 | test_c_file.h 42 | ) 43 | endif() 44 | 45 | 46 | add_executable(cppguard_tests ${source_list} ) 47 | target_include_directories(cppguard_tests PRIVATE ${CMAKE_CURRENT_LIST_DIR} utils) 48 | target_link_libraries(cppguard_tests cppguard::cppguard cppguard::auto_include) 49 | 50 | 51 | if (WIN32) 52 | target_link_libraries(cppguard_tests cppguard_tests_static_lib) 53 | target_link_libraries(cppguard_tests cppguard_tests_dyn_lib) 54 | endif() 55 | 56 | 57 | include(ctests.cmake) 58 | 59 | #target_compile_options(cppguard_tests PRIVATE -fsanitize=thread) 60 | #target_link_options(cppguard_tests PRIVATE -fsanitize=thread) 61 | -------------------------------------------------------------------------------- /cppguard_tests/utils/signal.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "signal.hpp" 23 | #include 24 | 25 | namespace utils 26 | { 27 | std::cv_status event_t::wait_with_timeout(const int& p_milliseconds) 28 | { 29 | std::unique_lock lock(m_signal_mutex); 30 | const bool is_notified=m_signal_condition.wait_for(lock, std::chrono::milliseconds(p_milliseconds), [=] 31 | { 32 | return m_notified; 33 | }); 34 | 35 | if(!is_notified) 36 | { 37 | return std::cv_status::timeout; 38 | } 39 | 40 | assert(m_notified); 41 | m_notified=false; 42 | return std::cv_status::no_timeout; 43 | } 44 | 45 | std::cv_status event_t::wait() 46 | { 47 | std::unique_lock lock(m_signal_mutex); 48 | m_signal_condition.wait(lock, [=] 49 | { 50 | return m_notified; 51 | }); 52 | 53 | assert(m_notified); 54 | m_notified=false; 55 | return std::cv_status::no_timeout; 56 | } 57 | 58 | void event_t::notify_one() 59 | { 60 | {//scope 61 | std::unique_lock lock(m_signal_mutex); 62 | m_notified=true; 63 | } 64 | 65 | m_signal_condition.notify_one(); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /cppguard/thread_watchdog/thread_watchdog.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | 23 | #include "cs_mutex.hpp" 24 | #include "one_shot_timer.hpp" 25 | #include "run_time_options.hpp" 26 | #include "synch_locks_held.hpp" 27 | 28 | struct SynchLocksHeld; 29 | class deadlock_monitor_t; 30 | 31 | struct thread_info_t 32 | { 33 | thread_info_t(const SynchLocksHeldPtr& p_sync_ptr, const std::thread::id& p_thread_id) : 34 | m_sync_ptr(p_sync_ptr), 35 | m_thread_id(p_thread_id) 36 | { 37 | } 38 | 39 | SynchLocksHeldPtr m_sync_ptr; 40 | std::thread::id m_thread_id; 41 | bool m_already_logged{false}; 42 | }; 43 | 44 | class thread_watchdog_t : public one_shot_timer_callback_t 45 | { 46 | public: 47 | thread_watchdog_t(deadlock_monitor_t& p_deadlock_monitor, runtime_options_t& p_runtime_options); 48 | void add_thread(const std::thread::id& p_thread_id, SynchLocksHeldPtr& p_snch); 49 | void remove_thread(const std::thread::id& p_thread_id); 50 | void one_shot_timer_callback() override; 51 | 52 | private: 53 | deadlock_monitor_t& m_deadlock_monitor; 54 | runtime_options_t& m_runtime_options; 55 | cs_mutex_t m_thread_list_mutex; 56 | std::vector m_threads; 57 | one_shot_timer_t m_one_shot_timer; 58 | bool m_debugger_present; 59 | }; -------------------------------------------------------------------------------- /quickstart_example/main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //************************************************************* 6 | //Quickstart example: open parent folder of this file in Visual Studio via 'Open Local Folder' 7 | //and select 'Quickstart_example' as startup item and run it 8 | //************************************************************* 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | const int NUM_PHILOSOPHERS=5; 15 | CRITICAL_SECTION forks[NUM_PHILOSOPHERS]; 16 | 17 | void philosopher_thread_function(int id, CRITICAL_SECTION& left_fork, CRITICAL_SECTION& right_fork) 18 | { 19 | while(true) 20 | { 21 | EnterCriticalSection(&left_fork); 22 | std::cout<<"Philosopher "< 24 | 25 | #include 26 | #include 27 | #include "attributes.hpp" 28 | #include "cppguard.h" 29 | #include "log_helper.hpp" 30 | #include "options_parser.hpp" 31 | #include "fmt/format.h" 32 | #include "fmt/std.h" 33 | #include "log.hpp" 34 | 35 | extern runtime_options_t g_runtime_options; 36 | extern deadlock_monitor_t* g_deadlock_monitor; 37 | 38 | TM_ATTRIBUTE_VISIBILITY int cppguard_set_options(const char* p_options, char* p_error_text, int p_error_text_size) 39 | { 40 | if(p_error_text==nullptr||p_error_text_size<50) 41 | { 42 | assert(false); 43 | return 0; 44 | } 45 | 46 | try 47 | { 48 | parse_options(p_options, g_runtime_options); 49 | 50 | if(g_runtime_options.m_log_type.load()!=static_cast(log_type_t::disabled)) 51 | { 52 | auto out=fmt::memory_buffer(); 53 | log_helper::create_options_log_text(g_runtime_options, out); 54 | logger::log_msg(out, g_runtime_options); 55 | } 56 | 57 | return 1; 58 | } catch(std::invalid_argument& p_ex) 59 | { 60 | memset(p_error_text, 0, p_error_text_size); 61 | fmt::format_to_n(p_error_text, p_error_text_size-1, "Invalid argument: '{}'", p_ex.what()); 62 | } 63 | 64 | return 0; 65 | } 66 | 67 | TM_ATTRIBUTE_VISIBILITY void cppguard_increase_ignore_counter(const unsigned int p_lock_counter, const unsigned int p_unlocks_counter) 68 | { 69 | deadlock_monitor_t::increase_ignore_counter(p_lock_counter, p_unlocks_counter); 70 | } 71 | 72 | -------------------------------------------------------------------------------- /cppguard/deadlock_monitor.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "graphcycles.h" 25 | #include "cs_mutex.hpp" 26 | #include "thread_watchdog.hpp" 27 | #include "run_time_options.hpp" 28 | #include "fmt/format.h" 29 | 30 | class deadlock_monitor_t 31 | { 32 | public: 33 | deadlock_monitor_t(runtime_options_t& p_runtime_options); 34 | static void increase_ignore_counter(unsigned int p_lock_counter, unsigned int p_unlocks_counter); 35 | void dlc_deadlock_check_before_try_lock(void* mu, void** p_hold_locks, uint64_t& p_id); 36 | void dlc_deadlock_check_before_lock(void* mu, void** p_hold_locks, uint64_t& p_graphid_handle); 37 | void dlc_deadlock_check_in_lock(void* mu, uint64_t p_id, void* p_hold_locks); 38 | void dlc_deadlock_check_after_lock(void* mu); 39 | void dlc_deadlock_delete_lock(void* mu); 40 | void cppguard_cnd_wait(const void* mu); 41 | void process_watchdog_error(const SynchLocksHeld& p_sync_locks, const uint64_t& p_hold_duration_sec); 42 | 43 | bool potential_deadlocks_found() const 44 | { 45 | return m_potential_deadlocks_found.load(); 46 | } 47 | 48 | bool other_errors_found() const 49 | { 50 | return m_other_errors_found.load(); 51 | } 52 | 53 | private: 54 | void GetGraphId(void* mu, uint64_t& p_id) const 55 | { 56 | assert(mu); 57 | m_deadlock_graph.GetId(mu, p_id); 58 | } 59 | 60 | SynchLocksHeld& Synch_GetAllLocks(const bool p_monitor_thread); 61 | 62 | runtime_options_t& m_runtime_options; 63 | GraphCycles m_deadlock_graph; 64 | cs_mutex_t m_deadlock_graph_mutex; 65 | thread_watchdog_t m_thread_watchdog; 66 | std::atomic_bool m_potential_deadlocks_found{false}; 67 | std::atomic_bool m_other_errors_found{false}; 68 | }; 69 | -------------------------------------------------------------------------------- /cppguard/output/log_helper.hpp: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #include 21 | 22 | #include "attributes.hpp" 23 | #include "fmt/format.h" 24 | 25 | class GraphCycles; 26 | struct SynchLocksHeld; 27 | 28 | struct lock_t; 29 | struct statistics_t; 30 | struct runtime_options_t; 31 | 32 | namespace log_helper 33 | { 34 | void create_options_log_text(const runtime_options_t& p_runtime_options, fmt::memory_buffer& p_out_buffer); 35 | TM_ATTRIBUTE_NOINLINE void build_deadlock_circle_log_text(const GraphCycles& p_deadlock_graph, void* mu, const uint64_t& mu_id, const uint64_t& other_node_id, fmt::memory_buffer& p_log_string); 36 | void create_summary(statistics_t& p_statistics, const bool& p_found_deadlocks, const bool& p_other_errors_found, fmt::memory_buffer& p_summary); 37 | TM_ATTRIBUTE_NOINLINE void build_delete_locked_lock_log_text(const std::thread::id& p_thread_id, const void* mu, fmt::memory_buffer& p_log_string); 38 | TM_ATTRIBUTE_NOINLINE void build_release_locked_deleted_by_other_thread_log_text(const std::thread::id& p_thread_id, const void* mu, fmt::memory_buffer& p_log_string); 39 | TM_ATTRIBUTE_NOINLINE void build_release_lock_which_thread_does_hold_log_text(const std::thread::id& p_thread_id, const void* mu, fmt::memory_buffer& p_log_string); 40 | void build_watchdog_error(const std::thread::id& p_thread_id, const bool& p_in_lock, const uint64_t& p_hold_duration_sec, fmt::memory_buffer& p_log_string); 41 | void build_watchdog_callstack(GraphCycles& p_deadlock_graph, const lock_t& p_lock, fmt::memory_buffer& p_log_string); 42 | void build_max_locks_per_thread_reached_text(const int& p_lock_count, const std::thread::id& p_thread_id, const void* mu, fmt::memory_buffer& p_log_string); 43 | void process_error(fmt::memory_buffer& p_log_text, const runtime_options_t& p_runtime_options, bool p_is_deadlock); 44 | } -------------------------------------------------------------------------------- /cppguard/output/log.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "log.hpp" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "run_time_options.hpp" 31 | #include 32 | #include 33 | 34 | namespace logger 35 | { 36 | void log_msg(fmt::memory_buffer& p_log_buffer, const runtime_options_t& p_runtime_options) 37 | { 38 | constexpr std::array null_byte{}; 39 | p_log_buffer.append(null_byte); 40 | 41 | static spinlock_t log_mutex; 42 | std::lock_guard lock(log_mutex); 43 | 44 | bool is_first_call=false; 45 | static bool first_call=true; 46 | if(first_call) 47 | { 48 | first_call=false; 49 | is_first_call=true; 50 | } 51 | 52 | #ifdef _WIN32 53 | if(p_runtime_options.m_use_outputdebugstring) 54 | { 55 | OutputDebugString(p_log_buffer.data()); 56 | } 57 | #endif 58 | 59 | switch(static_cast(p_runtime_options.m_log_type.load())) 60 | { 61 | case log_type_t::disabled: break; 62 | case log_type_t::to_stdout: std::cout<(p_log_buffer.size())); 76 | 77 | file.close(); 78 | 79 | } break; 80 | 81 | } 82 | 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /cppguard/include/cppguard.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | #pragma once 19 | 20 | #define CPPGUARD_ENABLED 21 | 22 | #if defined (__cplusplus) 23 | extern "C" { 24 | #endif 25 | 26 | //**public functions** 27 | 28 | /** 29 | * \brief Set options during runtime 30 | * \param p_options text containing options, key:value pairs seperated by comma 31 | * \param p_error_text: error text in case of error 32 | * \param p_error_text_size: max size of error text 33 | * \return 34 | */ 35 | int cppguard_set_options(const char* p_options, char* p_error_text, int p_error_text_size); 36 | 37 | /** 38 | * \brief: increase ignore counter for this thread 39 | * \param p_lock_counter: lock counter is increased by this value 40 | * \param p_unlocks_counter: unlock counter is increased by this value 41 | */ 42 | void cppguard_increase_ignore_counter(const unsigned int p_lock_counter, const unsigned int p_unlocks_counter); 43 | 44 | //**internal functions** 45 | 46 | //CRITICAL_SECTION 47 | #define InitializeCriticalSection(x) cppguard_initialize_critical_section(x) 48 | #define EnterCriticalSection(x) cppguard_enter_critical_section(x) 49 | #define TryEnterCriticalSection(x) cppguard_tryenter_critical_section(x) 50 | #define LeaveCriticalSection(x) cppguard_leave_critical_section(x) 51 | #define DeleteCriticalSection(x) cppguard_delete_critical_section(x) 52 | 53 | //std::mutex 54 | #if defined (__cplusplus) 55 | #if (__cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)) 56 | #define _Mtx_init_in_situ(x,y) cppguard_init_in_situ(x,y) 57 | #define _Mtx_destroy_in_situ(x) cppguard_mtx_destroy_in_situ(x) 58 | 59 | #define _Mtx_lock(x) cppguard_mtx_lock(x) 60 | #define _Mtx_unlock(x) cppguard_mtx_unlock(x) 61 | #define _Mtx_trylock(x) cppguard_mtx_trylock(x) 62 | 63 | #define _Cnd_wait(x, y) cppguard_cnd_wait(x,y) 64 | #define _Cnd_timedwait(x, y, z) cppguard_cnd_timedwait(x,y,z) 65 | #endif 66 | #endif 67 | 68 | 69 | #if defined (__cplusplus) 70 | } 71 | #endif 72 | -------------------------------------------------------------------------------- /cppguard/cppguard_rc.in: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | //#include "resource.h" 4 | 5 | #define APSTUDIO_READONLY_SYMBOLS 6 | ///////////////////////////////////////////////////////////////////////////// 7 | // 8 | // Generated from the TEXTINCLUDE 2 resource. 9 | // 10 | #include "winres.h" 11 | 12 | ///////////////////////////////////////////////////////////////////////////// 13 | #undef APSTUDIO_READONLY_SYMBOLS 14 | 15 | ///////////////////////////////////////////////////////////////////////////// 16 | // English (United States) resources 17 | 18 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 19 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 20 | 21 | #ifdef APSTUDIO_INVOKED 22 | ///////////////////////////////////////////////////////////////////////////// 23 | // 24 | // TEXTINCLUDE 25 | // 26 | 27 | 1 TEXTINCLUDE 28 | BEGIN 29 | "resource.h\0" 30 | END 31 | 32 | 2 TEXTINCLUDE 33 | BEGIN 34 | "#include ""winres.h""\r\n" 35 | "\0" 36 | END 37 | 38 | 3 TEXTINCLUDE 39 | BEGIN 40 | "\r\n" 41 | "\0" 42 | END 43 | 44 | #endif // APSTUDIO_INVOKED 45 | 46 | 47 | #define VER_FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_BUILD@,@VERSION_REVISION@ 48 | #define VER_FILEVERSION_STR "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_BUILD@.@VERSION_REVISION@\0" 49 | 50 | #define VER_PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_BUILD@,@VERSION_REVISION@ 51 | #define VER_PRODUCTVERSION_STR "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_BUILD@.@VERSION_REVISION@\0" 52 | 53 | 54 | ///////////////////////////////////////////////////////////////////////////// 55 | // 56 | // Version 57 | // 58 | 59 | VS_VERSION_INFO VERSIONINFO 60 | FILEVERSION VER_FILEVERSION 61 | PRODUCTVERSION VER_PRODUCTVERSION 62 | FILEFLAGSMASK 0x3fL 63 | #ifdef _DEBUG 64 | FILEFLAGS 0x1L 65 | #else 66 | FILEFLAGS 0x0L 67 | #endif 68 | FILEOS 0x40004L 69 | FILETYPE 0x2L 70 | FILESUBTYPE 0x0L 71 | BEGIN 72 | BLOCK "StringFileInfo" 73 | BEGIN 74 | BLOCK "0c0004b0" 75 | BEGIN 76 | VALUE "FileDescription", "CppGuard" 77 | VALUE "FileVersion", VER_FILEVERSION_STR 78 | VALUE "InternalName", "cppguard.dll" 79 | VALUE "LegalCopyright", "Copyright (C) 2023 Jochen Baier, email@jochen-baier.de" 80 | VALUE "OriginalFilename", "cppguard.dll" 81 | VALUE "ProductName", "CppGuard" 82 | VALUE "ProductVersion", VER_PRODUCTVERSION_STR 83 | END 84 | END 85 | BLOCK "VarFileInfo" 86 | BEGIN 87 | VALUE "Translation", 0xc00, 1200 88 | END 89 | END 90 | 91 | #endif // English (United States) resources 92 | ///////////////////////////////////////////////////////////////////////////// 93 | 94 | 95 | 96 | #ifndef APSTUDIO_INVOKED 97 | ///////////////////////////////////////////////////////////////////////////// 98 | // 99 | // Generated from the TEXTINCLUDE 3 resource. 100 | // 101 | 102 | 103 | ///////////////////////////////////////////////////////////////////////////// 104 | #endif // not APSTUDIO_INVOKED 105 | 106 | -------------------------------------------------------------------------------- /cppguard_tests_dyn_lib/dl_main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include 23 | 24 | #ifdef _WIN32 25 | #include 26 | #include 27 | 28 | struct loader_t 29 | { 30 | loader_t() 31 | { 32 | if(std::getenv("CPPGUARD_STATIC_TEST_IN_DLL_BEFORE_MAIN")==nullptr) 33 | { 34 | return; 35 | } 36 | 37 | 38 | CRITICAL_SECTION mu1; 39 | CRITICAL_SECTION mu2; 40 | 41 | InitializeCriticalSection(&mu1); 42 | InitializeCriticalSection(&mu2); 43 | 44 | // mu1 => mu2 45 | EnterCriticalSection(&mu1); 46 | EnterCriticalSection(&mu2); 47 | LeaveCriticalSection(&mu2); 48 | LeaveCriticalSection(&mu1); 49 | 50 | // mu2 => mu1 51 | EnterCriticalSection(&mu2); 52 | EnterCriticalSection(&mu1); // <<<<<<< OOPS 53 | 54 | //REQUIRE(false); 55 | 56 | LeaveCriticalSection(&mu1); 57 | LeaveCriticalSection(&mu2); 58 | 59 | DeleteCriticalSection(&mu1); 60 | DeleteCriticalSection(&mu2); 61 | } 62 | 63 | }; 64 | 65 | loader_t loader; 66 | 67 | struct static_struct_cs_counter_t 68 | { 69 | static_struct_cs_counter_t() 70 | { 71 | critical_sections.resize(1000); 72 | 73 | for(auto& cs:critical_sections) 74 | { 75 | InitializeCriticalSection(&cs); 76 | } 77 | } 78 | 79 | ~static_struct_cs_counter_t() 80 | { 81 | for(auto& cs:critical_sections) 82 | { 83 | DeleteCriticalSection(&cs); 84 | } 85 | } 86 | 87 | std::vector critical_sections; 88 | }; 89 | 90 | static_struct_cs_counter_t static_struct_cs_counter; 91 | 92 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 93 | { 94 | switch(ul_reason_for_call) 95 | { 96 | case DLL_PROCESS_ATTACH: 97 | { 98 | 99 | } break; 100 | 101 | case DLL_PROCESS_DETACH: 102 | { 103 | return TRUE; 104 | 105 | } break; 106 | default: break; 107 | } 108 | 109 | return TRUE; 110 | } 111 | #endif 112 | -------------------------------------------------------------------------------- /cppguard_tests_guarded_dyn_lib/dl_main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include 23 | #include 24 | 25 | #ifdef _WIN32 26 | #include 27 | #include 28 | 29 | const size_t NUM_CS=10; 30 | CRITICAL_SECTION critical_sections_good[NUM_CS]{}; 31 | 32 | std::mutex mutex_good; 33 | 34 | CRITICAL_SECTION mu1_bad; 35 | CRITICAL_SECTION mu2_bad; 36 | 37 | void good_test() 38 | { 39 | for(size_t i=0; i mu2_bad 59 | EnterCriticalSection(&mu1_bad); 60 | EnterCriticalSection(&mu2_bad); 61 | LeaveCriticalSection(&mu2_bad); 62 | LeaveCriticalSection(&mu1_bad); 63 | 64 | // mu2_bad => mu1_bad 65 | EnterCriticalSection(&mu2_bad); 66 | EnterCriticalSection(&mu1_bad); // <<<<<<< OOPS 67 | 68 | //REQUIRE(false); 69 | 70 | LeaveCriticalSection(&mu1_bad); 71 | LeaveCriticalSection(&mu2_bad); 72 | } 73 | 74 | 75 | BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) 76 | { 77 | switch(ul_reason_for_call) 78 | { 79 | case DLL_PROCESS_ATTACH: 80 | { 81 | DWORD id=GetCurrentThreadId(); 82 | int brk=1; 83 | 84 | for(size_t i=0; i 23 | #include 24 | #include "cppguard.h" 25 | 26 | #define DOCTEST_CONFIG_IMPLEMENT 27 | #include "doctest.h" 28 | 29 | #include 30 | 31 | void test_ok_cs_100t_10cs_bulk_thread_function2(std::vector& p_critical_sections, std::atomic_uint32_t& p_counter) 32 | { 33 | for(size_t i=0; i<10; ++i) 34 | { 35 | for(auto& cs:p_critical_sections) 36 | { 37 | EnterCriticalSection(&cs); 38 | } 39 | 40 | p_counter++; 41 | 42 | for(auto& cs:p_critical_sections) 43 | { 44 | LeaveCriticalSection(&cs); 45 | } 46 | } 47 | } 48 | 49 | struct static_test_before_main_deadlock_t 50 | { 51 | static_test_before_main_deadlock_t() 52 | { 53 | if(std::getenv("CPPGUARD_STATIC_TEST_BEFORE_MAIN")==nullptr) 54 | { 55 | return; 56 | } 57 | 58 | cs_maker_t cs_maker; 59 | 60 | CRITICAL_SECTION* mu1=cs_maker.make_scoped(); 61 | CRITICAL_SECTION* mu2=cs_maker.make_scoped(); 62 | 63 | // mu1 => mu2 64 | EnterCriticalSection(mu1); 65 | EnterCriticalSection(mu2); 66 | LeaveCriticalSection(mu2); 67 | LeaveCriticalSection(mu1); 68 | 69 | // mu2 => mu1 70 | EnterCriticalSection(mu2); 71 | EnterCriticalSection(mu1); // <<<<<<< OOPS 72 | 73 | //REQUIRE(false); 74 | 75 | LeaveCriticalSection(mu1); 76 | LeaveCriticalSection(mu2); 77 | } 78 | 79 | ~static_test_before_main_deadlock_t() 80 | { 81 | } 82 | 83 | }; 84 | 85 | static static_test_before_main_deadlock_t static_test; 86 | 87 | int main(int argc, char* argv[]) 88 | { 89 | doctest::Context context; 90 | context.applyCommandLine(argc, argv); 91 | 92 | if(std::getenv("CPPGUARD")==nullptr) 93 | { 94 | char error_text[1024]{}; 95 | const int res=cppguard_set_options("thread_watchdog_max_duration_sec:5,halt_on_error:1", error_text, _countof(error_text)); 96 | assert(res==1); 97 | } 98 | 99 | //context.addFilter("test-case", "*_exit_0"); 100 | 101 | const int res_run=context.run(); 102 | 103 | if(context.shouldExit()) 104 | { 105 | return res_run; 106 | } 107 | 108 | return res_run; 109 | } -------------------------------------------------------------------------------- /cppguard/api_monitors/mutex_monitor.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "deadlock_monitor.hpp" 23 | 24 | #include 25 | #include 26 | 27 | extern deadlock_monitor_t* g_deadlock_monitor; 28 | extern statistics_t g_statistics; 29 | 30 | extern "C" int __cdecl cppguard_mtx_lock(_Mtx_t t) 31 | { 32 | void* hold_locks=nullptr; 33 | assert(g_deadlock_monitor); 34 | 35 | uint64_t id=0; 36 | g_deadlock_monitor->dlc_deadlock_check_before_lock(t, &hold_locks, id); 37 | if(id==0) 38 | { 39 | return static_cast(_Mtx_lock(t)); 40 | } 41 | 42 | const int res=static_cast(_Mtx_lock(t)); 43 | g_deadlock_monitor->dlc_deadlock_check_in_lock(t, id, hold_locks); 44 | 45 | return res; 46 | } 47 | 48 | extern "C" int __cdecl cppguard_mtx_unlock(_Mtx_t t) 49 | { 50 | g_deadlock_monitor->dlc_deadlock_check_after_lock(t); 51 | return static_cast(_Mtx_unlock(t)); 52 | } 53 | 54 | extern "C" int __cdecl cppguard_mtx_trylock(_Mtx_t t) 55 | { 56 | void* hold_locks=nullptr; 57 | uint64_t id=0; 58 | g_deadlock_monitor->dlc_deadlock_check_before_try_lock(t, &hold_locks, id); 59 | if(id==0) 60 | { 61 | assert(false); 62 | return static_cast(_Mtx_trylock(t)); 63 | } 64 | 65 | const int res=static_cast(_Mtx_trylock(t)); 66 | if(res!=0) 67 | { 68 | return res; 69 | } 70 | 71 | g_deadlock_monitor->dlc_deadlock_check_in_lock(t, id, hold_locks); 72 | return res; 73 | } 74 | 75 | extern "C" void __cdecl cppguard_init_in_situ(_Mtx_t t, int i) 76 | { 77 | _Mtx_init_in_situ(t, i); 78 | } 79 | 80 | extern "C" void __cdecl cppguard_mtx_destroy_in_situ(_Mtx_t t) 81 | { 82 | g_deadlock_monitor->dlc_deadlock_delete_lock(t); 83 | _Mtx_destroy_in_situ(t); 84 | } 85 | 86 | extern "C" int __cdecl cppguard_cnd_wait(_Cnd_t c, _Mtx_t t) 87 | { 88 | g_deadlock_monitor->cppguard_cnd_wait(t); 89 | return static_cast(_Cnd_wait(c, t)); 90 | } 91 | 92 | #if _MSC_VER>=1938 //VS 2022, v143 93 | extern "C" int __cdecl cppguard_cnd_timedwait(_Cnd_t c, _Mtx_t t, const _timespec64*d) 94 | #else 95 | extern "C" int __cdecl cppguard_cnd_timedwait(_Cnd_t c, _Mtx_t t, const xtime*d) 96 | #endif 97 | { 98 | g_deadlock_monitor->cppguard_cnd_wait(t); 99 | return static_cast(_Cnd_timedwait(c, t, d)); 100 | } 101 | -------------------------------------------------------------------------------- /cppguard/helper/low_level_alloc.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | // A simple thread-safe memory allocator that does not depend on 19 | // mutexes or thread-specific data. It is intended to be used 20 | // sparingly, and only when malloc() would introduce an unwanted 21 | // dependency, such as inside the heap-checker, or the Mutex 22 | // implementation. 23 | 24 | // IWYU pragma: private, include "base/low_level_alloc.h" 25 | 26 | #pragma once 27 | 28 | #include 29 | #include 30 | 31 | class LowLevelAlloc 32 | { 33 | public: 34 | struct Arena; // an arena from which memory may be allocated 35 | 36 | // Returns a pointer to a block of at least "request" bytes 37 | // that have been newly allocated from the specific arena. 38 | // for Alloc() call the DefaultArena() is used. 39 | // Returns 0 if passed request==0. 40 | // Does not return 0 under other circumstances; it crashes if memory 41 | // is not available. 42 | static void* AllocWithArena(size_t request, Arena* arena); 43 | 44 | // Deallocates a region of memory that was previously allocated with 45 | // Alloc(). Does nothing if passed 0. "s" must be either 0, 46 | // or must have been returned from a call to Alloc() and not yet passed to 47 | // Free() since that call to Alloc(). The space is returned to the arena 48 | // from which it was allocated. 49 | static void Free(void* s); 50 | 51 | // ABSL_ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free 52 | // are to put all callers of MallocHook::Invoke* in this module 53 | // into special section, 54 | // so that MallocHook::GetCallerStackTrace can function accurately. 55 | 56 | // Create a new arena. 57 | // The root metadata for the new arena is allocated in the 58 | // meta_data_arena; the DefaultArena() can be passed for meta_data_arena. 59 | // These values may be ored into flags: 60 | enum 61 | { 62 | // Report calls to Alloc() and Free() via the MallocHook interface. 63 | // Set in the DefaultArena. 64 | kCallMallocHook=0x0001, 65 | }; 66 | // Construct a new arena. The allocation of the underlying metadata honors 67 | // the provided flags. For example, the call NewArena(kAsyncSignalSafe) 68 | // is itself async-signal-safe, as well as generatating an arena that provides 69 | // async-signal-safe Alloc/Free. 70 | static Arena* NewArena(uint32_t flags); 71 | 72 | // Destroys an arena allocated by NewArena and returns true, 73 | // provided no allocated blocks remain in the arena. 74 | // If allocated blocks remain in the arena, does nothing and 75 | // returns false. 76 | // It is illegal to attempt to destroy the DefaultArena(). 77 | static bool DeleteArena(Arena* arena); 78 | 79 | LowLevelAlloc()=delete; // no instances 80 | }; 81 | 82 | -------------------------------------------------------------------------------- /cppguard/thread_history/thread_history_helper.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "thread_history_helper.hpp" 23 | 24 | #include 25 | #include 26 | 27 | #include "synch_locks_held.hpp" 28 | #include "log_helper.hpp" 29 | #include "fmt/core.h" 30 | #include "fmt/std.h" 31 | 32 | namespace thread_history_helper 33 | { 34 | bool update_lock_history_after_lock(const void* mu, const uint64_t& id, SynchLocksHeld& held_locks, const runtime_options_t& p_runtime_options) 35 | { 36 | const int n=held_locks.n; 37 | int i=0; 38 | while(i!=n&&held_locks.locks[i].id!=id) 39 | { 40 | i++; 41 | } 42 | 43 | if(i==n) 44 | { 45 | // The deadlock id may have been reassigned after ForgetDeadlockInfo, 46 | // but in that case mu should still be present. 47 | i=0; 48 | while(i!=n&&held_locks.locks[i].mu!=mu) 49 | { 50 | i++; 51 | } 52 | if(i==n) 53 | { 54 | // mu missing means releasing unheld lock 55 | auto log_text=fmt::memory_buffer(); 56 | log_helper::build_release_lock_which_thread_does_hold_log_text(std::this_thread::get_id(), mu, log_text); 57 | log_helper::process_error(log_text, p_runtime_options, false); 58 | return false; 59 | } 60 | 61 | auto log_text=fmt::memory_buffer(); 62 | log_helper::build_release_locked_deleted_by_other_thread_log_text(std::this_thread::get_id(), mu, log_text); 63 | log_helper::process_error(log_text, p_runtime_options, false); 64 | return false; 65 | } 66 | 67 | if(held_locks.locks[i].count==1) { 68 | held_locks.n=n-1; 69 | held_locks.locks[i]=held_locks.locks[n-1]; 70 | held_locks.locks[n-1].id=InvalidGraphId(); 71 | held_locks.locks[n-1].mu=nullptr; 72 | held_locks.locks[n-1].count=0; 73 | } else { 74 | assert(held_locks.locks[i].count>0); 75 | held_locks.locks[i].count--; 76 | } 77 | 78 | return true; 79 | } 80 | 81 | void update_lock_history_in_lock(void* mu, const uint64_t& id, SynchLocksHeld& held_locks) 82 | { 83 | assert(mu); 84 | 85 | int n=held_locks.n; 86 | int i=0; 87 | while(i!=n&&held_locks.locks[i].id!=id) 88 | { 89 | i++; 90 | } 91 | 92 | if(i==n) 93 | { 94 | if(n==static_cast(held_locks.locks.size())) 95 | { 96 | assert(false); 97 | return; 98 | } 99 | 100 | // we have room for lock 101 | held_locks.locks[i].mu=mu; 102 | held_locks.locks[i].count=1; 103 | held_locks.locks[i].id=id; 104 | held_locks.n=n+1; 105 | 106 | } else 107 | { 108 | assert(held_locks.locks[i].count>0); 109 | held_locks.locks[i].count++; 110 | } 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /cppguard_tests/test_std_recursive_mutex_good.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "doctest.h" 23 | 24 | #include 25 | #include "utils/signal.hpp" 26 | 27 | //FIXME some variables could be optimized away in release? 28 | 29 | TEST_CASE("test_std_recursive_mutex_t1_a_exit_0") 30 | { 31 | std::recursive_mutex ma; 32 | ma.lock(); 33 | ma.unlock(); 34 | } 35 | 36 | TEST_CASE("test_std_recursive_mutex_t1_a_b_exit_0") 37 | { 38 | std::recursive_mutex ma; 39 | std::recursive_mutex mb; 40 | 41 | ma.lock(); 42 | mb.lock(); 43 | 44 | mb.unlock(); 45 | ma.unlock(); 46 | } 47 | 48 | TEST_CASE("test_std_mutex_t1_a_lock_recurse_good_exit_0") 49 | { 50 | std::recursive_mutex ma; 51 | ma.lock(); 52 | ma.lock(); 53 | ma.unlock(); 54 | ma.unlock(); 55 | } 56 | 57 | TEST_CASE("test_lock_guard_std_recursive_mutex_t1_a_exit_0") 58 | { 59 | std::recursive_mutex ma; 60 | 61 | { 62 | const std::lock_guard lock(ma); 63 | 64 | { 65 | const bool got_lock=ma.try_lock(); 66 | REQUIRE(got_lock); 67 | } 68 | 69 | } 70 | 71 | ma.unlock(); 72 | } 73 | 74 | TEST_CASE("test_std_lock_guard_std_recursive_mutex_t1_a_b_exit_0") 75 | { 76 | std::recursive_mutex ma; 77 | std::recursive_mutex mb; 78 | 79 | { 80 | const std::lock_guard < std::recursive_mutex> lock_a(ma); 81 | 82 | { 83 | const bool got_lock=ma.try_lock(); 84 | REQUIRE(got_lock); 85 | } 86 | 87 | const std::lock_guard < std::recursive_mutex> lock_b(mb); 88 | 89 | { 90 | const bool got_lock=mb.try_lock(); 91 | REQUIRE(got_lock); 92 | } 93 | } 94 | 95 | ma.unlock(); 96 | mb.unlock(); 97 | 98 | } 99 | 100 | TEST_CASE("test_lock_guard_std_mutex_t1_a_lock_recurse_exit_0") 101 | { 102 | std::recursive_mutex ma; 103 | { 104 | const std::lock_guard < std::recursive_mutex> lock_a(ma); 105 | const std::lock_guard < std::recursive_mutex> lock_b(ma); 106 | } 107 | 108 | } 109 | 110 | TEST_CASE("test_std_recursive_mutex_delete_without_use_exit_0") 111 | { 112 | { 113 | std::recursive_mutex m; 114 | } 115 | } 116 | 117 | TEST_CASE("test_std_recursive_mutex_delete_with_use_exit_0") 118 | { 119 | { 120 | std::recursive_mutex m; 121 | m.lock(); 122 | m.unlock(); 123 | } 124 | } 125 | 126 | TEST_CASE("test_std_recursive_mutex_mass_delete_exit_0") 127 | { 128 | for(size_t i=0; i<10000; ++i) 129 | { 130 | auto array_of_locks=std::make_unique(1000); 131 | 132 | for(size_t j=0; j<1000; j++) 133 | { 134 | array_of_locks[j].lock(); 135 | array_of_locks[j].unlock(); 136 | } 137 | 138 | } 139 | } 140 | 141 | -------------------------------------------------------------------------------- /cppguard/helper/one_shot_timer.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | 23 | #include "one_shot_timer.hpp" 24 | #include 25 | #include 26 | 27 | #include "attributes.hpp" 28 | 29 | class one_shot_timer_t; 30 | 31 | #ifdef _WIN32 32 | VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired) 33 | { 34 | UNUSED(TimerOrWaitFired) 35 | assert(lpParam!=nullptr); 36 | assert(TimerOrWaitFired); 37 | 38 | const one_shot_timer_t* one_shot_timer=static_cast(lpParam); 39 | one_shot_timer->timer_expired(); 40 | } 41 | #endif 42 | 43 | one_shot_timer_t::one_shot_timer_t(one_shot_timer_callback_t& p_one_shot_timer_callback) : 44 | m_one_shot_timer_callback(p_one_shot_timer_callback) 45 | #ifdef __linux__ 46 | , m_loop_thread(&one_shot_timer_t::loop, this) 47 | #endif 48 | { 49 | #ifdef _WIN32 50 | // Create the timer queue. 51 | hTimerQueue=CreateTimerQueue(); 52 | if(nullptr==hTimerQueue) 53 | { 54 | assert(false); 55 | } 56 | #endif 57 | } 58 | 59 | one_shot_timer_t::~one_shot_timer_t() 60 | { 61 | #ifdef _WIN32 62 | assert(hTimerQueue); 63 | if(!DeleteTimerQueue(hTimerQueue)) 64 | { 65 | assert(false); 66 | } 67 | hTimerQueue=nullptr; 68 | #else 69 | m_shutdown=true; 70 | m_loop_thread.join(); 71 | #endif 72 | } 73 | 74 | void one_shot_timer_t::start(const uint8_t p_duration_sec) 75 | { 76 | #if _WIN32 77 | if(!CreateTimerQueueTimer(&hTimer, hTimerQueue, TimerRoutine, this, p_duration_sec*1000, 0, 0)) 78 | { 79 | printf("Internal error: CreateTimerQueueTimer failed (%u)\n", GetLastError()); 80 | assert(false); 81 | } 82 | #else 83 | const std::lock_guard lock(m_mutex); 84 | assert(m_start_time_point.time_since_epoch().count()==0); 85 | m_start_time_point=std::chrono::steady_clock::now(); 86 | #endif 87 | } 88 | 89 | void one_shot_timer_t::timer_expired() const 90 | { 91 | m_one_shot_timer_callback.one_shot_timer_callback(); 92 | } 93 | 94 | #ifdef __linux__ 95 | void one_shot_timer_t::loop() 96 | { 97 | while(!m_shutdown) 98 | { 99 | std::this_thread::sleep_for(std::chrono::seconds(1)); 100 | 101 | {//scope 102 | const std::lock_guard lock(m_mutex); 103 | if(m_start_time_point.time_since_epoch().count()==0) 104 | { 105 | continue; 106 | } 107 | } 108 | 109 | { //scope 110 | const std::lock_guard lock(m_mutex); 111 | const auto now=std::chrono::steady_clock::now(); 112 | const uint64_t elapsed_ms=std::chrono::duration_cast(now-m_start_time_point).count(); 113 | if(elapsed_ms<5*1000) //FIXME 5 114 | { 115 | continue; 116 | } 117 | m_start_time_point={}; 118 | } 119 | 120 | m_one_shot_timer_callback.one_shot_timer_callback(); 121 | 122 | } 123 | } 124 | #endif 125 | 126 | -------------------------------------------------------------------------------- /docs/Options.md: -------------------------------------------------------------------------------- 1 | ### Options 2 | 3 | The options of CppGuard can be set via environment variable **CPPGUARD** or via function *cppguard_set_options*(): 4 | 5 | ``` 6 | set via environment variables: 7 | C:\dev\cppguard\out\build\x64-Debug>set CPPGUARD=halt_on_error:1,outputdebugstring:0 & test_target.exe 8 | 9 | set via function: 10 | std::string options = "log:C:/tmp/log.txt,halt_on_error:1,fail_process_in_case_of_error:1,outputdebugstring:0,"; 11 | options += "thread_watchdog:1,thread_watchdog_max_duration_sec:100,exit_code:42"; 12 | 13 | char error_text[1024]{}; 14 | const bool ok=cppguard_set_options(options.c_str(), error_text, sizeof(error_text)); 15 | assert(ok); 16 | ``` 17 | 18 | 19 | 20 | ##### Option list: 21 | 22 | | Option | Value range | Comment | 23 | | -------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------ | 24 | | halt_on_error | 0\|1, default: 0 | 0: continue
1: Terminate process if potential deadlock has been found. Stop on breakpoint if run inside debugger. | 25 | | fail_process_in_case_of_error | 0\|1, default: 0 | 0: normal exit with exit code of main()
1: if error occurred during runtime: fail process with exit 66 | 26 | | thread_watchdog | 0\|1, default: 1 | 0: disabled
1: observe time of locks spend before acquired and inside a lock
| 27 | | thread_watchdog_max_duration_sec | default: 30 sec, if a debugger is attached: min 120 sec | max duration a lock can spend waiting before a lock plus time spend inside a lock | 28 | | exit_code | 1..126, default: 66 | Process exit code in case of an error. Only used if halt_on_error:1 or fail_process_in_case_of_error:1 | 29 | | outputdebugstring | 0\|1, default: 1 | 0: disabled
1: log text will be added to the Visual Studio output windows | 30 | | log | disable\|stdout\|stderr\|pathtofile.txt, default: stdout | disable: no log
stdout: log to stdout
stderr: log to stderr
pathtofile.txt: Log to file. Do not quote path even if it contains spaces | 31 | 32 | 33 | 34 | ### Public APIs: 35 | 36 | ``` 37 | /** 38 | * \brief Set options during runtime 39 | * \param p_options text containing options, key:value pairs seperated by comma 40 | * \param p_error_text: error text in case of error 41 | * \param p_error_text_size: max size of error text 42 | * \return 43 | */ 44 | int cppguard_set_options(const char* p_options, char* p_error_text, int p_error_text_size); 45 | 46 | /** 47 | * \brief: increase ignore counter for this thread 48 | * \param p_lock_counter: lock counter is increased by this value 49 | * \param p_unlocks_counter: unlock counter is increased by this value 50 | */ 51 | void cppguard_increase_ignore_counter(const unsigned int p_lock_counter, const unsigned int p_unlocks_counter); 52 | 53 | Examples A: 54 | 55 | #ifdef CPPGUARD_ENABLED 56 | cppguard_increase_ignore_counter(2, 2); 57 | #endif 58 | 59 | EnterCriticalSection(CriticalSectionB); 60 | EnterCriticalSection(CriticalSectionA); 61 | ... 62 | LeaveCriticalSection(CriticalSectionA); 63 | LeaveCriticalSection(CriticalSectionB); 64 | 65 | Examples B: 66 | 67 | #ifdef CPPGUARD_ENABLED 68 | cppguard_increase_ignore_counter(1, 1); 69 | #endif 70 | std::mutex m_lock; 71 | const std::lock_guard < std::mutex> lock(m_lock); 72 | 73 | ``` 74 | 75 | 76 | 77 | ### Compile time options: 78 | 79 | | Define | Value range | Comment | 80 | | ----------------------------- | ----------- | ------------------------------------------------------------ | 81 | | CPPGUARD_MAX_LOCKS_PER_THREAD | default: 20 | Max number of locks a thread can acquire at the same time. Higher values will increase memory usage. | 82 | -------------------------------------------------------------------------------- /cppguard/stacktrace/stacktrace.cc: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "stacktrace.h" 23 | 24 | #ifdef _WIN32 25 | #include 26 | #pragma warning(push) 27 | #pragma warning(disable:4091) 28 | #include 29 | #pragma warning(pop) 30 | #endif 31 | 32 | #include 33 | 34 | #if _WIN32 35 | typedef USHORT NTAPI RtlCaptureStackBackTrace_Function( 36 | IN ULONG frames_to_skip, 37 | IN ULONG frames_to_capture, 38 | OUT PVOID* backtrace, 39 | OUT PULONG backtrace_hash); 40 | 41 | static RtlCaptureStackBackTrace_Function* const RtlCaptureStackBackTrace_fn=(RtlCaptureStackBackTrace_Function*)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlCaptureStackBackTrace"); 42 | 43 | static HANDLE process=nullptr; 44 | #endif 45 | 46 | TM_ATTRIBUTE_NOINLINE int GetStackFrames(void** result, int p_max_depth, int p_skip_count) 47 | { 48 | #if _WIN32 49 | assert(RtlCaptureStackBackTrace_fn); 50 | const int size=(RtlCaptureStackBackTrace_fn)(static_cast(p_skip_count)+1, static_cast(p_max_depth), result, nullptr); 51 | return size; 52 | #else 53 | return 0; 54 | #endif 55 | } 56 | 57 | void InitializeSymbolizer() 58 | { 59 | #if _WIN32 60 | 61 | if(process!=nullptr) { 62 | return; 63 | } 64 | process=GetCurrentProcess(); 65 | 66 | // Symbols are not loaded until a reference is made requiring the 67 | // symbols be loaded. This is the fastest, most efficient way to use 68 | // the symbol handler. 69 | SymSetOptions(SYMOPT_DEFERRED_LOADS|SYMOPT_UNDNAME); 70 | if(!SymInitialize(process, nullptr, true)) { 71 | // GetLastError() returns a Win32 DWORD, but we assign to 72 | // unsigned long long to simplify the ABSL_RAW_LOG case below. The uniform 73 | // initialization guarantees this is not a narrowing conversion. 74 | const unsigned long long error{GetLastError()}; // NOLINT(runtime/int) 75 | printf("CppGuard: Internal error: SymInitialize() failed: %llu", error); 76 | assert(false); 77 | } 78 | #endif 79 | } 80 | 81 | void printf_symbolinfo(const void* pc, fmt::memory_buffer& p_callstack_string) 82 | { 83 | #if _WIN32 84 | alignas(SYMBOL_INFO) char buf[sizeof(SYMBOL_INFO)+MAX_SYM_NAME]; 85 | SYMBOL_INFO* symbol=reinterpret_cast(buf); 86 | symbol->SizeOfStruct=sizeof(SYMBOL_INFO); 87 | symbol->MaxNameLen=MAX_SYM_NAME; 88 | 89 | if(!SymFromAddr(process, reinterpret_cast(pc), nullptr, symbol)) 90 | { 91 | return; 92 | } 93 | 94 | const std::string symbol_name=symbol->Name; 95 | 96 | // Get the line number information 97 | IMAGEHLP_LINE64 line; 98 | line.SizeOfStruct=sizeof(IMAGEHLP_LINE64); 99 | 100 | DWORD displacement; 101 | const bool get_line_ok=SymGetLineFromAddr64(process, reinterpret_cast(pc), &displacement, &line); 102 | 103 | fmt::format_to(std::back_inserter(p_callstack_string), "{}({},0): {}\n", get_line_ok ? line.FileName : "---", get_line_ok ? line.LineNumber : 0, symbol_name); 104 | #endif 105 | } 106 | 107 | void printf_call_stack_symbol_infos(void** pcs, int n, fmt::memory_buffer& p_callstack_string) 108 | { 109 | for(int i=0; i!=n; i++) 110 | { 111 | printf_symbolinfo(pcs[i], p_callstack_string); 112 | } 113 | } 114 | 115 | TM_ATTRIBUTE_NOINLINE void save_current_callstack_to_string(fmt::memory_buffer& p_callsstack_string, const int& p_skip_count) 116 | { 117 | #if _WIN32 118 | void* pcs[40]{}; 119 | const int s=GetStackFrames(pcs, _countof(pcs), p_skip_count); 120 | printf_call_stack_symbol_infos(pcs, s, p_callsstack_string); 121 | #endif 122 | } 123 | -------------------------------------------------------------------------------- /cppguard/thread_watchdog/thread_watchdog.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | 23 | #include "thread_watchdog.hpp" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "deadlock_monitor.hpp" 31 | #include "synch_locks_held.hpp" 32 | #include "timer.hpp" 33 | 34 | thread_watchdog_t::thread_watchdog_t(deadlock_monitor_t& p_deadlock_monitor, runtime_options_t& p_runtime_options) : 35 | m_deadlock_monitor(p_deadlock_monitor), 36 | m_runtime_options(p_runtime_options), 37 | m_one_shot_timer(*this), 38 | #ifdef _WIN32 39 | m_debugger_present(IsDebuggerPresent()) 40 | #else 41 | m_debugger_present(false) 42 | #endif 43 | { 44 | } 45 | 46 | void thread_watchdog_t::add_thread(const std::thread::id& p_thread_id, SynchLocksHeldPtr& p_snch) 47 | { 48 | if(!m_runtime_options.m_thread_watchdog_enabled) 49 | { 50 | return; 51 | } 52 | 53 | const std::lock_guard lock(m_thread_list_mutex); 54 | 55 | m_threads.emplace_back(p_snch, p_thread_id); 56 | 57 | if(m_threads.size()==1) 58 | { 59 | m_one_shot_timer.start(m_runtime_options.m_thread_watchdog_max_duration_sec>=10 ? 5 : 1); 60 | } 61 | } 62 | 63 | void thread_watchdog_t::remove_thread(const std::thread::id& p_thread_id) 64 | { 65 | if(!m_runtime_options.m_thread_watchdog_enabled) 66 | { 67 | return; 68 | } 69 | 70 | {//scope 71 | const std::lock_guard lock(m_thread_list_mutex); 72 | 73 | const auto found=std::find_if(m_threads.begin(), m_threads.end(), [&p_thread_id](const auto& o) { return o.m_thread_id==p_thread_id; }); 74 | 75 | if(found==m_threads.end()) 76 | { 77 | assert(false); 78 | return; 79 | } 80 | 81 | //copy last element to deleted place 82 | *found=m_threads.back(); 83 | m_threads.pop_back(); 84 | } 85 | } 86 | 87 | void thread_watchdog_t::one_shot_timer_callback() 88 | { 89 | SynchLocksHeldPtr found_long_locked; 90 | uint64_t elapsed_ms=0; 91 | 92 | {//scope 93 | const std::lock_guard lock(m_thread_list_mutex); 94 | 95 | const auto now=timer::get_ms_tick_counts(); 96 | 97 | for(auto& thread:m_threads) 98 | { 99 | const SynchLocksHeldPtr& sync_locks=thread.m_sync_ptr; 100 | const uint64_t lock_start_time_point=sync_locks->m_lock_start_time_point.load(); 101 | if(lock_start_time_point==0||(lock_start_time_point>=now)) 102 | { 103 | thread.m_already_logged=false; 104 | continue; 105 | } 106 | 107 | if(thread.m_already_logged) 108 | { 109 | continue; 110 | } 111 | 112 | elapsed_ms=now-lock_start_time_point; 113 | 114 | //avoid abort during debugging... 115 | const uint64_t max_duration_sec=(m_runtime_options.m_thread_watchdog_max_duration_sec<=120&&m_debugger_present) ? 120 : m_runtime_options.m_thread_watchdog_max_duration_sec.load(); 116 | if(elapsed_ms>=max_duration_sec*1000) 117 | { 118 | found_long_locked=sync_locks; 119 | thread.m_already_logged=true; 120 | break; 121 | } 122 | 123 | } 124 | } 125 | 126 | if(found_long_locked) 127 | { 128 | m_deadlock_monitor.process_watchdog_error(*found_long_locked, elapsed_ms/1000); 129 | } 130 | 131 | {//scope 132 | const std::lock_guard lock(m_thread_list_mutex); 133 | 134 | if(!m_threads.empty()) 135 | { 136 | //printf("loop beendet startet neuen timer\n"); 137 | m_one_shot_timer.start(m_runtime_options.m_thread_watchdog_max_duration_sec>=10 ? 5 : 1); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /cppguard/api_monitors/critical_section_monitor.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "deadlock_monitor.hpp" 23 | 24 | #include 25 | #include 26 | 27 | #ifdef _WIN32 28 | #include 29 | #else 30 | #define WINAPI 31 | #include "pthread.h" 32 | #define LPCRITICAL_SECTION void* 33 | 34 | void InitializeCriticalSection(LPCRITICAL_SECTION p_critical_section) 35 | { 36 | pthread_mutexattr_t Attr; 37 | pthread_mutexattr_init(&Attr); 38 | pthread_mutexattr_settype(&Attr, PTHREAD_MUTEX_RECURSIVE); 39 | pthread_mutex_init((pthread_mutex_t*)p_critical_section, &Attr); 40 | } 41 | 42 | void DeleteCriticalSection(LPCRITICAL_SECTION p_critical_section) 43 | { 44 | assert(p_critical_section); 45 | pthread_mutex_destroy((pthread_mutex_t*)p_critical_section); 46 | } 47 | 48 | void EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 49 | { 50 | pthread_mutex_lock((pthread_mutex_t*)lpCriticalSection); 51 | } 52 | 53 | void LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 54 | { 55 | pthread_mutex_unlock((pthread_mutex_t*)lpCriticalSection); 56 | } 57 | 58 | bool TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) 59 | { 60 | return pthread_mutex_trylock((pthread_mutex_t*)lpCriticalSection)==0; 61 | } 62 | 63 | #endif 64 | 65 | extern deadlock_monitor_t* g_deadlock_monitor; 66 | extern statistics_t g_statistics; 67 | 68 | TM_ATTRIBUTE_VISIBILITY extern "C" void WINAPI cppguard_initialize_critical_section(void* lpCriticalSection) 69 | { 70 | assert(lpCriticalSection); 71 | 72 | InitializeCriticalSection(static_cast(lpCriticalSection)); 73 | ++g_statistics.m_initial_critial_section_counter; 74 | } 75 | 76 | TM_ATTRIBUTE_VISIBILITY extern "C" void WINAPI cppguard_enter_critical_section(void* lpCriticalSection) 77 | { 78 | assert(lpCriticalSection); 79 | 80 | void* hold_locks=nullptr; 81 | assert(g_deadlock_monitor); 82 | 83 | uint64_t id=0; 84 | g_deadlock_monitor->dlc_deadlock_check_before_lock(lpCriticalSection, &hold_locks, id); 85 | if(id==0) 86 | { 87 | EnterCriticalSection(static_cast(lpCriticalSection)); 88 | return; 89 | } 90 | 91 | EnterCriticalSection(static_cast(lpCriticalSection)); 92 | g_deadlock_monitor->dlc_deadlock_check_in_lock(lpCriticalSection, id, hold_locks); 93 | } 94 | 95 | TM_ATTRIBUTE_VISIBILITY extern "C" void WINAPI cppguard_leave_critical_section(void* lpCriticalSection) 96 | { 97 | assert(lpCriticalSection); 98 | 99 | g_deadlock_monitor->dlc_deadlock_check_after_lock(lpCriticalSection); 100 | LeaveCriticalSection(static_cast(lpCriticalSection)); 101 | } 102 | 103 | TM_ATTRIBUTE_VISIBILITY extern "C" bool WINAPI cppguard_tryenter_critical_section(void* lpCriticalSection) 104 | { 105 | void* hold_locks=nullptr; 106 | uint64_t id=0; 107 | g_deadlock_monitor->dlc_deadlock_check_before_try_lock(lpCriticalSection, &hold_locks, id); 108 | if(id==0) 109 | { 110 | assert(false); 111 | return TryEnterCriticalSection(static_cast(lpCriticalSection)); 112 | } 113 | 114 | const bool ok=TryEnterCriticalSection(static_cast(lpCriticalSection)); 115 | if(!ok) 116 | { 117 | return ok; 118 | } 119 | 120 | g_deadlock_monitor->dlc_deadlock_check_in_lock(lpCriticalSection, id, hold_locks); 121 | return ok; 122 | } 123 | 124 | TM_ATTRIBUTE_VISIBILITY extern "C" void WINAPI cppguard_delete_critical_section(void* lpCriticalSection) 125 | { 126 | assert(lpCriticalSection); 127 | g_deadlock_monitor->dlc_deadlock_delete_lock(lpCriticalSection); 128 | DeleteCriticalSection(static_cast(lpCriticalSection)); 129 | ++g_statistics.m_delete_critial_section_counter; 130 | } 131 | 132 | -------------------------------------------------------------------------------- /cppguard/dl_main.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #ifdef _WIN32 23 | #include 24 | #else 25 | #include 26 | #endif 27 | 28 | #include 29 | #include 30 | #include 31 | #include "options_parser.hpp" 32 | #include 33 | 34 | #include "log.hpp" 35 | #include "log_helper.hpp" 36 | 37 | #include "fmt/format.h" 38 | #include "fmt/std.h" 39 | #include "debugbreak.h" 40 | 41 | runtime_options_t g_runtime_options; 42 | statistics_t g_statistics; 43 | deadlock_monitor_t* g_deadlock_monitor=nullptr; 44 | 45 | void set_options_from_env_variable(const std::string& p_env_var_name) 46 | { 47 | #ifdef _WIN32 48 | std::array env_buffer{}; 49 | size_t requiredSize=0; 50 | const errno_t res=getenv_s(&requiredSize, nullptr, 0, p_env_var_name.c_str()); 51 | if(res!=0||requiredSize==0) 52 | { 53 | return; 54 | } 55 | 56 | if(requiredSize>env_buffer.size()) 57 | { 58 | assert(false); 59 | return; 60 | } 61 | 62 | getenv_s(&requiredSize, env_buffer.data(), requiredSize, p_env_var_name.c_str()); 63 | 64 | printf("CppGuard: Env var '%s' variable is: '%s'\n", p_env_var_name.c_str(), env_buffer.data()); 65 | 66 | parse_options(env_buffer.data(), g_runtime_options); 67 | #else 68 | char* env_content=std::getenv(p_env_var_name.c_str()); 69 | if(env_content==nullptr) 70 | { 71 | return; 72 | } 73 | parse_options(env_content, g_runtime_options); 74 | 75 | #endif 76 | } 77 | 78 | struct loader_t 79 | { 80 | loader_t() 81 | { 82 | set_options_from_env_variable("CPPGUARD"); 83 | 84 | InitializeSymbolizer(); 85 | 86 | g_deadlock_monitor=new deadlock_monitor_t(g_runtime_options); 87 | 88 | if(g_runtime_options.m_log_type.load()!=static_cast(log_type_t::disabled)) 89 | { 90 | auto out=fmt::memory_buffer(); 91 | fmt::format_to(std::back_inserter(out), "========================================\n"); 92 | fmt::format_to(std::back_inserter(out), "CppGuard loaded [version: '{}.{}.{}.{}']\n", VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD, VERSION_REVISION); 93 | fmt::format_to(std::back_inserter(out), "========================================\n"); 94 | 95 | 96 | log_helper::create_options_log_text(g_runtime_options, out); 97 | 98 | logger::log_msg(out, g_runtime_options); 99 | 100 | 101 | } 102 | } 103 | 104 | ~loader_t() 105 | { 106 | assert(g_deadlock_monitor); 107 | 108 | const bool potential_deadlocks_found=g_deadlock_monitor->potential_deadlocks_found(); 109 | const bool other_errros_found=g_deadlock_monitor->other_errors_found(); 110 | delete g_deadlock_monitor; 111 | 112 | if(g_runtime_options.m_log_type.load()!=static_cast(log_type_t::disabled)) 113 | { 114 | auto summary=fmt::memory_buffer(); 115 | log_helper::create_summary(g_statistics, potential_deadlocks_found, other_errros_found, summary); 116 | logger::log_msg(summary, g_runtime_options); 117 | } 118 | 119 | const bool abort=potential_deadlocks_found||other_errros_found||(g_statistics.m_initial_critial_section_counter!=g_statistics.m_delete_critial_section_counter); 120 | 121 | if(abort&&g_runtime_options.m_override_process_exit_code_on_error) 122 | { 123 | #ifdef _WIN32 124 | if(IsDebuggerPresent()) 125 | #endif 126 | { 127 | debug_break(); 128 | } 129 | 130 | if(g_runtime_options.m_log_type.load()!=static_cast(log_type_t::disabled)) 131 | { 132 | auto out=fmt::memory_buffer(); 133 | fmt::format_to(std::back_inserter(out), "CppGuard: Terminate process with exit code {}\n", g_runtime_options.m_exit_code); 134 | logger::log_msg(out, g_runtime_options); 135 | } 136 | 137 | #ifdef _WIN32 138 | TerminateProcess(GetCurrentProcess(), g_runtime_options.m_exit_code); 139 | #else 140 | kill(getpid(), SIGKILL); 141 | #endif 142 | } 143 | 144 | } 145 | }; 146 | 147 | loader_t loader; 148 | -------------------------------------------------------------------------------- /cppguard/graphcycles/graphcycles.h: -------------------------------------------------------------------------------- 1 | //Licensed to the Apache Software Foundation (ASF) under one 2 | //or more contributor license agreements. See the NOTICE file 3 | //distributed with this work for additional information 4 | //regarding copyright ownership. The ASF licenses this file 5 | //to you under the Apache License, Version 2.0 (the 6 | //"License"); you may not use this file except in compliance 7 | //with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | //Unless required by applicable law or agreed to in writing, 12 | //software distributed under the License is distributed on an 13 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | //KIND, either express or implied. See the License for the 15 | //specific language governing permissions and limitations 16 | //under the License. 17 | 18 | // GraphCycles detects the introduction of a cycle into a directed 19 | // graph that is being built up incrementally. 20 | // 21 | // Nodes are identified by small integers. It is not possible to 22 | // record multiple edges with the same (source, destination) pair; 23 | // requests to add an edge where one already exists are silently 24 | // ignored. 25 | // 26 | // It is also not possible to introduce a cycle; an attempt to insert 27 | // an edge that would introduce a cycle fails and returns false. 28 | // 29 | // GraphCycles uses no internal locking; calls into it should be 30 | // serialized externally. 31 | 32 | // Performance considerations: 33 | // Works well on sparse graphs, poorly on dense graphs. 34 | // Extra information is maintained incrementally to detect cycles quickly. 35 | // InsertEdge() is very fast when the edge already exists, and reasonably fast 36 | // otherwise. 37 | // FindPath() is linear in the size of the graph. 38 | // The current implementation uses O(|V|+|E|) space. 39 | 40 | #pragma once 41 | 42 | #include 43 | #include 44 | #include 45 | 46 | #include "synch_locks_held.hpp" 47 | #include "attributes.hpp" 48 | 49 | class GraphCycles 50 | { 51 | public: 52 | GraphCycles(); 53 | ~GraphCycles(); 54 | 55 | // Return the id to use for ptr, assigning one if necessary. 56 | // Subsequent calls with the same ptr value will return the same id 57 | // until Remove(). 58 | void GetId(void* ptr, uint64_t& p_id) const; 59 | 60 | // Remove "ptr" from the graph. Its corresponding node and all 61 | // edges to and from it are removed. 62 | void RemoveNode(void* ptr) const; 63 | 64 | // Return the pointer associated with id, or nullptr if id is not 65 | // currently in the graph. 66 | void* Ptr(uint64_t id) const; 67 | 68 | void extra_infos(uint64_t id, std::thread::id& p_thread_id, uint8_t& p_hold_locks_count, std::array& p_hold_locks) const; 69 | 70 | // Attempt to insert an edge from source_node to dest_node. If the 71 | // edge would introduce a cycle, return false without making any 72 | // changes. Otherwise add the edge and return true. 73 | bool InsertEdge(uint64_t source_node, uint64_t dest_node) const; 74 | 75 | // Remove any edge that exists from source_node to dest_node. 76 | void RemoveEdge(uint64_t source_node, uint64_t dest_node) const; 77 | 78 | // Return whether node exists in the graph. 79 | bool HasNode(uint64_t node) const; 80 | 81 | // Return whether there is an edge directly from source_node to dest_node. 82 | bool HasEdge(uint64_t source_node, uint64_t dest_node) const; 83 | 84 | // Return whether dest_node is reachable from source_node 85 | // by following edges. 86 | bool IsReachable(uint64_t source_node, uint64_t dest_node) const; 87 | 88 | TM_ATTRIBUTE_NOINLINE void UpdateStackTrace(uint64_t id, SynchLocksHeld& p_all_locks) const; 89 | 90 | // Find a path from "source" to "dest". If such a path exists, 91 | // place the nodes on the path in the array path[], and return 92 | // the number of nodes on the path. If the path is longer than 93 | // max_path_len nodes, only the first max_path_len nodes are placed 94 | // in path[]. The client should compare the return value with 95 | // max_path_len" to see when this occurs. If no path exists, return 96 | // 0. Any valid path stored in path[] will start with "source" and 97 | // end with "dest". There is no guarantee that the path is the 98 | // shortest, but no node will appear twice in the path, except the 99 | // source and destination node if they are identical; therefore, the 100 | // return value is at most one greater than the number of nodes in 101 | // the graph. 102 | int FindPath(uint64_t source, uint64_t dest, int max_path_len, 103 | uint64_t path[]) const; 104 | 105 | // Set *ptr to the beginning of the array that holds the recorded 106 | // stack trace for id and return the depth of the stack trace. 107 | int GetStackTrace(uint64_t id, void*** ptr) const; 108 | 109 | // Check internal invariants. Crashes on failure, returns true on success. 110 | // Expensive: should only be called from graphcycles_test.cc. 111 | bool CheckInvariants() const; 112 | 113 | // ---------------------------------------------------- 114 | struct Rep; 115 | 116 | GraphCycles(const GraphCycles&)=delete; 117 | GraphCycles& operator=(const GraphCycles&)=delete; 118 | 119 | private: 120 | Rep* rep_; // opaque representation 121 | }; 122 | 123 | 124 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **CppGuard is now part of https://github.com/JochenBaier/BugInsight** 2 | 3 | ### Purpose 4 | 5 | **CppGuard** is developer tool for **testing** C++ applications made with Visual Studio **for deadlocks and wrong lock usage.** I can be used manually and within a CI system. CppGuard consists of the DLL *cppguard.dll* and the header *cppguard.h*. 6 | 7 | CppGuard uses the [deadlock detection algorithm](https://abseil.io/docs/cpp/guides/synchronization#deadlock-detection) from C++ library Abseil. This algorithm will detect a potential deadlock even if the executed program does not actually deadlock. It maintains an acquired-before graph and checks for cycles in this graph. The underlying graph algorithm is described [here](https://whileydave.com/2020/12/19/dynamic-cycle-detection-for-lock-ordering). There is no relationship between the Abseil project and CppGuard. 8 | 9 | ##### Advantages: 10 | 11 | - Test applications that cannot be tested on Linux with ThreadSanitizer 12 | - ThreadSanitizer often cannot cover 100% as some program parts are not portable: CppGuard covers this gap 13 | - Test with the Windows operating system on which the program will later run 14 | - Test applications that run too slowly with other Windows OS deadlock test tools 15 | - Test C/C++ DLLs loaded in a Java program ( Java Native Interfaces) 16 | - Test C/C++ DLLs loaded in a .Net program 17 | 18 | ###### Example deadlock detection: 19 | 20 | If locks are acquired in inconsistent order, it depends on the timing whether a deadlock occur or not. Example with 2 threads and mutex a and b: 21 | 22 | ``` 23 | std::mutex a; 24 | std::mutex b; 25 | 26 | //lock order: a -> b 27 | std::thread t1([&]() { 28 | const std::lock_guard lock_a(a); 29 | const std::lock_guard lock_b(b); 30 | }); 31 | 32 | //lock order: b -> a 33 | std::thread t2([&]() { 34 | const std::lock_guard lock_b(b); 35 | const std::lock_guard lock_a(a); 36 | }); 37 | 38 | t1.join(); 39 | t2.join(); 40 | ``` 41 | 42 | If t2 will acquired lock b after t1 has locked a and b no deadlock occur. 43 | if t1 got lock a and t2 got lock b a deadlock occur. 44 | 45 | 46 | CppGuard has detected the potential deadlock: 47 | 48 | ``` 49 | ========================================================================================================== 50 | CppGuard: [Error] Potential deadlock at lock: 0x1e2599ebd0, thread ID: 50092. 51 | Lock Call Stack: 52 | C:\dev\cppguard\cppguard\deadlock_monitor.cpp(152,0): deadlock_monitor_t::dlc_deadlock_check_before_lock 53 | C:\dev\cppguard\cppguard\api_monitors\mutex_monitor.cpp(37,0): cppguard_mtx_lock 54 | ... 55 | 56 | A cycle in the historical lock ordering graph has been observed: 57 | 58 | Thread ID: 50092 59 | Holding lock: 0x1e2599ec40 60 | Wait for lock: 0x1e2599ebd0 61 | Call Stack: 62 | C:\dev\cppguard\cppguard\api_monitors\mutex_monitor.cpp(37,0): cppguard_mtx_lock 63 | C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\mutex(56,0): std::_Mutex_base::lock 64 | ... 65 | 66 | Thread ID: 48624 67 | Holding lock: 0x1e2599ebd0 68 | Wait for lock: 0x1e2599ec40 69 | Call Stack: 70 | C:\dev\cppguard\cppguard\api_monitors\mutex_monitor.cpp(37,0): cppguard_mtx_lock 71 | C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.38.33130\include\mutex(56,0): std::_Mutex_base::lock 72 | ... 73 | ``` 74 | 75 | ### Features: 76 | 77 | - Deadlock detection: A warning or an error is generated if potential deadlock (a cycle) is detected (from Abseil algorithm) 78 | 79 | - Detect wrong lock usage: Detect locks which are released by threads which do no hold them (from Abseil algorithm) 80 | - Lock watchdog: The time a lock is waiting for a lock or holding a lock is monitored and a log or error is created if a maximum time is exceeded 81 | - Runs on Windows OS with the native lock APIs of Windows: Critical Section, std::mutex and std::recursive_mutex 82 | - Test C/C++ DLLs loaded in a Java or a .Net programs 83 | - Overhead depends on usage. Pretty good for IO bound applications (e.g. TCPI/IP based) 84 | - Works with Debug, Release and RelWithDebInfo builds 85 | - For basic usage no source code changes are needed. Minor changes (minimum 2 lines) to the build system (CMake, or Visual Studio Project) are needed 86 | 87 | ### Quickstart and building 88 | 89 | see [Quickstart](docs/Quickstart.md) 90 | 91 | ### Technical background: 92 | 93 | CppGuard provides: 94 | 95 | - cppguard.dll: This DLL and has to be linked to the project to test (import library: cppguard.lib) 96 | - [cppguard.h](cppguard/include/cppguard.h): This header file is automatically included in all source files using Visual Studio option [Forced Include File](https://learn.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file?view=msvc-170). 97 | Macros like *#define EnterCriticalSection(x) cppguard_enter_critical_section(x)* will intercept the lock API call 98 | 99 | ### Supported Lock APIs: 100 | 101 | - InitializeCriticalSection(), EnterCriticalSection(), TryEnterCriticalSection(), LeaveCriticalSection(), DeleteCriticalSection() 102 | 103 | - std::mutex, std::recursive_mutex: via internal functions from mutex header_Mtx_*() 104 | - std::lock_guard, std::unique_lock 105 | 106 | ### Not supported Lock APIs: 107 | 108 | - Slim Reader/Writer (SRW) Locks: no DeleteSRWLock() provided, needed to track delete of lock 109 | 110 | ### Not yet tested: 111 | 112 | - std::scoped_lock, std::shared_mutex, std::timed_mutex, std::recursive_timed_mutex std::shared_timed_mutex 113 | 114 | ### Options 115 | 116 | see [Options](docs/Options.md) 117 | 118 | ### Used Third-party software 119 | 120 | see [Third-party software](docs/Third_party_software.md) 121 | 122 | ### License 123 | 124 | CppGuard is licensed under the terms of the Apache 2.0 license. See LICENSE for more information. 125 | 126 | -------------------------------------------------------------------------------- /cppguard/helper/options_parser.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "options_parser.hpp" 23 | 24 | #include 25 | 26 | #include "pystring.h" 27 | #include "fmt/format.h" 28 | #include "fmt/std.h" 29 | 30 | void parse_options(const char* p_options_ptr, runtime_options_t& p_runtime_options) 31 | { 32 | const std::vector g_know_keys{"log", "outputdebugstring", "halt_on_error", "fail_process_in_case_of_error", "thread_watchdog", "thread_watchdog_max_duration_sec", "exit_code"}; 33 | const std::vector g_true_false{"1", "0"}; 34 | 35 | if(p_options_ptr==nullptr||p_options_ptr[0]=='\0') 36 | { 37 | throw std::invalid_argument(fmt::format("p_options is null or empty")); 38 | } 39 | 40 | const std::string p_options=p_options_ptr; 41 | 42 | const std::vector key_value_strings_sub_parts=pystring::split(pystring::lower(p_options), ","); 43 | if(key_value_strings_sub_parts.empty()) 44 | { 45 | throw std::invalid_argument(fmt::format("No key=value pairs found")); 46 | } 47 | 48 | // Split each substring by equals 49 | for(const auto& substring:key_value_strings_sub_parts) 50 | { 51 | const auto key_value_pair=pystring::partition(substring, ":"); 52 | if(key_value_pair[1]!=":") 53 | { 54 | throw std::invalid_argument(fmt::format("No valid key-value pair: '{}'", substring)); 55 | } 56 | 57 | const std::string key=pystring::strip(key_value_pair[0]); 58 | if(key.empty()) 59 | { 60 | throw std::invalid_argument(fmt::format("Option is empty for key-value pair: {}", substring)); 61 | } 62 | 63 | if(std::find(g_know_keys.begin(), g_know_keys.end(), key)==g_know_keys.end()) 64 | { 65 | throw std::invalid_argument(fmt::format("Option '{}' is not known", key)); 66 | } 67 | 68 | const std::string value=pystring::strip(key_value_pair[2]); 69 | if(value.empty()) 70 | { 71 | throw std::invalid_argument(fmt::format("Value from key-value pair '{}' is empty", substring)); 72 | } 73 | 74 | if(key=="log") 75 | { 76 | if(value=="disable") 77 | { 78 | p_runtime_options.m_log_type=static_cast(log_type_t::disabled); 79 | 80 | } else if(value=="stdout") 81 | { 82 | p_runtime_options.m_log_type=static_cast(log_type_t::to_stdout); 83 | 84 | } else if(value=="stderr") 85 | { 86 | p_runtime_options.m_log_type=static_cast(log_type_t::to_stderr); 87 | } else 88 | { 89 | p_runtime_options.m_log_type=static_cast(log_type_t::to_file); 90 | p_runtime_options.m_log_file=value; 91 | } 92 | 93 | } else if(key=="halt_on_error") 94 | { 95 | if(std::find(g_true_false.begin(), g_true_false.end(), value)==g_true_false.end()) 96 | { 97 | throw std::invalid_argument(fmt::format("Option '{}' requires value 1/0 but got '{}'", key, value)); 98 | } 99 | p_runtime_options.m_halt_on_error=(value=="1"); 100 | } else if(key=="fail_process_in_case_of_error") 101 | { 102 | if(std::find(g_true_false.begin(), g_true_false.end(), value)==g_true_false.end()) 103 | { 104 | throw std::invalid_argument(fmt::format("Option '{}' requires value 1/0 but got '{}'", key, value)); 105 | } 106 | p_runtime_options.m_override_process_exit_code_on_error=(value=="1"); 107 | } else if(key=="outputdebugstring") 108 | { 109 | if(std::find(g_true_false.begin(), g_true_false.end(), value)==g_true_false.end()) 110 | { 111 | throw std::invalid_argument(fmt::format("Option '{}' requires value 1/0 but got '{}'", key, value)); 112 | } 113 | p_runtime_options.m_use_outputdebugstring=(value=="1"); 114 | } else if(key=="thread_watchdog") 115 | { 116 | if(std::find(g_true_false.begin(), g_true_false.end(), value)==g_true_false.end()) 117 | { 118 | throw std::invalid_argument(fmt::format("Option '{}' requires value 1/0 but got '{}'", key, value)); 119 | } 120 | p_runtime_options.m_thread_watchdog_enabled=(value=="1"); 121 | } else if(key=="thread_watchdog_max_duration_sec") 122 | { 123 | if(!pystring::isdigit(value)) 124 | { 125 | throw std::invalid_argument(fmt::format("Value '{}' for option '{}' is not number", value, key)); 126 | } 127 | const int as_int_sec=std::stoi(value); 128 | const bool in_range=as_int_sec>1&&as_int_sec<=24*60*60; 129 | if(!in_range) 130 | { 131 | throw std::invalid_argument(fmt::format("Option '{}' requires value 2..{} but got '{}'", key, 24*60*60, value)); 132 | } 133 | 134 | p_runtime_options.m_thread_watchdog_max_duration_sec=as_int_sec; 135 | } else if(key=="exit_code") 136 | { 137 | if(!pystring::isdigit(value)) 138 | { 139 | throw std::invalid_argument(fmt::format("Value '{}' for option '{}' is not number", value, key)); 140 | } 141 | const int as_int=std::stoi(value); 142 | const bool in_range=as_int>=0&&as_int<127; 143 | if(!in_range) 144 | { 145 | throw std::invalid_argument(fmt::format("Option '{}' requires value 1..{} but got '{}'", key, 126, value)); 146 | } 147 | 148 | p_runtime_options.m_exit_code=as_int; 149 | } 150 | 151 | } 152 | 153 | } 154 | 155 | -------------------------------------------------------------------------------- /cppguard/helper/debugbreak.h: -------------------------------------------------------------------------------- 1 | /* Copyright (c) 2011-2021, Scott Tsai 2 | * 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions are met: 7 | * 8 | * 1. Redistributions of source code must retain the above copyright notice, 9 | * this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright notice, 11 | * this list of conditions and the following disclaimer in the documentation 12 | * and/or other materials provided with the distribution. 13 | * 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 18 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 19 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 20 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 21 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 22 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 23 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 24 | * POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | #ifndef DEBUG_BREAK_H 27 | #define DEBUG_BREAK_H 28 | 29 | #ifdef _MSC_VER 30 | 31 | #define debug_break __debugbreak 32 | 33 | #else 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | #define DEBUG_BREAK_USE_TRAP_INSTRUCTION 1 40 | #define DEBUG_BREAK_USE_BULTIN_TRAP 2 41 | #define DEBUG_BREAK_USE_SIGTRAP 3 42 | 43 | #if defined(__i386__) || defined(__x86_64__) 44 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 45 | __inline__ static void trap_instruction(void) 46 | { 47 | __asm__ volatile("int $0x03"); 48 | } 49 | #elif defined(__thumb__) 50 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 51 | /* FIXME: handle __THUMB_INTERWORK__ */ 52 | __attribute__((always_inline)) 53 | __inline__ static void trap_instruction(void) 54 | { 55 | /* See 'arm-linux-tdep.c' in GDB source. 56 | * Both instruction sequences below work. */ 57 | #if 1 58 | /* 'eabi_linux_thumb_le_breakpoint' */ 59 | __asm__ volatile(".inst 0xde01"); 60 | #else 61 | /* 'eabi_linux_thumb2_le_breakpoint' */ 62 | __asm__ volatile(".inst.w 0xf7f0a000"); 63 | #endif 64 | 65 | /* Known problem: 66 | * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. 67 | * 'step' would keep getting stuck on the same instruction. 68 | * 69 | * Workaround: use the new GDB commands 'debugbreak-step' and 70 | * 'debugbreak-continue' that become available 71 | * after you source the script from GDB: 72 | * 73 | * $ gdb -x debugbreak-gdb.py <... USUAL ARGUMENTS ...> 74 | * 75 | * 'debugbreak-step' would jump over the breakpoint instruction with 76 | * roughly equivalent of: 77 | * (gdb) set $instruction_len = 2 78 | * (gdb) tbreak *($pc + $instruction_len) 79 | * (gdb) jump *($pc + $instruction_len) 80 | */ 81 | } 82 | #elif defined(__arm__) && !defined(__thumb__) 83 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 84 | __attribute__((always_inline)) 85 | __inline__ static void trap_instruction(void) 86 | { 87 | /* See 'arm-linux-tdep.c' in GDB source, 88 | * 'eabi_linux_arm_le_breakpoint' */ 89 | __asm__ volatile(".inst 0xe7f001f0"); 90 | /* Known problem: 91 | * Same problem and workaround as Thumb mode */ 92 | } 93 | #elif defined(__aarch64__) && defined(__APPLE__) 94 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_BULTIN_DEBUGTRAP 95 | #elif defined(__aarch64__) 96 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 97 | __attribute__((always_inline)) 98 | __inline__ static void trap_instruction(void) 99 | { 100 | /* See 'aarch64-tdep.c' in GDB source, 101 | * 'aarch64_default_breakpoint' */ 102 | __asm__ volatile(".inst 0xd4200000"); 103 | } 104 | #elif defined(__powerpc__) 105 | /* PPC 32 or 64-bit, big or little endian */ 106 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 107 | __attribute__((always_inline)) 108 | __inline__ static void trap_instruction(void) 109 | { 110 | /* See 'rs6000-tdep.c' in GDB source, 111 | * 'rs6000_breakpoint' */ 112 | __asm__ volatile(".4byte 0x7d821008"); 113 | 114 | /* Known problem: 115 | * After a breakpoint hit, can't 'stepi', 'step', or 'continue' in GDB. 116 | * 'step' stuck on the same instruction ("twge r2,r2"). 117 | * 118 | * The workaround is the same as ARM Thumb mode: use debugbreak-gdb.py 119 | * or manually jump over the instruction. */ 120 | } 121 | #elif defined(__riscv) 122 | /* RISC-V 32 or 64-bit, whether the "C" extension 123 | * for compressed, 16-bit instructions are supported or not */ 124 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_TRAP_INSTRUCTION 125 | __attribute__((always_inline)) 126 | __inline__ static void trap_instruction(void) 127 | { 128 | /* See 'riscv-tdep.c' in GDB source, 129 | * 'riscv_sw_breakpoint_from_kind' */ 130 | __asm__ volatile(".4byte 0x00100073"); 131 | } 132 | #else 133 | #define DEBUG_BREAK_IMPL DEBUG_BREAK_USE_SIGTRAP 134 | #endif 135 | 136 | 137 | #ifndef DEBUG_BREAK_IMPL 138 | #error "debugbreak.h is not supported on this target" 139 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_TRAP_INSTRUCTION 140 | __attribute__((always_inline)) 141 | __inline__ static void debug_break(void) 142 | { 143 | trap_instruction(); 144 | } 145 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_DEBUGTRAP 146 | __attribute__((always_inline)) 147 | __inline__ static void debug_break(void) 148 | { 149 | __builtin_debugtrap(); 150 | } 151 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_BULTIN_TRAP 152 | __attribute__((always_inline)) 153 | __inline__ static void debug_break(void) 154 | { 155 | __builtin_trap(); 156 | } 157 | #elif DEBUG_BREAK_IMPL == DEBUG_BREAK_USE_SIGTRAP 158 | #include 159 | __attribute__((always_inline)) 160 | __inline__ static void debug_break(void) 161 | { 162 | raise(SIGTRAP); 163 | } 164 | #else 165 | #error "invalid DEBUG_BREAK_IMPL value" 166 | #endif 167 | 168 | #ifdef __cplusplus 169 | } 170 | #endif 171 | 172 | #endif /* ifdef _MSC_VER */ 173 | 174 | #endif /* ifndef DEBUG_BREAK_H */ 175 | -------------------------------------------------------------------------------- /cppguard/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | #Licensed to the Apache Software Foundation (ASF) under one 2 | #or more contributor license agreements. See the NOTICE file 3 | #distributed with this work for additional information 4 | #regarding copyright ownership. The ASF licenses this file 5 | #to you under the Apache License, Version 2.0 (the 6 | #"License"); you may not use this file except in compliance 7 | #with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | #Unless required by applicable law or agreed to in writing, 12 | #software distributed under the License is distributed on an 13 | #"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | #KIND, either express or implied. See the License for the 15 | #specific language governing permissions and limitations 16 | #under the License. 17 | 18 | cmake_minimum_required(VERSION 3.15) 19 | project(cppguard) 20 | set(CMAKE_CXX_STANDARD 14) 21 | 22 | 23 | set(VERSION_MAJOR 0) 24 | set(VERSION_MINOR 2) 25 | set(VERSION_BUILD 0) 26 | set(VERSION_REVISION 0) 27 | 28 | configure_file(cppguard_rc.in cppguard.rc @ONLY) 29 | 30 | 31 | set(source_list 32 | cppguard.rc 33 | dl_main.cpp 34 | graphcycles/graphcycles.cc 35 | graphcycles/graphcycles.h 36 | deadlock_monitor.cpp 37 | deadlock_monitor.hpp 38 | helper/low_level_alloc.cc 39 | helper/low_level_alloc.h 40 | dll_exports/deadlock_monitor_api.cpp 41 | stacktrace/stacktrace.cc 42 | stacktrace/stacktrace.h 43 | fmt/format.cc 44 | thread_watchdog/thread_watchdog.cpp 45 | thread_watchdog/thread_watchdog.hpp 46 | helper/cs_mutex.cpp 47 | helper/pystring.cpp 48 | helper/pystring.h 49 | helper/options_parser.cpp 50 | helper/options_parser.hpp 51 | helper/run_time_options.hpp 52 | helper/cs_mutex.cpp 53 | helper/cs_mutex.hpp 54 | 55 | helper/timer.hpp 56 | output/log.cpp 57 | output/log.hpp 58 | output/log_helper.cpp 59 | output/log_helper.hpp 60 | api_monitors/critical_section_monitor.cpp 61 | thread_history/thread_history_helper.cpp 62 | thread_history/thread_history_helper.hpp 63 | helper/attributes.hpp 64 | helper/one_shot_timer.cpp 65 | helper/one_shot_timer.hpp 66 | ) 67 | 68 | if (WIN32) 69 | set(source_list ${source_list} 70 | dll_exports/cppguard_exports.def 71 | api_monitors/mutex_monitor.cpp 72 | ) 73 | endif() 74 | 75 | add_library(cppguard SHARED ${source_list}) 76 | set_property(TARGET cppguard PROPERTY CXX_STANDARD 14) 77 | set_property(TARGET cppguard PROPERTY CXX_STANDARD_REQUIRED on) 78 | 79 | if(CPPGUARD_MAX_LOCKS_PER_THREAD) 80 | target_compile_definitions(cppguard PRIVATE MAX_LOCKS_PER_THREAD=${CPPGUARD_MAX_LOCKS_PER_THREAD}) 81 | else() 82 | target_compile_definitions(cppguard PRIVATE MAX_LOCKS_PER_THREAD=20) 83 | endif() 84 | 85 | 86 | target_compile_definitions(cppguard PRIVATE VERSION_MAJOR=${VERSION_MAJOR}) 87 | target_compile_definitions(cppguard PRIVATE VERSION_MINOR=${VERSION_MINOR}) 88 | target_compile_definitions(cppguard PRIVATE VERSION_BUILD=${VERSION_BUILD}) 89 | target_compile_definitions(cppguard PRIVATE VERSION_REVISION=${VERSION_REVISION}) 90 | 91 | 92 | foreach(_source IN ITEMS ${_source_list}) 93 | get_filename_component(_source_path "${_source}" PATH) 94 | string(REPLACE "${CMAKE_SOURCE_DIR}" "" _group_path "${_source_path}") 95 | string(REPLACE "/" "\\" _group_path "${_group_path}") 96 | source_group("${_group_path}" FILES "${_source}") 97 | endforeach() 98 | 99 | target_include_directories(cppguard 100 | PRIVATE 101 | ${CMAKE_CURRENT_LIST_DIR} 102 | thread_history 103 | stacktrace/ 104 | thread_watchdog/ 105 | graphcycles/ 106 | output/ 107 | helper/ 108 | pystring/ 109 | fmt 110 | PUBLIC 111 | ${CMAKE_CURRENT_LIST_DIR}/include 112 | ) 113 | 114 | 115 | add_library(cppguard_forced_include INTERFACE) 116 | if (WIN32) 117 | target_compile_options(cppguard_forced_include INTERFACE "/FI${CMAKE_CURRENT_LIST_DIR}/include/cppguard.h" ) 118 | endif() 119 | add_library(cppguard::auto_include ALIAS cppguard_forced_include) 120 | 121 | add_library(cppguard::cppguard ALIAS cppguard) 122 | 123 | 124 | if (WIN32) 125 | #flags for standard conformance 126 | target_compile_options(cppguard PRIVATE "/permissive-") 127 | target_compile_options(cppguard PRIVATE "/volatile:iso") 128 | target_compile_options(cppguard PRIVATE "/Zc:inline") 129 | target_compile_options(cppguard PRIVATE "/Zc:preprocessor") 130 | target_compile_options(cppguard PRIVATE "/Zc:lambda") 131 | target_compile_options(cppguard PRIVATE "/Zc:__cplusplus") 132 | target_compile_options(cppguard PRIVATE "/Zc:externConstexpr") 133 | target_compile_options(cppguard PRIVATE "/Zc:throwingNew") 134 | target_compile_options(cppguard PRIVATE "/EHsc") 135 | 136 | #this warnings as errors 137 | target_compile_options(cppguard PRIVATE "/we4062") # enumerator in switch not handled 138 | target_compile_options(cppguard PRIVATE "/we4296") # '>=': expression is always true 139 | target_compile_options(cppguard PRIVATE "/we4715") # not all control paths return a value 140 | target_compile_options(cppguard PRIVATE "/we4553") # '==': operator has no effect; did you intend '='? 141 | target_compile_options(cppguard PRIVATE "/we4552") # '>=': operator has no effect; expected operator with side-effect 142 | target_compile_options(cppguard PRIVATE "/we4456") # declaration of 'xxx' hides previous local declaration 143 | target_compile_options(cppguard PRIVATE "/we4239") # nonstandard extension used 144 | target_compile_options(cppguard PRIVATE "/we4706") # assignment within conditional expression 145 | target_compile_options(cppguard PRIVATE "/we4129") # unrecognized character escape sequence 146 | 147 | set_property(TARGET cppguard PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 148 | target_compile_options(cppguard PRIVATE "/W4" ) 149 | target_compile_options(cppguard PRIVATE "$<$:/GL>" ) 150 | target_link_options(cppguard PRIVATE "$<$:/LTCG>" ) 151 | target_link_libraries(cppguard PRIVATE dbghelp.lib) 152 | 153 | else() 154 | set(THREADS_PREFER_PTHREAD_FLAG ON) 155 | find_package(Threads REQUIRED) 156 | target_link_libraries(cppguard PRIVATE Threads::Threads) 157 | endif() 158 | 159 | 160 | #target_compile_options(cppguard PUBLIC -fsanitize=address) 161 | #target_link_options(cppguard PUBLIC -fsanitize=address) 162 | 163 | #target_compile_options(cppguard PUBLIC -fsanitize=thread) 164 | #target_link_options(cppguard PUBLIC -fsanitize=thread) 165 | 166 | -------------------------------------------------------------------------------- /docs/Quickstart.md: -------------------------------------------------------------------------------- 1 | # CppGuard Quickstart 2 | 3 | ##### Usage/**TL;DR** 4 | 5 | 1. Add CppGuard to an existing CMake project: 6 | 7 | ``` 8 | add_subdirectory(path_to/cppguard/cppguard) or 9 | add_subdirectory(path_to/cppguard/cppguard cppguard) #with binary_dir 'cppguard' 10 | ``` 11 | 12 | 2. Add 'cppguard' as a dependency to all targets you want to test: 13 | 14 | ``` 15 | target_link_libraries(target_to_test cppguard::cppguard cppguard::auto_include) 16 | ``` 17 | 18 | 3. Copy cppguard.dll after build to output directory 19 | 20 | ------ 21 | 22 | ##### Prerequisites 23 | 24 | - The target to test: Executable or DLL with access to source code created with Visual Studio 2019 or 2022 in C or C++, all C++ versions supported. 25 | 26 | - C-projects: C++ support has to be enabled: project(project_name C **CXX**) 27 | 28 | - To compile CppGuard: 29 | - Visual Studio 2019 or 2022 with C++14 support 30 | - CMake >=3.15 31 | 32 | - Use same compiler and platform toolset for CppGuard and the test target 33 | - git to download sources (optional) 34 | 35 | ##### CppGuard consists of 36 | 37 | - cppguard.dll: needs to be linked to test target (link cppguard.lib) 38 | - cppguard.h: needs to be included in all sources which contains locks, this is automated with '[/FI](https://learn.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file?view=msvc-170)' compile option 39 | 40 | ##### Getting CppGuard 41 | 42 | Clone source with git: 43 | 44 | ``` 45 | git clone https://github.com/JochenBaier/cppguard.git 46 | ``` 47 | 48 | or download as zip from GitHub repo 49 | 50 | ##### Build and run example 51 | 52 | - Open the main folder 'cppguard' in Visual Studio via "Open a local folder" 53 | - select 'quickstart_example' as startup item 54 | - run 'quickstart_example' 55 | 56 | Alternatively generate a VS solution with CMake: 57 | 58 | ``` 59 | cd cppguard 60 | mkdir build 61 | cd build 62 | cmake .. 63 | open *.sln from build folder with Visual Studio 64 | ``` 65 | 66 | ##### Deadlock detection 67 | 68 | The example 'quickstart_example' executes the dining philosophers problem. CppGuard has detected the deadlock. The code will still deadlock. The program has to be stopped manually. 69 | 70 | ``` 71 | ================= 72 | CppGuard loaded 73 | ================= 74 | Philosopher 0 picked up left fork 75 | Philosopher 3 picked up left fork 76 | Philosopher 4 picked up left fork 77 | Philosopher 1 picked up left fork 78 | Philosopher 2 picked up left fork 79 | 80 | ========================================================================================================== 81 | CppGuard: [Error] Potential deadlock at lock: 0x7ff75d0824c0, thread ID: 19784. 82 | Lock Call Stack: 83 | C:\dev\cppguard\cppguard\deadlock_monitor.cpp(148,0): deadlock_monitor_t::dlc_deadlock_check_before_lock 84 | C:\dev\cppguard\cppguard\api_monitors\critical_section_monitor.cpp(96,0): cppguard_enter_critical_section 85 | C:\dev\cppguard\quickstart_example\main.cpp(47,0): philosopher 86 | ... 87 | 88 | A cycle in the historical lock ordering graph has been observed: 89 | 90 | Thread ID: 19784 91 | Holding lock: 0x7ff75d082498 92 | Wait for lock: 0x7ff75d0824c0 93 | Call Stack: 94 | C:\dev\cppguard\cppguard\api_monitors\critical_section_monitor.cpp(96,0): cppguard_enter_critical_section 95 | C:\dev\cppguard\quickstart_example\main.cpp(47,0): philosopher 96 | ... 97 | 98 | Thread ID: 10272 99 | Holding lock: 0x7ff75d0824c0 100 | Wait for lock: 0x7ff75d0824e8 101 | Call Stack: 102 | C:\dev\cppguard\cppguard\api_monitors\critical_section_monitor.cpp(96,0): cppguard_enter_critical_section 103 | C:\dev\cppguard\quickstart_example\main.cpp(47,0): philosopher 104 | ... 105 | ``` 106 | 107 | *Hint: the Call Stack is also printed to the Visual Studio output window: double click on a line to go to the source location* 108 | 109 | To configure CppGuard to halt on case of deadlock add this to main.cpp of the example: 110 | 111 | ```c++ 112 | char error_text[1024]{}; 113 | int res=cppguard_set_options("halt_on_error:1", error_text, _countof(error_text)); 114 | assert(res == 1); 115 | ``` 116 | 117 | Alternatively set the environment variable 'CPPGUARD' with value 'halt_on_error:1' and start quickstart_example.exe on command line: 118 | 119 | ``` 120 | C:\dev\cppguard\out\build\x64-Debug>set CPPGUARD=halt_on_error:1 & quickstart_example.exe 121 | ``` 122 | 123 | Now the example will terminate before the deadlock occur or pause on a breakpoint if run inside a debugger: 124 | 125 | ``` 126 | CppGuard: [Error] Potential deadlock at lock: 0x7ff75d0824c0, thread ID: 19784. 127 | ... 128 | CppGuard: Process will be terminated with exit code '66' due to potential deadlock. Use option 'halt_on_error:0' to continue on error 129 | ``` 130 | 131 | ##### CMakeLists.txt of a target to test: 132 | 133 | The CMakeLists.txt of the example: 134 | 135 | ```cmake 136 | add_executable(quickstart_example main.cpp) 137 | 138 | #add this line to all targets you want to test 139 | target_link_libraries(quickstart_example cppguard::cppguard cppguard::auto_include) 140 | ``` 141 | 142 | **cppguard::cppguard**: will link cppguard and adds 'cppguard/include' folder to VS 'Additional included directories'. 143 | Use this for Exe or DLL targets. If the exe or DLL target does not contain any locks you can omit "cppguard::auto_include" 144 | 145 | **cppguard::auto_include**: sets '[/FI](https://learn.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file?view=msvc-170)' compile option with value 'cppguard.h'. This option includes 'cppguard.h' 146 | in all source files of the target. Use this option for all targets which contains locks. This header enables CppGuard to monitor the usage of Locks. 147 | 148 | 149 | 150 | ##### Usage without add_subdirectory(cppguard)/External build 151 | 152 | Build cppguard with CMake outside of your project: 153 | 154 | ``` 155 | cd cppguard/cppguard 156 | mkdir build 157 | cd build 158 | cmake .. 159 | cmake --build . //debug 160 | ``` 161 | 162 | Set '[/FI](https://learn.microsoft.com/en-us/cpp/build/reference/fi-name-forced-include-file?view=msvc-170)' compile option for all targets to test: 163 | 164 | ``` 165 | target_compile_options(test_target PRIVATE "/FIpath_to/cppguard/cppguard/include/cppguard.h" ) 166 | ``` 167 | 168 | Link cppguard: 169 | 170 | ``` 171 | add_library(cppguard STATIC IMPORTED GLOBAL) 172 | set_target_properties(cppguard PROPERTIES IMPORTED_LOCATION_DEBUG "path_to/cppguard/cppguard/build/Debug/cppguard.lib" ) 173 | set_target_properties(cppguard PROPERTIES IMPORTED_LOCATION_RELEASE "path_to/cppguard/cppguard/build/Release/cppguard.lib") 174 | set_target_properties(cppguard PROPERTIES IMPORTED_LOCATION_RELWITHDEBINFO "path_to/cppguard/cppguard/build/RelWithDebInfo/cppguard.lib") 175 | 176 | target_link_libraries(test_target cppguard) 177 | ``` 178 | -------------------------------------------------------------------------------- /cppguard/fmt/fmt/ostream.h: -------------------------------------------------------------------------------- 1 | // Formatting library for C++ - std::ostream support 2 | // 3 | // Copyright (c) 2012 - present, Victor Zverovich 4 | // All rights reserved. 5 | // 6 | // For the license information refer to format.h. 7 | 8 | #ifndef FMT_OSTREAM_H_ 9 | #define FMT_OSTREAM_H_ 10 | 11 | #include // std::filebuf 12 | 13 | #ifdef _WIN32 14 | # ifdef __GLIBCXX__ 15 | # include 16 | # include 17 | # endif 18 | # include 19 | #endif 20 | 21 | #include "format.h" 22 | 23 | FMT_BEGIN_NAMESPACE 24 | 25 | namespace detail { 26 | 27 | // Generate a unique explicit instantion in every translation unit using a tag 28 | // type in an anonymous namespace. 29 | namespace { 30 | struct file_access_tag {}; 31 | } // namespace 32 | template 33 | class file_access { 34 | friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; } 35 | }; 36 | 37 | #if FMT_MSC_VERSION 38 | template class file_access; 40 | auto get_file(std::filebuf&) -> FILE*; 41 | #endif 42 | 43 | inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) { 44 | FILE* f = nullptr; 45 | #if FMT_MSC_VERSION 46 | if (auto* buf = dynamic_cast(os.rdbuf())) 47 | f = get_file(*buf); 48 | else 49 | return false; 50 | #elif defined(_WIN32) && defined(__GLIBCXX__) 51 | auto* rdbuf = os.rdbuf(); 52 | if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) 53 | f = sfbuf->file(); 54 | else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf*>(rdbuf)) 55 | f = fbuf->file(); 56 | else 57 | return false; 58 | #else 59 | ignore_unused(os, data, f); 60 | #endif 61 | #ifdef _WIN32 62 | if (f) { 63 | int fd = _fileno(f); 64 | if (_isatty(fd)) { 65 | os.flush(); 66 | return write_console(fd, data); 67 | } 68 | } 69 | #endif 70 | return false; 71 | } 72 | inline bool write_ostream_unicode(std::wostream&, 73 | fmt::basic_string_view) { 74 | return false; 75 | } 76 | 77 | // Write the content of buf to os. 78 | // It is a separate function rather than a part of vprint to simplify testing. 79 | template 80 | void write_buffer(std::basic_ostream& os, buffer& buf) { 81 | const Char* buf_data = buf.data(); 82 | using unsigned_streamsize = std::make_unsigned::type; 83 | unsigned_streamsize size = buf.size(); 84 | unsigned_streamsize max_size = to_unsigned(max_value()); 85 | do { 86 | unsigned_streamsize n = size <= max_size ? size : max_size; 87 | os.write(buf_data, static_cast(n)); 88 | buf_data += n; 89 | size -= n; 90 | } while (size != 0); 91 | } 92 | 93 | template 94 | void format_value(buffer& buf, const T& value, 95 | locale_ref loc = locale_ref()) { 96 | auto&& format_buf = formatbuf>(buf); 97 | auto&& output = std::basic_ostream(&format_buf); 98 | #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) 99 | if (loc) output.imbue(loc.get()); 100 | #endif 101 | output << value; 102 | output.exceptions(std::ios_base::failbit | std::ios_base::badbit); 103 | } 104 | 105 | template struct streamed_view { 106 | const T& value; 107 | }; 108 | 109 | } // namespace detail 110 | 111 | // Formats an object of type T that has an overloaded ostream operator<<. 112 | template 113 | struct basic_ostream_formatter : formatter, Char> { 114 | void set_debug_format() = delete; 115 | 116 | template 117 | auto format(const T& value, basic_format_context& ctx) const 118 | -> OutputIt { 119 | auto buffer = basic_memory_buffer(); 120 | detail::format_value(buffer, value, ctx.locale()); 121 | return formatter, Char>::format( 122 | {buffer.data(), buffer.size()}, ctx); 123 | } 124 | }; 125 | 126 | using ostream_formatter = basic_ostream_formatter; 127 | 128 | template 129 | struct formatter, Char> 130 | : basic_ostream_formatter { 131 | template 132 | auto format(detail::streamed_view view, 133 | basic_format_context& ctx) const -> OutputIt { 134 | return basic_ostream_formatter::format(view.value, ctx); 135 | } 136 | }; 137 | 138 | /** 139 | \rst 140 | Returns a view that formats `value` via an ostream ``operator<<``. 141 | 142 | **Example**:: 143 | 144 | fmt::print("Current thread id: {}\n", 145 | fmt::streamed(std::this_thread::get_id())); 146 | \endrst 147 | */ 148 | template 149 | constexpr auto streamed(const T& value) -> detail::streamed_view { 150 | return {value}; 151 | } 152 | 153 | namespace detail { 154 | 155 | inline void vprint_directly(std::ostream& os, string_view format_str, 156 | format_args args) { 157 | auto buffer = memory_buffer(); 158 | detail::vformat_to(buffer, format_str, args); 159 | detail::write_buffer(os, buffer); 160 | } 161 | 162 | } // namespace detail 163 | 164 | FMT_EXPORT template 165 | void vprint(std::basic_ostream& os, 166 | basic_string_view> format_str, 167 | basic_format_args>> args) { 168 | auto buffer = basic_memory_buffer(); 169 | detail::vformat_to(buffer, format_str, args); 170 | if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return; 171 | detail::write_buffer(os, buffer); 172 | } 173 | 174 | /** 175 | \rst 176 | Prints formatted data to the stream *os*. 177 | 178 | **Example**:: 179 | 180 | fmt::print(cerr, "Don't {}!", "panic"); 181 | \endrst 182 | */ 183 | FMT_EXPORT template 184 | void print(std::ostream& os, format_string fmt, T&&... args) { 185 | const auto& vargs = fmt::make_format_args(args...); 186 | if (detail::is_utf8()) 187 | vprint(os, fmt, vargs); 188 | else 189 | detail::vprint_directly(os, fmt, vargs); 190 | } 191 | 192 | FMT_EXPORT 193 | template 194 | void print(std::wostream& os, 195 | basic_format_string...> fmt, 196 | Args&&... args) { 197 | vprint(os, fmt, fmt::make_format_args>(args...)); 198 | } 199 | 200 | FMT_EXPORT template 201 | void println(std::ostream& os, format_string fmt, T&&... args) { 202 | fmt::print(os, "{}\n", fmt::format(fmt, std::forward(args)...)); 203 | } 204 | 205 | FMT_EXPORT 206 | template 207 | void println(std::wostream& os, 208 | basic_format_string...> fmt, 209 | Args&&... args) { 210 | print(os, L"{}\n", fmt::format(fmt, std::forward(args)...)); 211 | } 212 | 213 | FMT_END_NAMESPACE 214 | 215 | #endif // FMT_OSTREAM_H_ 216 | -------------------------------------------------------------------------------- /cppguard_tests/test_thread_watchdog_good.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "doctest.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #ifdef _WIN32 30 | #include 31 | #endif 32 | 33 | #ifdef __linux__ 34 | #include "cppguard.h" 35 | #include "critical_section_linux.hpp" 36 | #endif 37 | 38 | #include "random_numbers.hpp" 39 | #include "utils/cs.hpp" 40 | #include "utils/signal.hpp" 41 | 42 | //FIXME some variables could be optimized away in release? 43 | 44 | TEST_CASE("test_cs_t1_duration_1_sec_ok_exit_0") 45 | { 46 | cs_maker_t cs_maker; 47 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 48 | 49 | EnterCriticalSection(CriticalSectionA); 50 | 51 | std::this_thread::sleep_for(std::chrono::seconds(1)); 52 | 53 | LeaveCriticalSection(CriticalSectionA); 54 | } 55 | 56 | TEST_CASE("test_cs_t1_duration_4_sec_ok_exit_0") 57 | { 58 | cs_maker_t cs_maker; 59 | 60 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 61 | 62 | EnterCriticalSection(CriticalSectionA); 63 | 64 | std::this_thread::sleep_for(std::chrono::seconds(4)); 65 | 66 | LeaveCriticalSection(CriticalSectionA); 67 | } 68 | 69 | TEST_CASE("test_x_times_cs_t1_duration_1_sec_ok_exit_0") 70 | { 71 | cs_maker_t cs_maker; 72 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 73 | 74 | for(size_t i=0; i<1000; ++i) 75 | { 76 | std::thread t1([&]() 77 | { 78 | EnterCriticalSection(CriticalSectionA); 79 | LeaveCriticalSection(CriticalSectionA); 80 | }); 81 | t1.join(); 82 | } 83 | } 84 | 85 | TEST_CASE("test_ok_cs_t1_bulk2_ok_exit_0") 86 | { 87 | const size_t cs_count=20; 88 | 89 | std::vector critical_sections; 90 | critical_sections.resize(cs_count); 91 | 92 | for(auto& cs:critical_sections) 93 | { 94 | InitializeCriticalSection(&cs); 95 | } 96 | 97 | for(auto& cs:critical_sections) 98 | { 99 | EnterCriticalSection(&cs); 100 | } 101 | 102 | for(auto& cs:critical_sections) 103 | { 104 | LeaveCriticalSection(&cs); 105 | } 106 | 107 | for(auto& cs:critical_sections) 108 | { 109 | DeleteCriticalSection(&cs); 110 | } 111 | 112 | } 113 | 114 | void test_thread_watchdog_cs_100t_10cs_bulk_thread_function(std::vector& p_critical_sections, std::atomic_uint32_t& p_counter) 115 | { 116 | for(size_t i=0; i<10; ++i) 117 | { 118 | for(auto*& cs:p_critical_sections) 119 | { 120 | EnterCriticalSection(cs); 121 | } 122 | 123 | p_counter++; 124 | 125 | for(auto*& cs:p_critical_sections) 126 | { 127 | LeaveCriticalSection(cs); 128 | } 129 | } 130 | } 131 | 132 | TEST_CASE("test_good_cs_multi_thread_ok_exit_0") 133 | { 134 | const size_t cs_count=20; 135 | const size_t thread_count=1000; 136 | std::atomic_uint32_t counter{0}; 137 | 138 | std::vector critical_sections; 139 | critical_sections.resize(cs_count); 140 | 141 | for(auto*& cs:critical_sections) 142 | { 143 | cs=new CRITICAL_SECTION; 144 | InitializeCriticalSection(cs); 145 | } 146 | 147 | std::vector threads; 148 | 149 | for(size_t i=0; i lock(m); 189 | std::this_thread::sleep_for(std::chrono::seconds(4)); 190 | } 191 | 192 | TEST_CASE("test_std_condition_variable_duration_10_sec_good_exit_0") 193 | { 194 | utils::event_t t; 195 | //should not trigger watchdog 196 | t.wait_with_timeout(10*1000); 197 | } 198 | 199 | TEST_CASE("test_std_condition_variable_duration_10_sec_with_signal_exit_0") 200 | { 201 | utils::event_t t; 202 | 203 | std::thread t1([&]() 204 | { 205 | std::this_thread::sleep_for(std::chrono::seconds(10)); 206 | t.notify_one(); 207 | }); 208 | 209 | 210 | //should not trigger watchdog 211 | t.wait(); 212 | 213 | t1.join(); 214 | } 215 | 216 | TEST_CASE("test_std_condition_variable_signal_short_blocked_exit_0") 217 | { 218 | std::mutex m_signal_mutex; 219 | std::condition_variable m_signal_condition; 220 | bool m_notified=false; 221 | 222 | utils::event_t signal; 223 | 224 | std::thread t1([&]() 225 | { 226 | std::this_thread::sleep_for(std::chrono::seconds(2)); 227 | 228 | std::unique_lock lock(m_signal_mutex); 229 | std::this_thread::sleep_for(std::chrono::seconds(2)); 230 | 231 | m_notified=true; 232 | m_signal_condition.notify_one(); 233 | }); 234 | 235 | 236 | std::unique_lock lock(m_signal_mutex); 237 | m_signal_condition.wait(lock, [&] 238 | { 239 | return m_notified; 240 | }); 241 | 242 | 243 | t1.join(); 244 | } 245 | 246 | void test_thread_watchdog_many_too_long_durations_but_ignore_erros_func(std::atomic_uint32_t& p_counter) 247 | { 248 | cs_maker_t cs_maker; 249 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 250 | 251 | EnterCriticalSection(CriticalSectionA); 252 | 253 | const int random_sleep_time=random_helper::thread_safe_uniform_int(0, 4); 254 | std::this_thread::sleep_for(std::chrono::seconds(random_sleep_time)); 255 | p_counter+=random_sleep_time; 256 | 257 | LeaveCriticalSection(CriticalSectionA); 258 | } 259 | 260 | TEST_CASE("test_thread_watchdog_many_too_long_durations_but_ignore_errors") 261 | { 262 | #ifdef _WIN64 263 | const size_t thread_count=10000; 264 | #else 265 | const size_t thread_count=1000; 266 | #endif 267 | std::atomic_uint32_t counter{0}; 268 | 269 | std::vector threads; 270 | 271 | for(size_t i=0; i 26 | #include 27 | #include "utils/signal.hpp" 28 | #include 29 | 30 | //FIXME some variables could be optimized away in release? 31 | 32 | TEST_CASE("test_std_recursive_mutex_t1_a_unlock_double_bad_exit_66") 33 | { 34 | std::recursive_mutex ma; 35 | ma.lock(); 36 | ma.unlock(); 37 | ma.unlock(); 38 | } 39 | 40 | TEST_CASE("test_std_recursive_mutex_unlock_other_thread_exit_66") 41 | { 42 | std::recursive_mutex ma; 43 | 44 | ma.lock(); 45 | 46 | //leave in other thread 47 | std::thread t([&]() 48 | { 49 | ma.unlock(); 50 | }); 51 | 52 | t.join(); 53 | } 54 | 55 | TEST_CASE("test_std_recursive_mutex_t1_a_b_t2_b_a_overlap_exit_66") 56 | { 57 | std::recursive_mutex m_a; 58 | std::recursive_mutex m_b; 59 | 60 | utils::event_t signal_t1_cs_a; 61 | utils::event_t signal_t2_cs_b; 62 | 63 | std::thread t([&]() 64 | { 65 | m_a.lock(); 66 | signal_t1_cs_a.notify_one(); 67 | 68 | signal_t2_cs_b.wait(); 69 | m_b.lock(); 70 | 71 | 72 | m_b.unlock(); 73 | m_a.unlock(); 74 | }); 75 | 76 | std::thread t2([&]() 77 | { 78 | signal_t1_cs_a.wait(); 79 | 80 | m_b.lock(); 81 | signal_t2_cs_b.notify_one(); 82 | 83 | m_a.lock(); 84 | 85 | m_a.unlock(); 86 | m_b.unlock(); 87 | 88 | }); 89 | 90 | t.join(); 91 | t2.join(); 92 | } 93 | 94 | TEST_CASE("test_std_recursive_mutex_t1_a_b_t2_b_a_maybe_overlap_exit_66") 95 | { 96 | std::recursive_mutex m_a; 97 | std::recursive_mutex m_b; 98 | 99 | std::thread t([&]() 100 | { 101 | m_a.lock(); 102 | m_b.lock(); 103 | 104 | m_b.unlock(); 105 | m_a.unlock(); 106 | }); 107 | 108 | std::thread t2([&]() 109 | { 110 | m_b.lock(); 111 | m_a.lock(); 112 | 113 | m_a.unlock(); 114 | m_b.unlock(); 115 | 116 | }); 117 | 118 | t.join(); 119 | t2.join(); 120 | } 121 | 122 | TEST_CASE("test_std_recursive_mutex_t1_a_b_t2_b_a_no_overlap_exit_66") 123 | { 124 | std::recursive_mutex m_a; 125 | std::recursive_mutex m_b; 126 | 127 | utils::event_t signal; 128 | 129 | std::thread t([&]() 130 | { 131 | m_a.lock(); 132 | m_b.lock(); 133 | 134 | m_b.unlock(); 135 | m_a.unlock(); 136 | 137 | signal.notify_one(); 138 | 139 | }); 140 | 141 | std::thread t2([&]() 142 | { 143 | signal.wait(); 144 | 145 | m_b.lock(); 146 | m_a.lock(); 147 | 148 | m_a.unlock(); 149 | m_b.unlock(); 150 | }); 151 | 152 | t.join(); 153 | t2.join(); 154 | } 155 | 156 | TEST_CASE("test_std_recursive_mutex_t1_a_b_t2_b_and_more_exit_66") 157 | { 158 | std::recursive_mutex m_a; 159 | std::recursive_mutex m_b; 160 | std::recursive_mutex m_c; 161 | std::recursive_mutex m_d; 162 | std::recursive_mutex m_e; 163 | 164 | utils::event_t block_thread; 165 | 166 | std::thread t1([&]() 167 | { 168 | m_a.lock(); 169 | m_b.lock(); 170 | 171 | m_b.unlock(); 172 | m_a.unlock(); 173 | }); 174 | 175 | std::thread t2([&]() 176 | { 177 | m_b.lock(); 178 | m_c.lock(); 179 | 180 | m_c.unlock(); 181 | m_b.unlock(); 182 | }); 183 | 184 | std::thread t3([&]() 185 | { 186 | m_c.lock(); 187 | m_d.lock(); 188 | 189 | m_d.unlock(); 190 | m_c.unlock(); 191 | }); 192 | 193 | std::thread t4([&]() 194 | { 195 | m_d.lock(); 196 | m_e.lock(); 197 | 198 | m_e.unlock(); 199 | m_d.unlock(); 200 | }); 201 | 202 | std::thread t5([&]() 203 | { 204 | m_e.lock(); 205 | m_a.lock(); 206 | 207 | m_a.unlock(); 208 | m_e.unlock(); 209 | }); 210 | 211 | t1.join(); 212 | t2.join(); 213 | t3.join(); 214 | t4.join(); 215 | t5.join(); 216 | } 217 | 218 | void test_bad_cs_100t_10cs_bulk_thread_function(std::vector& p_critical_sections, std::atomic_uint32_t& p_counter) 219 | { 220 | for(size_t i=0; i<10; ++i) 221 | { 222 | for(auto*& cs:p_critical_sections) 223 | { 224 | cs->lock(); 225 | } 226 | 227 | p_counter++; 228 | 229 | for(auto*& cs:p_critical_sections) 230 | { 231 | cs->unlock(); 232 | } 233 | } 234 | } 235 | 236 | TEST_CASE("test_std_recursive_mutex_100t_10_m_bulk_exit_66") 237 | { 238 | const size_t cs_count=20; 239 | const size_t thread_count=1000; 240 | std::atomic_uint32_t counter{0}; 241 | 242 | std::vector critical_sections; 243 | critical_sections.resize(cs_count); 244 | 245 | for(auto*& cs:critical_sections) 246 | { 247 | cs=new std::recursive_mutex(); //i know.. 248 | } 249 | 250 | std::vector critical_sections_wrong_order=critical_sections; 251 | 252 | std::random_device rd; 253 | std::mt19937 g(rd()); 254 | 255 | std::shuffle(critical_sections_wrong_order.begin(), critical_sections_wrong_order.end(), g); 256 | 257 | std::vector threads; 258 | 259 | for(size_t i=0; i lock_a(m_a); 285 | const std::lock_guard < std::recursive_mutex> lock_b(m_b); 286 | }); 287 | 288 | std::thread t2([&]() 289 | { 290 | const std::lock_guard < std::recursive_mutex> lock_b(m_b); 291 | const std::lock_guard < std::recursive_mutex> lock_a(m_a); 292 | }); 293 | 294 | t1.join(); 295 | t2.join(); 296 | } 297 | 298 | TEST_CASE("test_lock_guard_recursive__t1_a_b_t2_b_a_maybe_overlap_exit_66") 299 | { 300 | std::recursive_mutex m_a; 301 | std::recursive_mutex m_b; 302 | 303 | std::thread t1([&]() 304 | { 305 | const std::lock_guard < std::recursive_mutex> lock_a(m_a); 306 | const std::lock_guard < std::recursive_mutex> lock_b(m_b); 307 | }); 308 | 309 | std::thread t2([&]() 310 | { 311 | const std::lock_guard < std::recursive_mutex> lock_b(m_b); 312 | const std::lock_guard < std::recursive_mutex> lock_a(m_a); 313 | }); 314 | 315 | t1.join(); 316 | t2.join(); 317 | } 318 | 319 | TEST_CASE("test_std_recursive_mutex_delete_before_unlock_exit_66") 320 | { 321 | { 322 | std::recursive_mutex m; 323 | m.lock(); 324 | } 325 | } 326 | 327 | -------------------------------------------------------------------------------- /cppguard_tests/test_thread_watchdog_bad.cpp: -------------------------------------------------------------------------------- 1 | // This is an open source non-commercial project. Dear PVS-Studio, please check it. 2 | 3 | // PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http://www.viva64.com 4 | 5 | //Licensed to the Apache Software Foundation (ASF) under one 6 | //or more contributor license agreements. See the NOTICE file 7 | //distributed with this work for additional information 8 | //regarding copyright ownership. The ASF licenses this file 9 | //to you under the Apache License, Version 2.0 (the 10 | //"License"); you may not use this file except in compliance 11 | //with the License. You may obtain a copy of the License at 12 | // 13 | // http://www.apache.org/licenses/LICENSE-2.0 14 | // 15 | //Unless required by applicable law or agreed to in writing, 16 | //software distributed under the License is distributed on an 17 | //"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18 | //KIND, either express or implied. See the License for the 19 | //specific language governing permissions and limitations 20 | //under the License. 21 | 22 | #include "doctest.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include "utils/cs.hpp" 28 | #include "utils/signal.hpp" 29 | 30 | #if _WIN32 31 | #include 32 | #endif 33 | 34 | #ifdef __linux__ 35 | #include "cppguard.h" 36 | #include "critical_section_linux.hpp" 37 | #endif 38 | 39 | //FIXME some variables could be optimized away in release? 40 | 41 | TEST_CASE("test_cs_t1_duration_6_sec_bad_exit_66") 42 | { 43 | cs_maker_t cs_maker; 44 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 45 | 46 | EnterCriticalSection(CriticalSectionA); 47 | 48 | std::this_thread::sleep_for(std::chrono::seconds(6)); 49 | 50 | LeaveCriticalSection(CriticalSectionA); 51 | } 52 | 53 | TEST_CASE("test_cs_t1_missing_leave_bad_exit_66") 54 | { 55 | cs_maker_t cs_maker; 56 | 57 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 58 | EnterCriticalSection(CriticalSectionA); 59 | 60 | std::this_thread::sleep_for(std::chrono::hours(2)); 61 | } 62 | 63 | TEST_CASE("test_cs_2t_misssing_leave_bad_exit_66") 64 | { 65 | cs_maker_t cs_maker; 66 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 67 | 68 | EnterCriticalSection(CriticalSectionA); 69 | 70 | //leave in other thread 71 | std::thread t([&]() 72 | { 73 | EnterCriticalSection(CriticalSectionA); 74 | }); 75 | 76 | t.join(); 77 | } 78 | 79 | TEST_CASE("test_ok_cs_t1_40_bad_exit_66") 80 | { 81 | 82 | const size_t cs_count=20; 83 | 84 | std::vector critical_sections; 85 | critical_sections.resize(cs_count); 86 | 87 | for(auto& cs:critical_sections) 88 | { 89 | InitializeCriticalSection(&cs); 90 | } 91 | 92 | for(auto& cs:critical_sections) 93 | { 94 | EnterCriticalSection(&cs); 95 | } 96 | 97 | std::this_thread::sleep_for(std::chrono::seconds(100)); 98 | for(auto& cs:critical_sections) 99 | { 100 | DeleteCriticalSection(&cs); 101 | } 102 | 103 | } 104 | 105 | TEST_CASE("test_cs_t1_too_long_duration_exit_66") 106 | { 107 | cs_maker_t cs_maker; 108 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 109 | 110 | EnterCriticalSection(CriticalSectionA); 111 | 112 | std::this_thread::sleep_for(std::chrono::hours(2)); 113 | 114 | } 115 | 116 | TEST_CASE("test_cs_misssing_leave_n_exit_66") 117 | { 118 | cs_maker_t cs_maker; 119 | 120 | CRITICAL_SECTION* CriticalSectionA=cs_maker.make_scoped(); 121 | CRITICAL_SECTION* CriticalSectionB=cs_maker.make_scoped(); 122 | CRITICAL_SECTION* CriticalSectionC=cs_maker.make_scoped(); 123 | CRITICAL_SECTION* CriticalSectionD=cs_maker.make_scoped(); 124 | CRITICAL_SECTION* CriticalSectionE=cs_maker.make_scoped(); 125 | 126 | utils::event_t signal_t1_cs_a; 127 | 128 | EnterCriticalSection(CriticalSectionA); 129 | 130 | //leave in other thread 131 | std::thread t([&]() 132 | { 133 | signal_t1_cs_a.notify_one(); 134 | EnterCriticalSection(CriticalSectionA); 135 | }); 136 | 137 | signal_t1_cs_a.wait(); 138 | 139 | 140 | EnterCriticalSection(CriticalSectionB); 141 | EnterCriticalSection(CriticalSectionC); 142 | EnterCriticalSection(CriticalSectionD); 143 | EnterCriticalSection(CriticalSectionE); 144 | 145 | LeaveCriticalSection(CriticalSectionE); 146 | LeaveCriticalSection(CriticalSectionD); 147 | LeaveCriticalSection(CriticalSectionC); 148 | LeaveCriticalSection(CriticalSectionB); 149 | //missing: LeaveCriticalSection(&CriticalSectionB); 150 | 151 | t.join(); 152 | } 153 | 154 | void test_thread_watchdog_cs_100t_10cs_bulk_thread_function_bad(std::vector& p_critical_sections, std::atomic_uint32_t& p_counter) 155 | { 156 | for(size_t i=0; i<10; ++i) 157 | { 158 | for(auto*& cs:p_critical_sections) 159 | { 160 | EnterCriticalSection(cs); 161 | } 162 | 163 | //std::this_thread::sleep_for(std::chrono::milliseconds(1)); 164 | 165 | p_counter++; 166 | if(p_counter==900) 167 | { 168 | std::this_thread::sleep_for(std::chrono::seconds(20)); 169 | } 170 | 171 | for(auto*& cs:p_critical_sections) 172 | { 173 | LeaveCriticalSection(cs); 174 | } 175 | } 176 | } 177 | 178 | TEST_CASE("test_cs_multi_thread_bad_exit_66") 179 | { 180 | const size_t cs_count=20; 181 | const size_t thread_count=1000; 182 | std::atomic_uint32_t counter{0}; 183 | 184 | std::vector critical_sections; 185 | critical_sections.resize(cs_count); 186 | 187 | for(auto*& cs:critical_sections) 188 | { 189 | cs=new CRITICAL_SECTION; 190 | InitializeCriticalSection(cs); 191 | } 192 | 193 | std::vector threads; 194 | 195 | for(size_t i=0; i lock(m); 226 | std::this_thread::sleep_for(std::chrono::seconds(6)); 227 | } 228 | 229 | 230 | TEST_CASE("test_std_condition_variable_signal_blocked_too_long_exit_66") 231 | { 232 | std::mutex m_signal_mutex; 233 | std::condition_variable m_signal_condition; 234 | bool m_notified=false; 235 | 236 | utils::event_t signal; 237 | 238 | std::thread t1([&]() 239 | { 240 | std::this_thread::sleep_for(std::chrono::seconds(2)); 241 | 242 | //waits too long in mutex 243 | std::unique_lock lock(m_signal_mutex); 244 | std::this_thread::sleep_for(std::chrono::seconds(8)); 245 | 246 | m_notified=true; 247 | m_signal_condition.notify_one(); 248 | }); 249 | 250 | 251 | std::unique_lock lock(m_signal_mutex); 252 | m_signal_condition.wait(lock, [&] 253 | { 254 | return m_notified; 255 | }); 256 | 257 | 258 | t1.join(); 259 | } 260 | 261 | void test_cs_too_many_waiters_func(CRITICAL_SECTION& p_critical_sections, std::atomic_uint32_t& p_counter) 262 | { 263 | EnterCriticalSection(&p_critical_sections); 264 | 265 | std::this_thread::sleep_for(std::chrono::milliseconds(100)); 266 | p_counter++; 267 | 268 | LeaveCriticalSection(&p_critical_sections); 269 | } 270 | 271 | TEST_CASE("test_cs_too_many_waiters_exit_66") 272 | { 273 | const size_t thread_count=1000; 274 | std::atomic_uint32_t counter{0}; 275 | 276 | CRITICAL_SECTION cs; 277 | InitializeCriticalSection(&cs); 278 | 279 | 280 | std::vector threads; 281 | 282 | for(size_t i=0; i // std::reference_wrapper 12 | #include // std::unique_ptr 13 | #include 14 | 15 | #include "core.h" 16 | 17 | FMT_BEGIN_NAMESPACE 18 | 19 | namespace detail { 20 | 21 | template struct is_reference_wrapper : std::false_type {}; 22 | template 23 | struct is_reference_wrapper> : std::true_type {}; 24 | 25 | template const T& unwrap(const T& v) { return v; } 26 | template const T& unwrap(const std::reference_wrapper& v) { 27 | return static_cast(v); 28 | } 29 | 30 | class dynamic_arg_list { 31 | // Workaround for clang's -Wweak-vtables. Unlike for regular classes, for 32 | // templates it doesn't complain about inability to deduce single translation 33 | // unit for placing vtable. So storage_node_base is made a fake template. 34 | template struct node { 35 | virtual ~node() = default; 36 | std::unique_ptr> next; 37 | }; 38 | 39 | template struct typed_node : node<> { 40 | T value; 41 | 42 | template 43 | FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {} 44 | 45 | template 46 | FMT_CONSTEXPR typed_node(const basic_string_view& arg) 47 | : value(arg.data(), arg.size()) {} 48 | }; 49 | 50 | std::unique_ptr> head_; 51 | 52 | public: 53 | template const T& push(const Arg& arg) { 54 | auto new_node = std::unique_ptr>(new typed_node(arg)); 55 | auto& value = new_node->value; 56 | new_node->next = std::move(head_); 57 | head_ = std::move(new_node); 58 | return value; 59 | } 60 | }; 61 | } // namespace detail 62 | 63 | /** 64 | \rst 65 | A dynamic version of `fmt::format_arg_store`. 66 | It's equipped with a storage to potentially temporary objects which lifetimes 67 | could be shorter than the format arguments object. 68 | 69 | It can be implicitly converted into `~fmt::basic_format_args` for passing 70 | into type-erased formatting functions such as `~fmt::vformat`. 71 | \endrst 72 | */ 73 | template 74 | class dynamic_format_arg_store 75 | #if FMT_GCC_VERSION && FMT_GCC_VERSION < 409 76 | // Workaround a GCC template argument substitution bug. 77 | : public basic_format_args 78 | #endif 79 | { 80 | private: 81 | using char_type = typename Context::char_type; 82 | 83 | template struct need_copy { 84 | static constexpr detail::type mapped_type = 85 | detail::mapped_type_constant::value; 86 | 87 | enum { 88 | value = !(detail::is_reference_wrapper::value || 89 | std::is_same>::value || 90 | std::is_same>::value || 91 | (mapped_type != detail::type::cstring_type && 92 | mapped_type != detail::type::string_type && 93 | mapped_type != detail::type::custom_type)) 94 | }; 95 | }; 96 | 97 | template 98 | using stored_type = conditional_t< 99 | std::is_convertible>::value && 100 | !detail::is_reference_wrapper::value, 101 | std::basic_string, T>; 102 | 103 | // Storage of basic_format_arg must be contiguous. 104 | std::vector> data_; 105 | std::vector> named_info_; 106 | 107 | // Storage of arguments not fitting into basic_format_arg must grow 108 | // without relocation because items in data_ refer to it. 109 | detail::dynamic_arg_list dynamic_args_; 110 | 111 | friend class basic_format_args; 112 | 113 | unsigned long long get_types() const { 114 | return detail::is_unpacked_bit | data_.size() | 115 | (named_info_.empty() 116 | ? 0ULL 117 | : static_cast(detail::has_named_args_bit)); 118 | } 119 | 120 | const basic_format_arg* data() const { 121 | return named_info_.empty() ? data_.data() : data_.data() + 1; 122 | } 123 | 124 | template void emplace_arg(const T& arg) { 125 | data_.emplace_back(detail::make_arg(arg)); 126 | } 127 | 128 | template 129 | void emplace_arg(const detail::named_arg& arg) { 130 | if (named_info_.empty()) { 131 | constexpr const detail::named_arg_info* zero_ptr{nullptr}; 132 | data_.insert(data_.begin(), {zero_ptr, 0}); 133 | } 134 | data_.emplace_back(detail::make_arg(detail::unwrap(arg.value))); 135 | auto pop_one = [](std::vector>* data) { 136 | data->pop_back(); 137 | }; 138 | std::unique_ptr>, decltype(pop_one)> 139 | guard{&data_, pop_one}; 140 | named_info_.push_back({arg.name, static_cast(data_.size() - 2u)}); 141 | data_[0].value_.named_args = {named_info_.data(), named_info_.size()}; 142 | guard.release(); 143 | } 144 | 145 | public: 146 | constexpr dynamic_format_arg_store() = default; 147 | 148 | /** 149 | \rst 150 | Adds an argument into the dynamic store for later passing to a formatting 151 | function. 152 | 153 | Note that custom types and string types (but not string views) are copied 154 | into the store dynamically allocating memory if necessary. 155 | 156 | **Example**:: 157 | 158 | fmt::dynamic_format_arg_store store; 159 | store.push_back(42); 160 | store.push_back("abc"); 161 | store.push_back(1.5f); 162 | std::string result = fmt::vformat("{} and {} and {}", store); 163 | \endrst 164 | */ 165 | template void push_back(const T& arg) { 166 | if (detail::const_check(need_copy::value)) 167 | emplace_arg(dynamic_args_.push>(arg)); 168 | else 169 | emplace_arg(detail::unwrap(arg)); 170 | } 171 | 172 | /** 173 | \rst 174 | Adds a reference to the argument into the dynamic store for later passing to 175 | a formatting function. 176 | 177 | **Example**:: 178 | 179 | fmt::dynamic_format_arg_store store; 180 | char band[] = "Rolling Stones"; 181 | store.push_back(std::cref(band)); 182 | band[9] = 'c'; // Changing str affects the output. 183 | std::string result = fmt::vformat("{}", store); 184 | // result == "Rolling Scones" 185 | \endrst 186 | */ 187 | template void push_back(std::reference_wrapper arg) { 188 | static_assert( 189 | need_copy::value, 190 | "objects of built-in types and string views are always copied"); 191 | emplace_arg(arg.get()); 192 | } 193 | 194 | /** 195 | Adds named argument into the dynamic store for later passing to a formatting 196 | function. ``std::reference_wrapper`` is supported to avoid copying of the 197 | argument. The name is always copied into the store. 198 | */ 199 | template 200 | void push_back(const detail::named_arg& arg) { 201 | const char_type* arg_name = 202 | dynamic_args_.push>(arg.name).c_str(); 203 | if (detail::const_check(need_copy::value)) { 204 | emplace_arg( 205 | fmt::arg(arg_name, dynamic_args_.push>(arg.value))); 206 | } else { 207 | emplace_arg(fmt::arg(arg_name, arg.value)); 208 | } 209 | } 210 | 211 | /** Erase all elements from the store */ 212 | void clear() { 213 | data_.clear(); 214 | named_info_.clear(); 215 | dynamic_args_ = detail::dynamic_arg_list(); 216 | } 217 | 218 | /** 219 | \rst 220 | Reserves space to store at least *new_cap* arguments including 221 | *new_cap_named* named arguments. 222 | \endrst 223 | */ 224 | void reserve(size_t new_cap, size_t new_cap_named) { 225 | FMT_ASSERT(new_cap >= new_cap_named, 226 | "Set of arguments includes set of named arguments"); 227 | data_.reserve(new_cap); 228 | named_info_.reserve(new_cap_named); 229 | } 230 | }; 231 | 232 | FMT_END_NAMESPACE 233 | 234 | #endif // FMT_ARGS_H_ 235 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Ww][Ii][Nn]32/ 27 | [Aa][Rr][Mm]/ 28 | [Aa][Rr][Mm]64/ 29 | bld/ 30 | [Bb]in/ 31 | [Oo]bj/ 32 | [Ll]og/ 33 | [Ll]ogs/ 34 | 35 | # Visual Studio 2015/2017 cache/options directory 36 | .vs/ 37 | # Uncomment if you have tasks that create the project's static files in wwwroot 38 | #wwwroot/ 39 | 40 | # Visual Studio 2017 auto generated files 41 | Generated\ Files/ 42 | 43 | # MSTest test Results 44 | [Tt]est[Rr]esult*/ 45 | [Bb]uild[Ll]og.* 46 | 47 | # NUnit 48 | *.VisualState.xml 49 | TestResult.xml 50 | nunit-*.xml 51 | 52 | # Build Results of an ATL Project 53 | [Dd]ebugPS/ 54 | [Rr]eleasePS/ 55 | dlldata.c 56 | 57 | # Benchmark Results 58 | BenchmarkDotNet.Artifacts/ 59 | 60 | # .NET Core 61 | project.lock.json 62 | project.fragment.lock.json 63 | artifacts/ 64 | 65 | # ASP.NET Scaffolding 66 | ScaffoldingReadMe.txt 67 | 68 | # StyleCop 69 | StyleCopReport.xml 70 | 71 | # Files built by Visual Studio 72 | *_i.c 73 | *_p.c 74 | *_h.h 75 | *.ilk 76 | *.meta 77 | *.obj 78 | *.iobj 79 | *.pch 80 | *.pdb 81 | *.ipdb 82 | *.pgc 83 | *.pgd 84 | *.rsp 85 | *.sbr 86 | *.tlb 87 | *.tli 88 | *.tlh 89 | *.tmp 90 | *.tmp_proj 91 | *_wpftmp.csproj 92 | *.log 93 | *.tlog 94 | *.vspscc 95 | *.vssscc 96 | .builds 97 | *.pidb 98 | *.svclog 99 | *.scc 100 | 101 | # Chutzpah Test files 102 | _Chutzpah* 103 | 104 | # Visual C++ cache files 105 | ipch/ 106 | *.aps 107 | *.ncb 108 | *.opendb 109 | *.opensdf 110 | *.sdf 111 | *.cachefile 112 | *.VC.db 113 | *.VC.VC.opendb 114 | 115 | # Visual Studio profiler 116 | *.psess 117 | *.vsp 118 | *.vspx 119 | *.sap 120 | 121 | # Visual Studio Trace Files 122 | *.e2e 123 | 124 | # TFS 2012 Local Workspace 125 | $tf/ 126 | 127 | # Guidance Automation Toolkit 128 | *.gpState 129 | 130 | # ReSharper is a .NET coding add-in 131 | _ReSharper*/ 132 | *.[Rr]e[Ss]harper 133 | *.DotSettings.user 134 | 135 | # TeamCity is a build add-in 136 | _TeamCity* 137 | 138 | # DotCover is a Code Coverage Tool 139 | *.dotCover 140 | 141 | # AxoCover is a Code Coverage Tool 142 | .axoCover/* 143 | !.axoCover/settings.json 144 | 145 | # Coverlet is a free, cross platform Code Coverage Tool 146 | coverage*.json 147 | coverage*.xml 148 | coverage*.info 149 | 150 | # Visual Studio code coverage results 151 | *.coverage 152 | *.coveragexml 153 | 154 | # NCrunch 155 | _NCrunch_* 156 | .*crunch*.local.xml 157 | nCrunchTemp_* 158 | 159 | # MightyMoose 160 | *.mm.* 161 | AutoTest.Net/ 162 | 163 | # Web workbench (sass) 164 | .sass-cache/ 165 | 166 | # Installshield output folder 167 | [Ee]xpress/ 168 | 169 | # DocProject is a documentation generator add-in 170 | DocProject/buildhelp/ 171 | DocProject/Help/*.HxT 172 | DocProject/Help/*.HxC 173 | DocProject/Help/*.hhc 174 | DocProject/Help/*.hhk 175 | DocProject/Help/*.hhp 176 | DocProject/Help/Html2 177 | DocProject/Help/html 178 | 179 | # Click-Once directory 180 | publish/ 181 | 182 | # Publish Web Output 183 | *.[Pp]ublish.xml 184 | *.azurePubxml 185 | # Note: Comment the next line if you want to checkin your web deploy settings, 186 | # but database connection strings (with potential passwords) will be unencrypted 187 | *.pubxml 188 | *.publishproj 189 | 190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 191 | # checkin your Azure Web App publish settings, but sensitive information contained 192 | # in these scripts will be unencrypted 193 | PublishScripts/ 194 | 195 | # NuGet Packages 196 | *.nupkg 197 | # NuGet Symbol Packages 198 | *.snupkg 199 | # The packages folder can be ignored because of Package Restore 200 | **/[Pp]ackages/* 201 | # except build/, which is used as an MSBuild target. 202 | !**/[Pp]ackages/build/ 203 | # Uncomment if necessary however generally it will be regenerated when needed 204 | #!**/[Pp]ackages/repositories.config 205 | # NuGet v3's project.json files produces more ignorable files 206 | *.nuget.props 207 | *.nuget.targets 208 | 209 | # Microsoft Azure Build Output 210 | csx/ 211 | *.build.csdef 212 | 213 | # Microsoft Azure Emulator 214 | ecf/ 215 | rcf/ 216 | 217 | # Windows Store app package directories and files 218 | AppPackages/ 219 | BundleArtifacts/ 220 | Package.StoreAssociation.xml 221 | _pkginfo.txt 222 | *.appx 223 | *.appxbundle 224 | *.appxupload 225 | 226 | # Visual Studio cache files 227 | # files ending in .cache can be ignored 228 | *.[Cc]ache 229 | # but keep track of directories ending in .cache 230 | !?*.[Cc]ache/ 231 | 232 | # Others 233 | ClientBin/ 234 | ~$* 235 | *~ 236 | *.dbmdl 237 | *.dbproj.schemaview 238 | *.jfm 239 | *.pfx 240 | *.publishsettings 241 | orleans.codegen.cs 242 | 243 | # Including strong name files can present a security risk 244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 245 | #*.snk 246 | 247 | # Since there are multiple workflows, uncomment next line to ignore bower_components 248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 249 | #bower_components/ 250 | 251 | # RIA/Silverlight projects 252 | Generated_Code/ 253 | 254 | # Backup & report files from converting an old project file 255 | # to a newer Visual Studio version. Backup files are not needed, 256 | # because we have git ;-) 257 | _UpgradeReport_Files/ 258 | Backup*/ 259 | UpgradeLog*.XML 260 | UpgradeLog*.htm 261 | ServiceFabricBackup/ 262 | *.rptproj.bak 263 | 264 | # SQL Server files 265 | *.mdf 266 | *.ldf 267 | *.ndf 268 | 269 | # Business Intelligence projects 270 | *.rdl.data 271 | *.bim.layout 272 | *.bim_*.settings 273 | *.rptproj.rsuser 274 | *- [Bb]ackup.rdl 275 | *- [Bb]ackup ([0-9]).rdl 276 | *- [Bb]ackup ([0-9][0-9]).rdl 277 | 278 | # Microsoft Fakes 279 | FakesAssemblies/ 280 | 281 | # GhostDoc plugin setting file 282 | *.GhostDoc.xml 283 | 284 | # Node.js Tools for Visual Studio 285 | .ntvs_analysis.dat 286 | node_modules/ 287 | 288 | # Visual Studio 6 build log 289 | *.plg 290 | 291 | # Visual Studio 6 workspace options file 292 | *.opt 293 | 294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 295 | *.vbw 296 | 297 | # Visual Studio 6 auto-generated project file (contains which files were open etc.) 298 | *.vbp 299 | 300 | # Visual Studio 6 workspace and project file (working project files containing files to include in project) 301 | *.dsw 302 | *.dsp 303 | 304 | # Visual Studio 6 technical files 305 | *.ncb 306 | *.aps 307 | 308 | # Visual Studio LightSwitch build output 309 | **/*.HTMLClient/GeneratedArtifacts 310 | **/*.DesktopClient/GeneratedArtifacts 311 | **/*.DesktopClient/ModelManifest.xml 312 | **/*.Server/GeneratedArtifacts 313 | **/*.Server/ModelManifest.xml 314 | _Pvt_Extensions 315 | 316 | # Paket dependency manager 317 | .paket/paket.exe 318 | paket-files/ 319 | 320 | # FAKE - F# Make 321 | .fake/ 322 | 323 | # CodeRush personal settings 324 | .cr/personal 325 | 326 | # Python Tools for Visual Studio (PTVS) 327 | __pycache__/ 328 | *.pyc 329 | 330 | # Cake - Uncomment if you are using it 331 | # tools/** 332 | # !tools/packages.config 333 | 334 | # Tabs Studio 335 | *.tss 336 | 337 | # Telerik's JustMock configuration file 338 | *.jmconfig 339 | 340 | # BizTalk build output 341 | *.btp.cs 342 | *.btm.cs 343 | *.odx.cs 344 | *.xsd.cs 345 | 346 | # OpenCover UI analysis results 347 | OpenCover/ 348 | 349 | # Azure Stream Analytics local run output 350 | ASALocalRun/ 351 | 352 | # MSBuild Binary and Structured Log 353 | *.binlog 354 | 355 | # NVidia Nsight GPU debugger configuration file 356 | *.nvuser 357 | 358 | # MFractors (Xamarin productivity tool) working folder 359 | .mfractor/ 360 | 361 | # Local History for Visual Studio 362 | .localhistory/ 363 | 364 | # Visual Studio History (VSHistory) files 365 | .vshistory/ 366 | 367 | # BeatPulse healthcheck temp database 368 | healthchecksdb 369 | 370 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 371 | MigrationBackup/ 372 | 373 | # Ionide (cross platform F# VS Code tools) working folder 374 | .ionide/ 375 | 376 | # Fody - auto-generated XML schema 377 | FodyWeavers.xsd 378 | 379 | # VS Code files for those working on multiple tools 380 | .vscode/* 381 | !.vscode/settings.json 382 | !.vscode/tasks.json 383 | !.vscode/launch.json 384 | !.vscode/extensions.json 385 | *.code-workspace 386 | 387 | # Local History for Visual Studio Code 388 | .history/ 389 | 390 | # Windows Installer files from build outputs 391 | *.cab 392 | *.msi 393 | *.msix 394 | *.msm 395 | *.msp 396 | 397 | # JetBrains Rider 398 | *.sln.iml 399 | 400 | 401 | build/ 402 | out/ 403 | --------------------------------------------------------------------------------