Вопрос

Мне нужен хороший ответ в стиле Stack Overflow на первый вопрос в старой записи блога. Размер кода C++, что я повторю ниже:

Мне бы очень хотелось какой-нибудь инструмент (в идеале, основанный на G++), который показывает мне, какие части скомпилированного/связанного кода генерируются из каких частей исходного кода C++.Например, чтобы увидеть, создается ли экземпляр конкретного шаблона для сотен различных типов (это можно исправить с помощью специализации шаблона), не слишком ли встраивается код или являются ли определенные функции больше, чем ожидалось.

Это было полезно?

Решение

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

Сначала кое-что, что вы, возможно, уже знаете:

Команда addr2line принимает адрес и может сообщить вам, где находится исходный код, который реализует машинный код.Исполняемый файл должен быть создан с использованием символов отладки, и вам, вероятно, не захочется его сильно оптимизировать (в любом случае -O0, -O1 или -Os, вероятно, настолько высоки, насколько вам хотелось бы поначалу).У addr2line есть несколько флагов, и вам стоит прочитать страницу руководства, но вам обязательно нужно будет использовать -C или --demangle, если вы хотите видеть в выводе имена функций C++, которые имеют смысл.

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

Теперь, что вы хотите с этим сделать:

Вам нужно, чтобы objdump сообщал вам адрес и размер раздела .text.Здесь находится настоящий исполняемый машинный код.Есть несколько способов сделать это, но самый простой (по крайней мере для этого), вероятно, для вас:

objdump -h my_exe | grep text

Это должно привести к чему-то вроде:

 12  .text       0000049  000000f000  0000000f000 00000400  2**4

Если бы вы не использовали grep, это дало бы вам заголовок вроде:

Idx  Name        Size     VMA         LMA         File off  Algn

Я думаю, что для исполняемых файлов VMA и LMA должны быть одинаковыми, поэтому не имеет значения, какой из них вы используете, но я считаю, что LMA — лучший вариант.Вам также понадобится размер.

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

addr2line -e my_exe <address>

Результатом этого будет путь/имя файла, двоеточие и номер строки.Если бы вам нужно было подсчитать появление каждого уникального пути/файла:числа, вы могли бы посмотреть на те, которые имеют наибольшее количество.Перл хэши с использованием пути/файла:num в качестве ключа и счетчика в качестве значения будут простым способом реализовать это, хотя есть более быстрые способы, если вы обнаружите, что они работают слишком медленно.Вы также можете отфильтровать вещи, которые, по вашему мнению, не нужно включать заранее.Для отображения результатов вы можете отфильтровать разные строки одной и той же функции, но вы можете заметить, что разные строки внутри одной функции имеют разные счетчики, что может быть интересно.В любом случае, это можно сделать либо заставив addr2line сообщать вам имя функции, либо используя objdump -t на первом этапе и работая по одной функции за раз.

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

Если вы не знали, objdump и addr2line взяты из Бибиновые утилиты GNU пакет, который включает в себя несколько других полезных инструментов.

Другие советы

Если вы хотите найти источники раздувания кода в вашем коде C++, я использовал для этого «nm».Следующая команда выведет список всех символов в вашем приложении с самыми большими фрагментами кода и данных вверху:

nm --demangle --print-size --size-sort --reverse-sort <executable_or_lib_name> | less

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

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

"Используется вместо -c, чтобы создать исходный файл ассемблера с расширением .s вместо объектного файла.Это может быть полезно, если вам нужно изучить сгенерированный ассемблерный код."

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

Я не знаю, как сопоставить код->сгенерированную сборку в целом.

Для создания экземпляров шаблона вы можете использовать что-то вроде «strings -a |grep |sort -u|gc++filt», чтобы получить приблизительное представление о том, что создается.

Два других пункта, которые вы упомянули, на самом деле кажутся довольно субъективными.Что такое «слишком много» встраивания?Вы обеспокоены тем, что ваш двоичный файл раздувается?Единственное, что нужно сделать, это зайти в gdb и дизассемблировать вызывающую программу, чтобы посмотреть, что она сгенерировала, вообще нечего проверять на «чрезмерную» встраивание.

Что касается размера функции, мне снова интересно, почему это важно?Вы пытаетесь найти код, который неожиданно расширяется при компиляции?Как вообще определить ожидаемый размер инструмента для проверки?Опять же, вы всегда можете скрыть любую функцию, которая, как вы подозреваете, компилируется, чтобы получить гораздо больше кода, чем вам нужно, и точно увидеть, что делает компилятор.

В Visual C++ именно для этого и предназначены файлы .PDB.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top