Содержание к диссертации
Введение
Глава 1 Анализ существующих моделей надежности программного обеспечения 10
1.1. Основные положения теории надежности программного обеспечения 10
1.2. Особенности применения теории надежности для банковских тиражных программных систем 12
1.3. Классификация моделей надежности 15
1.4. Анализ моделей типа «черный ящик» 22
1.4.1. Экспоненциальный класс моделей 22
1.4.2. Гамма- и Вебула-классы моделей 34
1.4.3. Модели категории бесконечных отказов 39
1.5. Анализ моделей типа «белый ящик» 44
1.6. Анализ статических моделей надежности 46
1.7. Обзор используемых в исследовании технологий 51
1.7.1. Метрики кода программного обеспечения 51
1.7.2. Инструментирование исходного кода 52
1.7.3. Обнаружение дефектов с помощью анализа исходных кодов 56
1.7.4. Тестирование программного обеспечения 58
1.8. Выводы по главе 58
Глава 2 Построение модели надежности на основе нейросетей 60
2.1. Основные принципы модели 60
2.2. Методы реализации предложенных принципов 64
2.2.1. Общий вид разрабатываемой модели 64
2.2.2. Структурный взгляд на программную систему 65
2.2.3. Функциональный граф и иерархия системы 67
2.2.4. Мера связи функций в функциональном графе 69
2.2.5. Меры связи функций с функциональными блоками 70
2.2.6. Меры связи блоков высшего уровня в иерархии системы 71
2.2.7. Нейросеть изменения , 72
2.2.8. Сложность функций 75
2.2.9. Адаптация модели 16
2.2.10. Требования к процессу разработки 76
2.3. Архитектура нейросети изменений 77
2.3.1. Модель искусственного нейрона 78
2.3.2. Выбор архитектуры нейронной сети 80
2.3.3. Выбор алгоритма обучения нейронной сети 89
2.3.4. Выбор числа скрытых слоев и нейронов в них 95
2.4. Простейшая модель отказов для единичного изменения 99
2.4.1. Структура системы 99
2.4.2. Разработка системы 101
2.4.3. Оценка внесенного изменения 102
2.4.4. Распространения отказов по функциональному графу 102
2.4.5. Распространение отказов верх по иерархии 104
2.5. Расширение модели для многих изменений 105
2.5.1. Цепочка отказов 105
2.5.2. Добавление новых входов к нейросети изменений 106
2.5.3. Раздвоение модели 108
2.5.4. Реализация новых функций '. 109
2.7. Выводы по главе : 109
Глава 3 Разработка программного комплекса «Reliability Calculator» 111
3.1. Этапы применения комплекса «Reliability Calculator» Ill
3.2. Архитектура комплекса «Reliability Calculator» 113
3.2. Сущности и отношения в предметной области 115
3.3. Функции программного комплекса 119
3.3.1. Редактирование данных системы 120
3.3.2. Инструментирование исходного кода исследуемой программной системы 122
3.3.3. Загрузка результатов выполнения инструментированного кода. 123
3.3.4. Измерение сложности функций 125
3.3.5. Загрузка результатов измерения сложности функций 126
3.3.6. Загрузка сведений о запросах 126
3.3.7. Обучение нейросети изменения 129
3.3.8. Прогнозирование отказов по функциям 133
3.3.9. Прогнозирование отказов по функциональным блокам 134
3.3.10. Просмотр функционального графа 134
3.4. Выводы по главе 135
Глава 4 Применение ПК «RelCalc» для моделирования надежности крупной банковской тиражной программной системы 136
4.1. Характеристики исследуемой программной системы 136
4.2. Допущенные упрощения 137
4.3. Обучение модели 138
4.3. Тестирование модели 144
4.3.1. Применение первого класса нейросетей 145
4.3.2. Применение второго класса нейросетей 146
4.3.3. Применение третьего класса нейросетей 146
4.3.4. Прогнозирование числа отказов в функциональных блоках системы 148
4.4. Варианты внедре'ния модели 149
4.4.1. Внедрение модели в начале разработки 149
2.6.1. Внедрение модели в устоявшийся процесс разработки 150
4.4. Выводы по главе 152
Заключение 153
Литература
- Классификация моделей надежности
- Обзор используемых в исследовании технологий
- Структурный взгляд на программную систему
- Инструментирование исходного кода исследуемой программной системы
Введение к работе
В последние десятилетия наблюдается значительный рост сложности программного обеспечения (ПО). Так, если в начале 1990-х годов программа, состоящая из ЮО'ООО строк представляла собой серьезную разработку, то сейчас часто встречаются программные продукты, состоящие из нескольких тысяч исходных файлов и сотен тысяч функций. При этом файл из ІОО'ООО строк, зачастую, не самый большой и сложный в проекте.
Сложные программные продукты разрабатываются теперь не группой из двух-трех человек, а сотнями программистов, возможно находящихся на значительном расстоянии друг от друга. При этом каждый из них, как правило, занимается только своей частью проекта. Человека, который бы хорошо ориентировался во всем проекте, не существует. Но любому программисту, работающему в крупной команде, приходится иногда заниматься доработкой чужого кода. Этот, а также и многие другие факторы, приводят к тому, что разработать абсолютно надежное программное обеспечение невозможно - отказы программ встречаются часто и, порой, очень мешают работе пользователя.
В качестве примера, как одна ошибка,-допущенная программистом или проектировщиком на этапе разработки, может усложнить жизнь множеству людей, можно привести следующие случаи: американский космический аппарат Mariner 1 отправился 22 июля 1962 по направлению к Венере. Вследствие аппаратного отказа средств связи с ЦУП, корабль перешел на собственную программную систему пилотирования, содержащую дефект. В результате Mariner 1 сбился с курса, и его пришлось затопить в Атлантическом океане. Расследование показало, что в процессе системе навигации была совершена опечатка — при вводе одной из формул был пропущен символ [1]; в 1988-96 гг. генератор случайных чисел Kerberos широко использовался в различных системах шифрования. Предполагалось, что программа должна выбирать ключ случайным образом из многих миллиардов чисел, но генератор случайных чисел выбирал из гораздо меньшего набора численностью примерно в миллион. Как результат, в течение восьми лет любой пользователь мог без труда проникнуть в компьютерную систему, которая использовала модуль Kerberos [2];
1985-87 гг. Несколько человек получили смертельную дозу облучения во время сеансов радиационной терапии с медицинским ускорителем Therac-25. Основанная на предыдущей версии ускорителя, "улучшенная" модель Therac-25 могла генерировать два вида излучения: слабое электронное бета-излучение и нормальное рентгеновское излучение. Еще одно "улучшение" состояло в том, что вместо электромеханической защиты пациента в устройстве была реализована новая программная защита. Обе функции были некорректно реализованы неопытным программистом, результатом чего стали как минимум шесть смертей и огромное количество несмертельных случаев переоблучения [3];
15 января 1990 г. Ошибка в новой версии прошивки междугородних коммутаторов телефонной сети AT&T привела к тому, что коммутатор перезагружался, .если получал специфический сигнал от соседнего кохммутатора. Данный сигнал генерировался в тот момент, когда коммутатор восстанавливал свою работу после сбоя. 15 января 1990 один из коммутаторов подал тот подобный сигнал. Это привело к тому, что через некоторое время уже 114 соседних коммутаторов непрерывно перезагружались каждые 6 секунд, а 60 тыс. человек остались без междугородней связи на 9 часов, пока инженеры не установили на коммутаторы предыдущую версию прошивки [4].
Понятно, что оценка параметров надежности программных систем -необходимость. Такие оценки нужны как пользователям, так и разработчикам.
Особенно остро вопрос надежности встает в условиях разработки банковского тиражного программного обеспечения. Отличительной особенностью подобных программных комплексов является то, что они одновременно разрабатываются множеством программистов, каждая сборка программного продукта должна быть разработана и протестирована в сжатые сроки, а любой отказ может привести к потере средств и репутации банком.
Целью диссертационной работы является разработка и программная реализация математической модели, позволяющей прогнозировать характеристики надежности банковских тиражных программных систем и обладающей свойствами адаптивности, детализации и конкретизации.
При разработке модели были использованы основные положения теории надежности программного обеспечения; математический аппарат и методы теории искусственных нейронных сетей; основные положения и методы теории графов. При разработке программного модуля использовались основные положения теории реляционных баз данных, методы объектно-ориентированного программирования.
Научная новизна работы состоит в использовании качественно нового подхода к моделированию процесса оценки характеристик надежности банковского тиражного программного обеспечения, основанного на использовании нейросетевого аппарата для построения зависимости характеристик надежности от параметров внесенных в очередную сборку исследуемой системы изменений.
Практическую ценность работы составляют:
Методология моделирования оценки характеристик надежности банковского тиражного программного обеспечения.
Программный комплекс «Reliability Calculator», автоматизирующий все этапы применения модели.
8 3. Методика применения данного программного комплекса, полученные в результате практического его применения для моделирования оценки характеристик надежности крупной тиражной автоматизированной банковской системы.
Цель и поставленые задачи определили следующую структуру работы:
В первой главе рассматриваются основные положения и определения теории надежности программного обеспечения. Классифицируются, рассматриваются и анализируются некоторые существующие модели надежности программного обеспечения. При анализе моделей, основное внимание уделяется выделению их достоинств и недостатков. Отдельно рассматриваются некоторые смежные с теорией надежности области течения.
Во второй главе, на основании выводов из первой главы, ставится задача разработать новую модель надежности банковского тиражного программного обеспечения. Описываются основные принципы модели и предлагаются методы их реализации. Рассматривается простейший пример применения модели для прогнозирования числа отказов в программной системе, в которую вносится одно изменение.
Третья глава посвящена описанию разработанного программного комплекса (ПК) «Reliability Calculator», автоматизирующего применение модели. Описывается внутренняя архитектура ПО, использованные при разработке технологии. Рассматриваются все этапы применения ПО для моделирования характеристик надежности банковской тиражной программной системы.
В четвертой главе показываются результаты практического применения разработанного ПО и модели для прогнозирования числа отказов в очередной сборке крупной банковской тиражной программной системы. Делаются выводы о корректности заложенных в модель принципов и работоспособности разработанного ПО. Рассматриваются проблемы, которые могут возникнуть при внедрении модели и программного комплекса.
Результаты исследований позволили сформулировать следующие принципы, выносимые па защиту:
Математическая модель оценки характеристик надежности банковских тиражных программных систем на основе нейросетевых технологий.
Методика прогнозирования характеристик надежности банковского тиражного программного обеспечения, основанная на вычислении числа отказов очередной сборки программы путем анализа внесенных в исходный код изменений.
Алгоритм и структура программного комплекса, автоматизирующего все этапы применения разработанной модели надежности.
Результаты практического применения разработанного программного комплекса и модели для прогнозирования числа отказов в очередной сборке крупной банковской тиражной программной системы.
Классификация моделей надежности
Большинство моделей надежности описывают ее следующими двумя зависимостями: ju(t) - функция среднего значения (mean value function) - имеет смысл ожидаемого числа отказов к моменту времени t; X{t) - интенсивность отказов (failure intensity) - имеет смысл ожидаемого числа отказов в единицу времени в момент времени / и вычисляется как производная функции среднего значения по времени dju(t) dt Кроме того, модели предлагают различные зависимости для следующих функций: г,-(Д/ J thX)- показатель риска отдельного дефекта (defect hazard rate) - это вероятность того, что j-ый дефект проявится в виде отказа в течение времени А/ с момента предыдущего отказа tiA. Для большинства моделей эта величина одинакова для всех дефектов; z(At\tj_\)- показатель риска программы (software hazard rate) вероятность того, что в течение времени Д с момента предыдущего отказа м произойдет еще один отказ; R(At І ґм) - надежность программы - вероятность того, что в течение времени At с момента предыдущего отказа tiA не произойдет ни одного отказа.
На сегодняшний день существует такое огромное число различных моделей надежности ПО, что назвать их точное число не решается ни один исследователь. Однако на первый взгляд можно сказать, что их число измеряется не десятками, а сотнями. Связано такое их многообразие, прежде всего с тем, что невозможно однозначно сказать - какая модель лучше всего подойдет для данной разработки - каждая модель имеет свои достоинства и недостатки и не существует универсальной, подходящей для всех случаев модели.
К отечественным ученым, занимающимися проблемами надежности программного обеспечения, можно отнести, в первую очередь, профессора В.В. Липаева, работы которого посвящены всем аспектам, связанным с надежностью [16] и качеством [17] процесса разработки программного обеспечения, организации документооборота при разработке сложных систем [18], методам тестирования ПО [19] и т.п.. К числу ученых, занимавшихся непосредственно моделированием надежности программ, можно отнести, к примеру, работника НИИСИ РАН С.Г. Романюка, опубликовавшего несколько статей на эту тему [20]; Борисенков СВ. и Воропаев А.П., в соавторстве с коллегами, опубликовали статьи на тему моделей надежности в журнале «Двойные технологии» [21,22],
Большинство из существующих моделей являются разработками ученых США, Японии и Европы, выполненных по заказу военных ведомств или коммерческих организаций этих стран. К числу наиболее известных иностранных специалистов, когда-либо занимавшихся вопросами теории надежности программного обеспечения, можно отнести: профессора, сотрудника исследовательского отдела компании IBM Г. Майерса (Glenford J. Myers), опубликовавшего в 1970-1980-ых годах ряд книг [12,13] по вопросам, связанным с надежностью программного обеспечения; 3. Жилинского (Z. Jelinski) и П. Моранду (Р.В. Moranda), создавших самую известную модель надежности программного обеспечения по заказу ВМФ США [14]; профессора университета Лондона Б. Литтлвуда (В. Littlewood), также разработавшего известную модель надежности [15]; сотрудников компании AT&T М. Лю (Michael R. Lyu) и Дж. Мусу (J. Musa), под эгидой которых в конце 1990-ых годов была собрана команда профессионалов в области теории надежности ПО и, в 1996 году выпущена книга [7], полностью посвященная моделированию надежности ПО, а также многих других.
В связи с большим количеством моделей, возникает необходимость их классификации. В большинстве обзоров моделей, предлагается классификация лишь одной группы моделей - моделей типа «черный ящик» (black-box models). Эта группа моделей определяет надежность программного обеспечения без каких-либо знаний о внутреннем строении, архитектуре программы, основываясь лишь на данных тестирования. Самая известная подобная классификация приведена в [7] и классифицирует модели по следующим признакам: временной шкале (time domain) - для вычисления промежутков между событиями, в модели может использоваться либо календарное время, либо время выполнения программы процессором (execution time); категории (category) - ожидаемое число отказов в бесконечности (то есть limr оа ju(t) ) делит все модели на модели бесконечных отказов (infinite failure models) и модели конечных отказов (finite failure models); типу (type) - тип распределения числа отказов во времени - Пуассона или биномиальное; классу (class) - только для моделей конечных отказов -функциональная форма интенсивности отказов в терминах времени; семейству (family) - только для моделей бесконечных отказов -функциональная форма интенсивности отказов в терминах ожидаемого числа отказов.
Обзор используемых в исследовании технологий
При реализации многих программ, в функциональность которых входит оценка (по некоторым параметрам) или исследование внутренней структуры других программных систем, часто приходится сталкиваться с необходимостью трассировки событий (event tracing). Под трассировкой событий здесь понимается срабатывание триггеров при возникновении в оцениваемой системе некоторых событий. В большинстве случаев, создаются триггеры на вход и выход из функций. Таким образом, например, работают системы оценки производительности системы - так оценивается время выполнения каждой из функций. Далее, полученные результаты анализируются для получения информации об «узких местах» в системе.
Процесс внедрения в чужой исходный код подобных триггеров, называется инструментированием (instrumentation) [44]. Существует несколько способов организации инструментировапия - в зависимости от варианта, триггеры внедряются либо в исходный код исследуемой системы [45], либо в ее объектный код [45,46]. При этом второй вариант, предоставляя большую гибкость и меньшие требования к исследуемой системе, является более предпочтительным.
Естественным методом реализации варианта с внедрением триггеров в исходный код, является вставка в нужные его участки вызовов специальных функций. Однако, выполнение этих действий вручную возможно лишь в небольших по объему программных системах. В средних и, тем более, сложных системах необходимо использование специальных средств автоматизации ипструментирования. В листинге 1.1 показан фрагмент исходного кода исследуемой системы (язык С) до обработки, а в листинге 1.2 - после обработки подобным средством. надежность - как свойство системы выдавать компилируемый исходный код; максимальное покрытие - свойство системы внедрять триггеры в как можно большее число точек исследуемого исходного кода, подходящих под заданные условия.
Основной проблемой создания систем автоматизации инструментирования является поиск оптимальной грани между этими двумя чертами. Одним из методов реализации, является полный грамматический разбор исходного кода исследуемой системы. При этом фактически реализуется базовая часть компилятора - лексический и синтаксический анализаторы. К счастью, существуют специальные средства, позволяющие получать анализаторы на основе грамматики, описанной в определенном формате [47,48,49]. Кроме того, для большинства широко используемых языков программирования, подобные грамматики уже описаны [50,51].
При этом, как правило, при изменениях в синтаксисе языка, для продолжения нормальной работы системы необходимо просто заменить грамматику на более новую, оставив большую часть функциональности без изменений. Подобная реализация обычно позволяет также без особых доработок применять систему для различных компиляторов.
Однако, системы основанные на использовании грамматик, подходят лишь для некоторых языков, грамматики которых хорошо формализованы. Для сложных языков (таких как, например, C++) их применение практически невозможно (так, ни одна из существующих грамматик C++ не поддерживает полностью механизм шаблонов). Создание легко заменяемых и подходящих для нескольких компиляторов грамматик этого языка до сих пор является крайне сложной задачей. Известно, например, что в одном из широко используемых компиляторов C++ - GNU C++ compiler для лексического и синтаксического разбора используется набор процедур, занимающих в общей сложности более 100 000 строк исходного кода. Поддержка подобных объемов исходного кода и его адаптация для понимания исходных текстов для различных компиляторов не представляется возможной.
Еще одним недостатком подхода основанного на применении грамматик, является невозможность его применения для необработанного препроцессором исходного кода. Таким образом, исходный код может обрабатываться только в момент компиляции. При этом возможные ошибки, возникающие в процессе инструментирования должны исправляться вручную, что невозможно в случае пакетной сборки.
Другой вариант автоматизации процесса инструментирования -внедрение триггеров в исходный код на основании лексического и лишь некоторых элементов синтаксического анализа. Так, например, при разборе исходного кода на языке C++, из всего кода будут выделяться лишь интересующие пользователя конструкции - функции. При этом сложность задачи создания системы инструментирования значительно падает - поиск функций в исходном коде не является сложной задачей. Простота подхода позволяет уделить внимание другим элементам разбора - таким как, например, инструментирование кода, не обработанного препроцессором.
Задача поиска функций в исходном коде решена и успешно применяется во многих системах. Наиболее широко этот метод применяется в программах, создающих «индекс функций» для набора исходных кодов с целью последующего быстрого перемещения по ним в редакторе. В качестве примера такой программы можно привести Exuberant Ctags [52].
Структурный взгляд на программную систему
Структура программной системы должна быть строго систематизирована. В частности, вся система должна быть разбита на блоки, сведения о надежности которых могут быть интересны кому-либо из команды, работающей над системой; Однако, как минимум, должны быть сформированы минимальные структурные части системы, составляющие ее исходный код. Так, для программы написанной на языке Си, этими структурными частями будут функции (далее, для краткости, минимальные структурные блоки будут называться функциями). Блоки следующего (более высокого) уровня, таким образом, состоят функций.
В качестве структурных блоков высокого уровня, предлагается использовать иерархическое разбиение системы на: функциональные блоки - части системы, ответственные за выполнение определенной, видимой пользователю функции (например, в текстовом редакторе - открытие документа, установка выравнивания абзаца и т.п.). Теоретически, функциональные блоки могут включать в себя не только функции, но и другие функциональные блоки. Однако, в целях упрощения, будем рассматривать только линейное деление системы на функциональные блоки; подсистемы - части системы, включающие в себя функциональные блоки, выполняющие сродные функции (например, в текстовом редакторе - подсистема работы с файлами, форматирование абзаца и т.п.). Введением этих структурных блоков можно пренебречь; проекты - части системы, представляемые пользователю в виде отдельных, практически независимых частей (так, в офисном пакете подобными частями могут, быть.текстовый и табличный редакторы). Подобное разделение может быть интересно всем участникам процесса разработки - руководитель проекта знает о жизнеспособности проекта в целом. Руководителю разработки интересны параметры надежности в конкретных подсистемах проектов - с помощью этих данных он может принимать решения о направлении усилий команды разработчиков в ту или иную область. Простым программистам полезно знать о количестве ожидаемых отказов в функциях. Кроме того, подобное разделение крайне полезно для определения необходимости усиленного тестирования тех или иных частей программы.
Для наглядности, на Рис. 2.1 приведено возможное (но далеко неполное) разбиение автоматизированной банковской системы «RS-Bank» [66] v.5.1 на проекты, подсистемы, функциональные блоки и функции.
Для того чтобы максимально интегрировать процесс определения надежности с процессами разработки и тестирования программного продукта, необходимо, при обнаружении отказа четко определять - к каким частям системы он относится и какие другие части могут быть затронуты при его возможных проявлениях. Делать это необходимо на всех уровнях - т.е. определять не только подсистемы и функциональные блоки, но и функции, подверженные и затронутые дефектом.
Естественно, что происходить это должно автоматически, поскольку количество функция в крупных системах настолько велико, что выполнять это действие вручную практически невозможно. Кроме того, в больших проектах ручное поддержание иерархии структурных частей системы практически нереально. Поэтому, наиболее приемлемым вариантом является составление функционального графа - графа вызовов функций. Однако, статический анализ исходного кода на предмет построения подобного графа является очень сложной задачей, тем более что решив ее для одного языка программирования, при переходе к другому мы теряем это решение. Более приемлемым способом построения графа будет являться применение инструментирования исследуемого кода (чуть более подробно об этой технике было рассказано ранее в разделе 1.7.2) - метода, когда в начало и конец каждой функции вставляются вызовы специальной функции, которая записывает передаваемые ей аргументы (в нашем случае - название функции, возможно - комментарии или значения ее аргументов) в специальный файл в специальном формате. Далее данный файл анализируется для построения графа вызовов функций (функционального графа).
Кроме использования исключительно для моделирования надежности, функциональный граф может иметь много других полезных применений. Так, часто при исправлении дефекта, программист тратит значительное время на повторение дефекта с целью найти его причину. В случае наличия функционального графа и при условии, что тестировщик получил отказ на специальной сборке, программисту может быть передан трассировочный файл, указывающий на последовательность выполнения функций при отказе. Тогда становится проще найти конечную точку отказа - функцию, в которой находится дефект его вызвавший.
Помимо этого, если к указанным целям трассировки (занесение в файл просто последовательности вызываемых функций) добавить еще и возможность просмотра параметров, с которыми вызваны функции, найти причину отказа (дефект) часто становится возможно даже без. запуска программы.
Инструментирование исходного кода исследуемой программной системы
Как уже говорилось ранее, инструментирование, в данном случае, предназначено для построения функционального графа исследуемой программной системы. В частности, в отношении данной функциональности, система должна: предоставить, при необходимости, DLL с функциями, необходимыми для вывода информации о внутренней структуре исследуемой системы в текстовый файл; изменить исходный код исследуемой системы - добавить необходимые для создания функционального графа вызовы функций.
В реализованной программной системе модели, инструментирование происходит в два этапа - через промежуточный вывод в текстовый файл и последующую его загрузку в БД. Прямая выгрузка информации в БД при выполнении исследуемой программы отвергнута еще на ранних этапах разработки, как крайне неэффективная.
Подсистема инструментирования состоит из двух модулей. Модуль instrumCmn.dll представляет собой переработанную под нужды проекта утилиту Exuberant Ctags [52], написанную на языке С. Для совмещения этого модуля и остального прикладного кода, написанного на языках платформы .Net, на языке Managed C++ написан модуль relMark.dll, являющийся, по сути, шлюзом.
При создании системы «RelCalc» была поставлена задача инструментирования исходного кода на языке C++. Поскольку в этом языке существует возможность выхода из функции из любого места ее кода (с помощью оператора return), вариант с добавлением строк кода в начало и конец функции, не подходит. Вариант с добавлением строк кода перед каждым оператором возврата, также не подходит по причинам, описанным в 1.7.2. В результате, было решено в начало каждой функции вставлять код, создающий объект класса CMarkDllJVIarkFun, конструктор которого принимает два аргумента - название функции и относительный путь к исходному файлу, в котором находится определение этой функции. В конструкторе класс должен быть запрограммирован код, выводящий в трассировочный файл сведения о том, что программа зашла в указанную функцию, а деструктор (который выполнится независимо от места и момента выхода из функции) - код, сообщающий о том, что программа покинула указанную функцию-Объявление класса CMarkDllJVIarkFun должно быть помещено в заголовочный файл, который включается во все инструментированные файлы. К примеру, это может быть прекомпилированный заголовочный файл (precompiled header). Пример кода, до и после инструментирования модулем instrumCmn.dll, показан в листингах 3.1 и 3.2.
В результате сборки инструментированного исходного кода, и последующего выполнения получившихся исполняемых файлов, формируется трассировочный файл, В этом файле, в определенном формате, записан порядок вызова функций исследуемого исходного кода. Формат трассировочного файла может быть любым и определяется конкретной реализацией класса CMarkDlIJVTarkFim, однако реализованный модуль loadRS.dll (написан на языке С#) рассчитан на формат файла входных данных, пример которого показан в листинге 3.3.
Видно, что файл разделен на три колонки - Category, Detail и Message (ширина колонок не ограничена). В колонку Category записывается название трассируемой функции, предваренное обратным слешем. Колонка Detail имеет значение 1 для события «вход в функцию», и 2 - для выхода из нее. В колонку Message записывается относительный путь до файла, содержащего трассируемую функцию, а также наименование функционального блока, который выполняется в данный момент (после символа ).
В приведенном выше примере трассировочного файла, последовательность выполнения программы такая: 1. вызывается функция findj-ec из файла гес.ерр; 2. вызывается функция RFrec из файла гес.ерр; 3. вызывается функция KF_0 из файла rec_kf.cpp; 4. заканчивается выполнение функции KF_0 из файла rec_kf.cpp; 5. заканчивается выполнение функции RF_rec из файла гес.ерр; 6. заканчивается выполнение функции find_rec из файла гес.ерр. При выполнении процедуры загрузки трассировочного файла, необходимо указать собственно путь к трассировочному файлу и сборку, к которой он относится.