Подтвердить что ты не робот

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

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

Пример:

int main ()
{
    a ();
    b ();
    c ();
    return 0;
}

a () 
{
   d ();
   e ();
}

b ()
{
   e ();
   f ();
}

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

подсказки?

4b9b3361

Ответ 1

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

Как я подхожу к этому (я действительно реализовал все это один раз, когда мне пришлось отлаживать инструменты, необходимые для сборки gdb для новой архитектуры):

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

Используйте syscall ptrace для создания точки останова в функции main. Если вам повезет, ваша система имеет средство ptrace для создания контрольных точек и ведения бухгалтерии в ядре. Если это не так, вам нужно выяснить инструкцию точки останова для вашей архитектуры процессора и реализовать контрольные точки самостоятельно.

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

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

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

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

Ответ 2

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

У вас есть только два способа сделать это во время выполнения, и оба они составляют код приложения:

Инструмент исходный код

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

Вы можете сделать это полностью автоматизированным способом, используя Программу преобразования программ (PTS), которая может анализировать исходный код для создания АСТ, изменять АСТ и затем восстановить источник. Часто PTS позволяет вам записывать изменения как шаблоны исходного уровня формы, "если вы видите это, замените его на это". (Нет, регулярные выражения не могут этого сделать).

Расширенный пример того, как это можно найти в моей технической статье, Отраслевое покрытие для произвольных языков. В документе показано, что такое PTS, а затем показывает типичные преобразования инструментов.

Как сделать это "правильно" для языка, такого как C, может быть сложно, потому что вам нужно как основа, полный C-парсер, который является основным техническим достижением в своем собственном праве. Лучше всего получить такого зверя в заполненной форме (некоторые из PTS имеют это, считают Concinnelle или DMS), или вы никогда не сможете обойти часть инструментария. (Еще один ответ указывает на то, что GCC имеет большую часть встроенной функциональности инструментария).

Разумно, стоимость исполнения дополнительного инструментария довольно скромная (10-30%). Эта концепция была использована для реализации инструментов тестирования и временных профилей, которые работают, отслеживая динамический набор вызовов, который, как представляется, именно то, что вы хотите.

Инструмент объектного кода

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

Поскольку вы не можете разумно изменить процессорный чип, вам придется либо:

  • изменить объектный код (используя эквиваленты объектных кодов программных преобразований, см. предыдущий параграф для понятий или просмотреть инструменты, такие как PIN и Valgrind, для деталей реализации),

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

Реализация этих для реальных наборов команд усложняется самими наборами команд (набор инструкций Intel X86 огромен), форматы объектных файлов и т.д. Не ожидайте, что этот путь будет проще, чем исходный путь к инструментарию; просто ожидайте, что у него будет другой набор проблем.

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

Еще одна сложность с инструментами объектного кода заключается в том, что трудно определить, была ли выполнена функция foo, которая была встроена, ( "покрыта" ). У исходной инструментальной схемы эта проблема не возникает, потому что foo получает инструментарий, прежде чем он будет скомпилирован/вложен, поэтому инструментарий также встроен.

Темы

Обработка потоков - это ортогональная проблема. В каждом месте, где вы записываете факт X-calls-Y, вы просто присоединяете идентификатор потока (или его эквивалент) T, полученный из ОС, для создания X-calls-Y- в-Т. Из вашего вопроса неясно, почему вы хотите это сделать.

Статический анализ

Вы можете решить пропустить все проблемы реализации runtime и построить/получить статический анализатор, который может создать граф вызовов. Получение этих данных для C возможно с помощью Clang или DMS. Построить его сами, вероятно, сложно; теперь вам нужен полный анализатор C, полный поток данных и анализ точек и построение графика вызовов. Детали не будут вписываться в этот параграф.

Ответ 3

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

В Linux попробуйте взглянуть на то, как работает Pin.

В Windows просмотрите Detours.

Ответ 4

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

Вот ссылка для учебника: http://balau82.wordpress.com/2010/10/06/trace-and-profile-function-calls-with-gcc/

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

Затем вам необходимо перевести адреса в имена функций (которые могут выполняться с учетом номеров строк в файле исходного кода). Используя инструмент GNU binutils addr2line, вы можете преобразовать номера to- и from-address в строки, учитывая, что двоичный файл был скомпилирован с информацией об отладке (gcc -g ...).

Очень быстро начать работу с инструментами, используя этот подход.

EDIT:

Если у вас нет источника и только двоичные файлы под рукой, вы можете попробовать разобрать или разбор бинарных файлов на сборку. Таким образом, вы можете разделить программу на логические блоки, которые перескакивают в/из. Названия функций не могут быть легко восстановлены без исходного кода, поэтому вы можете рискнуть только отслеживать прыжки в логических блоках, в которые вы ранее разделили программу. Это то, что несколько отладчиков делают AFAIK. Я думаю, OllyDbg (только для Windows) и IDA pro может дать визуальную диаграмму потока сортировки или показать цветную блок-диаграмму или что-то в этом роде. Вам действительно нужно много работать без источника, тогда как если у вас есть исходный код, у вас есть все возможности делать именно то, что вы хотите. Добавьте только один шаг между write code -> compile -> execute, чтобы он стал write code -> insert instrumentation -> compile -> execute.

Ответ 5

Трудоемким способом является вставка printf в качестве первой и последней строки функции. Или с помощью отладчика. Проблемой могут быть отладчики, которые устанавливают все переменные в 0/null, "решая" ошибки в коде.

Мое решение было используя ctrace