Files
rocm-systems/source/lib/tests/buffering/buffering-serial.cpp
T
Jonathan R. Madsen b12ef4a75e Buffering: initial implementation and tests (#20)
* Update source/lib/common

- CMakeLists.txt
  - less verbose
  - rocprofiler-common-library uses rocprofiler-headers target
- mpl.hpp
  - metaprogramming header with type_list, size_of, index_of, and is_one_of
- record_header_buffer.{hpp,cpp}
  - wrapper class around atomic_ring_buffer and vector of rocprofiler_record_header_t
- atomic_ring_buffer.{hpp,cpp}
  - request function accepts wrap param when overwritting is not desirable
  - can_clear member function
  - clear member function for rewinding write pointer to start of buffer
- containers/CMakeLists.txt
  - include record_header_buffer.{hpp,cpp} in build target

* Update source/lib/tests: Buffering tests

- Added buffering tests. See comments in code for description

* atomic_ring_buffer -> ring_buffer

- remove ring_buffer implementation
- rename atomic_ring_buffer to ring_buffer

* atomic_ring_buffer -> ring_buffer

- remove ring_buffer implementation
- rename atomic_ring_buffer to ring_buffer

* Update record_header_buffer

- lock, unlock, is_locked, clear, save, and load member functions

* Buffering tests

- add buffer test for save/load capability

* Update rocprofiler_memcheck.cmake

- fix erroneous spaces causing incorrect string evaluation

* Update ring_buffer

- fix exception message

* undef HIP_PROF_API

- make sure HIP_PROF_API is undefined before including hip_runtime.h
- avoid directly including hip/hip_runtime.h

* Update rocprofiler_config_interfaces

- remove stale preprocessor defines that are from old rocprofiler/roctracer
  - HIP_PROF_HIP_API_STRING=1
  - PROF_API_IMPL=1

* Update run-ci.py

- fix paths to suppression files
- improve printing logs to console in github actions

* Update buffering implementation

- remove support for using malloc instead of mmap in ring_buffer
- provide some info functions in record_header_buffer
- improve the testing of the save-load buffer test

* Update run-ci.py

- fix CTEST_CUSTOM_COVERAGE_EXCLUDE

* Update hip/api_args.h

- remove undef HIP_PROF_API

* Update buffering-save-load.cpp

- updated comments

* Update record_header_buffer

- default ctor
- allocate member function
- is_allocated member function

* Update buffering-save-load.cpp

- tweaked usage of record_header_buffer to delay allocation
2023-08-30 11:34:03 -05:00

173 wiersze
6.2 KiB
C++

// MIT License
//
// Copyright (c) 2023 ROCm Developer Tools
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
#include "buffering.hpp"
#include "lib/common/container/record_header_buffer.hpp"
#include <gtest/gtest.h>
#include <pthread.h>
#include <cstdint>
#include <cstdlib>
#include <random>
#include <typeinfo>
namespace
{
namespace test = ::rocprofiler::test;
using uint_raw_array_t = test::raw_array<uint64_t, 32>;
using flt_raw_array_t = test::raw_array<double, 64>;
using record_header_buffer_t = rocprofiler::common::container::record_header_buffer;
// generates an array with random data
template <typename Tp, size_t N>
auto
generate_array(Tp _low = 0UL, Tp _high = 1000UL)
{
auto _v = test::raw_array<Tp, N>{};
test::generate(_v, _low, _high);
return _v;
}
// pulls out a raw array of the given type and puts back into a vector
template <typename Tp>
void
extract_header(std::vector<Tp>& _arr, rocprofiler_record_header_t* _hdr)
{
if(_hdr->kind == typeid(Tp).hash_code())
{
auto* _v = reinterpret_cast<Tp*>(_hdr->payload);
_arr.emplace_back(*_v);
}
else
{
GTEST_FAIL() << __PRETTY_FUNCTION__ << " failed";
}
}
} // namespace
TEST(buffering, serial)
{
// this test verifies that the buffering system is ordered properly
// and does not suffer from data loss or data corruption. We generate
// 240 raw arrays of data where 120 of them are twice as large as the
// the other 120 raw array and these two arrays contain data of different
// types. For each iteration, we randomize whether the uint64_t array with
// 32 elements or whether the double array with 64 elements gets inserted
// first. We then pull all the data back out of the buffer and verify
// that no arrays were lost and that none of the data was corrupted.
uint64_t n = 120;
// storage of the original data put into the buffer
auto _ui_history = std::vector<uint_raw_array_t>{};
auto _ui_result = std::vector<uint_raw_array_t>{};
// storage of the data extracted from the buffer
auto _fp_history = std::vector<flt_raw_array_t>{};
auto _fp_result = std::vector<flt_raw_array_t>{};
// a buffer to hold all the data
auto _buffer = record_header_buffer_t{n * (sizeof(uint_raw_array_t) + sizeof(flt_raw_array_t))};
// RNG use to make the ordering of the different sized records inconsistent
auto _gen = std::mt19937_64{std::random_device{}()};
auto _rng = std::uniform_int_distribution<short>{0, 1};
for(uint64_t i = 0; i < n; ++i)
{
// generate a 32*8 byte array
auto _u = generate_array<uint64_t, 32>();
// generate a 64*8 byte array
auto _f = generate_array<double, 64>();
// store the original data
_ui_history.emplace_back(_u);
_fp_history.emplace_back(_f);
EXPECT_EQ(_u, _ui_history.back()) << "uint not equal after emplace_back";
EXPECT_EQ(_f, _fp_history.back()) << "float not equal after emplace_back";
// randomize sequence of insertion into buffer
if(_rng(_gen) % 2 == 0)
{
_buffer.emplace(_u);
_buffer.emplace(_f);
}
else
{
_buffer.emplace(_f);
_buffer.emplace(_u);
}
EXPECT_EQ(_u, _ui_history.back()) << "uint not equal after emplace_back";
EXPECT_EQ(_f, _fp_history.back()) << "float not equal after emplace_back";
}
// get the records out of the buffer
auto _headers = _buffer.get_record_headers();
for(auto* itr : _headers)
{
ASSERT_TRUE(itr->payload) << "nullptr to payload not expected";
if(itr->kind == typeid(uint_raw_array_t).hash_code())
{
extract_header(_ui_result, itr);
}
else if(itr->kind == typeid(flt_raw_array_t).hash_code())
{
extract_header(_fp_result, itr);
}
else
{
GTEST_FAIL() << "unknown type id hash code: " << std::to_string(itr->kind);
}
}
// validate that we got the same number of records out that we put in
ASSERT_EQ(_ui_history.size(), _ui_result.size())
<< "UINT: " << _ui_history.size() << " vs. " << _ui_result.size();
ASSERT_EQ(_fp_history.size(), _fp_result.size())
<< "FLOAT: " << _fp_history.size() << " vs. " << _fp_result.size();
// validate there was no data corruption or data loss from storage in the buffer
for(size_t i = 0; i < n; ++i)
{
auto& _ui_lhs = _ui_history.at(i);
auto& _ui_rhs = _ui_result.at(i);
auto& _fp_lhs = _fp_history.at(i);
auto& _fp_rhs = _fp_result.at(i);
EXPECT_EQ(_ui_lhs, _ui_rhs) << "\n"
<< "UINT LHS:\n"
<< _ui_lhs.to_string() << "\n"
<< "UINT RHS:\n"
<< _ui_rhs.to_string() << "\n";
EXPECT_EQ(_fp_lhs, _fp_rhs) << "\n"
<< "FLOAT LHS:\n"
<< _fp_lhs.to_string() << "\n"
<< "FLOAT RHS:\n"
<< _fp_rhs.to_string() << "\n";
}
}