LCOV - code coverage report
Current view: top level - muse/framework/global - allocator.cpp Coverage Total Hit
Test: filtered_coverage.info Lines: 54.6 % 152 83
Test Date: 2026-11-05 04:07:02

            Line data    Source code
       1              : /*
       2              :  * SPDX-License-Identifier: GPL-3.0-only
       3              :  * MuseScore-CLA-applies
       4              :  *
       5              :  * MuseScore Studio
       6              :  * Music Composition & Notation
       7              :  *
       8              :  * Copyright (C) 2021 MuseScore Limited and others
       9              :  *
      10              :  * This program is free software: you can redistribute it and/or modify
      11              :  * it under the terms of the GNU General Public License version 3 as
      12              :  * published by the Free Software Foundation.
      13              :  *
      14              :  * This program is distributed in the hope that it will be useful,
      15              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      16              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17              :  * GNU General Public License for more details.
      18              :  *
      19              :  * You should have received a copy of the GNU General Public License
      20              :  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
      21              :  */
      22              : #include "allocator.h"
      23              : 
      24              : #include <algorithm>
      25              : #include <cstdlib>
      26              : #include <iostream>
      27              : #include <set>
      28              : #include <sstream>
      29              : 
      30              : #include "global/stringutils.h"
      31              : #include "log.h"
      32              : 
      33              : using namespace muse;
      34              : 
      35              : int ObjectAllocator::s_used = 0;
      36              : size_t ObjectAllocator::DEFAULT_BLOCK_SIZE(1024 * 256); // 256 kB
      37              : 
      38           13 : static inline size_t align(size_t n)
      39              : {
      40           13 :     return (n + sizeof(intptr_t) - 1) & ~(sizeof(intptr_t) - 1);
      41              : }
      42              : 
      43              : // ============================================
      44              : // ObjectAllocator
      45              : // ============================================
      46            0 : void ObjectAllocator::used()
      47              : {
      48              : #ifdef MUSE_ENABLE_CUSTOM_ALLOCATOR
      49              :     s_used++;
      50              : #endif
      51            0 : }
      52              : 
      53            0 : void ObjectAllocator::unused()
      54              : {
      55              : #ifdef MUSE_ENABLE_CUSTOM_ALLOCATOR
      56              :     s_used--;
      57              : #endif
      58            0 : }
      59              : 
      60            3 : ObjectAllocator::ObjectAllocator(const char* module, const char* name, destroyer_t dtor)
      61            3 :     : m_module(module), m_name(name), m_dtor(dtor)
      62              : {
      63            3 :     AllocatorsRegister::instance()->reg(this);
      64            3 : }
      65              : 
      66            3 : ObjectAllocator::~ObjectAllocator()
      67              : {
      68            3 :     AllocatorsRegister::instance()->unreg(this);
      69            3 : }
      70              : 
      71            0 : const char* ObjectAllocator::module() const
      72              : {
      73            0 :     return m_module;
      74              : }
      75              : 
      76            0 : const char* ObjectAllocator::name() const
      77              : {
      78            0 :     return m_name;
      79              : }
      80              : 
      81           13 : void* ObjectAllocator::alloc(size_t size)
      82              : {
      83           13 :     size = align(size);
      84              : 
      85           13 :     if (!m_chunkSize) {
      86            3 :         m_chunkSize = size;
      87              :     }
      88              : 
      89           13 :     assert(m_chunkSize == size);
      90              : 
      91           13 :     if (!m_free) {
      92            5 :         Block b = allocateBlock(m_chunkSize);
      93            5 :         m_blocks.push_back(b);
      94            5 :         m_free = b.begin;
      95              :     }
      96              : 
      97              :     // The return value is the current position of
      98              :     // the allocation pointer:
      99           13 :     Chunk* freeChunk = m_free;
     100              : 
     101              :     // Advance (bump) the allocation pointer to the next chunk.
     102              :     //
     103              :     // When no chunks left, the `m_free` will be set to `nullptr`, and
     104              :     // this will cause allocation of a new block on the next request:
     105           13 :     m_free = m_free->next;
     106              : 
     107           13 :     m_statistic.totalAllocatedCount++;
     108              : 
     109           13 :     return freeChunk;
     110              : }
     111              : 
     112            2 : void ObjectAllocator::free(void* chunk)
     113              : {
     114              :     // The freed chunk's next pointer points to the
     115              :     // current allocation pointer:
     116            2 :     reinterpret_cast<Chunk*>(chunk)->next = m_free;
     117              : 
     118              :     // And the allocation pointer is now set
     119              :     // to the returned (free) chunk:
     120            2 :     m_free = reinterpret_cast<Chunk*>(chunk);
     121              : 
     122            2 :     m_statistic.totalFreeCount++;
     123            2 : }
     124              : 
     125            2 : void ObjectAllocator::cleanup()
     126              : {
     127            2 :     if (m_blocks.empty()) {
     128            0 :         return;
     129              :     }
     130              : 
     131            2 :     std::set<Chunk*> freeChunks;
     132              :     {
     133            2 :         Chunk* free = m_free;
     134         4684 :         while (free) {
     135         4682 :             freeChunks.insert(free);
     136         4682 :             free = free->next;
     137              :         }
     138              :     }
     139              : 
     140            6 :     for (size_t bi = 0; bi < m_blocks.size(); ++bi) {
     141            4 :         const Block& b = m_blocks.at(bi);
     142            4 :         Chunk* chunk = b.begin;
     143         4693 :         for (size_t i = 0; i < b.chunkCount - 1; ++i) {
     144              :             // if not free chunk, then destroy object
     145         4689 :             if (freeChunks.find(chunk) == freeChunks.cend()) {
     146            9 :                 m_dtor(reinterpret_cast<void*>(chunk));
     147              :             }
     148              : 
     149         4689 :             chunk->next = reinterpret_cast<Chunk*>(reinterpret_cast<uint8_t*>(chunk) + b.chunkSize);
     150         4689 :             chunk = chunk->next;
     151              :         }
     152              : 
     153            4 :         if (freeChunks.find(chunk) == freeChunks.cend()) {
     154            2 :             m_dtor(reinterpret_cast<void*>(chunk));
     155              :         }
     156              : 
     157            4 :         if (bi < (m_blocks.size() - 1)) {
     158            2 :             chunk->next = m_blocks.at(bi + 1).begin;
     159              :         } else {
     160            2 :             chunk->next = nullptr;
     161              :         }
     162              :     }
     163              : 
     164            2 :     m_free = m_blocks.front().begin;
     165            2 : }
     166              : 
     167            5 : ObjectAllocator::Block ObjectAllocator::allocateBlock(size_t chunkSize) const
     168              : {
     169            5 :     size_t blockSize = std::max(DEFAULT_BLOCK_SIZE, chunkSize);
     170            5 :     size_t chunkCount = blockSize / chunkSize;
     171              : 
     172            5 :     Block b;
     173            5 :     b.begin = reinterpret_cast<Chunk*>(malloc(blockSize));
     174            5 :     b.chunkCount = chunkCount;
     175            5 :     b.chunkSize = chunkSize;
     176              : 
     177              :     // Once the block is allocated, we need to chain all
     178              :     // the chunks in this block:
     179              : 
     180            5 :     Chunk* chunk = b.begin;
     181              : 
     182         4694 :     for (size_t i = 0; i < chunkCount - 1; ++i) {
     183         4689 :         chunk->next = reinterpret_cast<Chunk*>(reinterpret_cast<uint8_t*>(chunk) + chunkSize);
     184         4689 :         chunk = chunk->next;
     185              :     }
     186              : 
     187            5 :     chunk->next = nullptr;
     188              : 
     189            5 :     return b;
     190              : }
     191              : 
     192            0 : void* ObjectAllocator::not_supported(const char* info)
     193              : {
     194            0 :     LOGE() << m_name << ": " << info << " not supported";
     195            0 :     std::abort();
     196              :     //return nullptr; // NOTREACHED
     197              : }
     198              : 
     199            6 : ObjectAllocator::Info ObjectAllocator::stateInfo() const
     200              : {
     201            6 :     Info info;
     202            6 :     info.module = m_module;
     203            6 :     info.name = m_name;
     204            6 :     info.chunkSize = m_chunkSize;
     205            6 :     info.blockCount = m_blocks.size();
     206            6 :     info.totalAllocatedCount = m_statistic.totalAllocatedCount;
     207            6 :     info.totalFreeCount = m_statistic.totalFreeCount;
     208              : 
     209           16 :     for (const Block& b : m_blocks) {
     210           10 :         info.totalChunks += b.chunkCount;
     211              :     }
     212              : 
     213            6 :     Chunk* free = m_free;
     214         9382 :     while (free) {
     215         9376 :         ++info.freeChunks;
     216         9376 :         free = free->next;
     217              :     }
     218              : 
     219            6 :     return info;
     220            0 : }
     221              : 
     222              : // ============================================
     223              : // AllocatorsRegister
     224              : // ============================================
     225            3 : void AllocatorsRegister::reg(ObjectAllocator* a)
     226              : {
     227            3 :     m_allocators.push_back(a);
     228            3 : }
     229              : 
     230            3 : void AllocatorsRegister::unreg(ObjectAllocator* a)
     231              : {
     232            3 :     m_allocators.remove(a);
     233            3 : }
     234              : 
     235            0 : void AllocatorsRegister::cleanupAll(const std::string& module)
     236              : {
     237            0 :     for (ObjectAllocator* a : m_allocators) {
     238            0 :         if (a->module() == module) {
     239            0 :             a->cleanup();
     240              :         }
     241              :     }
     242            0 : }
     243              : 
     244              : #define FORMAT(str, width) muse::strings::leftJustified(str, width)
     245              : #define TITLE(str) FORMAT(std::string(str), 20)
     246              : #define VALUE(val) FORMAT(std::to_string(val), 20)
     247              : 
     248            0 : void AllocatorsRegister::printStatistic(const std::string& title)
     249              : {
     250            0 :     std::stringstream stream;
     251            0 :     stream << "\n\n";
     252            0 :     stream << title << "\n";
     253            0 :     stream << "allocators: " << m_allocators.size() << '\n';
     254            0 :     stream << TITLE("Object") << TITLE("Total alloc") << TITLE("Total free") << TITLE("Used (leak?)") << TITLE("Object size") << "\n";
     255              : 
     256            0 :     uint64_t totalBytes = 0;
     257            0 :     uint64_t totalAllocatedCount = 0;
     258            0 :     uint64_t totalFreeCount = 0;
     259            0 :     uint64_t totalUsedCount = 0;
     260            0 :     for (ObjectAllocator* a : m_allocators) {
     261            0 :         ObjectAllocator::Info info = a->stateInfo();
     262            0 :         stream << FORMAT(info.name, 20)
     263            0 :                << VALUE(info.totalAllocatedCount)
     264            0 :                << VALUE(info.totalFreeCount)
     265            0 :                << VALUE(info.usedChunks())
     266            0 :                << VALUE(info.chunkSize)
     267            0 :                << "\n";
     268              : 
     269            0 :         totalAllocatedCount += info.totalAllocatedCount;
     270            0 :         totalFreeCount += info.totalFreeCount;
     271            0 :         totalUsedCount += info.usedChunks();
     272            0 :         totalBytes += info.allocatedBytes();
     273            0 :     }
     274              : 
     275            0 :     stream << "--------------------------------------------------------------------------------------------\n";
     276            0 :     stream << FORMAT("Total", 20) << VALUE(totalAllocatedCount) << VALUE(totalFreeCount) << VALUE(totalUsedCount) << "\n";
     277            0 :     stream << "Total allocated: " << totalBytes << " bytes\n";
     278              : 
     279            0 :     LOGD() << stream.str() << '\n';
     280            0 : }
     281              : 
     282            0 : void AllocatorsRegister::printState(const std::string& title)
     283              : {
     284            0 :     std::stringstream stream;
     285            0 :     stream << "\n\n";
     286            0 :     stream << title << "\n";
     287            0 :     stream << "allocators: " << m_allocators.size() << '\n';
     288            0 :     stream << TITLE("Object") << TITLE("blockCount") << TITLE("totalChunks") << TITLE("freeChunks") << TITLE("chunkSize")
     289            0 :            << TITLE("allocatedBytes") << "\n";
     290              : 
     291            0 :     uint64_t totalBytes = 0;
     292            0 :     for (ObjectAllocator* a : m_allocators) {
     293            0 :         ObjectAllocator::Info info = a->stateInfo();
     294            0 :         stream << FORMAT(info.name, 20)
     295            0 :                << VALUE(info.blockCount)
     296            0 :                << VALUE(info.totalChunks)
     297            0 :                << VALUE(info.freeChunks)
     298            0 :                << VALUE(info.chunkSize)
     299            0 :                << VALUE(info.allocatedBytes())
     300            0 :                << "\n";
     301              : 
     302            0 :         totalBytes += info.allocatedBytes();
     303            0 :     }
     304              : 
     305            0 :     stream << "-----------------------------------------------------\n";
     306            0 :     stream << "Total allocated: " << totalBytes << " bytes\n";
     307              : 
     308            0 :     LOGD() << stream.str() << '\n';
     309            0 : }
        

Generated by: LCOV version 2.0-1