LINUX.ORG.RU

segfault и прочие read access

 ,


1

4

Так и знал, что кодя на с-подобных столкнусь с этой ****. На ровных местах вылезает ошибка «доступ к памяти». Какая то переменная где-то кем-то удалилась и не может быть прочитана в другом месте.

Есть вот такая чепуха, которая в треде парсит логи на предмет ошибок и прочего

//lambda - вызывается тредом, передает локальную переменную строку
[](std::string line){
  if (std::regex_search(line, std::regex(".*(some err).*"))) debug();
  if (std::regex_search(line, std::regex(".*(some err2).*"))) debug();
  if (std::regex_search(line, std::regex(".*(some err3).*"))) debug();
  //...
}


Когда тред каким-то образом убивается (в моем случае это в основном при выходе из программы) вылезают ошибки доступа к памяти. По call-stack вижу что где то далеко в std::regexp (где то в 18 хопе вверх от моего вызова регекса). Как я понимаю, в regex_search() переменная далее идет по цепочке вызовов и в ккакой то момент она удаляется. Вот как такое предотвратить? Ведь на момент вызова regex_search() переменная существовала? Как я понимаю это из-за того что переменная локальная? Когда тред завершается принудительно переменная удаляется.

Может быть сделать некий конвеер, чтобы не локальную переменную передавать? Как лучше сделать, чтобы избежать такого?

★★★★

Данные выше по стеку текущего потока никуда не деваются. Вероятно где-то идёт работа с данными из другого стека, переменной в куче, которая удаляется, или же чем-то глобальным.

Чтобы такого не было, треду надо передавать все нужные данные в виде копий, либо ждать его завершения, либо не удалять данные, которые используются совместно.

Стоит запустить под valgrind да глянуть что идёт не так (смотреть файл report):

valgrind --fullpath-after=$PWD/ --track-origins=yes --track-fds=yes --log-file=report --leak-check=full ./your-app your-args

Можно и санитайзерами, но с ними мороки больше.

xaizek ★★★★★ ()
Последнее исправление: xaizek (всего исправлений: 1)
Ответ на: комментарий от xaizek

С копией тоже самое
Копирую и перадаю так

char buffer[8888];
std::size_t length = line.copy(buffer,line.size(),0);
buffer[length]='\0';
parse(string(buffer)); // parse - это лямбда где и происходит read error


Сейчас нет локальной переменной(только buffer)

gobot ★★★★ ()
Ответ на: комментарий от gobot

Я же говорю, что там копия (в параметре, который тоже локальная переменная) в принципе будет. Дополнительных действий не нужно.

valgrind может показать на участок памяти и сказать, где он был создан и возможно освобождён, и гадать не надо будет.

xaizek ★★★★★ ()

А ты все с языком борешься.

Когда тред каким-то образом убивается

Когда тред завершается принудительно […]

Зачем убивать тред?

в моем случае это в основном при выходе из программы

Он убивается, или присоединяется?

переменная далее идет по цепочке вызовов и в ккакой то момент она удаляется

Регулярке на вход подается line, или все же несколько строк?

Ведь на момент вызова regex_search() переменная существовала?

Где существовала?

Как я понимаю это из-за того что переменная локальная? Когда тред завершается принудительно переменная удаляется.

Локальная для какого треда? В каком треде вызывается regex_search?

Siborgium ★★ ()

Может быть сделать некий конвеер, чтобы не локальную переменную передавать?

Может быть, у вилки как-то зубцы согнуть, чтобы суп не проливался?

Siborgium ★★ ()
Ответ на: комментарий от t184256

Там с разных тредов поступают данные в лямбду... Не только моих, но и других. Это av_log_set_callback(). Если тред не убивать принудительно, а ожидать пока сам завершится, но нормально выходит. Кстати это Exception вызывается. В valgrind не полезу, а то застряну с ним вообще... Какие я могу проверки сделать в самой лямбде? Строка приходит ведь целая, я ее в дебагере вижу, почему ошибка не пойму

