ci(amd64/clang/lsan): fix leak sanitizer errors (#57)
Some checks failed
continuous-integration/drone/push Build is failing

Reviewed-on: #57
Co-authored-by: light7734 <light7734@tuta.io>
Co-committed-by: light7734 <light7734@tuta.io>
This commit is contained in:
light7734 2025-10-09 14:08:14 +00:00 committed by light7734
parent 80a3afbb75
commit 847ad7dd74
15 changed files with 273 additions and 199 deletions

View file

@ -1,139 +1,139 @@
---
# kind: pipeline
# type: exec
# name: amd64 — msvc
# trigger:
# branch:
# - main
# platform:
# os: windows
# arch: amd64
#
# steps:
# - name: unit tests
# shell: powershell
# commands:
# - ./tools/ci/amd64/msvc/unit_tests.ps1
#
# ---
kind: pipeline
type: exec
name: amd64 — msvc
trigger:
branch:
- main
platform:
os: windows
arch: amd64
steps:
- name: unit tests
shell: powershell
commands:
- ./tools/ci/amd64/msvc/unit_tests.ps1
---
kind: pipeline
type: docker
name: amd64 — gcc
trigger:
branch:
- main
#
steps:
# - name: unit tests
# image: ci:latest
# pull: if-not-exists
# commands:
# - ./tools/ci/amd64/gcc/unit_tests.sh
#
- name: unit tests
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/unit_tests.sh
- name: valgrind
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/gcc/valgrind.sh
#
# ---
# kind: pipeline
# type: docker
# name: amd64 — clang
# trigger:
# branch:
# - main
#
# steps:
# - name: code coverage
# image: ci:latest
# pull: if-not-exists
# environment:
# CODECOV_TOKEN:
# from_secret: CODECOV_TOKEN
# commands:
# - ./tools/ci/amd64/clang/coverage.sh
#
# - name: leak sanitizer
# image: ci:latest
# pull: if-not-exists
# commands:
# - ./tools/ci/amd64/clang/lsan.sh
#
# - name: memory sanitizer
# image: ci:latest
# pull: if-not-exists
# commands:
# - ./tools/ci/amd64/clang/msan.sh
#
# ---
# kind: pipeline
# type: docker
# name: static analysis
# trigger:
# branch:
# - main
#
# steps:
# - name: clang tidy
# image: ci:latest
# pull: if-not-exists
# privileged: true
# commands:
# - ./tools/ci/static_analysis/clang_tidy.sh
#
# - name: clang format
# image: ci:latest
# pull: if-not-exists
# commands:
# - ./tools/ci/static_analysis/clang_format.sh
#
# ---
# kind: pipeline
# type: docker
# name: documentation — development
# node:
# environment: ryali
# trigger:
# branch:
# - main
#
# steps:
# - name: build and deploy
# image: documentation:latest
# pull: if-not-exists
# commands:
# - pwd
# - cd docs
# - mkdir generated
# - touch generated/changelogs.rst
# - touch generated/api.rst
# - sphinx-build -M html . .
#
# - rm -rf /light_docs_dev/*
# - mv ./html/* /light_docs_dev/
#
# ---
#
# kind: pipeline
# type: docker
# name: documentation — production
# node:
# environment: ryali
# trigger:
# event:
# - tag
#
# steps:
# - name: build and deploy
# image: documentation:latest
# pull: if-not-exists
# commands:
# - cd docs
# - mkdir generated
# - touch generated/changelogs.rst
# - touch generated/api.rst
# - sphinx-build -M html . .
#
# - rm -rf /light_docs/*
# - mv ./html/* /light_docs/
---
kind: pipeline
type: docker
name: amd64 — clang
trigger:
branch:
- main
steps:
- name: code coverage
image: ci:latest
pull: if-not-exists
environment:
CODECOV_TOKEN:
from_secret: CODECOV_TOKEN
commands:
- ./tools/ci/amd64/clang/coverage.sh
- name: leak sanitizer
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/lsan.sh
- name: memory sanitizer
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/amd64/clang/msan.sh
---
kind: pipeline
type: docker
name: static analysis
trigger:
branch:
- main
steps:
- name: clang tidy
image: ci:latest
pull: if-not-exists
privileged: true
commands:
- ./tools/ci/static_analysis/clang_tidy.sh
- name: clang format
image: ci:latest
pull: if-not-exists
commands:
- ./tools/ci/static_analysis/clang_format.sh
---
kind: pipeline
type: docker
name: documentation — development
node:
environment: ryali
trigger:
branch:
- main
steps:
- name: build and deploy
image: documentation:latest
pull: if-not-exists
commands:
- pwd
- cd docs
- mkdir generated
- touch generated/changelogs.rst
- touch generated/api.rst
- sphinx-build -M html . .
- rm -rf /light_docs_dev/*
- mv ./html/* /light_docs_dev/
---
kind: pipeline
type: docker
name: documentation — production
node:
environment: ryali
trigger:
event:
- tag
steps:
- name: build and deploy
image: documentation:latest
pull: if-not-exists
commands:
- cd docs
- mkdir generated
- touch generated/changelogs.rst
- touch generated/api.rst
- sphinx-build -M html . .
- rm -rf /light_docs/*
- mv ./html/* /light_docs/

View file

@ -40,8 +40,16 @@ Device::~Device()
return;
}
vkc(vk_device_wait_idle(m_device));
vk_destroy_device(m_device, nullptr);
try
{
vkc(vk_device_wait_idle(m_device));
vk_destroy_device(m_device, nullptr);
}
catch (const std::exception &exp)
{
log_err("Failed to destroy vk device:");
log_err("\twhat: {}", exp.what());
}
}
void Device::initialize_logical_device()

View file

@ -1,6 +1,7 @@
#include <app/system.hpp>
#include <renderer/backend/vk/context/instance.hpp>
#include <renderer/backend/vk/utils.hpp>
#include <vulkan/vulkan_core.h>
#if defined(_WIN32)
#error "Unsupported platform (currently)"
@ -194,7 +195,7 @@ void Instance::initialize_instance()
const VkLayerSettingsCreateInfoEXT layer_settings_create_info = {
.sType = VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT,
.settingCount = settings.size(),
.pSettings = settings.data()
.pSettings = settings.data(),
};
auto layers = std::vector<const char *> {
@ -231,11 +232,12 @@ void Instance::initialize_instance()
void Instance::load_library()
{
library = dlopen("libvulkan.so.1", RTLD_NOW | RTLD_LOCAL);
if(!library)
{
library = dlopen("libvulkan.so", RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE);
}
constexpr auto runtime_loader_flags = RTLD_NOW | RTLD_DEEPBIND | RTLD_LOCAL | RTLD_NODELETE;
library = dlopen("libvulkan.so.1", runtime_loader_flags);
if (!library)
{
library = dlopen("libvulkan.so", runtime_loader_flags);
}
ensure(library, "Failed to dlopen vulkan library");
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)

View file

@ -42,49 +42,50 @@ Suite raii = "raii"_suite = [] {
// expect_not_nullptr(vk_destroy_surface_khr);
};
Case { "post load device functions state is correct" } = [] {
using namespace renderer::vk;
expect_not_nullptr(Instance::get());
expect_not_nullptr(vk_get_device_queue);
expect_not_nullptr(vk_create_command_pool);
expect_not_nullptr(vk_destroy_command_pool);
expect_not_nullptr(vk_allocate_command_buffers);
expect_not_nullptr(vk_free_command_buffers);
expect_not_nullptr(vk_begin_command_buffer);
expect_not_nullptr(vk_end_command_buffer);
expect_not_nullptr(vk_cmd_pipeline_barrier);
expect_not_nullptr(vk_queue_submit);
expect_not_nullptr(vk_queue_wait_idle);
expect_not_nullptr(vk_device_wait_idle);
expect_not_nullptr(vk_create_fence);
expect_not_nullptr(vk_destroy_fence);
expect_not_nullptr(vk_wait_for_fences);
expect_not_nullptr(vk_reset_fences);
expect_not_nullptr(vk_create_semaphore);
expect_not_nullptr(vk_destroy_semaphore);
expect_not_nullptr(vk_create_swapchain_khr);
expect_not_nullptr(vk_destroy_swapchain_khr);
expect_not_nullptr(vk_get_swapchain_images_khr);
expect_not_nullptr(vk_acquire_next_image_khr);
expect_not_nullptr(vk_queue_present_khr);
expect_not_nullptr(vk_create_image_view);
expect_not_nullptr(vk_destroy_image_view);
expect_not_nullptr(vk_create_render_pass);
expect_not_nullptr(vk_destroy_render_pass);
expect_not_nullptr(vk_create_frame_buffer);
expect_not_nullptr(vk_destroy_frame_buffer);
expect_not_nullptr(vk_create_shader_module);
expect_not_nullptr(vk_destroy_shader_module);
expect_not_nullptr(vk_create_pipeline_layout);
expect_not_nullptr(vk_destroy_pipeline_layout);
expect_not_nullptr(vk_create_graphics_pipelines);
expect_not_nullptr(vk_destroy_pipeline);
expect_not_nullptr(vk_cmd_begin_render_pass);
expect_not_nullptr(vk_cmd_end_render_pass);
expect_not_nullptr(vk_cmd_bind_pipeline);
expect_not_nullptr(vk_cmd_draw);
expect_not_nullptr(vk_cmd_set_viewport);
expect_not_nullptr(vk_cmd_set_scissors);
};
// TODO(Light): move device function symbols to device.cpp
// Case { "post load device functions state is correct" } = [] {
// using namespace renderer::vk;
// expect_not_nullptr(Instance::get());
//
// expect_not_nullptr(vk_get_device_queue);
// expect_not_nullptr(vk_create_command_pool);
// expect_not_nullptr(vk_destroy_command_pool);
// expect_not_nullptr(vk_allocate_command_buffers);
// expect_not_nullptr(vk_free_command_buffers);
// expect_not_nullptr(vk_begin_command_buffer);
// expect_not_nullptr(vk_end_command_buffer);
// expect_not_nullptr(vk_cmd_pipeline_barrier);
// expect_not_nullptr(vk_queue_submit);
// expect_not_nullptr(vk_queue_wait_idle);
// expect_not_nullptr(vk_device_wait_idle);
// expect_not_nullptr(vk_create_fence);
// expect_not_nullptr(vk_destroy_fence);
// expect_not_nullptr(vk_wait_for_fences);
// expect_not_nullptr(vk_reset_fences);
// expect_not_nullptr(vk_create_semaphore);
// expect_not_nullptr(vk_destroy_semaphore);
// expect_not_nullptr(vk_create_swapchain_khr);
// expect_not_nullptr(vk_destroy_swapchain_khr);
// expect_not_nullptr(vk_get_swapchain_images_khr);
// expect_not_nullptr(vk_acquire_next_image_khr);
// expect_not_nullptr(vk_queue_present_khr);
// expect_not_nullptr(vk_create_image_view);
// expect_not_nullptr(vk_destroy_image_view);
// expect_not_nullptr(vk_create_render_pass);
// expect_not_nullptr(vk_destroy_render_pass);
// expect_not_nullptr(vk_create_frame_buffer);
// expect_not_nullptr(vk_destroy_frame_buffer);
// expect_not_nullptr(vk_create_shader_module);
// expect_not_nullptr(vk_destroy_shader_module);
// expect_not_nullptr(vk_create_pipeline_layout);
// expect_not_nullptr(vk_destroy_pipeline_layout);
// expect_not_nullptr(vk_create_graphics_pipelines);
// expect_not_nullptr(vk_destroy_pipeline);
// expect_not_nullptr(vk_cmd_begin_render_pass);
// expect_not_nullptr(vk_cmd_end_render_pass);
// expect_not_nullptr(vk_cmd_bind_pipeline);
// expect_not_nullptr(vk_cmd_draw);
// expect_not_nullptr(vk_cmd_set_viewport);
// expect_not_nullptr(vk_cmd_set_scissors);
// };
};

View file

@ -76,6 +76,7 @@ Swapchain::Swapchain(ISurface *surface, IGpu *gpu, IDevice *device)
m_device->name(image, "swapchain image {}", idx++);
m_device->name(view, "swapchain image view {}", idx++);
}
m_device->wait_idle();
}
Swapchain::~Swapchain()
@ -90,6 +91,7 @@ Swapchain::~Swapchain()
m_device->wait_idle();
m_device->destroy_image_views(m_image_views);
m_device->destroy_swapchain(m_swapchain);
m_device->wait_idle();
}
catch (const std::exception &exp)
{

View file

@ -5,6 +5,7 @@ namespace lt::renderer::vk {
Messenger::Messenger(IInstance *instance, CreateInfo info)
: m_instance(static_cast<Instance *>(instance))
, m_user_data(std::move(info.user_data))
, m_user_callback(std::move(info.callback))
, m_debug_messenger(
m_instance,
VkDebugUtilsMessengerCreateInfoEXT {

View file

@ -10,7 +10,15 @@ Pass::Pass(
const lt::assets::ShaderAsset &vertex_shader,
const lt::assets::ShaderAsset &fragment_shader
)
: m_device(static_cast<Device *>(device))
: m_device(static_cast<Device *>(device)), m_layout(m_device->create_pipeline_layout(
VkPipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0u,
.pSetLayouts = nullptr,
.pushConstantRangeCount = 0u,
.pPushConstantRanges = nullptr,
}
))
{
auto *vertex_module = create_module(
vertex_shader.unpack(lt::assets::ShaderAsset::BlobTag::code)
@ -103,15 +111,7 @@ Pass::Pass(
.blendConstants = { 0.0f, 0.0, 0.0, 0.0 },
};
m_layout = m_device->create_pipeline_layout(
VkPipelineLayoutCreateInfo {
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 0u,
.pSetLayouts = nullptr,
.pushConstantRangeCount = 0u,
.pPushConstantRanges = nullptr,
}
);
auto attachment_description = VkAttachmentDescription {
.format = static_cast<Swapchain *>(swapchain)->get_format(),

View file

@ -8,7 +8,6 @@ class IInstance
{
public:
[[nodiscard]] static auto get(Api target_api) -> IInstance *;
IInstance() = default;
virtual ~IInstance() = default;

View file

@ -8,7 +8,6 @@
using ::lt::ecs::EntityId;
using ::lt::ecs::Registry;
using ::lt::surface::SurfaceComponent;
Suite raii = "surface"_suite = [] {
Case { "happy path won't throw" } = [&] {

View file

@ -1,7 +1,10 @@
#include <assets/shader.hpp>
#include <renderer/frontend/messenger.hpp>
#include <renderer/frontend/renderer/pass.hpp>
#include <renderer/test/utils.hpp>
using ::lt::renderer::IMessenger;
Suite raii = "pass_raii"_suite = [] {
Case { "happy path won't throw" } = [] {
auto fixture = Fixture_RendererSystem {};
@ -14,6 +17,9 @@ Suite raii = "pass_raii"_suite = [] {
lt::assets::ShaderAsset { "./data/test_assets/triangle.vert.asset" },
lt::assets::ShaderAsset { "./data/test_assets/triangle.frag.asset" }
);
expect_false(fixture.has_any_messages_of(IMessenger ::MessageSeverity::error));
expect_false(fixture.has_any_messages_of(IMessenger ::MessageSeverity::warning));
};
Case { "unhappy path throws" } = [] {

View file

@ -47,8 +47,7 @@ System::System(CreateInfo info)
}
System::~System()
{
}
= default;
void System::on_register()
{

View file

@ -148,7 +148,58 @@ public:
);
}
[[nodiscard]] auto has_any_messages() const -> bool
{
return m_user_data->m_has_any_messages;
}
[[nodiscard]] auto has_any_messages_of(
lt::renderer::IMessenger ::MessageSeverity severity
) const -> uint32_t
{
return m_user_data->m_severity_counter.contains(severity);
}
private:
static void messenger_callback(
lt::renderer::IMessenger::MessageSeverity severity,
lt::renderer::IMessenger::MessageType type,
const lt::renderer::IMessenger::MessageData &data,
std::any &user_data
)
{
// I know this makes the tests too verbose...
// but makes it easier to figure out what the problem is when things fail on ci
log_trc("vulkan: {}", data.message);
std::ignore = data;
std::ignore = type;
auto *fixture = std::any_cast<UserData *>(user_data);
fixture->m_has_any_messages = true;
++fixture->m_severity_counter[severity];
}
struct UserData
{
std::unordered_map<lt::renderer::IMessenger::MessageSeverity, uint32_t> m_severity_counter;
bool m_has_any_messages {};
};
lt::memory::Scope<UserData> m_user_data = lt::memory::create_scope<UserData>();
lt::memory::Scope<lt::renderer::IMessenger> m_messenger = lt::renderer::IMessenger::create(
constants::api,
lt::renderer::IInstance::get(constants::api),
lt::renderer::IMessenger ::CreateInfo {
.severities = lt::renderer::IMessenger ::MessageSeverity::all,
.types = lt::renderer::IMessenger ::MessageType::all,
.callback = &messenger_callback,
.user_data = m_user_data.get(),
}
);
lt::memory::Scope<lt::renderer::IDevice> m_device {
lt::renderer::IDevice::create(constants::api, gpu(), surface())
};
@ -188,6 +239,10 @@ private:
std::any &user_data
)
{
// I know this makes the tests too verbose...
// but makes it easier to figure out what the problem is when things fail on ci
log_trc("vulkan: {}", data.message);
std::ignore = data;
std::ignore = type;

View file

@ -41,7 +41,7 @@ public:
using Callback_T = std::function<void(
MessageSeverity message_severity,
MessageType message_type,
MessageData data,
const MessageData &data,
std::any &user_data
)>;

View file

@ -17,6 +17,7 @@ cmake . \
-DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_CXX_FLAGS=" \
-fsanitize=leak \
-fno-common \
-g \
-fno-omit-frame-pointer \
-std=c++23 \
@ -28,11 +29,11 @@ cmake . \
-lc++ \
-lc++abi \
-Wl,-rpath,/libcxx_lsan/lib" \
&& cmake --build ./build -j`nproc`
export LSAN_OPTIONS="suppressions=$(git rev-parse --show-toplevel)/tools/ci/amd64/clang/lsan.supp"
&& cmake --build ./build --target='renderer_tests' -j`nproc`
export LSAN_OPTIONS="suppressions=$(git rev-parse --show-toplevel)/tools/ci/amd64/clang/lsan.supp:fast_unwind_on_malloc=0:verbosity=1:report_objects=1"
export LSAN_SYMBOLIZER_PATH="$(which llvm-symbolizer)"
for test in $(find ./build -type f -name '*_tests' -executable); do
echo "Running $test"
"$test"
echo "Running $test"
"$test"
done

View file

@ -1,3 +1,4 @@
leak:libX11
leak:_dlopen
leak:_dlclose
leak:lt::renderer::vk::Device::destroy_swapchain