diff --git a/far/changelog b/far/changelog index 576d16871b..37b55b2edd 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,8 @@ +-------------------------------------------------------------------------------- +drkns 2024-05-06 12:34:28+01:00 - build 6331 + +1. Refactoring. + -------------------------------------------------------------------------------- drkns 2024-05-05 18:00:54+01:00 - build 6330 diff --git a/far/common/utility.hpp b/far/common/utility.hpp index 2233c23ea4..11fbea1b82 100644 --- a/far/common/utility.hpp +++ b/far/common/utility.hpp @@ -337,7 +337,7 @@ void copy_memory(detail::void_or_trivially_copyable auto const* const Source, de namespace detail { template requires std::same_as, void> - decltype(auto) cast_as(void_type* const BaseAddress, intptr_t const Offset) + constexpr decltype(auto) cast_as(void_type* const BaseAddress, intptr_t const Offset) { constexpr auto IsConst = std::is_const_v; @@ -359,10 +359,6 @@ namespace detail } } -} - -namespace detail -{ template concept buffer_type = std::same_as, void> || std::is_trivially_copyable_v; @@ -371,31 +367,31 @@ namespace detail } template -decltype(auto) view_as(detail::buffer_type auto const* const BaseAddress, intptr_t const Offset = 0) +constexpr decltype(auto) view_as(detail::buffer_type auto const* const BaseAddress, intptr_t const Offset = 0) { return detail::cast_as(static_cast(BaseAddress), Offset); } template -decltype(auto) view_as(unsigned long long const Address) +constexpr decltype(auto) view_as(unsigned long long const Address) { return view_as(static_cast(nullptr), Address); } template -decltype(auto) edit_as(detail::writable_buffer_type auto* const BaseAddress, intptr_t const Offset = 0) +constexpr decltype(auto) edit_as(detail::writable_buffer_type auto* const BaseAddress, intptr_t const Offset = 0) { return detail::cast_as(static_cast(BaseAddress), Offset); } template -decltype(auto) edit_as(unsigned long long const Address) +constexpr decltype(auto) edit_as(unsigned long long const Address) { return edit_as(static_cast(nullptr), Address); } template requires std::is_trivially_copyable_v -auto view_as_opt(detail::buffer_type auto const* const Begin, detail::buffer_type auto const* const End, size_t const Offset = 0) +constexpr auto view_as_opt(detail::buffer_type auto const* const Begin, detail::buffer_type auto const* const End, size_t const Offset = 0) { return static_cast(static_cast(Begin)) + Offset + sizeof(T) <= static_cast(static_cast(End))? view_as(Begin, Offset) : @@ -403,13 +399,13 @@ auto view_as_opt(detail::buffer_type auto const* const Begin, detail::buffer_typ } template requires std::is_trivially_copyable_v -auto view_as_opt(detail::buffer_type auto const* const Buffer, size_t const Size, size_t const Offset = 0) +constexpr auto view_as_opt(detail::buffer_type auto const* const Buffer, size_t const Size, size_t const Offset = 0) { return view_as_opt(Buffer, static_cast(static_cast(Buffer)) + Size, Offset); } template requires std::is_trivially_copyable_v -auto view_as_opt(std::ranges::contiguous_range auto const& Buffer, size_t const Offset = 0) +constexpr auto view_as_opt(std::ranges::contiguous_range auto const& Buffer, size_t const Offset = 0) { static_assert(detail::buffer_type>); return view_as_opt(std::ranges::cdata(Buffer), std::ranges::size(Buffer), Offset); diff --git a/far/far.natvis b/far/far.natvis index b4afa098ba..5c8c4e29e5 100644 --- a/far/far.natvis +++ b/far/far.natvis @@ -450,4 +450,56 @@ + + {{ scalar, {DataSize} bytes }} + {{ vector, {DataSize} bytes }} + + AllocationType + + {{ {DataSize} bytes }} + (unsigned char*)this + HeaderSize,[DataSize]na + + DataSize + + DataSize + (unsigned char const*)this + HeaderSize + + + + + {{ {DataSize} bytes }} + (wchar_t const*)((unsigned char const*)this + HeaderSize),[DataSize / sizeof(wchar_t)]na + + DataSize / sizeof(wchar_t) + + DataSize / sizeof(wchar_t) + (wchar_t const*)((unsigned char const*)this + HeaderSize) + + + + + {{ {sizeof(Stack) / sizeof(Stack[0])} frames }} + + + sizeof(Stack) / sizeof(Stack[0]) + Stack, na + + + + + + + + {{Count = {m_Size}}} + + m_Size + + m_Size + m_First + next + this,na + + + + diff --git a/far/memcheck.cpp b/far/memcheck.cpp index 5472c94598..89149261fa 100644 --- a/far/memcheck.cpp +++ b/far/memcheck.cpp @@ -60,63 +60,42 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace memcheck { -enum class allocation_type: unsigned +enum class allocation_type: uint8_t { - scalar = 0xa75ca1ae, - vector = 0xa77ec10e, + scalar = 0x5C, + vector = 0x7E, }; static const int EndMarker = 0xDEADBEEF; -struct MEMINFO +struct memory_block { + unsigned short HeaderSize; allocation_type AllocationType; - unsigned HeaderSize; - - size_t Size; + size_t DataSize; + size_t TotalSize; // Initializers aren't really needed here, just to stop GCC from complaining about them. void* Stack[10]{}; - MEMINFO* prev{}; - MEMINFO* next{}; + memory_block* prev{}; + memory_block* next{}; - int& end_marker() + static constexpr auto from_data(void* address, std::align_val_t Alignment) { - return *edit_as(this, Size - sizeof(EndMarker)); + return edit_as(address, 0 - aligned_size(sizeof(memory_block), static_cast(Alignment))); } -}; - -static MEMINFO* FirstMemBlock, *LastMemBlock; - -static auto to_real(void* address, std::align_val_t Alignment) -{ - return edit_as(address, 0 - aligned_size(sizeof(MEMINFO), static_cast(Alignment))); -} -static void* to_user(MEMINFO* address) -{ - return edit_as(address, address->HeaderSize); -} - -static void check_chain() -{ - if constexpr ((false)) + constexpr void* data() { - if (!FirstMemBlock) - return; - - auto p = FirstMemBlock; - - while (p->next) - p = p->next; - assert(p == LastMemBlock); + return edit_as(this, this->HeaderSize); + } - while (p->prev) - p = p->prev; - assert(p == FirstMemBlock); + constexpr int& end_marker() + { + return *edit_as(this, HeaderSize + aligned_size(DataSize, alignof(int))); } -} +}; static string format_type(allocation_type Type, size_t Size) { @@ -174,19 +153,106 @@ static string printable_ansi_string(void const* const Data, size_t const Size) return printable_string(encoding::ansi::get_chars({ static_cast(Data), Size })); } -static void poison_block(MEMINFO* Block) +static void poison_block(memory_block* Block) { ASAN_POISON_MEMORY_REGION(&Block->end_marker(), sizeof(EndMarker)); ASAN_POISON_MEMORY_REGION(Block, Block->HeaderSize); } -static void unpoison_block(MEMINFO* Block) +static void unpoison_block(memory_block* Block) { ASAN_UNPOISON_MEMORY_REGION(Block, sizeof(*Block)); ASAN_UNPOISON_MEMORY_REGION(Block + 1, Block->HeaderSize - sizeof(*Block)); ASAN_UNPOISON_MEMORY_REGION(&Block->end_marker(), sizeof(EndMarker)); } +static class blocks_list +{ +public: + void add(memory_block* const Block) + { + check_chain(); + + Block->prev = m_Last; + Block->next = nullptr; + + if (m_Last) + { + unpoison_block(m_Last); + m_Last->next = Block; + poison_block(m_Last); + } + else + m_First = Block; + + m_Last = Block; + + ++m_Size; + } + + void remove(memory_block const* const Block) + { + check_chain(); + + if (Block->prev) + { + unpoison_block(Block->prev); + Block->prev->next = Block->next; + poison_block(Block->prev); + } + + if (Block->next) + { + unpoison_block(Block->next); + Block->next->prev = Block->prev; + poison_block(Block->next); + } + + if (Block == m_Last) + m_Last = m_Last->prev; + + if (Block == m_First) + m_First = Block->next; + + --m_Size; + } + + auto begin() const + { + return m_First; + } + + auto size() const + { + return m_Size; + } + +private: + void check_chain() const + { + if constexpr ((false)) + { + if (!m_First) + return; + + auto p = m_First; + + while (p->next) + p = p->next; + assert(p == m_Last); + + while (p->prev) + p = p->prev; + assert(p == m_First); + } + } + + memory_block* m_First{}; + memory_block* m_Last{}; + size_t m_Size{}; +} +Blocks; + class checker { public: @@ -207,68 +273,30 @@ class checker } } - void register_block(MEMINFO *block) + void register_block(memory_block* const Block) { if (!m_Enabled) return; - check_chain(); - - block->prev = LastMemBlock; - block->next = nullptr; - - if (LastMemBlock) - { - unpoison_block(LastMemBlock); - LastMemBlock->next = block; - poison_block(LastMemBlock); - } - else - { - FirstMemBlock = block; - } + Blocks.add(Block); - LastMemBlock = block; + update_call_count(Block->AllocationType, true); - update_call_count(block->AllocationType, true); - ++m_AllocatedMemoryBlocks; - ++m_TotalAllocationCalls; - m_AllocatedMemorySize += block->Size; - m_AllocatedPayloadSize += block->Size - block->HeaderSize - sizeof(EndMarker); + m_AllocatedMemorySize += Block->TotalSize; + m_AllocatedPayloadSize += Block->DataSize; } - void unregister_block(MEMINFO *block) + void unregister_block(memory_block const* const Block) { if (!m_Enabled) return; - check_chain(); - - if (block->prev) - { - unpoison_block(block->prev); - block->prev->next = block->next; - poison_block(block->prev); - } - - if (block->next) - { - unpoison_block(block->next); - block->next->prev = block->prev; - poison_block(block->next); - } + Blocks.remove(Block); - if (block == LastMemBlock) - LastMemBlock = LastMemBlock->prev; + update_call_count(Block->AllocationType, false); - if (block == FirstMemBlock) - FirstMemBlock = {}; - - update_call_count(block->AllocationType, false); - ++m_TotalDeallocationCalls; - --m_AllocatedMemoryBlocks; - m_AllocatedMemorySize -= block->Size; - m_AllocatedPayloadSize -= block->Size - block->HeaderSize - sizeof(EndMarker); + m_AllocatedMemorySize -= Block->TotalSize; + m_AllocatedPayloadSize -= Block->DataSize; } void lock() { m_CS.lock(); } @@ -314,7 +342,7 @@ class checker Message += L'\n'; - far::format_to(Message, L" Blocks: {}\n"sv, m_AllocatedMemoryBlocks); + far::format_to(Message, L" Blocks: {}\n"sv, Blocks.size()); far::format_to(Message, L" Payload: {}\n"sv, m_AllocatedPayloadSize); far::format_to(Message, L" Bytes: {}\n"sv, m_AllocatedMemorySize); @@ -323,23 +351,23 @@ class checker Print(Message); Message.clear(); - for (auto i = FirstMemBlock->next; i; i = i->next) + for (auto i = Blocks.begin(); i; i = i->next) { unpoison_block(i); - const auto BlockSize = i->Size - i->HeaderSize - sizeof(EndMarker); - const auto UserAddress = to_user(i); + const auto Data = i->data(); + const auto Size = i->DataSize; const size_t Width = 80 - 7 - 1; Message = concat( L"--------------------------------------------------------------------------------\n"sv, - str(UserAddress), L", "sv, format_type(i->AllocationType, BlockSize), - L"\nData: "sv, BlobToHexString({ static_cast(UserAddress), std::min(BlockSize, Width / 3) }, L' '), - L"\nAnsi: "sv, printable_ansi_string(UserAddress, std::min(BlockSize, Width)), - L"\nWide: "sv, printable_wide_string(UserAddress, std::min(BlockSize, Width * sizeof(wchar_t))), + str(Data), L", "sv, format_type(i->AllocationType, Size), + L"\nData: "sv, BlobToHexString({ static_cast(Data), std::min(Size, Width / 3) }, L' '), + L"\nAnsi: "sv, printable_ansi_string(Data, std::min(Size, Width)), + L"\nWide: "sv, printable_wide_string(Data, std::min(Size, Width * sizeof(wchar_t))), L"\nStack:\n"sv); - os::debug::stack_frame Stack[ARRAYSIZE(MEMINFO::Stack)]; + os::debug::stack_frame Stack[ARRAYSIZE(memory_block::Stack)]; size_t StackSize; for (StackSize = 0; StackSize != std::size(Stack) && i->Stack[StackSize]; ++StackSize) @@ -360,19 +388,16 @@ class checker intptr_t m_CallNewDeleteVector{}; intptr_t m_CallNewDeleteScalar{}; - size_t m_AllocatedMemoryBlocks{}; size_t m_AllocatedMemorySize{}; size_t m_AllocatedPayloadSize{}; - size_t m_TotalAllocationCalls{}; - size_t m_TotalDeallocationCalls{}; bool m_Enabled{true}; }; static void* debug_allocator(size_t const size, std::align_val_t Alignment, allocation_type const type, bool const Noexcept) { - const auto BlockAlignment = std::max(alignof(MEMINFO), static_cast(Alignment)); - const auto HeaderSize = static_cast(aligned_size(sizeof(MEMINFO), BlockAlignment)); + const auto BlockAlignment = std::max(alignof(memory_block), static_cast(Alignment)); + const auto HeaderSize = static_cast(aligned_size(sizeof(memory_block), BlockAlignment)); assert(std::numeric_limits::max() - size >= HeaderSize + sizeof(EndMarker)); const auto realSize = HeaderSize + aligned_size(size, alignof(int)) + sizeof(EndMarker); @@ -381,27 +406,26 @@ static void* debug_allocator(size_t const size, std::align_val_t Alignment, allo { if (const auto RawBlock = _aligned_malloc(realSize, BlockAlignment)) { - const auto Info = static_cast(RawBlock); - placement::construct(*Info, type, HeaderSize, realSize); + const auto Block = static_cast(RawBlock); + placement::construct(*Block, HeaderSize, type, size, realSize); const auto FramesToSkip = 2; // This function and the operator // RtlCaptureStackBackTrace is invoked directly since we don't need to make debug builds Win2k compatible - if (const auto Captured = RtlCaptureStackBackTrace(FramesToSkip, static_cast(std::size(Info->Stack)), Info->Stack, {}); Captured < std::size(Info->Stack)) - Info->Stack[Captured] = {}; - - Info->end_marker() = EndMarker; - - const auto Address = to_user(Info); + if (const auto Captured = RtlCaptureStackBackTrace(FramesToSkip, static_cast(std::size(Block->Stack)), Block->Stack, {}); Captured < std::size(Block->Stack)) + Block->Stack[Captured] = {}; - assert(is_aligned(Address, static_cast(Alignment))); + Block->end_marker() = EndMarker; { SCOPED_ACTION(std::scoped_lock)(Checker); - Checker.register_block(Info); - poison_block(Info); + Checker.register_block(Block); + poison_block(Block); } - return Address; + const auto Data = Block->data(); + assert(is_aligned(Data, static_cast(Alignment))); + + return Data; } if (const auto Handler = std::get_new_handler()) @@ -415,29 +439,33 @@ static void* debug_allocator(size_t const size, std::align_val_t Alignment, allo } } -static void debug_deallocator(void* const Block, std::optional const Size, std::align_val_t Alignment, allocation_type type) noexcept +static void debug_deallocator(void* const Data, std::optional const Size, std::align_val_t Alignment, allocation_type type) noexcept { - if (!Block) + if (!Data) return; - const auto Info = to_real(Block, Alignment); + const auto Block = memory_block::from_data(Data, Alignment); { SCOPED_ACTION(std::scoped_lock)(Checker); - unpoison_block(Info); - Checker.unregister_block(Info); + unpoison_block(Block); + Checker.unregister_block(Block); } - assert(Info->AllocationType == type); - assert(Info->end_marker() == EndMarker); + const auto BlockAlignment = std::max(alignof(memory_block), static_cast(Alignment)); + + assert(Block->HeaderSize == static_cast(aligned_size(sizeof(memory_block), BlockAlignment))); + assert(Block->AllocationType == type); + assert(Block->end_marker() == EndMarker); if (Size) { - assert(Info->Size == Info->HeaderSize + aligned_size(*Size, alignof(int)) + sizeof(EndMarker)); + assert(Block->DataSize == Size); + assert(Block->TotalSize == Block->HeaderSize + aligned_size(*Size, alignof(int)) + sizeof(EndMarker)); } - placement::destruct(*Info); - _aligned_free(Info); + placement::destruct(*Block); + _aligned_free(Block); } static constexpr std::align_val_t default_alignment{ __STDCPP_DEFAULT_NEW_ALIGNMENT__ }; diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 25aad289ec..e4e205edd9 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6330 +6331