gobot ★★★★ ()
Ответ на: комментарий от xaizek

Да, память там освобождается тотально, вообще все удаляется что можно удалить. За тред, который рушит память, я не отвечаю. Как мне можно в самой лямбде проверки сделать? Строка ведь приходит в функцию, а потом куда то исчезает выше по стеку или как? Вот в лямбде она есть(строка), а потом ее раз и нет

gobot ★★★★ ()
Ответ на: комментарий от gobot

Строка ведь приходит в функцию, а потом куда то исчезает выше по стеку или как?

Она не может исчезать выше по стеку, потому что она лежит в стеке лямбды, так как ты передаешь ее по значению.

Siborgium ★★ ()
Ответ на: комментарий от gobot

В valgrind не полезу, а то застряну с ним вообще…

Не хочу учиться, хочу жениться. Я тебе и жену подобрал: @metaprog.

Тебе умные люди пытаются помочь (заметь, даже почти не посылают), но сформулировать проблему ты толком не можешь, тк сам не понимаешь, что происходит, а телепаты в отпуске. У тебя запросили информацию, которая бы помогла участникам треда разобраться, что у тебя происходит, а ты в ответ: «Ну нееееет, это сложна! Просто починить надо, а не отладочные логи собирать!». С таким подходом тебе в Job, может кто-нибудь баксов за 50 возьмётся отладить твой если можно так назвать код.

BackDoorLover ()
Ответ на: комментарий от gobot

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

Так и нужно делать. Или же можно устанавливать некоторую переменную флаг (сигнализирующую треду о необходимости завершиться), которую тред будет проверять постоянно. Доступ к флагу через мьютекс.

rumgot ★★★★★ ()
  1. изучи как работают санитайзеры, они покажут тебе все ошибки

  2. возможно, тебе следует использовать стороннюю библиотеку для regex.

например, https://github.com/hanickadot/compile-time-regular-expressions

возможно в С++23 это будет стандартом: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1433r0.pdf

fsb4000 ★★★★ ()
Ответ на: комментарий от rumgot

Зачем мьютекс? Тут atomic достаточно. Но у него все равно там косяки в кишках, которые он не показывает. Ты ведь не полностью его процитировал:

Если тред не убивать принудительно, а ожидать пока сам завершится, но нормально выходит. Кстати это Exception вызывается.

BackDoorLover ()
Ответ на: комментарий от gobot

Если тред не убивать принудительно

А зачем его вообще убивать? Сообщите что нужно завершить работу. А то кто его знает что будет при незавершённых операциях выделения/освобождения памяти, работы с файлами/потоками и т.п. И как это повлияет на последующие такие же операции? (тот же regexp тупо сбился на освобождении/выделение памяти) В этом случае так же деструкторы С++ не сработают.

Убивать можно только поток не владеющий динамической памятью. Т.е. тот, который держит все данные на своём стеке и ссылается на данные, которые гарантированно его переживут.

debug()

А тут нет общих ресурсов?

Какие я могу проверки сделать в самой лямбде?

Никак особо. Но если обеспечить: правильную работу пар конструкторо/деструктор С++ объектов, правильное владение ресурсами, правильное завершение функций и потоков, правильное освобождение ресурсов и т.п. То всё будет хорошо. В том числе и при Exception.

За тред, который рушит память, я не отвечаю.

А кто тогда за него отвечает? Ну если уж совсем так плохо, то форк+пайпы спасут твой код. Пусть навернётся чужой процесс с которым ты взаимодействуешь через пайп. Тогда у тебя всё ОК, а ОС всё вычистит.

З.Ы.: Видимо не зря «Похожие темы: Rust 0.10 (2014)». Всё намекает на неправильное владение ресурсами. :)

AlexVR ★★★★★ ()
Ответ на: комментарий от gobot

В valgrind не полезу, а то застряну с ним вообще…

О, небеса…

