Утечка памяти в функции C++C++

Программы на C++. Форум разработчиков
Ответить
Anonymous
 Утечка памяти в функции C++

Сообщение Anonymous »


Я работаю над проектом, в котором данные довольно часто передаются от функции к функции. Я использую библиотеки C++ как std::string, std::stringstream и т. д., но согласно внутреннему инструменту анализа кучи ESP32 и тому факту, что malloc прерывается после нескольких операций, у меня возникают утечки памяти.

Я публикую сокращенный набор исходного кода. Точкой входа является вызов

handleData(DATA_PACKET, 0, данные, data_len); Функция обратного вызова регистрируется как таковая:

CardDecomposer::getSingleton()->registerOutputProcessor([this](const Card &card){ Object::getSingleton()->onEvent( std::make_shared( iEvent::CreateEvent()->withSubEvent( // Обязательно скопируйте карту, не перемещайте ее Событие::CardEvent::CreateEvent(карта) ) ) ); }); DataHandler.cpp:

/** * @brief Обрабатывает пакет * * @param package_type тип пакета * @param канал канал * Данные о событии @param * Размер данных события @param event_size */ void handleData(uint8_t package_type, uint16_t канал, событие uint8_t*, uint16_t event_size){ НЕ ИСПОЛЬЗУЕТСЯ (канал); статус uint8_t; ESP_LOGD(TAG, «Пакет типа %u», package_type); переключатель (тип_пакета) { случай DATA_PACKET: { ESP_ERROR_CHECK(heap_trace_start(HEAP_TRACE_LEAKS)); if (Decomposer::getSingleton()->appendToBuffer(event, event_size)){ Декомпозер::getSingleton()->processBuffer(); } ESP_ERROR_CHECK(heap_trace_stop()); heap_trace_dump(); // Управление потоком следующий_пакет (0); перерыв; } по умолчанию: ESP_LOGW(TAG, "Тип необработанного пакета: %u", package_type) ; } } StringUtils.cpp:

/** * @brief Разбивает ввод на разделы на основе заданного разделителя. * */ std::vector StringUtils::SplitIntoSections(const std::string &str, const std::string &delimiter) { std::vector результат; size_t начало = 0; for (size_t найдено = str.find(разделитель); найдено != std::string::npos; найдено = str.find(разделитель, начало)) { result.emplace_back(str.begin() + start, str.begin() + найдено); начало = найдено + разделитель.размер(); } если (начало!= str.size()) result.emplace_back(str.begin() + start, str.end()); вернуть результат; } size_t StringUtils::count_strings(const char *str, const char *sub) { size_t sublen = strlen(sub); если (sublen == 0) вернуть 0; size_t разрешение = 0; for (str = strstr(str, sub); str; str = strstr(str + sublen, sub)) ++рез; вернуть разрешение; } size_t StringUtils::count_strings(const std::string &str, const std::string &sub) { если (sub.length() == 0) вернуть 0; число интервалов = 0; for (size_t offset = str.find(sub); offset != std::string::npos; смещение = str.find(sub, offset + sub.length())) { ++счет; } счетчик возврата; } Decomposer.cpp:

size_t Decomposer::processFirstCard(){ вернуть процессCompleteCards(0); } bool Decomposer::appendToBuffer(uint8_t* data, size_t len){ buffer.append((char*)data, len); // Буфер только что увеличился в размерах длина += длина; длина возврата != 0; } void Decomposer::processBuffer(){ // Проверяем, есть ли в буфере начало и конец size_t cardBeginnings; size_t cardEndings; делать{ cardBeginnings = StringUtils::count_strings(buffer, VC_BEGIN_TOKEN); cardEndings = StringUtils::count_strings(buffer, VC_END_TOKEN); if(cardBeginnings > 1 && cardEndings > 1){ // Обрабатываем текущий буфер и узнаем, что удалить из начала буфера size_t bytesProcessed =processFirstCard(); // Удалить первую карту в буфере, как она была обработана если (bytesProcessed > 0){ пытаться{ // Удаляем первую карту из строки buffer.erase(0, байтОбработано); // Устанавливаем длину длина -= байтОбработано; буфер.shrink_to_fit(); }catch(const std::Exception& ex){ длина += байтОбработано; ESP_LOGE(TAG, "Исключение при удалении обработанной карты: %s", ex.what()); fflush(стандартный вывод); } } } } while(cardBeginnings >= 2 && cardEndings >= 2); } void Decomposer::registerOutputProcessor(std::function функция){ ESP_LOGD(TAG, «Зарегистрированный процессор вывода»); обратный вызов = функция; } size_t Decomposer::processCompleteCards(size_t offset){ size_t обработанных байтов = 0; // Мы можем обработать данные // Извлекаем индексы всех полных карточек size_t cardBeginning = buffer.find(VC_BEGIN_TOKEN, смещение, strlen(VC_BEGIN_TOKEN)); size_t cardEnding = buffer.rfind(VC_END_TOKEN, buffer.length(), strlen(VC_END_TOKEN)); // Проверяем, найдены ли правильные местоположения if(cardBeginning == std::string::npos || cardEnding == std::string::npos){ ESP_LOGW(TAG, «Не удалось найти границы карты в буфере»); вернуть 0; } // Конец карты включает длину конечного токена cardEnding += strlen(VC_END_TOKEN); size_t cardLength = cardEnding-cardBeginning; утверждать (длина карты> 0); Assert(heap_caps_get_free_size(MALLOC_CAP_DEFAULT) > cardLength); // Конвертируем \r\n в \n std::vector card = Card::from_string( std::regex_replace( buffer.substr(cardBeginning, cardLength) + '\0', std::regex("\\r\\n"), "\n" ) ); ESP_LOGD(TAG, "Проанализировано %d карточек", card.size()); for(Карта-карта: карты){ //std::invoke(this->callback, card); } обработанные байты + = длина карты; вернуть обработанные байты; } Карта.cpp:

std::vector Card::from_string(const std::string& data) { карты std::vector; Ток карты; Bool начал = ложь; const std::vectorlines = StringUtils::SplitIntoSections(data, VC_END_LINE_TOKEN); for (std::string line: линии) { if ((line == VC_BEGIN_TOKEN) && !started) началось = правда; иначе, если ((строка == VC_END_TOKEN) && началось) { card.emplace_back(текущий); началось = ложь; } иначе, если (начато) { const cardPropertyList реквизит = CardProperty::from_string(line); current.addProperties(реквизит); } } возврат визитных карточек; } CardProperty.cpp:

std::vector CardProperty::from_string(const std::string& data) { свойства std::vector; std::vectorlines = StringUtils::SplitIntoSections(data, VC_END_LINE_TOKEN); for (std::string line: линии) { если (строка == VC_BEGIN_TOKEN || строка == VC_END_TOKEN) перерыв; std::vector tokens = StringUtils::SplitIntoSections(line, VC_ASSIGNMENT_TOKEN); если (tokens.size() >= 2) { std::vector property_tokens = StringUtils::SplitIntoSections(tokens.at(0), VC_SEPARATOR_TOKEN); std::string name = property_tokens[0]; если (имя!= VC_VERSION) { vCardParamList params = CardParam::from_string(StringUtils::join(property_tokens, VC_SEPARATOR_TOKEN)); Properties.emplace_back(CardProperty(name, tokens.at(1), params)); } } } вернуть свойства; } CardParam.cpp:

std::vector CardParam::from_string(const std::string& data) { std::vector параметры; std::vector tokens = StringUtils::SplitIntoSections(data, std::string(VC_SEPARATOR_TOKEN)); for (std::string token: токены) { int token_size = token.size(); если (token.starts_with(std::string(VC_TYPE_TOKEN))){ for (std::string type: StringUtils::SplitIntoSections(token.substr(token.size()-token_size-5, token_size-5), std::string(VC_TYPE_SEP_TOKEN))) params.emplace_back(vCardParam(type, vCardParam::Type)); }иначе, если (token.starts_with(VC_ENCODING_TOKEN)){ params.emplace_back(vCardParam(token.substr(token.size()-token_size-9, token_size-9), CardParam::Encoding)); }иначе, если (token.starts_with(VC_CHARSET_TOKEN)){ params.emplace_back(vCardParam(token.substr(token.size()-token_size-8, token_size-8), CardParam::Charset)); }еще{ params.emplace_back(vCardParam(токен)); } } вернуть параметры; } Объект.cpp:

/** * @brief Функция обратного вызова для событий * * @param event — событие, которое нужно обработать. */ void Object::onEvent(std::shared_ptr event){ // Перебираем все подсобытия for(std::shared_ptr subEvent : event->getSubEvents()){ if(subEvent->isOfType()){ CardEvent* cardEvent = Dynamic_cast(subEvent.get()); if(!cardEvent->getCard().isValid()){ ESP_LOGE(TAG, «Не предоставлена ​​действующая карта»); возвращаться; } пытаться{ ESP_LOGD(TAG, "Карта: %s (MD5 %s)", cardEvent->getCard()["property"].value().c_str(), CardUtils::getMD5AsString(cardEvent->getCard()).c_str ()); }catch(std::Exception& ex) { ESP_LOGE(TAG, «Исключение: %s», ex.what()); } if(storeOrUpdateContact(cardEvent->getStorageInformation(), vCardEvent->getCard())){ ESP_LOGI(TAG, «Контакт обновлен»); } }еще{ ESP_LOGW(TAG, "%s: Неизвестный тип подсобытия", __FUNCTION__); } } } Обратите внимание, что в DataHandler.cpp:processCompleteCards //std::invoke(this->callback, card); был раскомментирован, в результате чего зарегистрированный OutputProcessor не был вызван.
р>
При закомментированном обратном вызове возникает единственная утечка памяти, о которой внутренний анализатор понятия не имеет, где она находится. Всякий раз, когда я включаю обратный вызов, количество утечек памяти увеличивается примерно до 10, что похоже на утечку памяти на каждую обработанную карту, поскольку карты поступают пакетами примерно по 10.

Вы видите что-нибудь очевидное в моем коде? На данный момент я не могу понять, где может быть утечка памяти.
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «C++»