diff --git a/.github/workflows/basic-ci.yml b/.github/workflows/basic-ci.yml index 255030ec..7cc67bb4 100644 --- a/.github/workflows/basic-ci.yml +++ b/.github/workflows/basic-ci.yml @@ -38,10 +38,10 @@ jobs: - name: Build TypeART run: | - cmake -B build -DTEST_CONFIG=ON -DENABLE_CODE_COVERAGE=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} + cmake -B build -DTEST_CONFIG=ON -DENABLE_CODE_COVERAGE=ON -DSOFTCOUNTERS=ON -DLLVM_EXTERNAL_LIT=${EXTERNAL_LIT} cmake --build build --parallel - - name: Test TypeART with coverage (exludes lulesh) + - name: Test TypeART with coverage run: | cmake --build build --target lcov-clean cmake --build build --target test -- ARGS=-VV @@ -51,7 +51,7 @@ jobs: - name: Build TypeART release run: | - cmake -B build_lulesh -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON + cmake -B build_lulesh -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON -DSOFTCOUNTERS=ON cmake --build build_lulesh --parallel - name: Test TypeART release on lulesh diff --git a/.github/workflows/ext-ci.yml b/.github/workflows/ext-ci.yml index a8fe977f..9c38cb3a 100644 --- a/.github/workflows/ext-ci.yml +++ b/.github/workflows/ext-ci.yml @@ -8,7 +8,7 @@ on: jobs: build-and-run-testbench: runs-on: ubuntu-20.04 - if: "!contains(github.event.head_commit.message, '[ci skip]') || !contains(github.event.head_commit.message, '[ci ext skip]')" + if: "!contains(github.event.head_commit.message, '[ci skip]') && !contains(github.event.head_commit.message, '[ci ext skip]')" steps: - uses: actions/checkout@v2 @@ -49,7 +49,7 @@ jobs: - name: Build & install TypeART run: | - cmake -B build -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON + cmake -B build -DCMAKE_BUILD_TYPE=Release -DMPI_INTERCEPT_LIB=ON -DSHOW_STATS=ON -DSOFTCOUNTERS=ON cmake --build build --parallel --target install echo "TYPEART_PATH=${GITHUB_WORKSPACE}/install/typeart" >> $GITHUB_ENV diff --git a/CMakeLists.txt b/CMakeLists.txt index 7a5cf920..f816ec72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.14) project(typeart - VERSION 1.5.0 + VERSION 1.5 ) set(TYPEART_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/cmake/ToolchainOptions.cmake b/cmake/ToolchainOptions.cmake index 9a34592e..f89bed90 100644 --- a/cmake/ToolchainOptions.cmake +++ b/cmake/ToolchainOptions.cmake @@ -12,6 +12,8 @@ option(SOFTCOUNTERS "Enable software tracking of #tracked addrs. / #distinct che option(TEST_CONFIG "Set logging levels to appropriate levels for test runner to succeed" OFF) option(ENABLE_CODE_COVERAGE "Enable code coverage statistics" OFF) option(ENABLE_LLVM_CODE_COVERAGE "Enable llvm-cov code coverage statistics" OFF) +option(TEST_CONFIGURE_IDE "Add targets so the IDE (e.g., Clion) can interpret test files better" ON) +mark_as_advanced(TEST_CONFIGURE_IDE) include(AddLLVM) include(llvm-lit) diff --git a/lib/passes/instrumentation/TypeARTFunctions.cpp b/lib/passes/instrumentation/TypeARTFunctions.cpp index 68fa06b3..76e9182e 100644 --- a/lib/passes/instrumentation/TypeARTFunctions.cpp +++ b/lib/passes/instrumentation/TypeARTFunctions.cpp @@ -4,11 +4,11 @@ #include "TypeARTFunctions.h" +#include "support/Logger.h" + #include "llvm/IR/Argument.h" -#include "llvm/IR/CFG.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" -#include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/Type.h" @@ -49,13 +49,17 @@ llvm::Function* TAFunctionDeclarator::make_function(IFunc id, llvm::StringRef ba // f->setLinkage(GlobalValue::ExternalWeakLinkage); }; const auto do_make = [&](auto& name, auto f_type) { - auto fc = m.getOrInsertFunction(name, f_type); -#if LLVM_VERSION >= 10 - auto f = dyn_cast(fc.getCallee()); -#else - auto f = dyn_cast(fc); -#endif - setFunctionLinkageExternal(f); + const bool has_f = m.getFunction(name) != nullptr; + auto fc = m.getOrInsertFunction(name, f_type); + + Function* f{nullptr}; + if (has_f) { + LOG_WARNING("Function " << name << " is already declared in the module.") + f = dyn_cast(fc.getCallee()->stripPointerCasts()); + } else { + f = dyn_cast(fc.getCallee()); + setFunctionLinkageExternal(f); + } addOptimizerAttributes(f); return f; }; diff --git a/lib/runtime/AccessCountPrinter.h b/lib/runtime/AccessCountPrinter.h new file mode 100644 index 00000000..0f85a614 --- /dev/null +++ b/lib/runtime/AccessCountPrinter.h @@ -0,0 +1,114 @@ +// +// Created by ahueck on 30.12.20. +// + +#ifndef TYPEART_ACCESSCOUNTPRINTER_H +#define TYPEART_ACCESSCOUNTPRINTER_H + +#include "AccessCounter.h" +#include "support/Logger.h" +#include "support/Table.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace typeart::softcounter { +namespace memory { +struct MemOverhead { + static constexpr auto pointerMapSize = sizeof(RuntimeT::PointerMap); // Map overhead + static constexpr auto perNodeSizeMap = + sizeof(std::remove_pointer::iterator::_Link_type>::type) + + sizeof(RuntimeT::MapEntry); // not applicable to btree + static constexpr auto stackVectorSize = sizeof(RuntimeT::Stack); // Stack overhead + static constexpr auto perNodeSizeStack = sizeof(RuntimeT::StackEntry); // Stack allocs + double stack{0}; + double map{0}; +}; +inline MemOverhead estimate(Counter stack_max, Counter heap_max, Counter global_max, const double scale = 1024.0) { + MemOverhead mem; + mem.stack = double(MemOverhead::stackVectorSize + + MemOverhead::perNodeSizeStack * std::max(RuntimeT::StackReserve, stack_max)) / + scale; + mem.map = + double(MemOverhead::pointerMapSize + MemOverhead::perNodeSizeMap * (stack_max + heap_max + global_max)) / scale; + return mem; +} +} // namespace memory + +template +void serialise(const Recorder& r, llvm::raw_ostream& buf) { + if constexpr (std::is_same_v) { + return; + } else { + const auto memory_use = memory::estimate(r.getMaxStackAllocs(), r.getMaxHeapAllocs(), r.getGlobalAllocs()); + + Table t("Alloc Stats from softcounters"); + t.wrap_length = true; + t.put(Row::make("Total heap", r.getHeapAllocs(), r.getHeapArray())); + t.put(Row::make("Total stack", r.getStackAllocs(), r.getStackArray())); + t.put(Row::make("Total global", r.getGlobalAllocs(), r.getGlobalArray())); + t.put(Row::make("Max. Heap Allocs", r.getMaxHeapAllocs())); + t.put(Row::make("Max. Stack Allocs", r.getMaxStackAllocs())); + t.put(Row::make("Addresses checked", r.getAddrChecked())); + t.put(Row::make("Distinct Addresses checked", r.getSeen().size())); + t.put(Row::make("Addresses re-used", r.getAddrReuses())); + t.put(Row::make("Addresses missed", r.getAddrMissing())); + t.put(Row::make("Distinct Addresses missed", r.getMissing().size())); + t.put(Row::make("Total free heap", r.getHeapAllocsFree(), r.getHeapArrayFree())); + t.put(Row::make("Total free stack", r.getStackAllocsFree(), r.getStackArrayFree())); + t.put(Row::make("Null/Zero/NullZero Addr", r.getNullAlloc(), r.getZeroAlloc(), r.getNullAndZeroAlloc())); + t.put(Row::make("User-def. types", r.getNumUDefTypes())); + t.put(Row::make("Estimated memory use (KiB)", size_t(std::round(memory_use.map + memory_use.stack)))); + t.put(Row::make("Bytes per node map/stack", memory::MemOverhead::perNodeSizeMap, + memory::MemOverhead::perNodeSizeStack)); + + t.print(buf); + + std::set type_id_set; + const auto fill_set = [&type_id_set](const auto& map) { + for (const auto& [key, val] : map) { + type_id_set.insert(key); + } + }; + fill_set(r.getHeapAlloc()); + fill_set(r.getGlobalAlloc()); + fill_set(r.getStackAlloc()); + fill_set(r.getHeapFree()); + fill_set(r.getStackFree()); + + const auto count = [](const auto& map, auto id) { + auto it = map.find(id); + if (it != map.end()) { + return it->second; + } + return 0ll; + }; + + Table type_table("Allocation type detail (heap, stack, global)"); + type_table.table_header = '#'; + for (auto type_id : type_id_set) { + type_table.put(Row::make(std::to_string(type_id), count(r.getHeapAlloc(), type_id), + count(r.getStackAlloc(), type_id), count(r.getGlobalAlloc(), type_id), + typeart_get_type_name(type_id))); + } + + type_table.print(buf); + + Table type_table_free("Free allocation type detail (heap, stack)"); + type_table_free.table_header = '#'; + for (auto type_id : type_id_set) { + type_table_free.put(Row::make(std::to_string(type_id), count(r.getHeapFree(), type_id), + count(r.getStackFree(), type_id), typeart_get_type_name(type_id))); + } + + type_table_free.print(buf); + } +} +} // namespace typeart::softcounter + +#endif // TYPEART_ACCESSCOUNTPRINTER_H diff --git a/lib/runtime/AccessCounter.h b/lib/runtime/AccessCounter.h index 68dca194..8cc927b4 100644 --- a/lib/runtime/AccessCounter.h +++ b/lib/runtime/AccessCounter.h @@ -7,8 +7,6 @@ #include "RuntimeData.h" #include "RuntimeInterface.h" -#include "support/Logger.h" -#include "support/Table.h" #include #include @@ -31,6 +29,13 @@ class AccessRecorder { inline void incHeapAlloc(int typeId, size_t count) { ++curHeapAllocs; + + // Always check here for max + // A program without free would otherwise never update maxHeap (see test 20_softcounter_max) + if (curHeapAllocs > maxHeapAllocs) { + maxHeapAllocs = curHeapAllocs; + } + ++heapAllocs; if (count > 1) { ++heapArray; @@ -72,9 +77,10 @@ class AccessRecorder { } inline void decHeapAlloc() { - if (curHeapAllocs > maxHeapAllocs) { - maxHeapAllocs = curHeapAllocs; - } + // Removed, since we already increment maxHeapAllocs just in time: + // if (curHeapAllocs > maxHeapAllocs) { + // maxHeapAllocs = curHeapAllocs; + // } --curHeapAllocs; } @@ -120,6 +126,91 @@ class AccessRecorder { return instance; } + Counter getHeapAllocs() const { + return heapAllocs; + } + Counter getStackAllocs() const { + return stackAllocs; + } + Counter getGlobalAllocs() const { + return globalAllocs; + } + Counter getMaxHeapAllocs() const { + return maxHeapAllocs; + } + Counter getMaxStackAllocs() const { + return maxStackAllocs; + } + Counter getCurHeapAllocs() const { + return curHeapAllocs; + } + Counter getCurStackAllocs() const { + return curStackAllocs; + } + Counter getAddrReuses() const { + return addrReuses; + } + Counter getAddrMissing() const { + return addrMissing; + } + Counter getAddrChecked() const { + return addrChecked; + } + Counter getStackArray() const { + return stackArray; + } + Counter getHeapArray() const { + return heapArray; + } + Counter getGlobalArray() const { + return globalArray; + } + Counter getStackAllocsFree() const { + return stackAllocsFree; + } + Counter getStackArrayFree() const { + return stackArrayFree; + } + Counter getHeapAllocsFree() const { + return heapAllocsFree; + } + Counter getHeapArrayFree() const { + return heapArrayFree; + } + Counter getNullAlloc() const { + return nullAlloc; + } + Counter getZeroAlloc() const { + return zeroAlloc; + } + Counter getNullAndZeroAlloc() const { + return nullAndZeroAlloc; + } + Counter getNumUDefTypes() const { + return numUDefTypes; + } + const std::unordered_set& getMissing() const { + return missing; + } + const std::unordered_set& getSeen() const { + return seen; + } + const TypeCountMap& getStackAlloc() const { + return stackAlloc; + } + const TypeCountMap& getHeapAlloc() const { + return heapAlloc; + } + const TypeCountMap& getGlobalAlloc() const { + return globalAlloc; + } + const TypeCountMap& getStackFree() const { + return stackFree; + } + const TypeCountMap& getHeapFree() const { + return heapFree; + } + private: AccessRecorder() = default; AccessRecorder(AccessRecorder& other) = default; @@ -153,9 +244,6 @@ class AccessRecorder { TypeCountMap globalAlloc; TypeCountMap stackFree; TypeCountMap heapFree; - - template - friend void serialise(const Recorder& r, llvm::raw_ostream& buf); }; /** @@ -198,97 +286,6 @@ class NoneRecorder { } }; -namespace memory { -struct MemOverhead { - static constexpr auto pointerMapSize = sizeof(RuntimeT::PointerMap); // Map overhead - static constexpr auto perNodeSizeMap = - sizeof(std::remove_pointer::iterator::_Link_type>::type) + - sizeof(RuntimeT::MapEntry); // not applicable to btree - static constexpr auto stackVectorSize = sizeof(RuntimeT::Stack); // Stack overhead - static constexpr auto perNodeSizeStack = sizeof(RuntimeT::StackEntry); // Stack allocs - double stack{0}; - double map{0}; -}; -inline MemOverhead estimate(Counter stack_max, Counter heap_max, Counter global_max, const double scale = 1024.0) { - MemOverhead mem; - mem.stack = double(MemOverhead::stackVectorSize + - MemOverhead::perNodeSizeStack * std::max(RuntimeT::StackReserve, stack_max)) / - scale; - mem.map = - double(MemOverhead::pointerMapSize + MemOverhead::perNodeSizeMap * (stack_max + heap_max + global_max)) / scale; - return mem; -} -} // namespace memory - -template -void serialise(const Recorder& r, llvm::raw_ostream& buf) { - if constexpr (std::is_same_v) { - return; - } else { - const auto memory_use = memory::estimate(r.maxStackAllocs, r.maxHeapAllocs, r.globalAllocs); - - Table t("Alloc Stats from softcounters"); - t.wrap_length = true; - t.put(Row::make("Total heap", r.heapAllocs, r.heapArray)); - t.put(Row::make("Total stack", r.stackAllocs, r.stackArray)); - t.put(Row::make("Total global", r.globalAllocs, r.globalArray)); - t.put(Row::make("Max. Heap Allocs", r.maxHeapAllocs)); - t.put(Row::make("Max. Stack Allocs", r.maxStackAllocs)); - t.put(Row::make("Addresses checked", r.addrChecked)); - t.put(Row::make("Distinct Addresses checked", r.seen.size())); - t.put(Row::make("Addresses re-used", r.addrReuses)); - t.put(Row::make("Addresses missed", r.addrMissing)); - t.put(Row::make("Distinct Addresses missed", r.missing.size())); - t.put(Row::make("Total free heap", r.heapAllocsFree, r.heapArrayFree)); - t.put(Row::make("Total free stack", r.stackAllocsFree, r.stackArrayFree)); - t.put(Row::make("Null/Zero/NullZero Addr", r.nullAlloc, r.zeroAlloc, r.nullAndZeroAlloc)); - t.put(Row::make("User-def. types", r.numUDefTypes)); - t.put(Row::make("Estimated memory use (KiB)", size_t(std::round(memory_use.map + memory_use.stack)))); - t.put(Row::make("Bytes per node map/stack", memory::MemOverhead::perNodeSizeMap, - memory::MemOverhead::perNodeSizeStack)); - - t.print(buf); - - std::set type_id_set; - const auto fill_set = [&type_id_set](const auto& map) { - for (const auto& [key, val] : map) { - type_id_set.insert(key); - } - }; - fill_set(r.heapAlloc); - fill_set(r.globalAlloc); - fill_set(r.stackAlloc); - fill_set(r.heapFree); - fill_set(r.stackFree); - - const auto count = [](const auto& map, auto id) { - auto it = map.find(id); - if (it != map.end()) { - return it->second; - } - return 0ll; - }; - - Table type_table("Allocation type detail (heap, stack, global)"); - type_table.table_header = '#'; - for (auto type_id : type_id_set) { - type_table.put(Row::make(std::to_string(type_id), count(r.heapAlloc, type_id), count(r.stackAlloc, type_id), - count(r.globalAlloc, type_id), typeart_get_type_name(type_id))); - } - - type_table.print(buf); - - Table type_table_free("Free allocation type detail (heap, stack)"); - type_table_free.table_header = '#'; - for (auto type_id : type_id_set) { - type_table_free.put(Row::make(std::to_string(type_id), count(r.heapFree, type_id), count(r.stackFree, type_id), - typeart_get_type_name(type_id))); - } - - type_table_free.print(buf); - } -} - } // namespace softcounter #if ENABLE_SOFTCOUNTER == 1 diff --git a/lib/runtime/CallbackInterface.h b/lib/runtime/CallbackInterface.h index 44f3a545..e514cebb 100644 --- a/lib/runtime/CallbackInterface.h +++ b/lib/runtime/CallbackInterface.h @@ -21,7 +21,7 @@ void __typeart_alloc_global(const void* addr, int typeId, size_t count); void __typeart_free(const void* addr); void __typeart_alloc_stack(const void* addr, int typeId, size_t count); -void __typeart_leave_scope(size_t alloca_count); +void __typeart_leave_scope(int alloca_count); #ifdef __cplusplus } diff --git a/lib/runtime/Runtime.cpp b/lib/runtime/Runtime.cpp index 5b853981..149f97d3 100644 --- a/lib/runtime/Runtime.cpp +++ b/lib/runtime/Runtime.cpp @@ -1,5 +1,6 @@ #include "Runtime.h" +#include "AccessCountPrinter.h" #include "AccessCounter.h" #include "RuntimeInterface.h" #include "support/Logger.h" @@ -534,7 +535,7 @@ void TypeArtRT::onFreeHeap(const void* addr, const void* retAddr) { } } -void TypeArtRT::onLeaveScope(size_t alloca_count, const void* retAddr) { +void TypeArtRT::onLeaveScope(int alloca_count, const void* retAddr) { if (unlikely(alloca_count > stackVars.size())) { LOG_ERROR("Stack is smaller than requested de-allocation count. alloca_count: " << alloca_count << ". size: " << stackVars.size()); @@ -580,7 +581,7 @@ void __typeart_free(const void* addr) { RUNTIME_GUARD_END; } -void __typeart_leave_scope(size_t alloca_count) { +void __typeart_leave_scope(int alloca_count) { RUNTIME_GUARD_BEGIN; const void* retAddr = __builtin_return_address(0); typeart::TypeArtRT::get().onLeaveScope(alloca_count, retAddr); diff --git a/lib/runtime/Runtime.h b/lib/runtime/Runtime.h index 6fa4444c..76ee3e3d 100644 --- a/lib/runtime/Runtime.h +++ b/lib/runtime/Runtime.h @@ -162,7 +162,7 @@ class TypeArtRT final { void onFreeHeap(const void* addr, const void* retAddr); - void onLeaveScope(size_t alloca_count, const void* retAddr); + void onLeaveScope(int alloca_count, const void* retAddr); private: TypeArtRT(); diff --git a/lib/runtime/RuntimeData.h b/lib/runtime/RuntimeData.h index e59fd482..09943e77 100644 --- a/lib/runtime/RuntimeData.h +++ b/lib/runtime/RuntimeData.h @@ -23,6 +23,8 @@ #include #endif +#include + namespace typeart { using MemAddr = const void*; diff --git a/scripts/apply.sh.in b/scripts/apply.sh.in index db0be282..8efdeb76 100644 --- a/scripts/apply.sh.in +++ b/scripts/apply.sh.in @@ -1,8 +1,29 @@ #!/bin/bash target=$1 -ta_more_args=${2:-""} -optimize=${3:-" "} +optimize="" +ta_more_args="" + +shift # skip over $1 +while (( "$#" )); do + case "$1" in + -o|--optimization) + if [ -n "$2" ] && [ ${2:0:2} == "-O" ]; then + optimize=$2 + shift 2 + else + echo "Error: Optimization argument for $1 is erroneous: $2" >&2 + exit 1 + fi + ;; + *) # preserve other arguments + ta_more_args="$ta_more_args $1" + shift + ;; + esac +done +# set other positional arguments in their proper place +eval set -- "ta_more_args" extension="${target##*.}" tmpfile=${target##*/} @@ -24,12 +45,12 @@ function make_no_optim() { function make_with_optim() { # Order: heap, optimize, alloca with additional args.. - $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats | opt $optimize -S | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args -S 2>&1 + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats -S | opt $optimize -S | opt $typeart_plugin -typeart-no-heap -typeart-alloca -typeart-stats $ta_more_args -S 2>&1 } function compile() { local typeart_plugin="-load @TYPEART_ANALYSIS_PASS_DIR@/@TYPEART_ANALYSIS_PLUGIN@ -load @TYPEART_PASS_DIR@/@TYPEART_PLUGIN@ -typeart" - if [ "$optimize" == " " ]; then + if [ "$optimize" == "" ]; then make_no_optim else make_with_optim diff --git a/scripts/run.sh.in b/scripts/run.sh.in index 718e5a8a..db0dc0aa 100644 --- a/scripts/run.sh.in +++ b/scripts/run.sh.in @@ -1,8 +1,29 @@ #!/bin/bash target=$1 -ta_more_args=${2:-""} -optimize=${3:-" "} +optimize="" +ta_more_args="" + +shift # skip over $1 +while (( "$#" )); do + case "$1" in + -o|--optimization) + if [ -n "$2" ] && [ ${2:0:2} == "-O" ]; then + optimize=$2 + shift 2 + else + echo "Error: Optimization argument for $1 is erroneous: $2" >&2 + exit 1 + fi + ;; + *) # preserve other arguments + ta_more_args="$ta_more_args $1" + shift + ;; + esac +done +# set other positional arguments in their proper place +eval set -- "ta_more_args" extension="${target##*.}" tmpfile=${target##*/} @@ -24,7 +45,7 @@ function make_no_optim() { function make_with_optim() { # Order: heap, optimize, alloca with additional args.. - $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats | opt $optimize -S | opt $typeart_plugin -typeart-alloca -typeart-stats $ta_more_args | llc -x=ir -filetype=obj -o "$tmpfile".o + $compiler @TYPEART_INCLUDE_DIRS@ -O1 -Xclang -disable-llvm-passes -S -emit-llvm "$target" -o - | opt $typeart_plugin -typeart-stats $ta_more_args | opt $optimize -S | opt $typeart_plugin -typeart-no-heap -typeart-alloca -typeart-stats $ta_more_args | llc -x=ir -filetype=obj -o "$tmpfile".o } function compile() { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 07a7e825..191b606f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,6 +11,12 @@ function(configure_typeart_lit_site_cfg input output) set(TYPEARTPASS_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib/passes) set(TYPEARTPASS_RT_DIR ${CMAKE_BINARY_DIR}/lib/runtime) set(TYPEARTPASS_PROFILE_FILE ${TYPEART_PROFILE_DIR}/lit-code-%p.profraw) + if(SOFTCOUNTERS) + set(TYPEARTPASS_SOFTCOUNTER True) + else() + set(TYPEARTPASS_SOFTCOUNTER False) + endif() + set(LIT_SITE_CFG_IN_HEADER "## Autogenerated from ${input}\n## Do not edit!") configure_file(${input} ${output} @ONLY) @@ -44,3 +50,33 @@ add_test( if(MPI_INTERCEPT_LIB) add_subdirectory(lulesh) endif() + +if(TEST_CONFIGURE_IDE) + function(add_test_target_ide target header sources) + add_executable(${target} + EXCLUDE_FROM_ALL + ${header} + ${sources} + ) + + add_dependencies(${target} + ${TYPEARTPASS_TEST_DEPENDS} + ) + + target_include_directories(${target} + PRIVATE + ${PROJECT_SOURCE_DIR}/lib/typelib + ${PROJECT_SOURCE_DIR}/lib/runtime + ${PROJECT_SOURCE_DIR} + ) + endfunction() + + file(GLOB_RECURSE TYPEART_CXX_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) + file(GLOB_RECURSE TYPEART_CC_TESTS ${CMAKE_CURRENT_SOURCE_DIR}/*.c) + file(GLOB_RECURSE TYPEART_TEST_HEADER ${CMAKE_CURRENT_SOURCE_DIR}/*.h) + + add_test_target_ide(typeart_cxx_test_objects "${TYPEART_TEST_HEADER}" "${TYPEART_CXX_TESTS}") + set_target_properties(typeart_cxx_test_objects PROPERTIES LINKER_LANGUAGE CXX) + add_test_target_ide(typeart_cc_test_objects "${TYPEART_TEST_HEADER}" "${TYPEART_CC_TESTS}") + set_target_properties(typeart_cc_test_objects PROPERTIES LINKER_LANGUAGE C) +endif() diff --git a/test/lit.cfg b/test/lit.cfg index e57d8875..be659637 100644 --- a/test/lit.cfg +++ b/test/lit.cfg @@ -21,6 +21,9 @@ config.test_format = lit.formats.ShTest(execute_external=True) config.suffixes = ['.c','.cpp', '.llin'] config.excludes = ['Inputs', 'mpi_interceptor', 'lulesh'] +if config.softcounter_used: + config.available_features.add('softcounter') + profile_files = getattr(config, 'profile_file', None) typeart_lib_root = getattr(config, 'typeartpass_lib_dir', None) typeart_rt_root = getattr(config, 'typeartpass_rt_dir', None) diff --git a/test/lit.site.cfg.in b/test/lit.site.cfg.in index 76ce0757..1329e18b 100644 --- a/test/lit.site.cfg.in +++ b/test/lit.site.cfg.in @@ -7,6 +7,8 @@ config.typeartpass_lib_dir = "@TYPEARTPASS_LIBRARY_DIR@" config.typeartpass_rt_dir = "@TYPEARTPASS_RT_DIR@" config.typeartpass_script_dir = "@TYPEARTPASS_SCRIPT_DIR@" config.profile_file = "@TYPEARTPASS_PROFILE_FILE@" +config.softcounter_used = @TYPEARTPASS_SOFTCOUNTER@ + # Let the main config do the real work. config.loaded_site_config = True diff --git a/test/pass/misc/03_make_mismatch_callback.c b/test/pass/misc/03_make_mismatch_callback.c new file mode 100644 index 00000000..1aa7974a --- /dev/null +++ b/test/pass/misc/03_make_mismatch_callback.c @@ -0,0 +1,10 @@ +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s +// XFAIL: * + +#include +void __typeart_leave_scope(size_t alloca_count); + +int main(void) { + __typeart_leave_scope(0); + return 0; +} \ No newline at end of file diff --git a/test/pass/misc/04_make_callback_match.c b/test/pass/misc/04_make_callback_match.c new file mode 100644 index 00000000..a0a98362 --- /dev/null +++ b/test/pass/misc/04_make_callback_match.c @@ -0,0 +1,13 @@ +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s + +void __typeart_leave_scope(int alloca_count); + +int main(void) { + __typeart_leave_scope(0); + return 0; +} + +// CHECK: TypeArtPass [Heap & Stack] +// CHECK-NEXT: Malloc : 0 +// CHECK-NEXT: Free : 0 +// CHECK-NEXT: Alloca : 1 diff --git a/test/pass/misc/05_make_all_callbacks.c b/test/pass/misc/05_make_all_callbacks.c new file mode 100644 index 00000000..f3b606c3 --- /dev/null +++ b/test/pass/misc/05_make_all_callbacks.c @@ -0,0 +1,18 @@ +// RUN: %c-to-llvm %s | %apply-typeart -typeart-alloca -S 2>&1 | FileCheck %s + +#include "../../../lib/runtime/CallbackInterface.h" + +int main(void) { + int count = 0; + int type_id = 10; + size_t extent = 0; + void* addr = NULL; + __typeart_alloc(addr, type_id, extent); + __typeart_alloc_global(addr, type_id, extent); + __typeart_alloc_stack(addr, type_id, extent); + __typeart_free(addr); + __typeart_leave_scope(count); + return 0; +} + +// CHECK: TypeArtPass [Heap & Stack] \ No newline at end of file diff --git a/test/runtime/18_softcounter_empty.c b/test/runtime/18_softcounter_empty.c new file mode 100644 index 00000000..18574c59 --- /dev/null +++ b/test/runtime/18_softcounter_empty.c @@ -0,0 +1,31 @@ +// RUN: %run %s -o -O3 2>&1 | FileCheck %s +// REQUIRES: softcounter + +void __typeart_leave_scope(int alloca_count); + +int main(void) { + __typeart_leave_scope(0); // This simply "triggers" the runtime + return 0; +} + +// CHECK: Alloc Stats from softcounters +// CHECK-NEXT: Total heap : 0 , 0 , - +// CHECK-NEXT: Total stack : 0 , 0 , - +// CHECK-NEXT: Total global : 0 , 0 , - +// CHECK-NEXT: Max. Heap Allocs : 0 , - , - +// CHECK-NEXT: Max. Stack Allocs : 0 , - , - +// CHECK-NEXT: Addresses checked : 0 , - , - +// CHECK-NEXT: Distinct Addresses checked : 0 , - , - +// CHECK-NEXT: Addresses re-used : 0 , - , - +// CHECK-NEXT: Addresses missed : 0 , - , - +// CHECK-NEXT: Distinct Addresses missed : 0 , - , - +// CHECK-NEXT: Total free heap : 0 , 0 , - +// CHECK-NEXT: Total free stack : 0 , 0 , - +// CHECK-NEXT: Null/Zero/NullZero Addr : 0 , 0 , 0 +// CHECK-NEXT: User-def. types : 0 , - , - +// CHECK-NEXT: Estimated memory use (KiB) : 4 , - , - +// CHECK-NEXT: Bytes per node map/stack : 96 , 8 , - +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Allocation type detail (heap, stack, global) +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Free allocation type detail (heap, stack) \ No newline at end of file diff --git a/test/runtime/19_softcounter.c b/test/runtime/19_softcounter.c new file mode 100644 index 00000000..d5a204db --- /dev/null +++ b/test/runtime/19_softcounter.c @@ -0,0 +1,38 @@ +// RUN: %run %s -o -O3 2>&1 | FileCheck %s +// REQUIRES: softcounter + +#include + +int main(void) { + for (int i = 1; i <= 5; ++i) { + // 5 heap alloc and free: one single double, and then arrays + // max heap (concurrently) 1 + double* d = (double*)malloc(i * sizeof(double)); + free(d); + } + return 0; +} + +// CHECK: Alloc Stats from softcounters +// CHECK-NEXT: Total heap : 5 , 4 , - +// CHECK-NEXT: Total stack : 0 , 0 , - +// CHECK-NEXT: Total global : 0 , 0 , - +// CHECK-NEXT: Max. Heap Allocs : 1 , - , - +// CHECK-NEXT: Max. Stack Allocs : 0 , - , - +// CHECK-NEXT: Addresses checked : 0 , - , - +// CHECK-NEXT: Distinct Addresses checked : 0 , - , - +// CHECK-NEXT: Addresses re-used : 0 , - , - +// CHECK-NEXT: Addresses missed : 0 , - , - +// CHECK-NEXT: Distinct Addresses missed : 0 , - , - +// CHECK-NEXT: Total free heap : 5 , 4 , - +// CHECK-NEXT: Total free stack : 0 , 0 , - +// CHECK-NEXT: Null/Zero/NullZero Addr : 0 , 0 , 0 +// CHECK-NEXT: User-def. types : 0 , - , - +// CHECK-NEXT: Estimated memory use (KiB) : 4 , - , - +// CHECK-NEXT: Bytes per node map/stack : 96 , 8 , - +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Allocation type detail (heap, stack, global) +// CHECK-NEXT: 6 : 5 , 0 , 0 , double +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Free allocation type detail (heap, stack) +// CHECK-NEXT: 6 : 5 , 0 , double \ No newline at end of file diff --git a/test/runtime/20_softcounter_max.c b/test/runtime/20_softcounter_max.c new file mode 100644 index 00000000..382a8765 --- /dev/null +++ b/test/runtime/20_softcounter_max.c @@ -0,0 +1,38 @@ +// RUN: %run %s -o -O1 2>&1 | FileCheck %s +// REQUIRES: softcounter + +#include + +int main(void) { + for (int i = 1; i <= 6; ++i) { + // 6 heap alloc + // max heap (concurrently) 6 + double* d = (double*)malloc(sizeof(double)); + } + + return 0; +} + +// CHECK: Alloc Stats from softcounters +// CHECK-NEXT: Total heap : 6 , 0 , - +// CHECK-NEXT: Total stack : 0 , 0 , - +// CHECK-NEXT: Total global : 0 , 0 , - +// CHECK-NEXT: Max. Heap Allocs : 6 , - , - +// CHECK-NEXT: Max. Stack Allocs : 0 , - , - +// CHECK-NEXT: Addresses checked : 0 , - , - +// CHECK-NEXT: Distinct Addresses checked : 0 , - , - +// CHECK-NEXT: Addresses re-used : 0 , - , - +// CHECK-NEXT: Addresses missed : 0 , - , - +// CHECK-NEXT: Distinct Addresses missed : 0 , - , - +// CHECK-NEXT: Total free heap : 0 , 0 , - +// CHECK-NEXT: Total free stack : 0 , 0 , - +// CHECK-NEXT: Null/Zero/NullZero Addr : 0 , 0 , 0 +// CHECK-NEXT: User-def. types : 0 , - , - +// CHECK-NEXT: Estimated memory use (KiB) : {{[4-9]}} , - , - +// CHECK-NEXT: Bytes per node map/stack : 96 , 8 , - +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Allocation type detail (heap, stack, global) +// CHECK-NEXT: 6 : 6 , 0 , 0 , double +// CHECK-NEXT: {{(#|-)+}} +// CHECK-NEXT: Free allocation type detail (heap, stack) +// CHECK-NEXT: 6 : 0 , 0 , double \ No newline at end of file diff --git a/test/runtime/21_runtime_manual.cpp b/test/runtime/21_runtime_manual.cpp new file mode 100644 index 00000000..6fda999a --- /dev/null +++ b/test/runtime/21_runtime_manual.cpp @@ -0,0 +1,181 @@ +// RUN: clang++ -std=c++17 -I%S/../../ -I%S/../../lib/typelib -I%S/../../lib %s -o %s.exe +// RUN: %s.exe 2>&1 | FileCheck %s + +// FIXME this test doesn't add to the coverage data. + +#define ENABLE_SOFTCOUNTER 1 +#include "lib/runtime/AccessCounter.h" + +#include +#include + +using namespace typeart; + +#define o_(getter) std::cerr << recorder.getter << '\n' + +std::vector> sorted_v(const std::unordered_map& map) { + std::vector> sorted_elements(map.begin(), map.end()); + std::sort(sorted_elements.begin(), sorted_elements.end()); + return sorted_elements; +} + +std::vector sorted_v(const std::unordered_set& set) { + std::vector sorted_elements(set.begin(), set.end()); + std::sort(sorted_elements.begin(), sorted_elements.end()); + return sorted_elements; +} + +void test_heap(softcounter::AccessRecorder& recorder) { + recorder.incHeapAlloc(10, 1); + recorder.incHeapAlloc(10, 1); + + // CHECK: 2 + o_(getCurHeapAllocs()); + // CHECK: 2 + o_(getMaxHeapAllocs()); + + auto hallocs = sorted_v(recorder.getHeapAlloc()); + // CHECK: 1 + std::cerr << hallocs.size() << '\n'; + // CHECK: 10 2 + for (const auto& [id, count] : hallocs) { + std::cerr << id << " " << count << '\n'; + } + + recorder.decHeapAlloc(); + recorder.decHeapAlloc(); + // CHECK: 0 + o_(getCurHeapAllocs()); + // CHECK: 2 + o_(getMaxHeapAllocs()); + + recorder.decHeapAlloc(); + recorder.decHeapAlloc(); + // CHECK: -2 + o_(getCurHeapAllocs()); + // CHECK: 2 + o_(getMaxHeapAllocs()); +} + +void test_stack(softcounter::AccessRecorder& recorder) { + recorder.incStackAlloc(0, 1); + // CHECK: 1 + o_(getStackAllocs()); + // CHECK: 0 + o_(getMaxStackAllocs()); + + recorder.incStackAlloc(1, 1); + // CHECK: 2 + o_(getStackAllocs()); + // CHECK: 2 + o_(getCurStackAllocs()); + // CHECK: 0 + o_(getMaxStackAllocs()); + + recorder.decStackAlloc(2); + // CHECK: 2 + o_(getStackAllocs()); + // CHECK: 2 + o_(getMaxStackAllocs()); + + auto sallocs = sorted_v(recorder.getStackAlloc()); + // CHECK: 2 + std::cerr << sallocs.size() << '\n'; + // CHECK: 0 1 + // CHECK: 1 1 + for (const auto& [id, count] : sallocs) { + std::cerr << id << " " << count << '\n'; + } + + auto de_sallocs = sorted_v(recorder.getStackFree()); + // CHECK: 0 + std::cerr << de_sallocs.size() << '\n'; + + recorder.incStackFree(0, 1); + recorder.incStackFree(1, 1); + de_sallocs = sorted_v(recorder.getStackFree()); + // CHECK: 2 + std::cerr << de_sallocs.size() << '\n'; + // CHECK: 0 1 + // CHECK: 1 1 + for (const auto& [id, count] : de_sallocs) { + std::cerr << id << " " << count << '\n'; + } + + recorder.incStackAlloc(6, 1); + recorder.incStackFree(6, 1); + de_sallocs = sorted_v(recorder.getStackFree()); + // CHECK: 3 + std::cerr << de_sallocs.size() << '\n'; + // CHECK: 0 1 + // CHECK: 1 1 + // CHECK: 6 1 + for (const auto& [id, count] : de_sallocs) { + std::cerr << id << " " << count << '\n'; + } +} + +void test_global(softcounter::AccessRecorder& recorder) { + recorder.incGlobalAlloc(6, 1); + // CHECK: 1 + o_(getGlobalAllocs()); + + const auto& alloc = recorder.getGlobalAlloc(); + // CHECK: 1 + std::cerr << alloc.size() << '\n'; + // CHECK: 6 1 + for (const auto& [id, count] : alloc) { + std::cerr << id << " " << count << '\n'; + } +} + +int main() { + softcounter::AccessRecorder& recorder = softcounter::AccessRecorder::get(); + + test_heap(recorder); + test_stack(recorder); + test_global(recorder); + + recorder.incUDefTypes(2); + // CHECK: 2 + o_(getNumUDefTypes()); + + void* a1 = (void*)0x1; + void* a2 = (void*)0x2; + void* a3 = (void*)0x3; + recorder.incAddrMissing(a1); + // CHECK: 1 + o_(getAddrMissing()); + recorder.incAddrMissing(a2); + recorder.incAddrMissing(a3); + // CHECK: 3 + o_(getAddrMissing()); + recorder.incAddrMissing(a3); + // CHECK: 4 + o_(getAddrMissing()); + + auto mset = sorted_v(recorder.getMissing()); + // CHECK: 3 + std::cerr << mset.size() << '\n'; + // CHECK: 0x1 + // CHECK: 0x2 + // CHECK: 0x3 + for (auto& a : mset) { + std::cerr << a << '\n'; + } + + recorder.incUsedInRequest(a1); + recorder.incUsedInRequest(a3); + // CHECK: 2 + o_(getAddrChecked()); + + auto cset = sorted_v(recorder.getSeen()); + // CHECK: 2 + std::cerr << cset.size() << '\n'; + // CHECK: 0x1 + // CHECK: 0x3 + for (auto& a : cset) { + std::cerr << a << '\n'; + } + return 0; +}