Ты это, если пока и без valgrind не застрял, может воздержался бы тогда и от создания темы? Попробуй пока то да сё, а как застрянешь, вернёшься, мы тебе кое-что толковое присоветуем (пока пусть будет сюрпризом).

t184256 ★★★★★ ()

Зумеры-программисты - это как морские свинки… Ладно, книжку, ман или cppreference прочитать не в состоянии, но загуглить то уж можно.

anonymous ()

ты правильно понимаешь, что строка скопировалась. проблема в чём-то другом, скорее всего ты сделал неправильный вывод. удалится эта строка только тогда когда лямбда завершится, а не до этого.

вероятно ты прибиваешь тред когда лямбда выполняется и из-за этого возникает ошибка. надо подождать пока лямбда выполнится, а затем уже освобождать память. с лямбдами это никак не связано.

anonymous ()
Ответ на: комментарий от gobot

Там с разных тредов поступают данные в лямбду

лямбда - это не отдельная сущность. это просто функция без имени. если ты вызываешь лямбду в потоке, то она выполняется в этом же потоке.

точно так же как ты бы вызвал функцию f() в потоке, она бы выполнилась в том же потоке. в неё ничего не поступает, ты её вызываешь в том потоке в котором ты её вызываешь и она становится частью потока.

anonymous ()
Ответ на: комментарий от anonymous

лямбда - это не отдельная сущность. это просто функция без имени. если ты вызываешь лямбду в потоке, то она выполняется в этом же потоке.

Ну тут частично правильно. Лямбда реализуется как класс с перегруженным оператором вызова (operator()). Так вот, если лямбда захватывает что-то по ссылке и что-то читает/присваивает по этой ссылке, то при вызове этой лямбды из разных потоков будет … вероятно UB.

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от rumgot

operator() просто инлайнится и ничего не вызывается. но как она реализуется ему знать пока не нужно, он это всё просто понимает не правильно. «из разных потоков в эту лябмду приходят строки» - это не правильно и оператор() тут ни при чём. «в разных потоках вызывается эта лямбда» - вот это будет правильно. теперь он может осмыслить то, что он хочет сделать и сделать это корректно.

anonymous ()
Ответ на: комментарий от fsb4000

а ещё это могут быть разные лямбды, то есть копии, а не одна лямбда, если они передавались по значению в разные потоки…

Так это и будут копии. Но в контексте вопроса о захвате по ссылке - это не важно. Все равно ссылаться все будет на один объект.

rumgot ★★★★★ ()
Ответ на: комментарий от anonymous

operator() просто инлайнится и ничего не вызывается

Хорошо, инлайнится? Что значит формулировка ничего не вызывается?

но как она реализуется ему знать пока не нужно

Нужно понимать, что в рассмотрении вопроса о вызове лямды в нескольких потоках, может быть опасность когда есть захват по ссылке.

rumgot ★★★★★ ()
Ответ на: комментарий от fsb4000

это ему тоже пока знать не надо.

я понял в чём проблема, вы все думаете, что лямбды - это не анонимные функции, а некие объекты. они действительно есть некие объекты, но это детали реализации. проблема в том, что разработчики лямбд врядли предполагали, что с ними будут работать как с объектами, все же думали, что ними будут работать как с функциями.

объектность лямбд в с++ - это нерелевантная информация, про это лучше забыть. и это, @gobot, если тебе уже 3 человека 5 раз сказали про валгринд, то может стоит прислушаться.

anonymous ()
Ответ на: комментарий от gobot

Участок памяти, адрес? Он у меня есть, а что с ним делать?

Надо определить на стеке он или в куче, размер участка, что там было раньше. Это делает valgrind. Он по бинарнику увидит много чего, а мы здесь без исходников особо не разберёмся.

Да, память там освобождается тотально, вообще все удаляется что можно удалить.

Может даже больше чем можно, вот и идёт что-то не так. Можно попробовать освобождение закомментировать, но я не вижу смысла так гадать, это просто не эффективно.

