LINUX.ORG.RU

Поиск вызова конструкторов копирования и операторов копирования всех объектов

 


1

7

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

Куда смотреть? Valgrind (callgrind)? Что ещё есть? Статические анализаторы такое умеют?

UPD: меня интересуют конструктор копирования и оператор копирования.

★★

Последнее исправление: anonymous00 (всего исправлений: 2)

ctrl+alt+h в eclipse нажми

anonymous
()

Мне казалось, любое IDE имеет аналог find usage

XMs ★★★★★
()

Для однопоточного приложения лучший вариант gperf если в рантайме. Ещё вариант - собрать coverage репорт.

Но это всё в рантайме. В статике - надо уточнить, интересуют ли тебя тривиальные сгенерированные конструкторы?

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

С этими rvalue и move semantics не всё так просто. Глазам уже не доверяю, взгляд замыливается иногда.

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

Для однопоточного приложения лучший вариант gperf если в рантайме. Ещё вариант - собрать coverage репорт.

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

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

Ну, оптимизировать, то что не вызывается и смысла практически не имеет (если только по размеру сегмента кода). Т.е. всё равно нужно будет написать тесты и бенчмарки, а иначе - как понять, что оптимизация произведена а рефакторинг ничего не сломал?

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

Если тебя интересует работа с памятью в первую очередь, то видимо нужно использовать что-то типа google perf tools или heaptrack.

Если, общая производительность системы - сэмплерные профайлеры (например oprofile и perf). Тот же oprofile - умеет рисовать дерево вызовов, но надо понимать его механизм работы, что бы понимать чего в этом дереве окажется а чего нет.

Если время критического пути, то в зависимости от задачи, если задача вычислительная и без i/o - сгодится callgind, иначе, я не знаю другого способа(если кто расскажет - буду признателен) кроме как инструментальные профайлеры вроде easy_profiler или самопального велика.

Ещё, можно подумать на тему скриптов на gdb, типа таких.

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

А таки не расскажешь, с помощью какого инструмента (кроме намагниченной иглы и твёрдой руки), можно организовать:

смотреть прямо таки вызовы ф-ии по адресу в бинарнике

pon4ik ★★★★★
()

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

private:
    MyClass(const MyClass&) {}
    MyClass& operator=(MyClass) { return *this; }

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

Упомянутые поиск 'Usage' часто не работает нормально.

Deleted
()
Последнее исправление: Deleted (всего исправлений: 3)
Ответ на: комментарий от anonymous

Насколько я видел в libclang всё про компиляцию и ast traversal. Это конечно же, будет работать, но предложил ты другое.

А вот так чтобы прочитать дебажную инфу и построить колграф?

pon4ik ★★★★★
()

Сишные макросы и рефакторинг очень не дружат. Поэтому

1) текстовый поиск по сорцам

2) убирание конструктора (в приватные или = delete) и компиляция программы во всех возможных сочетаниях систем/опций - ошибки компиляции покажут «нормальные» использования.

Но с неявными преобразованиями типов при копировании как бороться - я хз. Это очень говнистая «фича» плюсов.

Ну и можно принтовать стэк в конструкторе или брэйкпойнт в дебаге. Это если число сценариев прогона небольшое и сильно нужен контекст выполнения видеть.

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

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

Так ты посмотришь зачем нужны coverage анализаторы или дальше будешь чушь пороть? Ну там почитаешь чего-нибудь про сложность затрагивания всех бранчей выполнения в тестовых условиях и всё такое.

... а иначе - как понять, что оптимизация произведена а рефакторинг ничего не сломал?

Для начала нужно определить какие виды оптимизаций допустимы (можно ли делать move, разделяемые внутренние состояния и т.д.), а для этого нужно смотреть как класс/модуль/ф-я используется в коде. Потом уже на новый контракт можно делать UT.

А таки не расскажешь, с помощью какого инструмента (кроме намагниченной иглы и твёрдой руки), можно организовать:

Берёшь gdb, смотришь адрес конструктора и ищешь его в коде. Далее этим же gdb (gdb info symbol или addr2line) можно найти кусок c++ кода где происходит вызов.

Можно проще, objdump -d и grep по call с нужной ф-ией. Под капотом gdb спрятана возьня с DWARF которая используется для соотнесения адреса call инструкции с куском c++ кода.

Если нужно разово и вызовов мало, то пойдёт objdump с ручной обработкой. Если объём выдачи большой, то будет стоить небольшого скрипта для gdb/addr2line. Мб есть какие-то готовые тулзы, но и напильником задача решается не очень сложно.

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

Но с неявными преобразованиями типов при копировании как бороться - я хз. Это очень говнистая «фича» плюсов.

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

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

но предложил ты другое

o_O Я предложил именно обход AST с проверкой всех вызовов функций.

прочитать дебажную инфу и построить колграф?

Чот сомнительно, что восстанавливать по бинарнику исходник будет проще.

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

Так-то хочется видеть использование конструкторов/операторов и стандартных классов, чаще всего std::string и при пользовании контейнерами.

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

Зачем тогда людям мозги е...шь, для общей оптимизации пойдёт grpof или другой оптимизатор с детальной статистикой по колчейнам.

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

Просто ты же понимаешь, что шланг не работает с

вызовы ф-ии по адресу в бинарнике

?

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

Явный обязательный static_cast<A>(b) или вызов A(b) облегчил бы чтение и убрал бы необходимость париться со словом explicit. Оно бы вообще стало ненужным.

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

