├── fuzzer ├── crashes │ ├── crash-heuristics │ └── crash-invalid-write-shared-gc-ptr ├── corpus.zip ├── include │ └── fuzzer │ │ ├── crc_calculator.hpp │ │ ├── fuzzer.h │ │ └── tokenizer.hpp ├── CMakeLists.txt └── src │ ├── tokenizer.cpp │ └── fuzzer_main.cpp ├── libmemplusplus ├── src │ ├── shared_gcptr.cpp │ ├── config.hpp.in │ ├── utils │ │ ├── log.cpp │ │ ├── timer.cpp │ │ ├── profiler_timer.cpp │ │ ├── env_options.cpp │ │ ├── profiler.cpp │ │ ├── utils.cpp │ │ └── statistics.cpp │ ├── heuristics │ │ └── heuristics.cpp │ └── containers │ │ ├── node.cpp │ │ ├── vertex.cpp │ │ ├── visualizers │ │ └── chunk_treap_visualizer.cpp │ │ └── chunk_treap.cpp ├── include │ └── mpplib │ │ ├── config.hpp │ │ ├── mpp.hpp │ │ ├── utils │ │ ├── profiler_definitions.hpp │ │ ├── colours.hpp │ │ ├── profiler_timer.hpp │ │ ├── profiler.hpp │ │ ├── env_options.hpp │ │ ├── timer.hpp │ │ ├── random.hpp │ │ ├── utils.hpp │ │ ├── log.hpp │ │ ├── macros.hpp │ │ └── statistics.hpp │ │ ├── gcptr.hpp │ │ ├── containers │ │ ├── node.hpp │ │ ├── vertex.hpp │ │ └── chunk_treap.hpp │ │ ├── exception.hpp │ │ ├── heuristics │ │ └── heuristics.hpp │ │ ├── gc.hpp │ │ ├── chunk.hpp │ │ └── shared_gcptr.hpp └── CMakeLists.txt ├── additional_info └── images │ ├── profiling.png │ ├── stats-runtime.png │ ├── linked_list_types.png │ └── treap.svg ├── .gitmodules ├── run-fuzzer.sh ├── tests ├── test_main.cpp ├── gtest_fixtures.hpp ├── CMakeLists.txt ├── security_tests.cpp ├── asan_tests.cpp └── chunk_treap_test.cpp ├── run_codechecker.sh ├── cmake ├── FindSphinx.cmake ├── FindValgrind.cmake └── clang-cxx-dev-tools.cmake ├── run-valgrind.sh ├── .github ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── pull_request_template.md └── workflows │ ├── benchmarking-mpp.yml │ ├── cpp_ci.yml │ └── codeql.yml ├── example_project ├── CMakeLists.txt └── src │ └── main.cpp ├── docs ├── make.bat ├── index.rst ├── conf.py └── CMakeLists.txt ├── mempp.code-workspace ├── CMakeLists.txt └── .clang-format /fuzzer/crashes/crash-heuristics: -------------------------------------------------------------------------------- 1 | CCEEGG -------------------------------------------------------------------------------- /fuzzer/corpus.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4drat/memplusplus/HEAD/fuzzer/corpus.zip -------------------------------------------------------------------------------- /fuzzer/crashes/crash-invalid-write-shared-gc-ptr: -------------------------------------------------------------------------------- 1 | CCCEEEGCE -------------------------------------------------------------------------------- /libmemplusplus/src/shared_gcptr.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/shared_gcptr.hpp" 2 | 3 | namespace mpp { 4 | } -------------------------------------------------------------------------------- /additional_info/images/profiling.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4drat/memplusplus/HEAD/additional_info/images/profiling.png -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libraries/googletest"] 2 | path = libraries/googletest 3 | url = https://github.com/google/googletest 4 | -------------------------------------------------------------------------------- /additional_info/images/stats-runtime.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4drat/memplusplus/HEAD/additional_info/images/stats-runtime.png -------------------------------------------------------------------------------- /additional_info/images/linked_list_types.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m4drat/memplusplus/HEAD/additional_info/images/linked_list_types.png -------------------------------------------------------------------------------- /run-fuzzer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ASAN_OPTIONS=detect_leaks=0 ./build/fuzzer/fuzzer ./fuzzer/corpus 4 | # ./build/fuzzer/fuzzer ./fuzzer/corpus 5 | -------------------------------------------------------------------------------- /tests/test_main.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | int main(int argc, char** argv) { 4 | ::testing::InitGoogleTest(&argc, argv); 5 | return RUN_ALL_TESTS(); 6 | } -------------------------------------------------------------------------------- /run_codechecker.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CodeChecker server --workspace ./codechecker-ws & 4 | CodeChecker store .codechecker --name mem++ --url http://localhost:8001/Default 5 | -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/config.hpp: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_GUARD 2 | #define INCLUDE_GUARD 3 | 4 | #define PROJECT_NAME "mpp" 5 | #define PROJECT_VER "0.3.5" 6 | #define PROJECT_VER_MAJOR "0" 7 | #define PROJECT_VER_MINOR "3" 8 | #define PROJECT_VER_PATCH "5" 9 | 10 | #endif // INCLUDE_GUARD 11 | -------------------------------------------------------------------------------- /libmemplusplus/src/config.hpp.in: -------------------------------------------------------------------------------- 1 | #ifndef INCLUDE_GUARD 2 | #define INCLUDE_GUARD 3 | 4 | #define PROJECT_NAME "@PROJECT_NAME@" 5 | #define PROJECT_VER "@MPP_VERSION@" 6 | #define PROJECT_VER_MAJOR "@VERSION_MAJOR@" 7 | #define PROJECT_VER_MINOR "@VERSION_MINOR@" 8 | #define PROJECT_VER_PATCH "@VERSION_PATCH@" 9 | 10 | #endif // INCLUDE_GUARD -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/mpp.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Smart pointers 4 | #include "mpplib/shared_gcptr.hpp" 5 | 6 | // GC part 7 | #include "mpplib/gc.hpp" 8 | 9 | // Memory allocator part 10 | #include "mpplib/memory_manager.hpp" 11 | 12 | /** @mainpage Memplusplus 13 | * 14 | * This project implements memory allocator with smart garbage collector. 15 | */ -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/profiler_definitions.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/profiler_timer.hpp" 4 | 5 | #if MPP_PROFILE == 1 6 | #define PROFILE_SCOPE(name) mpp::utils::profile::ProfilerTimer timer##__LINE__(name) 7 | #define PROFILE_FUNCTION() PROFILE_SCOPE(__PRETTY_FUNCTION__) 8 | #else 9 | #define PROFILE_SCOPE(name) 10 | #define PROFILE_FUNCTION() 11 | #endif -------------------------------------------------------------------------------- /cmake/FindSphinx.cmake: -------------------------------------------------------------------------------- 1 | #Look for an executable called sphinx-build 2 | find_program(SPHINX_EXECUTABLE 3 | NAMES sphinx-build 4 | DOC "Path to sphinx-build executable") 5 | 6 | include(FindPackageHandleStandardArgs) 7 | #Handle standard arguments to find_package like REQUIRED and QUIET 8 | find_package_handle_standard_args(Sphinx 9 | "Failed to find sphinx-build executable" 10 | SPHINX_EXECUTABLE) -------------------------------------------------------------------------------- /run-valgrind.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | valgrind --leak-check=full \ 4 | --show-leak-kinds=all \ 5 | --track-origins=yes \ 6 | --verbose \ 7 | --log-file=valgrind-out-example.log \ 8 | ./build/example_project/example_project-d 9 | 10 | valgrind --leak-check=full \ 11 | --show-leak-kinds=all \ 12 | --track-origins=yes \ 13 | --verbose \ 14 | --log-file=valgrind-out-tests.log \ 15 | ./build/tests/unit_tests 16 | -------------------------------------------------------------------------------- /libmemplusplus/src/utils/log.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/log.hpp" 2 | #include 3 | 4 | namespace mpp { namespace logging { 5 | LogLevel LogLevelFromString(const std::string& logLevel) 6 | { 7 | std::string logLevelStr = logLevel; 8 | std::transform(logLevelStr.begin(), logLevelStr.end(), logLevelStr.begin(), ::toupper); 9 | 10 | if (s_StringToLogLevel.find(logLevelStr) != s_StringToLogLevel.end()) 11 | return s_StringToLogLevel.at(logLevelStr); 12 | 13 | return LogLevel::FATAL; 14 | } 15 | }} -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | ### Is your feature request related to a problem? Please describe. 8 | 9 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 10 | 11 | ### Describe the solution you'd like 12 | 13 | A clear and concise description of what you want to happen. 14 | 15 | ### Describe alternatives you've considered 16 | 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | ### Additional context 20 | 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /tests/gtest_fixtures.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "gtest/gtest.h" 4 | 5 | #include "mpplib/memory_manager.hpp" 6 | 7 | class MppTestBase : public ::testing::Test 8 | { 9 | protected: 10 | void SetUp() override 11 | { 12 | mpp::g_memoryManager = std::make_unique(); 13 | } 14 | 15 | void TearDown() override 16 | { 17 | mpp::g_memoryManager.reset(); 18 | } 19 | }; 20 | 21 | class AllocatorTest : public MppTestBase 22 | {}; 23 | 24 | class SharedGcPtrTest : public MppTestBase 25 | {}; 26 | 27 | class SharedGcArrayTests : public MppTestBase 28 | {}; 29 | 30 | class GcTest : public MppTestBase 31 | {}; 32 | 33 | class AsanTest : public MppTestBase 34 | {}; -------------------------------------------------------------------------------- /example_project/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(example_project) 2 | 3 | add_executable(${PROJECT_NAME} src/main.cpp) 4 | 5 | target_include_directories(${PROJECT_NAME} 6 | PUBLIC ${PROJECT_SOURCE_DIR}/include 7 | ) 8 | 9 | set_target_properties(${PROJECT_NAME} 10 | PROPERTIES 11 | DEBUG_POSTFIX "-${STATIC_POSTFIX}d" 12 | RELEASE_POSTFIX "-${STATIC_POSTFIX}r" 13 | MINSIZEREL_POSTFIX "-${STATIC_POSTFIX}mr" 14 | RELWITHDEBINFO_POSTFIX "-${STATIC_POSTFIX}rd" 15 | ) 16 | 17 | if(MPP_SANITIZERS MATCHES "ON") 18 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O0 -fsanitize=address) 19 | else() 20 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O0) 21 | endif() 22 | 23 | target_link_libraries(${PROJECT_NAME} PRIVATE mpp::mpp -Wl,--export-dynamic) -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | ## Describe the bug 8 | 9 | A clear and concise description of what the bug is. 10 | 11 | ### To Reproduce 12 | 13 | Steps to reproduce the behavior: 14 | 15 | 1. Delete smart pointer 16 | 2. Try to reuse it 17 | 3. The pointer isn't null! 18 | 19 | ### Expected behavior 20 | 21 | A clear and concise description of what you expected to happen. 22 | 23 | ### Screenshots 24 | 25 | - ![Screenshot]() 26 | 27 | ### Desktop (please complete the following information) 28 | 29 | - OS: [e.g. Linux-18.04] 30 | - Library Version [e.g. 0.3.5] 31 | 32 | ### Additional context 33 | 34 | Add any other context about the problem here. 35 | -------------------------------------------------------------------------------- /fuzzer/include/fuzzer/crc_calculator.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mpp { namespace fuzzer { 6 | 7 | /** 8 | * @brief Calculates CRC32 of buf of size len. 9 | * @param buf pointer to buffer 10 | * @param len length of buffer 11 | * @return uint32_t crc32 of data 12 | */ 13 | uint32_t CRC32(const uint8_t* buf, std::size_t len) 14 | { 15 | uint32_t val, crc; 16 | uint8_t i; 17 | 18 | crc = 0xFFFFFFFF; 19 | while (len--) { 20 | val = (crc ^ *buf++) & 0xFF; 21 | for (i = 0; i < 8; i++) { 22 | val = val & 1 ? (val >> 1) ^ 0xEDB88320 : val >> 1; 23 | } 24 | crc = val ^ crc >> 8; 25 | } 26 | return crc ^ 0xFFFFFFFF; 27 | } 28 | }} -------------------------------------------------------------------------------- /fuzzer/include/fuzzer/fuzzer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "mpplib/shared_gcptr.hpp" 7 | 8 | namespace mpp { namespace fuzzer { 9 | constexpr uint32_t c_maxPointers = 32; 10 | constexpr uint32_t c_maxPointersInAVertex = 16; 11 | 12 | class Vertex 13 | { 14 | private: 15 | std::array, c_maxPointersInAVertex> m_gcs; 16 | uint64_t m_data; 17 | 18 | public: 19 | Vertex() 20 | : m_data(0x13371337deadbeef) 21 | { 22 | } 23 | 24 | void AddPointerAtIndex(uint32_t t_idx, const SharedGcPtr& t_ptr) 25 | { 26 | m_gcs.at(t_idx) = t_ptr; 27 | } 28 | 29 | uint64_t& GetData() 30 | { 31 | return m_data; 32 | } 33 | }; 34 | }} -------------------------------------------------------------------------------- /fuzzer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(fuzzer) 2 | 3 | # ################ SET FUZZER SOURCES ################# 4 | list(APPEND MPP_FUZZER_SOURCES 5 | src/fuzzer_main.cpp 6 | src/tokenizer.cpp 7 | ) 8 | 9 | add_executable(${PROJECT_NAME} ${MPP_FUZZER_SOURCES}) 10 | target_include_directories(${PROJECT_NAME} PUBLIC 11 | $ 12 | $ 13 | ) 14 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O1 -fsanitize=fuzzer,address) 15 | target_link_libraries(${PROJECT_NAME} PRIVATE mpp::mpp -fsanitize=fuzzer,address) 16 | 17 | # target_compile_options(${PROJECT_NAME} PRIVATE -g -O0) 18 | # target_link_libraries(${PROJECT_NAME} PRIVATE lib::mpp) 19 | set(FUZZ_RUNTIME 20 | 30 21 | CACHE STRING "Number of seconds to run fuzz tests during ctest run") 22 | 23 | add_test(NAME fuzz_tester COMMAND ${PROJECT_NAME} -max_total_time=${FUZZ_RUNTIME} ../corpus) -------------------------------------------------------------------------------- /libmemplusplus/src/utils/timer.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/timer.hpp" 2 | 3 | namespace mpp { namespace utils { namespace profile { 4 | Timer::Timer(const char* t_name) 5 | : m_name(t_name) 6 | { 7 | } 8 | 9 | Timer& Timer::TimerStart() 10 | { 11 | m_startTimepoint = std::chrono::high_resolution_clock::now(); 12 | return *this; 13 | } 14 | 15 | Timer& Timer::TimerEnd() 16 | { 17 | m_endTimePoint = std::chrono::high_resolution_clock::now(); 18 | return *this; 19 | } 20 | 21 | std::chrono::time_point Timer::GetStartTimepoint() 22 | { 23 | return m_startTimepoint; 24 | } 25 | 26 | std::chrono::time_point Timer::GetEndTimepoint() 27 | { 28 | return m_endTimePoint; 29 | } 30 | 31 | const char* Timer::GetName() 32 | { 33 | return m_name; 34 | } 35 | }}} -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /cmake/FindValgrind.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # Find Valgrind. 3 | # 4 | # This module defines: 5 | # Valgrind_INCLUDE_DIR, where to find valgrind/memcheck.h, etc. 6 | # Valgrind_PROGRAM, the valgrind executable. 7 | # Valgrind_FOUND, If false, do not try to use valgrind. 8 | # 9 | # If you have valgrind installed in a non-standard place, you can define 10 | # VALGRIND_PREFIX to tell cmake where it is. 11 | 12 | include(FindPackageHandleStandardArgs) 13 | 14 | find_path(Valgrind_INCLUDE_DIR valgrind.h 15 | /usr/include 16 | /usr/include/valgrind 17 | /usr/local/include 18 | /usr/local/include/valgrind 19 | ${VALGRIND_PREFIX}/include 20 | ${VALGRIND_PREFIX}/include/valgrind) 21 | 22 | find_program(Valgrind_PROGRAM NAMES valgrind PATH 23 | /usr/bin 24 | /usr/local/bin 25 | ${VALGRIND_PREFIX}/bin) 26 | 27 | find_package_handle_standard_args(Valgrind DEFAULT_MSG 28 | Valgrind_INCLUDE_DIR 29 | Valgrind_PROGRAM) 30 | 31 | mark_as_advanced(Valgrind_INCLUDE_DIR Valgrind_PROGRAM) 32 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(unit_tests) 2 | 3 | list(APPEND MPP_TESTS_SOURCES 4 | test_main.cpp 5 | gc_tests.cpp 6 | gc_graph_tests.cpp 7 | shared_gcptr_tests.cpp 8 | chunk_treap_test.cpp 9 | allocator_logic_tests.cpp 10 | security_tests.cpp # Hurray, after migrating to gtest we finally can test for abort signal! 11 | ) 12 | 13 | if(MPP_SANITIZERS MATCHES "ON") 14 | message(STATUS "Sanitizers are enabled, adding asan tests") 15 | list(APPEND MPP_TESTS_SOURCES asan_tests.cpp) 16 | endif() 17 | 18 | # Add a testing executable 19 | add_executable(unit_tests 20 | ${MPP_TESTS_SOURCES} 21 | ) 22 | 23 | if(MPP_SANITIZERS MATCHES "ON") 24 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O0 -fsanitize=address) 25 | else() 26 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O0) 27 | endif() 28 | 29 | target_link_libraries(${PROJECT_NAME} PUBLIC mpp::mpp gtest -Wl,--export-dynamic) 30 | 31 | add_test(unit_tester ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${PROJECT_NAME}) -------------------------------------------------------------------------------- /libmemplusplus/src/utils/profiler_timer.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/profiler_timer.hpp" 2 | 3 | namespace mpp { namespace utils { namespace profile { 4 | 5 | ProfilerTimer::ProfilerTimer(const char* t_name) 6 | : Timer(t_name) 7 | , m_stopped(false) 8 | { 9 | this->TimerStart(); 10 | } 11 | 12 | ProfilerTimer::~ProfilerTimer() 13 | { 14 | if (!m_stopped) { 15 | Stop(); 16 | } 17 | } 18 | 19 | void ProfilerTimer::Stop() 20 | { 21 | this->TimerEnd(); 22 | 23 | int64_t start = std::chrono::time_point_cast(GetStartTimepoint()) 24 | .time_since_epoch() 25 | .count(); 26 | int64_t end = std::chrono::time_point_cast(GetEndTimepoint()) 27 | .time_since_epoch() 28 | .count(); 29 | 30 | uint32_t threadId = std::hash{}(std::this_thread::get_id()); 31 | mpp::utils::profile::Profiler::Get().WriteProfile(GetName(), start, end, threadId); 32 | 33 | m_stopped = true; 34 | } 35 | }}} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/colours.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mpp { namespace utils { namespace colours { 6 | constexpr std::string_view RESET = "\u001b[0m"; 7 | constexpr std::string_view GREY = "\u001b[90m"; 8 | constexpr std::string_view BLACK = "\u001b[30m"; 9 | constexpr std::string_view RED = "\u001b[31m"; 10 | constexpr std::string_view GREEN = "\u001b[32m"; 11 | constexpr std::string_view YELLOW = "\u001b[33m"; 12 | constexpr std::string_view BLUE = "\u001b[34m"; 13 | constexpr std::string_view MAGENTA = "\u001b[35m"; 14 | constexpr std::string_view CYAN = "\u001b[36m"; 15 | constexpr std::string_view WHITE = "\u001b[37m"; 16 | 17 | constexpr std::string_view BRIGHT_BLACK = "\u001b[30;1m"; 18 | constexpr std::string_view BRIGHT_RED = "\u001b[31;1m"; 19 | constexpr std::string_view BRIGHT_GREEN = "\u001b[32;1m"; 20 | constexpr std::string_view BRIGHT_YELLOW = "\u001b[33;1m"; 21 | constexpr std::string_view BRIGHT_BLUE = "\u001b[34;1m"; 22 | constexpr std::string_view BRIGHT_MAGENTA = "\u001b[35;1m"; 23 | constexpr std::string_view BRIGHT_CYAN = "\u001b[36;1m"; 24 | constexpr std::string_view BRIGHT_WHITE = "\u001b[37;1m"; 25 | }}} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/profiler_timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/profiler.hpp" 4 | #include "mpplib/utils/timer.hpp" 5 | 6 | #include 7 | 8 | namespace mpp { namespace utils { namespace profile { 9 | /** 10 | * @brief Extended timer, to automate profiling 11 | */ 12 | class ProfilerTimer : public Timer 13 | { 14 | public: 15 | /** 16 | * @brief Construct a new Profiler Timer object 17 | * @param t_name name of the timer 18 | */ 19 | explicit ProfilerTimer(const char* t_name); 20 | 21 | //! @brief Deleted copy constructor 22 | ProfilerTimer(const ProfilerTimer&) = delete; 23 | 24 | //! @brief Deleted move constructor 25 | ProfilerTimer(ProfilerTimer&&) = delete; 26 | 27 | //! @brief Deleted copy assignment operator 28 | ProfilerTimer& operator=(const ProfilerTimer&) = delete; 29 | 30 | //! @brief Deleted move assignment operator 31 | ProfilerTimer& operator=(ProfilerTimer&&) = delete; 32 | 33 | //! @brief Destroys the Profiler Timer and stops the timer 34 | ~ProfilerTimer(); 35 | 36 | //! @brief stop the profiler timer 37 | void Stop(); 38 | 39 | private: 40 | //! @brief is profiler timer stopped 41 | bool m_stopped; 42 | }; 43 | }}} -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. 4 | 5 | Fixes # (issue) 6 | 7 | ## Type of change 8 | 9 | Please delete options that are not relevant. 10 | 11 | - [ ] Bug fix (non-breaking change which fixes an issue) 12 | - [ ] New feature (non-breaking change which adds functionality) 13 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 14 | - [ ] This change requires a documentation update 15 | 16 | # How Has This Been Tested? 17 | 18 | Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration 19 | 20 | - [ ] Test A 21 | - [ ] Test B 22 | 23 | **Test Configuration**: 24 | 25 | - Firmware version: 26 | 27 | - Toolchain: 28 | 29 | # Checklist: 30 | 31 | - [ ] My code follows the style guidelines of this project (use .clang-format) 32 | - [ ] I have performed a self-review of my own code 33 | - [ ] I have commented my code, particularly in hard-to-understand areas 34 | - [ ] I have made corresponding changes to the documentation 35 | - [ ] I have added tests that prove my fix is effective or that my feature works 36 | - [ ] New and existing unit tests pass locally with my changes 37 | - [ ] Any dependent changes have been merged and published in downstream modules 38 | -------------------------------------------------------------------------------- /tests/security_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mpplib/chunk.hpp" 8 | #include "mpplib/memory_manager.hpp" 9 | 10 | #include "gtest_fixtures.hpp" 11 | 12 | TEST_F(AllocatorTest, InvalidFree) 13 | { 14 | using namespace mpp; 15 | 16 | std::vector ptrs; 17 | ptrs.push_back(Allocate(128)); 18 | ptrs.push_back(Allocate(256)); 19 | ptrs.push_back(Allocate(512)); 20 | ptrs.push_back(Allocate(16)); 21 | ptrs.push_back(Allocate(128)); 22 | ptrs.push_back(Allocate(256)); 23 | ptrs.push_back(Allocate(512)); 24 | ptrs.push_back(Allocate(1024)); 25 | ptrs.push_back(Allocate(128)); 26 | ptrs.push_back(Allocate(128)); 27 | ptrs.push_back(Allocate(2048)); 28 | ptrs.push_back(Allocate(128)); 29 | ptrs.push_back(Allocate(128)); 30 | ptrs.push_back(Allocate(128)); 31 | 32 | Deallocate(ptrs[0]); 33 | Deallocate(ptrs[2]); 34 | Deallocate(ptrs[4]); 35 | Deallocate(ptrs[6]); 36 | Deallocate(ptrs[8]); 37 | Deallocate(ptrs[7]); 38 | Deallocate(ptrs[10]); 39 | 40 | // InvalidFree 41 | EXPECT_EXIT({ Deallocate((void*)0xdeadbeef); }, 42 | [](int status) { 43 | // With Valgrind, the exit status is 1, without it is SIGABRT 44 | return (WIFSIGNALED(status) && WTERMSIG(status) == SIGABRT) || 45 | WEXITSTATUS(status) == 1; 46 | }, 47 | "Invalid pointer deallocation detected!"); 48 | } 49 | -------------------------------------------------------------------------------- /libmemplusplus/src/utils/env_options.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/env_options.hpp" 2 | 3 | namespace mpp { namespace utils { 4 | EnvOptions::EnvOptions() 5 | : m_dumpObjectsGraph{ ObjectsGraphDumpType::DISABLED } 6 | , m_showStatistics{ false } 7 | , m_logLevel{ logging::LogLevel::FATAL } 8 | { 9 | SetOptions(); 10 | } 11 | 12 | void EnvOptions::SetOptions() 13 | { 14 | char* dump_graph = std::getenv("MPP_DUMP_OBJECTS_GRAPH"); 15 | char* show_stats = std::getenv("MPP_SHOW_STATISTICS"); 16 | char* log_level = std::getenv("MPP_LOG_LEVEL"); 17 | 18 | if (dump_graph && dump_graph[0] == '1') { 19 | m_dumpObjectsGraph = ObjectsGraphDumpType::SIMPLE; 20 | } else if (dump_graph && dump_graph[0] == '2') { 21 | m_dumpObjectsGraph = ObjectsGraphDumpType::ADVANCED; 22 | } else { 23 | m_dumpObjectsGraph = ObjectsGraphDumpType::DISABLED; 24 | } 25 | 26 | m_showStatistics = show_stats && show_stats[0] == '1'; 27 | 28 | if (log_level != nullptr) { 29 | m_logLevel = logging::LogLevelFromString(log_level); 30 | } 31 | } 32 | 33 | ObjectsGraphDumpType EnvOptions::GetMppDumpObjectsGraph() const 34 | { 35 | return m_dumpObjectsGraph; 36 | } 37 | 38 | bool EnvOptions::GetMppShowStatistics() const 39 | { 40 | return m_showStatistics; 41 | } 42 | 43 | logging::LogLevel EnvOptions::GetMppLogLevel() const 44 | { 45 | return m_logLevel; 46 | } 47 | 48 | EnvOptions& EnvOptions::Get() 49 | { 50 | static EnvOptions s_instance; 51 | return s_instance; 52 | } 53 | }} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/profiler.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace mpp { namespace utils { namespace profile { 9 | /** 10 | * @brief Class that provides features to profile and dump results 11 | */ 12 | class Profiler 13 | { 14 | private: 15 | //! @brief Stream to write to 16 | std::ofstream m_outputStream; 17 | 18 | //! @brief profile counts 19 | int32_t m_profileCount; 20 | 21 | public: 22 | //! @brief Construct a new Profiler object 23 | Profiler(); 24 | 25 | //! @brief Destroy the Profiler object, and write to mpplib-profile if needed 26 | ~Profiler(); 27 | 28 | /** 29 | * @brief Start new profiling session 30 | * @param t_name session name 31 | * @param t_filepath profile filename 32 | */ 33 | void BeginSession(const std::string& t_name, 34 | const std::string& t_filepath = "profile.json"); 35 | 36 | //! @brief Stop profiling session 37 | void EndSession(); 38 | 39 | /** 40 | * @brief Write info to profile 41 | * @param t_name timestamp name 42 | * @param t_start start time 43 | * @param t_end end time 44 | * @param t_threadId thread id 45 | */ 46 | void WriteProfile(const char* t_name, int64_t t_start, int64_t t_end, uint32_t t_threadId); 47 | 48 | //! @brief Write header to profile file 49 | void WriteHeader(); 50 | 51 | //! @brief Write footer to profile file 52 | void WriteFooter(); 53 | 54 | /** 55 | * @brief Get static instance 56 | * @return Profiler& 57 | */ 58 | static Profiler& Get(); 59 | }; 60 | }}} -------------------------------------------------------------------------------- /tests/asan_tests.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mpplib/chunk.hpp" 8 | #include "mpplib/memory_manager.hpp" 9 | 10 | #include "gtest_fixtures.hpp" 11 | 12 | TEST_F(AsanTest, DoubleFree) 13 | { 14 | using namespace mpp; 15 | 16 | std::vector ptrs; 17 | ptrs.push_back(Allocate(128)); 18 | ptrs.push_back(Allocate(256)); 19 | ptrs.push_back(Allocate(512)); 20 | ptrs.push_back(Allocate(16)); 21 | ptrs.push_back(Allocate(128)); 22 | ptrs.push_back(Allocate(256)); 23 | ptrs.push_back(Allocate(512)); 24 | ptrs.push_back(Allocate(1024)); 25 | ptrs.push_back(Allocate(128)); 26 | ptrs.push_back(Allocate(128)); 27 | ptrs.push_back(Allocate(2048)); 28 | ptrs.push_back(Allocate(128)); 29 | ptrs.push_back(Allocate(128)); 30 | ptrs.push_back(Allocate(128)); 31 | 32 | Deallocate(ptrs[0]); 33 | Deallocate(ptrs[2]); 34 | Deallocate(ptrs[4]); 35 | Deallocate(ptrs[6]); 36 | Deallocate(ptrs[8]); 37 | Deallocate(ptrs[7]); 38 | Deallocate(ptrs[10]); 39 | Deallocate(ptrs[12]); 40 | 41 | // DoubleFree 42 | EXPECT_EXIT({ Deallocate((void*)ptrs[8]); }, 43 | testing::ExitedWithCode(SIGHUP), 44 | "AddressSanitizer: use-after-poison on address"); 45 | } 46 | 47 | TEST_F(AsanTest, UseAfterFreeRead) 48 | { 49 | using namespace mpp; 50 | 51 | uint64_t* p1 = (uint64_t*)Allocate(128); 52 | *p1 = 0x1337133791237482; 53 | void* p2 = Allocate(128); 54 | 55 | Deallocate(p1); 56 | 57 | EXPECT_EXIT({ volatile bool res = *(uint64_t*)p1 == 0x1337133791237482; }, 58 | testing::ExitedWithCode(SIGHUP), 59 | "READ of size 8"); 60 | } 61 | 62 | TEST_F(AsanTest, UseAfterFreeWrite) 63 | { 64 | using namespace mpp; 65 | 66 | uint64_t* p1 = (uint64_t*)Allocate(128); 67 | *p1 = 0x1337133791237482; 68 | void* p2 = Allocate(128); 69 | 70 | Deallocate(p1); 71 | 72 | EXPECT_EXIT({ *p1 = 0x1234567812345678; }, testing::ExitedWithCode(SIGHUP), "WRITE of size 8"); 73 | } 74 | -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/env_options.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/log.hpp" 4 | #include "mpplib/utils/profiler_definitions.hpp" 5 | 6 | #include 7 | 8 | namespace mpp { namespace utils { 9 | enum class ObjectsGraphDumpType 10 | { 11 | SIMPLE, 12 | ADVANCED, 13 | DISABLED 14 | }; 15 | 16 | /** 17 | * @brief Class that wraps all environment options. 18 | */ 19 | class EnvOptions final 20 | { 21 | private: 22 | /** 23 | * @brief If environment variable MPP_DUMP_OBJECTS_GRAPH is set to 1 - dumps simple objects 24 | * graph. If set to 2 - dumps advanced objects graph. 25 | */ 26 | ObjectsGraphDumpType m_dumpObjectsGraph; 27 | 28 | /** 29 | * @brief true, if environment variable MPP_SHOW_STATISTICS is set. 30 | */ 31 | bool m_showStatistics; 32 | 33 | /** 34 | * @brief Environment variable MPP_LOG_LEVEL. 35 | */ 36 | logging::LogLevel m_logLevel; 37 | 38 | /** 39 | * @brief Constructor, that gets called before program start, to setup all 40 | * environment variables. 41 | */ 42 | void SetOptions(); 43 | 44 | public: 45 | /** 46 | * @brief Construct a new EnvOptions object 47 | */ 48 | EnvOptions(); 49 | 50 | /** 51 | * @brief Get the MppDumpObjectsGraph variable. 52 | * @return true, if variable is set, false - otherwise. 53 | */ 54 | ObjectsGraphDumpType GetMppDumpObjectsGraph() const; 55 | 56 | /** 57 | * @brief Get the MppShowStatistics variable. 58 | * @return true, if variable is set, false - otherwise. 59 | */ 60 | bool GetMppShowStatistics() const; 61 | 62 | /** 63 | * @brief Get the MppLogLevel variable. 64 | * @return LogLevel 65 | */ 66 | logging::LogLevel GetMppLogLevel() const; 67 | 68 | /** 69 | * @brief Get static instance 70 | * @return EnvOptions& 71 | */ 72 | static EnvOptions& Get(); 73 | }; 74 | }} -------------------------------------------------------------------------------- /libmemplusplus/src/utils/profiler.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/profiler.hpp" 2 | #include 3 | 4 | namespace mpp { namespace utils { namespace profile { 5 | Profiler::Profiler() 6 | : m_profileCount(0) 7 | { 8 | BeginSession("Mpplib profiling", "mpplib-profiling.json"); 9 | } 10 | 11 | Profiler::~Profiler() 12 | { 13 | Profiler::Get().EndSession(); 14 | } 15 | 16 | void Profiler::BeginSession(const std::string& t_name, const std::string& t_filepath) 17 | { 18 | m_outputStream.open(t_filepath); 19 | WriteHeader(); 20 | } 21 | 22 | void Profiler::EndSession() 23 | { 24 | WriteFooter(); 25 | m_outputStream.close(); 26 | m_profileCount = 0; 27 | } 28 | 29 | void Profiler::WriteProfile(const char* t_name, 30 | int64_t t_start, 31 | int64_t t_end, 32 | uint32_t t_threadId) 33 | { 34 | if (m_profileCount++ > 0) { 35 | m_outputStream << ","; 36 | } 37 | 38 | std::string name = std::string(t_name); 39 | std::replace(name.begin(), name.end(), '"', '\''); 40 | 41 | m_outputStream << "{"; 42 | m_outputStream << "\"cat\":\"function\","; 43 | m_outputStream << "\"dur\":" << (t_end - t_start) << ','; 44 | m_outputStream << "\"name\":\"" << name << "\","; 45 | m_outputStream << "\"ph\":\"X\","; 46 | m_outputStream << "\"pid\":0,"; 47 | m_outputStream << "\"tid\":" << t_threadId << ","; 48 | m_outputStream << "\"ts\":" << t_start; 49 | m_outputStream << "}"; 50 | 51 | m_outputStream.flush(); 52 | } 53 | 54 | void Profiler::WriteHeader() 55 | { 56 | m_outputStream << "{\"otherData\": {},\"traceEvents\":["; 57 | m_outputStream.flush(); 58 | } 59 | 60 | void Profiler::WriteFooter() 61 | { 62 | m_outputStream << "]}"; 63 | m_outputStream.flush(); 64 | } 65 | 66 | Profiler& Profiler::Get() 67 | { 68 | static Profiler s_instance; 69 | return s_instance; 70 | } 71 | }}} -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. mem++ documentation master file, created by 2 | sphinx-quickstart on Sat May 16 13:25:46 2020. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to mem++'s documentation! 7 | ================================= 8 | 9 | .. toctree:: 10 | :maxdepth: 2 11 | :caption: Contents: 12 | 13 | 14 | 15 | Indices and tables 16 | ================== 17 | 18 | * :ref:`genindex` 19 | * :ref:`modindex` 20 | * :ref:`search` 21 | 22 | Docs 23 | ==== 24 | 25 | .. doxygenclass:: mpp::MemoryManager 26 | :project: memplusplus 27 | :members: 28 | :protected-members: 29 | :private-members: 30 | 31 | .. doxygenclass:: mpp::Arena 32 | :project: memplusplus 33 | :members: 34 | :protected-members: 35 | :private-members: 36 | 37 | .. doxygenstruct:: mpp::Chunk 38 | :project: memplusplus 39 | :members: 40 | :protected-members: 41 | :private-members: 42 | 43 | .. doxygenclass:: mpp::ChunkTreap 44 | :project: memplusplus 45 | :members: 46 | :protected-members: 47 | :private-members: 48 | 49 | .. doxygenstruct:: mpp::Node 50 | :project: memplusplus 51 | :members: 52 | :protected-members: 53 | :private-members: 54 | 55 | .. doxygenclass:: mpp::GC 56 | :project: memplusplus 57 | :members: 58 | :protected-members: 59 | :private-members: 60 | 61 | .. doxygenclass:: mpp::Heuristics 62 | :project: memplusplus 63 | :members: 64 | :protected-members: 65 | :private-members: 66 | 67 | .. doxygenclass:: mpp::GcGraph 68 | :project: memplusplus 69 | :members: 70 | :protected-members: 71 | :private-members: 72 | 73 | .. doxygenclass:: mpp::Vertex 74 | :project: memplusplus 75 | :members: 76 | :protected-members: 77 | :private-members: 78 | 79 | .. doxygenclass:: mpp::GcPtr 80 | :project: memplusplus 81 | :members: 82 | :protected-members: 83 | :private-members: 84 | 85 | .. doxygenclass:: mpp::SharedGcPtr 86 | :project: memplusplus 87 | :members: 88 | :protected-members: 89 | :private-members: 90 | 91 | .. doxygenclass:: mpp::SharedGcPtrArray 92 | :project: memplusplus 93 | :members: 94 | :protected-members: 95 | :private-members: 96 | -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/gcptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/profiler_definitions.hpp" 4 | 5 | #include 6 | 7 | namespace mpp { 8 | 9 | /** 10 | * @brief Base class for all GcPtr types. 11 | */ 12 | class GcPtr 13 | { 14 | public: 15 | /** 16 | * @brief Destructor of GcPtr object. 17 | */ 18 | virtual ~GcPtr() = default; 19 | 20 | //! @brief Default constructor 21 | GcPtr() = default; 22 | 23 | //! @brief Default copy constructor 24 | GcPtr(const GcPtr&) = default; 25 | 26 | //! @brief Default move constructor 27 | GcPtr(GcPtr&&) = default; 28 | 29 | //! @brief Default copy assignment operator 30 | GcPtr& operator=(const GcPtr&) = default; 31 | 32 | //! @brief Default move assignment operator 33 | GcPtr& operator=(GcPtr&&) = default; 34 | 35 | /** 36 | * @brief Get pointer to controlled object as void. 37 | * @return void* pointer to controlled object 38 | */ 39 | virtual void* GetVoid() const = 0; 40 | 41 | /** 42 | * @brief Update pointer to controlled object 43 | * @param t_newPtr new pointer to controlled object 44 | */ 45 | virtual void UpdatePtr(void* t_newPtr) = 0; 46 | 47 | /** 48 | * @brief Use count of current object controlled by gcPtr of any type. 49 | * @return uint32_t use count 50 | */ 51 | virtual uint32_t UseCount() = 0; 52 | 53 | /** 54 | * @brief base print method 55 | * @param t_out ostream to write to. 56 | * @return std::ostream& that was used 57 | */ 58 | virtual std::ostream& Print(std::ostream& t_out) const 59 | { 60 | return t_out; 61 | } 62 | 63 | /** 64 | * @brief Overloaded "<<" operator to dump GcPtr. 65 | * @param t_out stream to write to. 66 | * @param t_gcPtr actual object to dump. 67 | * @return std::ostream& that was used 68 | */ 69 | friend std::ostream& operator<<(std::ostream& t_out, const GcPtr& t_gcPtr) 70 | { 71 | return t_gcPtr.Print(t_out); 72 | } 73 | }; 74 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/timer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace mpp { namespace utils { namespace profile { 6 | /** 7 | * @brief Timer class that provides basic time-measuring components 8 | */ 9 | class Timer 10 | { 11 | public: 12 | /** 13 | * @brief Construct a new Timer object 14 | * @param t_name timer name 15 | */ 16 | explicit Timer(const char* t_name); 17 | 18 | /** 19 | * @brief Start timer 20 | * @return Timer& 21 | */ 22 | Timer& TimerStart(); 23 | 24 | /** 25 | * @brief Stop timer 26 | * @return Timer& 27 | */ 28 | Timer& TimerEnd(); 29 | 30 | /** 31 | * @brief Get the start timepoint 32 | * @return std::chrono::time_point 33 | */ 34 | std::chrono::time_point GetStartTimepoint(); 35 | 36 | /** 37 | * @brief Get the end timepoint 38 | * @return std::chrono::time_point 39 | */ 40 | std::chrono::time_point GetEndTimepoint(); 41 | 42 | /** 43 | * @brief Get name of the timer 44 | * @return std::string& 45 | */ 46 | const char* GetName(); 47 | 48 | /** 49 | * @brief Get the elapsed time 50 | * @tparam T - can be any of std::chrono time types (std::chrono::milliseconds, ...) 51 | * @return std::chrono::duration 52 | */ 53 | template 54 | auto GetElapsed() -> auto 55 | { 56 | return std::chrono::duration_cast(m_endTimePoint - m_startTimepoint); 57 | } 58 | 59 | private: 60 | /** 61 | * @brief Name of the timer 62 | */ 63 | const char* m_name; 64 | 65 | /** 66 | * @brief Start time point 67 | */ 68 | std::chrono::time_point m_startTimepoint; 69 | 70 | /** 71 | * @brief End time point 72 | */ 73 | std::chrono::time_point m_endTimePoint; 74 | }; 75 | }}} -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # Configuration file for the Sphinx documentation builder. 2 | # 3 | # This file only contains a selection of the most common options. For a full 4 | # list see the documentation: 5 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 6 | 7 | # -- Path setup -------------------------------------------------------------- 8 | 9 | # If extensions (or modules to document with autodoc) are in another directory, 10 | # add these directories to sys.path here. If the directory is relative to the 11 | # documentation root, use os.path.abspath to make it absolute, like shown here. 12 | # 13 | # import os 14 | # import sys 15 | # sys.path.insert(0, os.path.abspath('.')) 16 | 17 | 18 | # -- Project information ----------------------------------------------------- 19 | 20 | project = 'mem++' 21 | copyright = '2022, madrat, l1th1um, akoul02' 22 | author = 'madrat, l1th1um, akoul02' 23 | 24 | 25 | # -- General configuration --------------------------------------------------- 26 | 27 | # Add any Sphinx extension module names here, as strings. They can be 28 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 29 | # ones. 30 | extensions = [ 31 | "breathe" 32 | ] 33 | 34 | # Breathe Configuration 35 | breathe_default_project = "memplusplus" 36 | 37 | # Add any paths that contain templates here, relative to this directory. 38 | templates_path = ['_templates'] 39 | 40 | # List of patterns, relative to source directory, that match files and 41 | # directories to ignore when looking for source files. 42 | # This pattern also affects html_static_path and html_extra_path. 43 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 44 | 45 | 46 | # -- Options for HTML output ------------------------------------------------- 47 | 48 | # The theme to use for HTML and HTML Help pages. See the documentation for 49 | # a list of builtin themes. 50 | # 51 | # html_theme = 'press' 52 | 53 | import maisie_sphinx_theme 54 | extensions.append("maisie_sphinx_theme") 55 | html_theme = 'maisie_sphinx_theme' 56 | html_theme_path = maisie_sphinx_theme.html_theme_path() 57 | 58 | # Add any paths that contain custom static files (such as style sheets) here, 59 | # relative to this directory. They are copied after the builtin static files, 60 | # so a file named "default.css" will overwrite the builtin "default.css". 61 | html_static_path = ['_static'] -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/random.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mpp { 7 | /** 8 | * @brief Class that provides API to work with 64 bit random values. 9 | * In insecure (debug) 10 | */ 11 | class Random 12 | { 13 | private: 14 | /** 15 | * @brief Random values generator. Will use previously 16 | * created random device. 17 | */ 18 | std::mt19937_64 m_gen; 19 | 20 | /** 21 | * @brief Generate random uniformally distributed numbers. 22 | */ 23 | std::uniform_int_distribution m_distribution; 24 | 25 | public: 26 | /** 27 | * @brief Construct a new Random object 28 | * @param t_min min value to generate 29 | * @param t_max max value to generate (limited to (2 << 63) - 1) 30 | * @param t_randSeed Seed source (by default system random seed /dev/random) 31 | */ 32 | explicit Random(std::size_t t_min = 0x0, 33 | std::size_t t_max = std::numeric_limits::max(), 34 | std::size_t t_randSeed = std::random_device{}()) 35 | : m_gen{ t_randSeed } 36 | , m_distribution{ t_min, t_max } 37 | { 38 | } 39 | 40 | /** 41 | * @brief Sets seed, to generate predictable values. Used only 42 | * in debug/fuzzer builds. 43 | * @param t_seed new seed value 44 | * @return Random& instance of current object 45 | */ 46 | Random& SetSeed(uint32_t t_seed) 47 | { 48 | m_gen.seed(t_seed); 49 | return *this; 50 | } 51 | 52 | /** 53 | * @brief Generate uniformally distributed random value [t_min, t_max] 54 | * @return std::size_t 64bit random value 55 | */ 56 | std::size_t operator()() 57 | { 58 | return m_distribution(m_gen); 59 | } 60 | 61 | /** 62 | * @brief Returns global instance of Random object 63 | * @return Random& global instance 64 | */ 65 | static Random& GetInstance() 66 | { 67 | static Random s_randomInstance; 68 | #if MPP_FUZZER_INSECURE == 1 || MPP_DEBUG == 1 69 | s_randomInstance.SetSeed(0); 70 | #endif 71 | return s_randomInstance; 72 | } 73 | }; 74 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/containers/node.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/chunk.hpp" 4 | #include "mpplib/utils/profiler_definitions.hpp" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | 12 | namespace mpp { 13 | /** 14 | * @brief Node class to use in ChunkTreap. 15 | */ 16 | struct Node final 17 | { 18 | public: 19 | /** 20 | * @brief Node priority in treap 21 | */ 22 | int32_t priority{ 0 }; 23 | 24 | /** 25 | * @brief Chunk related to the current node. 26 | */ 27 | Chunk* chunk{ nullptr }; 28 | 29 | /** 30 | * @brief Reference to the left node. 31 | */ 32 | Node* leftChild{ nullptr }; 33 | 34 | /** 35 | * @brief Reference to the right node. 36 | */ 37 | Node* rightChild{ nullptr }; 38 | 39 | /** 40 | * @brief Constructor, that construct node from chunk. 41 | */ 42 | explicit Node(Chunk* t_chunk); 43 | 44 | /** 45 | * @brief Copy-Constructor, that constructs node from const node reference. 46 | */ 47 | Node(const Node& t_node); 48 | 49 | /** 50 | * @brief Assignment operator, that constructs node from const node reference. 51 | */ 52 | Node& operator=(const Node& t_node); 53 | 54 | /** 55 | * @brief Copy-Constructor, that constructs node from r-value node reference. 56 | */ 57 | Node(Node&& t_node) noexcept; 58 | 59 | /** 60 | * @brief Assignment operator, that constructs node from r-value node reference. 61 | */ 62 | Node& operator=(Node&& t_node) noexcept; 63 | 64 | /** 65 | * @brief Default destructor. 66 | * @sa Delete. 67 | */ 68 | ~Node(); 69 | 70 | #if MPP_STATS == 1 71 | /** 72 | * @brief Dump text representation of the node. 73 | */ 74 | static std::ostream& DumpNode(std::ostream& t_out, Node* t_node); 75 | 76 | /** 77 | * @brief Overloaded output operator to print node using std::cout. 78 | */ 79 | friend std::ostream& operator<<(std::ostream& t_out, Node* t_node); 80 | #endif 81 | 82 | private: 83 | /** 84 | * @brief Deleter method. 85 | */ 86 | void Delete(); 87 | }; 88 | } -------------------------------------------------------------------------------- /.github/workflows/benchmarking-mpp.yml: -------------------------------------------------------------------------------- 1 | name: Benchmarking 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | jobs: 12 | build: 13 | if: "!contains(toJSON(github.event.commits.*.message), '[skip-bench]')" 14 | runs-on: ubuntu-latest 15 | 16 | steps: 17 | - uses: actions/checkout@v1 18 | 19 | - name: clone benchmarks 20 | run: | 21 | git clone https://github.com/m4drat/memplusplus-benchmarks 22 | cd memplusplus-benchmarks 23 | git submodule update --init --remote ./benchmarks/mempp/memplusplus 24 | git submodule update --init ./libraries/googletest 25 | git submodule update --init ./libraries/benchmark 26 | 27 | - name: configure_clang 28 | working-directory: memplusplus-benchmarks 29 | run: | 30 | CXX=clang++ cmake -S . -B build \ 31 | -DMPP_BENCH_ONLY_MEMPLUSPLUS=ON \ 32 | -DCMAKE_BUILD_TYPE=Release \ 33 | -DMPP_BUILD_SHARED_LIBS=OFF \ 34 | -DMPP_ENABLE_COVERAGE=OFF \ 35 | -DMPP_BUILD_FUZZER=OFF \ 36 | -DMPP_BUILD_EXAMPLE=OFF \ 37 | -DMPP_BUILD_TESTS=OFF \ 38 | -DMPP_BUILD_DOCS=OFF \ 39 | -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF \ 40 | -DMPP_SANITIZERS=OFF \ 41 | -DMPP_BUILD_SHARED_LIBS=OFF \ 42 | -DMPP_FULL_DEBUG=OFF \ 43 | -DMPP_SECURE=OFF \ 44 | -DMPP_PROFILE=OFF \ 45 | -DMPP_COLOUR=OFF \ 46 | -DMPP_STATS=OFF 47 | 48 | - name: build_clang 49 | working-directory: memplusplus-benchmarks 50 | run: | 51 | CXX=clang++ cmake --build build --config Release --target all 52 | 53 | - name: run_benchmarks 54 | working-directory: memplusplus-benchmarks 55 | run: | 56 | ./build/benchmarks/mempp/benchmark-mpp --benchmark_format=json | tee benchmark_result.json 57 | 58 | - name: Continuous Benchmark 59 | uses: benchmark-action/github-action-benchmark@v1.14.0 60 | with: 61 | name: Mem++ benchmarks 62 | tool: "googlecpp" 63 | output-file-path: ./memplusplus-benchmarks/benchmark_result.json 64 | github-token: ${{ secrets.GITHUB_TOKEN }} 65 | auto-push: true 66 | # Show alert with commit comment on detecting possible performance regression 67 | alert-threshold: "200%" 68 | comment-on-alert: true 69 | fail-on-alert: false 70 | -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/profiler_definitions.hpp" 4 | #include 5 | #include 6 | 7 | namespace mpp { namespace utils { 8 | 9 | /** 10 | * @brief Default backtrace size. 11 | */ 12 | static const uint32_t s_MAX_STACK_LEVELS = 50; 13 | 14 | /** 15 | * @brief Secure implementation of memset, that ensures that compiler will not optimize it out. 16 | * @param t_pointer Pointer to memory to be set. 17 | * @param t_targetBufferSize Destination buffer size. 18 | * @param t_fillChar Character to fill memory with. 19 | * @param t_sizeToRemove Size of memory to be removed. 20 | * @return void* Pointer to cleared memory. 21 | */ 22 | void* SecureMemset(void* t_pointer, 23 | size_t t_targetBufferSize, 24 | unsigned char t_fillChar, 25 | size_t t_sizeToRemove); 26 | 27 | /** 28 | * @brief Helper function to dump stacktrace. 29 | * @param t_out output stream to write to. 30 | * @param t_skip number of functions to skip from backtrace. 31 | */ 32 | void DumpStackTrace(std::ostream& t_out, int32_t t_skip = 1); 33 | 34 | /** 35 | * @brief Function to print error message and terminate program. 36 | * @param message message to print 37 | */ 38 | [[noreturn]] void ErrorAbort(const std::string& t_message); 39 | 40 | /** 41 | * @brief Behaves like an assert. 42 | * @param t_shouldAbort if true, calls utils::ErrorAbort. 43 | * @param t_message message to print. 44 | */ 45 | inline void ConditionalAbort(bool t_shouldAbort, const std::string& t_message) 46 | { 47 | if (t_shouldAbort) { 48 | ErrorAbort(t_message); 49 | } 50 | } 51 | 52 | /** 53 | * @brief Converts pointer to string (hexadecimal: 0x1337) representation 54 | * @param t_ptr pointer to convert 55 | * @return std::string string representation of the t_ptr 56 | */ 57 | std::string AddrToString(void* t_ptr); 58 | 59 | /** 60 | * @brief Performs binary search in container. 61 | * @param first iterator to begin of the range 62 | * @param last iterator to end of the range 63 | * @param value element to find 64 | * @param comp comparator 65 | * @return iterator to found element 66 | */ 67 | template 68 | T1* BinarySearch(ForwardIt first, ForwardIt last, const T2& value, Compare comp) 69 | { 70 | PROFILE_FUNCTION(); 71 | 72 | first = std::lower_bound(first, last, value, comp); 73 | if (!(first == last) && !(comp(value, *first))) 74 | return *first; 75 | return nullptr; 76 | } 77 | }} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace mpp { 7 | /** 8 | * @brief Defines general exceptions class for all project. 9 | * 10 | * All other exceptions should be inherited from this class. 11 | */ 12 | class Exception : virtual public std::exception 13 | { 14 | public: 15 | /** 16 | * @brief Constructor (C strings). 17 | * @param message C-style string error message. The string contents are copied upon 18 | * construction. Hence, responsibility for deleting the char* lies with the caller. 19 | */ 20 | explicit Exception(const char* message) 21 | : m_errorMsg(message) 22 | { 23 | } 24 | 25 | /** 26 | * @brief Constructor (C++ STL strings). 27 | * @param message The error message. 28 | */ 29 | explicit Exception(const std::string& message) 30 | : m_errorMsg(message) 31 | { 32 | } 33 | 34 | //! @brief Copy constructor. 35 | Exception(const Exception&) = default; 36 | 37 | //! @brief Move constructor. 38 | Exception(Exception&&) = default; 39 | 40 | //! @brief Copy assignment operator. 41 | Exception& operator=(const Exception&) = default; 42 | 43 | //! @brief Move assignment operator. 44 | Exception& operator=(Exception&&) = default; 45 | 46 | //! @brief Virtual destructor. 47 | ~Exception() override = default; 48 | 49 | /** 50 | * @brief Returns a pointer to the (constant) error description. 51 | * @return A pointer to a const char*. The underlying memory is in posession of the 52 | * Exception object. Callers must not attempt to free the memory. 53 | */ 54 | const char* what() const noexcept override 55 | { 56 | return m_errorMsg.c_str(); 57 | } 58 | 59 | protected: 60 | /** 61 | * @brief Error message. 62 | */ 63 | std::string m_errorMsg; 64 | }; 65 | 66 | /** 67 | * @brief No memory exception class. 68 | */ 69 | class NoMemoryException : public Exception 70 | { 71 | public: 72 | /** 73 | * @brief Default constructor. 74 | */ 75 | NoMemoryException() 76 | : Exception("No memory avaliable!\n") 77 | { 78 | } 79 | }; 80 | 81 | /** 82 | * @brief Unmap memory exception class. 83 | */ 84 | class UnmapMemoryException : public Exception 85 | { 86 | public: 87 | /** 88 | * @brief Default constructor. 89 | */ 90 | UnmapMemoryException() 91 | : Exception("Cannot unmap memory!\n") 92 | { 93 | } 94 | }; 95 | } -------------------------------------------------------------------------------- /mempp.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": { 8 | "files.associations": { 9 | "iostream": "cpp", 10 | "ostream": "cpp", 11 | "array": "cpp", 12 | "atomic": "cpp", 13 | "*.tcc": "cpp", 14 | "cctype": "cpp", 15 | "clocale": "cpp", 16 | "cmath": "cpp", 17 | "cstdarg": "cpp", 18 | "cstddef": "cpp", 19 | "cstdint": "cpp", 20 | "cstdio": "cpp", 21 | "cstdlib": "cpp", 22 | "cwchar": "cpp", 23 | "cwctype": "cpp", 24 | "deque": "cpp", 25 | "unordered_map": "cpp", 26 | "vector": "cpp", 27 | "exception": "cpp", 28 | "algorithm": "cpp", 29 | "memory": "cpp", 30 | "memory_resource": "cpp", 31 | "optional": "cpp", 32 | "string": "cpp", 33 | "string_view": "cpp", 34 | "system_error": "cpp", 35 | "tuple": "cpp", 36 | "type_traits": "cpp", 37 | "utility": "cpp", 38 | "fstream": "cpp", 39 | "initializer_list": "cpp", 40 | "iosfwd": "cpp", 41 | "istream": "cpp", 42 | "limits": "cpp", 43 | "new": "cpp", 44 | "sstream": "cpp", 45 | "stdexcept": "cpp", 46 | "streambuf": "cpp", 47 | "typeinfo": "cpp", 48 | "bitset": "cpp", 49 | "chrono": "cpp", 50 | "cinttypes": "cpp", 51 | "condition_variable": "cpp", 52 | "cstring": "cpp", 53 | "ctime": "cpp", 54 | "list": "cpp", 55 | "functional": "cpp", 56 | "iterator": "cpp", 57 | "map": "cpp", 58 | "numeric": "cpp", 59 | "random": "cpp", 60 | "ratio": "cpp", 61 | "regex": "cpp", 62 | "set": "cpp", 63 | "future": "cpp", 64 | "iomanip": "cpp", 65 | "mutex": "cpp", 66 | "thread": "cpp", 67 | "variant": "cpp", 68 | "cfenv": "cpp", 69 | "codecvt": "cpp", 70 | "complex": "cpp", 71 | "csetjmp": "cpp", 72 | "csignal": "cpp", 73 | "cuchar": "cpp", 74 | "forward_list": "cpp", 75 | "unordered_set": "cpp", 76 | "scoped_allocator": "cpp", 77 | "shared_mutex": "cpp", 78 | "typeindex": "cpp", 79 | "valarray": "cpp", 80 | "filesystem": "cpp", 81 | "any": "cpp", 82 | "bit": "cpp", 83 | "queue": "cpp" 84 | }, 85 | "cSpell.words": [ 86 | "alloc", 87 | "mmap", 88 | "nullptr" 89 | ], 90 | "codechecker.backend.compilationDatabasePath": "/home/madrat/memplusplus/compile_commands.json", 91 | "C_Cpp.files.exclude": { 92 | "memplusplus-benchmarks/benchmarks/mempp/memplusplus/**": true, 93 | "**/.vscode": true 94 | }, 95 | "C_Cpp.codeAnalysis.exclude": { 96 | "memplusplus-benchmarks/benchmarks/mempp/memplusplus/**": true, 97 | }, 98 | "search.exclude": { 99 | "**/memplusplus-benchmarks/benchmarks/mempp/memplusplus/": true 100 | }, 101 | "clangd.path": "/usr/bin/clangd-15", 102 | "editor.formatOnSave": true, 103 | "sarif-viewer.connectToGithubCodeScanning": "off" 104 | } 105 | } -------------------------------------------------------------------------------- /.github/workflows/cpp_ci.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | env: 12 | CTEST_OUTPUT_ON_FAILURE: 1 13 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 14 | 15 | jobs: 16 | build: 17 | runs-on: ubuntu-latest 18 | 19 | steps: 20 | - uses: actions/checkout@v1 21 | 22 | - name: checkout googletest 23 | run: git submodule update --init ./libraries/googletest 24 | 25 | - name: configure_g++ 26 | run: | 27 | CXX=g++ cmake -S . -B build \ 28 | -DCMAKE_BUILD_TYPE=Debug \ 29 | -DMPP_BUILD_SHARED_LIBS=OFF \ 30 | -DMPP_ENABLE_COVERAGE=ON \ 31 | -DMPP_BUILD_FUZZER=OFF \ 32 | -DMPP_BUILD_EXAMPLE=OFF \ 33 | -DMPP_BUILD_TESTS=ON \ 34 | -DMPP_BUILD_DOCS=OFF \ 35 | -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF \ 36 | -DMPP_SANITIZERS=ON \ 37 | -DMPP_VALGRIND=OFF \ 38 | -DMPP_BUILD_SHARED_LIBS=OFF \ 39 | -DMPP_FULL_DEBUG=ON \ 40 | -DMPP_SECURE=OFF \ 41 | -DMPP_PROFILE=OFF \ 42 | -DMPP_COLOUR=ON \ 43 | -DMPP_STATS=ON 44 | 45 | - name: build_g++ 46 | run: | 47 | CXX=g++ cmake --build build --config Debug --target all 48 | 49 | - name: test_unit 50 | run: | 51 | cd build 52 | ASAN_OPTIONS=detect_leaks=0 setarch `uname -m` -R ./tests/unit_tests 53 | 54 | - name: collect code coverage 55 | run: bash <(curl -s https://codecov.io/bash) || echo "Codecov did not collect coverage reports" 56 | 57 | - name: cleanup 58 | run: | 59 | rm -rf ./build/ 60 | 61 | - name: configure_clang 62 | run: | 63 | CXX=clang++ cmake -S . -B build \ 64 | -DCMAKE_BUILD_TYPE=Release \ 65 | -DMPP_ENABLE_COVERAGE=OFF \ 66 | -DMPP_BUILD_FUZZER=ON \ 67 | -DMPP_BUILD_EXAMPLE=OFF \ 68 | -DMPP_BUILD_TESTS=OFF \ 69 | -DMPP_BUILD_DOCS=OFF \ 70 | -DCMAKE_EXPORT_COMPILE_COMMANDS=OFF \ 71 | -DMPP_SANITIZERS=ON \ 72 | -DMPP_VALGRIND=OFF \ 73 | -DMPP_BUILD_SHARED_LIBS=OFF \ 74 | -DMPP_FULL_DEBUG=ON \ 75 | -DMPP_SECURE=ON \ 76 | -DMPP_PROFILE=ON \ 77 | -DMPP_COLOUR=ON \ 78 | -DMPP_STATS=OFF 79 | 80 | - name: build_clang 81 | run: | 82 | CXX=clang++ cmake --build build --config Release --target all 83 | 84 | - name: prepare_corpus 85 | run: | 86 | 7z x fuzzer/corpus.zip 87 | 88 | - name: test_fuzzer 89 | run: | 90 | cd build 91 | mkdir corpus && echo -ne "a1024a1024a128a0a10ddda1024a1024ddd" > ./corpus/testcase_1 92 | FUZZ_MODE=gc ASAN_OPTIONS=detect_leaks=0 ctest --build-config Release 93 | -------------------------------------------------------------------------------- /docs/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | find_package(Doxygen REQUIRED) 2 | find_package(Sphinx REQUIRED) 3 | 4 | # Find all the public headers for mpp (libmemplusplus) project 5 | get_target_property(LIBMEMPLUSPLUS_PUBLIC_HEADER_DIR mpp INTERFACE_INCLUDE_DIRECTORIES) 6 | file(GLOB_RECURSE LIBMEMPLUSPLUS_PUBLIC_HEADERS ${LIBMEMPLUSPLUS_PUBLIC_HEADER_DIR}/*.h) 7 | 8 | # project root 9 | set(DOXYGEN_INPUT_DIR ${PROJECT_SOURCE_DIR}/libmemplusplus) 10 | 11 | # doxygen output 12 | set(DOXYGEN_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/doxygen) 13 | 14 | # doxygen index file 15 | set(DOXYGEN_INDEX_FILE ${DOXYGEN_OUTPUT_DIR}/html/index.html) 16 | 17 | # doxygen config IN 18 | set(DOXYFILE_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in) 19 | 20 | # doxygen config OUT 21 | set(DOXYFILE_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile) 22 | 23 | #Replace variables inside @@ with the current values 24 | configure_file(${DOXYFILE_IN} ${DOXYFILE_OUT} @ONLY) 25 | 26 | #Doxygen won't create this for us 27 | file(MAKE_DIRECTORY ${DOXYGEN_OUTPUT_DIR}) 28 | 29 | # Only regenerate Doxygen when the Doxyfile or public headers change 30 | add_custom_command(OUTPUT ${DOXYGEN_INDEX_FILE} 31 | DEPENDS ${LIBMEMPLUSPLUS_PUBLIC_HEADERS} 32 | COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYFILE_OUT} 33 | MAIN_DEPENDENCY ${DOXYFILE_OUT} ${DOXYFILE_IN} 34 | COMMENT "Generating docs" 35 | VERBATIM) 36 | 37 | # Nice named target so we can run the job easily 38 | add_custom_target(Doxygen ALL DEPENDS ${DOXYGEN_INDEX_FILE}) 39 | 40 | set(SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}) 41 | set(SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/sphinx) 42 | set(SPHINX_INDEX_FILE ${SPHINX_BUILD}/index.html) 43 | 44 | # Only regenerate Sphinx when: 45 | # - Doxygen has rerun 46 | # - Our doc files have been updated 47 | # - The Sphinx config has been updated 48 | add_custom_command(OUTPUT ${SPHINX_INDEX_FILE} 49 | COMMAND 50 | ${SPHINX_EXECUTABLE} -b html 51 | # Tell Breathe where to find the Doxygen output 52 | -Dbreathe_projects.memplusplus=${DOXYGEN_OUTPUT_DIR}/xml 53 | ${SPHINX_SOURCE} ${SPHINX_BUILD} 54 | WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} 55 | DEPENDS 56 | # Other docs files you want to track should go here (or in some variable) 57 | ${CMAKE_CURRENT_SOURCE_DIR}/index.rst 58 | ${DOXYGEN_INDEX_FILE} 59 | MAIN_DEPENDENCY ${SPHINX_SOURCE}/conf.py 60 | COMMENT "Generating documentation with Sphinx") 61 | 62 | # Nice named target so we can run the job easily 63 | add_custom_target(Sphinx ALL DEPENDS ${SPHINX_INDEX_FILE}) 64 | 65 | # Add an install target to install the docs 66 | include(GNUInstallDirs) 67 | install(DIRECTORY ${SPHINX_BUILD} 68 | DESTINATION ${CMAKE_INSTALL_DOCDIR}) -------------------------------------------------------------------------------- /cmake/clang-cxx-dev-tools.cmake: -------------------------------------------------------------------------------- 1 | # Additional targets to perform clang-format/clang-tidy 2 | # Get all project files 3 | file(GLOB_RECURSE 4 | ALL_CXX_SOURCE_FILES 5 | *.[chi]pp *.[chi]xx *.cc *.hh *.ii *.[CHI] *.inl *.tpp 6 | ) 7 | 8 | set(EXCLUDE_PATTERNS ${EXCLUDE_PATTERNS} "/CMakeFiles/" "cmake" "build" "libraries" "3rd_party" ".git" "tests") 9 | 10 | # Delete exclude patterns 11 | foreach (SOURCE_FILE ${ALL_CXX_SOURCE_FILES}) 12 | foreach (EXCLUDE_PATTERN ${EXCLUDE_PATTERNS}) 13 | string(FIND ${SOURCE_FILE} ${EXCLUDE_PATTERN} EXCLUDE_FOUND) 14 | if (NOT ${EXCLUDE_FOUND} EQUAL -1) 15 | list(REMOVE_ITEM ALL_CXX_SOURCE_FILES ${SOURCE_FILE}) 16 | endif () 17 | endforeach () 18 | endforeach () 19 | 20 | # Adding clang-format target if executable is found 21 | find_program(CLANG_FORMAT "clang-format-15") 22 | if(CLANG_FORMAT) 23 | add_custom_target( 24 | clang-format 25 | COMMAND ${CLANG_FORMAT} 26 | -i 27 | -style=file 28 | ${ALL_CXX_SOURCE_FILES} 29 | ) 30 | endif() 31 | 32 | # Find all headers for all projects 33 | # get_target_property(MPP_INCLUDES mpp INCLUDE_DIRECTORIES) 34 | # if (MPP_BUILD_FUZZER) 35 | # get_target_property(FUZZING_HARNESS_INCLUDES fuzzer INCLUDE_DIRECTORIES) 36 | # endif() 37 | # if (MPP_BUILD_EXAMPLE) 38 | # get_target_property(EXAMPLE_PROJECT_INCLUDES example_project INCLUDE_DIRECTORIES) 39 | # endif() 40 | 41 | # foreach(dir ${MPP_INCLUDES} ${FUZZING_HARNESS_INCLUDES} ${EXAMPLE_PROJECT_INCLUDES}) 42 | # string(APPEND PROJECTS_INCLUDE_DIRS "-I${dir} ") 43 | # endforeach() 44 | 45 | set(EXAMPLE_PROJECT_INCLUDES ${ALL_INCLUDE_DIRECTORIES} ${PROJECT_SOURCE_DIR}/example_project/include) 46 | set(FUZZING_HARNESS_INCLUDES ${ALL_INCLUDE_DIRECTORIES} ${PROJECT_SOURCE_DIR}/fuzzer/include) 47 | set(LIBMEMPLUSPLUS_INCLUDES ${ALL_INCLUDE_DIRECTORIES} ${PROJECT_SOURCE_DIR}/libmemplusplus/include) 48 | 49 | # Adding clang-tidy target if executable is found 50 | find_program(CLANG_TIDY "clang-tidy-15") 51 | if(CLANG_TIDY) 52 | add_custom_target( 53 | clang-tidy 54 | COMMAND ${CLANG_TIDY} 55 | ${ALL_CXX_SOURCE_FILES} 56 | -checks=clang-analyzer-*,cppcoreguidelines*,performance-*,portability-*,readability-*,hicpp-*,cert-* 57 | -- 58 | -std=c++17 59 | -I${EXAMPLE_PROJECT_INCLUDES} 60 | -I${FUZZING_HARNESS_INCLUDES} 61 | -I${LIBMEMPLUSPLUS_INCLUDES} 62 | -DMPP_STATS=1 63 | -DMPP_COLOUR=1 64 | -DMPP_PROFILE=1 65 | -DMPP_SECURE=1 66 | -DMPP_FULL_DEBUG=1 67 | -DMPP_BUILD_EXAMPLE=1 68 | -DMPP_FILL_CHAR="\\x99" 69 | ) 70 | endif() 71 | 72 | # Adding scan-build target if executable is found 73 | find_program(CLANG_STATIC_ANALYZER "scan-build-15") 74 | if(CLANG_STATIC_ANALYZER) 75 | add_custom_target( 76 | clang-static-analyzer 77 | COMMAND ${CLANG_STATIC_ANALYZER} ./build.sh 78 | ) 79 | endif() -------------------------------------------------------------------------------- /libmemplusplus/src/heuristics/heuristics.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/heuristics/heuristics.hpp" 2 | #include "mpplib/containers/vertex.hpp" 3 | #include "mpplib/utils/macros.hpp" 4 | #include 5 | 6 | namespace mpp { 7 | Heuristics::LayoutedHeap Heuristics::LayoutGeneralGraph(std::unique_ptr& t_graph) 8 | { 9 | PROFILE_FUNCTION(); 10 | 11 | std::vector layoutedHeap; 12 | std::size_t neededSpace = 0; 13 | 14 | for (const auto& vertex : t_graph->GetAdjList()) { 15 | if (!vertex->IsChunk()) 16 | continue; 17 | layoutedHeap.push_back(vertex); 18 | neededSpace += vertex->GetLocationAsAChunk()->GetSize(); 19 | } 20 | 21 | return LayoutedHeap{ layoutedHeap, neededSpace }; 22 | } 23 | 24 | Heuristics::LayoutedHeap Heuristics::LayoutLinkedList(std::unique_ptr& t_llGraph) 25 | { 26 | PROFILE_FUNCTION(); 27 | 28 | std::vector layoutedLL; 29 | std::size_t neededSpace = 0; 30 | 31 | // Lambda to check if vertex is the list head 32 | auto isListHead = [](Vertex* t_vertex) { 33 | return (t_vertex->GetNeighbors().size() == 1 && 34 | t_vertex->GetPointingToGcPtrs().size() == 1 && 35 | !(*t_vertex->GetPointingVertices().begin())->IsChunk()); 36 | }; 37 | 38 | Vertex* head = nullptr; 39 | // Find head of the list 40 | auto headIt = std::find_if( 41 | t_llGraph->GetAdjList().begin(), t_llGraph->GetAdjList().end(), isListHead); 42 | 43 | if (headIt != t_llGraph->GetAdjList().end()) 44 | head = *headIt; 45 | 46 | // Layout linked list starting from head 47 | while (head != nullptr) { 48 | Vertex* oldHead = head; 49 | if (head->IsChunk()) { 50 | layoutedLL.push_back(head); 51 | neededSpace += head->GetLocationAsAChunk()->GetSize(); 52 | } 53 | 54 | head = (!head->GetNeighbors().empty()) ? *head->GetNeighbors().begin() : nullptr; 55 | t_llGraph->RemoveVertex(oldHead); 56 | } 57 | 58 | return LayoutedHeap{ layoutedLL, neededSpace }; 59 | } 60 | 61 | Heuristics::LayoutedHeap Heuristics::LayoutHeap() 62 | { 63 | PROFILE_FUNCTION(); 64 | 65 | // Divide the graph into weakly connected components, but remove all unreachable vertices. 66 | auto subgraphs = m_objectsGraph->ReachableWeaklyConnectedComponents(); 67 | 68 | for (auto& graph : subgraphs) { 69 | LayoutedHeap layoutedHeap = LayoutGeneralGraph(graph); 70 | m_layoutedHeap.insert( 71 | m_layoutedHeap.end(), layoutedHeap.vertices.begin(), layoutedHeap.vertices.end()); 72 | m_neededSpace += layoutedHeap.layoutedSize; 73 | } 74 | 75 | return LayoutedHeap{ m_layoutedHeap, m_neededSpace }; 76 | } 77 | } -------------------------------------------------------------------------------- /libmemplusplus/src/containers/node.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/containers/node.hpp" 2 | 3 | namespace mpp { 4 | Node::Node(Chunk* t_chunk) 5 | : priority(std::rand()) 6 | , chunk(t_chunk) 7 | , leftChild(nullptr) 8 | , rightChild(nullptr) 9 | { 10 | } 11 | 12 | Node::Node(const Node& t_node) 13 | : priority(t_node.priority) 14 | , chunk(t_node.chunk) 15 | , leftChild(t_node.leftChild ? new Node(*t_node.leftChild) : nullptr) 16 | , rightChild(t_node.rightChild ? new Node(*t_node.rightChild) : nullptr) 17 | { 18 | // @TODO: - smart pointers memory management 19 | } 20 | 21 | Node& Node::operator=(const Node& t_node) 22 | { 23 | if (&t_node == this) 24 | return *this; 25 | 26 | Delete(); 27 | 28 | priority = t_node.priority; 29 | chunk = t_node.chunk; 30 | 31 | // TODO - smart pointers memory management 32 | leftChild = t_node.leftChild ? new Node(*t_node.leftChild) : nullptr; 33 | rightChild = t_node.rightChild ? new Node(*t_node.rightChild) : nullptr; 34 | 35 | return *this; 36 | } 37 | 38 | Node::Node(Node&& t_node) noexcept 39 | : priority(t_node.priority) 40 | , chunk(t_node.chunk) 41 | , leftChild(t_node.leftChild) 42 | , rightChild(t_node.rightChild) 43 | { 44 | t_node.chunk = nullptr; 45 | t_node.leftChild = nullptr; 46 | t_node.rightChild = nullptr; 47 | } 48 | 49 | Node& Node::operator=(Node&& t_node) noexcept 50 | { 51 | if (&t_node == this) 52 | return *this; 53 | 54 | // Delete current object 55 | Delete(); 56 | 57 | // Construct new one 58 | priority = t_node.priority; 59 | chunk = t_node.chunk; 60 | leftChild = t_node.leftChild; 61 | rightChild = t_node.rightChild; 62 | 63 | t_node.chunk = nullptr; 64 | t_node.leftChild = nullptr; 65 | t_node.rightChild = nullptr; 66 | 67 | return *this; 68 | } 69 | 70 | Node::~Node() 71 | { 72 | Delete(); 73 | } 74 | 75 | void Node::Delete() 76 | { 77 | delete leftChild; 78 | leftChild = nullptr; 79 | delete rightChild; 80 | rightChild = nullptr; 81 | } 82 | 83 | #if MPP_STATS == 1 84 | std::ostream& DumpNode(std::ostream& t_out, Node* t_node) 85 | { 86 | t_out << "[" << reinterpret_cast(t_node) << "]" 87 | << "(" << std::to_string(t_node->priority) << ", {" << t_node->chunk 88 | << "}, L:" << reinterpret_cast(t_node->leftChild) 89 | << ", R:" << reinterpret_cast(t_node->rightChild); 90 | return t_out; 91 | } 92 | 93 | std::ostream& operator<<(std::ostream& t_out, Node* t_node) 94 | { 95 | DumpNode(t_out, t_node); 96 | return t_out; 97 | } 98 | #endif 99 | 100 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | # ################ SET GLOBAL VARIABLES ################# 4 | set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) 5 | set(CMAKE_CXX_STANDARD 20) 6 | 7 | # ################ DEFINE PROJECT ################# 8 | project(memplusplus LANGUAGES CXX) 9 | 10 | # ################ SET WARNING LEVELS ################# 11 | if(MSVC) 12 | add_compile_options(/W4) 13 | else() 14 | add_compile_options(-Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-unused-variable -Wno-exceptions -Wno-c++20-attribute-extensions) 15 | endif() 16 | 17 | # ################ GLOBAL OPTIONS ################# 18 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 19 | option(MPP_ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" OFF) 20 | endif() 21 | 22 | option(MPP_BUILD_FUZZER "Build mpp fuzzing harness" OFF) 23 | option(MPP_USE_FUZZER_DEFINITIONS "Build mpp with fuzzing-friendly defines" OFF) 24 | option(MPP_BUILD_EXAMPLE "Build mpp example project" ON) 25 | option(MPP_BUILD_TESTS "Build mpp tests" ON) 26 | option(MPP_BUILD_DOCS "Build mpp documentation" OFF) 27 | option(CMAKE_EXPORT_COMPILE_COMMANDS "Request generate of json file with all build commands" ON) 28 | 29 | message(STATUS "##################### GLOBAL OPTIONS #####################") 30 | message(STATUS "MPP_ENABLE_COVERAGE : ${MPP_ENABLE_COVERAGE}") 31 | message(STATUS "MPP_BUILD_FUZZER : ${MPP_BUILD_FUZZER}") 32 | message(STATUS "MPP_USE_FUZZER_DEFINITIONS : ${MPP_USE_FUZZER_DEFINITIONS}") 33 | message(STATUS "MPP_BUILD_EXAMPLE : ${MPP_BUILD_EXAMPLE}") 34 | message(STATUS "MPP_BUILD_TESTS : ${MPP_BUILD_TESTS}") 35 | message(STATUS "MPP_BUILD_DOCS : ${MPP_BUILD_DOCS}") 36 | 37 | # ################ ADD PROJECTS + DOCS + TESTS ################# 38 | add_subdirectory(libmemplusplus) 39 | 40 | if(MPP_ENABLE_COVERAGE) 41 | message(STATUS "[+] Building with code coverage support") 42 | target_compile_options(mpp PRIVATE --coverage -O0 -g -fno-inline) 43 | target_link_options(mpp INTERFACE --coverage) 44 | endif() 45 | 46 | if(MPP_BUILD_FUZZER) 47 | message(STATUS "[+] Building with fuzzer support!") 48 | message(STATUS " WARNING: Library will be build with sanitizers + with insecure defines!") 49 | enable_testing() 50 | add_subdirectory(fuzzer) 51 | endif() 52 | 53 | if(MPP_BUILD_EXAMPLE) 54 | message(STATUS "[+] Building with example project") 55 | add_subdirectory(example_project) 56 | endif() 57 | 58 | if(MPP_BUILD_TESTS) 59 | message(STATUS "[+] Building with tests") 60 | enable_testing() 61 | 62 | add_subdirectory(libraries/googletest) 63 | add_subdirectory(tests) 64 | 65 | if(MPP_ENABLE_COVERAGE) 66 | target_compile_options(unit_tests PRIVATE --coverage -O0 -g -fno-inline) 67 | target_link_options(unit_tests INTERFACE --coverage) 68 | endif() 69 | endif() 70 | 71 | if(MPP_BUILD_DOCS) 72 | message(STATUS "[+] Building with documentation") 73 | add_subdirectory(docs) 74 | endif() 75 | 76 | # ################ CLANG FORMAT / CLANG-TIDY ################# 77 | include(cmake/clang-cxx-dev-tools.cmake) -------------------------------------------------------------------------------- /.github/workflows/codeql.yml: -------------------------------------------------------------------------------- 1 | # For most projects, this workflow file will not need changing; you simply need 2 | # to commit it to your repository. 3 | # 4 | # You may wish to alter this file to override the set of languages analyzed, 5 | # or to provide custom queries or build logic. 6 | # 7 | # ******** NOTE ******** 8 | # We have attempted to detect the languages in your repository. Please check 9 | # the `language` matrix defined below to confirm you have the correct set of 10 | # supported CodeQL languages. 11 | # 12 | name: "CodeQL" 13 | 14 | on: 15 | push: 16 | branches: ["master"] 17 | pull_request: 18 | # The branches below must be a subset of the branches above 19 | branches: ["master"] 20 | 21 | jobs: 22 | analyze: 23 | name: Analyze 24 | runs-on: ubuntu-latest 25 | permissions: 26 | actions: read 27 | contents: read 28 | security-events: write 29 | 30 | strategy: 31 | fail-fast: false 32 | matrix: 33 | language: ["cpp"] 34 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 35 | # Use only 'java' to analyze code written in Java, Kotlin or both 36 | # Use only 'javascript' to analyze code written in JavaScript, TypeScript or both 37 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 38 | 39 | steps: 40 | - name: Checkout repository 41 | uses: actions/checkout@v3 42 | 43 | # Initializes the CodeQL tools for scanning. 44 | - name: Initialize CodeQL 45 | uses: github/codeql-action/init@v2 46 | with: 47 | languages: ${{ matrix.language }} 48 | # If you wish to specify custom queries, you can do so here or in a config file. 49 | # By default, queries listed here will override any specified in a config file. 50 | # Prefix the list here with "+" to use these queries and those in the config file. 51 | 52 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 53 | # queries: security-extended,security-and-quality 54 | 55 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 56 | # If this step fails, then you should remove it and run the build manually (see below) 57 | # - name: Autobuild 58 | # uses: github/codeql-action/autobuild@v2 59 | 60 | # ℹ️ Command-line programs to run using the OS shell. 61 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 62 | 63 | # If the Autobuild fails above, remove it and uncomment the following three lines. 64 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 65 | 66 | - run: | 67 | echo "Run, Build Application using script" 68 | ./build.sh codeql 69 | 70 | - name: Perform CodeQL Analysis 71 | uses: github/codeql-action/analyze@v2 72 | with: 73 | category: "/language:${{matrix.language}}" 74 | -------------------------------------------------------------------------------- /libmemplusplus/src/containers/vertex.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/containers/vertex.hpp" 2 | #include "mpplib/gc.hpp" 3 | #include "mpplib/utils/macros.hpp" 4 | #include "mpplib/utils/utils.hpp" 5 | 6 | namespace mpp { 7 | bool Vertex::IsChunk() const 8 | { 9 | return m_currLocationIsAChunk; 10 | } 11 | 12 | bool Vertex::IsRoot() const 13 | { 14 | return !m_currLocationIsAChunk; 15 | } 16 | 17 | void Vertex::UpdateLocationPtr(std::byte* t_location) 18 | { 19 | m_correspondingLocation = t_location; 20 | } 21 | 22 | void Vertex::AddNeighbor(Vertex* t_neighbor) 23 | { 24 | m_neighbors.insert(t_neighbor); 25 | } 26 | 27 | void Vertex::AddPointingVertex(Vertex* t_pointingVertex) 28 | { 29 | m_pointingToVertices.insert(t_pointingVertex); 30 | } 31 | 32 | std::set& Vertex::GetPointingVertices() 33 | { 34 | return m_pointingToVertices; 35 | } 36 | 37 | bool Vertex::RemoveNeighbor(Vertex* t_neighbor) 38 | { 39 | return m_neighbors.erase(t_neighbor); 40 | } 41 | 42 | std::set& Vertex::GetNeighbors() 43 | { 44 | return m_neighbors; 45 | } 46 | 47 | void Vertex::AddGcPtr(GcPtr* t_gcPtr) 48 | { 49 | m_pointingToGcPtrs.insert(t_gcPtr); 50 | } 51 | 52 | bool Vertex::RemoveGcPtr(GcPtr* t_gcPtr) 53 | { 54 | PROFILE_FUNCTION(); 55 | // Remove GcPtr pointer from current vertex 56 | auto toErase = m_pointingToGcPtrs.find(t_gcPtr); 57 | if (toErase != m_pointingToGcPtrs.end()) { 58 | m_pointingToGcPtrs.erase(toErase); 59 | return true; 60 | } 61 | return false; 62 | } 63 | 64 | std::set Vertex::GetAllOutgoingGcPtrs(const std::set& t_gcPtrs) 65 | { 66 | std::set::iterator begin; 67 | std::set::iterator end; 68 | 69 | begin = t_gcPtrs.lower_bound(reinterpret_cast(m_correspondingLocation)); 70 | if (m_currLocationIsAChunk) { 71 | end = t_gcPtrs.upper_bound(reinterpret_cast(m_correspondingLocation + 72 | GetLocationAsAChunk()->GetSize())); 73 | } else { 74 | end = t_gcPtrs.upper_bound(reinterpret_cast(m_correspondingLocation)); 75 | } 76 | 77 | return std::set(begin, end); 78 | } 79 | 80 | std::set& Vertex::GetPointingToGcPtrs() 81 | { 82 | return m_pointingToGcPtrs; 83 | } 84 | 85 | std::byte* Vertex::GetLoc() const 86 | { 87 | return m_correspondingLocation; 88 | } 89 | 90 | Chunk* Vertex::GetLocationAsAChunk() const 91 | { 92 | MPP_DEBUG_ASSERT(m_currLocationIsAChunk, "Current location is not a chunk!"); 93 | return reinterpret_cast(m_correspondingLocation); 94 | } 95 | 96 | std::string Vertex::ToString() const 97 | { 98 | std::stringstream stringStream; 99 | stringStream << static_cast(m_correspondingLocation); 100 | return stringStream.str(); 101 | } 102 | } -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Mozilla 4 | AccessModifierOffset: -4 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: false 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlines: Right 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: false 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: false 15 | AllowShortIfStatementsOnASingleLine: Never 16 | AllowShortLoopsOnASingleLine: false 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: false 20 | AlwaysBreakTemplateDeclarations: Yes 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterControlStatement: Always 25 | AfterClass: true 26 | AfterEnum: true 27 | AfterFunction: true 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: true 31 | AfterUnion: true 32 | BeforeCatch: false 33 | BeforeElse: true 34 | IndentBraces: false 35 | SplitEmptyFunction: false 36 | SplitEmptyRecord: false 37 | SplitEmptyNamespace: true 38 | BreakBeforeBinaryOperators: None 39 | BreakBeforeBraces: Mozilla 40 | BreakBeforeInheritanceComma: true 41 | BreakBeforeTernaryOperators: true 42 | BreakConstructorInitializers: BeforeComma 43 | BreakAfterJavaFieldAnnotations: false 44 | BreakStringLiterals: true 45 | ColumnLimit: 100 46 | CommentPragmas: '^ IWYU pragma:' 47 | CompactNamespaces: true 48 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 49 | ConstructorInitializerIndentWidth: 4 50 | ContinuationIndentWidth: 4 51 | Cpp11BracedListStyle: false 52 | DerivePointerAlignment: false 53 | DisableFormat: false 54 | ExperimentalAutoDetectBinPacking: false 55 | FixNamespaceComments: false 56 | ForEachMacros: 57 | - foreach 58 | - Q_FOREACH 59 | - BOOST_FOREACH 60 | IncludeCategories: 61 | - Regex: '^"(llvm|llvm-c|clang|clang-c)/' 62 | Priority: 2 63 | - Regex: '^(<|"(gtest|gmock|isl|json)/)' 64 | Priority: 3 65 | - Regex: '.*' 66 | Priority: 1 67 | IncludeIsMainRegex: '(Test)?$' 68 | IndentCaseLabels: true 69 | IndentWidth: 4 70 | IndentWrappedFunctionNames: false 71 | JavaScriptQuotes: Leave 72 | JavaScriptWrapImports: true 73 | KeepEmptyLinesAtTheStartOfBlocks: false 74 | MacroBlockBegin: '' 75 | MacroBlockEnd: '' 76 | MaxEmptyLinesToKeep: 1 77 | NamespaceIndentation: All 78 | ObjCBlockIndentWidth: 2 79 | ObjCSpaceAfterProperty: true 80 | ObjCSpaceBeforeProtocolList: false 81 | PenaltyBreakAssignment: 2 82 | PenaltyBreakBeforeFirstCallParameter: 19 83 | PenaltyBreakComment: 600 84 | PenaltyBreakFirstLessLess: 120 85 | PenaltyBreakString: 1000 86 | PenaltyExcessCharacter: 1000000 87 | PenaltyReturnTypeOnItsOwnLine: 9999 88 | PointerAlignment: Left 89 | ReflowComments: true 90 | SortIncludes: true 91 | SortUsingDeclarations: true 92 | SpaceAfterCStyleCast: false 93 | SpaceAfterTemplateKeyword: false 94 | SpaceBeforeAssignmentOperators: true 95 | SpaceBeforeParens: ControlStatements 96 | SpaceInEmptyParentheses: false 97 | SpacesBeforeTrailingComments: 1 98 | SpacesInAngles: false 99 | SpacesInContainerLiterals: true 100 | SpacesInCStyleCastParentheses: false 101 | SpaceBeforeRangeBasedForLoopColon: true 102 | SpaceBeforeInheritanceColon: true 103 | SpacesInParentheses: false 104 | SpaceBeforeCpp11BracedList: false 105 | SpacesInSquareBrackets: false 106 | IndentAccessModifiers: false 107 | Standard: Cpp11 108 | TabWidth: 8 109 | UseTab: Never 110 | ... 111 | 112 | -------------------------------------------------------------------------------- /libmemplusplus/src/utils/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/utils.hpp" 2 | 3 | #if MPP_DEBUG == 1 4 | #include // for __cxa_demangle 5 | #include // for dladdr 6 | #endif 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define __STDC_WANT_LIB_EXT1__ 1 16 | #include 17 | #include 18 | #include // for memset_s 19 | 20 | namespace mpp { namespace utils { 21 | 22 | void* SecureMemset(void* t_pointer, 23 | size_t t_targetBufferSize, 24 | unsigned char t_fillChar, 25 | size_t t_sizeToRemove) 26 | { 27 | #ifdef __STDC_LIB_EXT1__ 28 | memset_s(pointer, size_data, t_fillChar, size_to_remove); 29 | #else 30 | if (t_sizeToRemove > t_targetBufferSize) 31 | t_sizeToRemove = t_targetBufferSize; 32 | volatile unsigned char* dataPtr = static_cast(t_pointer); 33 | while ((t_sizeToRemove--) != 0u) { 34 | *dataPtr++ = t_fillChar; 35 | } 36 | #endif 37 | return t_pointer; 38 | } 39 | 40 | #if MPP_DEBUG == 1 41 | void DumpStackTrace(std::ostream& t_out, int32_t t_skip) 42 | { 43 | std::array callstack{}; 44 | std::array buf{}; // NOLINT 45 | int32_t frames = backtrace(callstack.data(), s_MAX_STACK_LEVELS); 46 | char** symbols = backtrace_symbols(callstack.data(), frames); 47 | 48 | for (int32_t i = t_skip; i < frames; ++i) { 49 | Dl_info info; 50 | if (dladdr(callstack.at(i), &info)) { 51 | char* demangled = NULL; 52 | int32_t status = 0; 53 | demangled = abi::__cxa_demangle(info.dli_sname, NULL, nullptr, &status); 54 | // NOLINTNEXTLINE 55 | std::snprintf(buf.data(), 56 | sizeof(buf), 57 | "%3d: %p - %s + %zd\n", 58 | i, 59 | callstack.at(i), 60 | status == 0 ? demangled : info.dli_sname, 61 | reinterpret_cast(callstack.at(i)) - 62 | reinterpret_cast(info.dli_saddr)); 63 | free(demangled); // NOLINT 64 | } else { 65 | // NOLINTNEXTLINE 66 | std::snprintf(buf.data(), sizeof(buf), "%-3d %p\n", i, 67 | callstack.at(i)); // NOLINT 68 | } 69 | t_out << buf.data(); 70 | // NOLINTNEXTLINE 71 | std::snprintf(buf.data(), 72 | sizeof(buf), 73 | "%*sat %s\n", 74 | static_cast((2 + sizeof(void*)) + 9), // NOLINT 75 | "", 76 | symbols[i]); 77 | t_out << buf.data(); 78 | } 79 | 80 | if (frames == s_MAX_STACK_LEVELS) { 81 | t_out << "[truncated]\n"; 82 | } 83 | 84 | free(symbols); // NOLINT 85 | } 86 | #endif 87 | 88 | void ErrorAbort(const std::string& t_message) 89 | { 90 | // Print error message 91 | std::cerr << t_message; 92 | 93 | #if MPP_DEBUG == 1 94 | // Dump stacktrace in debug 95 | DumpStackTrace(std::cerr); 96 | #endif 97 | // terminate the program using default terminate handler 98 | std::terminate(); 99 | } 100 | 101 | std::string AddrToString(void* t_ptr) 102 | { 103 | std::stringstream addrToStrSS; 104 | addrToStrSS << static_cast(t_ptr); 105 | return addrToStrSS.str(); 106 | } 107 | }} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/heuristics/heuristics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/containers/gc_graph.hpp" 4 | #include "mpplib/containers/vertex.hpp" 5 | #include "mpplib/utils/profiler_definitions.hpp" 6 | 7 | #include 8 | 9 | namespace mpp { 10 | /** 11 | * @brief Implements heuristics, that helps to relayout heap, 12 | * to efficiently place objects in memory. 13 | */ 14 | class Heuristics 15 | { 16 | private: 17 | /** 18 | * @brief All possible data structures, that heuristics 19 | * can identify. 20 | */ 21 | enum class DataStructures 22 | { 23 | LinkedList, 24 | DoubleLinkedList, 25 | Tree, 26 | Graph, 27 | Undefined 28 | }; 29 | 30 | /** 31 | * @brief Size required for new arena. 32 | */ 33 | std::size_t m_neededSpace{ 0 }; 34 | 35 | //! @brief Fully layouted heap. 36 | std::vector m_layoutedHeap; 37 | 38 | //! @brief Graph of all objects. 39 | std::unique_ptr& m_objectsGraph; 40 | 41 | public: 42 | //! @brief Layouted heap structure. Represents compacted and layouted memory. 43 | struct LayoutedHeap 44 | { 45 | //! @brief All chunks inside layouted heap. 46 | std::vector vertices; 47 | //! @brief Size of all chunks inside layouted heap. 48 | std::size_t layoutedSize; 49 | 50 | /** 51 | * @brief Construct a new Layouted Heap object 52 | * @param vertices All chunks inside layouted heap. 53 | * @param layoutedSize Size of all chunks inside layouted heap. 54 | */ 55 | LayoutedHeap(std::vector& vertices, std::size_t layoutedSize) 56 | : vertices(vertices) 57 | , layoutedSize(layoutedSize) 58 | { 59 | } 60 | }; 61 | 62 | /** 63 | * @brief Construct a new Heuristics object 64 | * @param t_objectsGraph Graph of all objects. 65 | */ 66 | explicit Heuristics(std::unique_ptr& t_objectsGraph) 67 | : m_objectsGraph(t_objectsGraph) 68 | { 69 | } 70 | 71 | ~Heuristics() = default; 72 | 73 | //! @brief Deleted copy constructor. 74 | Heuristics(const Heuristics&) = delete; 75 | 76 | //! @brief Deleted copy assignment operator. 77 | Heuristics& operator=(const Heuristics&) = delete; 78 | 79 | //! @brief Deleted move constructor. 80 | Heuristics(Heuristics&&) = delete; 81 | 82 | //! @brief Deleted move assignment operator. 83 | Heuristics& operator=(Heuristics&&) = delete; 84 | 85 | /** 86 | * @brief Layouts heap. 87 | * 88 | * This method tries to find all possible data structures in graph, and layout 89 | * them in the most efficient way. 90 | * @param t_objectsGraph reference to unique_ptr to GcGraph (will be divided into subgraphs) 91 | * @return pair of vector of vertices and size of all chunks 92 | */ 93 | LayoutedHeap LayoutHeap(); 94 | 95 | /** 96 | * @brief Tries to layout LinkedList data structure in the most efficient way. 97 | * @param t_llGraph Graph that should (but not necessarily) represent LinkedList. 98 | * @return LayoutedHeap Successfully layouted t_llGraph or its part. 99 | */ 100 | static LayoutedHeap LayoutLinkedList(std::unique_ptr& t_llGraph); 101 | 102 | /** 103 | * @brief Layouts unknown data structure in the most efficient way. 104 | * @param t_graph Graph with chunks that should be layouted. 105 | * @return LayoutedHeap Successfully layouted t_graph. 106 | */ 107 | static LayoutedHeap LayoutGeneralGraph(std::unique_ptr& t_graph); 108 | }; 109 | } 110 | -------------------------------------------------------------------------------- /fuzzer/src/tokenizer.cpp: -------------------------------------------------------------------------------- 1 | #include "fuzzer/tokenizer.hpp" 2 | #include "fuzzer/fuzzer.h" 3 | #include "mpplib/utils/macros.hpp" 4 | #include 5 | 6 | namespace mpp { namespace fuzzer { 7 | std::vector Tokenizer::Tokenize(const uint8_t* t_data, std::size_t t_size) 8 | { 9 | const uint8_t* dataStart = t_data; 10 | const uint8_t* dataEnd = (t_data + t_size); 11 | const uint8_t* currentDataPtr = dataStart; 12 | const std::size_t c_maxCommands = 128; 13 | 14 | std::vector commands; 15 | commands.reserve(c_maxCommands); 16 | 17 | while (currentDataPtr < dataEnd) { 18 | Command currentCommand; 19 | switch (*currentDataPtr++) { 20 | case 'a' /* Allocate */: { 21 | const std::size_t c_maxAllocSize = 0x4000400; 22 | GET_UINT32(allocSize, (allocSize >= c_maxAllocSize)); 23 | 24 | currentCommand = Command(Operation::Allocate, { allocSize }); 25 | break; 26 | } 27 | case 'd' /* Deallocate */: { 28 | currentCommand = Command(Operation::Deallocate, {}); 29 | break; 30 | } 31 | case 'C' /* Create shared pointer */: { 32 | // Command format: S 33 | GET_UINT16(pointerIdx, (pointerIdx >= c_maxPointers)); 34 | 35 | currentCommand = Command(Operation::CreateVertex, { pointerIdx }); 36 | break; 37 | } 38 | case 'D' /* Remove shared pointer */: { 39 | // Command format: D 40 | GET_UINT16(pointerIdx, (pointerIdx >= c_maxPointers)); 41 | 42 | currentCommand = Command(Operation::RemoveVertex, { pointerIdx }); 43 | break; 44 | } 45 | case 'E' /* Creates an edge between vtx1 and vtx2 */: { 46 | // Command format: E,, 47 | // Adds vtx1 to a list of pointers of the vtx2 at the index idx. 48 | GET_UINT16(vtxIdx1, (vtxIdx1 >= c_maxPointers)); 49 | GET_UINT16(vtxIdx2, (vtxIdx2 >= c_maxPointers)); 50 | GET_UINT16(ptrIdx, (ptrIdx >= c_maxPointersInAVertex)); 51 | 52 | currentCommand = Command(Operation::CreateEdge, { vtxIdx1, vtxIdx2, ptrIdx }); 53 | break; 54 | } 55 | case 'R' /* Removes an edge between vtx1 and vtx2 */: { 56 | // Command format: R, 57 | // Removes a pointer at the index idx from the list of pointers of the vtx1. 58 | GET_UINT16(vtxIdx1, (vtxIdx1 >= c_maxPointers)); 59 | GET_UINT16(vtxIdx2, (vtxIdx2 >= c_maxPointersInAVertex)); 60 | 61 | currentCommand = Command(Operation::RemoveEdge, { vtxIdx1, vtxIdx2 }); 62 | break; 63 | } 64 | case 'T' /* Finds a route from vtx1 to vtx2 (DFS) */: { 65 | // @TODO: Implement simple DFS 66 | break; 67 | } 68 | case 'P' /* Read data from vertex */: { 69 | GET_UINT16(vtxIdx, (vtxIdx >= c_maxPointers)); 70 | currentCommand = Command(Operation::ReadSharedData, { vtxIdx }); 71 | break; 72 | } 73 | case 'G' /* Collect garbage */: { 74 | // Calls mpp::CollectGarbage() 75 | currentCommand = Command(Operation::CollectGarbage, {}); 76 | break; 77 | } 78 | 79 | default: { 80 | currentCommand = Command(Operation::Invalid, {}); 81 | break; 82 | } 83 | } 84 | 85 | commands.push_back(currentCommand); 86 | 87 | if (commands.back().GetOp() == Operation::Invalid || commands.size() > c_maxCommands) { 88 | break; 89 | } 90 | } 91 | 92 | return commands; 93 | } 94 | }} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/log.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace mpp { namespace logging { 8 | enum class LogLevel 9 | { 10 | TRACE, 11 | DEBUG, 12 | INFO, 13 | WARNING, 14 | ERROR, 15 | FATAL, 16 | DISABLED 17 | }; 18 | 19 | static const std::unordered_map s_LogLevelToString = { 20 | { LogLevel::TRACE, "TRACE" }, { LogLevel::DEBUG, "DEBUG" }, 21 | { LogLevel::INFO, "INFO" }, { LogLevel::WARNING, "WARNING" }, 22 | { LogLevel::ERROR, "ERROR" }, { LogLevel::FATAL, "FATAL" }, 23 | { LogLevel::DISABLED, "DISABLED" } 24 | }; 25 | 26 | static const std::unordered_map s_StringToLogLevel = { 27 | { "TRACE", LogLevel::TRACE }, { "DEBUG", LogLevel::DEBUG }, 28 | { "INFO", LogLevel::INFO }, { "WARNING", LogLevel::WARNING }, 29 | { "ERROR", LogLevel::ERROR }, { "FATAL", LogLevel::FATAL }, 30 | { "DISABLED", LogLevel::DISABLED } 31 | }; 32 | 33 | LogLevel LogLevelFromString(const std::string& logLevel); 34 | }} 35 | 36 | #define MPP_COLOR_GREY "\u001b[90m" 37 | #define MPP_COLOR_YELLOW "\u001b[33m" 38 | #define MPP_COLOR_RED "\u001b[31m" 39 | #define MPP_COLOR_RESET "\u001b[0m" 40 | 41 | #define MPP_LOG(LEVEL, LEVEL_FMT, FMT, ...) \ 42 | if (LEVEL >= mpp::utils::EnvOptions::Get().GetMppLogLevel()) { \ 43 | /* NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ \ 44 | std::printf(LEVEL_FMT FMT __VA_OPT__(, ) __VA_ARGS__); \ 45 | } 46 | 47 | #define MPP_LOG_TRACE(FMT, ...) \ 48 | MPP_LOG(mpp::logging::LogLevel::TRACE, \ 49 | MPP_COLOR_GREY "🐾 [TRACE] ", \ 50 | FMT MPP_COLOR_RESET, \ 51 | __VA_ARGS__) 52 | #define MPP_LOG_DEBUG(FMT, ...) \ 53 | MPP_LOG(mpp::logging::LogLevel::DEBUG, \ 54 | MPP_COLOR_GREY "❓ [DEBUG] ", \ 55 | FMT MPP_COLOR_RESET, \ 56 | __VA_ARGS__) 57 | #define MPP_LOG_INFO(FMT, ...) \ 58 | MPP_LOG(mpp::logging::LogLevel::INFO, \ 59 | MPP_COLOR_GREY "📘 [INFO] ", \ 60 | FMT MPP_COLOR_RESET, \ 61 | __VA_ARGS__) 62 | #define MPP_LOG_WARNING(FMT, ...) \ 63 | MPP_LOG(mpp::logging::LogLevel::WARNING, \ 64 | MPP_COLOR_YELLOW "🚧 [WARNING] ", \ 65 | FMT MPP_COLOR_RESET, \ 66 | __VA_ARGS__) 67 | #define MPP_LOG_ERROR(FMT, ...) \ 68 | MPP_LOG(mpp::logging::LogLevel::ERROR, \ 69 | MPP_COLOR_RED "❌ [ERROR] ", \ 70 | FMT MPP_COLOR_RESET, \ 71 | __VA_ARGS__) 72 | #define MPP_LOG_FATAL(FMT, ...) \ 73 | MPP_LOG(mpp::logging::LogLevel::FATAL, \ 74 | MPP_COLOR_RED "⛔ [FATAL] ", \ 75 | FMT MPP_COLOR_RESET, \ 76 | __VA_ARGS__) \ 77 | mpp::utils::ErrorAbort("Fatal error occurred!"); -------------------------------------------------------------------------------- /libmemplusplus/src/containers/visualizers/chunk_treap_visualizer.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/containers/chunk_treap.hpp" 2 | 3 | namespace mpp { 4 | std::ostream& ChunkTreap::GenerateGraphvizLayout(std::ostream& t_out, 5 | const std::string& t_treapName, 6 | Node* t_root) const 7 | { 8 | PROFILE_FUNCTION(); 9 | 10 | std::function DumpNode = 11 | [](Node* t_node, std::ostream& t_out) -> std::ostream& { 12 | t_out << " [label=\"chunk = " << reinterpret_cast(t_node->chunk) << "\n" 13 | << "chunk.size = " << t_node->chunk->GetSize() << std::endl; 14 | t_out << "priority = " << t_node->priority << "\"];"; 15 | return t_out; 16 | }; 17 | 18 | std::function GenerateNull = 19 | [&DumpNode](Node* t_node, uint32_t t_nullCount, std::ostream& t_out) { 20 | t_out << " null" << t_nullCount << " [shape=point];" << std::endl; 21 | t_out << " " << (reinterpret_cast(t_node->chunk) ^ t_node->priority); 22 | DumpNode(t_node, t_out) << std::endl; 23 | t_out << " " << (reinterpret_cast(t_node->chunk) ^ t_node->priority) 24 | << " -> null" << t_nullCount << ";\n" 25 | << std::endl; 26 | }; 27 | 28 | std::function GenerateRecursive = 29 | [&GenerateNull, &GenerateRecursive, &DumpNode](Node* t_node, std::ostream& t_out) { 30 | static uint32_t nullCount = 0; 31 | 32 | if (t_node->leftChild != nullptr) { 33 | t_out << " " 34 | << (reinterpret_cast(t_node->chunk) ^ t_node->priority); 35 | DumpNode(t_node, t_out) << std::endl; 36 | 37 | t_out << " " 38 | << (reinterpret_cast(t_node->leftChild->chunk) ^ 39 | t_node->leftChild->priority); 40 | DumpNode(t_node->leftChild, t_out) << std::endl; 41 | 42 | t_out << " " 43 | << (reinterpret_cast(t_node->chunk) ^ t_node->priority) 44 | << " -> " 45 | << (reinterpret_cast(t_node->leftChild->chunk) ^ 46 | t_node->leftChild->priority) 47 | << ";\n" 48 | << std::endl; 49 | GenerateRecursive(t_node->leftChild, t_out); 50 | } else { 51 | GenerateNull(t_node, nullCount++, t_out); 52 | } 53 | 54 | if (t_node->rightChild != nullptr) { 55 | t_out << " " 56 | << (reinterpret_cast(t_node->chunk) ^ t_node->priority); 57 | DumpNode(t_node, t_out) << std::endl; 58 | 59 | t_out << " " 60 | << (reinterpret_cast(t_node->rightChild->chunk) ^ 61 | t_node->rightChild->priority); 62 | DumpNode(t_node->rightChild, t_out) << std::endl; 63 | 64 | t_out << " " 65 | << (reinterpret_cast(t_node->chunk) ^ t_node->priority) 66 | << " -> " 67 | << (reinterpret_cast(t_node->rightChild->chunk) ^ 68 | t_node->rightChild->priority) 69 | << ";\n" 70 | << std::endl; 71 | GenerateRecursive(t_node->rightChild, t_out); 72 | } else { 73 | GenerateNull(t_node, nullCount++, t_out); 74 | } 75 | }; 76 | 77 | t_out << "digraph " << t_treapName << " {\n"; 78 | t_out << " node [shape=rectangle]\n"; 79 | 80 | Node* startRoot = (t_root == nullptr) ? m_root : t_root; 81 | 82 | if (startRoot == nullptr) { 83 | t_out << std::endl; 84 | } else if (startRoot->leftChild == nullptr && startRoot->rightChild == nullptr) { 85 | t_out << " " << (reinterpret_cast(startRoot->chunk) ^ startRoot->priority) 86 | << " [label=\"chunk: " << reinterpret_cast(startRoot->chunk) 87 | << "\npriority: " << startRoot->priority << "\"];" << std::endl; 88 | } else { 89 | GenerateRecursive(startRoot, t_out); 90 | } 91 | 92 | t_out << "}" << std::endl; 93 | 94 | return t_out; 95 | } 96 | } -------------------------------------------------------------------------------- /fuzzer/include/fuzzer/tokenizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | namespace mpp { namespace fuzzer { 12 | #define CHECK_MORE_DATA() \ 13 | if (currentDataPtr >= dataEnd) { \ 14 | currentCommand = Command(Operation::Invalid, {}); \ 15 | break; \ 16 | } 17 | 18 | #define CHECK_HAS_AT_LEAST_N(length) \ 19 | if (currentDataPtr + length > dataEnd) { \ 20 | currentCommand = Command(Operation::Invalid, {}); \ 21 | break; \ 22 | } 23 | 24 | #define CONSUME_SINGLE() \ 25 | CHECK_HAS_AT_LEAST_N(sizeof(uint8_t)); \ 26 | currentDataPtr++; 27 | 28 | #define GET_PRIMITIVE_CHECKED(type, var_name, cond) \ 29 | CHECK_HAS_AT_LEAST_N(sizeof(type)); \ 30 | type var_name = *reinterpret_cast(currentDataPtr); \ 31 | \ 32 | if ((cond)) { \ 33 | currentCommand = Command(Operation::Invalid, {}); \ 34 | break; \ 35 | } \ 36 | \ 37 | currentDataPtr += sizeof(type); 38 | 39 | #define GET_UINT8(var_name, cond) GET_PRIMITIVE_CHECKED(uint8_t, var_name, cond) 40 | #define GET_UINT16(var_name, cond) GET_PRIMITIVE_CHECKED(uint16_t, var_name, cond) 41 | #define GET_UINT32(var_name, cond) GET_PRIMITIVE_CHECKED(uint32_t, var_name, cond) 42 | 43 | #define CHECK_DELIMETER() \ 44 | CHECK_HAS_AT_LEAST_N(sizeof(uint8_t)); \ 45 | if (*currentDataPtr != ',') { \ 46 | currentCommand = Command(Operation::Invalid, {}); \ 47 | break; \ 48 | } \ 49 | currentDataPtr++; 50 | 51 | class Tokenizer 52 | { 53 | public: 54 | /** 55 | * @brief Specifies all available tokens (aka opcodes) 56 | * that can be used to fuzz allocator logic 57 | */ 58 | enum class Operation 59 | { 60 | /* Allocate/Deallocate APIs */ 61 | Allocate, 62 | Deallocate, 63 | 64 | /* GC/GcPtr's APis */ 65 | CreateVertex, 66 | RemoveVertex, 67 | CreateEdge, 68 | RemoveEdge, 69 | ReadSharedData, 70 | CollectGarbage, 71 | 72 | /* Generic error command */ 73 | Invalid 74 | }; 75 | 76 | class Command 77 | { 78 | private: 79 | Operation m_op; 80 | std::vector m_arguments; 81 | 82 | public: 83 | Command() 84 | : m_op(Operation::Invalid) 85 | { 86 | } 87 | 88 | Command(Operation t_op, std::vector&& t_args) 89 | : m_op{ t_op } 90 | , m_arguments{ t_args } 91 | { 92 | } 93 | 94 | Operation GetOp() 95 | { 96 | return m_op; 97 | } 98 | 99 | std::vector& GetArgs() 100 | { 101 | return m_arguments; 102 | } 103 | }; 104 | 105 | /** 106 | * @brief Parse input string into sequence of opcodes and parameters to them. 107 | * @param t_data fuzzer-generated data 108 | * @param t_size size of fuzzer-generated data 109 | * @return std::vector> of opcodes and their parameters 110 | */ 111 | static std::vector Tokenize(const uint8_t* t_data, std::size_t t_size); 112 | }; 113 | }} -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/macros.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/utils.hpp" 4 | 5 | #if __has_include() && defined (MPP_SANITIZERS) 6 | #include 7 | #endif 8 | 9 | #if defined MPP_VALGRIND 10 | #include 11 | #include 12 | #endif 13 | 14 | namespace mpp { 15 | #define MPP_STRINGIFY(x) #x 16 | #define MPP_TO_STRING(x) MPP_STRINGIFY(x) 17 | 18 | #define PRIVATE_MPP_ASSERT(expr, msg) \ 19 | ((expr) ? (void)0 \ 20 | : mpp::utils::ErrorAbort("Assertation " #expr " failed in: " __FILE__ \ 21 | ":" MPP_TO_STRING(__LINE__) "\n")) 22 | 23 | #if MPP_DEBUG == 1 24 | #define MPP_DEBUG_ASSERT(expr, msg) PRIVATE_MPP_ASSERT((expr), (msg)) 25 | #else 26 | #define MPP_DEBUG_ASSERT(expr, msg) 27 | #endif 28 | 29 | #define MPP_RELEASE_ASSERT(expr, msg) PRIVATE_MPP_ASSERT((expr), (msg)) 30 | 31 | #if MPP_FULL_DEBUG == 1 || MPP_SECURE == 1 32 | #define MPP_MEMSET_SECURE_ONLY(ptr, value, size) \ 33 | do { \ 34 | MPP_DEBUG_ASSERT(ptr != nullptr, "Null pointer"); \ 35 | MPP_DEBUG_ASSERT(size > 0, "Size must be greater than 0"); \ 36 | utils::SecureMemset(ptr, size, value, size); \ 37 | } while (0) 38 | #else 39 | #define MPP_MEMSET_SECURE_ONLY(ptr, value, size) 40 | #endif 41 | 42 | #if MPP_FULL_DEBUG == 1 || MPP_SECURE == 1 43 | #define MPP_WIPE_CHUNK_SECURE_ONLY(chunk) \ 44 | do { \ 45 | MPP_MEMSET_SECURE_ONLY(Chunk::GetUserDataPtr(chunk), \ 46 | MPP_FILL_CHAR, \ 47 | chunk->GetSize() - sizeof(Chunk::ChunkHeader)); \ 48 | } while (0) 49 | #else 50 | #define MPP_WIPE_CHUNK_SECURE_ONLY(chunk) 51 | #endif 52 | 53 | #if defined MPP_SANITIZERS 54 | #define MPP_UNPOISON_MEM(ptr, size) ASAN_UNPOISON_MEMORY_REGION(ptr, size); 55 | #define MPP_POISON_MEM(ptr, size) ASAN_POISON_MEMORY_REGION(ptr, size); 56 | #define MPP_UNPOISON_USER_DATA_INSIDE_CHUNK(chunk) \ 57 | MPP_UNPOISON_MEM(Chunk::GetUserDataPtr(chunk), chunk->GetSize() - sizeof(Chunk::ChunkHeader)); 58 | #define MPP_POISON_USER_DATA_INSIDE_CHUNK(chunk) \ 59 | MPP_POISON_MEM(Chunk::GetUserDataPtr(chunk), chunk->GetSize() - sizeof(Chunk::ChunkHeader)); 60 | #else 61 | #define MPP_UNPOISON_MEM(ptr, size) 62 | #define MPP_POISON_MEM(ptr, size) 63 | #define MPP_UNPOISON_USER_DATA_INSIDE_CHUNK(chunk) 64 | #define MPP_POISON_USER_DATA_INSIDE_CHUNK(chunk) 65 | #endif 66 | 67 | #if defined MPP_VALGRIND 68 | #define MPP_RUNNING_ON_VALGRIND (RUNNING_ON_VALGRIND) 69 | #define MPP_VALGRIND_MALLOCLIKE_BLOCK(ptr, size) \ 70 | VALGRIND_MALLOCLIKE_BLOCK((ptr), (size), 0, 0); // NOLINT 71 | #define MPP_VALGRIND_FREELIKE_BLOCK(ptr) VALGRIND_FREELIKE_BLOCK((ptr), 0); // NOLINT 72 | #define MPP_VALGRIND_MAKE_MEM_DEFINED(ptr, size) VALGRIND_MAKE_MEM_DEFINED((ptr), (size)); // NOLINT 73 | #define MPP_VALGRIND_MAKE_MEM_UNDEFINED(ptr, size) \ 74 | VALGRIND_MAKE_MEM_UNDEFINED((ptr), (size)); // NOLINT 75 | #define MPP_VALGRIND_DEFINE_CHUNK_HEADER(chunk) \ 76 | MPP_VALGRIND_MAKE_MEM_DEFINED((chunk), sizeof(Chunk::ChunkHeader)); 77 | #define MPP_VALGRIND_DEFINE_CHUNK(chunk) \ 78 | do { \ 79 | MPP_VALGRIND_MALLOCLIKE_BLOCK(chunk, (chunk)->GetSize()); \ 80 | MPP_VALGRIND_DEFINE_CHUNK_HEADER(chunk); \ 81 | MPP_VALGRIND_MAKE_MEM_UNDEFINED(Chunk::GetUserDataPtr(chunk), \ 82 | (chunk)->GetSize() - sizeof(Chunk::ChunkHeader)); \ 83 | } while (0) 84 | #define MPP_VALGRIND_UNDEFINE_CHUNK(chunk) \ 85 | do { \ 86 | MPP_VALGRIND_FREELIKE_BLOCK(chunk); \ 87 | MPP_VALGRIND_DEFINE_CHUNK_HEADER(chunk); \ 88 | } while (0) 89 | #else 90 | #define MPP_RUNNING_ON_VALGRIND (0) 91 | #define MPP_VALGRIND_MALLOCLIKE_BLOCK(ptr, size) 92 | #define MPP_VALGRIND_FREELIKE_BLOCK(ptr) 93 | #define MPP_VALGRIND_MAKE_MEM_DEFINED(ptr, size) 94 | #define MPP_VALGRIND_MAKE_MEM_UNDEFINED(ptr, size) 95 | #define MPP_VALGRIND_DEFINE_CHUNK_HEADER(chunk) 96 | #define MPP_VALGRIND_DEFINE_CHUNK(chunk) 97 | #define MPP_VALGRIND_UNDEFINE_CHUNK(chunk) 98 | #endif 99 | } -------------------------------------------------------------------------------- /example_project/src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "mpplib/chunk.hpp" 10 | #include "mpplib/containers/gc_graph.hpp" 11 | #include "mpplib/gc.hpp" 12 | #include "mpplib/memory_manager.hpp" 13 | #include "mpplib/shared_gcptr.hpp" 14 | #include "mpplib/utils/macros.hpp" 15 | #include "mpplib/utils/profiler_definitions.hpp" 16 | 17 | using namespace mpp; 18 | 19 | template 20 | class Worker 21 | { 22 | public: 23 | struct alignas(64) ListNode 24 | { 25 | uint32_t index; 26 | uint32_t data; 27 | SharedGcPtr next; 28 | 29 | ListNode(uint32_t t_index, uint32_t t_data) 30 | : index(t_index) 31 | , data(t_data) 32 | , next(nullptr) 33 | {} 34 | }; 35 | 36 | SharedGcPtr m_LinkedListHead; 37 | uint32_t m_LinkedListSize; 38 | uint64_t m_xorshiftSeed; 39 | 40 | Worker(uint32_t t_LinkedListSize, uint64_t t_xorshiftSeed = 0x133796A5FF21B3C1) 41 | : m_LinkedListSize(t_LinkedListSize) 42 | , m_xorshiftSeed(t_xorshiftSeed) 43 | { 44 | if constexpr (RandomizedLinkedList) { 45 | m_LinkedListHead = CreateRandomizedLinkedList(t_LinkedListSize); 46 | } else { 47 | m_LinkedListHead = CreateLayoutedLinkedList(t_LinkedListSize); 48 | } 49 | } 50 | 51 | Worker(const Worker&) = delete; 52 | Worker(Worker&&) = delete; 53 | Worker& operator=(const Worker&) = delete; 54 | Worker& operator=(Worker&&) = delete; 55 | ~Worker() = default; 56 | 57 | uint32_t DoBenchmark() 58 | { 59 | SharedGcPtr& current = m_LinkedListHead; 60 | 61 | while (current->next != nullptr) { 62 | current->data = current->data ^ current->next->data ^ 0x1337AF12; 63 | current = current->next; 64 | } 65 | 66 | return current->data; 67 | } 68 | 69 | SharedGcPtr CreateLayoutedLinkedList(uint32_t size) 70 | { 71 | uint32_t data = 0xF7ADF3E1; 72 | SharedGcPtr head = MakeShared(0, data); 73 | SharedGcPtr current = head; 74 | 75 | for (uint32_t i = 1; i < size; ++i) { 76 | data = (data + 0xffffd) % ((2 << 20) + 1); 77 | current->next = MakeShared(i, data); 78 | current = current->next; 79 | } 80 | 81 | return head; 82 | } 83 | 84 | SharedGcPtr CreateRandomizedLinkedList(uint32_t size) 85 | { 86 | std::vector> nodes; 87 | nodes.reserve(size); 88 | uint32_t data = 0xF7ADF3E1; 89 | 90 | for (uint32_t i = 0; i < size; ++i) { 91 | data = (data + 0xffffd) % ((2 << 20) + 1); 92 | nodes.emplace_back(MakeShared(i, data)); 93 | } 94 | 95 | std::shuffle( 96 | std::begin(nodes), std::end(nodes), std::default_random_engine(m_xorshiftSeed)); 97 | 98 | for (uint32_t i = 0; i < size - 1; ++i) { 99 | nodes[i]->next = nodes[i + 1]; 100 | } 101 | 102 | return nodes[0]; 103 | } 104 | }; 105 | 106 | void logic() 107 | { 108 | using namespace mpp; 109 | 110 | Worker worker(8); 111 | std::cout << worker.DoBenchmark() << std::endl; 112 | 113 | std::cout << "Before GC" << std::endl; 114 | // g_memoryManager->VisHeapLayout(std::cout, nullptr); 115 | // Iterate linked list 116 | auto head = worker.m_LinkedListHead; 117 | while (head != nullptr) { 118 | std::cout << head << head->index << " " << head->data << std::endl; 119 | head = head->next; 120 | } 121 | 122 | // CollectGarbage(); 123 | 124 | std::cout << "After GC" << std::endl; 125 | // g_memoryManager->VisHeapLayout(std::cout, nullptr); 126 | // Iterate linked list 127 | head = worker.m_LinkedListHead; 128 | while (head != nullptr) { 129 | std::cout << head << head->index << " " << head->data << std::endl; 130 | head = head->next; 131 | } 132 | 133 | std::cout << std::endl; 134 | 135 | SharedGcPtr::ListNode>* newHead = &worker.m_LinkedListHead; 136 | while (newHead->Get() != nullptr) { 137 | std::cout << *newHead << newHead->Get()->index << " " << newHead->Get()->data << std::endl; 138 | newHead = &newHead->Get()->next; 139 | } 140 | 141 | head = nullptr; 142 | worker.m_LinkedListHead = nullptr; 143 | 144 | // MM::VisHeapLayout(std::cout, nullptr); 145 | // GC::GetInstance().Collect(); 146 | // MM::VisHeapLayout(std::cout, nullptr); 147 | } 148 | 149 | int main() 150 | { 151 | struct alignas(64) ListNode 152 | { 153 | uint32_t index; 154 | uint32_t data; 155 | SharedGcPtr next; 156 | 157 | ListNode(uint32_t t_index, uint32_t t_data) 158 | : index(t_index) 159 | , data(t_data) 160 | , next(nullptr) 161 | {} 162 | }; 163 | 164 | SharedGcPtr ptr = MakeShared(0, 0x1337); 165 | ptr->next = MakeShared(1, 0xdead); 166 | 167 | std::cout << ptr << '\n'; 168 | std::cout << ptr->next << '\n'; 169 | 170 | // g_memoryManager->VisHeapLayout(std::cout, ptr.Get()); 171 | 172 | // CollectGarbage(); 173 | 174 | ptr = ptr->next; 175 | 176 | return 0; 177 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/containers/vertex.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "mpplib/utils/profiler_definitions.hpp" 10 | #include 11 | #include 12 | 13 | namespace mpp { 14 | /** 15 | * @brief Represent Vertex in the graph. Each vertex represents unique memory location 16 | * (potentially a chunk) in the graph. 17 | */ 18 | class Vertex 19 | { 20 | private: 21 | //! @brief Location linked with the current vertex. 22 | std::byte* m_correspondingLocation; 23 | 24 | //! @brief Set of vertices that the current vertex points to. 25 | std::set m_neighbors; 26 | 27 | //! @brief Set of vertices, that are pointing to the current vertex. 28 | std::set m_pointingToVertices; 29 | 30 | //! @brief Set of GcPtr's that are pointing to the current chunk. 31 | std::set m_pointingToGcPtrs; 32 | 33 | //! @brief Says whether the m_correspondingLocation is a pointer to a valid chunk or not. 34 | bool m_currLocationIsAChunk; 35 | 36 | public: 37 | Vertex() = delete; 38 | /** 39 | * @brief Default vertex constructor. 40 | * @param t_loc location (possibly chunk) linked with current vertex. 41 | */ 42 | explicit Vertex(std::byte* t_location) 43 | : m_correspondingLocation(t_location) 44 | , m_currLocationIsAChunk(false) 45 | { 46 | } 47 | 48 | explicit Vertex(Chunk* t_location) 49 | : m_correspondingLocation(reinterpret_cast(t_location)) 50 | , m_currLocationIsAChunk(true) 51 | { 52 | } 53 | 54 | explicit Vertex(std::byte* t_location, bool t_currLocationIsAChunk) 55 | : m_correspondingLocation(t_location) 56 | , m_currLocationIsAChunk(t_currLocationIsAChunk) 57 | { 58 | } 59 | 60 | /** 61 | * @brief Checks whether location is a chunk or not. 62 | * @return true if location is a chunk, false otherwise. 63 | */ 64 | bool IsChunk() const; 65 | 66 | /** 67 | * @brief Checks whether location is a root node or not. 68 | * @return true if location is a root, false otherwise. 69 | */ 70 | bool IsRoot() const; 71 | 72 | /** 73 | * @brief Set new value for m_correspondingLocation. 74 | * @param t_location new value for location pointer. 75 | */ 76 | void UpdateLocationPtr(std::byte* t_location); 77 | 78 | /** 79 | * @brief Add new vertex, that points to the current vertex. 80 | * @param t_pointingVertex pointer to vertex. 81 | */ 82 | void AddPointingVertex(Vertex* t_pointingVertex); 83 | 84 | /** 85 | * @brief Get all vertices, that points to the current vertex. 86 | * @return reference to set of pointers to vertices 87 | */ 88 | std::set& GetPointingVertices(); 89 | 90 | /** 91 | * @brief Add the vertex, that current vertex points to. 92 | * @param t_neighbor vertex, to add to neighbors. 93 | */ 94 | void AddNeighbor(Vertex* t_neighbor); 95 | 96 | /** 97 | * @brief Remove the vertex that the current vertex points to. 98 | * @param t_neighbor vertex, to remove from neighbors. 99 | */ 100 | bool RemoveNeighbor(Vertex* t_neighbor); 101 | 102 | /** 103 | * @brief Get All vertices, that current vertex points to. 104 | * @return set of vertices 105 | */ 106 | std::set& GetNeighbors(); 107 | 108 | /** 109 | * @brief Adds GcPtr, that points to the current vertex (chunk). 110 | * @param t_gcPtr pointer to GcPtr to add to the current vertex. 111 | */ 112 | void AddGcPtr(GcPtr* t_gcPtr); 113 | 114 | /** 115 | * @brief Removes GcPtr from list of pointing to the current vertex GcPtr's. 116 | * @param t_gcPtr pointer to GcPtr to delete from list 117 | * @return true if GcPtr was deleted successfully, false - otherwise 118 | */ 119 | bool RemoveGcPtr(GcPtr* t_gcPtr); 120 | 121 | /** 122 | * @brief Get the All gcptrs located inside current vertex (chunk). 123 | * @param t_gcPtrs set of pointers to GcPtrs, obtained by calling GetGcPtrs() on instance of 124 | * GC class. 125 | * @return std::set& 126 | */ 127 | std::set GetAllOutgoingGcPtrs(const std::set& t_gcPtrs); 128 | 129 | /** 130 | * @brief Get all GcPtr's, that points to current vertex (chunk). 131 | * @return std::set& pointing to current vertex GcPtr's 132 | */ 133 | std::set& GetPointingToGcPtrs(); 134 | 135 | /** 136 | * @brief Get corresponding location. 137 | * @return std::byte* location (potentially chunk), that current vertex represents 138 | */ 139 | std::byte* GetLoc() const; 140 | 141 | /** 142 | * @brief Get corresponding location as a chunk object. The caller must check whether the 143 | * location is an actual chunk or not. 144 | * @sa GetCorrespondingLocation 145 | * @return Chunk* pointer that points to a chunk object 146 | */ 147 | Chunk* GetLocationAsAChunk() const; 148 | 149 | /** 150 | * @brief Get string representation of the current vertex. 151 | * @return std::string string representation of the vertex 152 | */ 153 | std::string ToString() const; 154 | }; 155 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/containers/chunk_treap.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/chunk.hpp" 4 | #include "mpplib/containers/node.hpp" 5 | #include "mpplib/utils/profiler_definitions.hpp" 6 | 7 | #include 8 | #include 9 | 10 | namespace mpp { 11 | /** 12 | * @brief ChunkTreap (CTreap) - Container class, that provides easy way of 13 | * inserting/finding freed chunks in arena. 14 | * 15 | * Data structure itself is a treap (tree + heap). Provides easy way to work with 16 | * chunks. Complexity (in general): Insert: O(logn) Remove: O(logn) Find[min/max]: 17 | * O(logn) 18 | */ 19 | class ChunkTreap final 20 | { 21 | public: 22 | // TODO 23 | /** 24 | * @brief Implements iteration logic for ChunkTreap. 25 | */ 26 | struct Iterator 27 | {}; 28 | 29 | /** 30 | * @brief Constructs empty chunk treap. 31 | * 32 | * Root node is nullptr. 33 | */ 34 | ChunkTreap(); 35 | 36 | // copy/assign 37 | ChunkTreap(const ChunkTreap& t_treap); 38 | ChunkTreap& operator=(const ChunkTreap& t_treap); 39 | ChunkTreap(ChunkTreap&& t_treap) noexcept; 40 | ChunkTreap& operator=(ChunkTreap&& t_treap) noexcept; 41 | 42 | /** 43 | * @brief Destructs chunk treap. 44 | */ 45 | ~ChunkTreap(); 46 | 47 | /** 48 | * @brief Finds min freed chunk in chunk treap in logN. 49 | * @return chunk of minimum size 50 | */ 51 | Chunk* MinSizeChunk() const; 52 | 53 | /** 54 | * @brief Finds max freed chunk in chunk treap in logN. 55 | * @return chunk of maximum size 56 | */ 57 | Chunk* MaxSizeChunk() const; 58 | 59 | /** 60 | * @brief Inserts new chunk in chunk treap in logN. 61 | * @param t_chunk the chunk to insert 62 | */ 63 | void InsertChunk(Chunk* t_chunk); 64 | 65 | /** 66 | * @brief Remove chunk from chunk treap in logN. 67 | * @warning Chunk must be in chunk treap! Otherwise this function segfaults. 68 | * @param t_chunk the chunk to remove 69 | * @return true if chunk was removed, false otherwise 70 | */ 71 | bool RemoveChunk(Chunk* t_chunk); 72 | 73 | /** 74 | * @brief Finds first chunk that size is greater or equal, to desiredSize. 75 | * @param t_desiredChunkSize desired chunk size 76 | * @return Found chunk of suitable size or nullptr 77 | */ 78 | Chunk* FirstGreaterOrEqualTo(std::size_t t_desiredChunkSize) const; 79 | 80 | /** 81 | * @brief Get root node. 82 | * @return root node 83 | */ 84 | Node* GetRootNode() 85 | { 86 | return m_root; 87 | } 88 | 89 | /** 90 | * @brief Wrapper around Delete(Node* t_root), to delete from the root. 91 | * @sa Delete(Node* t_root) 92 | */ 93 | void Delete(); 94 | 95 | /** 96 | * @brief Delete all nodes starting from t_root. 97 | */ 98 | void Delete(Node* t_root); 99 | 100 | /** 101 | * @brief Get number of nodes (aka freed chunks). 102 | * @return uint32_t number of freed chunks. 103 | */ 104 | uint32_t TotalFreeChunks() const; 105 | 106 | /** 107 | * @brief Get the amount of total memory held by chunks 108 | * inside chunk treap 109 | * @return std::size_t total amount of freed memory 110 | */ 111 | std::size_t TotalFreeMemory() const; 112 | 113 | /** 114 | * @brief Generates tree representation in form, that can be used in dot 115 | * (graphviz), to visualize tree structure. As inspiration i've used this article: 116 | * https://eli.thegreenplace.net/2009/11/23/visualizing-binary-trees-with-graphviz 117 | * 118 | * @param t_out std::ostream& to write to 119 | * @param t_treapName std::string - name of digraph 120 | * @param t_root root, to start from, if parameter is nullptr, dump whole 121 | * tree starting from m_root. 122 | * @return std::ostream that was passed as parameter 123 | */ 124 | std::ostream& GenerateGraphvizLayout(std::ostream& t_out, 125 | const std::string& t_treapName = "Treap", 126 | Node* t_root = nullptr) const; 127 | 128 | private: 129 | /** 130 | * @brief Chunk treap root node. 131 | */ 132 | Node* m_root; 133 | 134 | /** 135 | * @brief Keeps track of amount of freed chunks. 136 | */ 137 | uint32_t m_freedChunks; 138 | 139 | /** 140 | * @brief keeps track of total amount 141 | * of freed memory inside ChunkTreap 142 | */ 143 | std::size_t m_freedMemory; 144 | 145 | /** 146 | * @brief Inserts new node into ctreap. 147 | * @param t_node node to insert into chunk treap 148 | */ 149 | void InsertNode(Node* t_node); 150 | 151 | /** 152 | * @brief Сombines two subtrees t_left and t_right and returns the new tree. 153 | * @param t_left left subtree to merge 154 | * @param t_right right subtree to merge 155 | * @param t_root[out] new tree root 156 | */ 157 | static void MergeNodes(Node* t_left, Node* t_right, Node*& t_root); 158 | 159 | /** 160 | * @brief Separates tree into two subtrees by t_chunk. 161 | * @param t_root root, from which we should start splitting 162 | * @param[out] t_left left subtree 163 | * @param[out] t_right right subtree 164 | * @param t_chunk chunk to split by 165 | */ 166 | static void SplitNodesByElement(Node* t_root, 167 | Node*& t_left, 168 | Node*& t_right, 169 | Chunk* t_chunk); 170 | }; 171 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/gc.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/arena.hpp" 4 | #include "mpplib/chunk.hpp" 5 | #include "mpplib/containers/gc_graph.hpp" 6 | #include "mpplib/gcptr.hpp" 7 | #include "mpplib/heuristics/heuristics.hpp" 8 | #include "mpplib/utils/env_options.hpp" 9 | #include "mpplib/utils/profiler_definitions.hpp" 10 | 11 | #if MPP_STATS == 1 12 | #include "mpplib/utils/statistics.hpp" 13 | #include "mpplib/utils/timer.hpp" 14 | #endif 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | namespace mpp { 24 | // Forward declaration 25 | class MemoryManager; 26 | 27 | /** 28 | * @brief GarbageCollector class. 29 | */ 30 | class GarbageCollector 31 | { 32 | private: 33 | /** 34 | * @brief Newly created arena available space expansion threshold. If arena has less space 35 | * than required, new arena is created with size of old_size * @sa m_newAllocExpandFactor. 36 | */ 37 | static constexpr float m_newAllocExtendThreshold = 0.25f; 38 | 39 | //! @brief Newly created arena expansion factor. 40 | static constexpr float m_newAllocExpandFactor = 1.25f; 41 | 42 | /** 43 | * @brief All active GcPtr's. 44 | * 45 | * All GcPtr's that are currently used in the program are added to this list, to keep track 46 | * of them. 47 | */ 48 | std::unordered_set m_activeGcPtrs; 49 | 50 | /** 51 | * @brief Ordered set of all active GcPtr's, used only while performing Garbage-collection. 52 | * @warning Only valid in the context of GC::Collect(). 53 | */ 54 | std::set m_orderedActiveGcPtrs; 55 | 56 | //! @brief Cache of chunks in use for each arena. 57 | std::unordered_map> m_chunksInUseCache; 58 | 59 | #if MPP_STATS == 1 60 | //! @brief Used to keep track of garbage collector stats. 61 | std::unique_ptr m_gcStats; 62 | #endif 63 | 64 | //! @brief Total number of garbage collector invocations. 65 | uint32_t m_totalInvocations; 66 | 67 | //! @brief Reference to parent MemoryManager object. 68 | MemoryManager& m_memoryManager; 69 | 70 | /** 71 | * @brief Given objects graph builds a SIMPLE or ADVANCED graphviz representation and saves 72 | * it to the disk. 73 | * 74 | * @param t_graph Graph to render. 75 | * @param t_filename Filename to save as. 76 | * @param t_dumpType Graph dump type. 77 | */ 78 | static void SaveObjectsGraph( 79 | std::unique_ptr& t_graph, 80 | const std::string& t_filename = "objects-graph.dot", 81 | utils::ObjectsGraphDumpType t_dumpType = utils::ObjectsGraphDumpType::SIMPLE); 82 | 83 | public: 84 | /** 85 | * @brief Construct a new Garbage Collector object. 86 | * @param t_memoryManager Reference to parent MemoryManager object. 87 | */ 88 | explicit GarbageCollector(MemoryManager& t_memoryManager); 89 | 90 | /** 91 | * @brief Dumps current objects state. 92 | * 93 | * @param t_filename output file to save the graph to. 94 | * @param t_dumpType dump type (SIMPLE/ADVANCED). 95 | */ 96 | void DumpCurrentObjectsGraph( 97 | utils::ObjectsGraphDumpType t_dumpType = utils::ObjectsGraphDumpType::ADVANCED, 98 | const std::string& t_filename = "objects-graph.dot"); 99 | 100 | /** 101 | * @brief Returns in-use chunk if t_ptr points inside it, nullptr otherwise. 102 | * @param t_ptr Pointer to find chunk for. 103 | * @return Chunk* Pointer to chunk if t_ptr points inside it, nullptr otherwise. 104 | */ 105 | Chunk* FindChunkInUse(void* t_ptr); 106 | 107 | /** 108 | * @brief Creates an Arena that has enough space to fit all the objects. 109 | * @param t_requestedSize size of all objects that should be placed inside. 110 | * @return std::unique_ptr& newly created arena. 111 | */ 112 | std::unique_ptr& CreateGodArena(uint64_t t_requestedSize); 113 | 114 | /** 115 | * @brief Relocates all smart pointers from old arenas to newly created one, updating all 116 | * pointing to/from pointers. 117 | * @param t_godArena Arena to move all the pointers to. 118 | * @param t_layoutedData optimally-layouted heap. 119 | */ 120 | void RelocatePointers(std::unique_ptr& t_godArena, 121 | Heuristics::LayoutedHeap& t_layoutedData); 122 | 123 | /** 124 | * @brief Collect garbage. 125 | * 126 | * This method will construct graph of all in use chunks. 127 | * Then it will create Heuristics object to relayout data in the most efficient way. 128 | * After that, it will move all data to newly created arena, updating. 129 | * corresponding gcptr's. And in the end it will destroy unused arenas. 130 | * @return true if everything is good, false - otherwise. 131 | */ 132 | bool Collect(); 133 | 134 | /** 135 | * @brief Get reference to unordered set of currently active GcPtr's. 136 | * @return std::unordered_set& of currently used GcPtr's. 137 | */ 138 | std::unordered_set& GetGcPtrs() 139 | { 140 | return m_activeGcPtrs; 141 | } 142 | 143 | /** 144 | * @brief Transforms an unordered set of GcPtr's to a set of GcPtr's 145 | * and returns it as a copy. 146 | * @return std::set of currently used GcPtr's. 147 | */ 148 | std::set& GetOrderedGcPtrs() 149 | { 150 | return m_orderedActiveGcPtrs; 151 | } 152 | 153 | std::set BuildOrderedGcPtrs() 154 | { 155 | return std::set(m_activeGcPtrs.begin(), m_activeGcPtrs.end()); 156 | } 157 | 158 | /** 159 | * @brief Adds new gcptr to the list of active gcptr's. 160 | * @param t_ptr pointer to GcPtr to add. 161 | */ 162 | void AddGcPtr(GcPtr* t_ptr) 163 | { 164 | m_activeGcPtrs.insert(t_ptr); 165 | } 166 | }; 167 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/utils/statistics.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "mpplib/utils/env_options.hpp" 12 | 13 | namespace mpp { namespace utils { 14 | /** 15 | * @brief Class to easily manage all kind of statistics 16 | */ 17 | class Statistics 18 | { 19 | public: 20 | /** 21 | * @brief Structure that holds all possible kinds of arena statistics 22 | */ 23 | struct ArenaStats 24 | { 25 | /** 26 | * @brief Amount of total allocated memory (could be bigger than arena size, 27 | * if memory was reused) 28 | */ 29 | std::size_t totalAllocated; 30 | 31 | /** 32 | * @brief total amount of freed memory (if all allocations was deallocated, 33 | * should be equal to totalAllocated) 34 | */ 35 | std::size_t totalFreed; 36 | 37 | /** 38 | * @brief Biggest allocation that was performed in this arena 39 | */ 40 | std::size_t biggestAllocation; 41 | 42 | /** 43 | * @brief Smallest allocation that was performed in this arena 44 | */ 45 | std::size_t smallestAllocation; 46 | 47 | /** 48 | * @brief Arena full size 49 | */ 50 | std::size_t fullArenaSize; 51 | 52 | /** 53 | * @brief Arena was allocated for big chunk 54 | */ 55 | bool bigArena; 56 | 57 | /** 58 | * @brief Arena was created by GC 59 | */ 60 | bool gcCreatedArena; 61 | 62 | /** 63 | * @brief Update smallest allocation size 64 | * @param t_size New potentially smallest allocation size 65 | */ 66 | void UpdateSmallestAllocation(std::size_t t_size); 67 | 68 | /** 69 | * @brief Update biggest allocation size 70 | * @param t_size New potentially biggest allocation size 71 | */ 72 | void UpdateBiggestAllocation(std::size_t t_size); 73 | 74 | /** 75 | * @brief Update total freed memory 76 | * @param t_size delta of freed memory 77 | */ 78 | void IncreaseTotalFreed(std::size_t t_size); 79 | 80 | /** 81 | * @brief Update total allocated memory 82 | * @param t_size delta of allocated memory 83 | */ 84 | void IncreaseTotalAllocated(std::size_t t_size); 85 | }; 86 | 87 | /** 88 | * @brief Structure to hold all stats about GC::Collect() 89 | */ 90 | struct GcStats 91 | { 92 | /** 93 | * @brief Time wasted inside GC::Collect() 94 | */ 95 | std::chrono::duration timeWasted; 96 | 97 | /** 98 | * @brief Amount of memory cleaned by GC 99 | */ 100 | std::size_t memoryCleaned; 101 | 102 | /** 103 | * @brief total size of objects, that 104 | * remained active 105 | */ 106 | std::size_t activeObjectsTotalSize; 107 | }; 108 | 109 | /** 110 | * @brief Destroy the Statistics object 111 | * and call DumpStats (if env MPP_SHOW_STATISTICS=1) after program termination 112 | */ 113 | ~Statistics(); 114 | 115 | /** 116 | * @brief Get size in bytes converted to appropriate unit (mb, kb, ...) 117 | * @param t_bytes size in bytes 118 | * @param decimals number of signs after comma 119 | * @return std::string size in a specific unit 120 | */ 121 | static std::string FormattedSize(std::size_t t_bytes, uint32_t t_decimals = 3); 122 | 123 | /** 124 | * @brief Dump all arenas general stats 125 | * @param t_out - std::ostream& to write to 126 | * @return std::ostream& 127 | */ 128 | std::ostream& DumpArenasGeneralStats(std::ostream& t_out); 129 | 130 | /** 131 | * @brief Dump GC stats 132 | * @param t_out - std::ostream& to write to 133 | * @return std::ostream& 134 | */ 135 | std::ostream& DumpGcStats(std::ostream& t_out); 136 | 137 | /** 138 | * @brief Dump information about arenas. 139 | * @param t_out output stream to write to. 140 | * @param t_dumpActiveArenas dump active arenas. 141 | * @param t_dumpFreedChunks dump freed chunks. 142 | * @param t_dumpInUseChunks dump in use chunks. 143 | * @return std::ostream& stream reference 144 | */ 145 | std::ostream& DumpStats(std::ostream& t_out, 146 | bool t_dumpActiveArenas, 147 | bool t_dumpFreedChunks, 148 | bool t_dumpInUseChunks); 149 | 150 | /** 151 | * @brief Add gc stats to vector of all gc statistics 152 | * @param t_arenaStats std::unique_ptr 153 | */ 154 | void AddGcCycleStats(std::unique_ptr t_gcCycleStats); 155 | 156 | /** 157 | * @brief Add arena stats to vector of all arena statistics 158 | * @param t_arenaStats std::unique_ptr 159 | */ 160 | void AddArenaStats(const std::shared_ptr& t_arenaStats); 161 | 162 | /** 163 | * @brief Get Gc stats 164 | * @return std::unique_ptr& 165 | */ 166 | std::vector>& GetGcStats(); 167 | 168 | /** 169 | * @brief Get the vector of arena stats 170 | * @return std::vector>& 171 | */ 172 | std::vector>& GetArenaStats(); 173 | 174 | /** 175 | * @brief Return static instance of statistics object 176 | * @return Statistics 177 | */ 178 | static Statistics& GetInstance(); 179 | 180 | private: 181 | /** 182 | * @brief vector of all arenas statistics 183 | */ 184 | std::vector> m_arenasStats; 185 | 186 | /** 187 | * @brief Garbage Collector stats 188 | */ 189 | std::vector> m_gcCyclesStats; 190 | }; 191 | }} -------------------------------------------------------------------------------- /libmemplusplus/src/containers/chunk_treap.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/containers/chunk_treap.hpp" 2 | 3 | namespace mpp { 4 | 5 | ChunkTreap::ChunkTreap() 6 | : m_root{ nullptr } 7 | , m_freedChunks{ 0 } 8 | , m_freedMemory{ 0 } 9 | { 10 | } 11 | 12 | // @TODO: smart pointers memory management 13 | ChunkTreap::ChunkTreap(const ChunkTreap& t_treap) 14 | : m_root(new Node(*t_treap.m_root)) 15 | , m_freedChunks{ t_treap.m_freedChunks } 16 | , m_freedMemory{ t_treap.m_freedMemory } 17 | { 18 | } 19 | 20 | ChunkTreap& ChunkTreap::operator=(const ChunkTreap& t_treap) 21 | { 22 | if (&t_treap == this) 23 | return *this; 24 | 25 | Delete(); 26 | 27 | // TODO - smart pointers memory management 28 | m_root = new Node(*t_treap.m_root); 29 | m_freedChunks = t_treap.m_freedChunks; 30 | m_freedMemory = t_treap.m_freedMemory; 31 | 32 | return *this; 33 | } 34 | 35 | ChunkTreap::ChunkTreap(ChunkTreap&& t_treap) noexcept 36 | : m_root(t_treap.m_root) 37 | , m_freedChunks{ t_treap.m_freedChunks } 38 | , m_freedMemory{ t_treap.m_freedMemory } 39 | { 40 | t_treap.m_root = nullptr; 41 | t_treap.m_freedChunks = 0; 42 | t_treap.m_freedMemory = 0; 43 | } 44 | 45 | ChunkTreap& ChunkTreap::operator=(ChunkTreap&& t_treap) noexcept 46 | { 47 | if (&t_treap == this) { 48 | return *this; 49 | } 50 | 51 | Delete(); 52 | 53 | m_root = t_treap.m_root; 54 | m_freedChunks = t_treap.m_freedChunks; 55 | m_freedMemory = t_treap.m_freedMemory; 56 | 57 | t_treap.m_root = nullptr; 58 | t_treap.m_freedChunks = 0; 59 | t_treap.m_freedMemory = 0; 60 | 61 | return *this; 62 | } 63 | 64 | ChunkTreap::~ChunkTreap() 65 | { 66 | Delete(); 67 | } 68 | 69 | void ChunkTreap::Delete() 70 | { 71 | PROFILE_FUNCTION(); 72 | Delete(m_root); 73 | } 74 | 75 | void ChunkTreap::Delete(Node* t_root) 76 | { 77 | if (t_root == nullptr) { 78 | return; 79 | } 80 | 81 | // Delete recursively each node 82 | Delete(t_root->leftChild); 83 | t_root->leftChild = nullptr; 84 | 85 | // Delete recursively each node 86 | Delete(t_root->rightChild); 87 | t_root->rightChild = nullptr; 88 | 89 | delete t_root; 90 | t_root = nullptr; 91 | } 92 | 93 | uint32_t ChunkTreap::TotalFreeChunks() const 94 | { 95 | return m_freedChunks; 96 | } 97 | 98 | std::size_t ChunkTreap::TotalFreeMemory() const 99 | { 100 | return m_freedMemory; 101 | } 102 | 103 | Chunk* ChunkTreap::MinSizeChunk() const 104 | { 105 | Node* currentNode = m_root; 106 | 107 | for (; currentNode->leftChild; currentNode = currentNode->leftChild) 108 | ; 109 | 110 | return currentNode->chunk; 111 | } 112 | 113 | Chunk* ChunkTreap::MaxSizeChunk() const 114 | { 115 | Node* currentNode = m_root; 116 | 117 | for (; currentNode->rightChild; currentNode = currentNode->rightChild) 118 | ; 119 | 120 | return currentNode->chunk; 121 | } 122 | 123 | void ChunkTreap::InsertChunk(Chunk* t_chunk) 124 | { 125 | m_freedChunks++; 126 | m_freedMemory += t_chunk->GetSize(); 127 | 128 | // TODO - smart pointers memory management 129 | InsertNode(new Node(t_chunk)); 130 | } 131 | 132 | void ChunkTreap::InsertNode(Node* t_node) 133 | { 134 | PROFILE_FUNCTION(); 135 | 136 | Node* leftSubtree = nullptr; 137 | Node* rightSubtree = nullptr; 138 | 139 | // GenerateGraphvizLayout(std::cout, "root", m_root) << std::endl; 140 | SplitNodesByElement(m_root, leftSubtree, rightSubtree, t_node->chunk); 141 | // GenerateGraphvizLayout(std::cout, "leftSubtree", leftSubtree) << std::endl; 142 | // GenerateGraphvizLayout(std::cout, "rightSubtree", rightSubtree) << std::endl; 143 | MergeNodes(leftSubtree, t_node, leftSubtree); 144 | MergeNodes(leftSubtree, rightSubtree, m_root); 145 | } 146 | 147 | bool ChunkTreap::RemoveChunk(Chunk* t_chunk) 148 | { 149 | PROFILE_FUNCTION(); 150 | 151 | if (m_root == nullptr) { 152 | return false; 153 | } 154 | 155 | m_freedChunks--; 156 | m_freedMemory -= t_chunk->GetSize(); 157 | 158 | Node* leftSubtree = nullptr; 159 | Node* rightSubtree = nullptr; 160 | 161 | SplitNodesByElement(m_root, leftSubtree, rightSubtree, t_chunk); 162 | 163 | Node* parent = rightSubtree; 164 | Node* nodeToRemove = rightSubtree; 165 | for (; nodeToRemove->leftChild; 166 | parent = nodeToRemove, nodeToRemove = nodeToRemove->leftChild) 167 | ; 168 | 169 | if (nodeToRemove == rightSubtree) { 170 | rightSubtree = nodeToRemove->rightChild; 171 | } else { 172 | parent->leftChild = nodeToRemove->rightChild; 173 | } 174 | nodeToRemove->rightChild = nullptr; 175 | 176 | delete nodeToRemove; 177 | nodeToRemove = nullptr; 178 | 179 | MergeNodes(leftSubtree, rightSubtree, m_root); 180 | 181 | return true; 182 | } 183 | 184 | Chunk* ChunkTreap::FirstGreaterOrEqualTo(std::size_t t_desiredChunkSize) const 185 | { 186 | PROFILE_FUNCTION(); 187 | 188 | Node* currentNode = m_root; 189 | 190 | while (currentNode) { 191 | if (currentNode->chunk->GetSize() < t_desiredChunkSize) { 192 | currentNode = currentNode->rightChild; 193 | } else { 194 | if (currentNode->leftChild && 195 | currentNode->leftChild->chunk->GetSize() >= t_desiredChunkSize) { 196 | currentNode = currentNode->leftChild; 197 | } else { 198 | return currentNode->chunk; 199 | } 200 | } 201 | } 202 | 203 | return nullptr; 204 | } 205 | 206 | void ChunkTreap::MergeNodes(Node* t_left, Node* t_right, Node*& t_root) 207 | { 208 | if (!t_left || !t_right) { 209 | t_root = t_left ? t_left : t_right; 210 | return; 211 | } 212 | 213 | if (t_left->priority > t_right->priority) { 214 | MergeNodes(t_left->rightChild, t_right, t_left->rightChild); 215 | t_root = t_left; 216 | } else { 217 | MergeNodes(t_left, t_right->leftChild, t_right->leftChild); 218 | t_root = t_right; 219 | } 220 | } 221 | 222 | void ChunkTreap::SplitNodesByElement(Node* t_root, 223 | Node*& t_left, 224 | Node*& t_right, 225 | Chunk* t_chunk) 226 | { 227 | if (!t_root) { 228 | t_left = t_right = nullptr; 229 | return; 230 | } 231 | 232 | if ((t_root->chunk->GetSize() < t_chunk->GetSize()) || 233 | ((t_root->chunk->GetSize() == t_chunk->GetSize()) && t_root->chunk < t_chunk)) { 234 | SplitNodesByElement(t_root->rightChild, t_root->rightChild, t_right, t_chunk); 235 | t_left = t_root; 236 | } else { 237 | SplitNodesByElement(t_root->leftChild, t_left, t_root->leftChild, t_chunk); 238 | t_right = t_root; 239 | } 240 | } 241 | } -------------------------------------------------------------------------------- /fuzzer/src/fuzzer_main.cpp: -------------------------------------------------------------------------------- 1 | #include "fuzzer/crc_calculator.hpp" 2 | #include "fuzzer/fuzzer.h" 3 | #include "fuzzer/tokenizer.hpp" 4 | #include "mpplib/memory_manager.hpp" 5 | #include "mpplib/shared_gcptr.hpp" 6 | #include "mpplib/utils/log.hpp" 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | using namespace mpp::fuzzer; 17 | 18 | std::function g_MppFuzzOneInput; 19 | 20 | void HelpAndExit(char* executable) 21 | { 22 | std::cout << "Usage: FUZZ_MODE= " << executable << "\n"; 23 | std::cout << "Available fuzz targets: \n"; 24 | std::cout << " - alloc_api\n"; 25 | std::cout << " - gc\n"; 26 | 27 | exit(1); 28 | } 29 | 30 | int MppFuzzAllocApi(const uint8_t* Data, std::size_t Size) 31 | { 32 | mpp::MemoryManager memMgr = mpp::MemoryManager(); 33 | 34 | // translate input string into sequence of opcodes 35 | auto commands = Tokenizer::Tokenize(Data, Size); 36 | 37 | std::vector allocatedChunks; 38 | 39 | for (auto cmdIter = commands.begin(); cmdIter != commands.end(); cmdIter++) { 40 | Tokenizer::Command& cmd = *cmdIter; 41 | 42 | switch (cmd.GetOp()) { 43 | case Tokenizer::Operation::Allocate: { 44 | // std::cout << "Allocating chunk of size: " << cmd.second; 45 | allocatedChunks.push_back(memMgr.Allocate(cmd.GetArgs()[0])); 46 | // std::cout << ". Chunk allocated: " << allocatedChunks.back() << std::endl; 47 | break; 48 | } 49 | case Tokenizer::Operation::Deallocate: { 50 | if (allocatedChunks.empty()) { 51 | return 0; 52 | } 53 | uint32_t position = rand() % allocatedChunks.size(); 54 | void* chunk = allocatedChunks.at(position); 55 | // std::cout << "Deallocating chunk: " << chunk << ". Of size: " << 56 | // mpp::Chunk::GetHeaderPtr(chunk)->GetSize() << std::endl; 57 | memMgr.Deallocate(chunk); 58 | allocatedChunks.erase(allocatedChunks.begin() + position); 59 | break; 60 | } 61 | case Tokenizer::Operation::Invalid: 62 | default: 63 | return 0; 64 | } 65 | } 66 | 67 | return 0; 68 | } 69 | 70 | int MppFuzzGcApi(const uint8_t* Data, std::size_t Size) 71 | { 72 | mpp::g_memoryManager = std::make_unique(); 73 | 74 | // translate input string into sequence of opcodes 75 | auto commands = Tokenizer::Tokenize(Data, Size); 76 | std::array, c_maxPointers> pointers; 77 | 78 | for (auto cmdIter = commands.begin(); cmdIter != commands.end(); cmdIter++) { 79 | Tokenizer::Command& cmd = *cmdIter; 80 | 81 | switch (cmd.GetOp()) { 82 | case Tokenizer::Operation::CreateVertex: { 83 | MPP_DEBUG_ASSERT(cmd.GetArgs().size() == 1, 84 | "Got incorrect number of arguments for CreateVertex operation!"); 85 | MPP_LOG_DEBUG("CreateVertex(%d)\n", cmd.GetArgs()[0]); 86 | 87 | auto vtx = mpp::MakeShared(); 88 | pointers.at(cmd.GetArgs()[0]) = std::move(vtx); 89 | break; 90 | } 91 | case Tokenizer::Operation::RemoveVertex: { 92 | MPP_DEBUG_ASSERT(cmd.GetArgs().size() == 1, 93 | "Got incorrect number of arguments for RemoveVertex operation!"); 94 | MPP_LOG_DEBUG("RemoveVertex(%d)\n", cmd.GetArgs()[0]); 95 | 96 | pointers.at(cmd.GetArgs()[0]) = nullptr; 97 | break; 98 | } 99 | case Tokenizer::Operation::CreateEdge: { 100 | MPP_DEBUG_ASSERT(cmd.GetArgs().size() == 3, 101 | "Got incorrect number of arguments for CreateEdge operation!"); 102 | MPP_LOG_DEBUG("CreateEdge(%d, %d, %d)\n", 103 | cmd.GetArgs()[0], 104 | cmd.GetArgs()[1], 105 | cmd.GetArgs()[2]); 106 | 107 | auto [vtxIdx1, vtxIdx2, ptrIdx] = 108 | std::tie(cmd.GetArgs()[0], cmd.GetArgs()[1], cmd.GetArgs()[2]); 109 | mpp::SharedGcPtr& vtx_to_add = pointers.at(vtxIdx1); 110 | mpp::SharedGcPtr& vtx_add_to = pointers.at(vtxIdx2); 111 | 112 | if (vtx_to_add != nullptr && vtx_add_to != nullptr) { 113 | vtx_add_to->AddPointerAtIndex(ptrIdx, vtx_to_add); 114 | } 115 | break; 116 | } 117 | case Tokenizer::Operation::RemoveEdge: { 118 | MPP_DEBUG_ASSERT(cmd.GetArgs().size() == 2, 119 | "Got incorrect number of arguments for RemoveEdge operation!"); 120 | MPP_LOG_DEBUG("RemoveEdge(%d, %d)\n", cmd.GetArgs()[0], cmd.GetArgs()[1]); 121 | 122 | auto [vtxIdx, ptrIdx] = std::tie(cmd.GetArgs()[0], cmd.GetArgs()[1]); 123 | mpp::SharedGcPtr& vertex = pointers.at(vtxIdx); 124 | 125 | if (vertex != nullptr) 126 | vertex->AddPointerAtIndex(ptrIdx, nullptr); 127 | break; 128 | } 129 | case Tokenizer::Operation::ReadSharedData: { 130 | MPP_DEBUG_ASSERT(cmd.GetArgs().size() == 1, 131 | "Got incorrect number of arguments for ReadSharedData operation!"); 132 | MPP_LOG_DEBUG("ReadSharedData(%d)\n", cmd.GetArgs()[0]); 133 | 134 | auto vtxIdx = cmd.GetArgs()[0]; 135 | mpp::SharedGcPtr& vertex = pointers.at(vtxIdx); 136 | if (vertex != nullptr) { 137 | volatile uint64_t& data = vertex->GetData(); 138 | } 139 | 140 | break; 141 | } 142 | case Tokenizer::Operation::CollectGarbage: { 143 | MPP_LOG_DEBUG("CollectGarbage()\n"); 144 | mpp::CollectGarbage(); 145 | break; 146 | } 147 | case Tokenizer::Operation::Allocate: 148 | case Tokenizer::Operation::Deallocate: 149 | case Tokenizer::Operation::Invalid: 150 | default: 151 | return 0; 152 | } 153 | } 154 | 155 | return 0; 156 | } 157 | 158 | extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) 159 | { 160 | auto* fuzz_mode = std::getenv("FUZZ_MODE"); 161 | if (!fuzz_mode) { 162 | std::cout << "Environment variable FUZZ_MODE is not set! Fuzzing default target: " 163 | "MppFuzzAllocApi\n"; 164 | g_MppFuzzOneInput = MppFuzzAllocApi; 165 | return 0; 166 | } 167 | 168 | if (!std::strcmp(fuzz_mode, "alloc_api")) { 169 | std::cout << "Environment variable FUZZ_MODE is set to \"alloc_api\"! Fuzzing " 170 | "\"MppFuzzAllocApi\" target\n"; 171 | g_MppFuzzOneInput = MppFuzzAllocApi; 172 | } else if (!std::strcmp(fuzz_mode, "gc")) { 173 | std::cout << "Environment variable FUZZ_MODE is set to \"gc\"! Fuzzing " 174 | "\"MppFuzzGcApi\" target\n"; 175 | g_MppFuzzOneInput = MppFuzzGcApi; 176 | } else { 177 | HelpAndExit(*argv[0]); 178 | } 179 | 180 | return 0; 181 | } 182 | 183 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) 184 | { 185 | return g_MppFuzzOneInput(Data, Size); 186 | } 187 | -------------------------------------------------------------------------------- /libmemplusplus/src/utils/statistics.cpp: -------------------------------------------------------------------------------- 1 | #include "mpplib/utils/statistics.hpp" 2 | #include "mpplib/memory_manager.hpp" 3 | 4 | namespace mpp { namespace utils { 5 | void Statistics::ArenaStats::UpdateBiggestAllocation(std::size_t t_size) 6 | { 7 | if (t_size > biggestAllocation) { 8 | biggestAllocation = t_size; 9 | } 10 | } 11 | 12 | void Statistics::ArenaStats::UpdateSmallestAllocation(std::size_t t_size) 13 | { 14 | if (t_size < smallestAllocation) { 15 | smallestAllocation = t_size; 16 | } 17 | } 18 | 19 | void Statistics::ArenaStats::IncreaseTotalFreed(std::size_t t_size) 20 | { 21 | totalFreed += t_size; 22 | } 23 | 24 | void Statistics::ArenaStats::IncreaseTotalAllocated(std::size_t t_size) 25 | { 26 | totalAllocated += t_size; 27 | } 28 | 29 | Statistics::~Statistics() 30 | { 31 | #if MPP_STATS == 1 32 | if (EnvOptions::Get().GetMppShowStatistics()) { 33 | DumpStats(std::cerr, false, false, false); 34 | } 35 | #endif 36 | } 37 | 38 | std::string Statistics::FormattedSize(std::size_t t_bytes, uint32_t t_decimals) 39 | { 40 | if (t_bytes == 0) { 41 | return "0 bytes"; 42 | } 43 | 44 | std::stringstream formattedSizeSS; 45 | 46 | const uint32_t kilobyte = 1024; 47 | std::vector sizes{ "Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB" }; 48 | int32_t sizesIdx = std::floor(std::log(t_bytes) / std::log(kilobyte)); 49 | formattedSizeSS << std::setprecision((int32_t)t_decimals) << std::fixed 50 | << ((double)t_bytes / std::pow(kilobyte, sizesIdx)) << " " 51 | << sizes.at(sizesIdx); 52 | 53 | return formattedSizeSS.str(); 54 | } 55 | 56 | std::ostream& Statistics::DumpArenasGeneralStats(std::ostream& t_out) 57 | { 58 | uint32_t arenaIdx = 1; 59 | for (const auto& arenaStats : m_arenasStats) { 60 | t_out << " ----------------- Arena: " << arenaIdx << " ----------------- " 61 | << std::endl; 62 | t_out << "MPP - Total amount of allocated memory inside arena : " 63 | << FormattedSize(arenaStats->totalAllocated) << std::endl; 64 | t_out << "MPP - Total amount of freed memory inside arena : " 65 | << FormattedSize(arenaStats->totalFreed) << std::endl; 66 | t_out << "MPP - total freed == total allocated : "; 67 | if (arenaStats->totalAllocated == arenaStats->totalFreed) { 68 | #if MPP_COLOUR == 1 69 | t_out << col::GREEN << "true" << col::RESET << std::endl; 70 | #else 71 | t_out << "true" << std::endl; 72 | #endif 73 | } else { 74 | #if MPP_COLOUR == 1 75 | t_out << col::RED << "false" << col::RESET << std::endl; 76 | #else 77 | t_out << "false" << std::endl; 78 | #endif 79 | } 80 | 81 | if (arenaStats->biggestAllocation == 0) { 82 | t_out << "MPP - Biggest allocation size : \"No allocations " 83 | "have been made yet\"" 84 | << std::endl; 85 | } else { 86 | t_out << "MPP - Biggest allocation size : " 87 | << FormattedSize(arenaStats->biggestAllocation) << std::endl; 88 | } 89 | if (arenaStats->smallestAllocation == std::numeric_limits::max()) { 90 | t_out << "MPP - Smallest allocation size : \"No allocations " 91 | "have been made yet\"" 92 | << std::endl; 93 | } else { 94 | t_out << "MPP - Smallest allocation size : " 95 | << FormattedSize(arenaStats->smallestAllocation) << std::endl; 96 | } 97 | t_out << "MPP - Full size of arena : " 98 | << FormattedSize(arenaStats->fullArenaSize) << std::endl; 99 | t_out << "MPP - Arena was allocated for big chunk : " 100 | << (arenaStats->bigArena ? "true" : "false") << std::endl; 101 | t_out << "MPP - Arena was allocated by GC : " 102 | << (arenaStats->gcCreatedArena ? "true" : "false") << std::endl; 103 | t_out << std::endl; 104 | arenaIdx++; 105 | } 106 | return t_out; 107 | } 108 | 109 | std::ostream& Statistics::DumpGcStats(std::ostream& t_out) 110 | { 111 | uint32_t cycleIdx = 1; 112 | for (const auto& gcStats : m_gcCyclesStats) { 113 | t_out << " ----------------- Cycle: " << cycleIdx << " ----------------- " 114 | << std::endl; 115 | t_out << "GC - Time wasted inside GC::Collect() : " << gcStats->timeWasted.count() 116 | << " ms" << std::endl; 117 | t_out << "GC - Memory cleaned after GC::Collect() : " 118 | << FormattedSize(gcStats->memoryCleaned) << std::endl; 119 | t_out << "GC - Total size of active objects : " 120 | << FormattedSize(gcStats->activeObjectsTotalSize) << std::endl; 121 | t_out << std::endl; 122 | cycleIdx++; 123 | } 124 | return t_out; 125 | } 126 | 127 | std::ostream& Statistics::DumpStats(std::ostream& t_out, 128 | bool t_dumpActiveArenas, 129 | bool t_dumpFreedChunks, 130 | bool t_dumpInUseChunks) 131 | { 132 | t_out << "+============= STATISTICS DUMP START =============+" << std::endl; 133 | 134 | t_out << "MPP: MIN_CHUNK_SIZE : " << MemoryManager::g_MIN_CHUNK_SIZE << " bytes" 135 | << std::endl; 136 | t_out << "MPP: CHUNK_HEADER_SIZE : " << sizeof(Chunk::ChunkHeader) << " bytes" 137 | << std::endl; 138 | t_out << "MPP: DEFAULT_ARENA_SIZE : " << FormattedSize(MemoryManager::g_DEFAULT_ARENA_SIZE) 139 | << std::endl; 140 | t_out << "MPP: PAGE_SIZE : " << FormattedSize(MemoryManager::g_PAGE_SIZE) 141 | << std::endl; 142 | 143 | if (t_dumpActiveArenas) { 144 | t_out << "~~~~~~~~~~~~~~~~ All active arenas ~~~~~~~~~~~~~~~~" << std::endl; 145 | for (auto& arena : g_memoryManager->GetArenaList()) { 146 | Arena::DumpArena(t_out, arena, t_dumpFreedChunks, t_dumpInUseChunks) << std::endl; 147 | } 148 | } 149 | 150 | t_out << "~~~~~~~~~~~~~~~~~~ General stats ~~~~~~~~~~~~~~~~~~" << std::endl; 151 | DumpArenasGeneralStats(t_out); 152 | 153 | t_out << "~~~~~~~~~~~~~ Garbage Collector stats ~~~~~~~~~~~~~" << std::endl; 154 | DumpGcStats(t_out); 155 | 156 | t_out << "+============== STATISTICS DUMP END ==============+" << std::endl; 157 | return t_out; 158 | } 159 | 160 | void Statistics::AddGcCycleStats(std::unique_ptr t_gcCycleStats) 161 | { 162 | m_gcCyclesStats.push_back(std::move(t_gcCycleStats)); 163 | } 164 | 165 | void Statistics::AddArenaStats(const std::shared_ptr& t_arenaStats) 166 | { 167 | m_arenasStats.push_back(t_arenaStats); 168 | } 169 | 170 | std::vector>& Statistics::GetGcStats() 171 | { 172 | return m_gcCyclesStats; 173 | } 174 | 175 | std::vector>& Statistics::GetArenaStats() 176 | { 177 | return m_arenasStats; 178 | } 179 | 180 | Statistics& Statistics::GetInstance() 181 | { 182 | static Statistics s_stats; 183 | return s_stats; 184 | } 185 | }} -------------------------------------------------------------------------------- /additional_info/images/treap.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | Treap 11 | 12 | 13 | 14 | null0 15 | 16 | 17 | 18 | 19 | 140475077385286 20 | 21 | chunk = 0x7fc296940000 22 | chunk.size = 65536 23 | priority = 1904368710 24 | 25 | 26 | 27 | 140475077385286->null0 28 | 29 | 30 | 31 | 32 | 33 | 140474240928105 34 | 35 | chunk = 0x7fc296980000 36 | chunk.size = 65536 37 | priority = 597887337 38 | 39 | 40 | 41 | 140475077385286->140474240928105 42 | 43 | 44 | 45 | 46 | 47 | 140473381531004 48 | 49 | chunk = 0x7fc296960000 50 | chunk.size = 65536 51 | priority = 345484668 52 | 53 | 54 | 55 | 140474240928105->140473381531004 56 | 57 | 58 | 59 | 60 | 61 | 140473439188531 62 | 63 | chunk = 0x7fc2969c0000 64 | chunk.size = 65536 65 | priority = 334329395 66 | 67 | 68 | 69 | 140474240928105->140473439188531 70 | 71 | 72 | 73 | 74 | 75 | null1 76 | 77 | 78 | 79 | 80 | 140473381531004->null1 81 | 82 | 83 | 84 | 85 | 86 | null2 87 | 88 | 89 | 90 | 91 | 140473381531004->null2 92 | 93 | 94 | 95 | 96 | 97 | 140473666044395 98 | 99 | chunk = 0x7fc2969a0000 100 | chunk.size = 65536 101 | priority = 74252779 102 | 103 | 104 | 105 | 140473439188531->140473666044395 106 | 107 | 108 | 109 | 110 | 111 | null5 112 | 113 | 114 | 115 | 116 | 140473439188531->null5 117 | 118 | 119 | 120 | 121 | 122 | null3 123 | 124 | 125 | 126 | 127 | 140473666044395->null3 128 | 129 | 130 | 131 | 132 | 133 | null4 134 | 135 | 136 | 137 | 138 | 140473666044395->null4 139 | 140 | 141 | 142 | 143 | 144 | -------------------------------------------------------------------------------- /tests/chunk_treap_test.cpp: -------------------------------------------------------------------------------- 1 | #include "gtest/gtest.h" 2 | #include 3 | 4 | #include "mpplib/chunk.hpp" 5 | #include "mpplib/containers/chunk_treap.hpp" 6 | #include "mpplib/memory_manager.hpp" 7 | 8 | TEST(ChunkTreapTest, ChunkToDeleteLocatedInBetween) 9 | { 10 | using namespace mpp; 11 | 12 | ChunkTreap cTreap; 13 | 14 | MM memMgr = MM(); 15 | 16 | auto* ch1 = (Chunk*)memMgr.Allocate(50); 17 | auto* ch2 = (Chunk*)memMgr.Allocate(50); 18 | auto* ch3 = (Chunk*)memMgr.Allocate(50); 19 | 20 | ASSERT_TRUE((((std::size_t)ch1 < (std::size_t)ch2) && ((std::size_t)ch2 < (std::size_t)ch3))); 21 | 22 | ch1->SetSize(16); 23 | ch2->SetSize(16); 24 | ch3->SetSize(16); 25 | 26 | cTreap.InsertChunk(ch1); 27 | cTreap.InsertChunk(ch2); 28 | cTreap.InsertChunk(ch3); 29 | 30 | cTreap.RemoveChunk(ch2); 31 | 32 | if (cTreap.GetRootNode()->leftChild != nullptr) { 33 | ASSERT_TRUE(((cTreap.GetRootNode()->chunk == ch1) || (cTreap.GetRootNode()->chunk == ch3))); 34 | ASSERT_TRUE(((cTreap.GetRootNode()->leftChild->chunk == ch1) || 35 | (cTreap.GetRootNode()->leftChild->chunk == ch3))); 36 | } else { 37 | ASSERT_TRUE(((cTreap.GetRootNode()->chunk == ch1) || (cTreap.GetRootNode()->chunk == ch3))); 38 | ASSERT_TRUE(((cTreap.GetRootNode()->rightChild->chunk == ch1) || 39 | (cTreap.GetRootNode()->rightChild->chunk == ch3))); 40 | } 41 | 42 | memMgr.Deallocate(ch1); 43 | memMgr.Deallocate(ch2); 44 | memMgr.Deallocate(ch3); 45 | } 46 | 47 | TEST(ChunkTreapTest, CheckAllConstructorsAndAssignOperators) 48 | { 49 | using namespace mpp; 50 | 51 | ChunkTreap cTreap; 52 | 53 | Chunk* ch1 = (Chunk*)malloc(128); 54 | Chunk* ch2 = (Chunk*)malloc(128); 55 | Chunk* ch3 = (Chunk*)malloc(128); 56 | Chunk* ch4 = (Chunk*)malloc(128); 57 | Chunk* ch5 = (Chunk*)malloc(128); 58 | Chunk* ch6 = (Chunk*)malloc(128); 59 | Chunk* ch7 = (Chunk*)malloc(128); 60 | Chunk* ch8 = (Chunk*)malloc(128); 61 | Chunk* ch9 = (Chunk*)malloc(128); 62 | 63 | ch1->SetSize(32); 64 | ch2->SetSize(64); 65 | ch3->SetSize(96); 66 | ch4->SetSize(128); 67 | ch5->SetSize(160); 68 | ch6->SetSize(192); 69 | ch7->SetSize(224); 70 | ch8->SetSize(256); 71 | ch9->SetSize(288); 72 | 73 | cTreap.InsertChunk(ch1); 74 | cTreap.InsertChunk(ch2); 75 | cTreap.InsertChunk(ch3); 76 | cTreap.InsertChunk(ch4); 77 | cTreap.InsertChunk(ch5); 78 | cTreap.InsertChunk(ch6); 79 | cTreap.InsertChunk(ch7); 80 | cTreap.InsertChunk(ch8); 81 | cTreap.InsertChunk(ch9); 82 | 83 | ASSERT_TRUE(cTreap.MinSizeChunk()->GetSize() == 32); 84 | ASSERT_TRUE(cTreap.MaxSizeChunk()->GetSize() == 288); 85 | ASSERT_EQ(cTreap.TotalFreeChunks(), 9); 86 | 87 | // Generate graphviz layout just to check that at least it doesn't segfault 🤷 88 | std::stringstream ss; 89 | cTreap.GenerateGraphvizLayout(ss); 90 | 91 | ASSERT_TRUE(cTreap.RemoveChunk(ch1)); 92 | ASSERT_TRUE(cTreap.RemoveChunk(ch6)); 93 | ASSERT_TRUE(cTreap.RemoveChunk(ch9)); 94 | 95 | ASSERT_TRUE(cTreap.MaxSizeChunk()->GetSize() == 256); 96 | ASSERT_TRUE(cTreap.MinSizeChunk()->GetSize() == 64); 97 | ASSERT_EQ(cTreap.TotalFreeChunks(), 6); 98 | 99 | // Check move copy constructor 100 | ChunkTreap newTreap1 = std::move(cTreap); 101 | ASSERT_TRUE(newTreap1.MaxSizeChunk()->GetSize() == 256); 102 | ASSERT_TRUE(newTreap1.MinSizeChunk()->GetSize() == 64); 103 | ASSERT_EQ(newTreap1.TotalFreeChunks(), 6); 104 | 105 | ASSERT_EQ(newTreap1.FirstGreaterOrEqualTo(256), ch8); 106 | ASSERT_EQ(newTreap1.TotalFreeChunks(), 6); 107 | 108 | ASSERT_TRUE(newTreap1.RemoveChunk(ch8)); 109 | ASSERT_EQ(newTreap1.TotalFreeChunks(), 5); 110 | ASSERT_EQ(cTreap.TotalFreeChunks(), 0); 111 | ASSERT_EQ(cTreap.TotalFreeMemory(), 0); 112 | 113 | // Check copy constructor 114 | ChunkTreap newTreap2 = newTreap1; 115 | ASSERT_TRUE(newTreap2.MaxSizeChunk()->GetSize() == 224); 116 | ASSERT_TRUE(newTreap2.MinSizeChunk()->GetSize() == 64); 117 | ASSERT_EQ(newTreap2.TotalFreeChunks(), 5); 118 | 119 | ASSERT_EQ(newTreap2.FirstGreaterOrEqualTo(224), ch7); 120 | ASSERT_EQ(newTreap2.TotalFreeChunks(), 5); 121 | 122 | ASSERT_TRUE(newTreap2.RemoveChunk(ch7)); 123 | ASSERT_EQ(newTreap2.TotalFreeChunks(), 4); 124 | ASSERT_EQ(newTreap1.TotalFreeChunks(), 5); 125 | 126 | ASSERT_EQ(newTreap1.FirstGreaterOrEqualTo(224), ch7); 127 | ASSERT_TRUE(newTreap1.RemoveChunk(ch7)); 128 | ASSERT_EQ(newTreap1.FirstGreaterOrEqualTo(224), nullptr); 129 | 130 | // Check copy assignment operator 131 | ChunkTreap newTreap3; 132 | newTreap3 = newTreap2; 133 | ASSERT_TRUE(newTreap3.MaxSizeChunk()->GetSize() == 160); 134 | ASSERT_TRUE(newTreap3.MinSizeChunk()->GetSize() == 64); 135 | ASSERT_EQ(newTreap3.TotalFreeChunks(), 4); 136 | 137 | ASSERT_EQ(newTreap3.FirstGreaterOrEqualTo(160), ch5); 138 | ASSERT_EQ(newTreap3.TotalFreeChunks(), 4); 139 | 140 | ASSERT_TRUE(newTreap3.RemoveChunk(ch5)); 141 | ASSERT_EQ(newTreap3.TotalFreeChunks(), 3); 142 | ASSERT_EQ(newTreap2.TotalFreeChunks(), 4); 143 | ASSERT_EQ(newTreap2.FirstGreaterOrEqualTo(160), ch5); 144 | 145 | // Check move assignment operator 146 | ChunkTreap newTreap4; 147 | newTreap4 = std::move(newTreap3); 148 | ASSERT_TRUE(newTreap4.MaxSizeChunk()->GetSize() == 128); 149 | ASSERT_TRUE(newTreap4.MinSizeChunk()->GetSize() == 64); 150 | ASSERT_EQ(newTreap4.TotalFreeChunks(), 3); 151 | 152 | ASSERT_EQ(newTreap4.FirstGreaterOrEqualTo(128), ch4); 153 | ASSERT_EQ(newTreap4.TotalFreeChunks(), 3); 154 | 155 | ASSERT_TRUE(newTreap4.RemoveChunk(ch4)); 156 | ASSERT_EQ(newTreap4.TotalFreeChunks(), 2); 157 | ASSERT_EQ(newTreap4.FirstGreaterOrEqualTo(128), nullptr); 158 | 159 | free(ch1); 160 | free(ch2); 161 | free(ch3); 162 | free(ch4); 163 | free(ch5); 164 | free(ch6); 165 | free(ch7); 166 | free(ch8); 167 | free(ch9); 168 | } 169 | 170 | TEST(ChunkTreapTest, CorrectChunkInsertion) 171 | { 172 | using namespace mpp; 173 | 174 | ChunkTreap cTreap; 175 | Chunk* insertedChunk = (Chunk*)malloc(64); 176 | 177 | std::size_t insertedChunkSize = 128; 178 | insertedChunk->SetSize(insertedChunkSize); 179 | 180 | Chunk* result1 = cTreap.FirstGreaterOrEqualTo(insertedChunkSize); 181 | 182 | cTreap.InsertChunk(insertedChunk); 183 | 184 | Chunk* result2 = cTreap.FirstGreaterOrEqualTo(insertedChunkSize); 185 | 186 | ASSERT_TRUE((result1 == nullptr && result2 == insertedChunk)); 187 | 188 | free(insertedChunk); 189 | } 190 | 191 | TEST(ChunkTreapTest, NonexistingBigChunk) 192 | { 193 | using namespace mpp; 194 | 195 | ChunkTreap cTreap; 196 | 197 | Chunk* chunk1 = (Chunk*)malloc(64); 198 | Chunk* chunk2 = (Chunk*)malloc(64); 199 | Chunk* chunk3 = (Chunk*)malloc(64); 200 | 201 | chunk1->SetSize(32); 202 | chunk2->SetSize(64); 203 | chunk3->SetSize(96); 204 | 205 | cTreap.InsertChunk(chunk1); 206 | cTreap.InsertChunk(chunk2); 207 | cTreap.InsertChunk(chunk3); 208 | 209 | std::size_t nonexistentSize{ 128 }; 210 | 211 | Chunk* result = cTreap.FirstGreaterOrEqualTo(nonexistentSize); 212 | 213 | ASSERT_TRUE(result == nullptr); 214 | 215 | free(chunk1); 216 | free(chunk2); 217 | free(chunk3); 218 | } 219 | 220 | TEST(ChunkTreapTest, BasicFunctions) 221 | { 222 | using namespace mpp; 223 | 224 | ChunkTreap cTreap; 225 | 226 | Chunk* ch1 = (Chunk*)malloc(64); 227 | Chunk* ch2 = (Chunk*)malloc(64); 228 | Chunk* ch3 = (Chunk*)malloc(64); 229 | Chunk* ch4 = (Chunk*)malloc(64); 230 | 231 | ch1->SetSize(32); 232 | ch2->SetSize(32); 233 | ch3->SetSize(32); 234 | ch4->SetSize(32); 235 | 236 | cTreap.InsertChunk(ch1); 237 | cTreap.InsertChunk(ch2); 238 | cTreap.InsertChunk(ch3); 239 | cTreap.InsertChunk(ch4); 240 | 241 | ASSERT_TRUE((cTreap.MaxSizeChunk()->GetSize() == 32 && cTreap.MinSizeChunk()->GetSize() == 32)); 242 | 243 | free(ch1); 244 | free(ch2); 245 | free(ch3); 246 | free(ch4); 247 | } 248 | 249 | TEST(ChunkTreapTest, CorrectDelete) 250 | { 251 | using namespace mpp; 252 | 253 | ChunkTreap cTreap; 254 | 255 | Chunk* ch1 = (Chunk*)malloc(128); 256 | Chunk* ch2 = (Chunk*)malloc(128); 257 | Chunk* ch3 = (Chunk*)malloc(128); 258 | Chunk* ch4 = (Chunk*)malloc(128); 259 | Chunk* ch5 = (Chunk*)malloc(128); 260 | 261 | ch1->SetSize(32); 262 | ch2->SetSize(64); 263 | ch3->SetSize(128); 264 | ch4->SetSize(256); 265 | ch5->SetSize(512); 266 | 267 | cTreap.InsertChunk(ch1); 268 | cTreap.InsertChunk(ch2); 269 | cTreap.InsertChunk(ch3); 270 | cTreap.InsertChunk(ch4); 271 | 272 | ASSERT_EQ(cTreap.MaxSizeChunk()->GetSize(), 256); 273 | ASSERT_EQ(cTreap.MinSizeChunk()->GetSize(), 32); 274 | ASSERT_EQ(cTreap.TotalFreeChunks(), 4); 275 | 276 | ASSERT_TRUE(cTreap.RemoveChunk(ch1)); 277 | ASSERT_TRUE(cTreap.RemoveChunk(ch4)); 278 | 279 | ASSERT_EQ(cTreap.MaxSizeChunk()->GetSize(), 128); 280 | ASSERT_EQ(cTreap.MinSizeChunk()->GetSize(), 64); 281 | ASSERT_EQ(cTreap.TotalFreeChunks(), 2); 282 | 283 | free(ch1); 284 | free(ch2); 285 | free(ch3); 286 | free(ch4); 287 | free(ch5); 288 | } 289 | 290 | TEST(ChunkTreapTest, DeleteMany) 291 | { 292 | using namespace mpp; 293 | 294 | ChunkTreap cTreap; 295 | 296 | Chunk* ch1 = (Chunk*)malloc(128); 297 | Chunk* ch2 = (Chunk*)malloc(128); 298 | Chunk* ch3 = (Chunk*)malloc(128); 299 | Chunk* ch4 = (Chunk*)malloc(128); 300 | 301 | ch1->SetSize(32); 302 | ch2->SetSize(32); 303 | ch3->SetSize(32); 304 | ch4->SetSize(32); 305 | 306 | cTreap.InsertChunk(ch1); 307 | cTreap.InsertChunk(ch2); 308 | cTreap.InsertChunk(ch3); 309 | cTreap.InsertChunk(ch4); 310 | 311 | cTreap.RemoveChunk(ch2); 312 | cTreap.RemoveChunk(ch3); 313 | 314 | ASSERT_TRUE((cTreap.MaxSizeChunk()->GetSize() == 32 && cTreap.MinSizeChunk()->GetSize() == 32)); 315 | 316 | free(ch1); 317 | free(ch2); 318 | free(ch3); 319 | free(ch4); 320 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/chunk.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "mpplib/utils/macros.hpp" 4 | #include 5 | #include 6 | 7 | #if MPP_COLOUR == 1 8 | #include "mpplib/utils/colours.hpp" 9 | namespace col = mpp::utils::colours; 10 | #endif 11 | 12 | namespace mpp { 13 | 14 | /** 15 | * @brief Represent chunk of memory. 16 | */ 17 | struct Chunk 18 | { 19 | /** 20 | * @brief chunk header structure. 21 | */ 22 | struct ChunkHeader_t 23 | { 24 | /** 25 | * @brief Size of the previous chunk. 26 | */ 27 | std::size_t prevChunkSize; 28 | 29 | /** 30 | * @brief actual chunk header. 31 | * 32 | * Contains all required mark bits. Maximum amount of mark bits is 5. 33 | * Currently used only 2. 34 | * Bit-2: Previous chunk in use 35 | * Bit-3: Current chunk in use 36 | */ 37 | std::size_t chunkHeader; 38 | } ChunkHeader; 39 | 40 | static constexpr uint32_t EXTRACT_SIZE_SHIFT = 5; 41 | static constexpr uint32_t INFO_BITS_MASK = 0b11111; 42 | static constexpr uint32_t IS_USED_BIT_MASK = 0b1111; 43 | static constexpr uint32_t IS_USED_SHIFT = 3; 44 | static constexpr uint32_t IS_PREV_IN_USE_BIT_MASK = 0b111; 45 | static constexpr uint32_t IS_PREV_IN_USE_SHIFT = 2; 46 | 47 | /** 48 | * @brief Constructs new chunk at location, passed as first parameter. 49 | * @param t_newChunkPtr actual memory, where chunk is going to be constructed. 50 | * @param t_prevSize previous chunk size value. 51 | * @param t_chunkSize current chunk size value. 52 | * @param t_isInUse current chunk in use bit. 53 | * @param t_isPrevInUse previous chunk in use bit. 54 | * @return Chunk* with all fields set to passed parameters 55 | */ 56 | static Chunk* ConstructChunk(void* t_newChunkPtr, 57 | std::size_t t_prevSize, 58 | std::size_t t_chunkSize, 59 | uint8_t t_isInUse, 60 | uint8_t t_isPrevInUse) 61 | { 62 | MPP_DEBUG_ASSERT(t_newChunkPtr, "Chunk pointer is nullptr!"); 63 | auto* newChunk = static_cast(t_newChunkPtr); 64 | 65 | MPP_VALGRIND_DEFINE_CHUNK_HEADER(newChunk); 66 | MPP_UNPOISON_MEM(newChunk, t_chunkSize); 67 | 68 | newChunk->SetPrevSize(t_prevSize); 69 | newChunk->SetSize(t_chunkSize); 70 | newChunk->SetIsUsed(t_isInUse); 71 | newChunk->SetIsPrevInUse(t_isPrevInUse); 72 | 73 | return newChunk; 74 | } 75 | 76 | /** 77 | * @brief Get pointer to user data, from pointer to chunk. 78 | * @param t_chunk actual chunk, to get pointer to user data from. 79 | * @return void* pointer to start of user data in current chunk 80 | */ 81 | static void* GetUserDataPtr(Chunk* t_chunk) 82 | { 83 | MPP_DEBUG_ASSERT(t_chunk, "Chunk pointer is nullptr!"); 84 | return reinterpret_cast(reinterpret_cast(t_chunk) + 85 | sizeof(Chunk::ChunkHeader)); 86 | } 87 | 88 | /** 89 | * @brief Get pointer to chunk header from user data pointer. 90 | * @param t_userData pointer to user data 91 | * @return Chunk* pointer to chunk header 92 | */ 93 | static Chunk* GetHeaderPtr(void* t_userData) 94 | { 95 | MPP_DEBUG_ASSERT(t_userData, "t_userData pointer is nullptr!"); 96 | return reinterpret_cast(reinterpret_cast(t_userData) - 97 | sizeof(Chunk::ChunkHeader)); 98 | } 99 | 100 | /** 101 | * @brief Get previous chunk in memory. 102 | * @param t_chunk chunk pointer. 103 | * @return Chunk* located previously in memory chunk 104 | */ 105 | static Chunk* GetPrevChunk(Chunk* t_chunk) 106 | { 107 | MPP_DEBUG_ASSERT(t_chunk, "Chunk pointer is nullptr!"); 108 | return reinterpret_cast(reinterpret_cast(t_chunk) - 109 | t_chunk->GetPrevSize()); 110 | } 111 | 112 | /** 113 | * @brief Get next chunk in memory. 114 | * @param t_chunk chunk pointer. 115 | * @return Chunk* located right after specified chunk 116 | */ 117 | static Chunk* GetNextChunk(Chunk* t_chunk) 118 | { 119 | MPP_DEBUG_ASSERT(t_chunk, "Chunk pointer is nullptr!"); 120 | return reinterpret_cast(reinterpret_cast(t_chunk) + 121 | t_chunk->GetSize()); 122 | } 123 | 124 | /** 125 | * @brief Get previous size from chunk header. 126 | * @return std::size_t extracted from header previous size value 127 | */ 128 | std::size_t GetPrevSize() const 129 | { 130 | return (this->ChunkHeader.prevChunkSize >> EXTRACT_SIZE_SHIFT) << EXTRACT_SIZE_SHIFT; 131 | }; 132 | 133 | /** 134 | * @brief Set previous size in chunk header 135 | * @param size new size to set 136 | */ 137 | void SetPrevSize(std::size_t size) 138 | { 139 | this->ChunkHeader.prevChunkSize = size; 140 | } 141 | 142 | /** 143 | * @brief Get size of current chunk from its chunk header. 144 | * @return std::size_t current chunk size 145 | */ 146 | std::size_t GetSize() const 147 | { 148 | return (this->ChunkHeader.chunkHeader >> EXTRACT_SIZE_SHIFT) << EXTRACT_SIZE_SHIFT; 149 | }; 150 | 151 | /** 152 | * @brief Set size of current chunk. 153 | * 154 | * This function wouldn't change any flag bits. 155 | * @param size new size value. 156 | */ 157 | void SetSize(std::size_t size) 158 | { 159 | this->ChunkHeader.chunkHeader = 160 | (size | (INFO_BITS_MASK & this->ChunkHeader.chunkHeader)); 161 | } 162 | 163 | /** 164 | * @brief Get pointer to user data, from current chunk. 165 | * @return void* pointer to start of user data in current chunk 166 | */ 167 | std::byte* GetUserData() 168 | { 169 | return reinterpret_cast(this) + sizeof(Chunk::ChunkHeader); 170 | }; 171 | 172 | /** 173 | * @brief returns state of current chunk. (in use / isn't in use) 174 | * @return true if current chunk is in use 175 | * @return false if current chunk isn't in use 176 | */ 177 | bool IsUsed() const 178 | { 179 | return (this->ChunkHeader.chunkHeader & IS_USED_BIT_MASK) >> IS_USED_SHIFT; 180 | }; 181 | 182 | /** 183 | * @brief returns state of previous in memory chunk. (in use / isn't in use) 184 | * @return true if previous chunk is in use 185 | * @return false if previous chunk isn't in use 186 | */ 187 | bool IsPrevInUse() const 188 | { 189 | return (this->ChunkHeader.chunkHeader & IS_PREV_IN_USE_BIT_MASK) >> 190 | IS_PREV_IN_USE_SHIFT; 191 | }; 192 | 193 | /** 194 | * @brief Update value of PrevInUse flag. 195 | * @param opt new value for PrevInUse flag. 196 | */ 197 | void SetIsPrevInUse(uint8_t opt) 198 | { 199 | if (opt) { 200 | this->ChunkHeader.chunkHeader |= 1UL << IS_PREV_IN_USE_SHIFT; 201 | } else { 202 | this->ChunkHeader.chunkHeader &= ~(1UL << IS_PREV_IN_USE_SHIFT); 203 | } 204 | }; 205 | 206 | /** 207 | * @brief Update value of InUse flag. 208 | * @param opt new value for InUse flag. 209 | */ 210 | void SetIsUsed(uint8_t opt) 211 | { 212 | if (opt) { 213 | this->ChunkHeader.chunkHeader |= 1UL << IS_USED_SHIFT; 214 | } else { 215 | this->ChunkHeader.chunkHeader &= ~(1UL << IS_USED_SHIFT); 216 | } 217 | }; 218 | 219 | #if MPP_STATS == 1 220 | /** 221 | * @brief Helper method to dump chunk structure in human-readable format. 222 | * @param t_out stream to write to. 223 | * @param t_ch chunk pointer. 224 | * @return std::ostream& stream that was used 225 | */ 226 | static std::ostream& DumpChunk(std::ostream& t_out, Chunk* t_ch) 227 | { 228 | #if MPP_COLOUR == 1 229 | t_out << "[" << reinterpret_cast(t_ch) << "](" << t_ch->GetPrevSize() << ", " 230 | << col::BRIGHT_BLUE << t_ch->GetSize() << col::RESET; 231 | 232 | if (t_ch->IsUsed()) { 233 | t_out << "|InUse:" << col::GREEN << t_ch->IsUsed() << col::RESET; 234 | } else { 235 | t_out << "|InUse:" << col::RED << t_ch->IsUsed() << col::RESET; 236 | } 237 | 238 | if (t_ch->IsPrevInUse()) { 239 | t_out << "|PrevInUse:" << col::GREEN << t_ch->IsPrevInUse() << col::RESET << ")"; 240 | } else { 241 | t_out << "|PrevInUse:" << col::RED << t_ch->IsPrevInUse() << col::RESET << ")"; 242 | } 243 | #else 244 | t_out << "[" << reinterpret_cast(t_ch) << "](" << t_ch->GetPrevSize() << ", " 245 | << t_ch->GetSize() << "|InUse:" << t_ch->IsUsed() 246 | << "|PrevInUse:" << t_ch->IsPrevInUse() << ")"; 247 | #endif 248 | 249 | return t_out; 250 | } 251 | 252 | /** 253 | * @brief Overloaded "<<" operator to dump chunk structure in human-readable 254 | * format. 255 | * @param t_out stream to write to. 256 | * @param t_ch chunk pointer. 257 | * @return std::ostream& stream that was used 258 | */ 259 | friend std::ostream& operator<<(std::ostream& t_out, Chunk* t_ch) 260 | { 261 | return DumpChunk(t_out, t_ch); 262 | } 263 | #endif 264 | }; 265 | } -------------------------------------------------------------------------------- /libmemplusplus/include/mpplib/shared_gcptr.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "mpplib/gcptr.hpp" 8 | #include "mpplib/memory_manager.hpp" 9 | #include "mpplib/utils/profiler_definitions.hpp" 10 | 11 | namespace mpp { 12 | template 13 | class SharedGcPtrBase 14 | { 15 | protected: 16 | using ElementType = typename std::remove_extent::type; 17 | //! @brief pointer to the object. 18 | ElementType* m_objectPtr{ nullptr }; 19 | }; 20 | 21 | template 22 | class SharedGcPtrArray 23 | { 24 | protected: 25 | //! @brief Size of the created array 26 | uint32_t m_arraySize = 0; 27 | }; 28 | 29 | template<> 30 | class SharedGcPtrArray 31 | { 32 | }; 33 | 34 | /** 35 | * @brief SharedGcPtr class. Behaves like normal shared ptr. 36 | * @tparam Type of user object. 37 | */ 38 | template 39 | class SharedGcPtr final 40 | : public SharedGcPtrBase 41 | , public SharedGcPtrArray::value> 42 | , public GcPtr 43 | { 44 | friend class GarbageCollector; 45 | 46 | protected: 47 | using ElementType = typename std::remove_extent::type; 48 | 49 | //! @brief Number of references to the object. 50 | uint32_t* m_references{ nullptr }; 51 | 52 | //! @brief Releases the object, decrementing m_references. 53 | void DeleteReference(); 54 | 55 | //! @brief Decrements the reference counter and deletes the object if number of references 56 | //! is 0. 57 | void DecrementRefCounter(); 58 | 59 | public: 60 | //! @brief Default constructor. 61 | SharedGcPtr(); 62 | 63 | /** 64 | * @brief Construct SharedGcPtr from nullptr. 65 | * @param t_newData nullpointer. 66 | */ 67 | SharedGcPtr(std::nullptr_t t_newData); 68 | 69 | /** 70 | * @brief Construct SharedGcPtr from pointer to user object type. 71 | * @param t_obj pointer to user object. 72 | */ 73 | explicit SharedGcPtr(ElementType* t_obj); 74 | 75 | /** 76 | * @brief Copy-Constructor to construct from const SharedGcPtr reference. 77 | * @param t_other rvalue reference to another SharedGcPtr 78 | */ 79 | SharedGcPtr(const SharedGcPtr& t_other); 80 | 81 | /** 82 | * @brief Copy-Constructor to construct from const SharedGcPtr rvalue reference. 83 | * @param t_other const reference to another SharedGcPtr 84 | */ 85 | SharedGcPtr(SharedGcPtr&& t_other) noexcept; 86 | 87 | /** 88 | * @brief Construct SharedGcPtr which holds an array of objects of type ElementType 89 | * @param t_obj pointer to user object. 90 | * @param t_arraySize size of the array. 91 | */ 92 | SharedGcPtr(Type t_obj, uint32_t t_arraySize); 93 | 94 | //! @brief Destroys SharedGcPtr. 95 | ~SharedGcPtr() override; 96 | 97 | // SharedGcPtr& operator=(SharedGcPtr t_other); 98 | /** 99 | * @brief Overloaded assignment operator, to construct object from rvalue 100 | * reference. 101 | * @param t_other rvalue reference to SharedGcPtr. 102 | * @return SharedGcPtr& new object 103 | */ 104 | SharedGcPtr& operator=(SharedGcPtr&& t_other) noexcept; 105 | 106 | /** 107 | * @brief Overloaded assignment operator, to construct object from const 108 | * SharedGcPtr reference. 109 | * @param t_other const reference to SharedGcPtr. 110 | * @return SharedGcPtr& new object 111 | */ 112 | SharedGcPtr& operator=(const SharedGcPtr& t_other); 113 | 114 | /** 115 | * @brief Overloaded assignment operator, to construct object from user data 116 | * pointer. 117 | * @param t_newData pointer to user data of type Type. 118 | * @return SharedGcPtr& new object 119 | */ 120 | SharedGcPtr& operator=(Type* t_newData); 121 | 122 | /** 123 | * @brief Overloaded assignment operator, to construct object from nullptr. 124 | * @param t_newData nullptr 125 | * @return SharedGcPtr& new object 126 | */ 127 | SharedGcPtr& operator=(std::nullptr_t t_newData); 128 | 129 | //! @brief check, if two SharedGcPtr's are equal. 130 | bool operator==(const SharedGcPtr& t_other) const noexcept; 131 | 132 | //! @brief check, if two SharedGcPtr's are not equal. 133 | bool operator!=(const SharedGcPtr& t_other) const noexcept; 134 | 135 | //! @brief check, that lhs is less than or equal to rhs. 136 | bool operator<=(const SharedGcPtr& t_other) const noexcept; 137 | 138 | //! @brief check, that lhs is less than rhs. 139 | bool operator<(const SharedGcPtr& t_other) const noexcept; 140 | 141 | //! @brief check, that lhs is greater than or equal to rhs. 142 | bool operator>=(const SharedGcPtr& t_other) const noexcept; 143 | 144 | //! @brief check, that lhs is greater than rhs. 145 | bool operator>(const SharedGcPtr& t_other) const noexcept; 146 | 147 | /** 148 | * @brief Allows to use smart pointers as usual pointers. 149 | * @return Type* pointer to user data 150 | */ 151 | ElementType* operator->() const noexcept; 152 | 153 | /** 154 | * @brief Allows to use smart pointers as usual pointers. 155 | * @return Type& reference to user data 156 | */ 157 | ElementType& operator*() const noexcept; 158 | 159 | //! @brief Allows automatic conversions to bool. 160 | explicit operator bool() const; 161 | 162 | //! @brief Calculates distance between two pointers 163 | std::ptrdiff_t operator-(const SharedGcPtr& t_other) const noexcept; 164 | 165 | /** 166 | * @brief Retrieves element from the array by its index 167 | * @param t_index - index to access 168 | */ 169 | ElementType& operator[](std::ptrdiff_t t_index) const noexcept; 170 | 171 | //! @brief Tries to add SharedGcPtr to list of all active GcPtr's. 172 | void AddToGcList(); 173 | 174 | /** 175 | * @brief Tries to delete SharedGcPtr from list of all active GcPtr's. 176 | * @return true if succeed, false otherwise 177 | */ 178 | bool DeleteFromGcList(); 179 | 180 | /** 181 | * @brief wrapper around Reset(std::nullptr_t). 182 | * @sa Reset(std::nullptr_t). 183 | */ 184 | void Reset(); 185 | 186 | //! @brief Resets smart pointer. Decrements references counter. 187 | void Reset(std::nullptr_t); 188 | 189 | //! @brief Calls object destructor and deallocates memory 190 | void Destroy(); 191 | 192 | /** 193 | * @brief Performs swap operation on two SharedGcPtr's. 194 | * @param t_other reference to SharedGcPtr to swap with. 195 | */ 196 | void Swap(SharedGcPtr& t_other); 197 | 198 | /** 199 | * @brief Returns a raw pointer to user data. 200 | * @return Type* raw pointer to user data 201 | */ 202 | ElementType* Get() const noexcept; 203 | 204 | /** 205 | * @brief Overridden function, that returns raw pointer to user data. 206 | * @return void* pointer to user data. 207 | */ 208 | void* GetVoid() const noexcept override; 209 | 210 | /** 211 | * @brief Overridden function, that sets new value for object pointer. 212 | * @param t_newPtr new pointer. 213 | */ 214 | void UpdatePtr(void* t_newPtr) override; 215 | 216 | /** 217 | * @brief Return number of references to controlled object. 218 | * @return uint32_t number of references to controlled object. 219 | */ 220 | uint32_t UseCount() override; 221 | 222 | /** 223 | * @brief If this smart pointer is an array - return its size 224 | * @return uint32_t array size pointed to by current SharedPtr 225 | */ 226 | uint32_t GetArraySize(); 227 | 228 | /** 229 | * @brief Method to dump SharedGcPtr structure in human-readable format. 230 | * @param t_out stream to write to. 231 | * @return std::ostream& passed as parameter stream 232 | */ 233 | std::ostream& Print(std::ostream& t_out) const override; 234 | 235 | /** 236 | * @brief Check, if user tries to initialize SharedGcPtr with 237 | * invalid object. If so, abort program. 238 | * @param t_obj a pointer to the object that is used to initialize ShareGcPtr 239 | */ 240 | void CheckInvalidInitialization(ElementType* t_obj); 241 | }; 242 | 243 | /** 244 | * @brief Method that constructs a new SharedGcPtr. It will allocate enough space, create the 245 | * object and construct it. 246 | * @tparam T type of the user object. 247 | * @tparam Args type of variadic arguments to pass to user object constructor. 248 | * @param t_args variadic arguments to pass to user object constructor. 249 | * @return SharedGcPtr constructed SharedGcPtr. 250 | */ 251 | template 252 | SharedGcPtr MakeShared(Args&&... t_args) 253 | { 254 | PROFILE_FUNCTION(); 255 | Type* ptr = static_cast(Allocate(sizeof(Type))); 256 | Type* obj = MM::Construct(ptr, std::forward(t_args)...); 257 | return SharedGcPtr(obj); 258 | } 259 | 260 | /** 261 | * @brief Method that constructs a new SharedGcPtr that holds an array. 262 | * It will allocate enough space, create the object and construct it. 263 | * 264 | * @tparam T type of the user object. 265 | * @tparam Args type of variadic arguments to pass to user object constructor. 266 | * @param t_size array length 267 | * @param t_args variadic arguments to pass to user object constructor. 268 | * @return SharedGcPtr SharedGcPtr with constructed objects array. 269 | */ 270 | template 271 | SharedGcPtr MakeSharedN(uint32_t t_size, Args&&... t_args) 272 | { 273 | PROFILE_FUNCTION(); 274 | 275 | Type* ptr = static_cast(Allocate(sizeof(Type) * t_size)); 276 | 277 | for (uint32_t i = 0; i < t_size; i++) { 278 | [[maybe_unused]] Type* obj = 279 | MM::Construct(ptr + i, std::forward(t_args)...); 280 | } 281 | 282 | return SharedGcPtr(ptr, t_size); 283 | } 284 | } 285 | 286 | // include templates implementation 287 | #include "mpplib/shared_gcptr-imp.hpp" -------------------------------------------------------------------------------- /libmemplusplus/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # ################ SET UP PROJECT + VERSION ################# 2 | project(mpp LANGUAGES CXX) 3 | set(VERSION_MAJOR 0) 4 | set(VERSION_MINOR 3) 5 | set(VERSION_PATCH 5) 6 | set(MPP_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}) 7 | 8 | # ################ CHECK IF IT IS LINUX ################# 9 | if(WIN32) 10 | message(FATAL_ERROR "Windows is not currently supported!") 11 | endif() 12 | 13 | # ################ LIBRARY OPTIONS ################# 14 | if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") 15 | option(MPP_SANITIZERS "Build mpp with support of sanitizers" OFF) 16 | endif() 17 | 18 | option(MPP_BUILD_SHARED_LIBS "Request build of shared libraries" ON) 19 | option(MPP_FULL_DEBUG "Build mpp in full debug mode. Added extended security checks" OFF) 20 | option(MPP_SECURE "Build mpp in secure mode" OFF) 21 | option(MPP_PROFILE "Build mpp with support of profiling" ON) 22 | option(MPP_COLOUR "Build mpp with support of coloured output" ON) 23 | option(MPP_STATS "Build mpp with support of statistics dumping" ON) 24 | option(MPP_ENABLE_LOGGING "Build mpp with logging support" OFF) 25 | option(MPP_VALGRIND "Build mpp with valgrind" OFF) 26 | 27 | # ################ DEFAULT BUILD TYPE ################# 28 | if(NOT CMAKE_BUILD_TYPE) 29 | if("${CMAKE_BINARY_DIR}" MATCHES ".*(D|d)ebug$") 30 | message(STATUS "No build type selected, default to: Debug") 31 | set(CMAKE_BUILD_TYPE "Debug") 32 | else() 33 | message(STATUS "No build type selected, default to: Release") 34 | set(CMAKE_BUILD_TYPE "Release") 35 | endif() 36 | endif() 37 | 38 | # ################ SET DEFINES FOR DEBUG MODE ################# 39 | if(CMAKE_BUILD_TYPE MATCHES ".*(D|d)ebug$") 40 | # standart debug defines 41 | set(MPP_DEBUG ON) 42 | list(APPEND MPP_DEFINES MPP_DEBUG=1) 43 | list(APPEND MPP_DEFINES MPP_FILL_CHAR='\\x99') 44 | else() 45 | set(MPP_DEBUG OFF) 46 | list(APPEND MPP_DEFINES MPP_DEBUG=0) 47 | endif() 48 | 49 | if(MPP_ENABLE_LOGGING MATCHES "ON") 50 | list(APPEND MPP_DEFINES MPP_ENABLE_LOGGING=1) 51 | endif() 52 | 53 | # ################ SET DEFINES IF BUILDING WITH FUZZER ################# 54 | if(MPP_BUILD_FUZZER MATCHES "ON" OR MPP_USE_FUZZER_DEFINITIONS MATCHES "ON") 55 | set(MPP_SANITIZERS ON) 56 | list(APPEND MPP_DEFINES MPP_FUZZER_INSECURE=1) 57 | else() 58 | list(APPEND MPP_DEFINES MPP_FUZZER_INSECURE=0) 59 | endif() 60 | 61 | # extended debug features, such as full free-list checks for double-free 62 | if(MPP_FULL_DEBUG MATCHES "ON" AND MPP_DEBUG MATCHES "ON") 63 | message(STATUS "Building in full debug mode: MPP_FULL_DEBUG=1") 64 | set(MPP_FULL_DEBUG ON) 65 | list(APPEND MPP_DEFINES MPP_FULL_DEBUG=1) 66 | 67 | # In full debug mode we want to fill memory 68 | # with non-zero values, so we actually can easily crash 69 | # or spot a weird behaviour 70 | list(APPEND MPP_DEFINES MPP_FILL_CHAR='\\x99') 71 | else() 72 | set(MPP_FULL_DEBUG OFF) 73 | list(APPEND MPP_DEFINES MPP_FULL_DEBUG=0) 74 | endif() 75 | 76 | if(MPP_SECURE MATCHES "ON") 77 | message(STATUS "Building in secure mode: MPP_SECURE=1") 78 | list(APPEND MPP_DEFINES MPP_SECURE=1) 79 | list(APPEND MPP_DEFINES __STDC_WANT_LIB_EXT1__=1) 80 | 81 | # In secure mode we want to fill memory with zeroes 82 | # (prevents memleaks through string printing functions) 83 | if(MPP_FULL_DEBUG MATCHES "OFF" AND MPP_DEBUG MATCHES "OFF") 84 | list(APPEND MPP_DEFINES MPP_FILL_CHAR='\\x00') 85 | endif() 86 | else() 87 | list(APPEND MPP_DEFINES MPP_SECURE=0) 88 | endif() 89 | 90 | if(MPP_PROFILE MATCHES "ON") 91 | message(STATUS "Building in profiling mode: MPP_PROFILE=1") 92 | list(APPEND MPP_DEFINES MPP_PROFILE=1) 93 | else() 94 | list(APPEND MPP_DEFINES MPP_PROFILE=0) 95 | endif() 96 | 97 | if(MPP_COLOUR MATCHES "ON") 98 | message(STATUS "Building with support of coloured output: MPP_COLOUR=1") 99 | list(APPEND MPP_DEFINES MPP_COLOUR=1) 100 | else() 101 | list(APPEND MPP_DEFINES MPP_COLOUR=0) 102 | endif() 103 | 104 | if(MPP_STATS MATCHES "ON" AND MPP_DEBUG MATCHES "OFF") 105 | message(FATAL_ERROR "Statistics cannot be enabled in non-debug mode!") 106 | elseif(MPP_STATS MATCHES "ON" AND MPP_DEBUG MATCHES "ON") 107 | message(STATUS "Building with support of statistics dumping: MPP_STATS=1") 108 | list(APPEND MPP_DEFINES MPP_STATS=1) 109 | else() 110 | list(APPEND MPP_DEFINES MPP_STATS=0) 111 | endif() 112 | 113 | if(MPP_BUILD_TESTS MATCHES "ON") 114 | list(APPEND MPP_DEFINES MPP_BUILD_TESTS=1) 115 | endif() 116 | 117 | message(STATUS "##################### LIBRARY OPTIONS #####################") 118 | message(STATUS "MPP_SANITIZERS : ${MPP_SANITIZERS}") 119 | message(STATUS "MPP_VALGRIND : ${MPP_VALGRIND}") 120 | message(STATUS "MPP_BUILD_SHARED_LIBS : ${MPP_BUILD_SHARED_LIBS}") 121 | message(STATUS "MPP_FULL_DEBUG : ${MPP_FULL_DEBUG}") 122 | message(STATUS "MPP_DEBUG : ${MPP_DEBUG}") 123 | message(STATUS "MPP_SECURE : ${MPP_SECURE}") 124 | message(STATUS "MPP_PROFILE : ${MPP_PROFILE}") 125 | message(STATUS "MPP_COLOUR : ${MPP_COLOUR}") 126 | message(STATUS "MPP_STATS : ${MPP_STATS}") 127 | message(STATUS "MPP_ENABLE_LOGGING : ${MPP_ENABLE_LOGGING}") 128 | 129 | # ################ ADD CONFIG FILE ################# 130 | configure_file(${PROJECT_SOURCE_DIR}/src/config.hpp.in 131 | ${PROJECT_SOURCE_DIR}/include/mpplib/config.hpp @ONLY 132 | ) 133 | 134 | # ################ ADD INSTALL PATHS ################# 135 | # - CMAKE_INSTALL_LIBDIR 136 | # - CMAKE_INSTALL_BINDIR 137 | # - CMAKE_INSTALL_INCLUDEDIR 138 | include(GNUInstallDirs) 139 | 140 | # ################ SET SOURCES ################# 141 | list(APPEND MPP_SOURCES 142 | src/heuristics/heuristics.cpp 143 | src/containers/chunk_treap.cpp 144 | src/containers/gc_graph.cpp 145 | src/containers/visualizers/gc_graph_visualizer.cpp 146 | src/containers/visualizers/chunk_treap_visualizer.cpp 147 | src/containers/node.cpp 148 | src/containers/vertex.cpp 149 | src/shared_gcptr.cpp 150 | src/arena.cpp 151 | src/memory_manager.cpp 152 | src/gc.cpp 153 | src/utils/utils.cpp 154 | src/utils/env_options.cpp 155 | src/utils/log.cpp 156 | ) 157 | 158 | # ################ ADD SPECIFIC SOURCES ################# 159 | if(MPP_STATS MATCHES "ON") 160 | list(APPEND MPP_SOURCES src/utils/statistics.cpp) 161 | endif() 162 | 163 | if(MPP_PROFILE MATCHES "ON" OR MPP_STATS MATCHES "ON") 164 | list(APPEND MPP_SOURCES src/utils/timer.cpp) 165 | 166 | if(MPP_PROFILE MATCHES "ON") 167 | list(APPEND MPP_SOURCES src/utils/profiler.cpp) 168 | list(APPEND MPP_SOURCES src/utils/profiler_timer.cpp) 169 | endif() 170 | endif() 171 | 172 | # ################ SET TARGET NAME ################# 173 | if(MPP_SECURE MATCHES "ON") 174 | set(MPP_BASENAME "${PROJECT_NAME}-secure") 175 | else() 176 | set(MPP_BASENAME ${PROJECT_NAME}) 177 | endif() 178 | 179 | string(TOLOWER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_LC) 180 | 181 | if(NOT(CMAKE_BUILD_TYPE_LC MATCHES "^(release|relwithdebinfo|minsizerel)$")) 182 | set(MPP_BASENAME "${MPP_BASENAME}-${CMAKE_BUILD_TYPE_LC}") # append build type (e.g. -debug) if not a release version 183 | endif() 184 | 185 | if(MPP_BUILD_SHARED_LIBS) 186 | list(APPEND MPP_BUILD_TARGETS "shared") 187 | else() 188 | list(APPEND MPP_BUILD_TARGETS "static") 189 | endif() 190 | 191 | # ################ OUTPUT LIBRARY INFO ################# 192 | message(STATUS "##################### LIBRARY INFO #####################") 193 | message(STATUS "Library base name: ${MPP_BASENAME}") 194 | message(STATUS "Build type : ${CMAKE_BUILD_TYPE_LC}") 195 | message(STATUS "Install directory: ${CMAKE_INSTALL_LIBDIR}") 196 | message(STATUS "Build targets : ${MPP_BUILD_TARGETS}") 197 | 198 | # ################ COMPILE LIBRARY ################# 199 | if(MPP_BUILD_SHARED_LIBS) 200 | add_library(${PROJECT_NAME} SHARED ${MPP_SOURCES}) 201 | else() 202 | add_library(${PROJECT_NAME} STATIC ${MPP_SOURCES}) 203 | endif() 204 | 205 | # ################ ENABLE LTO OPTIMIZATIONS ################# 206 | # target_compile_options(${PROJECT_NAME} PRIVATE -flto) 207 | 208 | # ################ ALIAS TO USE AS AN INTERNAL PROJECT ################# 209 | add_library(mpp::mpp ALIAS ${PROJECT_NAME}) 210 | 211 | # ################ SET DEBUG COMPILER OPTIONS ################# 212 | if(CMAKE_BUILD_TYPE MATCHES ".*(D|d)ebug$") 213 | # ADDED TO SUPPORT DEBUGGING 214 | target_compile_options(${PROJECT_NAME} PRIVATE -g -O0) # to support backtrace dumping in debug mode 215 | target_link_libraries(${PROJECT_NAME} PRIVATE -ldl) 216 | endif() 217 | 218 | # ################ ENABLE SUPPORT OF SANITIZERS ################# 219 | if(MPP_SANITIZERS MATCHES "ON") 220 | message(WARNING "Sanitizers are enabled. This is not recommended for production builds!") 221 | target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address -g -O0 -fno-omit-frame-pointer) 222 | target_link_libraries(${PROJECT_NAME} PRIVATE -fsanitize=address) 223 | list(APPEND MPP_DEFINES MPP_SANITIZERS=1) 224 | endif() 225 | 226 | if(MPP_VALGRIND MATCHES "ON") 227 | find_package(Valgrind REQUIRED) 228 | 229 | if(Valgrind_FOUND) 230 | message(WARNING "Valgrind is enabled. This is not recommended for production builds!") 231 | message(STATUS "Valgrind include dir: ${Valgrind_INCLUDE_DIR}") 232 | target_include_directories(${PROJECT_NAME} PUBLIC ${Valgrind_INCLUDE_DIR}) 233 | list(APPEND MPP_DEFINES MPP_VALGRIND=1) 234 | else() 235 | message(FATAL_ERROR "Valgrind not found!") 236 | endif() 237 | endif() 238 | 239 | if(MPP_BUILD_FUZZER MATCHES "ON") 240 | target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address,fuzzer-no-link) 241 | target_link_options(${PROJECT_NAME} PRIVATE -fsanitize=address,fuzzer-no-link) 242 | endif() 243 | 244 | target_compile_definitions(${PROJECT_NAME} 245 | PUBLIC ${MPP_DEFINES} 246 | ) 247 | 248 | set_target_properties(${PROJECT_NAME} 249 | PROPERTIES 250 | VERSION ${MPP_VERSION} 251 | OUTPUT_NAME ${MPP_BASENAME} 252 | ) 253 | 254 | # ################ UPDATE INCLUDE DIRECTORIES ################# 255 | target_include_directories(${PROJECT_NAME} PUBLIC 256 | $ 257 | $ 258 | $ 259 | ) 260 | 261 | message(STATUS "Used defines : ${MPP_DEFINES}") 262 | 263 | # ################ INSTALL LIBRARY ################# 264 | 265 | # Install targets 266 | install(TARGETS ${PROJECT_NAME} 267 | EXPORT ${PROJECT_NAME}-config 268 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 269 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 270 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 271 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 272 | ) 273 | 274 | # Install headers 275 | install(DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/include/ 276 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 277 | 278 | # Install Config 279 | set(CONFIG_INSTALL_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}) 280 | 281 | install(EXPORT ${PROJECT_NAME}-config 282 | NAMESPACE ${PROJECT_NAME}:: 283 | DESTINATION ${CONFIG_INSTALL_DIR} 284 | ) 285 | 286 | # Install version file 287 | include(CMakePackageConfigHelpers) 288 | set(VERSION_CONFIG_FILE ${CMAKE_CURRENT_BINARY_DIR}/cmake/${PROJECT_NAME}-config-version.cmake) 289 | 290 | write_basic_package_version_file(${VERSION_CONFIG_FILE} 291 | VERSION ${MPP_VERSION} 292 | COMPATIBILITY SameMajorVersion 293 | ) 294 | 295 | install(FILES ${VERSION_CONFIG_FILE} 296 | DESTINATION ${CONFIG_INSTALL_DIR} 297 | ) --------------------------------------------------------------------------------