xaizek ★★★★★ ()
Ответ на: комментарий от anonymous

я понял в чём проблема, вы все думаете, что лямбды - это не анонимные функции, а некие объекты. они действительно есть некие объекты, но это детали реализации

Это реализация, да, но не та реализация, которая называется implementation defined.

Реализация лямбд частично описывается стандартом, вот например пункт 1 (N4659 (C++17)):

The type of a lambda-expression (which is also the type of the closure object) is a unique, unnamed non-union class type, called the closure type, whose properties are described below.

Далее там описано, что лямбда имеет operator() и прочее.

разработчики лямбд врядли предполагали, что с ними будут работать как с объектами, все же думали, что ними будут работать как с функциями

Ну я бы так не утверждал. Лямбда - это то чем она и является - объектом с перегруженным operator(). Если нет списка захвата - то это объект без членов и его можно привести к обычному указателю на функцию. А вот со списком захвата так уже не прокатит. Также лямбда может хранить состояние и менять его. Поэтому все-таки я бы относился к лямбде как к объекту.

rumgot ★★★★★ ()
Последнее исправление: rumgot (всего исправлений: 2)
Ответ на: комментарий от rumgot

Что значит формулировка ничего не вызывается?

ну просто заинлайнит лямбду как любую функцию, возьмёт и встроит в тело потока и всё. не будет никаких захватов, не будет никаких вызовов лямбд, будет просто стоять ссылка («захваченая») и дальше сразу код лямбды.

я думаю его затруднение где-то рядом. он в лямбды что-то там отправляет, а компилятор наверное взял и заинлайнил лямбду как он это и должен делать.

anonymous ()
Ответ на: комментарий от rumgot

Далее там описано, что лямбда имеет operator() и прочее.

это по-моему очень зря. во-первых, есть лябда, а есть замыкание для этой лямбды - это две разные вещи, и мне кажется, что стандарт всё таки говорит об этом, а не о том, что лямбды - это объекты. во-вторых, очень плохо, что люди читают стандарт чтобы найти описание лямбда. лямбда - это анонимная функция, всё, конец предложения.

это конечно как-то не очень. горе от ума какое-то, слишком много написали в стандарте.

anonymous ()
Ответ на: комментарий от anonymous

Когда на физфаке нам читали курс ММФ (методы математической физики) мы называли его «наука о лямбдях». Потом мы на студенческом форуме замутили опрос и ещё по факультету ходили и у всех девушек спрашивали: «Как бы ты отреагировала, если бы тебе сказали, что ты отъявленная лямбдь?». Почему-то все обижались, наверное, не любят математическую физику.

Но что-то я оффтоплю, я хотел написать, что ТСу ненужно заморачиваться с лямбдями. Пусть попробует убрать лямбдь и написать обычную функцию.

BackDoorLover ()
Ответ на: комментарий от BackDoorLover

пожалуй соглашусь. но зато сколько тс уже знает: все виды сегфолтов, потокобезопасность, и про реализацию лямбдей тоже что-то слышал. ну круто же, это же уже программист.

потом ещё придёт такой: а хотите я вам расскажу как в с++ работают лямбды? а ему такие: лямбды в с++ работают ох*ительно, о чём тут говорить.

anonymous ()
Ответ на: комментарий от anonymous

есть лябда, а есть замыкание для этой лямбды

Ок. Тут согласен.

лямбда - это анонимная функция, всё, конец предложения

Ну как бы этого достаточно до поры. А вот захочешь ты првести лямбду со списком захвата(ну ок, переменную типа замыкания лямбды) указателю на функцию - а компилятор такой: хер тебе. Вопрос почему так? Вот, если не учитываешь детали реализации - то тут нужно просто запоминать (зубрить), что так нельзя. А в случае, когда все таки почитываешь иногда стандарт, то логично и понятно, почему так. Или например, почему в лямбде без mutable нельзя изменять захваченный копированием оъект.

rumgot ★★★★★ ()