addr2line

О! Круто не знал, спасибо.

Так ты посмотришь зачем нужны coverage анализаторы или дальше будешь чушь пороть? Ну там почитаешь чего-нибудь про сложность затрагивания всех бранчей выполнения в тестовых условиях и всё такое.

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

Но автоматических инструментальных профайлера(времени выполнения) для нативного кода я знаю всего два - gperf и callgrind и у обоих есть недостатки. Если расскажешь ещё про какой - с интересом послушаю.

Для начала нужно определить какие виды оптимизаций допустимы (можно ли делать move, разделяемые внутренние состояния и т.д.), а для этого нужно смотреть как класс/модуль/ф-я используется в коде. Потом уже на новый контракт можно делать UT.

Для начала - надо найти узкое мест[оа] же...

Upd: лол, всю жизнь думал, что gprof инструментальный, ан нет, он тоже сэмплерный. Нафига он тогда нужен на сегодняшний день я не совсем понимаю :(

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

Явный обязательный static_cast<A>(b) или вызов A(b) облегчил бы чтение

+16 символов на ровном месте чтение точно не облегчают...

и убрал бы необходимость париться со словом explicit

а это просто какое-то шизофреническое раздвоение личности - с многочисленными +16 символами он не парится, а разовые 8 прямо доставляют кучу гемора.

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

gprof инструментальный, ан нет, он тоже сэмплерный

Точнее, он блин и инструментальный и сэмплерный.

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

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

f(«sadas asd as das d asd as da s»); - типичный случай, где автор строки и не догадывается, что тут будет на каждый вызов создан временный std::string, если есть только строкое определение f(). А с пользовательские числовые типы и забытый explicit добавляют веселья.

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

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

де автор строки и не догадывается

если автор - программист на С++, то догадывается. Любой конструктор с одним аргументом нужно делать explicit по умолчанию, это хорошая практика, описанная во многих статьях. Можно подумать, что это костыль, но на самом деле, это не так, это в рамках идеологии С++ (конструктор - такой же метод, а для метода поведение 'implicit' очень интуитивно, поэтому оно по дефолту. конструктор с одним параметром - особый случай, и его надо подчеркнуть - ставим «explicit». нет проблем).

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

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

Читать то, что в коде выражено явно, как-то проще, чем догадываться,

Если будешь писать код как удак, то он и будет состоять в основном из «явного» мусора вроде кастов. И вместо того, чтобы уделять внимание логике приложения разработчик будет урабатываться разбором паразитного кода пропуская настоящие баги.

f(«sadas asd as das d asd as da s»); - типичный случай, где автор строки и не догадывается, что тут будет на каждый вызов создан временный std::string, если есть только строкое определение f()

С какого это уя не догадывается? Начиная с с++11 это типичная практика - одновременно и метод понимающий move семантику, и конструктор объекта если вдруг его нужно приготовить. Если автор f(...) решил что ф-я принимает только std::string, а не char*, то всё равно придётся сделать std::string.

Проблема возникает когда в приложении бывают char* со строками для std::string и какие-то другие, например специальные бинарные данные. Из-за неявных преобразований можно случайно засунуть строку в неправильный метод. Чтобы избегать подобных проблем либо конструктор делают explicit, либо char* заворачивают в специальный объект. Эта проблема в основном характерна для PODов

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

Для начала - надо найти узкое мест[оа] же...

И находишь их, в частности, из контракта приложения. Если известно что приложение где-то должно отсорировать дохринильён GiB, то нужно сразу брать алгоритм не хуже N*Log(N), а не ждать когда проблема всплывёт в продакшене или в тестах (в тестах вообще может не оказаться дохринильёна GiB). Короче, ты запарил тупить.

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

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

в тестах вообще может не оказаться дохринильёна GiB

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

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

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

а мне легко. я вообще лучше тебя. вот смотри: мой пароль от аккаунта на ЛОРе - kwdU9nbi. меня не забанят, а тебя за такойже фокус навеки отправят на помойку ЛОРа. живи с этим

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

конструктор - такой же метод, а для метода поведение 'implicit' очень интуитивно

А? Где это еще в плюсах есть неявные вызовы методов?

Это в рамках разве что идеологии запутывания. Логично было бы иметь ключевое слово implicit, или другой способ явно создать приведение типов по умолчанию. Это тот случай, когда афтар не сразу понял что наделал.

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

Если автор f(...) решил что ф-я принимает только std::string, а не char*, то всё равно придётся сделать std::string.

И каждый раз копировать байтики и делать аллокацию только потому, что автор функции и вызыватель функции - последователи бест практисыз. Два ордена Ленина этой фиче!

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

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

А что там интуитивно - на практике это только проблемы приносит. Так что фича - говно. Жаль в компиляторах выключить нельзя.

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

И каждый раз копировать байтики и делать аллокацию только потому, что автор функции и вызыватель функции - последователи бест практисыз

А при чем тут вызыватель? И что ты предлагаешь, если функция принимает string, а у тебя char*?

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

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

Ты совсем отсталый в развитии и не понимаешь разницу между фичей языка и дизайном API?

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

а мне легко

Ну поделись тайным знанием то.

меня не забанят, а тебя за такойже фокус навеки отправят на помойку ЛОРа

Как будто это что-то трагическое...

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

Как будто это что-то трагическое...

главное, что ты сам понимаешь что ничего страшного что ты ничтожество, тогда это не трагедия

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

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

pon4ik ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.