Предисловие В очередном сборнике Трудов Института системного программирования РАН представлены девять статей сотрудников ИСП РАН, посвященных различным вопросам теории и практике системного программирования. Статья В.В. Рубанова «Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки» посвящена методам описания расширяемых встраиваемых систем и построения соответствующих инструментов кросс-разработки (симулятор, ассемблер, дисассемблер, компоновщик, отладчик и т.п.). Рассматривается общий процесс проектирования встраиваемых систем и описывается роль инструментария кросс-разработки. Обсуждаются языки для описания моделей встраиваемых систем и методы получения инструментария кросс-разработки на основе таких описаний. Проводится сравнительный анализ рассмотренных решений. В статье А.К. Петренко, О.Л. Петренко и В.В. Кулямина «Роль научных организаций в подготовке ИТ-специалистов» рассматривается проблема подготовки специалистов высокой квалификации в области информационных технологий (ИТ). Предлагается использовать для решения проблем современного ИТ-образования подход, основанный на принципах «системы Физтеха», прежде всего — ресурсы научных и научно-производственных учреждений. В рамках такой системы студенты получают возможность участвовать в работе по специальности совместно с ведущими специалистами в соответствующей области. В статье В.В. Липаева «Проблемы экономики производства крупных программных продуктов» обсуждаются особенности экономики современного производства крупных программных продуктов, а также оценка их экономических характеристик путем маркетинговых исследований или по статистике прототипов продукта. Сформулированы проблемы организации, планирования и применения экономически обоснованных методов автоматизации производства сложных комплексов программ, а также обеспечения их качества с учетом затрат ресурсов. И.Б. Бурдонов и А.С. Косачев представили статью «Обобщённые семантики тестового взаимодействия», в которой развиваются результаты, описанные в предыдущих публикациях авторов. Обобщается семантику тестового взаимодействия на основе допущения наблюдения отказов, не обязательно совпадающих с множеством разрешаемых действий (и даже не обязательно вложенных в него). Изложение состоит из двух частей: сначала рассматриваются модели без приоритетов, а потом вводятся приоритеты. В статье Е.В. Корныхина «Генерация тестовых данных для тестирования арифметических операций центральных процессоров» Рассматривается задача генерации тестовых данных для тестирования арифметической подсистемы центральных процессоров. Для ее решения предлагается использовать метод, позволяющий строить тестовые данные систематически на основе 5
формального описания поведения отдельных команд микропроцессора. Предложенный метод апробирован на командах арифметической подсистемы микропроцессоров MIPS64. Статья К.Н. Долговой и А.В. Чернова «О некоторых задачах обратной инженерии» содержит краткое введение в проблематику задачи декомпиляции программ как одной из задач обратной инженерии. Рассматриваются возможности и недостатки существующих инструментальных средств декомпиляции программ. Представлены результаты сравнительного тестирования декомпиляторов для языка Си на разработанном наборе тестовых примеров. В статье П.Н. Яковенко и А.В. Сапожникова «Подход к реализации переносимого TTCN-3 отладчика» приводится краткое описание интерфейса TCI-TL. Рассматриваются подходы к реализации отладчика: времени выполнения и «посмертный». Описываются особенности реализации операций отладки на базе TL-интерфейса. Обсуждается проблема обработки запущенных таймеров при остановке в контрольной точке. Статья Д.А. Лизоркина «Язык модификации данных формата XML функциональными методами» посвящена описанию языка модификации XML-данных, в основу которого были положены функциональные методы программирования и язык функционального программирования Scheme. Функции, являющиеся в языках функционального программирования объектами первого класса, используются в предлагаемом языке модификации XML-данных в роли обработчиков для операций модификации, что позволяет достичь выразительной мощности и расширяемости набора операций модификации при сохранении синтаксической простоты языка. Проводится анализ алгоритмической сложности выполнения операций предлагаемого языка и рассматриваются детали его реализации функциональными методами. Наконец, в статье В.А. Семенова, С.В. Морозова, О.А. Тарлапана и И.В. Энковича «Нечеткое сравнение коллекций: семантический и алгоритмический аспекты» рассматривается задача нечеткого сравнения коллекций в приложениях реконсиляции. Задача возникает при оптимистической репликации структурированных данных и документов и имеет многочисленные приложения в таких актуальных областях, как управление конфигурацией программного обеспечения, управление мобильными базами данных, построение платформ и систем коллективной инженерии. Анализируются стандартные типы коллекций языков объектноориентированного моделирования, для которых описываются и обосновываются способы представления, журнализации, вычисления, принятия и согласования изменений. Для выделенных типов коллекций дается строгая, семантически содержательная интерпретация конфликтов и предлагаются конструктивные методы их идентификации и разрешения. Член-корреспондент РАН 6
В.П. Иванников
Обзор методов описания встраиваемой аппаратуры и построения инструментария кросс-разработки
симулятор, отладчик и профилировщик. В качестве инструментальной машины, как правило, выступает обычная рабочая станция. В отличие от производства реальных микросхем, для построения кросс-инструментария достаточно некоторого высокоуровневого описания целевой системы – прежде всего структуры памяти/регистров и системы команд с временными характеристиками исполнения. Это делает возможным раннее создание инструментария кросс-разработки еще в процессе проектирования аппаратуры. Использование кросс-инструментария на этом этапе играет ключевую роль при решении следующих задач: 1.
Прототипирование целевой аппаратуры и исследование проектных альтернатив (design space exploration) – разработка набора типовых тестов (т.е. программ для целевой машины), их запуск и профилирование на различных вариантах аппаратуры позволяет получать оценки эффективности того или иного проектного варианта и принимать решения о выработке новых улучшений, например, оптимизации системы команд ядра, добавлении / удалении тех или иных функциональных блоков, регистров и сопроцессоров.
2.
Раннее создание приложений – программное обеспечение для целевой платформы должно быть создано и предварительно отлажено еще до появления реальной аппаратуры. Это необходимо для сокращения времени выхода на рынок полного решения в виде «аппаратура + программы».
3.
Верификация спецификаций аппаратуры – использование построенного кросс-симулятора позволяет проводить его взаимную верификацию с симуляторами, полученными на основе точной VHDL/Verilog спецификации целевой системы (после того, как такая спецификация будет создана на позднем этапе проектирования). Такая верификация играет важную роль в процессе финального обеспечения качества перед запуском аппаратуры в производство.
В.В. Рубанов Аннотация. Статья посвящена обзору методов описания расширяемых встраиваемых систем и построения соответствующих инструментов кросс-разработки (симулятор, ассемблер, дисассемблер, компоновщик, отладчик и т.п.). Рассматривается общий процесс проектирования встраиваемых систем и описывается роль инструментария кросс-разработки. Обсуждаются языки для описания моделей встраиваемых систем и методы получения инструментария кросс-разработки на основе таких описаний. Проводится сравнительный анализ рассмотренных решений.
Введение В современном мире все большее распространение получают системы на основе встраиваемых процессоров, предназначенных для эффективного выполнения узкого класса задач в условиях жестких ограничений на соотношение производительности, энергопотребления, размера и стоимости изготовления кристалла. Такие системы можно встретить практически в каждом электронном устройстве, начиная от бытовой техники и кончая самолетами и военными комплексами. При этом большую популярность приобретает подход к построению встраиваемых систем на основе расширяемых процессоров, включающих некоторое базовое микропроцессорное ядро (soft core), которое дополняется в процессе проектирования специфическими для конкретной системы расширениями в виде сопроцессоров и/или дополнительных функциональных блоков, расширяющих систему команд и подсистему памяти ядра. При таком подходе одно и то же ядро повторно используется в системах различного назначения, существенно сокращая затраты на проектирование. При этом использование специализированных для каждой системы расширений обеспечивает высокую техническую эффективность в смысле баланса указанных выше показателей. В процессе создания встраиваемых систем важнейшую роль играет инструментарий кросс-разработки, позволяющий выполнять разработку, отладку и профилирование программ для целевой системы с использованием инструментальной машины с отличной от целевой архитектурой. Основными компонентами такого инструментария являются ассемблер, компоновщик, 7
Конечно, важно, чтобы после завершения проектирования аппаратуры полученные кросс-инструменты были пригодны для собственно производственного применения при дальнейшей разработке реальных приложений. В данной статье будут рассмотрены различные современные средства описания моделей аппаратуры, пригодные для построения на основе таких описаний соответствующих кросс-инструментов. При рассмотрении таких методов создания кросс-инструментария будем иметь в виду следующие «идеальные» требования. 1.
8
Получаемый кросс-инструментарий должен обладать высокой скоростью работы (десятки миллионов модельных тактов в секунду на современных рабочих станциях) и потактовой точностью моделирования.
2.
В процессе построения должен обеспечиваться быстрый цикл внесения согласованных изменений в кросс-инструменты для отражения различных вариантов аппаратной системы, возникающих как в процессе проектирования ядра, так и в процессе разработки расширений и выборе конфигурации полной системы.
3.
В случае расширяемой аппаратуры необходима возможность разделения разработки базового инструментария (для базового ядра) и соответствующих модулей/инструментов для различных расширений с возможностью комбинации соответствующих компонентов при построении расширенного инструментария для полной системы 1.
Применение такого «идеального» метода позволило бы эффективно решать поставленные выше задачи прототипирования расширяемой аппаратуры с потактовой точностью, верификации VHDL/Verilog моделей и собственно разработки реальных приложении как на этапе проектирования, так и на этапе эксплуатации аппаратуры. Статья состоит из введения, трех разделов и заключения. Во втором разделе рассматривается процесс проектирования встраиваемых систем и описывается роль инструментария кросс-разработки. В разделе 3 дается обзор языков для описания моделей встраиваемых систем и соответствующих методов получения инструментария кросс-разработки на основе таких описаний. В четвертом разделе проводится сравнительный анализ рассмотренных решений. В заключении подводятся итоги и предлагаются направления создания новых методов.
встраиваемые системы можно найти в самых различных областях от той же военной индустрии до бытовых устройств. Особенностью проектирования классической встраиваемой системы (см., например [5], [8]) является изначальное построение программно-аппаратного комплекса «в целом» под заранее известный набор фиксированных задач. При этом проектирование встраиваемой системы состоит в построении спецификаций ее аппаратных и программных компонентов, пригодных для производства реальных устройств и выполняющих заданные функции в рамках определенных ограничений (обычно быстродействие, энергопотребление, размер и стоимость изготовления кристаллов). В качестве конечной спецификации программной части системы выступает образ начального содержимого памяти системы (firmware), представляющий собой двоичные коды программ (машинные команды) и начальные данные. Спецификацией аппаратуры является описание на некотором языке, пригодное для дальнейшего полностью автоматического синтеза технологических спецификаций для производства реальных микросхем.
1.1. Обобщенная систем
схема
проектирования
Рассмотрим известную (см. например [5-6]) проектирования встраиваемой системы (рис. 1).
встраиваемых обобщенную
схему
1. Проектирование встраиваемых систем В СССР первыми встраиваемыми компьютерными системами можно считать специализированные бортовые вычислительные машины для военных и космических отраслей. Первые такие машины начали разрабатывать в конце 1950-х (см. [1]-[3]) на базе появившихся тогда сплавных транзисторов, и в начале 1960-х их уже стали применять на практике. В [1] в качестве одних из первых таких систем упоминаются передвижные компьютеры для нужд ПВО (1960-1962), обеспечивавшие управление зенитно-ракетными комплексами с сопровождением многих десятков целей. В зарубежных источниках [4] первой широко известной встраиваемой системой называют бортовой компьютер космического корабля Apollo (середина 1960-х). Долгое время основной областью применения встраиваемых систем были именно задачи космического и военного назначения. В современном мире 1
Дело в том, что за разработку базового процессора и за разработку расширений могут отвечать разные компании, причем каждая из них, как правило, помимо разграничения ответственности, желает сохранить детали конструкции соответствующей аппаратуры в тайне. 9
Рис. 1. Обобщенная схема проектирования встраиваемой системы 10
На первом этапе происходит определение требований к системе. Определяются необходимые функциональные характеристики системы и задаются ограничения. Типовыми ограничениями являются быстродействие, энергопотребление, размер и стоимость изготовления кристаллов в рамках заданного технологического процесса производства микросхем. На следующем этапе выполняется декомпозиция системы на аппаратные и программные компоненты (HW/SW partitioning). Принимаются решения об общей структуре системы (в первую очередь, число и характеристики вычислительных блоков) и выполняется отображение требуемой функциональности на программные и аппаратные части. Далее процесс разделяется на две ветви – для проектирования программных и аппаратных компонентов. Выходом аппаратной ветви являются модели аппаратуры. В этой ветви принимаются решения об архитектуре выделенных в системе аппаратных вычислительных устройств. Для программируемых компонентов определяется состав функциональных блоков (включая внешние модули расширений для специфических вычислений), структура памяти (включая регистры) и система команд. Результатом проектирования программной части являются модели программных компонентов, совместимые с соответствующими аппаратными моделями. Процесс носит итеративный характер, и точность описания моделей на каждой итерации постепенно повышается от высокоуровневых функциональных описаний до синтезируемых спецификаций аппаратуры и машинных кодов программ, соответствующих этой аппаратуре. Каждая итерация заканчивается интеграцией результатов программной и аппаратных ветвей, моделированием полученной системы, проверкой функциональной корректности и сбором соответствующих оценок ключевых параметров для их анализа с целью дальнейшей оптимизации. На основании полученных таким образом оценок принимаются решения о пересмотре декомпозиции между программными и аппаратными компонентами, о конкретных изменениях программ и аппаратуры, например, добавлении/удалении вычислительных блоков или оптимизации системы команд. Цикл повторяется до получения конкретных спецификаций программ и аппаратуры, которые совместно задают встраиваемую систему, удовлетворяющую всем заданным требованиям. Затем проводится верификация спецификаций, и цикл проектирования завершается подготовкой отчуждаемого продукта, пригодного для интеграции в более крупные проекты «систем на чипе» (SoC) или для запуска в отдельное производство. Такой продукт обычно включает в себя:
3) набор инструментов кросс-разработки (среда программирования, см. подраздел 1.2) для создания и отладки новых программ; 4) документацию для программистов (справочники по архитектуре системы, по системе команд, по поставляемому системному программному обеспечению и различным библиотекам, по среде программирования). Существует много различных методов и средств автоматизации проектирования аппаратуры (см., например, обзор [7]). В случае встраиваемых систем огромное внимание уделяется задаче оптимального разбиения системы на аппаратные и программные компоненты (HW/SW partitioning and codesign) – см. [8-13]. Однако в данной статье мы ограничимся только рассмотрением создания и использования (кроме собственно основного назначения для разработки реальных программ) кросс-инструментария как средства получения дополнительных данных для поддержки принятия проектных решений в процессе проектирования аппаратуры (см. следующие разделы). Конкретные методы использования таких данных выходят за рамки данной статьи.
1.2. Разработка программ с помощью кросс-инструментария Целью использования кросс-инструментов является создание на инструментальной машине файла с двоичным образом (firmware) начального содержимого памяти (как машинные команды, так и данные) для целевой аппаратной системы. Такой файл затем используется для загрузки в конкретные целевые устройства. В качестве языков программирования встраиваемых систем выступают обычно C (а также его расширенные подмножества типа Embedded C [14]) и ассемблер. Ассемблер используется как в виде вставок в код на языке С, так и в виде отдельных ассемблерных модулей. В случае встраиваемых систем программирование на ассемблере остается важной составляющей создания программ ввиду, как правило, жестких требований к высокой производительности и малому объему программного кода. На рис. 2 представлена типовая схема разработки программ с помощью кроссинструментария. Обычно процесс взаимодействия программиста с кросс-инструментами происходит через визуальную интегрированную среду разработки (IDE) со встроенным редактором, механизмами поддержки проектов, различными средствами управления и анализа результатов работы отдельных инструментов.
1) синтезируемые RTL (register transfer level) описания аппаратуры (обычно на VHDL/Verilog); 2) исходные (на С/ассемблере) и машинные (firmware) коды базовых системных и прикладных программ для целевой аппаратуры; 11
12
Интегрированная cреда разработки
с соответствующими исправлениями зависящих от них кодов команд и значений данных. На этом заканчивается этап сборки программы. Полученный абсолютный модуль можно преобразовать в образ памяти для непосредственной загрузки в целевую аппаратуру с помощью программатора. Для программиста взаимодействие с исполняемой моделью аппаратуры осуществляется через отладчик, который позволяет просматривать состояние модели (содержимое памяти, регистров, шин, сигналов) и осуществлять управляемое (в том числе, пошаговое) выполнение целевой программы на уровне отдельных команд или строчек исходного кода. Совместно с отладчиком используются различные виды профилировщиков и средств анализа, визуализирующих необходимые характеристики модели (как статические, так и времени исполнения) и соответствующие статистики. В качестве примеров можно привести: 1) подсчет числа тактов и количества раз исполнения для каждой строчки программы (на уровне исходных кодов языка программирования высокого уровня, на уровне ассемблерных текстов и на уровне дисассемблированных команд процессора); 2) визуализация графа вызовов функций и статистика затраченных для каждого узла тактов и количества раз исполнения; 3) список функций программы, их размеров в программном коде и отражение количества вызовов и суммарных затрат (тактов) на их выполнение (с учетом и без учета вызова потомков); 4) статистика доступа к различным областям памяти; 5) статистика используемого объема памяти;
Рис. 2. Разработка программ с помощью кросс-инструментов.
6) различные статистики на уровне операционной системы (в терминах задач и примитивов синхронизации, зарегистрированных в системе).
Реальные программы обычно состоят из нескольких модулей, каждому из которых соответствует файл с исходными текстами программ (на языках высокого уровня или ассемблера). Компилятор транслирует модули на языке высокого уровня в промежуточные ассемблерные модули. Ассемблер отвечает за преобразование ассемблерных модулей (как написанных вручную, так и сгенерированных компилятором) в объектные модули с машинными кодами и данными для целевой аппаратуры. В качестве формата объектных модулей обычно используется ELF [15-16], включающий в себя различные секции (например, секции исполняемого кода, секции данных, секции с информацией о символах и секции с отладочной информацией). Компоновщик выполняет сборку нескольких объектных модулей в один абсолютный модуль с объединением соответствующих секций входных объектных файлов. При этом выполняются перемещения символов по абсолютным адресам памяти (автоматически или в соответствии с заданной программистом картой памяти) 13
В качестве модели целевой системы для кросс-отладчика чаще всего выступает симулятор, позволяющий моделировать целевую аппаратуру полностью на инструментальной машине. Существуют симуляторы различного уровня абстракции от функционального симулятора на уровне всей системы до симуляторов на уровне системы команд (в том числе потактово-точных) и симуляторов, эмулирующих точную структуру аппаратуры на уровне функциональных блоков и конкретных вентилей. В качестве симулятора в составе инструментария кросс-разработки обычно используется симулятор уровня системы команд (в том числе, с учетом конвейерных эффектов с потактовой точностью). Также отладчик может поддерживать отладку непосредственно аппаратной модели в виде реального чипа или модели в ПЛИС (FPGA), подключаемой к инструментальной машине, что может иметь место на финальных стадиях проектирования и на 14
стадии эксплуатации. Использование настоящей аппаратуры позволяет запускать программы в режиме реального времени, однако программный симулятор предоставляет гораздо больше возможностей для отладки и анализа программ.
1.3. Прототипирование на основе кросс-инструментария Уже на этапе проектирования для прототипирования и верификации целевой системы часто используется инструментарий кросс-разработки (см. например [17-19]). Это позволяет эффективно выполнять тонкую оптимизацию системы (как аппаратуры, так и программ для нее) с использованием системных и прикладных целевых программ, близких к реальным. Благодаря этому, дальнейший переход к выпуску промышленного набора результатов проектирования происходит бесшовно, то есть последняя итерация проектирования и верификации оканчивается результатами, готовыми к производственному применению (спецификацией аппаратуры и программами для нее). Программа Требования и ограничения
1 Дизайнер
нет
Описание аппаратуры
3
Удовлетворение требованиям и ограничениям
моделей аппаратуры и методы построения кросс-
В данном разделе рассматриваются существующие языки описания моделей целевой аппаратуры и соответствующие методы построения инструментария кросс-разработки на основе таких описаний. Выделяются три группы средств описания: 1) языки описания аппаратуры с возможностью спецификаций для производства микросхем;
синтеза
реальных
3) языки программирования общего назначения.
2.1. Языки синтезируемого описания аппаратуры
4 5
2. Языки описания соответствующие инструментов
2) языки ADL (Architecture Description Languages) для высокоуровневого описания аппаратуры;
Кроссинструментарий
2
инструментов выполняется сборка, отладка, запуск и профилирование целевой программы (3). Получаются (4) значения необходимых характеристик системы, которые используются (5) для оценки удовлетворения требованиям/ограничениям и принятия решения о путях дальнейшей оптимизации системы (изменения программы/системы команд процессора, добавление/удаление аппаратных расширений, регистров и т.п.).
Функциональная корректность и профили производительности и энергопотребления
да Окончание проектирования
Рис. 3. Прототипирование системы с помощью кросс-инструментов. Общий процесс прототипирования аппаратуры на основе кроссинструментария показан на рис. 1.3. Роль инструментария кросс-разработки в этом процессе заключается в валидации функциональной корректности предполагаемой системы и получении достаточно точных оценок ее характеристик (в первую очередь, профилей производительности и энергопотребления, а также размеров требуемой памяти). Для этого на каждой итерации создается конкретная целевая программа и задается описание модели аппаратуры (1). Затем инструментарий кросс-разработки адаптируется под заданную модель аппаратуры (2). С помощью полученных кросс15
Рассмотрим три основных языка HDL (Hardware Definition Language) [20], которые используются современными разработчиками для описания моделей аппаратуры, пригодных для дальнейшего автоматического синтеза спецификаций для производства реальных чипов. Это VHDL, Verilog и SystemC. Первые два языка были разработаны в середине 80х и до сих пор являются наиболее популярными классическими HDL-языками; SystemC – относительно современная разработка, находящаяся в стадии развития, но стремительно набирающая популярность. На верхнем уровне эти языки очень схожи – модель аппаратуры описывается в виде взаимодействующих модулей (блоков), для каждого из которых определяется интерфейс и реализация. Интерфейсы модулей описывают входные, выходные и двусторонние порты, с помощью которых модули соединяются друг с другом для обмена данными и управляющими сигналами. Реализация задает элементы внутреннего состояния и порядок вычисления значений выходных интерфейсов на основе этого состояния и значений входных портов, а также правила обновления внутреннего состояния. Вычисление новых значений выходных интерфейсов и внутреннего состояния инициируется событиями в виде изменения уровней входных сигналов. Важную роль в описании аппаратуры с использованием языков HDL играет понятие времени, которое моделируется целочисленной величиной с 16
возможностью привязки к физическому времени. Для описания причинноследственных отношений между событиями в рамках одной единицы модельного времени используется понятие дельта-задержки (delta delay), которая разделяет последовательно выполняющиеся события, происходящие в одну и ту же единицу модельного времени. При описании реализации модуля могут использоваться выполняющиеся параллельно процессы, которые обычно представляют собой бесконечные циклы ожидания наступления некоторых заданных событий (называемых списком чувствительности процесса) с их последующей обработкой и возвратом к ожиданию новых изменений. Рассмотрим каждый из языков более подробно.
3.1.1. VHDL VHDL [21-24] был разработан в недрах Министерства Обороны США, изначально предназначаясь для облегчения унифицированного описания микросхем, которые включались сторонними поставщиками в различные решения для этого ведомства. Первая официальная версия VHDL появилась в 1987 году в виде стандарта IEEE 1076-1987 [24]. Многие семантические и синтаксические элементы VHDL заимствованы из языка Ada. Подобно Ada, VHDL – это строго типизированный язык, не чувствительный к регистру символов. В дополнение к стандартным базовым возможностям Ada, VHDL включает расширенные логические операции (например, nand и nor), двунаправленную индексацию массивов, а также дополнительные типы, такие как time, bit, bit_vector, character, string. Позже в VHDL ввели понятие 9-значной (U,X,0,1,Z,W,H,L,-) логики (см. IEEE Std 1164 [25]) и понятие знаковых/беззнаковых типов (см. IEEE standard 1076.3 [26]). Принципиальной особенностью VHDL является поддержка конструкций для задания параллелизма, свойственного аппаратуре, а именно модулей и процессов. Интерфейс модуля задается с помощью ключевого слова entity, ключевое слово architecture обозначает описание реализации, которое заключается между begin и end. Внутри такого блока могут задаваться константы (constant), сигналы (signal) и собственно поведение в виде набора операторов, в том числе, сгруппированных в виде параллельно выполняющихся процессов (с помощью ключевого слова process). Внутри процессов могут объявляться переменные (variable). Важным различием переменных и сигналов является то, что значение переменной меняется сразу после выполнения соответствующего оператора (понятие времени не ассоциируется с понятием переменной), а значение сигнала меняется только после окончания текущей итерации выполнения процесса. Пример 1 иллюстрирует реализацию на языке VHDL простого мультиплексора (см. рис. 4).
17
Рис. 4. Простой мультиплексор
entity mux is port (c, d, e, f: in std_logic; s: in std_logic_vector(1 downto 0); mux_out: out std_logic); end mux; architecture mux_impl of mux is begin muxl: process (s, c, d, e, f) begin case s is when “00” => mux_out <= c; when “01” => mux_out <= d; when “10” => mux_out <= e; when others => mux_out <= f; end case; end process muxl; end mux_impl; Пример 1. Простой мультиплексор на VHDL.
3.1.2. Verilog Язык Verilog [21], [28-30] был разработан компанией Gateway Design Automation в 1985 году для целей проектирования интегральных схем на логическом уровне. В 1989 году компания Gateway Design Automation была куплена Cadence, которая сделала этот язык публичным. В 1995 году Verilog стал стандартом IEEE 1364 [29]. Verilog наследует многое из языка C, поддерживает конструкции препроцессора C и большинство основных управляющих конструкций, таких 18
как “if”, “while” и т.п. Verilog полностью поддерживает операторы языка C, но также обладает дополнительными возможностями для удобной обработки двоичных данных (например, операциями ~^ для XNOR или >>> для арифметического сдвига с заполнением знаковым битом). Однако в Verilog не поддерживаются пользовательские типы, структуры, указатели и рекурсивные функции. Типы данных в Verilog имеют явную битовую ширину. В отличие от VHDL, Verilog является слабо типизированным языком, что позволяет смешивать присвоения элементов разного типа за счет неявного преобразования типов. Как и в случае VHDL, принципиальным отличием Verilog от своего языкапрототипа является поддержка конструкций, выполняющихся параллельно. Модули в Verilog определяются с помощью ключевого слова module и могут быть вложенными. Не происходит явного разделения интерфейса и реализации, как в VHDL. В декларативной части модуля определяются входные (input), выходные (output), двунаправленные (inout) порты, внутренние сигналы (wire) и переменные (reg).
module bcircuit (a, b, av, bv, w, wv); input a, b; output w; input [7:0] av, bv; output [7:0] wv; wire d; wire [7:0] dv; reg e; reg [7:0] ev; . . . endmodule
Важным отличием Verilog от VHDL является подверженность недетерминированному поведению (race conditions) модели на основе описания Verilog и отсутствие таких эффектов в VHDL. Более подробный сравнительный обзор VHDL и Verilog можно найти в [34]. Пример 3 иллюстрирует реализацию простого мультиплексора (см. рис. 4) на языке Verilog.
module mux (c, d, e, f, s, mux_out); input c, d, e, f; input [1:0] s; output mux_out; reg mux_out; always @ (c or d or e begin case (s) 2’b00: mux_out = 2’b01: mux_out = 2’b10: mux_out = default: mux_out endcase end endmodule
or f or s) c; d; e; = f;
Пример 3. Простой мультиплексор на Verilog.
3.1.3. SystemC
Пример 2. Описание модуля и его интерфейсов в Verilog В процедурной части модуля определяется его поведение. Для этого могут использоваться конструкции вентильного уровня (определяется структура модуля в виде соединенных между собой вентилей определенных типов), параллельные присвоения (assign) или процедурные блоки (always или initial). Процедурные блоки аналогичны процессам VHDL. Конструкции языка в рамках процедурного блока выполняются последовательно, в то время как все процедурные блоки выполняются параллельно. Интересно отметить наличие оператора неблокирующего присваивания <=, который может использоваться для присваивания новых значений в рамках процедурного блока. В отличие от обычного присваивания =, изменение значения, присвоенного оператором <=, происходит только в конце текущего кванта времени. 19
Работа над языком SystemC [30-31] была начата в середине 1990-х в качестве внутреннего проекта компании Synopsis, и язык был открыт сообществу в 1999 году. В 2000 году был учрежден консорциум Open SystemC Initiative [31], в который вошли такие заинтересованные компании, как Mentor Graphics, Cadence, ARM, CoWare. Образование этого консоциума позволило проводить работы по развитию SystemC в интересах всего сообщества. Первая версия SystemC 1.0 вышла в этом же году и сменилась следующей в 2001 году. В 2005 году для SystemC появился стандарт IEEE Std 1666 [32]. В настоящее время ведутся работы над созданием новой версии SystemC 3.0. На самом деле, SystemC не является самостоятельным языком. Фактически, это библиотека классов, типов и макросов C++, которая позволяет удобно описывать программно-аппаратную систему на различных уровнях абстракции. Благодаря хорошим возможностям языка C++ для определения пользовательских типов, описания на SystemC выглядят достаточно выразительным образом, что позволяет эффективно расширять семантику 20
базового языка. К основным расширениям SystemC, явно отсутствующим в C++, относятся: 1) понятие модельного времени; 2) возможности описания параллельно выполняющихся вычислений; 3) дополнительные типы данных, отражающие специфику проектирования аппаратуры (в первую очередь, многозначная логика 1, 0, X, Z). Основным преимуществом SystemC над языками VHDL и Verilog является возможность использовать одно и то же окружение и язык для описания системы от самых высокоуровневых моделей на C++ до структурных синтезируемых моделей уровня RTL. Теоретически это позволяет постепенно детализировать описание системы в процессе проектирования. Однако пока инструменты поддержки процесса разработки с использованием SystemC уступают инструментарию классических языков VHDL и Verilog. Это приводит к тому, что SystemC в основном используется как «улучшенный» C++ для описания только высокоуровневых моделей системного уровня с последующим переходом на VHDL или Verilog для описания оптимизированной RTL модели для реального синтеза аппаратуры. Так же, как и в VHDL и Verilog, основным строительным блоком в SystemC является модуль, который объявляется с использованием ключевого слова SC_MODULE. В модуле могут определяться порты (sc_in, sc_out, sc_inout), данные модуля (включая ссылки на другие модули) и методы. В каждом модуле обязательно должен присутствовать метод-конструктор, который в SystemC обозначается SC_CTOR. Также может объявляться деструктор с использованием обычного синтаксиса C++. Остальные методы делятся на параллельные и обычные (вспомогательные). Обычные методы применяются, как повторно используемые функции для упрощения реализации параллельных методов. Параллельные методы SystemC аналогичны процессам VHDL и Verilog и служат основным средством для описания деталей поведения модели. В SystemC выделяют два основных типа параллельных методов – SC_METHOD и SC_THREAD. Тип метода (обычный или подтип параллельного) указывается в конструкторе модуля. Основное различие между SC_METHOD и SC_THREAD заключается в порядке их исполнения в рамках модели. SC_METHOD запускается в ответ на события из своего списка чувствительности столько раз, сколько раз срабатывает эти события. При этом каждый запуск независим друг от друга в смысле сохранения значений локальных переменных, и выполняется он до конца метода или до явного return. В SC_METHOD нельзя применять функцию wait для динамического контроля над приостановкой процесса. SC_THREAD же запускается только один раз в начале исполнения модели, поэтому обычно реализация SC_THREAD представляет собой бесконечный цикл while(1). Принципиальной особенностью SC_THREAD является возможность 21
использовать функцию wait для приостановки исполнения процесса до наступления заданных условий. При этом управление передается ядру симулятора и возвращается только при наступлении этих условий. Выполнение продолжается со следующей после wait конструкции с сохранением предыдущих значений локальных переменных метода. С точки зрения эффективности симуляции SC_METHOD является более быстрым вариантом SC_THREAD. В SystemC вводятся следующие основные дополнительные типы данных: •
sc_string – строковое представление чисел в различных форматах (например, число -2 может быть представлено как -0d2 (десятичный), 0b1110 (4-х битный двоичный знаковый), 0b0010 (4-х битный двоичный беззнаковый));
•
sc_int
, sc_uint – знаковые и беззнаковые целочисленные типы заданной битовой ширины N;
•
sc_bigint, sc_biguint – знаковые и беззнаковые целочисленные типы заданной битовой ширины N, превышающей длину значений типа int, который поддерживается на инструментальной машине;
•
sc_fixed, sc_ufixed и др. – типы данных с фиксированной точкой
•
sc_logic и sc_lv – 4-х значный бит (1, 0, X, Z) и вектор таких битов соответственно.
Для облегчения описания взаимодействия модулей, кроме портов, в SystemC предлагаются более сложные каналы sc_mutex, sc_fifo и sc_semaphore. Пример 4 иллюстрирует реализацию простого мультиплексора (см. рис. 1.4) на языке SystemC. В отличие от VHDL и Verilog, в которых может использоваться интерпретатор описания для моделирования системы, в SystemC задается полная исполняемая модель, и описание на SystemC компилируется в исполняемый файл на инструментальной машине обычным компилятором C++ с использованием специальных библиотек SystemC. В начале симуляции модели, аналогично методу main в С, в SystemC управление передается в метод sc_main(argc, argv), в котором происходит инициализация модулей системы и, в конечном итоге, запуск параллельных процессов инициализированных модулей с помощью функции sc_start().
22
SC_MODULE (mux) { public: sc_in <sc_logic> sc_in <sc_lv<2>> sc_out <sc_logic>
крупный производитель средств автоматизированной разработки аппаратуры (EDA) поддерживает собственные симуляторы для различных HDL-языков. Synopsis предлагает уже ставшую классической среду VCS [35], способную симулировать модели (в том числе смешанные) на всех трех основных языках - VHDL, Verilog и SystemC. Аналогичные смешанные симуляторы предлагаются компаниями Mentor Graphics (ModelSim [36]) и Cadence (NCSim [37], Incisive Verification Platform [38]). Все эти симуляторы поддерживаются интегрированными средами разработки и отладки моделей, в которых одним из основных средств визуализации отладки являются диаграммы изменения сигналов (waveforms). Основной проблемой HDL-симуляторов является низкая скорость работы порядка 10-50 тыс. модельных тактов в секунду на современных рабочих станциях, в то время как типовой цикл моделирования алгоритмов цифровой обработки сигналов требует миллиарды тактов для обработки репрезентативного тестового сигнала. Такая скорость обусловлена высокой точностью моделирования на слишком низком уровне. Таким образом, скорость работы и отсутствие средств поддержки программирования на уровне системы команд делает применение HDL-симуляторов неприемлемым для эффективной разработки прикладных программ. Тем не менее, такие симуляторы играют важную роль в процессе дизайна и верификации самой аппаратуры.
c, d, e, f; s; mux_out;
SC_CTOR (mux) { SC_METHOD (do_mux); sensitive << c << d << e << f << s; } void do_mux () { sc_uint<2> temp_s = switch (temp_s) { case 0: mux_out case 1: mux_out case 2: mux_out default: mux_out } }
s.read(); = = = =
c.read(); break; d.read(); break; e.read(); break; f.read();
}
2.3. ADL языки Пример 4. Простой мультиплексор на SystemC.
2.2. Кросс-инструменты поддержки HDL языков Поскольку спецификации аппаратуры на языках VHDL, Verilog и SystemC принципиально не содержат явного описания системы команд, кроссинструменты поддержки разработки прикладных программ на уровне языка ассемблера (ассемблер, дисассемблер, компоновщик) для описанной таким образом аппаратуры не могут быть получены автоматизированным образом. Поэтому главным доступным кросс-инструментом для описанной на этих языках аппаратуры является симулятор. Для VHDL и Verilog кросс-симуляторы представляют собой программы для инструментальной машины, которые автоматически настраиваются для моделирования целевой аппаратуры на основе HDL-описания. Для VHDL и Verilog используются как подходы с интерпретацией таких описаний, так и с компиляцией модели в код инструментальной машины. Для SystemC-моделей характерен подход с компиляцией. Для заданного начального состояния полученные модели способны моделировать поведение системы с очень высокой точностью (состояние отдельных регистров, буферов, сигналов, шин и т.п. с квантованием по тактам или даже по отдельным событиям). Каждый 23
Изначально ADL (Architecture Description Language) языки (см. обзоры [3942]) начали появляться в начале 90-х в ответ на потребность в описании модели вычислительной аппаратуры на уровне явной системы команд для обеспечения раннего прототипирования встраиваемых микропроцессоров. Основу описания аппаратуры на ADL-языке составляет спецификация элементов состояния системы (регистры, память) и соответствующей системы команд, осуществляющей вычислительные операции над этими элементами. Основным фактором эффективного использования ADL в процессе проектирования аппаратуры является возможность автоматической генерации необходимых кросс-инструментов (прежде всего, симулятора и ассемблера) для обеспечения моделирования заданных тестовых программ на том или ином варианте аппаратуры. Процесс конструктивного исследования различных проектных альтернатив при проведении предварительного дизайна аппаратуры является наиболее типичным местом эффективного применения ADL-решений. Исходя из прикладных требований, архитектор выполняет разбиение задач на аппаратно и программно реализуемые части (SW/HW partitioning – см. разд. 1), то есть решает, какие функции следует заложить в виде аппаратной реализации, а какие – в виде прикладных программ. Программная реализация функций обеспечивает большую гибкость и минимизирует аппаратные ресурсы (что приводит к меньшей площади кристалла, меньшему энергопотреблению и стоимости), однако, с другой 24
стороны, существуют ограничения на производительность реального времени, которую во многих случаях программная реализация обеспечить не может (а повышение тактовой частоты ведет к ухудшению упомянутых параметров). Ясно, что теоретически существует некий оптимальный баланс между программной и аппаратной реализацией для конкретного класса алгоритмов, нахождением которого собственно и занимается архитектор на этапе проектирования. Однако ввиду того, что процесс разбиения задач между аппаратурой и программами носит эвристический характер на основе грубых оценок, необходима экспериментальная проверка корректности и эффективности различных вариантов. Для этой цели используют симуляцию тестовых программных задач на подразумеваемом аппаратном обеспечении (в виде симулятора). Вот почему наличие быстро обновляемого инструментария кросс-разработки очень важно на этом этапе, и ADL-решения наиболее подходят для достижения упомянутой цели. Процесс использования ADL-решения начинается с описания общей архитектуры предполагаемой системы на формальном языке с фокусом на системе команд. Можно построить данный процесс на основе имеющихся библиотек решений, которые дизайнер может лишь слегка изменять вместо того, чтобы создавать заново. Инструменты поддержки должны уметь проверять целостность и корректность созданного описания. При корректном описании автоматически генерируется инструментарий кросс-разработки, с помощью которого конструктор может оценить ключевые параметры производительности предполагаемой системы и решить, насколько текущая архитектура удовлетворяет необходимым критериям. Среди критериев, анализируемых путем выполнения, профилировки и отладки программ, можно выделить семантическую корректность результата, производительность, энергопотребление, степень использования системы команд и внутренних аппаратных компонентов (регистров, функциональных единиц, шин) во время выполнения заданных приложений. Если какие-то параметры неудовлетворительны, то на основе полученной информации выявляются узкие места и принимаются решения по изменению аппаратуры (изменению системы команд и пересмотру баланса между аппаратурой и программным обеспечением). Данные изменения вносятся в ADL-описание и программы, и процесс повторяется. Таким образом, основными преимуществами ADL являются быстрота описания модели системы (за счет использования более высокоуровневых абстракций, чем в HDL) и автоматическая генерация инструментария кросс-разработки, что обеспечивает быструю оценку параметров различных вариантов аппаратуры на этапе проектирования. Если все критерии соблюдены, то происходит детализация описания системы и начинается процесс детальной разработки. Некоторые ADL-решения поддерживают синтез шаблонов на HDL-языках, обеспечивая плавный переход к производственной доработке аппаратуры в системах проектирования аппаратного обеспечения. 25
Рассмотрим наиболее успешные ADL-языки и соответствующие инструменты более подробно.
3.3.1. nML Язык nML [43-44] изначально был разработан в Техническом университете Берлина в 1991 году и является пионером в области ADL-решений, ориентированных на высокоуровневое описание системы команд. В этом университете nML использовался в качестве способа описания аппаратуры для настраиваемого симулятора SIGH/SIM и компилятора CBC (с языка ALDiSP). Язык nML получил дальнейшее развитие в бельгийском научноисследовательском центре микроэлектроники IMEC, где в рамках дочерней компании Target Compiler Technologies была создана коммерческая среда разработки [45], ориентированная на DSP-архитектуры. В эту среду входят компилятор CHESS (с языка C), симулятор CHECKERS, ассемблер, дисассемблер и компоновщик. Также поддерживается синтез шаблонов VHDL-описания. В nML выделяются определения типов (type), элементов хранения данных (mem) и собственно иерархическое описание системы команд, задаваемое с помощью атрибутных грамматик в виде OR и AND-правил. Элементами грамматики служат операции (op) и режимы адресации (mode). Для операций обязательные атрибуты включают в себя описание поведения на расширенном подмножестве C (action), ассемблерного синтаксиса (syntax) и отображения в машинные коды (image). Для режимов адресации – только синтаксис и двоичный код. Корневая операция имеет фиксированное имя instruction. Базовые типы данных nML включают в себя: •
int(n) - тип знаковых целых n-битных чисел в дополнительном коде;
•
card(n) - тип n-битных беззнаковых чисел;
•
float(n,m) - тип знаковых чисел с плавающей точкой с n-битной мантиссой и m-битным основанием, в соответствии со стандартом IEEE754;
•
fix(n,m) - тип знаковых чисел с фиксированной точкой, с n битами до и m битами после двоичной точки;
•
bool - булевский тип; предопределены две константы true и false; при приведении к целому true получает значение -1, а false значение 0.
Пример 5 иллюстрирует описание в nML тривиального процессора с шестнадцатью 16-битными регистрами и парой команд сложения и вычитания: 26
type word = card(16) type index = card(4) mem mem
\ 16-битные данные \ индекс для адресации 16 регистров
RF[16, word] \ 16 регистров размера word PC[1, word] \ регистр-счетчик команд
mode REG(i:index)=RF[i] \ режим прямой регистровой адресации syntax = format("R%d",i) \ синтаксис вида R0, R1, .., R15 image = format("%4b",i) \ тривиальное отображение номера \ регистра в 4 бита машинного слова op instruction (x:instr_action) action = { PC = PC + 1; x.action; } syntax = x.syntax image = x.image op instr_action = add_op | sub_op
\ OR-правило
op add_op (dst:REG, src:REG) \ AND-правило action = { dst = dst + src; } syntax = format("ADD %s, %s”, dst.syntax, src.syntax) image = format(“0000 %s %s”, dst.image, src.image) op sub_op (dst:REG, src:REG) action = { dst = dst - src; } syntax = format("SUB %s, %s”, dst.syntax, src.syntax) image = format(“0001 %s %s”, dst.image, src.image)
Пример 5. Тривиальный процессор в nML Существенным ограничением nML является отсутствие механизмов описания многотактовых функциональных единиц и конвейеров. Кроме того, в nML поддерживаются только команды фиксированной длины, не поддерживается описание межкомандных зависимостей, и производительность симулятора, опубликованная в [17], невысока.
3.3.2. ISDL Язык ISDL был разработан группой CAA (Computer-Aided Automation) университета MIT, США [46] и впервые представлен на конференции по автоматизированному дизайну DAC [47] в 1997 году. Основной специализацией ISDL является описание VLIW-архитектур. Изначально язык задумывался для поддержки настраиваемых компилятора, ассемблера и симулятора, а также генератора Verilog-описаний на основе ISDLспецификаций. Как и nML, ISDL, главным образом, позволяет на основе 27
использования атрибутной грамматики описывать систему команд процессора, включающую в себя семантику поведения, синтаксис ассемблера, машинные коды, а также описание ресурсных конфликтов. К важным достоинствам языка можно отнести возможность специфицировать задержки и конфликтные ситуации для параллелизма уровня команд (Instruction Level Parallelism, ILP) в виде логических правил, хотя явное описание конвейера отсутствует. Описание ISDL состоит из следующих секций: • Format – формат машинного слова (разбиение на именованные битовые поля); • Global_Definitions – глобальные определения лексем (Token) и нетерминальных символов (Non_Terminal); • Storage – элементы-хранилища (регистры, память, стек, управляющие и специальные регистры); • Instruction_Set – спецификация системы команд в виде набора операций, описание каждой из которых включает следующие атрибуты: o мнемоника; o параметры в виде лексем и нетерминальных символов; o бинарное кодирование в машинном слове; o поведение в виде RTL описания над ресурсами-хранилищами; o время выполнения и другие параметры стоимости (например, энергопотребление): o задержки; • Constraints – ограничения на совместимость различных операций, как в рамках одной инструкции, так и между соседними инструкциями, а также ограничения в ассемблерном синтаксисе; ограничения записываются в виде логических правил, включающих в себя ссылки на параметры, константы и логические операции. Описание тривиального процессора в ISDL приведено в примере 6. SECTION Format IW = OPF[4], DSTF[4], SRCF[4]; SECTION Global_Definitions Token “R”[0..15] REG {[0..15];}; Non_Terminal DST: REG {$$ = REG;} {RF[REG]}; Non_Terminal SRC: REG {$$ = REG;} {RF[REG]}; SECTION Storage RegFile RF = 16, 16 // 16 16-битных регистров ProgramCounter PC = 16 // 16-битный счетчик команд SECTION Instruction_Set Field ALU_OP: ADD DST, SRC 28
// ассемблерный синтаксис
{ IW.OPF = 0x0; IW.DSTF = DST; IW.SRCF = SRC; } // двоичное кодирование { DST <- DST + SRC; } // поведение {} // побочные эффекты { cycle = 1; size = 1; }// длительность и размер { latency = 1; } // задержка
3.3.3. EXPRESSION
SUB DST, SRC // ассемблерный синтаксис { IW.OPF = 0x1; IW.DSTF = DST; IW.SRCF = SRC; } // двоичное кодирование { DST <- DST - SRC; } // поведение {} // побочные эффекты { cycle = 1; size = 1; }// длительность и размер { latency = 1; } // задержка Пример 6. Тривиальный процессор в ISDL Для описания поведения используется собственный C-подобный язык с включенной библиотекой функций и расширенных операций работы над данными на битовом уровне. Базовые типы языка ограничены только знаковым и беззнаковым целыми, а также числами с плавающей точкой с параметрами, зависящими от инструментальной платформы, на которой работают инструменты ISDL. Анализируя возможности ISDL, можно выявить следующие недостатки: • нет механизмов описания поведения операции на конкретных тактах / стадиях конвейера, что делает невозможным потактовоточное моделирование; • каждую операцию можно привязать только к одному функциональному «полю» (field), что затрудняет корректное описание использования ресурсов много-тактовыми командами; • нет механизмов описания глобальных аспектов архитектуры таких, как прерывания, аппаратные циклы, конвейер; • имеется лишь ограниченное число базовых типов (например, нет поддержки строк и чисел с фиксированной точкой). Кроме того, к сожалению, отсутствуют в доступном виде реальные инструментальные средства, поддерживающие ISDL, так как инициаторы проекта ограничились только реализацией ассемблера, некоторых модулей симулятора GENSIM и кодогенератора для компилятора в качестве диссертационных работ MIT. 29
Язык EXPRESSION разрабатывался в Университете Калифорнии (University of California, Irvine, США) [48] и был впервые представлен на конференции DATE в 1999 году [49]. Этот язык поддерживает широкий класс встраиваемых систем с ILP и иерархиями памяти от RISC, DSP, ASIP до VLIW. EXPRESSION позволяет создавать интегрированное описание структуры и поведения подсистемы процессор-память. Спецификация на EXPRESSION состоит из шести секций (первые три отвечают за поведение, последние три за структуру): • OP_GROUP – спецификация операций (элементарных команд (OP_CODE) c описанием параметров и поведения); • INSTR – описание формата команды в виде набора ячеек (SLOTS), имеющих определенное положение и ширину в командном слове и ответственных за определенный функциональный модуль; • OP_MAPPING – отображение общих (generic) операций компилятора на машинные операции, описанные в первой секции; данное описание используется при генерации кодогенератора компилятора; • описание структурных компонентов – функциональные устройства (UNIT), элементы памяти (STORAGE), шины (CONNECTION) и порты (PORT); задаются связи между ними, а также некоторые свойства (например, типы операций, которое может выполнять устройство, и количество параллельно выполняемых за такт операций); • описание конвейера (PIPELINE) в виде упорядоченных именованных стадий, связанных с функциональными устройствами; секция также содержит описание каналов передачи данных (DTPATHS). • STORAGE PARAMETERS – описание свойств элементов памяти: тип (регистровая память, кэш, SRAM, DRAM), количество и размерность ячеек, ассоциативность кэша, адресное пространство, время доступа. Пример 7 содержит описание тривиального модельного процессора на языке EXPRESSION. (OP_GROUP alu_ops (OP_CODE add (OP_TYPE DATA_OP) (OPERANDS (DST reg) (SRC1 reg) (SRC2 reg)) (BEHAVIOR DST = SRC1 + SRC2) ) (OP_CODE sub (OP_TYPE DATA_OP) (OPERANDS (DST reg) (SRC1 reg) (SRC2 reg)) (BEHAVIOR DST = SRC1 - SRC2) ) ) 30
альтернатив (DSE) и не рассчитаны на создание кросс-инструментов для разработки реальных прикладных программ. Поэтому полностью отсутствует промежуточный ассемблерный уровень – сценарий использования предполагает компиляцию программы на C во внутреннее промежуточное представление, которое непосредственно используется симулятором для получения оценок производительности, но не позволяет вести пошаговую отладку программы и использовать алгоритмы на ассемблере. Соответственно, в языке отсутствуют средства описания ассемблерного синтаксиса и двоичного кодирования команд. Также, ввиду наличия детальной структурной составляющей, начальное создание спецификации EXPRESSION является относительно трудоемким по сравнению с nML и ISDL. Кроме того, необходимость согласовывать поведенческие и структурные части описания делает неудобными и чреватыми ошибками изменения на уровне системы команд. В этом смысле EXPRESSION стоит между чистыми поведенческими ADL-решениями и структурными описаниями HDL-уровня.
(VAR_GROUPS (reg RF) ) (INSTR (WORDLEN 12) (SLOTS ((TYPE DATA) (BITWIDTH 12) (UNIT ALU)) ) ) (SUBTYPE UNIT ExUnit) (SUBTYPE STORAGE RegFile) (ExUnit ALU (CONNECTIONS AluRfConn) (OPCODES alu_ops) (CAPACITY 1) )
2.4. Языки программирования общего назначения
(RegFile RF (CONNECTIONS AluRfConn) ) (PIPELINE FETCH DECODE EX) (EX: ALU) (DTPATHS (TYPE BI (ALU RF AluRfConn) )) (STORAGE PARAMETERS (RF (TYPE REGFILE) (SIZE 16) (WIDTH 16) ) ) Пример 7. Тривиальный процессор в EXPRESSION На основе описания EXPRESSION автоматически генерируются компилятор EXPRESS и симулятор SYMPRESS [50-51]. Также существуют дополнительные утилиты для исследования и оценки использования иерархий памяти (MEMOREX) и визуальное средство для автоматизации процесса оценки и анализа различных архитектурных решений в процессе дизайна аппаратуры (V-SAT). Однако опубликованная скорость работы симулятора SYMPRESS является недостаточной для интерактивного процесса обработки миллиардов тактов в типичном цикле разработки современных алгоритмов цифровой обработки сигналов. К сожалению, как сам язык EXPRESSION, так и поддерживающие его средства нацелены только на обеспечение процесса исследования проектных 31
Еще одним методом описания модели аппаратуры и построения соответствующего инструментария кросс-разработки является непосредственное использование высокоуровневых языков программирования общего назначения для реализации симулятора, ассемблера, дисассемблера, отладчика и прочих необходимых инструментов без использования каких либо специализированных для аппаратного обеспечения формальных описаний. Основными языками для этой цели обычно служат C и C++. На практике большинству производителей встраиваемых процессоров (см. например, [52-57]) и многим компаниям, специализирующимся на разработке кросс-инструментов под заказ (например [58-65]), приходится использовать именно этот подход, чтобы получить кросс-инструментарий производственного качества как по уровню производительности, так и по удобству и богатству функциональности. Это становится возможным благодаря универсальным возможностям указанных языков программирования для реализации различных решений, оптимизированных под конкретную целевую аппаратуру. Однако это достается дорогой ценой в терминах трудоемкости и календарного времени на создание кросс-инструментов, так как подразумевает большой объем ручного программирования. В случае разработки «с чистого листа» затраты на построение производственной версии полного набора кроссинструментария составляют около 6-9 месяцев, и для этого требуются команды, включающие около 10 квалифицированных программистов. Адаптация уже существующих для подобной аппаратуры собственных (знакомых конкретным программистам) решений и повторное использование соответствующих библиотек позволяет сократить эти сроки и трудозатраты в несколько раз. Однако главным ограничением такого подхода все равно 32
остается требование к наличию устоявшегося описания целевой аппаратуры, так как адаптация построенных вручную инструментов даже к незначительным изменениям в спецификации аппаратуры требует существенных затрат и чревата ошибками. Например, как правило, несколько дней требуется для полного цикла проектирования, кодирования и тестирования необходимых изменений для отражения смены кодировки, синтаксиса и поведения всего одной команды. Данное ограничение не позволяет использовать такой подход на этапе проектирования аппаратуры, оставляя его возможным для применения только при построении инструментов для разработки прикладных программ уже на этапе серийного производства чипов (в условиях устоявшейся спецификации).
3. Анализ существующих подходов В предыдущих разделах были рассмотрены различные средства описания аппаратуры и соответствующие методы построения кросс-инструментов. В данном разделе проводится итоговый сравнительный анализ рассмотренных решений. Итак, качественные характеристики описанных выше классов языков для описания аппаратуры и соответствующих методов построения кроссинструментария представлены в табл. 1. На основе данных, приведённых в табл. 1, можно сделать следующие выводы (см. краткую сводку в табл. 2): 1.
2.
HDL-языки описания аппаратуры позволяют описывать наиболее точные модели аппаратуры и автоматически получать соответствующий симулятор, однако их использование в качестве основы для построения кросс-инструментария для прототипирования аппаратуры и разработки прикладных программ не представляется целесообразным ввиду низкой скорости работы симулятора, высокой трудоемкости построения модели аппаратуры и сложности внесения в нее изменений на уровне системы команд, а также из-за отсутствия в языке конструкций для задания информации, необходимой для построения остальных кроссинструментов в дополнение к симулятору.
Метод
Характеристика Уровень детализации описания модели аппаратуры
На основе HDL-языков
На основе ADL-языков
(VHDL, Verilog, SystemC)
(nML, ISDL, EXPRESSION, …) Высокоуровневое описание на уровне системы команд и иерархии памяти с элементами структурного описания в некоторых языках.
Специальных возможностей для описания аппаратуры нет, но общие возможности языков программирования позволяют задать модель на любом уровне.
Только на уровне Поддержка спецификации модулей расширяемых архитектур
Отсутствует (кроме EXPRESSION, но и там без возможности разделения описаний)
Специальных возможностей нет, но можно реализовать вручную.
Внесение изменений в спецификацию аппаратуры на уровне системы команд
Трудоемко из-за структурного характера описания (система команд явно не описывается)
Относительно легко и согласованным образом. На основе измененной спецификации инструменты обновляются автоматически.
Трудоемко и чревато несогласованностями, так как одно и то же свойство (например, код операции) может быть запрограммировано в нескольких местах разных инструментов.
Построение симулятора
Генерируется автоматически
Генерируется автоматически
Программируется вручную
Точность симуляции
Потактово-точная Потактовая точность обычно не достигается
Возможно достижение потактовой точности
Скорость симуляции
Низкая (~0,05 – 0,1 MIPS)
Средняя (~1-5 MIPS)
Высокая (~10-20 MIPS)
Построение ассемблера,дисассемблера, компоновщика, отладчика и т.д.
Невозможно из-за отсутствия необходимой информации в спецификации
Генерируются автоматически. Однако функциональность и скорость работы, как правило, невысокого уровня.
Программирование всех необходимых инструментов вручную. Возможно достижение высокого качества по скорости работы и функциональности.
ADL-языки предоставляют наиболее удобные возможности для описания моделей аппаратуры на уровне системы команд, а соответствующий метод автоматического построения кросс-инструментария позволяет эффективно использовать получаемые инструменты для прототипирования аппаратуры. Однако использование существующих ADL-решений не позволяет получать инструменты качества, достаточного для разработки реальных программ, из-за неточности получаемой модели, невысокой скорости работы и ограниченной функциональности получаемых инструментов.
3.
33
34
Детальное описание точной структуры на уровне регистровых передач (RTL).
На основе языков программирования общего назначения (С, С++, …)
Таблица 1. Характеристики языков описания аппаратуры и соответствующих методов построения кросс-инструментов. Ручная реализация необходимых кросс-инструментов на языках программирования общего назначения на сегодняшний день является
практически единственным методом построения полного набора кроссинструментария производственного качества (с высокой скоростью работы, достаточной точностью модели и удобной функциональностью, адаптированной под конкретную аппаратуру). Однако этот способ практически исключает возможность использовать такой кроссинструментарий на этапе прототипирования аппаратуры из-за высокой трудоемкости и временных затрат для создания начальной версии инструментария и для последующих циклов его обновления для отражения различных вариаций проектируемой аппаратуры. Отдельно стоит отметить, что ни один из указанных методов явно не поддерживает расширяемые архитектуры в смысле обеспечения разделения технической ответственности и интеллектуальной собственности в получаемых для полной системы инструментах между производителями отдельных компонентов. Область применения Для прототипирования аппаратуры Для производственной разработки прикладных программ
На основе HDL-языков Ограничено Практически невозможно
На основе ADL-языков Эффективно Ограничено
На основе C, C++, … Практически невозможно Эффективно
Таблица 2. Эффективность применения кросс-инструментов, получаемых рассмотренными методами на основе различных языков. Таким образом, наиболее близким (хотя и с существенными недостатками) из рассмотренных методов построения кросс-инструментов для решения поставленных задач является метод автоматической генерации кросс-инструментов на основе спецификации на некотором ADL-языке. Рассмотрим более подробно сравнительные характеристики описанных выше ADL языков и соответствующих инструментальных средств поддержки (см. табл. 3). Таким образом, ни один из существующих ADL-языков не позволяет описывать потактово-точные модели аппаратуры, включающие в себя все типичные для встраиваемых архитектур возможности: конвейер, прерывания, циклы с нулевой задержкой, периферию, зависимости между командами и отдельными операндами (для диагностики конфликтов). Поддержка расширяемой аппаратуры присутствует только в EXPRESSION, но и там соответствующие описания могут быть созданы только в рамках единой спецификации всей системы, что не позволяет разделять описания отдельных компонентов. Кроме того, ни одно из существующих инструментальных решений на основе ADL-языков не поддерживает генерацию полного набора 35
кросс-инструментов, а скорость работы и удобство использования отдельных получаемых компонентов остаются на низком уровне. Характеристика
nML
ISDL
EXPRESSION
Описание синтаксиса ассемблера и бинарного кодирования команд
+
+
-
Описание потактового поведения команд
-
-
неявно
Описание иерархии памяти
+
+
+
Описание структуры функциональных модулей
-
-
+
Общие возможности
Поддержка описания различных особенностей аппаратуры Машинное слово переменной длины
-
+
-
Межкомандные зависимости
-
+
?
Ограничения на комбинации операндов команд
+
+
-
Конвейер
-
-
+
Прерывания
-
-
-
Циклы с нулевой задержкой
-
-
-
Сопроцессоры
-
-
+
Периферийные устройства
-
-
-
+
+
+
106
?
+106
Генерация ассемблера
+
+
-
Генерация дисассемблера
+
-
-
Генерация компоновщика
+
+
-
Генерация пошагового символьного отладчика
-
-
-
Интегрированная среда разработки (IDE)
-
-
-
Возможности инструментальной поддержки Генерация симулятора Потактово-точная симуляция Скорость симулятора (модельных тактов в сек.)
Таблица 3. Возможности основных современных ADL-языков и соответствующих инструментальных средств поддержки. 36
Таким образом, ни один из существующих ADL-языков не позволяет описывать потактово-точные модели аппаратуры, включающие в себя все типичные для встраиваемых архитектур возможности: конвейер, прерывания, циклы с нулевой задержкой, периферию, зависимости между командами и отдельными операндами (для диагностики конфликтов). Поддержка расширяемой аппаратуры присутствует только в EXPRESSION, но и там соответствующие описания могут быть созданы только в рамках единой спецификации всей системы, что не позволяет разделять описания отдельных компонентов. Кроме того, ни одно из существующих инструментальных решений на основе ADL-языков не поддерживает генерацию полного набора кросс-инструментов, а скорость работы и удобство использования отдельных получаемых компонентов остаются на низком уровне.
4. Заключение В статье была рассмотрена задача построения инструментария кроссразработки для расширяемых встраиваемых систем. Показана важность раннего решения этой задачи уже на этапе проектирования аппаратуры для обеспечения тонкой оптимизации проектных решений в процессе прототипирования системы, для верификации HDL-моделей и, наконец, для разработки реальных целевых программ. Были сформулированы требования к «идеальному» методу создания кросс-инструментов, и в призме этих требований были рассмотрены и проанализированы существующие средства описания аппаратуры, пригодные для автоматизированного построения кроссинструментов на их основе. Выделено три класса таких средств – HDL языки синтезируемого описания аппаратуры (VHDL, Verilog, SystemC), ADL-языки (nML, ISDL, EXPRESSION) и языки программирования общего назначения (C/C++). К сожалению ни одно из существующих решений не удовлетворяет сформулированным требованиям, не позволяя эффективно строить кроссинструментарий с необходимыми свойствами. Именно поэтому, по мнению автора, перспективным направлением является исследование и разработка новых методов автоматизированного построения кросс-инструментов для расширяемых встраиваемых систем на основе комбинированных описаний моделей аппаратуры. В частности, интересным направлением кажется объединение преимуществ высокоуровневого описания системы команд на языках типа ADL и эффективного описания деталей управляющей логики и периферии на языках программирования общего назначения. Такая комбинации позволила бы получать кросс-инструментарий с достаточными скоростью работы и точностью моделирования, обеспечивая при этом возможность быстрого внесения согласованных изменений для отражения различных вариаций аппаратуры, возникающих в процессе проектирования (как на уровне изменений ядра, так и на уровне изменений расширений и их состава в полной системе). Именно такой кросс-инструментарий был бы 37
пригоден для эффективного решения поставленных задач прототипирования встраиваемой системы, верификации HDL-моделей и разработки реальных программ. Литература [1] Я.А. Хетагуров. Из истории развития специализированных бортовых вычислительных машин. http://www.computer-museum.ru/histussr/special.htm. [2] В.В. Липаев. Из истории развития отечественной вычислительной техники для военных систем управления в реальном времени. http://www.computer-museum.ru/histussr/16.htm. [3] К. Колпаков. История развития авиационных бортовых цифровых вычислительных машин в России. http://www.computer-museum.ru/histussr/stpc.htm. [4] http://en.wikipedia.org/wiki/Apollo_Guidance_Computer. [5] А. Бухтев. Проектирование встроенных систем: от концепции до кристалла. Журнал «Электронные компоненты», 2007, №1. [6] A. Parker, etc. System-Level Design. The VLSI Handbook—2nd ed. CRC Press, 2007. [7] Donald R. Cottrell. Design Automation Technology Roadmap. The VLSI Handbook— 2nd ed. CRC Press, 2007. [8] Software-Hardware Codesign. // IEEE Design & Test of Computers, January-March 2000. pp.92-99. [9] M L Vallejo, J C Lopez, ”On the hardware-software partitioning problem: System Modeling and partitioning techniques”, ACM TODAES, V-8, 2003. [10] K Ben Chehida, M Auguin, ”HW/SW partitioning approach for reconfigurable system design”, CASES 2002. [11] J Henkel, R Ernst, ”An approach to automated hardware/software partitioning using a flexible granularity that is driven by high-level estimation Techniques”, IEEE Transactions on VLSI, V-9, 2001. [12] R Ernst, J Henkel, T Benner. ”Hardware-software co-synthesis for microcontrollers”, IEEE Design and Test,V-10, Dec 1993. [13] Wayne Wolf. Embedded Computing Systems and Hardware/Software Co-Design. The VLSI Handbook—2nd ed. CRC Press, 2007. [14] Embedded C. Стандарт ISO/IEC TR 18037:2004. [15] Executable and Linking Format in Wikipedia. http://en.wikipedia.org/wiki/Executable_and_Linkable_Format. [16] Generic ELF Specification. http://www.linux-foundation.org/spec/book/ELFgeneric/ELF-generic/book1.html. [17] M. Hartoog, J. Rowson, P. Reddy. Generation of Software Tools from Processor Descriptions for Hardware/Software Codesign. Design Automation Conference (DAC) 1997. [18] Lin Yung-Chia. Hardware/Software Co-design with Architecture Description Language. Programming Language Lab. NTHU. 2003. [19] Д.Ю. Булычев. Разработка программно-аппаратных систем на основе описания макроархитектуры. Сборник Системное программирование. Санкт-Петербург, 2004. [20] Z. Navabi. Languages for Design and Implementation of Hardware. The VLSI Handbook—2nd ed. CRC Press, 2007.
38
[21] А.К. Поляков. Языки VHDL и VERILOG в проектировании цифровой аппаратуры. // М.: Солон-Пресс, 2003. 320 с. [22] П.Н. Бибило. Синтез логических схем с использованием языка VHDL. // М.: СОЛОН-Р, 2002. 384 с. [23] Volnei A. Pedroni. Circuit Design with VHDL. // MIT Press, 2004. [24] IEEE Standard VHDL Language Reference Manual. IEEE Std 1076-1987. [25] IEEE Standard Multivalue Logic System for VHDL Model Interoperability. IEEE Std 1164. [26] IEEE Standard VHDL Synthesis Packages. IEEE Std 1076.3-1997. [27] M. Rofoue, Z. Navabi. RT Level Hardware Description with VHDL. The VLSI Handbook—2nd ed. CRC Press, 2007. [28] Weng Fook Lee. Verilog Coding for Logic Synthesis. // John Wiley & Sons, 2003. [29] IEEE Standard Hardware Description Language Based on the Verilog Hardware Description Language. IEEE Std 1364-2005. [30] Z. Navabi. Register Transfer Level Hardware Description with Verilog. The VLSI Handbook—2nd ed. CRC Press, 2007. [31] Open SystemC Initiative. http://www.systemc.org. [32] IEEE Standard System C Language Reference Manual. IEEE Std 1666-2005. [33] S. Mirkhani and Z. Navabi. Register-Transfer Level Hardware Description with SystemC. The VLSI Handbook—2nd ed. CRC Press, 2007. [34] Stephen Bailey. Comparison of VHDL, Verilog and SystemVerilog. Model Technology White Paper. [35] Synopsys VCS http://www.synopsys.com/products/simulation/simulation.html http://www.synopsys.com/products/simulation/vcs_ds.pdf [36] Mentor Graphics ModelSim. http://www.mentor.com/products/fv/digital_verification/index.cfm. [37] Cadence NC-Sim. http://www.cadence.com/datasheets/4492C_IncisiveVerilog_DSfnl.pdf [38] Cadence Incisive Simulators. http://cadence.com/products/functional_ver/simulation/index.aspx [39] P. Mishra and N. Dutt. Architecture description languages for programmable embedded systems. // IEEE Proceedings Computers and Digital Techniques., Vol. 152, No. 3, May 2005. [40] W. Qin, and S. Malik. Architecture description languages for retargetable compilation. // The Compiler Design Handbook, CRC Press, 2002. [41] H. Tomiyama, A. Halambi, P. Grun, N. Dutt, A. Nicolau. Architecture Description Languages for Systems-on-Chip Design. // Proc. Asia Pacific Conf. on Chip Design Language, 1999, pp. 109–116. [42] Rainer Leupers. Retargetable Code Generation for Digital Signal Processors. Kluwer Academic Publishers, 1997. [43] M. Freericks. The nML Machine Description Formalism. Technical Report 1991/15, TU Berlin, Fachbereich Informatik, 1991. [44] A. Fauth, J. Van Praet, M. Freericks. Describing instruction set processors using nML. In Proc. of ED&TC, 1995. [45] Chess/Checkers Products. Target Compiler Technology. http://www.retarget.com/. [46] ISDL Project Homepage. http://caa.lcs.mit.edu/caa/home.html. [47] G. Hadjiyannis, S. Hanono, S. Devadas. ISDL: An Instruction Set Description Language for Retargetability. Design Automation Conference (DAC) 1997. [48] EXPRESSION Homepage. http://www.cecs.uci.edu/~aces/index.html.
39
[49] Ashok Halambi, Peter Grun, Vijay Ganesh, Asheesh Khare, Nikil Dutt and Alex Nicolau. EXPRESSION: A Language for Architecture Exploration through Compiler/Simulator Retargetability, DATE 99. [50] P. Mishra, A. Shrivastava, N. Dutt. ADL-driven Software Toolkit Generation for DSE. ACM Transactions on Design Automation of Electronic Systems, pp. 1-31, 2006. [51] M. Reshadi, N. Dutt, P. Mishra. A Retargetable Framework for Instruction-Set Architecture Simulation. ACM Transactions on Embedded Computing Systems, Vol. 5, No. 2, pp. 431–452, May 2006. [52] Analog Devices Processor Development Tools. http://www.analog.com/processors/platforms/processorDevTools.html. [53] Texas Instruments Tools & Software Overview. http://focus.ti.com/dsp/docs/dspfindtoolswbytooltype.tsp?sectionId=3&tabId=2088&too lTypeId=1&familyId=44. [54] Freescale CodeWarrior Development Tools. http://www.freescale.com/webapp/sps/site/homepage.jsp?nodeId=012726&tid=FSH. [55] LSI DSP Products. http://www.lsi.com/networking_home/networking_products/dsps/index.html. [56] NXP (Philips Semiconductors) Development Tools for Microcontrollers. http://www.nxp.com/products/microcontrollers/support/development_tools/. [57] ARM RealView Development Tools. http://www.arm.com/products/DevTools/. [58] TASKING - Embedded Software Development Tools. http://www.tasking.com/. [59] Raisonance Embedded Development Tools. http://www.raisonance.com/. [60] Signum Embedded Development Tools. http://signum.com/. [61] Nohau (ICE Technology) Development Tools. http://www.icetech.com/. [62] Keil Embedded Development Tools. http://www.keil.com/. [63] Green Hills MULTI Integrated Development Environment. http://www.ghs.com/products/MULTI_IDE.html. [64] IAR Embedded Development Tools. http://www.iar.com/. [65] iSystem Solutions for Embedded System Development. http://www.isystem.com/.
40
общественно-экономического прогресса становится одной из наиболее серьезных и тяжело устранимых причин возникающих кризисов. Обучение
Роль научных организаций в подготовке ИТ- специалистов А. К. Петренко, О. Л. Петренко, В. В. Кулямин {petrenko,olga,kuliamin}@ispras.ru}
Исследования
Аннотация. Рассматривается проблема подготовки специалистов высокой квалификации в области информационных технологий (ИТ), чья профессиональная карьера связывается не только с использованием таких технологий, а, в первую очередь, с созданием новых технологий мирового уровня. В статье предлагается использовать для решения проблем современного ИТ-образования подход, основанный на принципах «системы Физтеха», прежде всего — ресурсы научных и научнопроизводственных учреждений. В рамках такой системы студенты получают возможность участвовать в работе по специальности совместно с ведущими специалистами в соответствующей области. Это позволяет им перенимать как практические навыки работы и неявные, плохо вербализуемые знания, так и чрезвычайно важные для современных ИТ-специалистов навыки анализа командной работы и профессионального общения.
Рис.1. Треугольник Лаврентьева. В современном мире действует ряд факторов, которые еще более обостряют проблемы, возникающие из-за несоответствия возможностей образовательных структур потребностям общества. • Ускоренное технологическое развитие и быстрое проникновение его результатов в повседневную жизнь. Это приводит к тому, что невозможно предсказать набор навыков и знаний, которые понадобятся нынешним молодым людям через 10-15 лет. •
Быстрая смена областей и географического положения очагов экономического роста, делающая необходимым поддержание высокой мобильности рынка труда, возможности для его участников быстро и эффективно изменять область деятельности, дабы с минимальными потерями избегать последствий множества социальных и экономических микрокризисов, возникающих вследствие ускоренного развития.
•
Вовлеченность широких слоев населения в глобальные экономические процессы, еще более увеличивающая необходимость обеспечения высокой мобильности специалистов. Недостаток ее приводит к расслоению населения, нарастанию различий между представителями ранее однородных социо-культурных групп, связанному с разной их вовлеченностью в технологическое развитие и процессами миграции. Это тяжелее всего сказывается на молодежи и еще больше усугубляет возникающие в различных регионах мира и областях экономики кризисные явления.
1. Проблемы ИТ-образования Потенциал развития общества во многом определяется соответствием системы образования потребностям и нуждам этого развития. При необходимости ускоренного развития общества всегда возникает потребность в инновациях в системе образования. Так, в 40-50-х годах прошлого века, когда необходимо было ускоренными темпами восстанавливать экономику страны и одновременно развивать новые наукоемкие отрасли промышленности, ведущие советские ученые [1] сформулировали принцип подготовки специалистов высокого уровня — их полноправное участие в работе коллективов, выполняющих научные исследования и разработки для решения конкретных промышленных задач. Этот принцип был кратко сформулирован М. А. Лаврентьевым как «наука– кадры–производство» и известен под названием «треугольник Лаврентьева» (см. Рис. 1) [2]. Идея такой интеграции обучения с научно-производственной практикой активно реализовывалась в советское время, но в 90-ые по многим причинам система такой подготовки была нарушена. Неспособность подготовить специалистов, которые необходимы для дальнейшего как технического, так и 41
Производство
Информационные технологии (ИТ) являются одним из фокусов развития современного общества. Несмотря на то, что они широко используются в современной экономике, их эволюция продолжается, и нас еще ждут серьезные преобразования, связанные с более широким и систематичным 42
Все перечисленные факторы обуславливают необходимость повышенного внимания к подготовке ИТ-специалистов, особенно специалистов высокой квалификации, чья профессиональная карьера связывается, в первую очередь, не с использованием имеющихся технологий, а с созданием новых технологий мирового уровня. Для решения задачи подготовки таких специалистов необходимы специальные меры, которые помогли бы обеспечить соответствие получаемых знаний и навыков как практическим потребностям самих студентов и их будущих работодателей, так и целям развития общества в целом. Одним из проявлений имеющихся в нашей стране проблем ИТ-образования является нехватка специалистов в области ИТ. По данным исследования АП КИТ [3] потребность российской экономики в ИТ-специалистах в 2007 году составила 188 тыс. человек, что в 2.7 раз больше, чем число выпускников профильных образовательных учреждений в этом же году. Прогнозы показывают, что потребность в ИТ-специалистах будет каждый год расти, опережая количество выпускников по соответствующим специальностям в 3-6 раз. 43
Теоретическая информатика (computer science)
Информационные системы
Программное обеспечение
Бизнес
Аппаратное обеспечение
Программное обеспечение
Информационные системы
Информационные технологии
Программная инженерия
После 1990-х годов
Теоретическая информатика
Все острее становится проблема подготовки ИТ-специалистов высшей квалификации, обладающих широким кругозором и глубокими знаниями, как в этой области, так и в ряде смежных, способных адекватно оценивать перспективы и риски новых технологий, проводить исследования в ИТ, видеть во всей полноте альтернативные решения конкретной технической задачи и их влияние на итоговые, социально-экономические характеристики ИТпродуктов и услуг.
Аппаратное обеспечение
Компьютерная инженерия
•
Источники знаний по ИТ, включая программы курсов и знания преподавателей, чаще всего развиваются медленнее, чем технологии, поэтому освоение новых технологий становится все труднее, так же, как и подготовка педагогов, способных обучить им. Быстрое развитие приводит к необходимости часто менять область деятельности, чтобы оставаться в числе ведущих ИТ-специалистов, и поэтому у таких специалистов все меньше времени остается для передачи накопленного опыта и знаний, участия в воспроизводстве кадров в этой отрасли.
Электротехника и компьютерная инженерия
•
До 1990-х годов
Электротехника
использованием ИТ во всех сферах человеческой деятельности. Поэтому в ИТ достаточно ярко отражаются все особенности современного развития. • Информационные технологии развиваются исключительно быстро, достаточно не следить за последними достижениями и тенденциями в течении 3-5 лет, чтобы полностью потеряться в потоке новых терминов и технологических новшеств. При этом их развитие кумулятивно — новые технологии часто включают в себя элементы почти всех предшествующих.
Организационные нужды
Рис. 2. Схема изменения содержания дисциплин ИТ. Второе проявление тех же проблем — это несоответствие уровня подготовки выпускников требованиям работодателей. Для решения этой частной проблемы существует несколько подходов: дополнительное обучение специалистов при поступлении на работу в компанию или организация совместных с крупными фирмами программ обучения в институте. Недостаток этих подходов состоит в том, что таким образом готовят специалистов для конкретных работ, и любое серьезное изменение внешних факторов требует их переподготовки, Соответственно, действуя таким образом, подготовить специалиста высшей квалификации можно только случайно. Это происходит из-за того, что фирмы готовят специалистов только для себя, не разделяя подготовку на базовый и прикладной уровень. Базовый уровень должен быть основой многих специальностей в области ИТ, а прикладной уровень должен достаточно быстро формироваться на основе базового. Для нормального развития экономики нужно также выпускать определенное 44
количество специалистов высокой квалификации, которым необходимо получать знания базового уровня с некоторым запасом, чтобы легче было адаптировать их к конкретным задачам, а также можно было принимать участие в создании новых технологий и знаний. При переходе определенных границ базовый уровень знаний по специальности изменяется, например, изменения требований к знаниям в разных областях хорошо видны на диаграмме, показывающей трансформацию содержания специальностей в рекомендациях ACM по преподаванию вычислительной техники и информатики [4] — Рис. 2.
2. Система Физтеха и ее использование в настоящее время Проблемы подготовки специалистов в области ИТ-технологий вызывают появление многочисленных предложений, направленных на повышение эффективности подготовки специалистов разного уровня. Однако в первую очередь стоит использовать уже имеющиеся ресурсы. Прежде всего, можно выделить «систему Физтеха» — принципы системы образования в МФТИ, впервые сформулированные лауреатом Нобелевской премии, академиком Петром Леонидовичем Капицей в письме И. В. Сталину в 1946 году [5,6]: 1) тщательный отбор одаренных и склонных к творческой работе представителей молодежи; 2) непосредственное участие в обучении ведущих научных работников и тесном контакте с ними в их творческой обстановке; 3) индивидуальный подход к отдельным студентам с целью развития их творческих задатков при отсутствии имеющейся сейчас в вузах перегрузки второстепенными предметами по общей программе и механического заучивания (следствие необходимости массового обучения); 4) ведение воспитания с первых же шагов в атмосфере технических исследований и конструктивного творчества с использованием для этого лучших лабораторий страны. Приведенный набор принципов образования в свое время способствовал подготовке очень хороших специалистов. Один из способов реализации этих принципов — создание базовых кафедр в научных учреждениях страны. Институт системного программирования РАН является одной из ведущих исследовательских организаций в области разработки программного обеспечения. Одну из своих задач институт видит в подготовке специалистов в области ИТ-технологий. ИСП РАН является базовой организацией для двух кафедр системного программирования ведущих вузов страны: факультета ВМиК МГУ им. М.В. Ломоносова и МФТИ. Специфика ИСП РАН как института Академии наук позволяет в настоящее время использовать 45
потенциал научных учреждений для воспитания нового поколения специалистов в области ИТ. Одна из особенностей обучения на базе института — это знакомство студентов с методикой и культурой научных исследований и научной работой изнутри. Кроме того, известно, что знания делятся на явные, передаваемые общедоступным, кодированным способом, и неявные — скрытые, незафиксированные знания, которые передаются только посредством обмена опытом и совместной работы над различными задачами [7]. Для передачи скрытых знаний базовая организация является практически идеальной средой. Во время такой работы студенты получают не только знания и практические навыки работы по специальности, но и ряд навыков социального и коммуникативного характера, которые, в основном, и делают их к концу обучения грамотными специалистами, способными эффективно работать в коллективе над общей задачей. Общая схема навыков и знаний, которые необходимы современным специалистам в области ИТ, изображена на Рис. 3. Организованность Автомотивация Инициативность и творческий подход
Профессиональное общение Командная работа Умение преподносить информацию Этичное поведение
Персональные навыки
Коммуникативные навыки
Технические навыки
Знания
Умение искать информацию Систематизация информации Выделение важных моментов Оценка рисков Построение и анализ моделей Изобретение и анализ решений Целостная оценка ситуации
Знания по специальности Знания по ИТ в целом Юридические и этические аспекты ИТ Знания из других областей
Рис. 3. Схема знаний и навыков специалиста по ИТ. При использовании модели Физтеха необходимо учитывать реалии современного общества. Сейчас ее действенность сильно зависит от экономической эффективности базовых организаций и от их способности 46
удержать студентов при помощи материальных или иных стимулов. Есть и другие факторы, требующие внести поправки в эту систему при ее использовании в современной ситуации. Если раньше студент работал над одной, достаточно узкой темой в ходе обучения, а становясь аспирантом, часто продолжал исследования по той же теме, то более высокая неопределенность капиталистической экономики и более динамичное развитие бизнеса и технологий требуют более широкого и комплексного подхода при обучении студента в процессе работы. Основные техники, используемые в таком подходе, таковы. • Центральным элементом системы передачи знаний студентам является традиционное участие работников базовых организаций в обучении в рамках обычной вузовской программы. •
В приобретение практических навыков работы по специальности основной вклад вносит непосредственное участие студентов в проектах, ведущихся в базовой организации. При этом они ставятся в условия, в которых полноценное общение между всеми участниками проекта необходимо для его успешного проведения.
•
Чтобы сгладить переход от исключительно персонального обучения к работе внутри коллектива используются техники персонального наставничества. При этом 2-3 студента прикрепляются к работнику базовой организации, и он обязан следить за организацией их времени, оказывать им организационную или наставническую помощь в трудных и новых для них ситуациях, а также указывать необходимые источники дополнительной информации.
•
Студенты пополняют знания по специальности и смежным областям, участвуя в тренингах и программах дополнительного обучения, имеющихся в базовой организации.
•
Основой для получения полного набора навыков по специальности и расширения кругозора студента является его участие в различных проектах, в разных видах деятельности над разными типами продуктов или услуг.
•
На завершающих этапах обучения необходимо дополнительно стимулировать дальнейший профессиональный рост студента, его мотивацию и ответственность. Для этого студенты привлекаются к обсуждению основных проектных решений, им предоставляется возможность высказывать свою точку зрения и аргументировать ее, они участвуют в решении творческих задач, выступают на семинарах с докладами по темам, касающихся их работы.
47
3. Заключение В данной работе авторы предлагают использовать ресурсы научных и научнопроизводственных учреждений для повышения качества обучения специалистов в области ИТ. Основные идеи такого обучения были сформулированы еще в середине XX века в виде «системы Физтеха». В ее рамках студенты с некоторого момента все больше времени проводят не в учебных аудиториях, а в качестве непосредственных участников реальных проектов, выполняемых в базовой организации. Однако одной вовлеченности в реальные, иногда рутинные производственные проекты для подготовки будущих создателей новых технологий недостаточно. Простое участие студентов лучших вузов страны в практической работе, как правило, не позволяет раскрыть и развить их творческий потенциал. Распространенная практика использования студентов лишь как относительно дешевой рабочей силы порождает мнение, что и сама «система Физтеха» в современных условиях перестала работать. В действительности такие умозаключения возникают из-за того, что существенная часть идей П. Л. Капицы упускается. Требуется не только формальная вовлеченность студентов в практическую работу базовых организаций. Нужны специальные техники и усилия по обучению в процессе работы, способствующие расширению кругозора студентов, получению ими навыков эффективного профессионального общения, повышению их мотивации и ответственности. Представленный в работе подход активно используется при обучении студентов ВМиК МГУ и МФТИ в Институте системного программирования РАН и дает значимые положительные результаты. По убеждению авторов использование этого подхода способно преодолеть нарастающее в последнее время расхождение между характеристиками специалистов в области ИТ, необходимых индустрии высоких технологий и исследовательским организациям, и уровнем выпускников большинства вузов по ИТ-специальностям. Таким образом, становится возможным привести образовательную систему в этой области в соответствие с нуждами дальнейшего развития страны и избежать возникновения серьезных кризисных явлений. Специфика обучения в базовых организациях позволяет перейти к формированию человеческого капитала, т.е. капитала в форме интеллектуальных способностей и практических навыков, полученных в процессе образования и практической деятельности человека. В настоящее время в экономике происходят структурные сдвиги, направленные на переход от «материальной» к «интеллектуальной» экономике — экономике, основанной на знаниях (knowledge-based economy) [8,9]. Понятие экономики, основанной на знаниях, отражает признание того, что научные знания и специализированные уникальные навыки их носителей становятся главным источником и ключевым фактором развития 48
материального и нематериального производства, обеспечения устойчивого экономического развития. В этих условиях человеческий капитал становится основным ресурсом развития общества. Литература [1] С. А. Христианович, М. А. Лаврентьев, С. А. Лебедев. Назревшие задачи организации научной работы. Правда, 14.02.1956. [2] Н. Л. Добрецов. «Треугольник Лаврентьева»: принципы организации науки в Сибири. Вестник Российской академии наук, т. 71, № 5, 2001, стр. 428–436. [3] Аналитическое исследование "ИТ-кадры в российской экономике. Численность занятых, текущая потребность и прогноз на 2012 год в ИТ-индустрии и отраслях народного хозяйства". http://www.apkit.ru/default.asp?artID=5835. [4] Computing Curricula 2005. The Overview Report. The Joint Task Force for Computing Curricula 2005. http://www.acm.org/education/curric_vols/CC2005-March06Final.pdf. [5] П. Л. Капица. Письма о науке. М., 1989. [6] http://museum.phystech.edu/books/book-museum/chapter4.html. [7] C. Meyer. Relentless Growth: How Silicon Valley's Innovation Secrets Can Work for You. New York: The Free Press, 1998. [8] F. Machlup. The Production and Distribution of Knowledge in the United States. Princeton: Princeton University Press, 1962. [9] P. Drucker. The Age of Discontinuity; Guidelines to Our changing Society. New York: Harper and Row, 1969.
49
Проблемы экономики производства крупных программных продуктов В.В. Липаев [email protected] Аннотация. Рассматриваются особенности экономики современного производства крупных программных продуктов, создаваемых коллективно по инициативе разработчиков или заказчика-потребителя, а также оценка их экономических характеристик путем маркетинговых исследований или по статистике прототипов продукта. Представлены проблемы анализа, выделения и оценивания факторов, определяющих экономические характеристики производства конкретных программных продуктов, экономического обоснования и прогнозирования эффективности технологий их производства. Сформулированы проблемы организации, планирования и применения экономически обоснованных методов автоматизации производства сложных комплексов программ, а также обеспечения их качества с учетом затрат ресурсов.
1. Введение Во многих предприятиях и ВУЗах отношение к программам для ЭВМ исторически базируется на подходе, как к «искусству и художественному творчеству» отдельных специалистов (программирование «в малом»). При этом считается, что невозможно применять, какие-либо экономические характеристики для определения и прогнозирования стоимости и результатов такого творчества, и они оцениваются только с позиции выполняемых функций и «эстетики» их реализации. Такие программы не предназначены для массового тиражирования и распространения как программного продукта на рынке, их оценивают качественно и интуитивно, преимущественно, как “художественные произведения”. При этом, как правило, нет конкретного независимого заказчика-потребителя, определяющего требования к программам и их финансирование, программы не ограничиваются заказчиком допустимой стоимостью, трудоемкостью и сроками их создания, требованиями обеспечения заданного качества и документирования. Их разработчики не знают и не применяют регламентирующих, нормативных документов производства, вследствие чего жизненный цикл таких изделий имеет непредсказуемый характер по структуре, содержанию, качеству и стоимости основных результатов творчества. Их создание не определяется экономическими 51
характеристиками и регламентированными производственными процессами и далее не рассматривается. Для небольших относительно простых проектов программных средств, во многих случаях достаточно достоверными могут быть интуитивные оценки требуемых экономических ресурсов, которые выполняются опытными руководителями, реализовавшими несколько аналогичных проектов. Однако интуитивные оценки руководителями размеров и сложности крупных программных проектов (программирование «в большом»), как правило, отличаются большими ошибками при планировании экономических характеристик − сроков, трудоемкости и стоимости создания продуктов. Это во многих случаях приводит к значительному запаздыванию завершения разработок и к превышению предполагавшихся затрат. Практика последних лет показывает, что вследствие пренебрежения тщательным экономическим обоснованием до 15% проектов сложных программных комплексов не доходит до завершения, а почти половина проектов не укладывается в выделенные ресурсы, бюджет и сроки, не обеспечивает требуемые характеристики качества. Типичны ситуации, когда отставание сроков внедрения промышленных систем управления и обработки информации полностью зависит от неготовности для них программных продуктов. Приступая к разработке сложных программных проектов, заказчики и исполнители, прежде всего, должны пытаться понять, целесообразно ли экономически создание соответствующих продуктов, и оценить, какова будет возможная эффективность применения готового продукта, оправдаются ли затраты на его разработку и использование. Поэтому такие технические проекты традиционно должны начинаться с анализа и разработки экономического обоснования предстоящего жизненного цикла предполагаемого продукта. Заказчику проекта необходимо оценивать реальную потребность в создании продукта и возможную конкурентоспособность, а потенциальному разработчику предполагаемого продукта – проводить оценку реализуемости проекта в условиях и ресурсах, предлагаемых заказчиком. Часто разработчики не в состоянии привести заказчику или руководителю проекта достаточно обоснованные доказательства нереальности выполнения выдвигаемых требований к продукту при предложенных ограниченных значениях бюджета и сроков. Это, как правило, означает, что программная часть системы с самого начала выходит из-под экономического контроля, и возможна катастрофа с реализацией и завершением проекта всей системы в требуемый срок с заданным качеством. Массовое создание сложных и дорогих программных продуктов промышленными методами и большими коллективами специалистов вызвало необходимость их достоверного экономического анализа и оценки, четкой организации производства, планирования работ по затратам, этапам и срокам 52
реализации. Для решения этих задач еще в 1980-е годы начала формироваться новая область знания и инженерная дисциплина − экономика производства крупных программных продуктов [1]. Необходимо было научить специалистов анализу и оцениванию конкретных факторов, влияющих на экономические характеристики проектов программных продуктов со стороны реально существующих и потенциально возможных воздействий, а также ограничений ресурсов проектов комплексов программ. Это привело к появлению новой области экономической науки и практики − экономики производственных процессов и жизненного цикла сложных программных продуктов как части экономики промышленности и вычислительной техники в общей экономике предприятий и государства. Ее основной задачей является прогнозирование, эффективное управление, распределение ресурсов и экономное использование необходимых быстро возрастающих капиталовложений в производство сложных комплексов программ высокого качества и различного назначения. Развитие этой области экономики связано с большими трудностями, типичными для новых разделов науки и техники, появляющихся на стыке различных областей знания. В данном случае особенности состоят в том, что менеджеры и разработчики комплексов программ, как правило, не знают даже основ экономики промышленного производства сложной продукции, а экономисты современного производства не представляют сущность и свойства объектов разработки − программных продуктов, а также особенностей экономики технологических процессов их проектирования, производства и применения. Объективно положение осложнено трудностью измерения экономических характеристик таких объектов. Широкий спектр количественных и качественных показателей, которые с различных сторон характеризуют содержание компонентов и комплексов программ, и невысокая достоверность оценки их значений определяют значительную дисперсию при попытке описать и измерить свойства создаваемых сложных программных продуктов для их экономического прогнозирования и оценки. Крупные программные продукты являются одними из наиболее сложных объектов, создаваемых человеком, и в процессе их производства творческая работа специалистов – поиск новых методов, альтернативных решений и способов осуществления заданных требований, а также формирование и декомпозиция этих требований – составляет значительную часть всех трудозатрат. Индустриализация производства комплексов программ позволяет автоматизировать нетворческие, технические и рутинные операции и этапы, а также облегчить творческие процессы за счет селекции, обработки и отображения информации, необходимой для принятия творческих решений. Следствием этого должно являться значительное сокращение доли затрат на творческий труд в непосредственных затратах на производство комплексов программ. 53
В производстве программ неуклонно повышаются размеры и сложность создаваемых продуктов, что вызывает возрастание затрат творческого труда на единицу размера новых программ. В перспективе, несмотря на автоматизацию и повышение инструментальной оснащенности технологии разработки комплексов программ, доля творческого труда при создании полностью новых крупных программных продуктов возрастает. Даже при сокращении суммарных затрат на разработку программных компонентов за счет автоматизации нетворческого труда, все более определяющей для экономических характеристик создания сложных программных продуктов становится доля затрат на творческий труд, и возрастают требования к творческим способностям при отборе и обучении специалистов. По мере повышения квалификации коллективов специалистов и автоматизации творческой части труда следует ожидать асимптотического приближения проектов к предельным значениям относительных экономических характеристик новых разработок. Эти значения определяются интеллектуальными возможностями человека по интенсивности принятия творческих решений. Им соответствуют наличие предельных значений производительности труда и длительности разработки сложных комплексов программ. Вряд ли можно ожидать в ближайшие годы радикального повышения производительности труда при создании полностью новых, крупных программных продуктов. Еще более консервативна длительность таких разработок [2, 5, 12]. Создание программных средств как сложной производственной продукции существенно повысило актуальность экономического обоснования, необходимость прогнозирования и измерения их характеристик качества и процессов производства. Основной целью производства многих программных продуктов является повышение эффективности промышленных систем обработки информации и/или управления объектами, в которых применяются сложные комплексы программ. Такими системами могут быть средства автоматизированного управления самолетами, системами вооружения или электростанциями, информационно-справочные системы административного управления, системы автоматизации проектирования и обучения. В ряде случаев программные продукты невозможно или очень трудно характеризовать непосредственной экономической эффективностью. Примером могут служить комплексы программ в административных системах, в системах управления воздушным движением или космическими аппаратами, а также военного назначения или автоматизации научных экспериментов. В таких случаях при анализе программ невозможно определять прямую экономическую эффективность систем в зависимости от затрат ресурсов, и целесообразно исключать из анализа характеристики эффективности программных продуктов. В результате исследование экономической эффективности создания комплексов программ приходится проводить по величине затрат ресурсов на производство программного 54
продукта в предположении, что обеспечена реализация заданных их функциональных характеристик с требуемым качеством. Зачастую при экономическом анализе проектирования и производства предполагается [4, 6, 9], что для продукта зафиксирован эффект от его создания и использования, и необходимо выявить все основные факторы, способствующие минимизации совокупных производственных затрат на всем жизненном цикле. Для этого должен быть подготовлен согласованный между заказчиком и разработчиком утвержденный документ, в котором определяются цели и задачи проекта, требуемые характеристики продукта и доступные экономические и другие ресурсы для его реализации. Эти данные должны быть предварительно сбалансированы, и они должны обеспечивать реализацию целей проекта при выделенных ресурсах с минимальным допустимым риском. Однако масштабы целей и функций сложных программных проектов имеют устойчивую тенденцию изменяться и увеличиваться по мере развития, а первоначально выделяемые ресурсы – не удовлетворять их реализацию. Экономическое обоснование проектов на начальном этапе их развития должно содержать оценки рисков реализации поставленных целей, обеспечивать возможность планирования и выполнения жизненного цикла (ЖЦ) программного продукта или указывать на недопустимо высокий риск его реализации и целесообразность прекращения разработки. Большую часть рисков и негативных последствий производства можно избежать, используя существующие методы оценивания и прогнозирования производственных затрат, а также управления проектами программных продуктов для их успешного завершения.
2. Особенности экономики программных продуктов
производства
крупных
При экономическом анализе и обосновании проектов сложных комплексов программ возможны два сценария: • создание и весь жизненный цикл комплекса программ ориентируется на массовое тиражирование и распространение их на рынке среди заранее не известных пользователей, в различных сферах и внешней среде применения; при этом отсутствует конкретный внешний потребительзаказчик, который определяет и диктует основные требования к программному продукту, выделяет ресурсы и финансирует проект; • разработка программного продукта предполагается с определенным, относительно небольшим тиражом, с известной областью и внешней средой применения, для конкретного потребителя-заказчика, который определяет требования к функциям и характеристикам качества, финансирует и выделяет ресурсы. Эти сценарии принципиально различаются методами экономического анализа и обоснования экономических характеристик продукта. Первый 55
сценарий базируется на маркетинговых исследованиях рынка программных продуктов и на стремлении поставщика занять на рынке достаточно выгодное место. Для этого ему необходимо определить наличие на рынке всей гаммы близких по назначению и функциям продуктов, оценить их эффективность, стоимость и применяемость, а также возможную конкурентоспособность предполагаемого к разработке программного продукта для потенциальных пользователей и их возможное число. Следует оценить рентабельность затрат на создание и обеспечение всего ЖЦ нового программного продукта, выявить факторы, функциональные, экономические и конструктивные показатели качества, которые способны привлечь достаточное число покупателей и оправдать затраты на предстоящую разработку. Для этого разработчикам необходимо проводить оценки возможной конкурентоспособности предполагаемой продукции на рынке по величине соотношения: • возможной эффективности (ценности, достоинств) последующего применения программного продукта и способности удовлетворить потребности пользователей при их приобретении и использовании; • к стоимости (цене, затратам), которую готов заплатить пользователь при приобретении и эксплуатации данного программного продукта. При выборе поставщика и продукции покупатель обычно стремится максимизировать это соотношение как за счет поиска продуктов с наибольшими эффективностью и качеством, так и за счет минимальной стоимости покупаемого продукта. В этом сценарии при организации производства вся ответственность за цели, функции и показатели качества продукта ложатся на его производителей, и особую роль при экономических оценках должны играть специалисты по маркетинговому анализу предполагаемого распространения продукта на рынке. Они должны оценивать: конъюнктуру и риск успешного продвижения создаваемого продукта на рынок, сроки и график выполнения этапов жизненного цикла, достаточность ресурсов для реализации проекта и длительность развития, модификаций и распространения версий программного продукта. Для этого им нужны, в частности, прогнозы экономических характеристик производства предполагаемого продукта. Такие проекты обычно относительно невелики по объему и срокам реализации первой версии программмного продукта, однако могут предполагать длительный жизненный цикл и множество модификаций для адаптации к нуждам и среде пользователей. Второй сценарий предполагает наличие определенного заказчикапотребителя конкретного программного продукта, который должен соответствовать его функциональным, техническим и экономическим требованиям. Заказчик может выбирать конкурентоспособного поставщикаразработчика и оценивать его возможность реализовать проект с необходимым качеством с учетом ограничений требуемого бюджета, сроков и других ресурсов. При этом результаты разработки не обязательно подлежат широкому тиражированию, они могут не поступать на рынок; маркетинговые 56
исследования для таких продуктов являются второстепенными и предварительно могут не проводиться. Однако для заказчика и разработчиков при заключении контракта необходимо достаточно достоверное прогнозирование требований к программному продукту и экономическое обоснование необходимых ресурсов по трудоемкости, стоимости, срокам и другим показателям. Заказчик заинтересован в получении продукта высокого качества при минимальных затратах, а разработчик желает получить максимальную оплату за созданный продукт и достаточные ресурсы на его производство. Противоположность интересов поставщика и потребителя при оценке экономических характеристик, стоимости и других ресурсов проекта, требует поиска компромисса, при котором производитель программного продукта не продешевит, а заказчик не переплатит за конкретные выполненные работы и весь проект. Поэтому оба партнера заинтересованы в достоверном экономическом прогнозировании и обосновании процессов проектирования и производства программного продукта. Во многих случаях эффективность систем новой техники и программных продуктов в процессе производства приходится прогнозировать в условиях неопределенности целей, требований к функциям, различных факторов и характеристик заказываемых систем и продуктов. Зачастую бывают недостаточно известны перспективы внедрения, распространения и применения объектов производства – новых программных продуктов. Значительные неопределенности содержатся также в экономических характеристиках производственных технологий, а также инструментальных средств автоматизации проектирования и изготовления комплексов программ. Положение усугубляется трудностью поэтапного определения качества компонентов и его прогнозирования в процессе производства, что непосредственно отражается на экономических характеристиках реализации продукта в целом. Методы и достоверность экономического анализа производства и жизненного цикла крупных комплексов программ можно разделить на две части, существенно различающиеся свойствами производственных процессов, экономическими характеристиками и влияющими на них факторами. В первой части ЖЦ производятся: системный анализ, проектирование, разработка, тестирование и испытания первой (базовой) версии программного продукта. Номенклатура работ, их трудоемкость, длительность и другие характеристики на этих этапах ЖЦ существенно зависят от свойств создаваемого продукта, требуемых показателей качества, внешней и технологической среды разработки. Изучение подобных зависимостей для различных прототипов программных продуктов позволяет достаточно достоверно прогнозировать состав и основные экономические характеристики производства, планы и графики работ для вновь создаваемых продуктов. 57
Вторая часть ЖЦ, отражающая применение, сопровождение и развитие версий программного продукта, связана не только с экономическими характеристиками продукта и среды производства. При этом экономические характеристики производства зависят от интенсивности изменений версий, сложности и стоимости каждой модификации программного продукта. Совокупность версий сложных программных продуктов обычно характеризуются длительной непрерывной эксплуатацией, продол-0жительность которой значительно превышает длительность производства первой версии. После того как программный продукт создан и испытан, в ряде случаев он становится недоступным для разработчиков и применяется неизменным до внедрения очередной версии при модификации системы. В процессе сопровождения программы могут подвергаться эпизодическим корректировкам, которые должны регистрироваться, накапливаться и передаваться пользователям экземпляров системы. Необходимо обеспечивать адекватность документации каждой версии эксплуатируемого продукта в любой момент времени. Номенклатура работ на этапах сопровождения более или менее стабильная, а их трудоемкость и длительность могут сильно варьироваться и зависят от массовости и других факторов распространения и применения конкретного программного продукта. Успех продукта у пользователей и на рынке, а также экономические характеристики процесса развития версий трудно предсказать, и определяющими становятся потребительские характеристики и качество продукта, а его экономические особенности с позиции производства отходят на второй план. Вследствие этого трудно прогнозировать экономические характеристики, трудоемкость и необходимое число специалистов, поддерживающих этапы производства модификаций программного продукта. Это затрудняет обобщение экономических характеристик производства для различных версий продуктов и прогнозирование на их основе аналогичных характеристик новой разработки. В результате их приходится проводить итерационно на базе накопления опыта и анализа экономических характеристик модификаций конкретных типов и версий программных продуктов. Наиболее сильно на экономические характеристики производства программных продуктов (кроме требуемых функций) влияют их масштаб – размер, а также требования к качеству. Оценка размера будущего программного продукта необходима, поскольку она является частью одной из наиболее важных задач проекта: возможности реализации требований заказчика и потребителей при доступных ресурсах. Нереальные ожидания, основанные на неточных экономических оценках проектирования и производства, представляют собой одну из частых причин провала проектов. Измерение размера, оценка и составление графика производства программного продукта сложным образом переплетаются в процессе планирования проекта. Довольно сложно создать реальный график (учитывающий обязанности исполнителей, их зависимости, перекрытие 58
действий, а также дату поставки продукта) без информации о размере трудозатрат, требуемых для выполнения каждой частной задачи и создания компонентов. Таким образом, измерение размера (сложности) компонентов и комплекса в целом должно предшествовать оценке полных экономических характеристик проекта, а эта оценка, в свою очередь, должна предшествовать составлению производственного графика работ. Качество продуктов характеризуется многими показателями, состав которых зависит от класса, функций и конкретного назначения комплекса программ. По мере повышения требований к качеству затраты на производство увеличиваются все более высокими темпами. Одновременно расширяется диапазон неопределенности достигаемого качества при допустимых затратах. В результате для сложных программных продуктов всегда есть риск проявления неустраненных ошибок и недостаточной достоверности оценок качества. Специфицирование требований и оценивание характеристик качества программного продукта − ключевой фактор обеспечения их адекватного применения. Это может быть достигнуто на основе выделения, определения и реализации подходящих характеристик с учетом целей использования и функциональных задач комплекса программ с использованием стандартизированных или формализованных метрик [2, 5, 11]. Задача состоит в создании, выборе и прогнозировании наиболее адекватных экономических критериев для обобщенного описания эффективности производства, стоимости создания и использования сложных комплексов программ в зависимости от их назначения, области применения, требуемого качества и других факторов. Для сложных продуктов с высокими требованиями к качеству проектирование, развитие и применение систем обеспечения и контроля качества должно сопровождать весь жизненный цикл основной продукции − комплексов программ. Включение комплексов программ в контур систем управления производством промышленной продукции или динамическими объектами приводит к необходимости обеспечения ими высокого качества решения задач, безопасности и надежности их функционирования не ниже, чем у аппаратных компонентов соответствующих систем. Поэтому комплексы программ должны тщательно тестироваться и проходить контрольные испытания для определения уровня качества, безопасности и надежности их применения, что может требовать особенно больших затрат ресурсов.
3. Проблемы анализа экономики программных продуктов
производства
Экономические характеристики реальных завершенных разработок, собираются, накапливаются и обрабатываются с начала 80-х годов в разных отечественных организациях и за рубежом [1, 3]. Они позволили получать и прогнозировать основные обобщенные экономические показатели процессов 59
производства комплексов программ. При оценке размера вновь созданных комплексов программ и трудоемкости их полной разработки обычно не учитывались компоненты операционных систем, драйверы, средства контроля и тестирования, а также повторно используемые компоненты. При этом обычно рассматривался полный технологический процесс производства от начала подготовки технического задания до завершения испытаний базовой версии продукта. Учитывались все категории специалистов, участвующих в создании программ и обеспечивающих разработку, а также все виды работ, связанные с созданием программного продукта на выделенном интервале времени. Наиболее полно и подробно основные закономерности и влияние факторов на экономические характеристики процессов разработки в 80-е годы представлены в [1] для более десяти моделей прогнозирования. В 1981-м году на основе исследования процессов разработки 63 программных продуктов была опубликована модель прогнозирования под названием КОМОСТ [1]. В последующем эта модель была развита, детализирована и опубликована как СОСОМО, а в 2000-м году – под названием СОСОМО II [10]. В этой модели на основе анализа более 160 реальных проектов разработки комплексов программ различной сложности уточнены рейтинги влияния выделенных факторов на основные экономические характеристики. Кроме того, в 1988-м году были опубликованы [3] результаты анализа экономических характеристик (комплексный проект ПРОМЕТЕЙ) на основе обобщения результатов разработки свыше 250 отечественных проектов сложных программных продуктов. В общем случае для оценки экономических характеристик новых проектов необходимы следующие исходные данные: • обобщенные характеристики использованных ресурсов и экономические показатели завершенных разработок − прототипов, а также оценки влияния на их характеристики различных факторов объекта и среды разработки; реализованные и обобщенные перечни выполненных работ и реальные графики проведенных ранее разработок различных классов комплексов программ; • цели и содержание частных работ в процессе создания сложных комплексов программ и требования к их выполнению для обеспечения необходимого качества продукта в целом; • структура и содержание документов, являвшихся результатом выполнения частных работ. Этапы жизненного цикла программ существенно различаются между собой степенью определенности и прогнозируемости их характеристик. Соответственно изменяются возможности подготовки и достоверность планов проведения работ. В процессе разработки сложных программных продуктов уточняются и детализируются их спецификации требований, функции, структура и характеристики компонентов. Поэтому на начальных этапах, в особенности, принципиально новых проектов, трудно точно спланировать все 60
последующие этапы. В результате планирование проводится итерационно в соответствии с последовательным повышением достоверности и точности сведений об объекте и среде разработки. Так как отсутствует достоверная пооперационная статистика разработки программ, пока невозможно создать типовые нормативы на большинство частных операций при производстве сложных комплексов программ. Отсюда принципиальной особенностью разработки комплексов программ является активное участие руководителей и заказчиков проекта в составлении планов на базе исследованных характеристик прототипов завершенных разработок и своего личного опыта. Такие планы должны иметь разумные ограничения в детализации работ на уровне, обеспечивающем необходимую управляемость всего процесса проектирования и производства. Сложные комплексы программ обычно являются компонентами систем, реализующими их основные, функциональные свойства и создающими предпосылки для последующих изменений их жизненного цикла. При экономическом анализе ЖЦ сложных программных комплексов целесообразно выделять два крупных этапа: анализ и проектирование программного продукта и собственно его производство на основе проекта. При проектировании необходимо формировать функции, требования к качеству и архитектуру предполагаемого комплекса программ, оценивать его экономические характеристики и решать − следует ли реализовывать производство программного продукта. Экономическое обоснование проектов на начальном этапе их производства должно содержать оценки экономических рисков реализации поставленных целей, обеспечивать возможность планирования и выполнения жизненного цикла продукта или указывать на недопустимо высокий риск его реализации и целесообразность прекращения проектирования и производства. Множество текущих состояний и модификаций компонентов в жизненном цикле сложных комплексов программ менеджерам необходимо упорядочивать, контролировать их развитие и реализацию участниками проекта. Организованное, контролируемое и методичное отслеживание динамики проектирования и производства, а также изменений в жизненном цикле программ и данных, их разработка при строгом учете и контроле ресурсов и каждого изменения, является базовой проблемой эффективного, поступательного развития каждого крупного комплекса программ и системы методами программной инженерии [2, 5, 7, 8]. Основным способом совершенствования производства программных продуктов и повышения квалификации специалистов в этой области многие менеджеры считают накопление и распространение опыта наиболее успешных разработчиков, который выражается в быстром продвижении проектов. Однако, только повышая квалификацию отдельных разработчиков, невозможно решать современные проблемы коллективного производства крупных программных продуктов, а еще менее вероятно, что так можно 61
подготовиться к тем требованиям, которые выдвинет будущее. Производительность индивидуального труда наиболее продуктивных разработчиков может во много раз превышать производительность наименее продуктивных, в то время как численность последних в такое же число раз превышает численность первых [2, 8, 12]. Кроме того, опыт наиболее продуктивных разработчиков не может быть применен в промышленных масштабах крупных проектов, поскольку ориентирован на людей с исключительными способностями. Для удовлетворения требований к производству сложных программных продуктов в промышленном масштабе должны быть исключены надежды на расточительное использование таланта отдельных высоко квалифицированных разработчиков для выполнения рутинных повторяющихся процессов, чтобы они могли больше времени тратить на постановку, формализацию и организацию производства сложных программ [7]. Методы, стандарты и шаблоны программной инженерии уже доказали возможности повторного использования знаний в производстве сложной программной продукции. Однако многие компании, занимающиеся производством программных продуктов, не уделяют должного внимания экономике и эффективному применению современных методов автоматизации и обеспечения всего жизненного цикла программных средств. Эти проблемы и их компоненты в разной степени могут отражаться на экономических характеристиках, качестве и конкурентоспособности отечественных программных продуктов, однако на них целесообразно обращать внимание и по возможности решать заказчикам, руководителям и разработчикам сложных программных продуктов. Проблема экономического прогнозирования развития проектов в программной инженерии заключается, прежде всего, в разработке, освоении и использовании методов для расчета экономических характеристик производства сложных программных продуктов. Цель экономического обоснования проектов комплексов программ состоит в помощи их руководителям определять: • целесообразно ли проводить или продолжать работы над конкретным проектом программного продукта в направлении детализации требований, функций и экономических характеристик, или следует его прекратить вследствие недостаточных ресурсов специалистов, времени или возможной стоимости и трудоемкости производства; • при наличии достаточных ресурсов, следует ли провести маркетинговые исследования для определения рентабельности полного выполнения проекта и производства программного продукта для поставки заказчику или на рынок; • достаточно ли полно и корректно формализованы требования к проекту, на основе которых проводились расчеты экономических характеристик, 62
•
или их следует откорректировать и выполнить повторный анализ с уточненными исходными данными; есть ли возможность применить готовые повторно используемые компоненты, в каком относительном размере от всего комплекса программ, и рентабельно ли их применять в конкретном проекте, или весь проект целесообразно разрабатывать как полностью новый.
4. Проблемы организации экономически эффективного производства программных продуктов Стратегической проблемой в жизненном цикле современных систем стало обеспечение и совершенствование качества производства сложных программных продуктов при реальных ограничениях на использование доступных ресурсов производства. Для каждого крупного проекта, выполняющего ответственные функции, необходимо прогнозировать требующиеся ресурсы, разрабатывать и применять комплексную систему качества, специальные планы и программу, методологию и инструментальные средства, обеспечивающие требуемые качество, надежность и безопасность функционирования программного продукта. Для этого следует использовать современные методы и стандарты при подготовке промышленных технологий, методик производства и испытания конкретных продуктов, однозначно отражающих степень удовлетворения исходных требований заказчика и пользователей, а также для сравнения продуктов разных поставщиков и выявления среди них предпочтительных. Базой эффективного управления проектом крупного программного комплекса является план, в котором задачи исполнителей частных работ должны быть согласованы с выделяемыми для них ресурсами, а также между собой, по результатам и срокам их достижения. Планирование программных проектов должно обеспечивать компромисс между требующимися характеристиками создаваемой системы и ограниченными ресурсами, необходимыми на ее разработку и применение. По мере уточнения исходных данных об объекте разработки, внешней среде применения и ресурсах, в процессе системного анализа и проектирования возрастает достоверность планирования, которое должно проходить следующие этапы:
• • •
обследование объектов и среды проектирования для предварительной формализации целей, назначения и задач проекта; первичное прогнозирование возможных характеристик и требований к программному продукту на базе обобщения данных ранее реализованных подобных прототипов и создание концепции проекта; управление детализацией и реализацией плана производства, его оперативной корректировкой и перераспределением ресурсов в соответствии с особенностями развития компонентов программного комплекса; 63
•
обобщение и накопление результатов планирования и управления конкретным проектом для использования впоследствии этих данных в качестве прототипов при производстве программных продуктов. На каждом этапе должен проводиться поиск эффективных технических и экономических решений реализации проекта, исследование и сопоставление альтернативных действий, которые должны приводить к достижению поставленных целей производства программного продукта. Уже при первичном прогнозировании развития проекта должны оцениваться альтернативные характеристики объекта и среды разработки и выбираться наиболее подходящие для производства в соответствии с поставленными целями и имеющимися ресурсами. Сравнение альтернатив следует проводить по величине достигаемого эффекта проекта в зависимости от затрат на его достижение (желательно, по показателю “эффективность/стоимость”). Для сокращения затрат возникла необходимость в новых технологиях, методах создания и управления сложными проектами программных продуктов. Это приводит к увеличению роли интеграции таких компонентов, соответствующих методов и инструментария программной инженерии. Однако вследствие их принципиальной новизны и сложности, они трудно воспринимались традиционными программистами компонентов и преподавателями отечественной высшей школы. Коренные отличия между методами и инструментарием индивидуального, «художественного» программирования небольших программ и технологией планомерного, регламентированного производства крупных программных продуктов приводят к тому, что последние медленно осваиваются и входят в практику слаженной работы больших коллективов специалистов. Эти обстоятельства отражаются на существенном отставании от мирового уровня отечественных программных продуктов по конкурентоспособности, количеству и качеству. Неопределенность применяемых понятий, требований и характеристик качества присущая крупным, наукоемким проектам комплексов программ, а также многочисленные спекуляции разработчиков на их значимости приучили заказчиков не доверять рекламируемым достоинствам программных продуктов [7, 8]. Во многих случаях контракты и предварительные планы на создание сложных программных комплексов и баз данных подготавливаются и оцениваются на основе неформализованных представлений заказчиков и разработчиков о требуемых функциях и характеристиках качества систем. Многочисленные провалы проектов выявили необходимость формализации методов взаимодействия и обеспечения взаимопонимания разработчиков с заказчиком или потенциальными пользователями создаваемого продукта с самого начала проекта с целью конкретизации его функций и требований к качеству. Ошибки, обусловленные неопределенностью или некорректностью технических заданий и спецификаций требований, могут и должны 64
выявляться на ранних стадиях проектирования, что способствует его ускорению и повышению качества. Возрастание сложности и ответственности современных задач, решаемых крупными системами, а также возможного ущерба от недостаточного качества комплексов программ значительно повысило актуальность проблемы освоения методов стандартизированного описания требований и оценивания характеристик качества на различных этапах ЖЦ. Выявилась необходимость систематизации реальных характеристик качества программных продуктов, применения стандартов для выбора из них необходимой номенклатуры и требуемых значений для конкретных комплексов программ. Обещания разработчиков в контрактах с заказчиками создать высококачественные продукты в согласованные сроки во многих случаях не выполняются, как вследствие различий в понимании требуемого качества, так и из-за неумения оценить ресурсы, необходимые для достижения заданного качества программ. Широкое многообразие классов и видов программ, обусловленное различными функциями систем, предопределяет формальные трудности, связанные с методами и процедурами доказательства соответствия поставляемых продуктов условиям контрактов и требованиям потребителей. По мере расширения облестей применения и увеличения сложности программных продуктов выделились области, в которых ошибки или недостаточное качество программ или данных могут нанести ущерб, значительно превышающий положительный эффект от их использования. В этих критических случаях недопустимы аномалии и дефекты функционирования программных продуктов при любых искажениях исходных данных, сбоях и частичных отказах аппаратуры и других нештатных ситуациях. Проблемы формирования требований к характеристикам и качеству программного продукта включают анализ свойств, характеризующих его функционирование с учетом технологических и ресурсных возможностей производства. При этом под качеством функционирования понимается совокупность свойств, обусловливающих пригодность продукта обеспечивать надежное и своевременное представление требуемой информации потребителю для ее дальнейшего использования по назначению. В соответствии с принципиальными особенностями программного продукта при проектировании должны выбираться номенклатура и значения требований к характеристикам качества, необходимым для его эффективного применения пользователями; эти требования впоследствии отражаются в технической документации и в спецификации требований на конечный продукт. Каждый критерий качества может использоваться, если определена его метрика, и может быть указан способ ее оценивания и сопоставления с требующимся значением. Для конкретных видов программных продуктов доминирующие критерии качества выделяются и определяются при проектировании систем их функциональным назначением и требованиями технического задания. 65
Удостоверение достигнутого качества функционирования сложных программных продуктов и методов обеспечения их жизненного цикла базируется на сертификации, аттестованной проблемно-ориентированными испытательными лабораториями. Применение сертифицированных систем качества на предприятиях-разработчиках должно гарантировать высокое, устойчивое качество процессов производства конечного программного продукта. Основой сертификации должны быть детальные и эффективные методики испытаний конкретных продуктов, специально разработанные тестовые задачи и генераторы тестов для их формирования, а также квалификация и авторитет испытателей. Для этого заказчики должны выбирать подрядчиков-исполнителей своих проектов, которые имеют системы обеспечения качества программных средств и сертификаты, удостоверяющие реализацию и применение системы качества производства предприятием-разработчиком. Обеспечение и удостоверение качества сложных программных продуктов должно базироваться на проверках и испытаниях:
•
технологий обеспечения жизненного цикла программных комплексов, поддержанных регламентированными системами качества; • готового программного продукта с полным комплектом адекватной эксплуатационной документации. Глубокая взаимосвязь качества разработанных комплексов программ с качеством технологии их создания и с затратами на разработку становится особенно существенной при необходимости получения конечного продукта с предельно высокими значениями показателей качества. Это привело к существенному изменению в последние годы объектов, методологии и культуры в области создания и совершенствования комплексов программ. Непрерывный рост требований к качеству стимулировал создание и активное применение международных стандартов и регламентированных технологий, автоматизирующих процессы ЖЦ, начиная с инициирования проекта. Для решения этой задачи необходимо детально исследовать современные процессы производства и использования программ различных классов и назначения − встроенных, коммерческих, административных, учебных, уникальных и др. Проблемы оценивания и выбора квалифицированных и надежных специалистов-подрядчиков, способных создавать сложные программные продукты и базы данных требуемого качества в разумные сроки с учетом ограничений на используемые ресурсы, стоят остро для многих заказчиков и пользователей современных вычислительных систем. Для их решения поставщикам комплексов программ, (кроме программистов–кодировщиков), необходимо иметь системных аналитиков, архитекторов и топ-менеджеров проектов, а также специалистов по комплексированию, испытаниям и обеспечению качества современных сложных программных продуктов. Они должны знать передовые индустриальные методы, технологии и международные стандарты, поддерживающие и регламентирующие ЖЦ 66
комплексов программ, а также инструментальные системы обеспечения качества, верификации, тестирования и сертификации программных продуктов. Для этого требуется, прежде всего, системотехническая квалификация предприятий и специалистов, берущихся за производство крупных программных продуктов высокого качества.
5. Заключение За последние годы ряд исследований и работ по сбору и обобщению экономических данных о производстве программных продуктов заложили основы для методов и моделей оценивания затрат [1, 10, 13]. Имеющиеся модели не всегда точны, как хотелось бы, но могут весьма существенно помочь в экономическом анализе и обосновании решений при создании сложных программных продуктов. Для сбора и обобщения экономических характеристик о производстве программных продуктов необходимо детально исследовать требуемые ресурсы для современных процессов создания и использования комплексов программ различных классов и назначения. Необходимы активные исследования на разных уровнях детализации, начиная от экономики и планирования создания программных продуктов в масштабах страны или предприятия и кончая экономикой выполнения частных операций отдельными специалистами при проектировании или производстве компонентов и конкретных продуктов. Одна из важнейших задач состоит в том, чтобы увязать четкими экономическими категориями взаимодействие разных специалистов и предприятий в типовой производственной цепочке: заказчик−разработчик − изготовитель−пользователь. Для этого объект − программный продукт – и все процессы взаимодействия в цепочке должны быть связаны системой экономических и технических характеристик, в той или иной степени использующих основные экономические показатели − реальные затраты ресурсов: финансов, труда и времени специалистов, затрачиваемых на создание конечного продукта. Проблема состоит в создании и представлении специалистам современных методов экономического анализа, оценивания и прогнозирования необходимых ресурсов при производстве комплексов программ. Тем самым, должен быть выделен очень важный, базовый раздел из всей экономики жизненного цикла программных комплексов. Такое выделение определяется тем, что без подобных базовых исследований имеющихся прототипов вряд ли возможно последующее серьезное развитие экономики в этой отрасли. Внимание должно быть сосредоточено на концептуальной основе распределения затрат труда в процессе разработки компонентов и комплексов программ, на факторах, определяющих реальные трудозатраты и другие экономические характеристики, а также на исследовании их в реализованных современных разработках. 67
В жизненном цикле сложных комплексов программ для обеспечения их высокого качества целесообразно выделять специалистов, ответственных за анализ, оценивание и прогнозирование экономических характеристик производства, за соблюдение промышленной технологии создания и совершенствование программных продуктов, за измерение и контроль затрат, качества комплексов программ в целом и их компонентов. Проблема состоит в том, чтобы научить и приучить специалистов к анализу и оцениванию конкретных экономических факторов, влияющих на характеристики функционирования программных продуктов со стороны реально существующих и потенциально возможных негативных воздействий при производстве при наличии ограничений на ресурсы проектов. Необходимы подготовка и воспитание квалифицированных специалистов в области индустрии, экономики и производства сложных программных комплексов, их обучение методам и современной программистской культуре промышленного создания крупных высококачественных программных продуктов. Литература [1] Боэм Б.У. Инженерное проектирование программного обеспечения. Пер. с англ. /Под ред. А.А. Красилова. – М.: Радио и связь, 1985. 512 стр. [2] Гецци К., Джазайери М., Мандриоли Д. Основы инженерии программного обеспечения. Пер. с англ. – СПб.: БХВ-Петербург. 2005. 832 стр. [3] Липаев В.В., Потапов А.И. Оценка затрат на разработку программных средств. – М.: Финансы и статистика. 1988. 224 стр. [4] Липаев В.В. Технико-экономическое обоснование проектов сложных программных средств. – М.: СИНТЕГ. 2004. 284 стр. [5] Липаев В.В. Программная инженерия. Методологические основы. Учебник. – М.: ТЕИС. 2006. 608 стр. [6] Организация производства и управления предприятием. Учебник. Под ред. О.Г. Туровца. – М.: ИНФРА-М. 2006. 544 стр. [7] Соммервилл И. Инженерия программного обеспечения. 6-е издание. Пер. с англ. – М.: Вильямс. 2002. 624 стр. [8] Фатрелл Р. Т., Шафер Д. Ф., Шафер Л. И. Управление программными проектами: достижение оптимального качества при минимальных затратах. Пер. с англ. – М.: Вильямс. 2003. 1136 стр. [9] Экономика промышленного предприятия. Учебник. Под ред. Е.Л. Кантора. – М.: МарТ. 2007. 864 стр. [10] Boehm B.W. et al. Software cost estimation with COCOMO II. Prentice Hall PTR. New Jersey. 2000. 506 стр. [11] Grady R. Practical software metrics for project management and process improvement. – Englewood Cliffs. NY. Prentice-Hall. 1992. 376 стр. [12] Jones C. Applied software measurement, assuring productivity and quality. McGrawHill. NY. 1996. 432 стр. [13] Londeix B. Cost estimation for software development. Cornwall: Addison-Wesley. 1987. 312 стр.
68
2. P-семантика без приоритетов 2.1. Формализация взаимодействия
Обобщённые семантики тестового взаимодействия И.Б. Бурдонов, А.С. Косачев
1. Введение В нашей работе [4] введён класс семантик тестового взаимодействия, основанного на наблюдениях двух типов: наблюдение внешнего действия, выполняемого тестируемой системой, и наблюдение отсутствия действий из некоторого множества действий, называемое отказом. При этом тестовое воздействие сводится к разрешению системе выполнять любое действие из заданного множества действий. Предполагалось, что отказ либо не наблюдается при данном тестовом воздействии (Q-отказ), либо совпадает с множеством разрешаемых действий (R-отказ). Такая семантика называлась R/Q-семантикой и задавалась двумя семействами подмножеств алфавита внешних действий: R и Q. Подробное изложение теории конформности с доказательствами утверждений содержится в докторской диссертации И.Бурдонова [7], теория конформности для класса, так называемых βγδсемантик излагается в нашей книге [6]. В этих работах предполагалось отсутствие приоритетов между действиями, которые тестируемая система может выполнять при данном тестовом воздействии: действие может выполняться независимо от того, какие ещё действия разрешаются тестовым воздействием. В то же время для реальных программных и аппаратных систем такая модель не всегда адекватно отражает требуемое поведение системы. В нашей статье [8] предлагается способ введения приоритетов в теорию конформности для R/Q-семантики: в модель системы, отношение конформности, методы генерации тестов и оператор композиции (сборки составной системы из взаимодействующих между собой компонентов). В данной работе мы обобщаем семантику взаимодействия, допуская наблюдение отказов, не обязательно совпадающих с множеством разрешаемых действий (и даже не обязательно вложенных в него). Рассмотрение состоит из двух частей: сначала рассматриваются модели без приоритетов, а потом вводятся приоритеты.
69
Верификация конформности понимается как проверка соответствия исследуемой системы заданным требованиям (рис.1). В модельном мире система отображается в реализационную модель (реализацию), требования – в спецификационную модель (спецификацию), а их соответствие – в бинарное отношение конформности. Если требования выражены в терминах взаимодействия системы с окружающим миром, возможно тестирование как проверка конформности в процессе тестовых экспериментов, когда тест подменяет собой окружение системы. Для такой проверки предполагается, что реализационная модель существует (так называемая, тестовая гипотеза), хотя может быть неизвестной. В модельном мире по спецификации генерируются модельные тесты и определяется отношение «реализация проходит тест». Набор тестов полон, если реализация проходит каждый тест из набора тогда и только тогда, когда она конформна спецификации. Модельные тесты транслируются в реальные тестовые программы, которые прогоняются на тестируемой системе. Реальное отношение «проходит» должно адекватно отражаться в модельном отношении «проходит». Само отношение конформности и его тестирование (в частности, отношение «проходит») базируются на той или иной модели взаимодействия.
Рис.1. Тестирование конформности Мы будем рассматривать такие семантики взаимодействия, которые основаны только на внешнем, наблюдаемом поведении системы и не учитывают её внутреннее устройство, отображаемое на уровне модели в понятии состояния. В этом случае говорят о тестировании методом «чёрного ящика» или 70
функциональном тестировании. Мы можем наблюдать только такое поведение реализации, которое, во-первых, «спровоцировано» тестом (управление) и, вовторых, наблюдаемо во внешнем взаимодействии. Такое взаимодействие может моделироваться с помощью так называемой машины тестирования [4,6,7,9,10,15]. Она представляет собой «чёрный ящик», внутри которого находится реализация (рис. 2). Управление сводится к тому, что оператор машины, выполняя тест (понимаемый как инструкция оператору), нажимает какие-то кнопки на клавиатуре машины, «приказывая» или «разрешая» реализации выполнять те или иные действия, которые могут им наблюдаться. Наблюдения (на «дисплее» машины) бывают двух типов: наблюдение некоторого действия, выполняемого реализацией, и наблюдение отказа как отсутствия каких бы то ни было действий из некоторого множества действий.
Рис.2. Машина тестирования Следует подчеркнуть, что при управлении оператор разрешает реализации выполнять именно множество действий, а не обязательно одно действие. Например, при тестировании реактивных систем, основанных на обмене стимулами и реакциями, посылка одного стимула из теста в реализацию может интерпретироваться, как разрешение реализации выполнять только одно действие – приём этого стимула. Однако приём тестом ответной реакции должен означать разрешение реализации выдавать любую реакцию как раз для того, чтобы проверить, правильна эта реакция или нет. Мы будем считать, что оператор нажимает одну кнопку A, но на кнопке «написано», вообще говоря, не одно действие, а множество разрешаемых действий A. Когда происходит наблюдение (действие или отказ), кнопка автоматически отжимается, и все внешние действия считаются запрещёнными. Далее оператор может нажимать другую (или ту же самую) кнопку. В то же время множество разрешаемых действий – это, вообще говоря, не любое подмножество множества всех внешних действий. В вопросе о том, какие множества действий могут разрешаться тестом, а какие нет, среди исследователей существует большое разнообразие точек зрения. Например, для реактивных систем обычно считается, что нельзя (или бессмысленно) смешивать посылку стимулов с приёмом реакций (Ян Тритманс). Но существует и прямо противоположный подход: нельзя «тормозить» выдачу реакций реализацией; поэтому, даже посылая стимул, тест должен быть готов к приёму любой реакции (А.Ф. Петренко). 71
Также следует подчеркнуть, что наблюдаться может, вообще говоря, не любой отказ. И здесь разные исследователи опираются на разные предположения. Для тех же реактивных систем долгое время считалось, что тест может наблюдать отсутствие реакций (quiescence, стационарность), например, по тайм-ауту, но не видит, принимает ли реализация посланный ей стимул или нет (input refusal, блокировка стимула). С другой стороны, в последние годы появляется всё больше и больше работ, в которых такие блокировки стимулов полностью или частично допускаются [1-6,11,12,13]. Также и реакции, если они принимаются тестом по разным «выходным каналам», можно принимать не все, а лишь те, которые относятся к одному или нескольким выбранным каналам. Это даёт наблюдение отказа, называемого «частичной стационарностью» – отсутствие реакций из множества не всех реакций, а только тех, что относятся к данному выходному каналу [11,12]. Для наблюдения отказа нужно, чтобы оператор каким-то образом указал соответствующее множество внешних действий. В [4,6,7,8] предполагалось, что это множество совпадает с множеством разрешаемых действий: если оператор нажимает кнопку A, разрешая реализации выполнять действия из множества A, то наблюдаться может только отказ A, который означает, что реализация не может выполнить ни одно действие из A. В то же время существуют случаи, когда такого совпадения нет. Например, в реактивных системах без «торможения» реакций, когда стационарность наблюдаема, а блокировки стимулов не наблюдаемы, при посылке в реализацию стимула с одновременным приёмом всех реакций отказ может означать только стационарность – отсутствие реакций, ничего не говоря о том, могла бы реализация принять стимул или нет. Здесь множество разрешаемых действий – это «стимул + все реакции», а наблюдаемый отказ – «все реакции». Более того, «в ответ» на одно и то же тестовое воздействие (нажатие кнопки машины тестирования) может наблюдаться не обязательно какой-то один отказ. С тестовым воздействием может быть связано множество отказов, один из которых и будет наблюдаться. Например, в реактивных системах с несколькими выходными каналами посылка стимула с одновременным приёмом реакций по нескольким каналам может вызвать наблюдение частичной стационарности по одному из этих каналов. В данной статье мы расширяем класс рассматриваемых семантик, предполагая, что оператор указывает не только множество разрешаемых действий, но и множество ожидаемых отказов, которые могут наблюдаться. Это означает, что на кнопке написано множество наблюдений, возможных после нажатия этой кнопки: разрешаемые внешние действия и ожидаемые наблюдаемые отказы как множества действий. Итак, семантика взаимодействия определяется алфавитом внешних действий L и набором кнопкок – семейством P⊆Π(L∪Π(L)). Там, где это нужно для однозначного понимания, кнопку P∈P мы будем называть P-кнопкой. Для кнопки P∈P множество разрешаемых действий обозначим Pq=P∩L, а 72
множество ожидаемых наблюдаемых отказов Pr=P∩Π(L). Операции “r” и “q” распространим также на семейства кнопок: Uq={Pq|P∈U} и Ur={Pr|P∈U}. Предполагается, что любое внешнее действие из алфавита разрешается некоторой кнопкой ∪(Pq)=L. Отказ, являющийся элементом семейства Pr, то есть наблюдаемый (в принципе) отказ, будем называть Rотказом. Такую семантику мы называем P-семантикой. Она отличается от R/Q-семантики в [4,6,7,8] тем, что для P-кнопки P не обязательно Pr={Pq}, как для R-кнопки в R/Q-семантике, то есть R-отказов, ожидаемых при нажатии P-кнопки, может быть несколько, и они не обязательно совпадают с множеством разрешённых действий. Если Pr=∅, то такая P-кнопка соответствует Q-кнопке в R/Q-семантике, и такую P-кнопку мы также будем называть Q-кнопкой в P-семантике. Соответственно, P-кнопку P, содержащую отказы Pr≠∅, будем называть также R-кнопкой в P-семантике.
2.2. Безопасное тестирование При тестировании возможно возникновение тупика, когда никакого наблюдения нет, и неизвестно, будет ли оно через какое-то время или не будет никогда. Это возможно при нажатии кнопки P, если реализация не может выполнить ни одно разрешённое действие из Pq, но отказов из Pr также нет – для каждого отказа R∈Pr\Pq реализация могла бы выполнить некоторое неразрешённое действие z∈R\Pq. В R/Q-семантике это было возможно только при нажатии Q-кнопки, но в P-семантике это возможно также при нажатии R-кнопки P, которая содержит отказ, не вложенный во множество разрешённых действий, то есть, когда ∪Pr∨Pq. Понятно, что, если мы хотим, чтобы тест заканчивался через конечное время с вынесением соответствующего вердикта (pass или fail), то мы должны избегать при тестировании возникновения тупика. Кроме внешних, наблюдаемых действий, реализация может совершать внутренние, ненаблюдаемые (и, следовательно, неразличимые оператором) действия, которые обозначаются символом τ. Выполнение τ-действий не регулируется оператором – они всегда разрешены. Отказ может наблюдаться только тогда, когда реализация не может выполнять не только внешние действия из этого отказа, но и τ-действия. Предполагается, что любая конечная последовательность любых действий совершается за конечное время, а бесконечная последовательность – за бесконечное время. Бесконечная последовательность τ-действий называется дивергенцией («зацикливание») и обозначается символом Δ. Само по себе возникновение дивергенции не опасно, однако при наличии дивергенции любое продолжение тестирования, то есть нажатие любой кнопки, не гарантирует наблюдения через конечное время. Это объясняется тем, что оператор, ничего не наблюдая, не знает, случится ли такое наблюдение в будущем, или реализация так и будет 73
бесконечно долго выполнять свои внутренние действия. Для конечных (по времени выполнения) тестов это плохо. Кроме этого, мы вводим специальное, не регулируемое кнопками, действие, которое называем разрушением и обозначаем символом γ. Оно моделирует любое запрещённое или недекларированное поведение реализации. Например, в терминах пред- и постусловий поведение программы определено (постусловием) только в том случае, когда выполнено предусловие обращения к ней. Если же предусловие нарушено, поведение программы считается полностью неопределённым. Семантика разрушения предполагает, в частности, что в результате такого поведения система может быть разрушена. Если в реализации после выполнения некоторого внешнего действия возможно разрушение, нажатие кнопки, разрешающей это действие, может привести к разрушению. Опасность возникновения разрушения при тестировании подразумевается его семантикой. Итак, поскольку мы ограничиваемся конечными по времени выполнения тестами и не хотим разрушать реализацию, мы должны избегать при тестировании возникновения тупиков, попыток выхода из дивергенции и разрушения. Такое тестирование называется безопасным. Можно также отметить, что нажатие кнопки P с пустым множеством Pq разрешаемых действий не эквивалентно отсутствию нажатой кнопки. В обоих случаях все внешние действия запрещены, однако при нажатии R-кнопки P, то есть Pr≠∅, возможно наблюдение отказа R∈Pr: оператор узнаёт об остановке машины, когда она не может выполнять внутренние действия, разрушение и действия из отказа R, даже если бы они были разрешены. Кнопка P с пустым множеством разрешённых действий Pq не может вызвать разрушения после действия (никакого действия быть не может), но она опасна, если есть дивергенция, как и любая другая кнопка. Кнопка {∅} запрещает все внешние действия и разрешает наблюдение только пустого отказа, означающего остановку машины, когда она не может выполнять внутренние действия и разрушение. Пустую кнопку ∅ вообще никогда нельзя нажимать, поскольку никакого наблюдения быть не может; такая кнопка соответствует отсутствию нажатой кнопки. Поэтому будем считать, что ∅∉P.
2.3. LTS-модель и трассовая модель В качестве модели реализации и спецификации мы используем систему помеченных переходов (LTS – Labelled Transition System). LTS – это ориентированный граф с выделенной начальной вершиной, дуги которого помечены некоторыми символами. Формально, LTS – это совокупность S=LTS(VS,L,ES,s0), где VS – непустое множество состояний (вершин графа), L – алфавит внешних действий, ES⊆VS×(L∪{τ,γ})×VS – множество переходов (помеченных дуг графа), s0∈VS – начальное состояние 74
(начальная вершина графа). Переход из состояния s в состояние s` по действию z обозначается s⎯z→s`. Обозначим s⎯z→ =def ∃s` s⎯z→s` и s⎯z⏐ =def ⌠s` s⎯z→s`. Выполнение LTS, помещённой в «чёрный ящик» машины тестирования, сводится к выполнению того или иного перехода, определённого в текущем состоянии и разрешаемого нажатой кнопкой (τ- и γ-переходы разрешены при нажатии любой кнопки и при отсутствии нажатой кнопки). Состояние s LTS-модели S называется стабильным, если в нём не определены τ- и γпереходы:
действий, R-отказов, символов Δ и γ, называется соответствующей «взгляду» на реализацию в P-семантике.
stab(s,S) =def s⎯τ⏐ & s⎯γ⏐.
На уровне модели безопасное тестирование, прежде всего, предполагает формальное определение отношения безопасности «кнопка безопасна в модели после R-трассы». При безопасном тестировании будут нажиматься только безопасные кнопки. Это отношение различно для реализационной и спецификационной моделей. В LTS-реализации I отношение безопасности означает, что нажатие кнопки P∈P после R-трассы σ не может a) означать попытку выхода из дивергенции (после трассы нет дивергенции), b) вызывать разрушение (после действия, разрешаемого кнопкой) и c) приводить к тупику. В произвольной Pсемантике для определения условий a) и b) достаточно F-трасс модели, но для определения условия c), вообще говоря, недостаточно F-трасс, и приходится использовать LTS-модель. Последнее условие означает, что в каком бы стабильном состоянии после трассы не оказалась реализация, при нажатии кнопки P будет какое-либо наблюдение.
Состояние называется дивергентным, если в нём начинается бесконечная цепочка τ-переходов (в частности, τ-цикл, в том числе, τ-петля). Отказ порождается стабильным состоянием, в котором нет переходов по действиям из этого отказа. Если в данном состоянии при данной нажатой кнопке (или отсутствии нажатых кнопок) возможно выполнение нескольких действий, то недетерминированным образом выбирается одно из них. Также, если в стабильном состоянии при нажатой кнопке P возможно выполнение внешних действий из Pq, и имеются один или несколько отказов из Pr, то недетерминированным образом выбирается выполнение одного из этих действий, или никакое действие не выполняется, а оператор наблюдает один из отказов. Это отличается от R/Q-семантики, где при отказе никакое действие не может выполняться, поскольку Pr={Pq}. Обозначим через obs(s,P,S) множество действий и отказов, порождаемых состоянием s LTS-модели S, когда нажата кнопка P: obs(s,P,S) =def {z∈Pq|s⎯z→}∪{R∈Pr|∀z∈R∪{τ,γ} s⎯z⏐}. Для получения трасс (последовательностей наблюдений) LTS достаточно добавить в каждом стабильном состоянии виртуальные петли, помеченные порождаемыми действиями и отказами, а также добавить Δ-переходы во всех дивергентных состояниях. После этого рассматриваются все конечные маршруты LTS, начинающиеся в начальном состоянии и не продолжающиеся после Δ- или γ-перехода. Трассой маршрута считается последовательность пометок его переходов с пропуском τ-переходов. Такие трассы мы называем полными или F-трассами, а множество F-трасс LTS S – полной трассовой моделью или F-моделью, и обозначаем F(S). F-трасса, все отказы которой принадлежат семейству Pr, называется R-трассой. Это те трассы, которые могут наблюдаться на машине тестирования в P-семантике (дивергенция и разрушение считаются условно наблюдаемыми). Множество всех R-трасс LTS, то есть проекция её F-модели на алфавит, состоящий из всех внешних 75
R-моделью,
Обозначим через obs(σ,P,S) множество действий и отказов, продолжающих трассу во множестве F-трасс LTS-модели S, то есть порождаемых всеми состояниями после трассы σ LTS-модели S, после нажатия кнопки P: obs(σ,P,S) =def {u∈P|σ⋅〈u〉∈F(S)}=∪{obs(s,P,S)|s∈(S after σ)}.
2.4. Гипотеза о безопасности и безопасная конформность
P safeγΔin I after σ =def ∀z∈Pq σ⋅〈z,γ〉∉F(I) & σ⋅〈Δ〉∉F(I). P safe in I after σ =def P safeγΔin I after σ & ∀s∈(I after σ) (stab(s,I) ⇒ obs(s,P,I)≠∅). Существует важный частный случай, когда вся информация, необходимая для определения отношения safe in, содержится в F-трассах модели. Это случай, когда для каждой кнопки P все R-отказы состоят только из разрешаемых действий ∪Pr⊆Pq: P safe in I after σ=def P safeγΔin I after σ & (Pr=∅ ⇒ σ⋅〈Pq〉∉F(I)). В спецификации отношение безопасности кнопок, называемое safe by, определяется не однозначно. Фактически, речь идёт о правилах, которым должно быть подчинено это отношение. Задание спецификации означает задание не только LTS-модели S, но и отношения safe by, подчиняющегося этим правилам. Таких правил два. Правило 1): если кнопка безопасна после трассы, то её нажатие не может вызвать разрушение и попытку выхода из дивергенции, а кроме того, хотя бы в одном (не обязательно в каждом) 76
состоянии после трассы возможно наблюдение при нажатии этой кнопки (в этом состоянии нет тупика). Правило 2): если после трассы (хотя бы в одном состоянии после трассы) есть некоторое наблюдение, которое можно получить, нажимая кнопку, не приводящую к разрушению или попытке выхода из дивергенции, то одна из таких кнопок должна быть безопасна после трассы. Это правило требует «максимального» использования спецификации, то есть в ней не должно быть трасс, которые не могут быть проверены при тестировании, за исключением, конечно, таких трасс, проверка которых может вызвать разрушение или попытку выхода из дивергенции. Такое отношение safe by всегда существует: достаточно объявить безопасной каждую кнопку, которая не приводит к разрушению или попытке выхода из дивергенции и разрешает наблюдение, продолжающее трассу: ∀P∈P ∀u 1)
P safe by S after σ ⇒ P safeγΔin S after σ & ∃s∈(S after σ) obs(s,P,S)≠∅.
2)
P safeγΔin S after σ & ∃s∈(S after σ) u∈obs(s,P,S) ⇒ ∃R∈P R safe by S after σ & u∈obs(s,R,S).
Заметим, что в последней строке вместо u∈obs(s,R,S) можно написать просто u∈R. В отличие от отношения safe in, правила отношения safe by всегда могут быть определены только в терминах F-трасс модели. Это можно объяснить тем, что спецификация должна говорить о безопасности или опасности реализации, а также о её конформности или неконформности только на основании трасс. ∀P∈P ∀u 1) P safe by S after σ ⇒ P safeγΔin S after σ & obs(σ,P,S)≠∅. 2) P safeγΔin S after σ & u∈obs(σ,P,S) ⇒ ∃R∈P R safe by S after σ & u∈obs(σ,R,S). Заметим, что в последней строке вместо u∈obs(σ,R,S) можно написать просто u∈R. Безопасность кнопок определяет безопасность наблюдений (действий и Rотказов) после R-трассы. Наблюдение безопасно, если безопасна какая-нибудь кнопка, которой оно принадлежит. Теперь мы можем определить безопасные трассы. R-трасса безопасна, если 1) модель не разрушается с самого начала (сразу после включения машины ещё до нажатия первой кнопки), то есть в ней нет трассы 〈γ〉, 2) каждый символ трассы безопасен после непосредственно предшествующего ему префикса трассы. Множества безопасных трасс 77
реализации I и спецификации S обозначим SafeIn(I) и SafeBy(S) соответственно. Требование безопасности тестирования выделяет класс безопасных реализаций, то есть таких, которые могут быть безопасно протестированы для проверки их конформности или неконформности заданной спецификации. Этот класс определяется следующей гипотезой о безопасности: реализация I безопасна для спецификации S, если 1) в реализации нет разрушения с самого начала, если этого нет в спецификации, 2) после общей безопасной трассы реализации и спецификации любая кнопка, безопасная в спецификации, безопасна после этой трассы в реализации: I safe for S =def
(〈γ〉∉F(S) ⇒ 〈γ〉∉F(I)) & ∀σ∈SafeBy(S)∩SafeIn(I) ∀P∈P (P safe by S after σ ⇒ P safe in I after σ).
Следует отметить, что гипотеза о безопасности не проверяема при тестировании и является его предусловием. После этого можно определить отношение (безопасной) конформности: реализация I безопасно конформна (или просто конформна) спецификации S, если она безопасна и выполнено тестируемое условие: любое наблюдение, возможное в реализации в ответ на нажатие безопасной (в спецификации) кнопки, разрешается спецификацией: I saco S =def
I safe for S & ∀σ∈SafeBy(S)∩SafeIn(I) ∀P safe by S after σ obs(σ,P,I) ⊆ obs(σ,P,S).
2.5. Отказы и множество разрешаемых действий Выше мы указали на важный подкласс P-семантик с ограничением вложенности: для каждой кнопки P все R-отказы состоят только из разрешаемых действий ∪Pr⊆Pq. Этот подкласс семантик оказывается не эквивалентным классу всех P-семантик. Мы покажем, что существует такая семантика P, такая спецификация S и такая реализация I, которая безопасна I safe forP S, но не конформна I sacoP S, а для любой семантики P1 с ограничением вложенности имеет место I sacoP1 S. Это означает, что семантики без ограничения вложенности обладают большей способностью различения моделей, чем семантики с таким ограничением. Рассмотрим пример на Рис. 3. Здесь для семантики P без ограничения вложенности реализация безопасна I safe forP S, но не конформна спецификации I sacoP S. Действительно, трасса σ=〈{b},a〉 безопасна в реализации и спецификации: σ∈SafeBy(S)∩SafeIn(I). Кнопка P={b} безопасна в спецификации после этой трассы: P safe by S after σ. В реализации после нажатия этой кнопки 78
может наблюдаться действие: b∈obs(σ,P,I), а в спецификации – не может: b∉obs(σ,P,S). Заметим, что после трассы μ=〈a〉 любая кнопка из P безопасна в спецификации (и в реализации) и любое наблюдение, возможное в реализации (действие a или b), возможно также в спецификации. Семантики с ограничением вложенности могут содержать только следующие кнопки: {a}, {a,{a}} {b}, {b,{b}}, {a,b,{a}}, {a,b,{b}}, {a,b,{a,b}}, {a,b,{a},{b}}. В спецификации в самом начале (после пустой трассы) безопасны только кнопки первой строки, поскольку действие b разрушающее. Следовательно, трасса σ=〈{b},a〉 оказывается опасной в спецификации. Остаётся безопасной трасса μ=〈a〉, но после неё любое наблюдение, возможное в реализации (действие a или b), возможно также в спецификации. Тем самым, в любой семантике P1 с ограничением вложенности имеет место I sacoP1 S.
синхронными. Остальные внешние действия z∈A\B и z∈B\A, а также символы τ и γ называются асинхронными. Переход по такому символу выполняется в одной из LTS при сохранении состояния другой LTS. Результатом композиции двух LTS I и T становится LTS I≡≠T в алфавите A≡≠B =def (A\B)∪(B\A). Её состояния – это пары состояний it LTS-операндов, начальное состояние – это пара начальных состояний, а переходы порождаются следующими правилами вывода: (1) z∈(A∪{τ,γ})\B & i⎯z→i` it⎯z→i`t, it⎯z→it`, (2) z∈(B∪{τ,γ})\A & t⎯z→t` (3) z∈A∩B & i⎯z→i` & t⎯z→t` it⎯τ→i`t`. Тестирование понимается как замкнутая композиция LTS-реализации I в алфавите A и LTS-теста T в противоположном алфавите B=A. Мы будем предполагать, что в тесте нет разрушения и τ-переходов. Для обнаружения Rотказа R в тесте (но не в реализации!) допускается специальный переход по R-отказу, который может срабатывать тогда, когда в реализации нет τ- и γпереходов, а также переходов по действиям из R. Для удобства мы будем записывать в тесте переход не по R, а по множеству противоположных действий R. (4) ∀z∈R∪{τ,γ} i⎯z⏐ & t⎯R→t`
Рис.3. Семантика P без ограничения вложенности: L = {a,b} и P={{a,{b}},{b}}.
2.6. Параллельная композиция и генерация тестов В LTS-теории взаимодействие двух систем моделируется оператором параллельной композиции. Мы используем оператор композиции, аналогичный тому, который определяется в алгебре процессов CCS (Calculus of Communicating Systems) [14,16]. Будем считать, что для каждого внешнего действия z определено противоположное действие z такое, что z=z. Например, посылке стимула из теста соответствует приём теста в реализации, а выдаче реакции реализацией соответствует приём этой реакции в тесте. Операцию «подчёркивание» распространим на множества действий: A={a|a∈A}. Параллельное выполнение двух LTS в алфавитах A и B понимается так, что переходы по противоположным действиям z и z, где z∈A∩B, выполняются синхронно, то есть в обеих LTS одновременно, причём в композиции это становится τ-переходом. Такие действия называются 79
it⎯τ→it`.
Заметим, что переход по R-отказу играет ту же роль, что θ-переход в R/Qсемантике. Но здесь есть два важных отличия. Во-первых, при нажатии Pкнопки P переход по R-отказу R∈Pr может срабатывать даже тогда, когда в реализации могут выполняться действия z∈Pq\R. В то же время θ-переход срабатывает только тогда, когда ни одно действие не может выполняться, поскольку Pr={Pq}. Во-вторых, в P-семантике кнопка не однозначно определяет возможный отказ, поскольку множество Pr может содержать несколько отказов. Также множество разрешаемых действий, то есть действий, противоположных тем, которыми помечены переходы в состоянии теста, в P-семантике не однозначно определяет возможный в тесте переход по R-отказу, поскольку может быть несколько P-кнопок P с одним и тем же множеством разрешаемых действий Pq, но с разными множествами отказов Pr. В отличие от этого, в R/Q-семантике множество разрешаемых действий, образующее R-кнопку, однозначно определяет соответствующий R-отказ, поскольку Pr={Pq}. Это и является причиной того, почему в P-семантике мы говорим о переходе по R-отказу, а не о θ-переходе. Поскольку алфавиты реализации и теста противоположны, композиционный алфавит пуст, и в композиционной LTS есть только τ- и γ-переходы. При безопасном тестировании γ-переходы недостижимы. Выполнению теста соответствует прохождение τ-маршрута, начинающегося в начальном состоянии композиции I≡≠T. Тест заканчивается, когда достигается 80
терминальное состояние теста. Каждому такому терминальному состоянию назначается вердикт pass или fail. Реализация проходит тест, если состояния теста с вердиктом fail недостижимы. Реализация проходит набор тестов, если она проходит каждый тест из набора. Набор тестов значимый, если каждая конформная реализация его проходит; исчерпывающий, если каждая неконформная реализация его не проходит; полный, если он значимый и исчерпывающий. Задача заключается в генерации полного набора тестов по спецификации. Обычно ограничиваются так называемыми управляемыми тестами, то есть тестами, которые могут пониматься как однозначная инструкция оператору машины (без лишнего недетерминизма). Для этого множество наблюдений, для которых определены переходы в данном состоянии теста, должно совпадать с какой-нибудь кнопкой P∈P (точнее, c P, поскольку при композиции CCS тест определяется в противоположном алфавите). Множество внешних действий, для которых определены переходы в данном состоянии теста, должно быть множеством Pq действий, противоположных разрешаемым действиям, а множество отказов, по которым определены переходы, должно совпадать с множеством Pr. Оператор, исполняя тест, однозначно определяет, какую кнопку ему нужно нажимать в данном состоянии теста, и для каждого возможного наблюдения у него есть «инструкция» по дальнейшему поведению, задаваемая соответствующим переходом теста по этому наблюдению. Полным набором всегда является набор всех примитивных тестов. Примитивный тест строится по одной выделенной безопасной R-трассе спецификации. Для этого сначала в трассу перед каждым наблюдением вставляется какая-нибудь безопасная (после префикса трассы) P-кнопка P, которой это наблюдение принадлежит. Перед каждым R-отказом R вставляется безопасная кнопка P такая, что R∈Pr, а перед каждым действием z – безопасная кнопка P, разрешающая это действие, то есть z∈Pq. Безопасность трассы гарантирует наличие такой безопасной кнопки P. Выбор кнопки P может быть неоднозначным, то есть по одной безопасной трассе спецификации можно сгенерировать, вообще говоря, множество разных примитивных тестов. После расстановки кнопок получается последовательность, которая во втором разделе статьи называется P-историей. По ней и строится LTS-тест (рис. 4). Его состояниями становятся расставленные кнопки, начальное состояние – это первая в трассе кнопка, символы переходов из состояния-кнопки P – это все возможные наблюдения (точнее, противоположные им): действия z∈Pq и отказы R∈Pr. Если это не последняя кнопка, то один переход ведёт в состояние, соответствующее следующей кнопке. Остальные переходы ведут в терминальные состояния. Вердикт pass назначается тогда, когда соответствующая R-трасса есть в спецификации, а вердикт fail – когда нет. Такой вердикт соответствует строгим тестам, которые, во-первых, значимые (не ловят ложных ошибок) и, 81
во-вторых, фиксируют обнаруженные ошибки (выносят вердикт fail, а не pass). Любой строгий тест можно заменить на объединение примитивных тестов, которое обнаруживает те же самые ошибки. Для R∈Pr и b,c∈L σ=〈R,b,c〉⇒вставляем безопасные кнопки⇒ ⇒〈A,R,B,b,C,c〉 такие, что: A,B,C∈P, R∈A, b∈B, c∈C. имеет Наблюдение u индекс «хороший», если наблюдение u есть в спецификации после префикса трассы; иначе – индекс «плохой».
Рис.4. Примитивный тест для безопасной R-трассы σ На практике возникают две основные проблемы, связанные с применением теории тестирования конформности: проблема недетерминизма и глобального тестирования и проблема бесконечности полного тестового набора. Эти проблемы в общем виде рассматривались в [4,6,7,8] для R/Q-семантики. Обобщение до P-семантики не вносит в это рассмотрение ничего принципиально нового. Поэтому мы не будем здесь останавливаться на этих проблемах.
3. P-семантика с приоритетами До сих пор мы предполагали отсутствие приоритетов между действиями, которые тестируемая система может выполнять в данной ситуации: любое определённое в реализации и разрешённое оператором действие может быть выполнено (выбор одного из таких действий выполняется недетерминированным образом). В то же время для реальных программных и аппаратных систем это правило не всегда адекватно отражает требуемое поведение системы. Рассмотрим несколько примеров. Выход из дивергенции. При наличии дивергенции запрос, поступающий извне, может бесконечно долго игнорироваться системой, если он имеет тот 82
же приоритет, что и внутренняя активность. Если речь идёт о составной системе, собранной из нескольких компонентов, то дивергенция может быть естественным результатом бесконечного взаимодействия компонентов между собой. И в этом случае для обработки запроса, поступающего в систему (в один из её компонентов) извне, он должен иметь больший приоритет, чем внутреннее взаимодействие. Выход из осцилляции (приоритет приёма над выдачей). Под осцилляцией понимается бесконечная цепочка выдачи реакций реактивной системой. Для того, чтобы такую цепочку можно было прервать, заставив систему обрабатывать поступающий извне стимул, приём стимула должен иметь больший приоритет, чем выдача реакций. Приоритет выдачи над приёмом в неограниченных очередях. Этот обратный пример характерен для неограниченной очереди, используемой в качестве буфера между взаимодействующими системами, в частности, при асинхронном тестировании (тестировании в контексте). Здесь нужно, чтобы выборка из очереди была приоритетней постановки в очередь. В противном случае очередь имеет право только принимать сообщения и никогда их не выдавать. При асинхронном тестировании для входной очереди это означает, что все стимулы, посылаемые тестом, не доходят до реализации, бесконечно накапливаясь в очереди. Соответственно, для выходной очереди это означает, что тест может не получать никаких реакций от реализации, хотя она их выдаёт, поскольку они «оседают» в очереди. Прерывание цепочки действий. Команда «отменить» (cancel) должна останавливать выполнение последовательности действий, инициированной предыдущим запросом, и вызывать цепочку завершающих действий. При отсутствии приоритетов такая команда, даже если она выдана сразу после выдачи запроса, имеет право быть выполнена только после того, как вся обработка закончится, то есть, фактически, ничего «не отменяет». Приоритетная обработка входных воздействий. Если в систему поступает одновременно несколько запросов, то часто требуется их обработка в соответствии с некоторыми приоритетами между ними. Это часто реализуется в виде очереди запросов с приоритетами или в виде нескольких очередей запросов с приоритетами между очередями. К этому типу приоритетов относится и обработка аппаратных прерываний в операционной системе. В существующих теориях тестирования конформности (conformance testing) подразумевается отсутствие приоритетов [10]. Это не даёт возможности проверять при тестировании выполнение тех требований к системе, которые могут быть выражены только в форме приоритетов. В [8] предложен способ введения приоритетов для R/Q-семантики. В данной статье мы распространяем этот способ на P-семантику. При таком распространении, на самом деле, мало что меняется, за исключением того, что при нажатии кнопки наблюдаемый отказ не обязательно совпадает с множеством разрешаемых оператором действий (от которого и зависят приоритеты). 83
3.1. Предикаты на переходах LTS-модели Независимо от наличия или отсутствия приоритетов семантика взаимодействия предполагает, что выполняться может только то действие, которое определено в реализации и разрешено оператором машины тестирования. Если приоритетов нет, то выполняться может любое определённое и разрешённое действие, и выбор выполняемого действия не детерминирован. Наличие приоритетов означает, что не все определённые и разрешённые действия могут выполняться, то есть выполнимость действия зависит также от того, какие ещё действия определены и/или разрешены. Эту зависимость можно изобразить в виде предиката от множества разрешённых действий, который назначается переходу LTS-модели. Поскольку для перехода s⎯z→s` известно его предсостояние s, а для этого состояния s известно, какие ещё переходы в нём начинаются, предикат на переходе можно считать не зависящим от множества определённых (в состоянии s) действий. В то же время переходы по одному и тому же действию, ведущие из разных состояний, могут иметь разные предикаты. LTS-модель с приоритетами – это LTS, алфавит которой – это декартово произведение алфавита внешних действий и множества предикатов на алфавите внешних действий Π={π:Π(L)→Bool}: S=LTS(VS,L×Π,ES,s0). Переход s⎯z,π→s` может выполняться только тогда, когда оператор разрешает такое множество внешних действий Q⊆L, что z∈Q∪{τ,γ} и π(Q)=true. Если есть несколько выполнимых действий, выполняется одно из них, выбираемое, по-прежнему, недетерминированным образом. Предикат можно понимать как булевскую функцию от булевских переменных z1,z2,…, взаимнооднозначно соответствующих внешним действиям из алфавита L. Например, для предиката π=a&¬b∨c переход s⎯z,π→s` может выполняться только тогда, когда оператор разрешил такое множество внешних действий Q, что z∈Q∪{τ,γ} & (a∈Q & b∉Q ∨ c∈Q). Это означает, что действие z – это внутреннее действие, разрушение или внешнее действие, разрешённое оператором, а также выполнено хотя бы одно из двух условий: 1) оператор разрешил действие a и не разрешил действие b, 2) оператор разрешил действие c. Итак, в общем случае предикат – это булевская функция от множества разрешённых действий. Можно отметить важный частный случай, когда предикат зависит только от разрешённых и определённых внешних действий. Иными словами, предикат на переходе s⎯z,π→s` не зависит булевских переменных, соответствующих внешним действиям, по которым нет переходов из состояния s. Это означает, что выполнимость перехода зависит только от того, разрешено ли действие z, и какие ещё действия определены в состоянии s и разрешены оператором. В этом случае реализацию не 84
интересуют (она «не видит») те действия, которые оператор разрешает, но они всё равно не могут выполняться, поскольку не определены в текущем состоянии реализации. Нажимая кнопку, оператор как бы «подсвечивает» некоторые действия реализации, определённые в её текущем состоянии, и выполнимость перехода по каждому из них определяется соответствующим предикатом от множества «подсвеченных» действий. Предикат π как булевская функция от булевских переменных-действий может быть представлен в виде совершенной дизъюнктивной нормальной формы (СДНФ) π=η1∨η2∨…, где ηi=xi1&xi2&…, xij=zj или xij=¬zj, и zj пробегает множество всех внешних действий. Тогда переход s⎯z,π→s` можно заменить на множество кратных переходов с предикатамиВ свою очередь, дизъюнкту ηi дизъюнктами s⎯z,ηi→s`. взаимнооднозначно соответствует множество Qi тех действий, для которых xij=zj. При композиции это множество является множеством действий, разрешаемых партнёром. Тем самым, мы можем считать, что на переходах написаны не произвольные предикаты, а множества разрешаемых действий s⎯z,Piq→s`, или кнопки s⎯z,Pi→s`. Когда нажимается некоторая кнопка Pi, выполняться могут только переходы вида s⎯z,Piq→s` или s⎯z,Pi→s`, где z∈Piq∪{τ,γ}. Такой переход от LTS с предикатами к LTS с кнопками аналогичен переходу от расширенных автоматов (EFSM – Extended Finite State Machine) к обычным автоматам (FSM). Для заданной Pсемантики речь идёт только о тех кнопках, которые принадлежат семейству P; переходы по другим кнопкам при тестировании, фактически, игнорируются – они не могут выполняться при тех тестовых возможностях, которые задаются семантикой.
3.2. Стабильность, отказы, разрушение и дивергенция Для машины без приоритетов отказ R наблюдается в стабильном состоянии (состоянии без τ- и γ-переходов), в котором нет переходов по действиям из R. Если есть приоритеты, то меняется, прежде всего, само понятие стабильности. Оно становится условным, то есть зависящим от множества разрешённых действий Pq: состояние s LTS S стабильно, если для всех τ- и γ-переходов из этого состояния их предикаты от Pq ложны π(Pq)=false. stab(s,P,S) =def ⌠π π(Pq) & (s⎯τ,π→ ∨ s⎯γ,π→). Соответственно, меняется условие наблюдения отказа R∈Pr: отказ наблюдается в стабильном состоянии, в котором для всех переходов s⎯z,π→ по действиям z∈R их предикаты ложны π(Pq)=false. Здесь мы должны уточнить, что происходит, когда кнопка отжимается. Для машины без приоритетов кнопка автоматически отжимается при любом наблюдении действия или отказа. После действия машина может выполнять любые τ-переходы (а также γ-переход), но после отказа машина стоит, 85
поскольку отказ происходит в стабильном состоянии, в котором нет τ- и γпереходов. Однако для машины с приоритетами отжатие кнопки меняет множество разрешённых действий, если только не была нажатой кнопка P с Pq=∅, не разрешающая ни одного внешнего действия. После наблюдения реализация начинает выполнять τ-переходы с приоритетом π(∅)=true. Заметим, что таким наблюдением может быть не только действие, но и отказ. Причина этого в том, что отказ R∈Pr означал невозможность выполнения внешних действий z∈R, а также τ- и γ-переходов, поскольку их предикаты стали ложны π(Pq)=false. После отжатия кнопки P множество разрешённых внешних действий пусто, и теперь могут выполняться τ- и γпереходы с предикатами π(∅)=true. Далее оператор может снова нажать ту же кнопку или другую кнопку. Если допускается переключение кнопок, то есть нажатие второй кнопки без ожидания наблюдения по первой кнопке, то это интерпретируется как отжатие первой кнопки с последующим нажатием второй кнопки. Мы будем считать, что «в промежутке» между двумя кнопками создаётся ситуация, когда ни одна кнопка не нажата, и реализация может выполнять τ- и γ-переходы с предикатами π(∅)=true. Общая парадигма здесь заключается в том, что ситуация отсутствия тестового воздействия возникает всегда при включении машины (до нажатия первой кнопки), после любого наблюдения и между двумя тестовыми воздействиями при отсутствии наблюдения по первому воздействию. Более подробно переключение кнопок рассматривается в следующем подразделе. При отсутствии приоритетов разрушение возможно либо с самого начала до нажатия какой-либо кнопки, либо после выполнения некоторого внешнего действия, разрешённого нажатой кнопкой. В первом случае любое тестирование опасно (машину нельзя включать). При наличии приоритетов выполнимость перехода по разрушению s⎯γ,π→, как и для любого перехода, определяется предикатом π, который зависит от множества разрешённых действий. Поэтому следует говорить о выполнимом или не выполнимом разрушении в зависимости от нажатой кнопки. Разрушение может стать выполнимым при нажатии или отжатии кнопки, то есть разрушение возможно не только с самого начала или после внешнего действия, но также сразу после нажатия кнопки P, если π(Pq)=true, или после наблюдения отказа из Pr, если π(∅)=true. В последнем случае, правда, разрушение было выполнимым и до нажатия кнопки P. Мы уже говорили, что даже для машины без приоритетов проблема дивергенции состоит не в дивергенции самой по себе, а в выходе из неё. При наличии приоритетов, если внешнее воздействие имеет больший приоритет, чем внутренняя активность, дивергенция прекращается. Теперь выполнимость τ-действий зависит от нажатой кнопки, и мы можем косвенно управлять ими и, следовательно, дивергенцией. Тогда можно говорить о выполнимой 86
дивергенции: при одной нажатой кнопке (или когда нет нажатой кнопки) все τ-действия бесконечной цепочки выполнимы, а при другой – нет и, следовательно, нет «зацикливания». Выйти из дивергенции, которая начинает выполняться после кнопки A, можно с помощью кнопки B, при которой дивергенция не выполнима. Заметим, что для этого требуется переключение кнопок, то есть нажатие кнопки без наблюдения (которого может не быть). Единственный случай, когда из дивергенции нельзя гарантированно выйти, – это когда дивергенция выполнима при нажатии любой кнопки.
3.3. Переключение кнопок В машине без приоритетов кнопку можно нажимать либо после включения машины, либо после того, как произошло наблюдение по предыдущей кнопке. Иными словами, запрещается переключать кнопки без наблюдения, отжимая одну кнопку и нажимая другую. Этот запрет объясняется тем, что, если приоритетов нет, возможность переключения кнопок не увеличивает мощность тестирования. Действительно, если была нажата кнопка P, а потом без наблюдения нажата другая кнопка Q (а кнопка P отжата), то в этом интервале времени реализация могла выполнять только τ-действия. Но τдействия всегда разрешены, поэтому реализация могла бы выполнять их и в том случае, когда вместо кнопки P сразу нажималась кнопка Q (а второй раз, естественно, не нажималась). Тем самым, любое поведение, которое можно наблюдать в первом случае, можно было бы наблюдать и во втором случае. При наличии приоритетов переключение без наблюдения необходимо для полноты тестирования, поскольку различные множества разрешённых действий по-разному влияют на выполнение τ-действий (τ-переходы тоже могут иметь предикаты), что приводит к внешне различимым поведениям. Например, если в реактивной системе приём стимула приоритетнее выдачи реакций и τ-действий, последние выполняются только тогда, когда реализация не может принять стимул, посылаемый ей тестом. На рис. 5 показан пример, где для получения тестом реакции !y после стимула ?a нельзя сразу посылать этот стимул (тогда после него будет реакция !x), а нужно сначала послать стимул ?b, например, с помощью кнопки {?b}, а потом переключить эту кнопку на кнопку, посылающую стимул ?a, например, {?a}. Если реализация принимает стимул ?b, то переключение нужно успеть сделать до приёма стимула ?b. Если же реализация блокирует стимул ?b (нет пунктирного перехода), то можно «не торопиться». Если блокировка {?b} наблюдаема, то есть нажималась не кнопка {?b}, а кнопка {?b,{?b}}, можно сначала дождаться блокировки {?b}, а потом послать стимул ?a.
87
Рис.5. Переключение кнопок Тем не менее, в пользу запрета на переключение кнопок имеются разумные аргументы и в случае наличия приоритетов. Дело в том, что переключение кнопок позволяет «обходить» тупики: если оператор переключает кнопку P на (любую) другую кнопку Q, то возникновение тупика при нажатии кнопки P не препятствует такому переключению (как на рис. 5, когда нет пунктирного перехода по стимулу ?b, а блокировка {?b} не наблюдаема при посылке стимула ?b). Но если возможен тупик, то при безопасном тестировании мы не можем нажимать кнопку P без последующего переключения на другую кнопку, то есть с ожиданием наблюдения (на 0 без пунктирного перехода кнопку {?b} всегда нужно переключать на кнопку {?a} или какую-то другую). Тем самым, если можно переключать кнопки, то условие безопасности кнопок становится более сложным (ниже мы его подробно рассмотрим). Если переключений кнопок нет, тестирование выглядит более привычно как чередующаяся последовательность тестовых воздействий (нажимается кнопка) и наблюдений. Кроме того, в этом случае к работе оператора предъявляется меньше временных требований.
3.4. Временные ограничения на работу оператора (теста) Введение приоритетов усложняет работу оператора, налагая более сложные требования по времени. Если приоритетов нет, то оператор должен уметь достаточно быстро нажимать кнопку после включения машины или после предыдущего наблюдения. Заметим, что если оператор не успевает достаточно быстро нажать кнопку, ничего страшного не случится, поскольку машина успеет выполнить только одно или несколько τ-действий, которые (в машине без приоритетов) она может выполнить и в том случае, когда кнопка была нажата немедленно. Иными словами, мы требуем, чтобы оператор мог работать быстро, но не заставляем его всегда работать быстро. Если приоритеты есть, то возможность наблюдения тех или иных поведений реализации требует не только достаточно быстрой скорости работы оператора, но также достаточно медленной, средней и т.д. В примере на рис. 6 стимул ?a может приниматься в трёх состояниях 1, 2 и 3, но реакции после этого различны: !x, !y или !z. Эти состояния связаны τ-переходами, которые выполнимы только в том случае, когда тест не посылает стимул ?a. Поэтому 88
реакция !x будет наблюдаться, только если оператор быстро пошлёт стимул ?a, реакция !z – только если оператор не будет торопиться, а реакция !y – только при средней скорости работы.
Рис.6. Интервалы времени Если есть переключение кнопок, то такое переключение также нужно уметь делать с различными интервалами времени, чтобы «заставить» реализацию проходить нужное число τ-переходов между двумя кнопками. Таким образом, для машины с приоритетами следует учитывать временные задержки, которые делает оператор между наблюдением и последующим нажатием кнопки или между двумя нажатиями кнопок при их переключении без наблюдения. Мы можем считать, что в погодные условия включены также те факторы, которые влияют на «свободу воли» оператора, определяя те или иные временные задержки при нажатии кнопок. Это согласуется с тем, что оператор должен моделировать любую скорость работы окружения. Работа оператора моделирует выполнение тестовой программы на компьютере. Такая программа недетерминирована только на некотором уровне абстракции, когда мы отвлекаемся от других программ или аппаратуры, влияющих на её поведение.
3.5. Истории Если приоритетов нет, то возможность наблюдения действия после некоторой предыстории взаимодействия не зависит от того, какая именно нажимается кнопка, разрешающая это действие. При наличии приоритетов это становится важным, поскольку различным кнопкам соответствуют различные множества разрешённых действий, и при нажатии одной кнопки предикат может оказаться истинным, и действие может наблюдаться, а при нажатии другой – ложным, и действие не может наблюдаться. Поэтому теперь нужно запоминать не только наблюдения, но также те кнопки, которые нажимал оператор. Тем самым, результатом тестового эксперимента становится последовательность действий, отказов и кнопок. Такую последовательность мы будем называть историей. Чтобы в истории отличить Q-кнопку P от отказа (и то и другое – подмножество внешних действий), мы будем любую кнопку заключать в кавычки и писать “P”, а не просто P. Если не ограничиваться только безопасным тестированием, то мы должны включить в 89
истории также разрушение и дивергенцию, и после них история не может продолжаться, аналогично трассам. Очевидно, что в истории каждому внешнему действию z непосредственно предшествует кнопка “P”, разрешающая это действие z∈Pq, а каждому отказу R – R-кнопка “P”, допускающая этот отказ R∈Pr. Могут или не могут идти две кнопки подряд, зависит от того, разрешено или запрещено переключение кнопок. Для заданной P-семантики истории будем называть P-историями. Определим их более формально. Рассмотрим LTS с предикатами S. Для множества разрешённых действий Pq переход s⎯z,π→s` будем называть Pq-выполнимым, если его предикат от Pq истинен π(Pq)=true. Будем говорить, что для множества разрешённых действий Pq τ-маршрут Pq-выполним, если все его переходы Pq-выполнимы. Пустая P-история заканчивается в состояниях, достижимых из начального состояния по ∅-выполнимым τ-маршрутам, то есть после включения машины до нажатия какой-либо кнопки. Пусть P-история σ заканчивается во множестве состояний S after σ. Рассмотрим различные продолжения этой P-истории. Мы будем предполагать, что P-история не заканчивается разрушением или дивергенцией, поскольку после дивергенции и разрушения нет продолжений. Продолжение кнопкой P, где P∈P. Если допускается переключение кнопок, такое продолжение всегда возможно. Если переключения кнопок нет, Pистория не должна заканчиваться кнопкой. Переключение интерпретируется как отжатие первой кнопки, а потом нажатие второй кнопки. Поэтому сначала реализация может выполнить любой ∅-выполнимый τ-маршрут, начинающийся в состоянии из S after σ, а затем продолжить выполнение по любому Pq-выполнимому τ-маршруту. Множество концов таких маршрутов и будет множеством состояний S after σ⋅〈“P”〉. Заметим, что, если история не заканчивалась на кнопку, то концы всех ∅-выполнимых τ-маршрутов уже входят в S after σ. Продолжение внешним действием z. Такое продолжение возможно только в том случае, когда сама P-история имеет вид σ⋅〈“P”〉, где P∈P и z∈Pq, то есть заканчивается кнопкой P, разрешающей действие z. Наблюдение действия z происходит, когда совершается Pq-выполнимый переход по z из состояния после предшествующей P-истории, то есть переход s⎯z,π→s`, где s∈(S after σ⋅〈“P”〉) и π(Pq)=true. В результате такого перехода кнопка автоматически отжимается, и далее могут выполняться ∅выполнимые τ-маршруты до тех пор, пока не возникнет разрушение, пока не будет нажата кнопка (та же самая или другая), или пока оператор не выключит машину, заканчивая сеанс тестирования. Множество концов этих τ-маршрутов и является множеством S after σ⋅〈“P”,z〉. 90
Продолжение R-отказом R. Такое продолжение возможно только в том случае, когда сама P-история имеет вид σ⋅〈“P”〉, где P∈P и R∈Pr. Отказ R возникает в таком состоянии s∈(S after σ⋅〈“P”〉), в котором выполнено условие: для каждого перехода s⎯z,π→s`, где z∈R∪{τ,γ} должно быть π(Pq)=false. После отказа кнопка отжимается и реализация может выполнить ∅-выполнимый τ-маршрут, начинающийся в одном из состояний, где наблюдался отказ. Множество концов этих τ-маршрутов и является множеством S after σ⋅〈“P”,R〉.Заметим, что состояния, где наблюдался отказ, тоже входят в это множество (для пустого τ-маршрута). Продолжение разрушением γ. Такое продолжение возможно только в том случае, когда в некотором состоянии s∈(S after σ) переход s⎯γ,π→s` Pq-выполнимым, если P-история заканчивается кнопкой P∈P (наблюдения ещё не было, и продолжает действовать кнопка P), или ∅-выполним в противном случае (после наблюдения не действует никакая кнопка). Поскольку после разрушения нет продолжения, нас не интересует множество состояний после такого продолжения. Заметим, что если разрушение может возникнуть после отказа при нажатии кнопки, то оно могло возникнуть и до нажатия этой кнопки: если возможна история σ⋅〈“P”,R,γ〉, где R∈Pr, то возможна также история σ⋅〈γ〉. Продолжение дивергенцией Δ. Поскольку опасна не сама дивергенция, а попытка выхода из неё, нас будет интересовать только такая дивергенция, которая выполнима при нажатой кнопке P. Такая дивергенция возникает после P-истории вида σ⋅〈“P”〉, где P∈P, если есть бесконечный Pqвыполнимый τ-маршрут, начинающийся в состоянии из S after σ⋅〈“P”〉 (очевидно, достаточно считать, что маршрут начинается в состоянии из S after σ). В этом случае символ Δ будет продолжать P-историю после кнопки P. Поскольку после дивергенции нет продолжения, нас не интересует множество состояний после такого продолжения. Теперь аналогично трассам определим полные истории, или F-истории как Pистории для P=Π(L∪Π(L)), когда любой набор внешних действий и множеств внешних действий является P-кнопкой. Множество F-историй LTS S – обозначим так же, как множество F-трасс, – F(S), поскольку в дальнейшем мы будем рассматривать только истории, а не трассы. Теперь Pистория LTS – это такая её F-история, в которой встречаются кнопки только из семейства P, а отказы – это только R-отказы (отказы из Pr). Обозначим через obs(s,P,S) множество действий и отказов, порождаемых состоянием s LTS-модели S, когда нажата кнопка P: obs(s,P,S) =def {z∈Pq|∃π π(Pq) & s⎯z,π→} ∪ {R∈Pr|∀z∈R∪{τ,γ} ⌠π π(Pq) & s⎯z,π→}. 91
Обозначим через obs(σ,P,S) множество действий и отказов, продолжающих историю во множестве F-историй LTS-модели S, то есть порождаемых всеми состояниями после истории σ LTS-модели S, после нажатия кнопки P: obs(σ,P,S) =def {u∈P|σ⋅〈“P”,u〉∈F(S)} = ∪{obs(s,P,S)|s∈(S after σ)}.
3.6. Безопасность и конформность без переключения кнопок Поскольку выполнимость переходов LTS-модели с приоритетами зависит от предикатов на этих переходах, меняются отношения безопасности кнопок в реализации (safe in) и спецификации (safe by). Если нет переключения кнопок, то отношения safe in и safe by определяются почти так же, как для машины без приоритетов, за тем исключением, что вместо R-трасс рассматриваются P-истории, безопасность или опасность кнопки определяется только после P-истории, не заканчивающейся кнопкой, продолжение внешним действием зависит от кнопки, дивергенция возможна лишь после кнопки, разрушения не должно быть не только после действия, но также сразу после нажатия кнопки и после R-отказа. В последнем случае, если после нажатия кнопки наблюдается отказ, а потом (после автоматического отжатия кнопки) возникает разрушение, то это разрушение было выполнимо и до нажатия кнопки. В дальнейшем нас не будет интересовать безопасность кнопок после опасных историй, то есть таких, прохождение которых может вызывать разрушение. Поэтому случай разрушения после отказа можно не рассматривать. Определение отношения безопасности в реализации без переключения кнопок: P safeγin I after σ =def σ⋅〈“P”,γ〉∉F(I) & ∀u∈P σ⋅〈“P”,u,γ〉∉F(I). P safe1in I after σ =def P safeγin I after σ & σ⋅〈“P”,Δ〉∉F(I) & ∀s∈(I after σ⋅〈“P”〉) (stab(s,P,I) ⇒ obs(s,P,I)≠∅). Случай, когда для каждой кнопки разрешаемых действий ∪Pr⊆Pq:
P
все R-отказы состоят только из
P safe1in I after σ =def P safeγin I after σ & σ⋅〈“P”,Δ〉∉F(I) & (Pr=∅ ⇒ σ⋅〈“P”,Pq〉∉F(I)). Требования к отношению безопасности в спецификации без переключения кнопок: ∀P∈P ∀u
92
1) P safe1by S after σ ⇒ P safeγin S after σ & σ⋅〈“P”,Δ〉∉F(S) & ∃s∈(S after σ⋅〈“P”〉) obs(s,P,S)≠∅. 2) P safeγin S after σ & σ⋅〈“P”,Δ〉∉F(S) & ∃s∈(S after σ⋅〈“P”〉) u∈obs(s,P,S) ⇒ ∃R∈P R safe1by S after σ & u∈obs(s,R,S). Аналогично случаю отсутствия приоритетов, правила отношения safe by всегда могут быть определены только в терминах F-историй модели: ∀P∈P ∀u 1) P safe1by S after σ ⇒ P safeγin S after σ & σ⋅〈“P”,Δ〉∉F(S) & ∃u∈P σ⋅〈“P”,u〉∈F(S). 2) P safeγin S after σ & σ⋅〈“P”,Δ〉∉F(S) & u∈obs(σ,P,S) ⇒ ∃R∈P R safe1by S after σ & u∈obs(σ,R,S). На основе отношений безопасности кнопок в реализации и спецификации определяются безопасные наблюдения, безопасные P-истории Safe1In(I) и Safe1By(S), гипотеза о безопасности и безопасная конформность аналогично тому, как это делалось для трасс без приоритетов. I safe for S =def (〈γ〉∉F(S) ⇒ 〈γ〉∉F(I)) & ∀σ∈Safe1By(S)∩Safe1In(I) ∀P∈P (P safe1by S after σ ⇒ P safe1in I after σ). I saco S =def I safe for S & ∀σ∈Safe1By(S)∩Safe1In(I) ∀P safe1by S after σ obs(σ,P,I)⊆obs(σ,P,S).
3.7. Безопасность и конформность с переключением кнопок Если допускается переключение кнопок, мы можем обходить запрет на возникновение при нажатии кнопки тупика, а также выполняемой дивергенции, просто переключаясь на другую кнопку. Соответственно, модифицируются отношения безопасности: удаляются условия, подчёркнутые волнистой линией, и остаются условия, связанные только с разрушением. Определение отношения безопасности в реализации с переключением кнопок: P safe2in I after σ =def P safeγin I after σ. Требования к отношению безопасности в спецификации без переключения кнопок: ∀P∈P ∀u 1) P safe2by S after σ ⇒ P safeγin S after σ. 2) P safeγin S after σ & ∃s∈(S after σ⋅〈“P”〉) u∈obs(s,P,S) ⇒ ∃R∈P R safe2by S after σ & u∈obs(s,R,S). Или, в терминах F-историй модели: ∀P∈P ∀u
93
1) P safe2by S after σ ⇒ P safeγΔin S after σ. 2) P safeγin S after σ & u∈obs(σ,P,S) ⇒ ∃R∈P R safe2by S after σ & u∈obs(σ,R,S). Однако возникает вопрос: сколько раз оператор может переключать кнопки? Мы исходим из следующей парадигмы тестирования: целью тестовых воздействий (нажатия кнопок) является получение наблюдений. Тест не может содержать цепочки переключений кнопок, которая не приводит гарантированно к какому-нибудь наблюдению. Поскольку мы рассматриваем только конечные (по времени выполнения) тесты, цепочка переключений кнопок должна быть конечной и заканчиваться нажатием кнопки, после которой оператору гарантируется получение какого-нибудь наблюдения, что даёт ему возможность, в частности, закончить сеанс тестирования. Это означает, что все кнопки в цепочке, кроме последней, безопасны после непосредственно предшествующего им префикса истории по отношению safe2in/by, а последняя кнопка – по отношению safe1in/by: P & & P & &
safe in I after σ =def ∃P0=P,P1,…,Pn∈P ∀i=0..n-1 Pi safe2in I after σ⋅〈“P0”,“P1”,…,“Pi-1”〉 Pn safe1in I after σ⋅〈“P0”,“P1”,…,“Pn-1”〉, safe by S after σ =def ∃P0=P,P1,…,Pn∈P ∀i=0..n-1 Pi safe2by S after σ⋅〈“P0”,“P1”,…,“Pi-1”〉 Pn safe1by S after σ⋅〈“P0”,“P1”,…,“Pn-1”〉.
Таким образом, отношение безопасности с индексом “2” определяет продолжение истории кнопкой, не вызывающей разрушение, а отношение безопасности с индексом “1” дополнительно запрещает тупик и попытку выхода из выполняемой дивергенции. Понятно, что 1-безопасная кнопка также и 2-безопасна, но обратное, вообще говоря, не верно. Для полной безопасности кнопки после истории требуется, чтобы она была 2-безопасна, и если она не 1-безопасна, то после неё можно было разместить конечную (в том числе пустую) цепочку 2-безопасных кнопок, а затем 1-безопасную кнопку, гарантирующую наблюдение. На основе отношений безопасности кнопок в реализации и спецификации определяются безопасные действия, безопасные истории, гипотеза о безопасности и безопасная конформность аналогично тому, как это делалось для случая без переключения кнопок. Для n=1,2: I safe for S =def (〈γ〉∉F(S) ⇒ 〈γ〉∉F(I)) & ∀σ∈SafeBy(S)∩SafeIn(I) ∀P∈P (P safenby S after σ ⇒ P safenin I after σ); I saco S =def I safe for S & ∀σ∈SafeBy(S)∩SafeIn(I) ∀P safenby S after σ obs(σ,P,I)⊆obs(σ,P,S). 94
Итак, мы определили безопасность и конформность для P-семантики с приоритетами в двух модификациях: с переключением и без переключения кнопок. Очевидно, что P-семантика без приоритетов является частным случаем P-семантики с приоритетами, когда предикаты всех переходов тождественно истинны. При этом не важно, рассматривается семантика с переключением или без переключения кнопок.
пересчитанных переходов-операндов πit&πti. В целом композиционные переходы порождаются следующими правилами вывода:
3.8. Параллельная композиция и генерация тестов
Как и в случае машины без приоритетов, тестирование понимается как композиция LTS-реализации I в алфавите A и LTS-теста T в противоположном алфавите B=A. Мы также будем предполагать, что в тесте нет разрушения. Переходы в тесте не имеют предикатов, точнее, их предикаты константно истинны. Поэтому в композиционной LTS все переходы (а это уже только τ- и γ-переходы) – это пересчитанные предикаты переходов реализации. Поскольку композиционный алфавит пуст, эти предикаты константны (true или false).
Рассмотрим композицию двух LTS с приоритетами I и T в алфавитах A и B соответственно. Возьмём любое композиционное состояние it. При композиции множество разрешённых внешних действий для LTS I в состоянии i – это множество противоположных внешних действий, по которым есть переходы из состояния t другой LTS T, и наоборот. Поэтому, прежде всего, нам нужно пересчитать предикаты переходов из этих состояний. В силу коммутативности оператора композиции (с точностью до изоморфизма, то есть именования состояний it или ti), нам достаточно рассмотреть только пересчёт предикатов одной LTS, для определённости, LTS I. Пересчёт предикатов другой LTS делается аналогично. Для перехода i⎯z,πi→i` мы должны в предикат πi, понимаемый как булевская функция от булевских переменных-действий, подставить константное значение каждой переменной, соответствующей синхронному действию z∈A∩B. Если есть переход t⎯z,πt→t`, то подставляется значение true, иначе – false. Получается новый предикат πit. Заметим, что вычисление нового предиката на переходе из состояния i зависит от состояния t, с которым оно компонуется, то есть для разных состояний t будут, вообще говоря, разные предикаты πit. Новый предикат πit может быть не константным, поскольку в нём могут остаться переменные, соответствующие асинхронным внешним действиям из A\B. Кроме того, теперь этот предикат следует понимать как предикат в композиционном алфавите A≡≠B=(A\B)∪(B\A), хотя реально он не зависит от переменных, соответствующих действиям из B\A. (Предикат πti, наоборот, может зависеть от этих переменных, но не зависит от переменных, соответствующих действиям из A\B.) Асинхронный переход соответствует одному переходу в одном из LTSоперандов. Он может выполняться, если может выполняться наследуемый переход. Следовательно, предикат асинхронного композиционного перехода совпадает с предикатом наследуемого перехода после пересчёта, то есть не с исходным предикатом πi, а с предикатом πit. Синхронный переход – это одновременное выполнение переходов в каждом LTS-операнде. Он может выполняться, если могут выполняться оба перехода-операнда. Следовательно, предикат синхронного композиционного перехода равен конъюнкции 95
(1) (2) (3)
z∈(A∪{τ,γ})\B & i⎯z,πi→i` z∈(B∪{τ,γ})\A & t⎯z,πt→t` z∈A∩B & i⎯z,πi→i` & t⎯z,πt→t`
it⎯z,πit→i`t, it⎯z,πti→it`, it⎯τ,πit&πti→i`t`.
Если нет переключения кнопок, то в тесте нет τ-переходов. Точнее, такой переход может быть только в состоянии, соответствующем отсутствию нажатой кнопки: t⎯τ→ влечёт ∀z≠τ t⎯z⏐. Оператор выжидает, прежде, чем нажать кнопку. Таких τ-переходов из состояния t может быть несколько – оператор выбирает, какую кнопку ему нажать. Заметим, однако, что наличие в тесте τ-переходов только создаёт лишний нетедерминизм и не увеличивает мощности тестирования. Для обнаружения R-отказа R в тесте (но не в реализации!) используется переход по R-отказу аналогично случаю без приоритетов. (4) ∀z∈R∪{τ,γ} ⌠π π(Pq) & i⎯z,π→ & t⎯R→t` & t⎯τ⏐ it⎯τ→it`, где Pq={z∈L|t⎯z→}. Такому состоянию теста t соответствует кнопка P=Pq∪Pr, где Pr множество R-отказов R, для которых в состоянии определены переходы t⎯R→. Если допускается переключение кнопок, то в тесте оно отображается в виде τперехода из состояния, соответствующего одной кнопке, в состояние, соответствующее другой кнопке. В этом случае нужно, чтобы R-переход мог срабатывать независимо от этого τ-перехода. Иными словами, из правила вывода (4) удаляется условие, подчёркнутое волнистой линией. В композиции реализации и теста все предикаты константны, и мы можем удалить все переходы с ложными предикатами. После этого при безопасном тестировании оставшиеся γ-переходы должны быть недостижимы. Тогда, как и для машины без приоритетов, выполнению теста соответствует прохождение τ-маршрута, начинающегося в начальном состоянии композиции и заканчивающегося в терминальном состоянии, которому назначен вердикт pass или fail. 96
Примитивный тест строится точно так же, как для машины без приоритетов. Отличие лишь в том, что без приоритетов мы строили тест по безопасной Rтрассе, превращая её в одну из P-историй, а теперь сразу начинаем с некоторой безопасной P-истории. Кроме того, если в истории есть переключение с кнопки P на кнопку Q, то в тесте проводится τ-переход “P”⎯τ→“Q”. По-прежнему, набор всех примитивных тестов полон, а любой управляемый строгий тест можно заменить на объединение примитивных тестов, которое обнаруживает те же самые ошибки.
3.9. Примеры задания приоритетов Покажем, как задаются приоритеты с помощью предикатов на переходах LTSмодели для примеров, приведённых во введении. Выход из дивергенции. Переход по внешнему действию имеет тождественно истинный предикат, а τ-переход имеет предикат π, истинный только на пустом подмножестве алфавита внешних действий: π(U)=(U=∅). Выход из осцилляции (приоритет приёма над выдачей). Переход по стимулу имеет тождественно истинный предикат, а переход по реакции имеет предикат π, истинный на любом подмножестве действий, не содержащем стимулов: π(U)=(∀?x ?x∉U). Обычно также подразумевается, что внутренняя активность менее приоритетна, чем приём стимула, то есть τ-переход имеет такой же предикат, как переход по реакции. Приоритет выдачи над приёмом в неограниченных очередях. Переход по реакции имеет тождественно истинный предикат, а переход по стимулу имеет предикат π, истинный на любом подмножестве действий, не содержащем реакций: π(U)=(∀!y !y∉U). Обычно также подразумевается, что внутренняя активность менее приоритетна, чем выдача реакции, то есть τпереход имеет такой же предикат, как переход по стимулу. Прерывание цепочки действий. Переход по команде «отменить» (cancel) имеет тождественно истинный предикат, а все остальные переходы имеют предикат π, истинный на любом подмножестве действий, не содержащем “cancel”: π(U)=(cancel∉U). Приоритетная обработка входных воздействий. Множество стимулов разбивается на непересекающиеся подмножества X1,X2,… с линейным приоритетом: стимулы из подмножества с большим индексом имеют больший приоритет. Предикат πi на переходе по стимулу из Xi истинен на любом подмножестве действий, не содержащем стимулов из подмножества с большим номером: πi(U)=(∀j>i U∩Xj=∅). Возможна также дифференциация переходов из некоторого состояния по одному и тому же стимулу в зависимости от наличия или отсутствия менее приоритетных стимулов. Например, один переход по стимулу из Xi выполняется, если окружение предлагает менее приоритетные стимулы 97
πi1(U)=πi(U)&(∃j
3.10. «Торговля» между партнёрами при композиции Композиция LTS с приоритетами основана на пересчёте предикатов переходов, ведущих из состояния i одного операнда, в зависимости от множества действий, разрешаемых соответствующим состоянием t другого операнда. Разрешаемое действие – это такое синхронное действие z, для которого в состоянии t есть переход по противоположному действию z. Пересчитанный предикат может стать тождественно ложным, то есть переход с таким предикатом никогда не будет выполняться. Поэтому, если учитывать только те действия, по которым есть переходы с нетождественно ложными предикатами, пересчёт предикатов меняет множество действий, разрешаемых состоянием i, что, в свою очередь, может повлиять на пересчёт предикатов в состоянии t. А тогда, в свою очередь, меняется множество действий, разрешаемых состоянием t, что требует нового пересчёта предикатов переходов в состоянии i и нового изменения множества действий, разрешаемых состоянием i. И так далее. Этот процесс «торговли» между партнёрами может быть бесконечным или заканчиваться, если они «договорятся» придти к какому-то согласованному решению. Необходимость такой «торговли» и её реализацию с помощью τ-переходов с предикатами мы покажем на нескольких примерах.
98
приоритет приёма над выдачей
Равноприоритетность приёма и выдачи
приоритет выдачи над приёмом
приоритет приёма над выдачей
A1
B1
C1
D1
A1≡≠B1
A1≡≠C1
A1≡≠D1
B2
C2
D2
A2≡≠B2
A2≡≠C2
A2≡≠D2
1
A2
2
(или τ-переход) имеет предикат, истинный на любом подмножестве действий, не содержащем стимулов. Если LTS с такими приоритетами готова выполнить как приём стимула ?x, так и выдачу реакции !y, а её партнёр выполняет только выдачу !x или только приём ?y, то однозначно определяется передача сообщения x из второй LTS в первую или, соответственно, сообщения y из первой LTS во вторую. Также, если партнёр готов как принимать ?y, так и выдавать !x, но эти действия имеют имеют равные приоритеты, или выдача приоритетнее приёма, то в композиции будет гарантированно осуществляться передача сообщения x. Однако если обе LTS применяют одинаковую стратегию – приоритет приёма над выдачей, то возникнет тупик: каждый хочет принимать, но не хочет выдавать. Эта стратегия изображена на рис. 7 в строке 1. Если нас это не устраивает, необходима «торговля»: выяснив, что партнёр и принимает и выдаёт, мы должны только принимать, но не выдавать. Эта стратегии изображена на Рис. 7 в строке 2. Аналогичные две стратегии возможны для симметричного примера приоритета выдачи на приёмом. Теперь рассмотрим случай, когда задан циклический приоритет действий: если данное действие не может быть выполнено, то предпринимается попытка выполнить следующее по приоритету действие и так далее. (Нам будет безразлично, является ли действие передачей стимула, выдачей реакции или ещё каким-то действием.) На Рис. 8 в строке 1 показан пример, когда таких действий три: 0, 1 и 2 с приоритетами 0>1>2>0; начальное действие, с которого начинается торговля, – действие 0. Если партнёр придерживается аналогичной стратегии торговли, но начинает с действия 2, то при композиции возможен бесконечный цикл торговли. Чтобы его избежать, стратегия может быть скорректирована так, чтобы приоритеты перестали быть циклическими (строка 2): после того, как перебраны все действия и все они отклонены партнёром, принимается решение (показано пунктиром) «согласиться» на те действия, которые последний раз предлагал партнёр.
Рис. 7. Две стратегии приоритетности приёма над выдачей (τ-переходы с тождественно ложными предикатами не показаны) Сначала рассмотрим ещё раз пример приоритета приёма над выдачей: переход по стимулу имеет тождественно истинный предикат, а переход по реакции 99
100
приоритеты: 0>1>2>0
приоритеты: 2>0>1>2
A1
B1
приоритеты: 0>1>2
приоритеты: 2>0>1
A2
B2
На каждом шаге у нас есть пара множеств Pi,Qi+1 или Pi+1,Qi. В первом случае следующий пересчёт происходит в системе и следующая пара – это Pi+2,Qi+1; во втором случае следующий пересчёт происходит в окружении и следующая пара – это Pi+1,Qi+2. Торговля заканчивается, когда множество при пересчёте не меняется: при переходе от пары Pi,Qi+1 к паре Pi+2,Qi+1, если Pi+2=Pi, или при переходе от пары Pi+1,Qi к паре Pi+1,Qi+2, если Qi+2=Qi.
A1≡≠B1
1
A2≡≠B1 = A2≡≠B2
2
В состоянии “i” определён переход по действию i с предикатом true.
В состоянии “i” определён переход по действию i с предикатом true.
В состоянии “ii” определён синхронный τ-переход (по действиям i и i) с предикатом true.
Рис. 8. Две стратегии циклического приоритета (τ-переходы с тождественно ложными предикатами не показаны). В общем случае торговля выглядит следующим образом. Пусть в состоянии системы s определены переходы s⎯z,π→sz,π по действиям z∈P0 с нетождественно ложными предикатами π. Система «узнаёт», что в её полном окружении (которое имеет противоположный алфавит и с которым она образует замкнутую систему с пустым алфавитом) определены переходы с нетождественно ложными предикатами по множеству действий Q0. После пересчёта предикатов в системе получается множество действий P1, а в окружении – Q1. Пересчёт предикатов происходит недетерминированно в системе или в её окружении. Если сначала пересчитываются предикаты системы, то мы получаем пару множеств P1,Q0; в противном случае – пару P0,Q1. Далее в первом случае происходит пересчёт предикатов в окружении, и мы получаем пару P1,Q2, где Q2 пересчитано на основе P1, а во втором случае – в системе, и мы получаем пару P2,Q1, где P2 пересчитано на основе Q1. И так далее (см. рис. 9 слева). 101
Для i>0 : Pi={z∈L|∃π s⎯z,π→ & π(Qi-1)} Рис. 9. Общая стратегия торговли Моделирование такой торговли с помощью τ-переходов с предикатами может быть выполнено следующим образом (см. рис. 9 справа). Мы будем использовать переходы по множеству разрешаемых действий: ⎯z,Qi→ означает переход ⎯z,π→ по предикату π, который истинен только на множестве Qi: π(Q) = (Q=Qi). Состояние s преобразуется во множество состояний si. Сначала имеем состояние s0, в котором определены переходы s0⎯z,false→sz,π по внешним действиям z∈P0 с тождественно ложными предикатами, а также τ-переход s0⎯τ,Q0→s1 по множеству разрешаемых действий Q0. Такой τ-переход, естественно, необходимо иметь для каждого Q0. В конце τ-перехода находится состояние s1, соответствующее множеству действий P1, которое получается после пересчёта предикатов по множеству Q0. В этом состоянии определяются переходы s1⎯z,Q0→sz,π по действиям из z∈P1 по множеству Q0. Также в состоянии 1 определяется τ-переход s1⎯τ,Q1→s2 по множеству разрешаемых действий Q1≠Q0 (для каждого такого Q1). В конце этого второго τ-перехода находится состояние s2, соответствующее множеству действий P2, которое получается после пересчёта предикатов по множеству Q1. В этом состоянии определяются переходы s2⎯z,Q1→sz,π по действиям z∈P2 по множеству Q1. Также в состоянии 2 определяется τ-переход s2⎯τ,Q1→s3 по множеству разрешаемых действий Q2≠Q1 (для каждого такого Q2). И так далее. 102
Естественно, для конкретной стратегии торговли какие-то из этих состояний могут отождествляться. Если возникает бесконечная цепочка τ-переходов (в частности, цикл), то такая дивергенция соответствует «зацикливанию» при торговле.
4. Заключение Можно рассматривать семантики, в которых при включении машины, после наблюдения и при переключении кнопок может не допускаться выполнение реализацией τ- и γ-переходов, даже если они ∅-выполнимы. Можно считать, что сразу после включения машины и сразу после наблюдения машина стоит, и может выполнять какие-то действия только после нажатия кнопки. Также переключение кнопок не интерпретируется как отжатие первой кнопки (с разрешением ∅-выполнимых τ- и γ-действий), а потом нажатие второй кнопки. Иными словами, после включения машины, после наблюдения и между двумя кнопками при переключении кнопок нет никакого «пустого» промежутка. Такая семантика, очевидно, предполагает более сильные тестовые возможности, чем слабая семантика, рассматриваемая в данной статье. Эти семантики имеют разные требования по безопасности и конформности. Любое поведение, которое можно наблюдать при сильной семантике можно наблюдать и при слабой семантике: достаточно подобрать подходящие погодные условия, когда оператор успевает нажать или переключить кнопку достаточно быстро. Верно и обратное: поведение при слабой семантике наблюдается при сильной семантике, если добавить пустую кнопку и явно нажимать её. Однако условия безопасности для этих семантик разные. При слабой семантике мы всегда должны рассчитывать на возможность выполнения τ- и γ-действий (при наличии приоритетов, они должны быть ∅выполнимы) после наблюдения по кнопке P, а такие действия могут давать дивергенцию или разрушение; тем самым, кнопка P будет опасной. При сильной семантике мы можем просто не нажимать в этой ситуации пустую кнопку после такого наблюдения, поскольку она опасна, а кнопка P будет безопасной. Отсюда же вытекают и соответствующие различия в конформности: реализация может быть опасной при слабой семантике и, следовательно, не конформной, но безопасной и конформной при сильной семантике. При тех же условиях безопасности (например, когда в спецификации нет дивергенции, разрушения и ненаблюдаемых отказов) и при наличии приоритетов сильная семантика предъявляет более жёсткие условия конформности. Это объясняется тем, что мы получаем возможность различать реализации, в которых некое действие b, разрешаемой кнопкой B, выполняется сразу после действия a или через промежуточную ∅выполнимую, но не B-выполнимую τ-активность. 103
Кроме генерации тестов, важнейшей проблемой теории конформности является проблема монотонности – сохранения конформности при композиции. В общем случае композиция реализаций, конформных своим спецификациям, может быть не конформна композиции этих спецификаций. Частным, но важным, случаем этой проблемы является проблема асинхронного тестирования, когда имеется два компонента: реализация и известная среда передачи. Здесь также композиция конформной реализации со средой может быть не конформна композиции спецификации с этой средой. Для R/Q-семантики без приоритетов эта проблема решается с помощью, так называемого, монотонного преобразования спецификаций: композиция конформных реализаций оказывается конформной композиции преобразованных спецификаций. Или, для асинхронного тестирования: композиция конформной реализации со средой конформна композиции преобразованной спецификации с этой средой. Монотонное преобразование выполняется для R/Q-семантик, в которых все отказы наблюдаемы, то есть Q=∅. В общем случае R/Q-семантики сначала выполняется, так называемое, пополнение спецификации. Пополненная спецификация эквивалентна (имеет тот же класс безопасных и тот же класс конформных реализаций) исходной спецификации в R/Q-семантике, а кроме того, эквивалентна сама себе в R∪Q/∅-семантике. Пополнение решает также проблему рефлексивности («самоприменимости») спецификации, которая в R/Q-семантике может быть не конформна сама себе. Тем самым, совокупность преобразования пополнения и монотонного преобразования решает общую проблему монотонности и рефлексивности для любой R/Q-семантики [4,6,7]. Для R/Q-семантик с приоритетами проблемы монотонности и рефлексивности ещё не решены. Также эти проблемы не решены в общем случае P-семантики: как с приоритетами, так и без них.. Литература [1] Бурдонов И.Б., Косачев А.С. Тестирование компонентов распределенной системы. Труды Всероссийской научной конференции «Научный сервис в сети ИНТЕРНЕТ», Изд-во МГУ, 2005 [2] Бурдонов И.Б., Косачев А.С. Верификация композиции распределенной системы. Труды Всероссийской научной конференции «Научный сервис в сети ИНТЕРНЕТ», Изд-во МГУ, 2005 [3] Bourdonov I., Kossatchev A., Kuliamin V. Formal Conformance Testing of Systems with Refused Inputs and Forbidden Actions. Proc. Of MBT 2006, Vienna, Austria, March 2006 [4] Бурдонов И.Б., Косачев А.С., Кулямин В.В. Формализация тестового эксперимента. «Программирование», 2007, No. 5 [5] Бурдонов И.Б., Косачев А.С., Кулямин В.В. Безопасность, верификация и теория конформности. Материалы Второй международной научной конференции по проблемам безопасности и противодействия терроризму, Москва, МНЦМО, 2007 [6] Бурдонов И.Б., Косачев А.С., Кулямин В.В. Теория соответствия для систем с блокировками и разрушением. «Наука», 2008
104
[7] Бурдонов И.Б. Теория конформности для функционального тестирования программных систем на основе формальных моделей. Диссертация на соискание учёной степени д.ф.-м.н., Москва, 2007 http://www.ispras.ru/~RedVerst/RedVerst/Publications/TR-01-2007.pdf [8] Бурдонов И.Б., Косачев А.С. Системы с приоритетами: конформность, тестирование, композиция. Труды ИСП РАН, т. 14, 2008 [9] van Glabbeek R.J. The linear time – branching time spectrum. In J.C.M. Baeten and J.W. Klop, editors, CONCUR’90, Lecture Notes in Computer Science 458, SpringerVerlag, 1990, pp 278–297 [10] van Glabbeek R.J. The linear time - branching time spectrum II; the semantics of sequential processes with silent moves. Proceedings CONCUR ’93, Hildesheim, Germany, August 1993 (E. Best, ed.), LNCS 715, Springer-Verlag, 1993, pp. 66-81 [11] Heerink L., Tretmans J. Refusal Testing for Classes of Transition Systems with inputs and Outputs. In T.Mizuno, N.Shiratori, T.Higashino, A.Togashi, eds. Formal Description Techniques and Protocol Specification, Testing and Verification. Chapman & Hill, 1997 [12] Heerink L. Ins and Outs in Refusal Testing. PhD thesis, University of Twente, Enschede, The Netherlands, 1998 [13] Lestiennes G., Gaudel M.-C. Test de systemes reactifs non receptifs. Journal Europeen des Systemes Automatises, Modelisation des Systemes Reactifs, pp. 255–270. Hermes, 2005 [14] Milner R. A Calculus of Communicating Processes. LNCS, vol. 92, Springer-Verlag, 1980. [15] Milner R. Modal characterization of observable machine behaviour. In G. Astesiano & C. Bohm, editors: Proceedings CAAP 81, LNCS 112, Springer, pp. 25-34 [16] Milner R. Communication and Concurrency. Prentice-Hall, 1989
105
Аннотация. Рассматривается задача генерации тестовых данных для тестирования арифметической подсистемы центральных процессоров. Для ее решения предлагается использовать метод, позволяющий строить тестовые данные систематически на основе формального описания поведения отдельных команд микропроцессора. Предложенный метод апробирован на командах арифметической подсистемы микропроцессоров MIPS64.
моделях. Одним из часто используемых подходов является встраивание в сам дизайн процессора дополнительной схемы проверки его работы (BIST) [2]. Генераторы тестовых программ разрабатываются с учетом этой дополнительной схемы. При своем выполнении на микропроцессоре с внедренной BIST такие тестовые программы генерируют, помимо основных результатов, протокол своей работы, на основе которого делается заключение об успешности тестирования. Однако не во всех случаях можно дополнить дизайн процессора дополнительной схемой. В таких случаях следует пользоваться другими методами тестирования — к таким методам относится и предлагаемый метод генерации тестовых программ. Проводимое тестирование микропроцессора, как и любое функциональное тестирование, должно проверять соответствие требованиям, предъявляемым к операциям микропроцессора, и быть «достаточно представительным» (обеспечивать высокий уровень «покрытия») [3]. Для некоторых микропроцессоров функциональные требования операций сформулированы в открытом стандарте архитектуры [4]. Например, так выглядит описание требований к арифметической операции ADD в стандарте архитектуры MIPS64:
1. Постановка задачи
Purpose: To add 32-bit integers. If an overflow occurs, then trap.
Генерация тестовых данных для тестирования арифметических операций центральных процессоров Е.В. Корныхин [email protected]
Одним из способов тестирования микропроцессоров является проверка корректности их работы на некотором наборе программ (такие программы называют тестовыми программами). Программы компилируются, загружаются в память и выполняются микропроцессором. Результаты этого выполнения протоколируются и используются для анализа правильности работы микропроцессора. Задача построения тестовой программы может быть разбита на две подзадачи. Первая подзадача — сгенерировать последовательность вызовов операций процессора, в которой на месте операндов отсутствуют конкретные данные, но присутствует информация о том, как должна исполняться каждая команда, например, должно ли происходить переполнение при выполнении команды сложения. Такие последовательности называют тестовыми шаблонами (test template). Вторая подзадача — дополнить тестовые шаблоны конкретными числами, чтобы в результате получить готовую тестовую программу. Настоящая работа посвящена второй подзадаче. Алгоритм решения первой подзадачи можно найти, например, в работе [1]. Предлагаемая в данной работе методика построения тестовых данных для арифметической подсистемы процессоров не работает напрямую с микропроцессором — она использует описания поведения отдельных операций процессора. Поэтому ее можно отнести к классу модельноориентированных методик (model-based). Многие исследователи для тестирования микропроцессоров пользуются методами, не основанными на 107
Description: rd ← rs + rt The 32-bit word value in GPR rt is added to the 32-bit value in GPR rs to produce a 32-bit result. • If the addition results in 32-bit 2’s complement arithmetic overflow, the destination register is not modified and an Integer Overflow exception occurs. • If the addition does not overflow, the 32-bit result is signed-extended and placed into GPR rd. Restrictions: If either GPR rt or GPR rs does not contain signed-extended 32-bit values (bits 63..31 equal), then the result of the operation is UNPREDICTABLE. Такие описания обычно формулируют все функциональные требования к операции, не вдаваясь в детали реализации. Предлагаемый метод построения тестов нацелен на достижение полного покрытия всех возможных комбинаций ситуаций, описанных в функциональных требованиях, – ветвей функциональности – в рамках небольших, содержащих две-четыре команды программ. В приведенном выше описании выделены две ветви функциональности – сложение с переполнением («If the addition results in ... overflow…») и нормальное сложение («If the addition does not overflow …»). Будем называть тестовым вариантом набор значений аргументов одной команды. Ветви 108
функциональности разбивают пространство тестовых вариантов на классы эквивалентности: в один класс эквивалентности попадают тестовые варианты, при выполнении которых исполняется одна ветвь функциональности. Классы эквивалентности такого разбиения пространства тестовых вариантов будем называть тестовыми ситуациями. Кроме ветвей функциональности, классы эквивалентности в пространстве тестовых вариантов могут быть выделены на основании дополнительных эвристик. Источниками дополнительных эвристик могут служить общие знания об ошибках (например, ошибки часто происходят на значениях, близких к границе области допустимых значений параметра команды) или дополнительные знания разработчиковсхемотехников (узкие места реализации). В этих случаях могут быть выделены дополнительные классы эквивалентности в пространстве тестовых вариантов. Итак, каждой команде процессора соответствует свой набор тестовых ситуаций. В тестовой программе может использоваться несколько команд, аргументы которых могут зависеть друг от друга. В результате могут возникать различные комбинации тестовых ситуаций, а количество тестовых программ, необходимых для покрытия всех возможных комбинаций, возрастает очень быстро. В таком случае вычисление тестовых данных становится крайне трудоемкой задачей, требующей автоматизации.
2. Существующие методы Если рассматривать тестовую ситуацию в отдельности, то можно увидеть, что она представима набором условий, которые должны быть выполнены одновременно. Это наводит на мысль использовать для построения подходящих тестовых данных какую-нибудь технику решения систем условий. К таким техникам можно отнести CSP (constraint satisfaction problem) [15], SAT (SATisfiabitily problem) [5] и линейное программирование [13]. SAT – задача проверки выполнимости: для данной формулы логики предикатов требуется построить значения свободных переменных, при которых формула примет истинное значение. Существующие SATинструменты базируются либо на вероятностных алгоритмах, либо на алгоритме DPLL (Davis-Putnam-Logemann-Loveland algorithm), основанном на переборе с возвратом по глубине формулы конъюнктивного вида. Эффективность работы таких инструментов определяется размером формулы, истинность которой они выясняют, включающим как количество переменных в ней, так и размеры областей значений отдельных переменных. В арифметической подсистеме современных процессоров приходится иметь дело с 64-битными переменными, представляющими натуральные числа. Размер формулы, зависящей лишь от одной такой переменной, равен 264, а на практике переменных может быть много. SAT-инструментов, способных эффективно справляться с формулами такого размера, сейчас не существует. 109
Поэтому применение SAT-инструментов к этой задаче достаточно ограничено. Видимо, этим объясняется более распространенное применение SAT-инструментов к задаче ATPG [8,10] — генерации входных сигналов схемы. Кроме того, отмеченные особенности SAT-инструментов не исключают возможности использовать их в качестве составных частей других инструментов. В таком случае SAT-инструменту будет передана лишь подзадача, которая будет уже лежать в области применимости этого инструмента. К примерам такого использования SAT-инструментов можно отнести SAT-based (Bounded) Model Checking [9], SAT-based Planning [12], SAT-based Formal Verification [11]. Задача линейного программирования известна очень давно [13]. Она имеет огромное количество полезных приложений (задача о максимальном паросочетании, транспортная задача и многие другие). Для описания арифметических команд процессоров применяются нелинейные операции, которые не дают использовать линейное программирование. Однако показано [14], что некоторые битовые операции над натуральными числами выражаются в виде задач целочисленного линейного программирования. Это дает надежду на использование целочисленного линейного программирования для решения частных задач в рамках более общих инструментов, хотя такая возможность пока не реализована. Инструменты на основе CSP представляют серьезную альтернативу остальным технологиям. Constraint – «ограничение» – булевское выражение над произвольными переменными с ограниченными областями значений. Задача остается всё той же – найти значения переменных, удовлетворяющих всем заданным ограничениям. CSP отличается от других подходов алгоритмами нахождения нужных значений переменных. В основе большинства CSP-инструментов лежит семейство алгоритмов MAC (Maintaining Arc Consistency) [15]. В них семейство ограничений представляется гиперграфом. Вершины этого гиперграфа – переменные, гипердуги – ограничения (гипердуга – подмножество вершин, размер которого не обязательно равен двум). Алгоритм пытается постепенным сужением области значений переменных достичь выполнения всех ограничений, сначала выбирается одна гипердуга, сужаются области значений ее вершинпеременных, это затрагивает другие гипердуги – к ним алгоритм применяется рекурсивно. CSP-подход используется в хорошо известном инструменте генерации тестовых данных для тестовых шаблонов Genesys-Pro от IBM [16]. Это инструмент наиболее хорошо подходит для решения задаче, поставленной в данной статье. Однако закрытость инструмента Genesys-Pro не дает возможности провести более глубокий анализ используемых в нем технологий. Известно, что разработчики IBM решили написать свою версию алгоритма семейства MAC, адаптированную под определенные задачи. Кроме того, Genesys-Pro работает с моделью, в которой гиперграф ограничений уже описан. Заметим, что создание модели не входит в задачи Genesys-Pro и должно быть сделано вручную. Кроме Genesys-Pro, существуют и другие 110
работы по применению CSP к рассматриваемой в статье задаче. Например, для работы инструмента MA2TG [17] необходима модель архитектуры микропроцессора на некотором языке с добавленными в модель ограничениями (assert), что также приходится делать вручную, а сами авторы не дают каких-либо систематических способов выделения ограничений. В подходе, предлагаемом в данной статье, вопрос выделения ограничений описан более четко.
3. Предлагаемый метод решения Предлагается использовать специальную нотацию для записи отдельных тестовых ситуаций команд, а их комбинирование и вычисление тестовых данных будет производиться автоматически. В методе используется наличие описания поведения команд процессора в открытом стандарте архитектуры. Такие стандарты архитектуры доступны для MIPS64 [4], PowerPC [18], UltraSPARC [19] и многих других архитектур микропроцессоров. Построение тестовых данных по предлагаемому методу для данной команды выполняется следующим образом: 1. В открытом стандарте архитектуры найти формальное или полуформальное описание поведения данной команды. 2. Выделить тестовые ситуации, возникающие при выполнении данной команды; эти тестовые ситуации обычно соответствуют ветвям функциональности команды (они извлекаются из найденного описания поведения команды). Иногда на основе дополнительных эвристик (часто встречающихся ошибок, знаний разработчиков схемы) определяются дополнительные ситуации. 3. Описать выделенные ситуации на предлагаемом языке (TeSLa, см. ниже). Если такая ситуация соответствует ветви функциональности, ее описание задается условиями некоторой ветви потока управления псевдокода. Если в тестовую ситуацию включены дополнительные к ветви функциональности условия, их тоже следует описать на предлагаемом языке. Предлагаемый язык является уточнением псевдокода, на котором обычно описывается поведение команд процессора. В результате этого шага получится файл – описание тестовой ситуации. 4. Запустить специальный генератор, передав ему в качестве параметра описание тестовой ситуации, полученное на шаге 3. Генератор имеет API, позволяющий встроить вызов в другие программы тестирования процессора. В результате работы генератора будут получены тестовые данные для данной команды, отвечающие тестовой ситуации, переданной генератору.
3.1. Язык описания тестовых ситуаций TeSLa Язык описания тестовых ситуаций TeSLa [22] (Test Situation Language) представляет собой простой императивный язык с единственным типом 111
данных – целыми числами, состоящими из заданного числа бит (никаких явных ограничений на число бит не предполагается), операторами присваивания и утверждения (см. ниже). Язык включает все операции псевдокода, которые обычно используются для описания поведения команд процессора: • получение бита числа с заданным номером (например, «х[7]» – 7-й бит числа х; биты нумеруются от младших к старшим; младший бит имеет номер 0; биты располагаются в числе по убыванию номеров); • получение диапазона бит с заданными номерами границ этого диапазона (например, «х[8. .5]» – диапазон бит числа х с 8-го по 5-й, включая оба граничных бита); • конкатенация чисел (например, «x.y» – число, двоичная запись которого состоит из двоичной записи числа х, за которой следует двоичная запись числа у); • битовая степень числа – конкатенация числа с самим собой нужное количество раз (например, «х^5» – 5 раз повторенная запись числа х); • привычные арифметические операции (сложение, вычитание, умножение); • операции сравнения чисел на «больше-меньше»; • операции знакового увеличения размера числа (например, «(64)x» – это 64-х битное число, равное и по модулю и по знаку числу х); • логические операции AND и OR; • оператор присваивания (например, «х := 5;»); • оператор утверждения (например, «ASSERT x = 5;» – утверждение, что при исполнении данного оператора значение переменной х должно равняться 5). Описание ситуации на таком языке состоит из последовательности операторов присваивания и утверждения. Заканчиваться последовательность должна «оператором ситуации», который семантически идентичен оператору утверждения с меткой-идентификатором предиката этого оператора. После выполнения последовательности операторов из описания тестовой ситуации процессор должен находиться в той тестовой ситуации, описание которой и составлялось. Поскольку язык является пока только прототипом, он не включает условный оператор и операторы цикла. Однако для описания всех тестовых ситуаций арифметических команд процессора MIPS64, приводящих к исключениям (exception), условный оператор и оператор цикла не требуются, и средств даже прототипа языка хватает. Язык не включает логическую операцию NOT. Это связано с техникой работы генератора. Однако опять же, например, для описания всех тестовых ситуаций арифметических команд процессора MIPS64, приводящих к исключениям, операция NOT не требуется. В некоторых случаях приходится использовать вспомогательных версии предикатов, используемых в псевдокоде, операторов сравнения, логических 112
операторов, в которые уже внесён оператор NOT (например, вместо NOT(NotWordValue (х)) использовать WordValue(x)).
3.2. Генератор тестовых данных Генератор [22] на входе получает файл с описанием тестовой ситуации, транслирует его в промежуточное представление и исполняет промежуточное представление, формируя значения аргументов данной команды. Генератор основан на CLP (Constraint Logic Programming) – логическом программировании с ограничениями [21]. Такой генератор позволяет, используя механизмы логического программирования (унификацию и перебор с возвратом), собирать по ходу выполнения логической программы наборы ограничений и тут же проверять их на совместность (здесь работает как раз CSP-часть логического интерпретатора). Несовместность набора ограничений на текущем шаге – достаточный повод сделать возврат. Выполнение логической программы может соответствовать тексту описания тестовой ситуации (поскольку описание тестовой ситуации есть последовательность операторов). А ограничения, добавляемые на очередном шаге выполнения логической программы, будут следовать из текущего оператора в описании тестовой ситуации. В качестве логического интерпретатора в генераторе используется инструмент с открытым кодом ECLiPSe [20]. Этот логический интерпретатор является достаточно мощным и обладает удобными средствами для трансляции в него описаний тестовых ситуаций. Отсутствие в языке описания тестовых ситуаций операторов цикла гарантирует завершение работы инструмента на любом описании тестовой ситуации.
На Рис. 1 представлена структура всей системы генерации тестов. Подсистема, о которой идет речь в данной статье, называется test data generator. Она получает на вход тестовый шаблон от test generator и возвращает ему тестовые данные (test data), соответствующие этому тестовому шаблону. Для своей работы test data generator использует модели тестовых ситуаций – описания их на языке TeSLa, которые составляются из открытого стандарта. Цифрами помечены шаги ручной работы: 1 – выделение тестовых ситуаций, 2 – составление описаний тестовых ситуаций на языке TeSLa.
3.3. Пример В качестве примера применения метода рассмотрим построение тестовых данных для операции ADD. В стандарте архитектуры MIPS64 описание этой операции располагается на странице 35. Описание состоит из нескольких частей: • Format фиксирует порядок операндов в команде (сначала возвращаемое значение, а потом аргументы); • Purpose одним предложением описывает семантику команды; • Description содержит более полное описание функциональности команды (из этого поля получаются ветви функциональности команды ADD); • Restrictions содержит предусловие выполнения команды; • Operation состоит из псевдокода, описывающего поведение команды ADD; • Exceptions фиксирует те исключение, которые может порождать команда ADD; • Programming Notes содержит дополнительный текстовый комментарий, не относящийся напрямую к остальным частям описания. Приведенный формат описания арифметической команды является стандартным для описания архитектуры MIPS64. Выделяем ветви функциональности, читая Description: Description: rd ← rs + rt The 32-bit word value in GPR rt is added to the 32-bit value in GPR rs to produce a 32-bit result. • If the addition results in 32-bit 2’s complement arithmetic overflow, the destination register is not modified and an Integer Overflow exception occurs. • If the addition does not overflow, the 32-bit result is signed-extended and placed into GPR rd.
Рис. 1. Структура системы генерации тестов
113
Поскольку описание функциональности разбивается на два случая ( «If the addition results … overflow» и «If the addition does not overflow…» ), команда ADD имеет две ветви функциональности – каждая соответствует своему случаю. Первая ветвь соответствует возникновению переполнения, вторая ветвь – нормальному исполнению команды. Выделим тестовые ситуации на 114
основе найденных ветвей функциональности. Первой тестовой ситуацией будет «возникновение переполнения», второй – «нормальное исполнение команды». Дополнительных тестовых ситуаций выделять не будем. Далее каждую тестовую ситуацию опишем на TeSLa. Для этого задействуем Operation:
По сравнению с Operation, добавилась информация о размерах переменных. Она получена из чтения Description. Осталось запустить генератор, который построит значения для переменных, перечисленных в начале описания тестовой ситуации с ключевым словом VAR. Например, для первой тестовой ситуации могут быть сгенерированы такие значения: rs = 2147483650, rt = 2147493620, а для второй – rs = 147483650, rt = 47493620. Генератору предоставляется самостоятельный выбор конкретных значений в тех случаях, когда подходящий тестовый вариант не единственный. Это дает возможность при разных запусках генератора строить разные значения и получать на выходе разные тестовые программы, что может способствовать более качественному тестированию процессора.
4. Заключение Вначале идет предусловие: если верно, что «NotWordValue( GPR[rs] ) or NoWordValue( GPR[rt] )», то поведение операции непредсказуемо (UNPREDICTABLE). Во всех остальных случаях сначала идет присваивание в переменную temp (обратите внимание, ее размерность – 33 бита), а затем ветвление, как раз и обозначающее две ветви функциональности (две тестовые ситуации). Чтобы произошла тестовая ситуация «возникновение переполнения», достаточно, чтобы temp32 ≠ temp31. Чтобы произошла тестовая ситуация «нормальное исполнение команды», наоборот, достаточно, чтобы temp32 = temp31. Тогда на TeSLa код первой тестовой ситуации будут выглядеть следующим образом:
VAR rs : 32; VAR rt : 32; ASSERT WordValue(rs) AND WordValue(rt); temp := rs[31].rs[31..0] + rt[31].rt[31..0]; SITUATION temp[32] # temp[31] IS IntegerOverflow . а второй тестовой ситуации – следующим образом:
В данной работе представлен метод генерации тестовых данных для тестирования арифметической подсистемы центральных процессоров, нацеленный на достижение различных комбинаций тестовых ситуаций в последовательностях обращений к арифметическим командам. Метод позволяет строить тестовые данные систематически на основе формального описания поведения отдельных команд микропроцессора. Предложенный метод апробирован на командах арифметической подсистемы микропроцессоров MIPS64, сейчас он начинает использоваться в промышленном проекте по тестированию одного из микропроцессоров этого семейства. В данный момент созданы прототипы языка описания тестовых ситуаций и генератора данных по этим описаниям, но значимые практические результаты пока не получены. На следующем этапе язык описания тестовых ситуаций будет расширен, чтобы в нем можно было определить все тестовые ситуации арифметических операций процессора MIPS64 (это требуется для практического применения метода). В дальнейшем предполагается доработать предложенный метод генерации тестовых данных, чтобы он стал применим для команд микропроцессора, не имеющих такого подробного формального или полуформального описания (к таким командам относятся, например, операции с памятью). Литература
VAR rs : 32; VAR rt : 32; ASSERT WordValue(rs) AND WordValue(rt); temp := rs[31].rs[31..0] + rt[31].rt[31..0]; SITUATION temp[32] = temp[31] IS NormalAddition .
[1] А. С. Камкин. Генерация тестовых программ для микропроцессоров. Труды ИСП РАН т. 14, ч. 2, стр. 23-64. [2] http://en.wikipedia.org/wiki/Built-in_self-test. [3] В. В. Кулямин, А. К. Петренко. Тестирование на основе моделей: теория, инструменты, применения — Ломоносовские чтения 2004. [4] MIPS64® Architecture for Programmers Volume II: The MIPS64® Instruction Set (http://www.cs.ucr.edu/~junyang/teach/F04_203A/MIPS64manual.pdf )
115
116
[5] J. Gu, P. W. Purdom, J. Franco, B. W. Wah. Algorithms for the Satisfiability (SAT) Problem: A Survey. DIMACS series in Discrete Mathematics and Theoretical Computer Science, vol. 35, pp. 19-152, American Mathematical Society, Providence, RI, 1997. [6] http://en.wikipedia.org/wiki/DPLL_algorithm. [7] http://en.wikipedia.org/wiki/Automatic_test_pattern_generation. [8] Z. Zeng, K. R. Talupuru, M. Ciesielski. Functional test generation based on word-level SAT. Journal of Systems Architecture: the EUROMICRO Journal, 51(8):488-511, 2005. [9] Heon-Mo Koo, P. Mishra. Test generation using SAT-based bounded model checking for validation of pipelined processors. Great Lakes Symposium on VLSI, Proceedings of the 16-th ACM Great Lakes symposium on VLSI, pp. 362-365, Philadelphia, PA, USA. 2006. [10] G. Fey, S. Junhao, R. Drechsler. Efficiency of Multi-Valued Encoding in SAT-based ATPG. ISMVL, pp. 25, 2006. [11] M. Ganai, A. Gupta. SAT-Based Scalable Formal Verification Solutions. Springer. 2007. [12] Ji-Ae Shin, E. Davis. Processes and continuous change in a SAT-based planner. Artificial Intelligence, 166(1-2):194-253, 2005. [13] http://ru.wikipedia.org/wiki/Линейное_программирование. [14] R. Brinkmann, R. Drechsler. RTL-datapath verification using integer linear programming. IEEE VLSI Design’01 & Asia and South Pacific Design Automation Conference, Bangalore, pp. 741-746, 2002. [15] А. Л. Семенов. Методы распространения ограничений: основные концепции. Труды Совещания по интервальной математике и методам распространения ограничений ИМРО'03, Новосибирск, 2003. [16] E. Bin, R. Emek, G. Shurek, A. Ziv. Using a constraint satisfaction formulation and solution techniques for random test program generation. IBM Systems Journal Artificial Intelligence. Volume 41, no. 3. 2002. [17] Li Tun Li, Zhu Dan, Guo Yang, Liu GongJie, Li SiKun. MA2TG: a functional test program generator for microprocessor verification. Proceedings of 8-th Euromicro Conference on Digital System Design, pp. 176-183, 2005. [18] Enhanced PowerPC Architecture http://www01.ibm.com/chips/techlib/techlib.nsf/techdocs/852569B20050FF778525699600682CC7. [19] UltraSPARC Architecture 2005 Specification (Hyperprivileged Edition) http://opensparc-t1.sunsource.net/specs/UA2005-current-draft-HP-EXT.pdf. [20] K. Apt, M. Wallace. Constraint Logic Programming using Eclipse. Cambridge University Press, 2007. [21] K. Marriott, Peter J. Stuckey. Programming with Constraints. MIT Press, 1998. [22] TeSLa-project http://tesla-project.googlecode.com.
117
О некоторых задачах обратной инженерии К.Н. Долгова, А.В. Чернов {katerina, cher}@ispras.ru Аннотация. В статье даётся краткое введение в проблематику задачи декомпиляции программ как одной из задач обратной инженерии. Рассматриваются возможности и недостатки существующих инструментальных средств декомпиляции программ.
1. Введение В настоящее время в комплексном программном обеспечении широко применяются программные приложения, разработанные сторонними производителями. В ряде случаев такие приложения предоставляются без исходного кода на языке высокого уровня, необходимого для их аудита с точки зрения информационной безопасности их использования. Несмотря на это, такие приложения обязательно должны быть исследованы для оценки рисков их использования. Ни бинарный код, ни ассемблерный листинг, полученный в результате дизассемблирования, не позволяют с приемлемыми трудозатратами оценить взаимосвязь элементов программы, а также идентифицировать в программе стандартные алгоритмические конструкции. Восстановление программы на языке высокого уровня дает возможность преодолеть указанные выше трудности. Программные приложения, представленные в виде исполняемых файлов или на языке ассемблера, сложны для анализа их специалистами в области информационной безопасности, криптографии и т.д. и должны быть предоставлены им для анализа на более высоком уровне представления. В качестве одного из инструментальных средств повышения уровня абстракции представления программы может использоваться декомпилятор. Под декомпилятором мы будем понимать инструментальное средство, получающее на вход программу на языке ассемблера и выдающее на выход эквивалентную ей программу на некотором языке высокого уровня. Задача декомпиляции была поставлена в 60-е годы XX века сразу же, когда стали широко применяться компиляторы с языков высокого уровня, но не утратила своей актуальности и по сей день [2]. Эта задача не решена в полной мере из-за наличия ряда трудностей принципиального характера. В частности, 119
при компиляции программы из языка высокого уровня в язык ассемблера характерно отображение «многие к одному» концепций языка высокого уровня в концепции языка ассемблера, и, как следствие, однозначное восстановление программы на языке высокого уровня становится зачастую невозможным. В силу указанных выше причин полностью автоматический декомпилятор реализовать принципиально невозможно. Поэтому системы декомпиляции программ должны работать во взаимодействии с аналитиком, который (зачастую методом проб и ошибок) управляет процессом декомпиляции. В ходе декомпиляции программы решаются следующие задачи: выделение структурных единиц программы, в частности, подпрограмм в однородном ассемблерном листинге, выявление параметров подпрограмм и возвращаемых ими значений, структурный анализ, то есть восстановление операторов циклов, ветвлений и т. п., восстановление типов данных, как базовых, так и производных и другие. Поскольку все эти задачи достаточно трудоемки и алгоритмически неразрешимы, на сегодняшний день нет известных декомпиляторов, восстанавливающих программы в какой-либо язык высокого уровня, которые качественно справлялись бы со всеми перечисленными выше задачами. Для решения задач посредством использования декомпиляторов требуется хорошо представлять возможности используемого инструмента, и для достижения наилучшего результата, возможно, потребуется использовать набор декомпиляторов в некоторой композиции. В данной работе предлагается обзор наиболее известных декомпиляторов в язык Си из бинарных файлов, рассматривается набор тестов, на основе которого можно сделать сравнительный анализ работоспособности декомпиляторов, и выполняется этот анализ. В данной работе в качестве процессорной архитектуры, с которой ведётся декомпиляция, выбрана архитектура Intel i386, наиболее распространённая в настоящее время. В листингах фрагментов программ на языке ассемблера используется синтаксис AT&T [3]. Предлагаемая работа имеет следующую структуру. Поскольку и в литературе, и на практике зачастую смешиваются понятия дизассемблирования программы и декомпиляции программы, уместно рассмотреть различия этих задач. Этому посвящен второй раздел статьи. В третьем разделе статьи дается описание основных подзадач декомпиляции с описанием возникающих трудностей при их решении. В четвертом разделе приводится обзор языка Си с точки зрения обратной инженерии. Пятый раздел посвящен описанию существующих декомпиляторов для языка Си. В пятом разделе представлены результаты сравнительного тестирования декомпиляторов на разработанном наборе тестовых примеров. В заключении сформулированы выводы работы и направления дальнейших исследований.
120
2. Декомпиляция и дизассемблирование Рассмотрим независимо друг от друга задачу дизассемблирования и задачу декомпиляции программ. Под декомпиляцией понимается построение программы на языке высокого уровня, эквивалентной исходной программе на языке низкого уровня (языке ассемблера). Под дизассемблированием понимается построение программы на языке ассемблера, эквивалентной исходной программе в машинном коде. Программа в машинном коде представляется либо в виде исполняемого модуля в стандартном для целевой операционной системы формате (например, для Win32 в формате PE [16], а для Linux – в формате ELF [15]), либо в виде дампа содержимого памяти, либо в виде трассы исполнения программы. Традиционно декомпиляция рассматривается в более широком смысле, а именно, как построение программы на языке высокого уровня по программе в машинном коде. Очевидно, что в такой постановке задача декомпиляции поглощает задачу дизассемблирования. Такое «широкое» понимание декомпиляции излишне, поскольку дизассемблирование и декомпиляция решают разные по сути задачи, хотя и используют схожие методы (в частности, построение графа потока управления и исполняемого покрытия программы). Так, при дизассемблировании выполняется трансляция исполняемого файла, представляемого в виде набора машинных команд, в программу на языке ассемблера. При декомпиляции программа с представления низкого уровня транслируется в представление высокого уровня. Дальнейшим этапом повышения уровня абстракции программы может быть рефакторинг, посредством которого из программы на языке Си можно, например, получить программу на языке Си++. Рассмотрим разбиение задач декомпиляции и дизассемблирования на подзадачи. Так, при дизассемблировании требуется решать следующие основные задачи: ¾ Разделение кода и данных. Для каждой ячейки программы (или ячейки памяти дампа) должно быть установлено, хранит ли ячейка исполняемые инструкции или данные. Задача эта сама по себе алгоритмически неразрешима [5] и не всегда может быть решена однозначно (например, в случае самомодифицирующегося кода, динамически подгружаемого кода и т. п.). ¾ Замена абсолютных адресов на символические. При декомпиляции должны быть решены следующие основные задачи: ¾ Выделение функций в потоке инструкций. ¾ Выявление параметров и возвращаемых значений. ¾ Восстановление структурных конструкций языка высокого уровня. ¾ Замена всех обращений к памяти на конструкции языка высокого уровня (в частности, сюда входит идентификация обращения к локальным переменным и параметрам и их замена на символические 121
имена, идентификация обращений к массивам и их замена на операции с массивами и т. д.). ¾ Восстановление типов объектов языка высокого уровня, выявленных на предыдущем шаге. В дальнейшем мы будем рассматривать задачу декомпиляции в узкой постановке, то есть как задачу трансляции программы, представленной на языке низкого уровня, в частности, на языке ассемблера, в программу на языке высокого уровня, в частности, на Си.
3. Обзор основных подзадач декомпиляции Рассмотрим основные задачи декомпиляции и подходы к их решению.
3.1. Выделение функций Одной из основных структурных единиц программ на языке Си являются функции, которые могут принимать параметры и возвращать значения. Откомпилированная программа, однако, состоит из потока инструкций, функции в котором никак структурно не выделяются. Как правило, компиляторы генерируют код с одной точкой входа в функцию и одной точкой выхода из функции. При этом в начало кода, генерируемого для функции, помещается последовательность машинных инструкций, называемая прологом функции, а в конец кода – эпилог функции. И прологи, и эпилоги функций, как правило, стандартны для каждой архитектуры и лишь незначительно варьируются. Например, стандартный пролог и эпилог функции для архитектуры i386 показаны ниже: Пролог: pushl %ebp movl %esp, %ebp Эпилог: movl %ebp, %esp popl %ebp ret Прологи и эпилоги функций могут быть легко выделены в потоке инструкций. Кроме того, при работе с трассами можно считать, что инструкции, на которые управление передается с помощью инструкции call, являются точками входа в функции, а инструкции ret завершают функции. Возникает соблазн считать инструкции, расположенные между прологом и эпилогом, или между точками входа и выходом, телом функции, однако в этом случае можно натолкнуться на ряд сложностей. Во-первых, при компиляции программы могут быть указаны опции, влияющие на форму пролога и эпилога функции. 122
Например, опция компилятора GCC –fomit-frame-pointer подавляет использование регистра %ebp в качестве указателя на текущий стековый кадр, когда это возможно. В этом случае пролог и эпилог функции будут, как таковые, отсутствовать. Во-вторых, отдельные оптимизационные преобразования могут разрушать исходную структуру функций программы. Очевидным примером такого оптимизационного преобразования является встраивание тела функции в точку вызова. Встроенная функция не существует как отдельная структурная единица программы, и ее автоматическое выделение представляется затруднительным. Существуют оптимизирующие преобразования, которые приводят к появлению в машинном коде конструкций, принципиально невозможных в языках высокого уровня. Таким оптимизирующим преобразованием является, например, sibling call optimization. Если список параметров двух функций идентичен, и первая функция вызывает вторую с этими параметрами, то инструкция вызова подпрограммы call может быть преобразована в инструкцию безусловного перехода jmp в середину тела второй функции. Результатом такого рода «неструктурных» оптимизаций будет появление переходов из одной функции в другую, появление функций с несколькими точками входа или несколькими точками выхода. Другим источником «неструктурных» конструкций в машинной программе являются операторы обработки исключений в таких языках, как Си++. Таким образом, хотя в типичном случае компилятор генерирует хорошо структурированный код, поддающийся разбиению на функции, достаточно легко может быть получен и «неструктурированный» код. Следует отметить, что в этом случае влияние программиста, пишущего программу на языке Си, на структуру генерируемого кода ограничено возможностями языка Си, не позволяющего бесконтрольной передачи управления между функциями и не поддерживающего механизм исключений. Поэтому можно предполагать, что если восстанавливается программа с языка ассемблера, полученная в результате компиляции программы на языке Си, то она не содержит «неструктурных» особенностей, описанных выше, и может быть разбита на функции.
3.2. Выявление параметров и возвращаемых значений В языках высокого уровня, в частности, Си поддерживается передача параметров в функции и возврат значений. В языке Си существует только передача параметров по значению, в других языках могут поддерживаться и другие механизмы. Заметим, что здесь мы рассматриваем только механизмы передачи параметров, отображаемые в генерируемый машинный код. Передача параметров по имени, передача параметров в шаблоны и другие механизмы периода компиляции программы здесь не рассматриваются. Способы передачи параметров и возврата значений для каждой платформы специфицированы и являются составной частью так называемого ABI (application binary interface). Под платформой здесь понимается, как обычно, 123
тип процессора и тип операционной системы, например, Win32/i386 или Linux/x86_64. Одной из задач ABI является обеспечение совместимости по вызовам приложений и библиотек, скомпилированных разными компиляторами одного языка или написанных на разных языках. Так, для платформы win32/i386 используется несколько соглашений о передаче параметров. Соглашение о передаче параметров _cdecl используется по умолчанию в программах на Си и Си++ и имеет следующие особенности [9]: 1. Параметры передаются в стеке и заносятся в стек справа налево (то есть первый в списке параметр заносится в стек последним). 2. Параметры выравниваются в стеке по границе 4 байт, и адреса всех параметров кратны 4. То есть параметры типа char и short передаются как int, но и дополнительное выравнивание для размещения, например, double не производится. 3. Очистку стека производит вызывающая функция. 4. Регистры %eax, %ecx, %edx и %st(0) – %st(7) могут свободно использоваться (не должны сохраняться при входе в функцию и восстанавливаться при выходе из нее). 5. Регистры %ebx, %esi, %edi, %ebp не должны модифицироваться в результате работы функции. 6. Значения целых типов, размер которых не превосходит 32 бит, возвращаются в регистре %eax, 64-битных целых типов – в регистрах %eax и %edx, вещественных типов – в регистре %st(0). 7. Если функция возвращает результат структурного типа, то место под возвращаемое значение должно быть зарезервировано вызывающей функцией. Адрес этой области памяти передается как (скрытый) первый параметр. Отметим, что этот набор правил – это именно соглашения, которые «добровольно» выполняются в сгенерированном коде. Пока речь не заходит об интерфейсе с независимо скомпилированными сторонними модулями, программист может в определенной мере модифицировать эти правила, существенно затрудняя задачу автоматического восстановления функций. Опять же можно предполагать, что если программа декомпилируется из автоматически полученного ассемблерного кода (либо компилятором, либо дизассемблером), то в ней используются только соглашения о передаче параметров из некоторого предопределенного множества. Причем в одной программе для разных функций не могут использоваться разные соглашения о передаче параметров. На первом этапе решения задачи выявления параметров функций следует определить следующие особенности вызова функций: 1. Используемое соглашение о передаче параметров. Требуется определить, какое соглашение из набора предопределенных соглашений используется в программе. 124
2.
Размер области параметров функции. Почти все соглашения о передаче параметров могут быть достаточно надежно идентифицированы по используемым инструкциям. Так, соглашение о передаче параметров stdcall требует, чтобы параметры из стека удалялись вызываемой функцией. Для этого может использоваться единственная инструкция системы команд i386 – ret N, где N – размер удаляемых из стека параметров. Таким образом, использование этой инструкции для возврата из функции указывает как на соглашение о передаче параметров, так и на размер параметров функции. В случае вызова функции по указателю при статическом анализе нам может быть неизвестен адрес вызываемой функции. В этом случае не представляется возможным отследить, как возвращается управление из вызываемой функции. Определение соглашения о вызовах тогда должно быть отложено на фазы последующего анализа. Итак, на фазе выявления параметров и возвращаемых значений определяется размер передаваемых в функцию параметров и способ возврата значения из функции. В дальнейшем эта информация используется как начальная при восстановлении символических имен и восстановлении типов.
3.3. Структурный анализ Одним из результатов предыдущих фаз анализа ассемблерного листинга программы является разбиение потока инструкций ассемблерного листинга на отдельные функции и выявление точек входа в функции и возврата из функций. Инструкции ассемблерной программы в функции могут рассматриваться как представление нижнего уровня (Low-level intermediate representation) [12]. В частности, представление низкого уровня отличается от представления высокого уровня (программы на языке Си) отсутствием структурных управляющих конструкций (if, for и т. п.). Для восстановления управляющих конструкций сначала строится граф потока управления программы. По графу потока управления строится дерево доминаторов, затем дуги графа потока управления классифицируются на «прямые», «обратные» и «косые». На основании этой информации уже можно выполнять непосредственно структурный анализ, то есть восстановление высокоуровневых управляющих конструкций [6]. Поиском в глубину в графе выделяются шаблоны основных структурных конструкций, которые затем организуются в иерархическую структуру.
3.4. Восстановление типов Задача автоматического восстановления типов данных на настоящее время – одна из задач в области декомпиляции, наименее проработанных с теоретической точки зрения. Ее можно условно разделить на подзадачу 125
восстановления базовых типов данных языка, таких как char, unsigned long и т. п., и на подзадачу восстановления производных типов, таких как типы структур, массивов и указателей. В работе [13] рассматривается восстановление как базовых, так и производных типов при декомпиляции, однако этот подход имеет ряд существенных недостатков, и отсутствует его практическая реализация. В работе [4] описан подход к автоматическому восстановлению производных типов языка по исполняемому файлу. Такой подход используется для анализа на уязвимость программ в виде исполняемых файлов и поэтому не применим напрямую к задаче восстановления типов при декомпиляции. На практике же все декомпиляторы, кроме Hex-Rays, вообще не восстанавливают даже базовые типы переменных, а в выражениях используют явное приведение типов, что делает восстановленные выражения сложными для понимания и модификации.
4. Языки высокого уровня с точки зрения обратной инженерии Языки высокого уровня позволяют повысить уровень абстракции представления реализуемого алгоритма, избавляя программиста от необходимости заботиться о низкоуровневых деталях. Эти языки соперничают друг с другом по простоте использования и гибкости, а разработчики компиляторов соперничают по производительности сгенерированного ими кода. Следовательно, имеется большое количество разнообразных языков высокого уровня, и для каждого из них существует множество компиляторов. При восстановлении программ по программе на языке низкого уровня, имея широкое представление о языке высокого уровня, нужно с достаточной точностью восстановить то, что было написано на языке высокого уровня в исходном тексте программы. Точность и трудозатраты восстановления программы сильно зависят от языка высокого уровня, на котором была написана исходная программа. Язык Си формально считается языком высокого уровня, однако в нем присутствует много черт языка низкого уровня. В частности, в языке Си поддерживается прямой доступ к памяти и работа с указателями. При обращении к элементам массива не контролируется выход за его пределы, то есть возможен доступ к областям памяти, не имеющим никакого отношения к массиву. С другой стороны, в языке Си поддерживаются такие высокоуровневые конструкции, как производные типы данных: массивы, структуры, объединения, а также условные операторы, циклы и т. д. На практике особую значимость имеют декомпиляторы, транслирующие ассемблерный листинг в язык Си. Во-первых, восстанавливать программы, написанные изначально на языке Си, удобно, потому что это процедурный язык и у него много низкоуровневых особенностей. Во-вторых, язык Си 126
широко применяется в промышленном программировании, и большое количество системных приложений написано именно на языке Си. С другой стороны, восстанавливать программу из ассемблера в объектноориентированный язык принципиально сложнее, да и к тому же программа, реализованная на основе процедурной парадигмы программирования, может быть переведена в объектно-ориентированную программу посредством рефакторинга ее кода. Следовательно, в данной работе ограничим множество рассматриваемых декомпиляторов теми, которые восстанавливают на языке Си программы, представленные либо на языке ассемблера, либо в виде исполняемых файлов.
5. Декомпиляторы в язык Си В данном разделе дается краткое описание существующих на сегодняшний момент декомпилятров в язык Си. Это – декомпиляторы Boomerang [5], DCC [8], REC [14] и плагин Hex-Rays [10] к дизассемблеру IdaPro [11]. Все рассматриваемые декомпиляторы, кроме плагина Hex-Rays, на вход принимают исполняемый файл, и выдают программу на языке Си. В том случае, когда декомпилятор оказывается не в состоянии восстановить некоторый фрагмент исходной программы на языке Си, этот фрагмент сохраняется в виде ассемблерной вставки. Надо заметить, что даже небольшие исходные программы после декомпиляции зачастую содержат очень много ассемблерных вставок, что практически сводит на нет эффект от декомпиляции. В отличие от этого, плагин Hex-Rays принимает на вход программу, являющуюся результатом работы дизассемблера Ida Pro, то есть схему программы на ассемблеро-подобном языке программирования. В качестве результата Hex-Rays выдает восстановленную программу в виде схемы на Сиподобном языке программирования. Тем не менее, для простоты мы в дальнейшем объединим процесс дизассемблирования с использованием Ida Pro и последующей декомпиляции.
5.1. Boomerang Декомпилятор Boomerang [5] является программным обеспечением с открытым исходным кодом (open source). Разработка этого декомпилятора активно началась в 2002 году, но сейчас проект развивается достаточно вяло. Изначально задачей проекта была разработка такого декомпилятора, который восстанавливает исходный код из исполняемых файлов, вне зависимости от того, с использованием какого компилятора и с какими опциями исполняемый файл был получен. Для этого в качестве внутреннего представления было решено использовать представление программы со статическими одиночными присваиваниями (SSA). Однако, несмотря на поставленную цель, в результате декомпилятор не сильно адаптирован под различные компиляторы и чувствителен к применению различных опций, в частности, опций 127
оптимизации. Еще одной особенностью, затрудняющей использование декомпилятора Boomerang, является то, что в нем не поддерживается распознавание стандартных функций библиотеки Си.
5.2. DCC Проект по разработке этого декомпилятора [8] был открыт в 1991 году и закрыт в 1994 году с получением главным разработчиком степени PhD. В качестве входных данных декомпилятор DCC принимает 16-битные исполняемые файлы в формате DOS EXE. Алгоритмы декомпиляции, реализованные в этом декомпиляторе, основаны на теории графов (анализ потока данных и потока управления). Для распознавания библиотечных функций используется сигнатурный поиск, для которого была разработана библиотека сигнатур. Однако надо заметить, что, несмотря на это, декомпилятор плохо справляется с выявлением функций стандартной библиотеки.
5.3. REC Этот проект [14] был открыт в 1997 году компанией BackerStreet Software, но вскоре закрылся из-за ухода ведущего разработчика проекта. Позднее разработка декомпилятора продолжилась его автором в статусе собственного продукта. Сейчас декомпилятор распространяется свободно, а развивается достаточно вяло. Одной из особенностей рассматриваемого декомпилятора является то, что он восстанавливает исполняемые файлы в различных форматах, в частности ELF и PE. Также декомпилятор REC можно использовать на различных платформах. В ходе тестирования этого декомпилятора было отмечено, что наиболее успешно декомпилятор восстанавливает исполняемые файлы, полученные при компиляции с включением опций, которые отвечают за отключение оптимизаций и добавление отладочной информации.
5.4. Hex-Rays Как уже говорилось, инструмент Hex-Rays [10] не является самостоятельным программным продуктом, а распространяется в виде плагина к дизассемблеру IdaPro [11]. Это самое новое из рассматриваемых средств декомпиляции: плагин появился на рынке в 2007 году. Особенностью данного инструмента является то, что он, как отмечалось, восстанавливает программы, полученные на выходе дизассемблера Ida Pro. Среди алгоритмов, используемых в HexRays, заслуживают внимания алгоритм сигнатурного поиска FLIRT [1] и алгоритм поиска параметров в стеке PIT (Parameter Identification and Tracking). В таблице 1 представлена сводная характеристика всех рассматриваемых декомпилятров.
128
Boomerang
DCC
REC
распознавание библиотечных функций активность разработки
нет
заявлено
нет
HexRays да
да
нет
да
да
переносимость
нет
да
да
да
open source
да
да
нет
нет
Таблица 1. Сравнительный анализ декомпиляторов
6. Исследование возможностей декомпиляторов В этом разделе приведены результаты тестирования возможностей рассмотренных декомпиляторов. Для тестирования был разработан тестовый набор программ на языке Си, покрывающий основные языковые конструкции языка Си. Тестирование проводилось по следующей методике. Исходный код программы на Си компилировался компилятором gcc 3.4.5 в среде Debian Linux и компилятором Borland C++ 3.1 в среде Windows XP. В первом случае результатом работы компилятора являлся файл формата ELF для архитектуры ia32, во втором – исполняемый файл DOS для 16-битного режима процессора. Исполняемый файл формата ELF подавался на вход декомпиляторам Boomerang, REC и Hex-Rays, работающим в среде Windows XP. Исполняемый файл формата DOS~EXE подавался на вход декомпилятору DCC. Результат декомпиляции сравнивался с исходным текстом. Такая комбинация инструментальных и целевых сред была выбрана по следующим причинам. Во-первых, декомпилятор DCC поддерживает только 16-битные исполняемые модули DOS, поэтому для оценки качества работы декомпилятора был использован компилятор 16-битного режима. Декомпиляторы Boomerang и REC, наоборот, не поддерживают 16-битный режим DOS. Исполняемый модуль подавался на вход декомпиляторам в формате ELF, а не в естественном для Windows формате PE, поскольку, как оказалось, декомпиляторы Boomerang и REC некорректно обнаруживают точку начала программы на Си в файлах формата PE. Качество работы каждого декомпилятора для каждого теста оценивалось по четырехбальной экспертной шкале, приведенной в таблице 2. Так, оценка «3» выставлялась в случаях, когда в декомпилированной программе использовались адресная арифметика вместо массивов или приведение типов для получения указательных значений вместо корректного объявления типов переменных. Кроме того, оценка «3» выставлялась, если в 129
результате декомпиляции цикл for оказывался преобразовыванным в цикл while.
Количество Комментарий баллов за тест Декомпилятор закончил работу с ошибкой 0 выполнения или пустым результатом. Декомпилятор выдал ассемблерный код. Программа 1 на Си не была получена. Декомпилятор выдал программу на языке Си, которая 2 либо не компилируется, либо работает неверно (неэквивалентна исходной), либо содержит ассемблерные вставки, то есть недекомпилированные фрагменты программы. Декомпилятор выдал корректную программу, которая 3 эквивалентна исходной, но в ней используются конструкции, отличные от конструкций исходной программы. Декомпилятор выдал программу, которая 4 эквивалентна исходной, и в которой используются те же конструкции, которые использовались в исходной программе. Таблица 2. Шкала оценки декомпиляторов
6.1. Система тестов Тестовый набор содержал следующие основные группы. 1) Типы. В тестах этой группы проверялась корректность восстановления типов переменных и параметров функций. В языке Си поддерживается богатый набор базовых целочисленных типов от типа char до типа unsigned long long. Декомпилятор должен по возможности точно восстановить как размер, так и знаковость переменной. Также рассматривались типы указателей на базовые типы. Проверялись факт обнаружения того, что переменная обладает указательным, а не целым типом, а также корректность восстановления целевого типа указателя. Для массивов проверялся факт обнаружения того, что переменная является локальным или глобальным массивом, точность восстановления типа элементов массива, точность восстановления размера массива как для одномерных, так и для многомерных массивов. 130
2)
3)
4)
5)
Для структурных типов проверялся факт распознавания использования структурного типа и точность восстановления полей структур. Кроме того, были рассмотрены разные комбинации указательных, массивовых и структурных типов и оценена корректность восстановления таких составных типов. В частности, рассматривались массивы структур, указатели на структуры, структуры, содержащие массивы, структуры, содержащие указатели на самих себя. Языковые конструкции. В тестах этой группы проверялась корректность восстановления управляющих структур программы. Проверялась корректность восстановления оператора if с простым условием, в том числе и с отсутствующей частью else, операторов цикла while и do while с простыми условиями. В другой группе тестов проверялась корректность восстановления логических операций && (логическое «и»), || (логическое «или») в условиях операторов if и циклов. Согласно семантике языка Си эти операторы транслируются в условные и безусловные переходы, то есть являются конструкциями, задающими поток управления, а не вычисления значений. Декомпиляторы должны по возможности восстанавливать сложные условия в операторах языка. Отдельно проверялась корректность восстановления структурных операторов передачи управления, таких как break, continue и return. Оператор switch рассматривался отдельно, так как в большинстве компиляторов он транслируется в косвенный безусловный переход, где адрес перехода выбирается из таблицы в соответствии с вычисленным в заголовке оператора значением. Декомпиляторы должны распознавать использование этого оператора в программе. Функции. В тестах этой группы проверялась корректность выделения параметров функций и локальных переменных в условиях разных соглашений о вызовах. Кроме того, проверялась корректность обработки рекурсивных функций. Оптимизации. В тестах этой группы проверялась корректность работы декомпиляторов в ситуации, когда при компиляции были использованы некоторые оптимизационные преобразования, такие как открытая вставка функций (inlining) и оптимизации вызовов функций (tail call optimization, tail recursion optimization, sibling call optimization). Взаимодействие с окружением. В данной группе находился тест, проверяющий корректность обнаружения функции main в исполняемых файлах формата PE. Как известно, выполнение программы на языке Си начинается с функции main, которой передается определенный список параметров. Однако в исполняемых файлах вызову функции main предшествует выполнение специального кода, задача которого настроить окружение программы на Си, что заключается, в частности, в создании стандартных потоков ввода-вывода, инициализации служебных структур 131
данных управления динамической памятью и т. п. Этот код частично написан на языке ассемблера, кроме того, он не представляет интереса, так как является стандартным для всех программ. Поэтому декомпиляторы должны игнорировать этот инициализационный код и начинать декомпиляцию непосредственно с функции main. Кроме того, в тестах этой группы проверялось распознавание стандартных библиотечных функций языка Си (например, strlen и т. п.). Реализация сигнатурного поиска присутствует в декомпиляторе DCC, но наиболее развита эта технология в декомпиляторе Hex-Rays.
6.2. Результаты тестирования В таблице 3 приводятся результаты работы декомпиляторов на выбранном наборе тестов в соответствии с системой оценок, приведенной в таблице 2. Каждый столбец таблицы соответствует декомпилятору, а каждая строка – тесту. Общий результат для каждого декомпилятора получен суммированием оценок по всем тестам. Из всех рассмотренных декомпиляторов только Boomerang поддерживает декомпиляцию оператора switch. Остальные декомпиляторы генерируют в этом случае некорректный код на языке ассемблера. Только декомпилятор REC сумел восстановить цикл for, в то время как остальные декомпиляторы в этом случае генерируют программу, использующую цикл while. BomRec DCC Hexmerang Rays struct 3 2 3 3 массивы 4 3 3 4 типы данных unsigned int 4 3 3 3 unsigned short 3 3 3 3 логические операции 4 4 4 4 циклы for 3 4 3 3 структурные конструкции циклы while 4 3 4 4 языка циклы do while 4 4 4 4 оператор switch 4 2 2 2 функции рекурсия 4 4 4 4 inlining 2 2 – 2 оптимизация tail recursion 2 2 – 2 обнаружение функции main в PE файлах 1 1 – 4 обнаружение функций стандартной 2 2 3 4 библиотеки 48 43 40 50 сумма баллов Таблица 3. Результаты тестирования декомпиляторов 132
Наиболее развитым в настоящее время является декомпилятор Hex-Rays, который, в отличие от других декомпиляторов, поддерживает распознавание массивов и распознавание библиотечных функций, хотя даже и у Hex-Rays имеется много слабых сторон.
[15] Tool Interface Standards (TIS). Executable and Linkable Format (ELF). http://www.x86.org/intel.doc/tools.htm [16] Tool Interface Standards (TIS). Portable Executable Formats (PE). http://www.x86.org/intel.doc/tools.htm
7. Заключение. В данной работе рассмотрена задача декомпиляции как восстановления программы на языке высокого уровня по программе на языке ассемблера или в машинных кодах. Дано краткое описание основных шагов процесса декомпиляции программы. В работе представлено сравнительное тестирование существующих декомпиляторов и указаны их сильные и слабые стороны. Так, ни один существующий на данный момент декомпилятор не поддерживает в достаточной мере восстановление типов данных, как базовых, так и производных. Существующие декомпиляторы испытывают сложности с восстановлением цикла for и оператора switch. Наиболее развитым из всех декомпиляторов является Hex-Rays, однако он является коммерческим и с закрытыми исходными кодами, поэтому его доработка невозможна. Поэтому представляются актуальными разработка и реализация алгоритмов, позволяющих восстанавливать базовые и производные типы данных в процессе декомпиляции. Алгоритмы должны быть апробированы в рамках экспериментальной инструментальной среды декомпиляции программ. Разработка таких алгоритмов и инструментальной среды декомпиляции программ является направлением дальнейших исследований авторов. Литература [1] Гуильфанов И. FLIRT – Fast Library Identification and Recognition Technology. http://www.idapro.ru/description/flirt/ [2] Щеглов К.Е. Обзор алгоритмов декомпиляции//Электронный журнал «Исследовано в России». http://zhurnal.ape.relarn.ru/articles/2001/116.pdf [3] AT\&T Assembly Syntax. http://sig9.com/articles/att-syntax [4] G. Balakrishnan, T. Reps. Analyzing Memory Accesses in x86 Executables. Compiler Construction vol. 2985/2004, Springer Berlin / Heidelberg, 2004, стр. 5-23. [5] Boomerang Decompiler Home Page. http://boomerang.sourceforge.net/ [6] C. Cifuentes, D. Simon, A. Fraboulet. Assembly to High-Level Language Translation. Technical Report 439. Department of Computer Science and Electrical Engineering. The University of Queensland. 1998. [7] M. Davis. Computability and Unsolvability, New York: McGraw-Hill, 1958. [8] DCC Decompiler Home Page. http://www.itee.uq.edu.au/~cristina/dcc.html [9] Agner Fog. Calling conventions for different C++ compilers and operating systems. [10] Hex-Rays Decompiler SDK. http://www.hex-rays.com/ [11] Интерактивный дизассемблер Ida Pro. http://www.idapro.ru/ [12] Steven S. Muchnik. Advanced Compiler Design And Implementation. [13] А. Mycroft. Type-based decompilation. In European Symp. on Programming, 1999. [14] REC Decompiler Home Page. http://www.backerstreet.com/rec/
133
134
Подход к реализации переносимого TTCN-3 отладчика П.Н. Яковенко, А.В. Сапожников
1. Введение TTCN-3 [1, 16] является языком программирования, предназначенным для тестирования программных и аппаратных систем по принципу «черного ящика» [4]. Основу TTCN-3 тестов составляют сценарии вида «запрос-ответ». Тестовая программа стимулирует целевую систему, посылая ей запросы. Целевая система реагирует на них, возвращая ответ. На основе оценки реакции целевой системы на стимулы выносится вердикт: тест прошел или нет. Взаимодействие с тестируемой системой производится через точки доступа (порты), для которых в TTCN-3 определен ряд синхронных и асинхронных операций, таких как посылка сообщения и вызов процедуры. Особенность TTCN-3 состоит в том, что реализация коммуникационных операций, используемых в тестовом сценарии, возлагается на разработчика этого сценария [14, 15]. Для этого он должен реализовать операции коммуникационного интерфейса времени выполнения, определенного в стандарте языка TTCN-3. Другими словами разработчик должен реализовать набор функций с заданным прототипом на C, Java или каком-либо другом языке. Помимо коммуникационного интерфейса в стандарте TTCN-3 определяется ряд других интерфейсов времени выполнения, позволяющих адаптировать платформо-независимые тесты к специфике конкретной целевой системы [2,3]. Традиционно TTCN-3 применяется в области телекоммуникаций для тестирования реализации протоколов в коммутационном оборудовании [12]. Пакеты тестов для некоторых протоколов, в частности, SIP и IPv6 находятся в свободном доступе. В последние годы наблюдается тенденция применения языка TTCN-3 для тестирования встраиваемых систем [8], в частности, электронных блоков управления в автомобилях [7, 11]. Применение TTCN-3 не ограничивается тестированием аппаратных устройств, подключенных к некоторому каналу связи. TTCN-3 также используется для тестирования CORBA-интерфейсов[9] и веб-сервисов [10]. 135
Начиная с редакции 3.1.1, в языке TTCN-3 появился интерфейс регистрации событий TCI-TL (TTCN-3 Control Interface – Test Logging) [3]. Далее в тексте для краткости мы будем называть его просто TL. При помощи этого интерфейса разработчик может получать извещения о событиях, возникающих в тестовом сценарии в ходе его выполнения, и обрабатывать их. Набор событий покрывает практически все операции языка TTCN-3, включая операции взаимодействия с целевой системой, вызов функций, операцию присваивания и многие другие. Тестовый сценарий на языке TTCN-3 представляет собой сравнительно сложную программу. Поэтому тестовый сценарий сам по себе должен быть протестирован и отлажен. Заманчивой является возможность реализации отладчика для языка TTCN-3, использующего для реализации операций отладки только стандартные интерфейсы времени выполнения. Такой отладчик не привязан к какой-либо конкретной среде разработки языка TTCN3. Для него не важно, компилируется или интерпретируется код TTCN-3, в какой целевой язык он компилируется, и на какой платформе исполняется тестовый сценарий. Мы задаемся вопросом, можно ли, пользуясь только операциями TLинтерфейса, реализовать типовые операции отладки, а именно: • расстановку контрольных точек и приостановку выполнения в них; • просмотр стека вызовов функций; • пошаговую трассировку; • просмотр значений переменных. Если операций TL-интерфейса недостаточно, то в чем заключается его ограниченность? Каким образом следует расширить интерфейс для устранения найденных ограничений? В этой статье мы даем ответы на поставленные вопросы. Материал статьи организован следующим образом. В разделе 2 дается краткое описание интерфейса TCI-TL. В разделе 3 рассматриваются подходы к реализации отладчика: времени выполнения и «посмертный». В разделе 4 описываются особенности реализации операций отладки на базе TLинтерфейса. В разделе 5 рассматривается проблема обработки запущенных таймеров при остановке в контрольной точке. В разделе 6 подводятся итоги работы.
2. Обзор интерфейса TCI-TL Основное предназначение TL-интерфейса состоит в реализации разнообразных механизмов регистрации событий, представляющих трассу событий тестового сценария в пригодном для чтения текстовом виде, в формате XML [5], в виде диаграмм MSC [6], в графическом виде [13] и т.д. TL-интерфейс состоит из более чем ста операций, обеспечивающих 136
исчерпывающую информацию о том, что происходит в тестовом сценарии во время его выполнения. Каждая операция интерфейса связана с определенным событием в тестовой системе. Если классифицировать события в соответствии с их взаимосвязью с операторами языка, то можно выделить несколько классов, среди которых: • события, связанные с запуском и остановкой тестовых сценариев; • события, связанные с запуском и остановкой параллельных компонентов в тестовом сценарии; • события, связанные с конфигурированием портов (точек взаимодействия) в тестовом сценарии; • события, связанные с передачей данных – обменом сообщениями, вызовом удаленных процедур и обработкой исключений, возникших во время выполнения удаленной процедуры; • события, связанные с управлением таймерами в тестовом сценарии. Для каждого события определен фиксированный набор атрибутов, посредством которых предоставляется дополнительная информация о событии. Часть атрибутов является общей для всех событий. Например, для каждого события сообщается позиция в исходном файле, на которой было сформировано событие. Помимо общих атрибутов, событие, как правило, обладает набором специфичных атрибутов. С точки зрения разработчика операция TL-интерфейса представляет собой функцию обратного вызова. При возникновении события система поддержки выполнения программ вызывает соответствующую функцию, передавая ей значения атрибутов события в качестве фактических параметров. Таким образом, разработчик должен реализовать более ста функций и предоставить их тела на этапе сборки исполняемого модуля. В простейшем случае тела всех функций могут быть пустыми. Все операции TL интерфейса имеют прототип вида void tli<имя атрибуты>);
события>(<общие
атрибуты>,
<специфичные
Например, после выполнения операции присваивания формируется событие, сообщающее о том, что переменная получила новое значение. При этом вызывается функция tliVar, имеющая следующий прототип: void tliVar (String long int String long int TriComponentId String TciValue
additionalMessage, timeStamp, sourceFile, lineNumber, componentId, variableName, variableValue); 137
Здесь additionalMessage – дополнительное текстовое сообщение, timeStamp – время возникновения события, sourceFile – имя исходного файла, lineNumber – позиция в исходном файле, сomponentId – идентификатор компонента, сформировавшего событие, variableName – имя переменной, variableValue – новое значение переменной. Помимо события tliVar, мы также будем часто использовать события tliSEnter и tliSLeave. Это события генерируются соответственно при входе в новую область видимости и выходе из нее. Атрибуты событий указывают тип области видимости, ее имя, список значений фактических параметров и т.д. Имя и параметры области видимости определены только для функций, «альтстепов» (специальные функции в TTCN-3) и непосредственно тестовых сценариев, которые с точки зрения синтаксиса языка TTCN-3 являются разновидностью функций. Для составного оператора (нескольких операторов, обрамленных фигурными скобками), также представляющего собой область видимости, значение этих атрибутов не определено. В большинстве случаев типы формальных параметров TL-функций соответствуют определенным типам данных целевого языка, такого как Java или C. Например, тип String соответствует типу char* языка С. Однако некоторые типы, такие как TciValue, являются абстрактными типами данных, и доступ к ним осуществляется только посредством операций, определенных в стандарте.
3. Подходы к реализации отладчика Многие современные отладчики, такие как WinDbg [17], gdb [18], dbx [19], являются отладчиками времени выполнения. Такой подход предполагает, что отлаживаемая программа выполняется под контролем отладчика. Процесс отладки происходит параллельно с выполнением программы. Альтернативным подходом является «посмертная» отладка, при которой отладка производится после завершения программы. Как правило, под «посмертными» понимают отладчики, использующие файл с записанным в нем содержимым памяти процесса после его аварийного останова [20]. Такие отладчики могут лишь исследовать состояние программы на момент ее останова и не могут применяться для трассировки программы. К «посмертным» можно также отнести отладчики, основывающиеся на трассе событий, собранной в ходе выполнения программы [21]. Трасса формируется путем прогона инструментированной версии программы, т.е. программы, в определенные точки которой вставлены обращения к служебным функциям, добавляющим в трассу информацию о состоянии программы в точке вызова. В дальнейшем под «посмертной» отладкой мы будем понимать полноценную отладку программы на базе трассы событий. Отладчики времени выполнения обладают рядом несомненных преимуществ: в любой момент времени им доступны все объекты программы, они могут 138
изменять значения переменных и переопределять адрес следующей выполняемой команды. Пользователю не требуется прогонять программу для сбора трассы, притом, что время выполнения программы может быть значительным, а сама трасса крайне объемной. «Посмертные» отладчики в основном используются в тех случаях, когда отладка времени выполнения затруднительна, или вмешательство отладчика может повлиять на поведение программы, приводя к некорректным результатам, например, при отладке параллельных программ и систем реального времени. Заметим, что «посмертная» отладка не ограничена последовательным движением по трассе событий от начала к концу. Отладчик может двигаться по трассе в любую сторону, заглядывать при необходимости вперед; перескакивать с одного фрагмента трассы на другой. В чем принципиальная важность подхода «посмертной» отладки с точки зрения реализации отладчика на базе TL-интерфейса? Дело в том, что некоторые ограничения TL-интерфейса, о которых будет говориться ниже, не позволяют корректно реализовать все отладочные операции в отладчике времени выполнения. Однако в случае «посмертной» отладки эти ограничения можно преодолеть путем просмотра по трассе вперед, что не представляется возможным при отладке времени выполнения.
4. Типовые операции отладчика Этот раздел посвящен реализации типовых операций отладчика на базе TL интерфейса. Рассматривается реализация механизма контрольных точек, пошаговой трассировки, просмотра стека вызовов функций и просмотра значения переменной по имени.
Традиционно остановка в контрольной точке подразумевает, что поток управления остановлен непосредственно перед выполнением оператора, расположенного на данной позиции. В нашем случае остановка в контрольной точке происходит непосредственно после выполнения оператора на заданной позиции. Реализовать привычную семантику контрольных точек можно лишь при «посмертной» отладке, когда позицию следующего оператора можно легко узнать путем просмотра следующего события в трассе. Каким образом можно устранить указанное ограничение? Предположим, что в TL-интерфейс добавлено событие, сигнализирующее о смене текущей позиции в исходном файле TTCN-3. Назовем это событие tliSChange. Правило его генерации события состоит в следующем. Если компилятор программы TTCN-3 генерирует код для некоторой строки файла, то непосредственно перед выполнением сгенерированного кода формируется событие tliSChange. Очевидно, что это событие информирует о движении потока управления в ходе выполнения тестового сценария. Обрабатывая это событие, мы можем реализовать механизм контрольных точек с привычной семантикой. Событие tliSChange позволяет устранить еще одно ограничение TL интерфейса. Дело в том, что события TL-интерфейса не покрывают все множество операторов языка TTCN-3. Например, оператор while не генерирует событий. События также не генерируются ни для объявления переменной (если только ей не присваивается начальное значение), ни в точке вызова функции. Это значит, что мы не можем установить контрольную точку на заголовке цикла while и в ряде других случаев. При пошаговой трассировке мы будем «перепрыгивать» через такие операторы. Событие tliSChange устраняет это ограничение.
4.2. Пошаговая трассировка
4.1. Установка контрольных точек Приостановка выполнения тестового сценария в контрольной точке может быть реализована путем отслеживания значений двух атрибутов во всех поступающих TL-событиях. Эти атрибуты определяют позицию события через имя исходного TTCN-3 файла и номер строки в файле. Позиция события сравнивается с позициями определенных пользователем контрольных точек. В случае нахождения совпадения выполнение тестового сценария блокируется путем невозврата из TL-функции. Одной из особенностей TL-интерфейса является отсутствие события, информирующего о смене текущей позиции в исходном файле. Текущая позиция может быть определена только на основании последнего сгенерированного события. Событие всегда генерируется после выполнения соответствующего оператора TTCN-3. Узнать во время выполнения позицию следующего оператора не представляется возможным. В силу этой особенности семантика контрольных точек, реализуемых посредством TL-интерфейса, отличается от традиционного подхода. 139
Реализация пошаговой трассировки во многом схожа с реализацией механизма контрольных точек. Действительно, выполнение каждого шага можно рассматривать как установку одноразовой контрольной точки на определенной позиции. Позиция эта динамическая и не известна в момент начала трассировки, а вычисляется по мере выполнения тестового сценария и обработки поступающих событий. Позиция окончания трассировки определяется ее типом: • при трассировке «внутрь» (StepIn) остановка происходит на следующем поступившем событии; • при трассировке «через» (StepOver) остановка происходит на следующем событии, если только это не событие вызова функции; в противном случае остановка происходит сразу после возврата из вызываемой функции; • при трассировке «наружу» (StepOut) остановка происходит сразу после возврата из текущей функции в вызывающую функцию. 140
Для реализации трассировок «через» и «наружу» необходимо отслеживать вызов и возврат из функции. В TL-интерфейсе есть операции tliSEnter и tliSLeave, соответствующие событиям входа в новую область видимости и выхода из нее. Они могут быть использованы для решения этой задачи. При этом мы должны исключить из рассмотрения события tliSEnter и tliSLeave, соответствующие области видимости составного оператора, если он не представляет собой тело функции. Очевидно, что при трассировке «наружу» остановка всегда будет происходить на следующем событии, которое поступает после события tliSLeave, характеризующего возврат в вызываемую функцию. Рассмотрим теперь нетривиальный случай трассировки «через», т.е. ситуацию, когда следующий выполняемый оператор в тестовом сценарии является вызовом функции. Признаком такой ситуация является то, что первое полученное событие после начала трассировки – это событие tliSEnter. В этом случае мы должны учитывать, что вызываемая функция может обращаться к другим функциям; вызов также сформирует события tliSEnter и tliSLeave. Остановка должна произойти после получения того события tliSLeave, которое соответствует возврату из первой вызванной функции. Для его нахождения достаточно поддерживать счетчик пар событий tliSEnter–tliSLeave, получаемых в ходе выполнения шага трассировки.
4.3. Просмотр стека вызовов функций Для реализации просмотра стека вызовов функций мы отслеживаем событие входа в новую область видимости tliSEnter. При этом мы игнорируем те события, которые соответствуют входу в область видимости составного оператора. Наименование вызванной функции содержится в одном из атрибутов этого события. Среди других атрибутов есть также значения фактических параметров. Каждый элемент стека вызовов, помимо названия функции, должен хранить информацию о позиции текущего оператора в этом фрейме. Очевидно, что для всех фреймов, кроме вершины стека, этим оператором является обращение к функции, соответствующей следующему фрейму. Позиция текущего оператора во внутренних фреймах в общем случае корректно определена быть не может. Дело в том, что в TL-интерфейсе отсутствует событие «вызов функции». Событие tliSEnter генерируется, когда поток управления входит в новую область видимости; поэтому позиция, сообщаемая в этом событии, расположена внутри вызываемой функции. Это может быть, например, позиция фигурной скобки, открывающей тело функции. Предложенное в разделе 4.1 расширение TL-интерфейса в виде события tliSChange позволяет скомпенсировать отсутствие события «вызов функции» и устранить ограничения, связанные с показом текущей позиции во внутреннем фрейме. 141
4.4. Просмотр значений переменных Система поддержки выполнения программы извещает об изменениях значений переменных посредством события tliVar. Для параметров модуля имеется отдельное событие tliModulePar. Оба события имеют два специфичных для них атрибута: имя объекта (переменная, константа, параметр модуля) и его новое значение. Значения фактических параметров функции сообщаются в момент ее вызова посредством атрибутов события tliSEnter. Основной проблемой в реализации просмотра значения переменной, запрашиваемой у отладчика по имени, является определение принадлежности переменной, новое значение которой было получено в событии tliVar, к той или иной области видимости. Дело в том, что TL-интерфейс не содержит события, информирующего об объявлении переменной; следовательно, эту информацию нужно косвенным образом извлекать из TL-событий. Только обладая этой информацией, мы можем установить момент, когда поток управления покидает область видимости переменной, а это, в свою очередь, необходимо для корректного отображения значения переменной. Заметим, что отсутствие события объявления переменной также не позволяет нам различить две ситуации, когда переменная не инициализирована и когда она вообще не определена.
4.4.1. Компонентные переменные Переменная в языке TTCN-3 может быть локальной или объявленной на уровне компонента. Во втором случае время существования переменной совпадает со временем жизни компонента. Если компонент является главным тестовым компонентом, неявно создаваемым при запуске тестового сценария, то время жизни компонентной переменной совпадает со временем выполнения тестового сценария. Область видимости компонентной переменной распространяется на все функции и тестовые сценарии, явно объявленные при помощи оператора “runs on” как выполняющиеся на данной компоненте. В отличие от переменных, константы могут быть объявлены и на уровне модуля – самом верхнем уровне иерархий областей видимости в языке TTCN-3. Область видимости таких констант распространяется на все тестовые сценарии и функции, объявленные в данном модуле. Если какой-либо другой модуль импортирует эту константу, то она также видима во всех функциях и тестовых сценариях импортирующего модуля. Параметры модуля могут быть объявлены только на уровне модуля. В языке TTCN-3 не допускается перекрытие имен. Если на уровне модуля объявлена константа myConst, то нигде больше в модуле не должно присутствовать объявление объектов с таким же именем. При этом наличие одинаково именованных объектов допускается в не вложенных областях видимости. Например, в языке TTCN-3 допустимо следующее объявление функции f: 142
function f(in integer a) runs on MyComponent { if (a > 0) { var integer v; } if (a < 0) { var integer v; } }
Оператор log(v) распечатает, разумеется, единицу, однако в рамках TLинтерфейса нельзя сказать, что значение v в этой точке равно единице. Действительно, при выполнении тестового сценария tc сгенерируется ряд событий, из которых нам интересна часть, соответствующая вызову функции f: tliVar(“v”, 1) tliSEnter(“f”) tliVar(“v”, 2) tliSLeave(“f”)
Если же в этом примере заменить v на a, то имя переменной, объявленной в теле условного оператора, будет совпадать с именем формального параметра функции f, и компилятор выдаст ошибку. Несмотря на недопустимость перекрытия имен, отладчик должен уметь отличать объекты уровня модуля от «компонентных» объектов, а эти объекты, в свою очередь, нужно отличать от локальных объектов. Рассмотрим пример, показывающий важность знания области видимости переменной: type component MyComponent { } function f() runs on MyComponent { var integer v; v := 2; //здесь генерируется событие tliVar (v == 2) } testcase tc() runs on MyComponent { var integer v; v := 1; //здесь генерируется событие tliVar (v == 1) f(); log(v); // v == 1 или v == 2? } В этом примере в тестовом сценарии tc объявляется переменная v, ей присваивается единица (при этом генерируется событие tliVar для переменной с именем “v”) и вызывается функция f. В функции f также объявляется переменная v, и ей присваивается двойка (при этом снова генерируется событие tliVar для переменной с именем “v”). После возврата из функции f тестовый сценарий tc выводит на экран значение v. 143
// // // //
v = 1 вызов функции f v = 2 возврат из функции f
Получив такую последовательность событий, мы можем неверно предположить, что оба события tliVar относятся к одной и той же переменной, и v – компонентная переменная. Тогда просмотр значения v на позиции оператора log(v) в отладчике ошибочно покажет двойку, в то время как оператор log(v) выведет на экран единицу. Если же мы будем считать, что v – локальная переменная, и события относятся к двум разным одноименным переменным (как оно и есть в нашем примере), объявленным в тестовом сценария tc и функции f, то просмотр значения v на позиции оператора log(v) в отладчике покажет единицу. Следовательно, в момент получения события tliVar для переменной v необходимо уметь определять, является ли она компонентной или локальной. В общем случае не представляется возможным выделить среди всех событий те из них, которые относятся к компонентным переменным, . Тем не менее, если придерживаться простого соглашения по написанию программ TTCN-3, то в большинстве случаях можно корректно определять характер переменной. Предположим, что переменная v объявлена в компоненте MyComponent, и для нее указано начальное значение, например, следующим образом: type component MyComponent { var integer v := 1; } Тогда система поддержки выполнения программы сгенерирует событие tliVar в момент инициализации переменной. Инициализация происходит во время создания компонента. Для главного тестового компонента это произойдет в интервале между командой на запуск тестового сценария (в этот момент генерируется событие tliTcExecute) и началом выполнения операторов в теле тестового сценария (в этот момент генерируется событие tliTcStarted). Таким образом, все события tliVar, полученные в период между событиями tliTcExecute и tliTcStarted, относятся к инициализации компонентных переменных и констант. Дополнительные параллельные компоненты в языке TTCN-3 должны явно порождаться при помощи оператора create. При этом генерируется событие 144
tliCCreate. Оператор create только создает новый экземпляр компонента заданного типа. Для запуска функции во вновь созданном компоненте необходимо выполнить оператор start, указав имя функции и значения формальных параметров. При этом генерируется событие tliCStart. Такое разделение сделано для того, чтобы иметь возможность должным образом подключить порты нового компонента до начала выполнения в нем функции. Для параллельных компонентов все события tliVar, полученные в период между событиями tliCCreate и tliCStart, относятся к инициализации компонентных переменных и констант. Таким образом, если в TTCN-3 программе все компонентные переменные инициализируются начальными значениями, то, применяя вышеизложенный метод, в большинстве случаев можно отделять события tliVar, относящиеся к компонентным переменным, от всех остальных. Отметим особенность языка TTCN-3, из-за которой изложенный выше алгоритм в ряде случаев будет работать неверно. В TTCN-3 функция или тестовый сценарий, явно объявленные как выполняющиеся в некотором компоненте, могут вызывать обычную функцию (без объявления “runs on”), допускающую вызов из любой точки программы. Рассмотрим следующий пример: type component MyComponent { var integer v := 1;//здесь генерируется событие tliVar (v == 1) } function f() // _НЕ_ объявлена на компоненте { var integer v; v := 2; //здесь генерируется событие tliVar (v == 2) } testcase tc() runs on MyComponent { f(); log(v); // v == 1, но отладчик ошибочно выдаст v == 2 } Отличие этого примера от предыдущего состоит в том, что объявление переменной v перенесено из тестового сценария tc в компонент MyComponent. При этом из декларации функции f удалено объявление “runs on”. В противном случае область видимости локальной переменной v в функции f перекрывалась бы с областью видимости компонентной переменной v в типе MyComponent, что недопустимо в языке TTCN-3. 145
Функция f не связана с каким-либо определенным типом компонента, поэтому переменные, объявленные в компоненте MyComponent, не видны в ней, и объявление локальной переменной v допустимо. При применении предложенного выше алгоритма просмотр значения переменной v на позиции оператора log(v) в отладчике выдаст двойку, что не является корректным. Скорректировать алгоритм в соответствии с этой особенностью не представляется возможным, потому что TL-интерфейс не информирует о том, каким образом объявлена вызываемая функция – с “runs on” или без этой спецификации. Можно предложить следующее универсальное решение задачи распознавания характера переменной, простое в реализации для разработчиков TTCN-3 компилятора, но, тем не менее, требующее внесения изменений в стандарт TTCN-3: в событии tliVar передавать не просто имя переменной, а дополнять его префиксом для объектов уровня модуля и «компонентных» объектов. Объекты уровня модуля дополняются именем модуля, отделенным точкой, компонентные объекты дополняются именем типа компонента, отделенным двоеточием. Именование локальных объектов оставляется без изменений, т.е. без префикса. Например, MyModule.object – объект уровня модуля, MyComponent:object – объект уровня компоненты, object – локальный объект. Такой подход к именованию объектов согласуется с синтаксисом языка TTCN-3. В рамках стандартного интерфейса можно предложить пользователю придерживаться определенного стиля именования объектов. Например, можно начинать все имена объектов уровня модуля с символов g_, компонентные объекты – с символов ct_. Тогда механизм распознавания областей видимости в отладчике может опираться на подобный стиль именования объектов. Тем не менее, такое решение не является стандартным, а, значит, отладчик теряет универсальность. Что касается констант, объявленных на уровне модуля, то событие tliVar для них генерируется до начала выполнения тестового сценария. Поэтому все события tliVar, которые происходят до события tliTcExecute, информирующего о запуске тестового сценария, относятся к инициализации объектов уровня модуля.
4.4.2. Блочные переменные Язык TTCN-3 позволяет объявлять локальные переменные в теле составного оператора. Значение таких переменных становится неопределенным, когда поток управления покидает тело составного оператора. В следующем примере значение переменной v не определено на позиции оператора log(v).
146
if (...) { var integer v := 1; //здесь генерируется событие tliVar (v == 1) } log(v); //ошибка, значение v не определено Запрос у отладчика значения переменной v в точке log(v) должен выдать, что переменная v не определена, однако реализация такой функциональности не представляется возможной, несмотря на то, что при входе и выходе из составного блока генерируются события tliSEnter и tliSLeave соответственно. Дело в том, что в TL-интерфейсе отсутствует событие, информирующее об объявлении переменной. Это значит, что локальная переменная, для которой мы получили событие tliVar, может быть объявлена не только в текущем блоке, но и в любом блоке, который его объемлет, вплоть до блока, представляющего собой тело функции. Решить эту проблему в рамках стандартного TL-интерфейса не представляется возможным. Разработчик отладчика может следовать одной из двух стратегий: либо предполагать, что каждая переменная инициализируется начальным значением, и первое событие tliVar определяет точку определения переменной, либо предполагать, что блочные переменные в TTCN-3 программе отсутствуют. Другими словами, при этом подходе всегда следует предполагать, что локальные переменные объявляются только на уровне тела функции. Последнее ограничение для целей отладки не является существенным, и оно было принято в нашей реализации. Действительно, в TTCN-3 перекрытие имен не разрешается, одноименные переменные не могут быть объявлены в разных блоках, если один из них вложен в другой. Поэтому единственным неудобством для пользователя является, что значение «блочной» переменной будет видно в отладчике после выхода из блока вплоть до момента возврата из функции.
4.4.3. Формальные параметры функций и тестовых сценариев Среди множества объектов, значения которых могут быть запрошены пользователем во время сеанса отладки, выделяются фактические параметры функции и тестового сценария. Значения всех фактических параметров передаются посредством атрибутов события tliSEnter. К сожалению, список значений фактических параметров не содержит имен этих параметров, как они определены в TTCN-3 программе, и получить имена параметров каким-либо другим образом нельзя. В качестве одного из вариантов, отладчик может поддерживать псевдонимы для формальных параметров, например, присваивая им имена вида @<порядковый_номер>. @1 для первого параметра, @2 – для второго и т.д. Разумеется, такое решение является нестандартным. 147
В качестве универсального решения мы предлагаем расширить TCIинтерфейс, добавив в него две функции: tciGetTestCaseParametersNames и tciGetFunctionParametersNames, позволяющие запросить список имен формальных параметров тестового сценария и функции по соответствующему имени. Наличие этих функций позволяет снять отмеченное ограничение.
5. Обработка таймеров выполнения
в
отладчике
времени
В языке TTCN-3 имеется встроенная поддержка таймеров. Для таймеров определен набор операций, в число которых входят операция запуска и остановки таймера, проверка того, истекло ли время таймера и т.д. Понятие времени в TTCN-3 не является жестко закрепленным и определяется пользователем путем реализации соответствующих операций в интерфейсе времени выполнения TRI. Как правило, понятие времени в тестовом сценарии соответствует понятию реального времени, но, вообще говоря, время может быть ускоренным или замедленным вариантом реального времени, событийным или каким-либо еще. В отладчике времени выполнения остановка тестового сценария в контрольной точке, если к этому моменту активен хотя бы один таймер, может привести к некорректному поведению тестового сценария после возобновления выполнения. Таймеры, активные в момент выхода на контрольную точку, необходимо отключать на время простоя в этой контрольной точке. Действительно, рассмотрим пример: timer T; T.start(3.0); //запустить таймер на 3 секунды P.send(Request); alt { []P.receive {setverdict(pass)} // тест прошел []T.timeout {setverdict(fail)} // тест _НЕ_ прошел } В этом примере таймер T устанавливается на три секунды, и затем через порт P отправляется сообщение Request. После этого тестовый сценарий ожидает прихода одного из двух событий: либо в порт P приходит какой-то ответ, и тогда вердикт теста устанавливается в pass (т.е. «тест прошел»), либо истекает время таймера T, и вердикт устанавливается в fail (т.е. «тест не прошел»). Таймер T предназначен для проверки того, что обмен данными через порт P (запрос-ответ) укладывается в три секунды. Предположим, что посылка и прием сообщения в совокупности занимают две секунды. В этом случае тест пройдет успешно. Если приостановить выполнение тестового сценария на десять секунд после запуска таймера, но перед отправкой сообщения, то в 148
операторе alt сработает вторая ветка, и тестовый сценарий закончится с вердиктом fail. Решение этой проблемы выходит за рамки данной работы. Отметим лишь, что отладчик может поддерживать список активных таймеров и информировать пользователя о тех случаях, когда выполнение тестового сценария приостанавливается при наличии запущенных таймеров.
6. Заключение В этой статье предложен подход к реализации отладчика для языка TTCN-3 на базе стандартного интерфейса регистрации событий TCI-TL. Показано, что TL-интерфейс позволяет реализовать типовые операции отладки, такие как установка контрольных точек, пошаговая трассировка, просмотр стека вызовов функций и значений объектов (переменных, констант, параметров модуля). Предложенный подход лег в основу реализации отладчика в среде Telelogic Tester. Ряд ограничений TL-интерфейса не позволяет реализовать операции отладки с той семантикой, которую они имеют в таких широко используемых отладчиках, как WinDbg, gdb, dbx и др. Для устранения наиболее критичных ограничений достаточно внести три изменения в интерфейсы времени выполнения языка TTCN-3: • добавить в TL интерфейс операцию tliSChange, которая информирует об изменении текущей позиции (в исходном TTCN-3 файле) потока управления во время выполнения тестового сценария; • в операции tliVar, информирующей об изменении значения переменной, сообщать имя переменной вместе с префиксом, по которому можно определить характер переменной; для переменных и констант, объявленных на компоненте, таким префиксом является имя типа компоненты, для констант, объявленных на уровне модуля, таким префиксом является имя модуля; • добавить в TCI интерфейс две операции, позволяющие запросить имена формальных параметров функции (например, tciGetFunctionParametersNames) и тестового сценария (например, tciGetTestCaseParametersNames) по соответствующему имени. Отметим, что отладчик, реализованный на базе стандартного TL-интерфейса, не зависит от конкретной среды разработки для языка TTCN-3. Более того, для отладчика не существенно, компилируется или интерпретируется TTCN-3 код, в какой целевой язык он компилируется и на какой целевой платформе исполняется. В дальнейшем планируется провести исследование и реализовать в отладчике обработку таймеров в контрольных точках и возможность изменения значений переменных. 149
Литература [1] ETSI. Methods for Testing and Specification (MTS). The Testing and Test Control Notation version 3; Part 1 TTCN-3 Core Language; ETSI ES 201 873-1, V3.2.1, 2007 [2] ETSI. Methods for Testing and Specification (MTS) .The Testing and Test Control Notation version 3; Part 5 TTCN-3 Runtime Interface (TRI); ETSI ES 201 873-5, V3.2.1, 2007 [3] ETSI. Methods for Testing and Specification (MTS) .The Testing and Test Control Notation version 3; Part 6 TTCN-3 Control Interface (TCI); ETSI ES 201 873-6, V3.2.1, 2007 [4] B. Beizer. Software Testing Techniques, 2nd Edition. New York. Van Nostrand Reinhold. 1990 [5] Extensible Markup Language (XML) 1.0, W3C Recommendation, 1998 [6] Message Sequence Chart (MSC), ITU-T Recommendation Z.120, 1999 [7] S. Burton, A. Baresel, I. Schieferdecker. “Automated testing of automotive telematics systems using TTCN-3”. Proceedings of 3rd Workshop on Systems Testing and Validation. Paris, France. 2004 [8] I. Schieferdecker, J. Grossmann. “Testing of Embedded Control Systems with Continuous Signals”. Proceedings of 2nd Workshop on Modeling of Embedded Systems. Dagstuhl. Germany. 2006 [9] M. Li, A. Rennoch, I. Schieferdecker, D. Witaszek, O. Halabi, A. Vouffou, A. Yin, “Experience Report on Conformance Tests for CORBA ORBs”. Proceedings of Second Asia-Pacific Conference on Quality Software. Hong Kong. 2001 [10] I. Schieferdecker, G. Din, D. Apostolidis. “Distributed functional and load tests for Web services”. International Journal on Software Tools for Technology Transfer. Volume 7. Number 4. 2005 [11] I. Schieferdecker, A. Rennoch, E. Höfig. “TTCN-3 – A Test Technology for the Automotive Domain” Proceedings of Simulation und Test in der Funktions und Softwareentwicklung für die Automobilelektronik. Berlin. Germany. 2005 [12] I. Schieferdecker, A. Rennoch. “Industrial use of TTCN-3 – Scope and Limits”, International Conference on Software Tests. Düsseldorf. Germany. 2005 [13] G. Din, J. Zander, S. Pietsch. “Test exectution logging and visualization techniques”. Proceedings of Software and Systems Engineering and their Applications. Paris. France. 2004 [14] S. Schulz, T. Vassiliou-Gioles. “Implementation of TTCN-3 Test Systems using the TRI”, Proceedings of 14th International Conference on Testing Communicating Systems. Berlin. Germany. 2002 [15] I. Schieferdecker, T. Vassiliou-Gioles. “Realizing distributed TTCN-3 test systems with TCI”. Proceedings of 15th International Conference on Testing Communicating Systems. Cannes. France. 2003 [16] C. Willcock, T. Deiß, S. Tobies, S. Schulz, S. Keil, F. Engler. “An Introduction to TTCN-3”. Wiley. 2005 [17] J. Robbins. “Debugging Applications for Microsoft® .NET and Microsoft Windows®”. Microsoft Press. 2003 [18] R. M. Stallman, R. H. Pesch, S. Shebs. “Debugging with GDB“. GNU Press. 2003 [19] B. Tuthill, K.J. Dunlap, "Debugging with dbx". UNIX Programmer's Supplementary Documents, vol. 1. Computer Systems Research Group. Dept. of Electrical Eng. and Computer Science. Univ. of California. Berkeley. 1986
150
[20] M. Hof. “Post Mortem Debugger for Oberon”. Procedings of Joint Modular Languages Conference, Ulm. Germany. 1994 [21] В.А.Крюков, Р.В.Удовиченко. "Отладка DVM- программ". Программирование . 2001. № 3
151
Язык модификации данных формата XML функциональными методами Д.А. Лизоркин [email protected] Аннотация: Задача внесения модификаций в данные формата XML является актуальной по природе слабоструктурированных данных. До настоящего времени не был разработан такой язык модификации XML-данных, который бы выступал в роли общепризнанного мирового стандарта. В данной статье предлагается язык модификации XML-данных, в основу которого были положены функциональные методы программирования и язык функционального программирования Scheme. Функции, являющиеся в языках функционального программирования объектами первого класса, используются в предлагаемом языке модификации XML-данных в роли обработчиков для операций модификации, что позволяет достичь выразительной мощности и расширяемости набора операций модификации при сохранении синтаксической простоты языка. Производится анализ алгоритмической сложности выполнения операций предлагаемого языка и рассматриваются детали его реализации функциональными методами.
1. Введение Популярность языка разметки XML в качестве формата для хранения, передачи и обработки слабоструктурированных данных делает актуальной для практических приложений потребность в языке модификации XMLдокументов. Несмотря на актуальность данной задачи, доминирующее внимание консорциума W3C, которому принадлежит авторство большинства спецификаций платформы XML [1], в предыдущие годы было направлено на язык запросов из XML-документов [2], и работа над языком модификации XML-данных была начата в Консорциуме сравнительно недавно [3] и в настоящий момент находится в черновом статусе. В настоящей статье предлагается язык модификации XML-документов, основанный на подходе к обработке XML-данных функциональными методами, в частности, с помощью языка функционального программирования Scheme. Благодаря близкому соответствию иерархической структурой XML-документа вложенным спискам языка функционального 153
программирования Scheme и свойству функций Scheme быть объектами первого класса обеспечивается выразительная гибкость предлагаемого языка модификаций и минимизируется проблема потери соответствия [4] по сравнению с другими существующими языками модификации XMLдокументов. Подход к обработке XML-данных функциональными методами базируется на формате SXML — представлении XML-документов в виде вложенных списков, текстуально записываемых при помощи S-выражений. Языки SXML и XML могут рассматриваться как два синтаксически различных представления Информационного Пространства XML [5]. Для демонстрации соответствия между вложенными тегами XML и вложенными списками SXML, на рис. 1 приведен пример простого XML-документа и его представления на SXML. Каждая из информационных единиц Информационного Пространства XML представляется в виде S-выражения, первым членом которого является либо имя информационной единицы (для типов элемент и атрибут), либо служебное имя, предусмотренное для информационной единицы данного типа в грамматике SXML [6]. Заметим, что спецификация SXML обеспечивает поддержку пространств имен XML, и существует парсер, позволяющий для заданного XML-документа сконструировать его представление на SXML [7]. <doc> Text node <empty/>
(*TOP* (*PI* xml "version='1.0'") (doc (tag (@ (attr1 "value1") (attr2 "value2")) (nested "Text node") ) (empty) ))
Рис. 1: XML-документ (слева) и его представление в SXMLУниверсальность представления на SXML узлов всех типов обеспечивает простоту обработки документов формата SXML, а функции Scheme, обладающие свойством объектов первого класса, позволяют прозрачным образом интегрировать конструкции предлагаемого в настоящей статье языка модификации с пользовательскими действиями приложения. Необходимо сразу же отметить, что в функциональной парадигме программирования под модификацией данных понимается нечто отличное от модификации в контексте процедурной и объектно-ориентированной парадигм программирования. Чисто функциональный стиль подразумевает отсутствие в программе побочных эффектов, что исключает такие широко принятые в процедурном и объектно-ориентированном подходе операции как присваивание новых значений глобальным переменным или перестановка 154
указателей [9]. Модификация в функциональном стиле является операцией, не разрушающей исходные данные, и модификация исходного документа осуществляется за счет конструирования нового документа. Благодаря отсутствию в программе побочных эффектов, для конструирование модифицированного документа не требуется глубокое копирование исходного документа [8], но, как более подробно обсуждается в разделе 6, неизменные в результате модификации части документа используются в виде единой физической копии для его исходной и результирующей версий [9]. Приложение само решает, каким образом обрабатывать исходный и модифицированный документы. Например, приложение может использовать исходный документ лишь в локальном блоке своего кода, и тогда части, специфичные для исходного документа, будут освобождены автоматическим сборщиком мусора при выходе программы из данного локального блока [10]. Вопрос обеспечения работы нескольких конкурентных приложений над общим документом вкратце затрагивается в разделе будущих исследований, и его детальное исследование находится за пределами данной статьи. Дальнейшее содержание статьи организовано следующим образом. В разделе 2 дается обзор существующих языков модификации XML-данных и обсуждаются преимущества функционального подхода. В разделе 3 предлагается идея использование функций в качестве обработчиков для операций модификации, иллюстрируется выразительность данного подхода и показывается, как с помощью обработчиков могут быть выражены наиболее распространенные операции существующих языков модификации. В разделе 4 идея обработчиков расширяется возможностью согласованной обработки нескольких узлов документа, что позволяет естественно реализовать такие операции модификации, как перемещение поддеревьев. В разделе 5 вводится сокращенный синтаксис для наиболее употребительных конструкций предлагаемого языка модификаций. Раздел 6 посвящен деталям реализации предлагаемого языка модификаций функциональными методами программирования. Вопрос алгоритмической сложности вычисления запросов на модификацию предлагаемого языка рассматривается в разделе 7. Будущие исследования обсуждаются в разделе 8. Раздел 9 завершает статью.
2. Существующие языки модификации XML-данных До настоящего времени какого-либо единого стандарта языка модификации XML-данных не существовало. Так, разработанный консорциумом W3C язык запросов к XML-данным XQuery [2] в своей текущей версии является языком, предназначенным лишь для выборки данных и не предоставляющим никаких средств для внесения модификаций в XML-документы. С другой стороны, ввиду большого распространения технологии XML и естественности задачи внесения модификации в XML-данные для практических приложений, имеется ряд разработанных промышленных и академических языков для модификации XML-данных. 155
При анализе существующих языков модификации XML-данных в первую очередь необходимо отметить работу [11], в которой предлагается детально проработанный язык модификации с описанной формальной семантикой, который является расширением XQuery. По аналогии с понятием запроса в языке XQuery в языке модификаций, предлагаемом в [11], вводится понятие запроса на модификацию (update query). Запрос на модификацию состоит из одной или нескольких операций модификации (update expression), где каждая операция модификации производит некоторое базовое действие над обрабатываемым XML-документом. В работе [11] вводятся следующие виды операций модификации: • • • •
вставка нового узла в дерево документа; удаление узла; замена узла новым узлом; переименование узла.
Для выбора узлов XML-документа, подлежащих изменению данной операцией модификации, в [11] используется язык адресации структурных частей XML-документа XPath [12]. По семантике вычисления запроса на модификацию, операции модификации, входящие в запрос, независимы друг от друга: изменения, производимые в XML-документе одной операцией модификации, не учитываются при выборе узлов, подлежащих изменению другой операцией модификации. Несмотря на то что набор введенных в [11] операций модификации позволяет выразить любое необходимое изменение XML-документа [13], в языке отсутствует возможность расширения функциональности этих операций или добавления пользовательских операций. В настоящей статье семантика операций модификации определяется с помощью обработчика, представляющего собой произвольную функцию с заданной сигнатурой, благодаря чему достигается полная выразительная мощность языка модификаций из [11] и обеспечивается возможность для приложения по определению собственных операций модификации, равноправных с базовыми операциями вставки, удаления, замены и переименования узлов. Необходимо также отметить, что в языке модификаций, предложенном в [11], не предоставляется важной операция перемещения поддеревьев [13] и предлагается реализовывать ее, как комбинацию операций удаления и вставки узлов. Хотя операция перемещения действительно может быть реализована таким образом, в работе [11] не предоставляется никаких средств по обеспечению согласованности операций удаления и вставки при их комбинации с целью получения эффекта перемещения поддерева. В настоящей статье разработывается механизм зависимой обработки совокупности из нескольких узлов, благодаря которому достигается возможность согласованной модификации узлов, расположенных в разных частях документа, и, в частном случае, функциональность операции 156
перемещения поддеревьев. Разработанный сокращенный синтаксис языка модификаций предоставляет приложению возможность записи операции перемещения поддеревьев в виде наглядного выражения. Язык модификации XML-данных, предлагаемый в работе [14], также разработан как расширение к XQuery, и в нем используются богатые возможности XQuery по адресации частей обрабатываемого документа и конструированию новых узлов. Несмотря на всю мощность XQuery как языка запросов, при реализации на его основе языка модификаций авторам работы [14] потребовалось расширить язык XPath, являющийся составной частью XQuery, возможностью явной адресации значения конкретного атрибута. Проблема адресации отдельного значения атрибута с целью его модификации не возникает в SXPath — реализации XPath на языке функционального программирования Scheme, — поскольку SXPath позволяет обрабатывать элементы и атрибуты унифицированным образом ввиду их однородного способа представления на SXML [15]. В работе [14] вводится операция суб-модификации (sub-update), позволяющая производить поиск других узлов относительно целевого узла модификации и осуществлять для них рекурсивный вызов операции модификации. По своему назначению операция суб-модификации схожа с предлагаемым в настоящей работе механизмом зависимой обработки совокупности из нескольких узлов. При этом предлагаемый механизм обладает большей декларативностью по сравнению с операцией суб-модификации. Более декларативным является и предлагаемый в настоящей работе способ задания обрабатываемых узлов, подлежащих модификации. Для сравнения, в языке модификации, разработанном в [14], итерация по обрабатываемым узлам полностью перекладывается на прикладного программиста, что приводит к необходимости использования чрезмерно большого числа итеративных конструкций (for-update) в запросах на модификацию. Необходимо также отметить, что в работе [14] язык модификаций исследовался в контексте хранения XML-документов в реляционной базе данных, ввиду чего у авторов упомянутой работы возникли проблемы реализационного характера по поддержанию корректной контекстной позиции узлов при вставке нового содержимого между существующими данными [14]. В разрабатываемом группой XML:DB языке модификации XML-документов XUpdate [16] вводится набор операций, аналогичный уже рассмотренным, и для записи запросов на модификацию используется язык XML и специальное пространство имен. Язык XUpdate пока развит хуже по сравнению с рассмотренными выше работами [11] и [14], и до настоящего времени находится в черновом статусе разработки.
157
3. Операция модификации как обработчик узла Модификация на предлагаемом в данной статье языке выражается с помощью декларативного запроса, который мы по аналогии с введенной в [11] терминологией будем называть запросом на модификацию (update query). В контексте функциональных методов программирования можно говорить о том, что запрос на модификацию является атомарным в том смысле, что в реализации, детали которой будут обсуждаться в разделе 6, модификация осуществляется за один проход по дереву модифицируемого документа. Запрос на модификацию может состоять из одной или нескольких операций модификации, где операция модификации предназначена для выполнения некоторого базового действия над частью обрабатываемого документа. Каждая операция модификации адресуется к набору некоторых узлов в документе и выполняет модификацию каждого узла данного набора по отдельности. Анализируя семантику предлагаемых в [11] операций модификации, можно заметить, что любая из этих операций может быть в обобщенном виде представлена как пара значений: (выражение_XPath обработчик) ,
(1)
где выражение_XPath служит для выбора в обрабатываемом XMLдокументе набора узлов, подлежащих модификации, а обработчик специфицирует воздействие конкретной операции модификации на каждый из выбранных узлов. Обработчик может рассматриваться, как функция, которая получает на вход узел, подлежащий модификации, и возвращает результат воздействия конкретной операции модификации на данный узел. Предложенное представление операции модификации в виде пары значений получает естественную реализацию в терминах языка функционального программирования Scheme, поскольку в Scheme функции обладают свойством объектов первого класса. Функции как объекты первого класса могут, в частности, передаваться в качестве аргументов другим функциям, что позволяет нам рассматривать обработчик как черный ящик и, таким образом, абстрагироваться от внутренней реализации конкретного обработчика в дизайне языка модификаций. Рассматривая операцию модификации XML-данных в контексте языка функционального программирования Scheme и формата SXML, определим обработчик как функцию, имеющую следующую сигнатуру: (lambda (node) ...) ,
(2)
где node — это узел, подлежащий обработке с помощью данной операции модификации. Поскольку результат операции модификации естественным образом зависит от узла, подлежащего обработке, функция, введенная для представления обработчика, принимает данный узел в качестве аргумента. В 158
соответствии с тем значением, которое вернет обработчик, будет реализовываться та или иная операция модификации. Будем считать допустимыми возвращаемыми значениями обработчика либо единственный узел, либо набор узлов. В том случае, когда обработчик возвращает единственный узел, этот новый узел будет замещать собой в документе обрабатываемый узел (который был фактическим параметром для данного вызова обработчика). Если обработчик возвращает набор узлов, то этот набор узлов предполагается упорядоченным, и на место обрабатываемого узла в документе подставляются все узлы из полученного набора. В частности, если возвращаемый набор узлов пуст, то обрабатываемый узел удаляется из документа, и вместо него ничего не подставляется [17]. В соответствии с предложенным в данном разделе представлением операции модификации в виде пары значений, с помощью выражения_XPath будет выбираться набор узлов исходного документа, которые подлежат обработке в данной операции модификации. Каждый узел из выбранного набора рассматривается как обрабатываемый узел, и с ним, как с фактическим параметром, вызывается обработчик. Возвращаемый результат обработчика формирует новый узел или узлы, которые заменяют собой обрабатываемый узел. В частности, возвращаемый результат может включать в себя и сам обрабатываемый узел, что производит эффект добавления в дереве документа некоторых новых узлов к обрабатываемому узлу. Несмотря на простоту рассмотренной семантики обработчика и сопоставленного с ним выражения_XPath, предложенное представление операции модификации в терминах, приближенных к языку функционального программирования Scheme, обладает достаточно широкими возможностями для выражения разнообразных модификаций документов формата SXML. Далее в данном разделе рассматривается реализация с помощью идеи обработчиков некоторых наиболее употребительных операций модификации, затем обсуждаются возможности обработчиков при выражении более сложных операций модификации.
3.1. Реализация некоторых операций функциональными методами
модификации
В данном пункте показывается, как имеющиеся в работе [11] операции модификации получают естественное и лаконичное воплощение на языке функционального программирования Scheme при обработке документов в формате SXML.
3.1.1. Вставка нового узла в документ В работах [11] и [14] предлагается 3 разновидности операции вставки нового узла в документ: 159
• • •
вставка после (insert following), добавляющая новый узел сразу же после обрабатываемого узла таким образом, что они оба имеют общий родительский узел; вставка перед (insert preceding), добавляющая новый узел сразу же перед обрабатываемым узлом таким образом что они оба имеют общий родительский узел; вставка внутрь (insert into), добавляющая новый узел в качестве дочернего для обрабатываемого узла, последним по порядку документа (document order [12]).
В терминах SXML и функционального программирования эти операции вставки узла представляют собой простую комбинацию примитивов работы над списковыми структурами данных. Так, эффект “вставки перед” и “вставки после” достигается, когда обработчик формирует список, состоящий из добавляемого в документ нового узла и обрабатываемого узла. Соответственно, если в формируемом списке новый узел идет первым, то в дереве документа он будет добавлен перед обрабатываемым узлом (вставка перед), если вторым — то после обрабатываемого (вставка после). С целью параметризации обработчиков новым узлом, подлежащим вставке, обработчики реализуются как возвращаемый результат функции, которая и осуществляет необходимую параметризацию. Данный подход возможен благодаря тому, что функции Scheme являются объектами первого класса. Реализация обработчиков для “вставки перед” (insert preceding) и “вставки после” (insert following) приведена ниже: (define (insert-preceding new-node) (lambda (node) (list new-node node))) (define (insert-following new-node) (lambda (node) (list node new-node))) Заметим, что при параметризации каждой из записанных функций для нескольких разных значений нового узла new-node будет получено несколько разных обработчиков. Рассмотренный дизайн обработчиков по добавлению узлов в виде возвращаемых результатов функций удобен для приложения, поскольку подлежащий вставке новый узел new-node, как правило, известен на этапе формулирования операции модификации, тогда как обрабатываемый узел node становится известным только на этапе обработки дерева документа. При определении семантики операции “вставки внутрь” следует особо выделять случай, когда обрабатываемый узел является текстовым узлом, т.е. по определению не может содержать узлов, вложенных в него. Реакция на 160
подобную ситуацию может различаться в зависимости от нужд конкретного приложения; ниже показывается реализация, которая оставляет обрабатываемый текстовый узел без изменения: (define (insert-into new-node) (lambda (node) (if (not (pair? node)) ; текстовый node ; оставляем без изменения (append node (list new-node))))) В рассматриваемых далее в данном разделе примерах используются выражения модификаций, исходно предложенные в [11], и для них приводятся и обсуждаются аналоги на языке Scheme, записанные в терминах предлагаемой идеи обработчиков. Пример 1 Запрос, содержащий операцию модификации на “вставку перед”, в терминах синтаксиса, разработанного в [11], записывается в виде: UPDATE INSERT <warning>High Blood Pressure! PRECEDING //blood_pressure[systolic>180] В контексте предложенной в данной статье идеи использования обработчиков для выражения операций модификации, рассматриваемый запрос получает естественное эквивалентное воплощение на Scheme для обработки документов в форме SXML: (sxml:modify `("//blood_pressure[systolic>180]" ,(insert-preceding '(warning "High Blood Pressure!"))))
3.1.2. Удаление узла Операция удаления узла удаляет из документа обрабатываемый узел и все узлы, вложенные в него. Если рассматривать документ в виде дерева, то можно говорить о том, что в данной операции происходит удаление целого поддерева, корнем которого служит обрабатываемый узел. Выше в данном разделе при обсуждении семантики различных возвращаемых значений обработчиков было замечено, что эффект удаления узла достигается в том случае, когда обработчик возвращает в качестве результата пустой набор узлов. В терминах SXML пустой набор узлов представляется пустым списком языка Scheme, и поэтому удаление узла обеспечивает такой обработчик, который игнорирует обрабатываемый узел и всегда возвращает пустой список: (define delete (lambda (node) '())) Необходимо отметить, что в отличие от рассмотренных в предыдущем пункте обработчиков по вставке узла, обработчик для удаления узла не требует параметризации. Пример 2 Запрос на удаление узлов на языке модификаций, выработанном в работе [11], записывается следующим образом: UPDATE DELETE //blood_pressure[systolic>180] С использованием обработчика для удаления узла, эквивалентный запрос на модификацию документов формата SXML имеет на Scheme следующий вид:
Функция sxml:modify реализует запрос на модификацию, который может состоять из одной или более операций модификации. Операция модификации получает естественную нотацию в виде списка, состоящего из двух членов: выражения XPath и функции, играющей роль обработчика. Использованные при записи операции модификации выражения квази-цитирования (quasiquote, сокращенно обозначаемое символом "`") и снятия цитирования (unquote, сокращенно обозначаемое символом ",") показывают, что первый член списка представляет собой константное выражение, а второй член должен быть вычислен. Результатом функции sxml:modify является в свою очередь функция, которая и осуществляет обработку документа формата SXML. В полной аналогии с примером 1 реализуются примеры на “вставку после” и “вставку перед”, и единственное отличие заключается в использовании соответствующего обработчика для каждой из этих операций. 161
(sxml:modify `("//blood_pressure[systolic>180]" ,delete)) Заметим, что по сравнению с примером 1 в выражении языка Scheme поменялся только используемый обработчик. Благодаря гибкости предлагаемого подхода замена обработчика позволяет полностью изменить эффект производимой модификации.
3.1.3. Замена узла новым узлом Операция замены узла замещает обрабатываемый узел и всё его содержимое новым узлом, который задается в качестве параметра операции. Если рассматривать документ в виде дерева, то можно говорить о том, что в данной операции происходит замещение новым поддеревом целого поддерева, корнем которого служит обрабатываемый узел. Обработчик для замены узла новым узлом во многом напоминает рассмотренные ранее обработчики для вставки узла с тем упрощением, что 162
теперь обрабатываемый узел node полностью игнорируется, и возвращаемым значением обработчика является новый узел new-node: (define (replace new-node) (lambda (node) new-node)) Пример 3 Запрос на замену узла новым узлом на языке модификаций, разработанном в [11], имеет следующий вид: UPDATE REPLACE //job[.="bit banger"] WITH <profession>Comp. Scientist С помощью предлагаемой в данной статье идеи эквивалентный результат достигается на Scheme так:
обработчиков
(sxml:modify `("//job[.='bit banger']" ,(replace '(profession "Comp. Scientist"))))
3.1.4. Переименование узла Операция переименования узла изменяет имя узла и оставляет неизменным его содержимое. Операция переименования осмысленна для узлов, которые обладают именами, т.е. для элементов и атрибутов. При обработке XML-данных на языке Scheme элементы и атрибуты выражаются в виде S-выражений единообразным образом, и имя всегда является первым членом S-выражения, соответствующего данному элементу или атрибуту. В случае, когда обрабатываемый узел не имеет имени (например, для текстового узла), в зависимости от семантики конкретного приложения может требоваться различная реакция обработчика. Рассмотрим такой вариант обработчика, который оставляет текстовый узел без изменения: (define (rename new-name) (lambda (node) (if (pair? node) ; именованный узел (cons new-name (cdr node)) node))) По аналогии с операциями вставки узла и замены узла новым узлом обработчик для переименования узла реализован как возвращаемый результат функции, которая осуществляет параметризацию обработчика новым именем new-name. Благодаря параметризации можно получить для нескольких разных значений нового имени new-name несколько разных обработчиков, 163
каждый из которых определяет переименование обрабатываемых узлов на соответствующее новое имя. Приведенное тело обработчика может быть легко дополнено функциональностью, позволяющей для узла типа “инструкция обработки” переименовывать ее указание адреса (PI target) [19], что сделает семантику рассматриваемой операции переименования узла полностью совместимой с [11]. Пример 4 Переименование узла в терминах языка модификаций [11] записывается в виде: UPDATE RENAME //job[.='bit banger'] AS "profession" С использованием предлагаемой идеи обработчиков, эквивалентный запрос на модификацию может быть записан на Scheme так: (sxml:modify `("//job[.='bit banger']" ,(rename 'profession))) Здесь 'profession имеет тип данных символ, и этот тип данных используется в SXML для представления имен элементов и атрибутов.
3.2. Возможности языка Scheme по выражению операций модификации Помимо рассмотренных в предыдущем пункте примеров конкретных обработчиков для наиболее употребительных операций модификации, этими операциями далеко не ограничивается возможности, которые предоставляет приложению использование предложенной идеи обработчиков. При определении обработчиков и, тем самым, операций модификации над документами формата SXML приложению становятся доступны выразительная мощность языка программирования общего назначения Scheme и большой набор стандартных функций для работы со списковыми структурами данных. В частности, конструкторы различных типов узлов реализуются на Scheme конструкторами списков. Более того, наличие в языке Scheme выражений квази-цитирования (quasiquote) и снятия цитирования (unquote) позволяет компактным и наглядным образом комбинировать константные выражения и фрагменты вычисляемых выражений [18]. Аналогичные идеи используются в синтаксисе XSLT для комбинирования литеральных элементов результата [1] и исполняемых инструкций. Тело обработчика может включать в себя вызовы библиотеки SXPath — реализации языка XPath на Scheme, — что позволяет приложению 164
адресоваться к структурным частям обрабатываемого SXML-документа в соответствии со взаимоотношением этих частей с обрабатываемым узлом как контекстным узлом XPath. Примитивы map и filter языка Scheme обеспечивают приложение при работе с узлами SXML-документа возможностями итерирования и фильтрации последовательности узлов в терминах языка запросов к XMLдокументам XQuery. Арифметико-логические и условные выражения языка XQuery реализуются на Scheme соответствующими стандартными функциями. Благодаря возможности использования в теле обработчика произвольного выражения языка программирования общего назначения Scheme и интегрированности Scheme с узлами SXML-документа на уровне списковых структур данных, предложенная в данной статье идея обработчиков обеспечивает приложение выразительным и мощным механизмом для определения операций модификации.
4. Зависимая обработка нескольких узлов Хотя предложенные в предыдущем разделе обработчики узлов обеспечивают достаточно богатые возможности по выражению операций модификации документов, операции типа перемещения поддерева из одного места в документе на другое место обработчиками рассмотренного вида не покрываются. Это объясняется тем, что обработчик вида, рассмотренного в предыдущем разделе, является функцией только от одного обрабатываемого узла, тогда как операция перемещения затрагивает сразу два узла: исходное местонахождение поддерева и его желаемое новое положение. В обобщенном случае можно говорить о том, что обработчиками, рассмотренными в предыдущем разделе, не могут быть выражены такие операции модификации, которые включают в себя зависимую обработку сразу нескольких узлов, располагающихся в разнесенных друг от друга местах обрабатываемого дерева документа. Позиционирование операции перемещения поддерева как одной из базовых операций редактирования древовидных структур данных [13] мотивирует дальнейшее расширение предлагаемой в данной статье идеи обработчиков возможностью осуществлять перемещения поддеревьев1. Необходимо отметить, что в работе [11] операция перемещения не обеспечивается, и вместо этого предлагается имитировать ее как независимое последовательное применения операций удаления и вставки, причем никаких средств согласованного использования этих двух операций не предоставляется. 1
Название операции “перемещение поддерева” выбрано в соответствии с терминологией, введенной в работе [13]. В контексте предлагаемой в настоящей статье идеи обработчиков, корень поддерева, подлежащего перемещению, задается обрабатываемым узлом. 165
Предлагаемый в данной статье способ представления операции модификации в виде пары значений (выражение_XPath обработчик) по сути, есть не что иное, как способ сопоставления обработчиков и соответствующих обрабатываемых узлов. Поскольку в соответствии с семантикой сначала происходит сопоставление обработчиков с узлами, а затем за один проход по дереву документа осуществляется его модификация в соответствии с возвращаемыми значениями обработчиков, не имеет принципиального значения, является ли обработчик функцией только от одного узла или сразу от нескольких узлов. Теоретически, возможности функционального программирования и гибкость предлагаемого подхода не накладывают никаких принципиальных ограничений на количество узлов, которые могут быть обработаны вместе с помощью единого обработчика. С другой стороны, эксперименты с реализованными ранними прототипами показали, что функциональность, предоставляемая для зависимой обработки более чем двух разнесенных друг от друга по дереву документа узлов редко находит применение в практических задачах, но при этом значительно усложняет пользовательский интерфейс языка модификаций. В качестве сбалансированного решения в данной статье предлагается дальнейшее расширение идеи обработчиков возможностью зависимой обработки не более чем двух разнесенных друг относительно друга по дереву документа узлов. Предлагаемое расширение предоставляет приложению гибкие и мощные средства для формулирования практических операции модификаций к документу (в частности, операцию перемещения поддеревьев) и сохраняет простоту пользовательского интерфейса. Дополнительно, как будет показано в разделе 7, предлагаемый способ обработки обладает хорошими априорными свойствами в плане алгоритмической сложности вычислений, проводимых при модификации документа. Для обеспечения функциональности зависимой обработки двух узлов документа введем понятие базового узла.
4.1. Базовый узел Базовым узлом для операции модификации назовем такой узел, относительно которого вычисляется выражение_XPath данной операции модификации. Поскольку при помощи выражения XPath в операции модификации выбираются обрабатываемые узлы, можно говорить о том, что базовый узел определяет те узлы, которые выбираются, и, соответственно, обрабатываются в данной операции модификации. Как отмечалось в разделе 1, запрос на модификацию состоит, вообще говоря, из нескольких операций модификации, которые можно считать упорядоченными между собой. Считаем, что базовым узлом для первой по 166
порядку операции модификации в данном запросе на модификацию всегда является корневой узел обрабатываемого документа. Для последующих по порядку операций модификации корневой узел обрабатываемого документа служит базовым узлом в том и только том случае, если выражение_XPath данной операции модификации является абсолютным путем доступа (absolute location path) [12]. Если в операции модификации используется выражение_XPath другого вида, например, относительный путь доступа (relative location path), то в этом случае базовым узлом последовательно считается каждый из обрабатываемых узлов предыдущей операции модификации данного запроса на модификацию. Сформулированное правило сопоставления базового узла с конкретной операцией модификации хорошо согласуется с семантикой вычисления путей доступа в спецификации XPath: абсолютный путь доступа вычисляется относительно корневого узла документа, а относительный путь доступа — относительно контекстного узла контекста вычисления. Использование относительного пути доступа XPath в операции модификации связывает эту операцию с предыдущей, и вычисление набора узлов, подлежащих обработке, осуществляется способом, во многом напоминающем вычисление последовательности шагов доступа в языке XPath: • • •
рассматривается набор обрабатываемых узлов предыдущей операции модификации; для каждого узла из данного набора, как для базового узла, вычисляется выражение_XPath текущей операции модификации; получаемые в результате вычисления выражения_XPath узлы становятся обрабатываемыми узлами в текущей операции модификации.
Расширим сигнатуру обработчика (являющегося функцией в подходе, предлагаемом в данной статье) вторым параметром — базовым узлом операции модификации: (lambda (node base-node) ...) . (3) Рассмотренные в разделе 3 обработчики для наиболее употребительных операций модификации могут быть очевидным образом расширены на предмет наличия в сигнатуре обработчика нового аргумента base-node. Ввиду того, что базовый узел не влияет на результаты обработчиков для обсуждавшихся в разделе 3 наиболее употребительных операций модификации, базовый узел добавляется в эти обработчики просто в качестве фиктивного параметра. С другой стороны, из расширенной базовым узлом сигнатуры обработчика легко видеть, что теперь он позволяет осуществлять зависимую обработку сразу двух узлов. Выразительные возможности языка XPath по адресации 167
структурных частей XML-документа позволяют базовому узлу и обрабатываемому узлу располагаться в разных частях обрабатываемого документа. Функциональность зависимой обработки двух узлов, располагающихся в разнесенных местах документа, иллюстрируется в следующем подразделе реализацией упоминавшейся ранее в данном разделе операции перемещения поддерева.
4.2. Перемещение поддерева При наличии введенного понятия базового узла перемещение поддерева может быть реализовано совокупностью двух операций модификации. На основе рассмотренной семантики вычисления нескольких операций модификации узел – корень поддерева, подлежащего перемещению, – может выступать в роли обрабатываемого узла для первой из двух операций модификации и в роли базового узла — для второй операции. Эффект перемещения поддерева достигается тогда, когда первая операция модификации удаляет свой обрабатываемый узел из документа, а вторая операция вставляет в документ свой базовый узел (являющийся обрабатываемым узлом для предыдущей операции модификации). Поскольку обе операции являются компонентами единого запроса на модификацию, влияние этих операций на обрабатываемый документ атомарно и с точки зрения реализации осуществляется за один проход по дереву документа. Введенное правило вычисления базового узла естественным образом подходит для операции перемещения поддерева, потому что в практических задачах модификации новое местоположение перемещаемого поддерева часто задается относительно его исходного местоположения, и как раз при этом условии обрабатываемый узел первой из двух операций модификации, осуществляющих перемещение, становится базовым узлом для второй операции модификации. Поскольку перемещение поддерева включает в себя удаление поддерева в его старом местоположении и вставку поддерева в новое местоположение, при формализации соответствующих действий в виде обработчиков могут быть почти без изменений использованы обработчики, рассмотренные в разделе 3. В зависимости от различных способов вставки перемещаемого поддерева на его новое местоположение разумно различать такие варианты, как “перемещение перед”, “перемещение после” и “перемещение внутрь”, соответствующие аналогичным вариантам операции вставки узла. Пример 5 Предположим, что электронная версия некоторой книги состоит из элементов с именем глава (chapter), и каждая глава содержит элементы с именем параграф (para). Переместим последний параграф главы, имеющей заголовок “Введение” (“Introduction”), в следующую по порядку главу в качестве ее первого параграфа. 168
Запрос на желаемую модификацию реализуется следующей совокупностью из двух операций модификации, связанных друг с другом посредством базового узла: (sxml:modify `("/book/chapter[title='Introduction']/ para[last()]" ,(lambda (node base-node) '())) `("following::chapter[1]/para[1]" ,(lambda (node base-node) (list base-node node)))) Первая операция модификации выбирает параграф, подлежащий перемещению, и удаляет его по месту его исходного положения. Обрабатываемый параграф, удаленный из документа первой операцией модификации, тем не менее, является базовым узлом для второй операции модификации, поскольку во второй операции модификации в качестве выражения XPath используется относительный путь доступа. Вторая операция модификации осуществляет вставку перемещаемого параграфа – базового узла base-node – перед первым по счету параграфом главы, следующей за главой с заголовком “Введение”. Совокупный эффект обоих операций модификации рассматриваемого примера обеспечивает желаемое перемещение узла. Необходимо отметить, что обе операции модификации рассмотренного примера являются составляющими одного запроса на модификацию, и поэтому с точки зрения реализации, детали которой обсуждаются в разделе 6, выполняются за один проход по дереву обрабатываемого документа.
Предлагаемые правила сокращенного синтаксиса для наиболее употребительных операций модификации приведены в табл. 2. Для операций модификации, требующих параметризации обработчиков (например, для вставки или переименования узлов), необходимые дополнительные параметры в сокращенном синтаксисе записываются после ключевого слова, идентифицирующего операцию модификации. Необходимо также заметить, что каждая из операций перемещения поддерева, представленная в табл. 2 одним списком, переписывается в терминах полного синтаксиса в совокупность из двух операций модификации, связанных друг с другом посредством базового узла. При перезаписи из сокращенного синтаксиса в полный синтаксис дополнительный параметр операции перемещения поддерева становится первым членом пары, выражающей вторую из двух операций модификации, в совокупность которых переписывается данная операция перемещения. операция_модификации_в_сокращенном_синтаксисе ::= `( ,выражение_XPath delete ) | `(,выражение_XPath insert-following,добавляемый_узел_на_SXML)| `(,выражение_XPath insert-preceding,добавляемый_узел_на_SXML)| `(,выражение_XPath insert-into ,добавляемый_узел_на_SXML)| `(,выражение_XPath replace ,заменяющий_узел_на_SXML)| `(,выражение_XPath rename ,новое_имя_узла)| `(,выражение_XPath move-following ,выражение_XPath-новое_место_узла)| `(,выражение_XPath move-preceding ,выражение_XPath-новое_место_узла)| `(,выражение_XPath move-into ,выражение_XPathновое_место_узла)Табл. 2: Сокращенный синтаксис для наиболее
употребительных операций модификации
5. Сокращенный синтаксис для описания модификаций Используемое в предыдущих разделах представление операции модификации в виде пары (выражение_XPath обработчик) может рассматриваться как полный синтаксис записи операции модификации. Помимо полного синтаксиса, являющегося универсальным для широкого класса операций модификации, в данной статье также предлагается сокращенный синтаксис для записи некоторых наиболее употребительных операций модификации. Для рассмотренных выше операций вставки, удаления, переименования узла и т.п. предлагается идентифицировать каждую из этих операций своим ключевым словом, которое выражается типом данных “символ” языка Scheme. Благодаря быстроте операции сравнения в Scheme двух символов [20], при перезаписи запроса на модификацию из сокращенной формы записи в полную форму введенные ключевые слова можно быстро заменить сопоставленными им обработчиками. 169
Помимо уменьшения чисто текстуального размера запроса на модификацию, предлагаемый сокращенный синтаксис также обеспечивает большую наглядность благодаря тому, что позволяет избавиться от функций в теле запроса на модификацию, и вместо них используются более привычные строки, символы и списки. По сравнению с полным синтаксисом, сокращенный синтаксис обладает тем важным преимуществом, что может быть без потерь отображен на постоянное хранилище данных типа файла или на канал передачи данных, — тогда как тип данных функция, являющийся неотъемлемой частью полного синтаксиса, может существовать только во время вычисления программы, в оперативной памяти. Данное свойство сокращенного синтаксиса может быть полезно для таких областей применения предлагаемого языка модификации как генерация скрипта редактирования (edit script) в виде запроса на модификацию при определении различий между двумя деревьями [13]. 170
6. Реализация Предложенный в данной статье язык модификации XML-документов был реализован фукнциональными методами программирования на языке функционального программирования Scheme. Необходимо заметить, что функциональный стиль программирования подразумевает определенные правила, накладываемые на сделанную реализацию. А именно, чистый функциональный стиль запрещает использование в программе таких побочных эффектов, как изменение значений, однажды связанных с глобальными идентификаторами. В контексте выполненной реализации языка модификации функциональный стиль программирования означает сохранение в неизменном виде документа, подлежащего модификации, и конструирование нового документа, выражающего собой результат вычисление требуемого запроса на модификацию к исходному документу. В отличие от процедурной и объектно-ориентированной парадигм программирования, в которых модификация обычно реализуется как принудительное изменение исходных данных, функциональная парадигма программирования позволяет гарантировать, что данные, однажды связанные с идентификатором, останутся постоянными на протяжении всего времени, пока к этим данным есть доступ из какой-либо области программы [9]. Это свойство функционального программирования обладает тем важным преимуществом, что конструирование дерева модифицированного документа не требует глубокого копирования дерева исходного документа, и части, являющиеся общими для обоих деревьев, автоматически хранятся в единственной физической копии. Возможность использования физически разделяемого поддерева, общего для нескольких деревьев, иллюстрируется на рис. 2. Предположим, что некоторый запрос на модификацию затрагивает узел, обозначенный на рис. 2 буквой О. Тогда в результате выполнения запроса на модификацию будут переконструированы только те части дерева обрабатываемого документа, которые обозначены на рис. 2 жирными линиями, а остальные части дерева останутся неизменными и, таким образом, будут существовать в разделяемой физической копии для исходного и сконструированного деревьев. Из рис. 2 легко видеть, что модификация в SXML-документе некоторого узла затрагивает изменение этого узла и всех его узлов-предков. Поскольку на практике глубина XML-документов и соответствующих им SXML-документов обычно бывает небольшой, выполнение запроса на модификацию, выбирающего в документе лишь несколько узлов в качестве обрабатываемых, требует переконструирования только небольшой части модифицируемого документа. Узлы, остающиеся неизменными для исходной и модифицированной версий документа, существуют в единственной 171
физической копии, и обе версии документа адресуются к этим узлам по ссылкам.
Рис. 2: Пример дерева SXML-документа, в которым производится модификация узла. Буквой O обозначен обрабатываемый узел, жирными линиями выделены те части дерева, которые изменяются в результате модификации В зависимости от конкретной прикладной задачи, приложение, использующее функциональную реализацию предлагаемого языка запросов, само определяет, каким образом обрабатывать исходную и сконструированную новую версию модифицируемого SXML-документа. Например, приложение может использовать исходную версию документа только в локальном блоке своего кода, и тогда узлы, специфичные для исходной версии документа и не используемые в других местах прикладной программы, будут освобождены автоматическим сборщиком мусора [10]. Соображения по вопросу обеспечения конкурентной обработки общего SXML-документа несколькими приложениями обсуждается в разделе 8. В сделанной реализации вычисление запроса на модификацию SXMLдокумента осуществляется в следующие два этапа: •
172
На первом этапе все обработчики сопоставляются с соответствующими им обрабатываемыми узлами, т.е. для каждой операции модификации вычисляется выражение_XPath этой операции относительно соответствующего базового узла. Заметим, что выработанный способ задания базовых узлов позволяет не
соответствуют разные базовые узлы. Для обработчиков, относящихся к одной операции модификации, очередность их применения принимается совпадающей с очередностью, которую имеют соответствующие обработчикам базовые узлы в порядке документа [12], подлежащего модификации. Введенная очередность применения обработчиков позволяет ввести логичную и интуитивно ясную семантику операции перемещения поддеревьев. А именно, ситуации, когда с одним узлом ассоциировано несколько обработчиков одной и той же операции модификации, соответствует такой запрос на перемещение, при котором несколько разных поддеревьев перемещается на общее новое место. Например, если все сноски в электронном представлении некоторой книги перемещаются в главу “Приложение”, то в соответствии с принятым соглашением об очередности применения обработчиков сноски попадут в эту главу в точности в том порядке, в котором они встречались по тексту, что полностью согласуется с интуитивным представлением об ожидаемом результате перемещения.
вызывать ни одного обработчика при определении базовых узлов для всех операций модификации. •
На втором этапе осуществляется обход дерева SXML-документа, для обрабатываемых узлов вызываются обработчики, и в соответствии с возвращаемыми результатами обработчиков конструируется дерево нового SXML-документа, соответствующего результату выполнения запроса на модификацию над исходным документом.
При вычислении выражений XPath, производимом на первом этапе обработки запроса на модификацию, желательно для каждого узла, выбранного выражением XPath, получить также все его узлы-предки, поскольку, как отмечалось выше, предков обрабатываемого узла в новой версии SXMLдокумента необходимо переконструировать. Для получения предков для узлов, адресуемых с помощью выражения XPath, была выбрана реализация XPath функциональными методами, обеспечивающая хранение предков контекстного узла в контексте вычисления и, благодаря этому, оптимизирующая вычисление обратных осей XPath [8]. Упомянутая реализация XPath позволяет получить результат вычисления выражения XPath в форме наборов контекстов, которые содержат всех предков для каждого из выбранных узлов SXML-документа. Наличие известных предков каждого из обрабатываемых узлов позволяет организовать целенаправленный обход дерева SXML-документа на втором этапе обработки запроса на модификацию. А именно, благодаря тому, что корень дерева обрабатываемого документа является предком для каждого из обрабатываемого узлов, и обход дерева осуществляется сверху вниз, для любого поддерева можно сразу же установить, какие из обрабатываемых узлов располагаются внутри данного поддерева. Если в некотором поддереве не содержится ни одного обрабатываемого узла, то обход поддерева вниз может быть прекращен, потому что поддерево остается неизменным в результате выполнения запроса на модификацию. Необходимо отметить, что семантика предлагаемого языка модификаций не противоречит тому, чтобы с одним и тем же узлом документа было ассоциировано сразу несколько обработчиков. Когда имеется несколько обработчиков, ассоциированных с одним узлом, результатом обработки данного узла служит суперпозиция применения обработчиков, и принимается следующая очередность применения обработчиков к узлу: • •
Если какие-то два обработчика относятся к разным операциям модификации, то эти обработчики применяются в том порядке, в каком они записаны в запросе на модификацию; Если два обработчика относятся к одной операции модификации, то ввиду отсутствия совпадающих узлов в результате вычисления выражения XPath этим обработчикам с необходимостью 173
Выполненная реализация языка модификаций обеспечивает проверку корректности производимых модификаций в соответствии с условиями правильной сформированности XML-документов (XML well-formedness) [19]. В частности, производится проверка на отсутствие дублирующих атрибутов у данного элемента и атомарность значений атрибутов. По аналогии с языком запросов к XML-документам XQuery [2], сделанная реализация языка модификаций разрешает приложению в результате модификации элемента записывать его атрибуты и прочее содержимое в произвольном порядке. Поскольку в синтаксисе SXML атрибуты данного элемента должны быть вложены в общий список атрибутов, непосредственно следующий за именем элемента, реализация производит объединение списков атрибутов, если в результате модификации у данного элемента их получилось несколько, и переносит список объединенных атрибутов в позицию, непосредственно следующую за именем элемента и предшествующую всему прочего содержимому.
7. Алгоритмическая сложность В отношении алгоритмической сложности вычисления языка модификаций, предлагаемого в данной статье, можно сформулировать следующую теорему. Теорема 1 Введем следующие обозначения для характеристик запроса на модификацию и XML-документа, обрабатываемого с помощью данного запроса: n — количество узлов в обрабатываемом документе; m — количество операций модификации в запросе на модификацию; 174
kmax — максимальный размер выражения_XPath по всем операциям модификации [21]; T(n) — максимальное время работы каждого из обработчиков, присутствующих в запросе на модификацию. Время может зависеть от числа узлов в документе, например, в случае применения обработчика к корневому узлу документа; N(n) — суммарное количество новых узлов, добавляемых в документ в результате выполнения запроса на модификацию. Тогда время вычисления запроса на модификацию может быть оценено сверху следующим выражением: O( m·kmax·n6 ) + + m·n2·T(n+N(n)) + + O( (n+N(n))2·log2(n+N(n)) ) .
3.
Доказательство теоремы заключается в последовательном рассмотрении каждого из обозначенных этапов и получении верхней оценки времени вычисления по каждому этапу. 1.
(4) (5) (6)
Замечание 1 Рассмотренные в разделах 3 и 4 обработчики для наиболее употребительных операций модификации вычисляются за константное время. В справедливости данного замечания легко убедиться простым анализом реализаций рассмотренных обработчиков. Отметим, что те из обработчиков, которые требуют параметризации (например, узлом, добавляемым в документ, или новым именем узла), будучи параметризованы, выполняются за константное время. Сама параметризация относится к фазе статического анализа запроса на модификацию, равно как и разбор участвующих в запросе на модификацию выражений XPath. Замечание 2 Данной теоремой устанавливается гарантированное полиномиальное от размера документа и от количества операций модификации время вычисления произвольного запроса на модификацию, при условии, что T(n) и N(n) полиномиальны от своих аргументов, т.е. время выполнения каждого обработчика полиномиально от размера документа, и суммарное количество новых узлов, добавленных в документ в результате выполнения запроса на модификацию, полиномиально от размера исходного документа.
к этапу сопоставления обработчиков с узлами;
2.
к этапу обхода дерева документа и вызова обработчиков на обрабатываемых узлах; 175
Как обсуждалось в разделе 6, на этапе сопоставления обработчиков с узлами документа производится вычисление выражений_XPath всех операций модификации относительно базовых узлов этих операций. Ключевым моментом на данном этапе обработки является то, что в соответствии с семантикой предлагаемого в настоящей статье языка запросов максимальное количество базовых узлов для произвольной операции модификации не зависит от количества операций модификации в запросе на модификацию. Даже в том случае, когда все операции модификации в некотором запросе на модификацию связаны посредством базовых узлов (т.е. базовыми узлами каждой последующей операции модификации являются обрабатываемые узлы предыдущей операции модификации), максимальное количество базовых узлов для любой операции модификации не может превосходить общее число узлов в обрабатываемом документе, т.е. число n . Для вычисления выражений_XPath используется разработанная автором реализация XPath функциональными методами, которая включает в себя средства оптимизации [21], позволяющие получить гарантированную верхнюю оценку времени вычисления, равную: O( kmax·n5 ) . Выражения XPath вычисляются относительно каждого из базовых узлов каждой из операции модификации. Умножая время вычисления выражения XPath на количество базовых узлов (которых, как показано выше, не больше n для каждой операции модификации) и на количество операций модификации m, получим (4).
2.
Доказательство 1 (теоремы) Присутствующие в утверждении теоремы слагаемые, помеченные как (4), (5) и (6), относятся соответственно к следующим этапам вычисления запроса на модификацию: 1.
к проверке корректности модификаций, произведенных по результатам вычисления обработчиков.
176
На этапе обхода дерева SXML-документа производится вызов обработчиков на обрабатываемых узлах и формирование нового дерева в соответствии с возвращаемыми результатами обработчиков. Количество обработчиков, которое может быть ассоциировано с одним узлом SXML-документа, не превосходит суммарного количества базовых узлов по всем операциям модификации, т.е. произведения m·n . Поскольку по семантике предлагаемого языка запросов несколько обработчиков, ассоциированных с одним и тем же узлом, вызываются по правилам суперпозиции, на время выполнения одного обработчика влияют узлы, которые могли быть добавлены в результате вызовов
предыдущих по порядку обработчиков, и поэтому это время оценивается сверху выражением T(n+N(n)). Произведение максимального времени работы одного обработчика на максимальное количество ассоциированных с одним узлом обработчиков и на количество узлов в исходном документе и дает (5). 3.
Производимые проверки корректности сделанных модификаций уже не зависят от вида запроса на модификацию или количества операций модификации в нем, но зависят только от сконструированного результирующего SXML-документа. В терминах введенных в условии теоремы обозначений, данное соображение означает, что временная оценка этапа проверки корректности сделанных модификаций зависит только от числа (n+N(n)), т.е. от количества узлов в новом SXMLдокументе. Среди производимых проверок асимптотически доминирует проверка на отсутствие у данного элемента нескольких атрибутов с одинаковыми именами. Проверка на отсутствие совпадающих имен атрибутов у данного элемента производится с помощью известного алгоритма сортировки слиянием по именам атрибутов данного элемента. Время работы алгоритма сортировки слиянием оценивается выражением O( (n+N(n))·log2(n+N(n)) ) .
Ввиду того, что данная проверка производится для каждого элемента в результирующем SXML-документе, записанная оценка умножается на число узлов в документе, что окончательно дает (6). Сумма полученных временных оценок по каждому из этапов вычисления запроса на модификацию и обеспечивает утверждение теоремы.
8. Будущие исследования Объектом дальнейших исследований является предоставление полного набора свойств транзакций при модификации SXML-документов, и, прежде всего, обеспечение изоляции транзакций, осуществляющих конкурентную работу над общим SXML-документом. Ввиду того, что выполнение любого запроса на модификацию SXMLдокумента подразумевает переконструирование корневого узла дерева документа2, максимальный параллелизм транзакций, который представляется реальным обеспечить в выбранном подходе обработки XML-данных 2
данное утверждение справедливо для любого запроса на модификацию, который содержит по крайней мере один обрабатываемый узел 177
функциональными методами, выражается схемой “много читателей, один писатель”. В то время как одна транзакция осуществляет модификацию SXML-документа, остальные транзакции могут продолжать читать те части документа, которые не затрагиваются проводимой модификацией. Изолированность транзакций на уровне отсутствия чтения “грязных данных” [22] может быть реализована с помощью механизма блокировок древовидных структур [23] или мониторов Хоара. Целью текущих исследований является разработка таких методов изоляции транзакций, которые бы гарантировали отсутствие неповторяющихся чтений и проблемы фантомов, и которые бы в минимальной степени влияли на параллелизм транзакций.
9. Заключение Статья посвящена вопросу исследования и разработки языка модификации для XML-данных. Были проанализированы ограничения, присущие существующим языкам модификации XML-данных, и для преодоления этих ограничений были предложены функциональные методы программирования. При определении семантики действий, осуществляемой конкретной операцией модификации, было предложено использовать функции, обладающие в языке функционального программирования Scheme свойством объектов первого класса, в роли обработчиков для операций модификации. Представление XML-данных в виде списков формата SXML позволило — благодаря наличию в языке Scheme большого количества стандартных функций для работы со списками — в простой и лаконичной форме сформулировать обработчики для всех наиболее употребительных операций модификации. Для согласованного обеспечения операций модификации, подобных операции перемещения поддеревьев, была выработана более широкая возможность зависимой обработки сразу нескольких расположенных в разных местах документа узлов. Предложенная концепция зависимой обработки узлов естественным образом расширяет идею обработчиков благодаря введенному понятию базового узла операции модификации. Для возможности написания запросов на модификацию без явной передачи функций в тело запроса, был разработан сокращенный синтаксис для набора наиболее употребительных операций модификации. В отношении предложенного языка модификаций была сформулирована и формально доказана верхняя временная оценка, утверждающая гарантированное полиномиальное от размера документа и от количества операций модификации время вычисления любого запроса на модификацию, при условиях, что каждый обработчик выполняется за полиномиальное время и что суммарное количество новых узлов, добавляемых в документ в результате модификации, полиномиально от размера исходного документа. 178
Практическая применимость предложенных в статье идей была проиллюстрирована сделанной реализацией. Реализация использовалась при тестировании операций модификации в разрабатываемой в Институте системного программирования РАН XML-СУБД Sedna [24]. Список литературы [1] Когаловский М.Р. Глоссарий по стандартам платформы XML. Версия 7 (17-122006). // Электронные библиотеки. – М.: ИРИО, 2006. http://www.elbib.ru/index.phtml?page=elbib/rus/methodology/xmlbase/glossary_XML [2] The World Wide Web Consortium (W3C). XQuery 1.0: An XML Query Language : W3C Recommendation / Boag S. et al. (eds.). – 2007, 23 Jan. http://www.w3.org/TR/2007/REC-xquery-20070123/ [3] The World Wide Web Consortium (W3C). XQuery Update Facility 1.0 : W3C Working Draft / Chamberlin D. et al. (eds.). – 2007, 28 Aug. http://www.w3.org/TR/2007/WD-xquery-update-10-20070828/ [4] Лизоркин Д.А., Лисовский К.Ю. SXML: XML-документ как S-выражение // Электронные библиотеки. – М.: ИРИО, 2003. – Т. 6, вып. 2. – ISSN 1562-5419 = Russian digital libraries journal. http://www.elbib.ru/index.phtml?page=elbib/rus/journal/2003/part2/LK [5] The World Wide Web Consortium (W3C). XML Information Set : W3C recommendation / Cowan J., Tobin R. (eds.). – 2nd edition. – 2004, 4 Feb. http://www.w3.org/TR/xml-infoset/ [6] Kiselyov O. SXML specification / Rev. 3.0. – ACM SIGPLAN Notices. – New York: ACM Press, 2002. – Vol. 37, N 6. – pp. 52-58. – ISSN 0362-1340. http://okmij.org/ftp/Scheme/SXML.html [7] Kiselyov O. A better XML parser through functional programming : Proc. Practical Aspects of Declarative Languages: 4th int. symposium, PADL'2002, Portland, 1920 Jan., 2002 // Lecture Notes in Computer Science / Krishnamurthi S., Ramakrishnan C.R. (eds.). – Springer-Verlag Heidelberg, 2002. – Vol. 2257. – pp. 209-224. – ISSN: 0302-9743. http://www.okmij.org/ftp/papers/XML-parsing.ps.gz [8] Лизоркин Д.А. Оптимизация вычисления обратных осей языка XML Path при его реализации функциональными методами // Сборник трудов Института системного программирования РАН / Под ред. чл.-корр. РАН Иванникова В.П. – М.: ИСП РАН, 2004. – Т. 8, ч. 2. – 214 c. – с. 93-119. – ISBN 5-89823-026-2. http://modis.ispras.ru/Lizorkin/Publications/xpath-context.ps [9] Abelson H., Sussman G.J., Sussman J. Structure and Interpretation of Computer Programs. – 2nd ed. – London; New York: The MIT Press; McGraw Hill, 1996. – 657 pp. – ISBN 0-262-01153-0. http://mitpress.mit.edu/sicp/ [10] Boehm H.-J. Space efficient conservative garbage collection : Proc. conf. on Programming Language Design and Implementation (SIGPLAN'93), Albuquerque, NM, Jun. 1993 // ACM SIGPLAN Notices. – New York: ACM Press, 1993. – Vol. 28(6). – pp. 197-206; New York: ACM Press, 2004. – Vol. 39, issue 4. – pp. 490-501. – ISSN 0362-1340. http://citeseer.ist.psu.edu/259238.html [11] Lehti P. Design and Implementation of a Data Manipulation Processor for a XML Query Language : Ph.D. thesis. – 2001, Aug. www.lehti.de/beruf/diplomarbeit.pdf
179
[12] Консорциум W3C. Язык XML Path (XPath) версия 1.0 = XML Path Language (XPath) Version 1.0 : Рекомендация Консорциума W3C / Под ред. Clark J., DeRose S.; пер. с англ. Усманов Р. – 1999, 16 ноя. http://citforum.ru/internet/xpath/index.shtml [13] S. S. Chawathe, A. Rajaraman, H. Garcia-Molina and J. Widom. Change Detection in Hierarchically Structured Information. Technical report; detailed version of paper appearing in SIGMOD 1996. http://www-db.stanford.edu/c3/papers/html/tdiff3-8/tdiff3-8.html [14] Igor Tatarinov, Zachary G. Ives, Alon Y. Halevy, Daniel S. Weld. Updating XML : Proc. ACM SIGMOD int. conf. on Management of Data (SIGMOD'01), Santa Barbara, California, 21-24 May, 2001 // SIGMOD Conference / Aref W.G. (ed.). – New York: ACM Press, 2001. – pp. 413-424. – ISBN 1-58113-332-4. http://citeseer.comp.nus.edu.sg/603731.html [15] Лизоркин Д.А., Лисовский К.Ю. Язык XML Path (XPath) и его функциональная реализация SXPath // Электронные библиотеки. – М.: ИРИО, 2003. – Т. 6, вып. 4. – ISSN 1562-5419 = Russian digital libraries journal. http://www.elbib.ru/index.phtml?page=elbib/rus/journal/2003/part4/LL [16] A. Laux, L. Martin. XUpdate update language : XML:DB Working Draft. – 14 Sep, 2000. http://xmldb-org.sourceforge.net/xupdate/xupdate-wd.html [17] Лизоркин Д.А., Лисовский К.Ю. Реализация XLink – языка ссылок XML – с помощью функциональных методов // Программирование. – М.: Наука, 2005. – N 1. – С. 52-72. – ISSN 0361-7688 = Programming and computer software. [18] Лизоркин Д.А. Язык запросов к совокупности XML-документов, соединенных при помощи ссылок языка XLink // Сборник трудов Института системного программирования РАН / Под ред. чл.-корр. РАН Иванникова В.П. – М.: ИСП РАН, 2004. – Т. 8, ч. 2. – 214 c. – с. 121-153. – ISBN 5-89823-026-2; Программирование. – М.: Наука, 2005. – N 3. – ISSN 0361-7688 = Programming and computer software. [19] The World Wide Web Consortium (W3C). Extensible Markup Language (XML) 1.0 (Fourth Edition) : W3C Recommendation / Bray T. et al. (eds.). – 2006, 16 Aug. http://www.w3.org/TR/2006/REC-xml-20060816 [20] Лизоркин Д.А., Лисовский К.Ю. Пространства имен в XML и SXML // Электронные библиотеки. – М.: ИРИО, 2003. – Т. 6, вып. 3. – ISSN 1562-5419 = Russian digital libraries journal. http://www.elbib.ru/index.phtml?page=elbib/rus/journal/2003/part3/LL [21] Лизоркин Д.А. Функциональные методы обработки XML-данных / Ph.D. thesis = Диссертация на соискание ученой степени кандидата физико-математических наук по специальности 05.13.11. – 2005, 11 ноя. [22] Кузнецов С.Д. Современные технологии баз данных : Годовой курс для студентов 3-го курса. – Центр Информационных Технологий. http://citforum.ru/database/osbd/contents.shtml [23] Гарсиа-Молина Г., Ульман Дж.Д., Уидом Дж. Системы Баз Данных. Полный курс / Пер. с англ. Варакина С.А.; под ред. Слепцова А.В. – М,: Вильямс, 2003. – 1088 с.: ил. – ISBN 5-8459-0384-X (рус.) [24] Гринев М., Кузнецов С.Д, Фомичев А. XML-СУБД Sedna: технические особенности и варианты использования // Открытые системы. – 2004, вып 8. http://www.osp.ru/os/2004/08/185085/
180
Нечеткое сравнение коллекций: семантический и алгоритмический аспекты В.А. Семенов, С.В. Морозов, О.А. Тарлапан, И.В. Энкович1 Аннотация. Рассматривается задача нечеткого сравнения коллекций в приложениях реконсиляции. Задача возникает при оптимистической репликации структурированных данных и документов и имеет многочисленные приложения в таких актуальных областях, как управление конфигурацией программного обеспечения, управление мобильными базами данных, построение платформ и систем коллективной инженерии. В работе анализируются стандартные типы коллекций языков объектноориентированного моделирования, для которых описываются и обосновываются способы представления, журнализации, вычисления, принятия и согласования изменений. Для выделенных типов коллекций дается строгая, семантически содержательная интерпретация конфликтов и предлагаются конструктивные методы их идентификации и разрешения. Примеры иллюстрируют предлагаемые решения, а также служат мотивацией создания универсальной, основанной на модельном представлении оригинальной среды коллективной инженерии с развитыми возможностями семантически корректной и функционально содержательной реконсиляции дивергентных реплик данных.
1. Введение Сравнение коллекций является одной из традиционных задач сопоставления версий и анализа изменений при оптимистической репликации структурированных данных и документов [1]. Программные средства сравнения, предоставляющие подобную функциональность, являются неотъемлемыми компонентами современных систем контроля версий. Подобные системы нашли применение в задачах управления конфигурацией программного обеспечения (software configuration management), управления мобильными базами данных (mobile database management), организации цифровых архивов, документооборота (document management), построения платформ и систем коллективной инженерии (collaboration workspaces, concurrent engineering environments), управления web–контентом (content management). Так, среди наиболее популярных решений коллективной 1
Работа коллектива поддержана РФФИ (грант 07-01-00427) 181
программной инженерии следует отметить системы управления версиями ПО: CVS, Subversion, OpenCM, BitKeeper, Visual SourceSafe, Perforce, Synergy CM и т.п. [2]. Оформленные в виде программных утилит с пользовательским интерфейсом средства сравнения имеют многочисленные самостоятельные приложения. Например, утилиты командной строки cmp, diff, sdiff, diff3 известного проекта GNU [3] позволяют определить первый отличный байт для заданной пары файлов, вычислить минимальное множество отличий между двумя файлами, интерактивно объединить два файла в один общий, а также представить структуру изменений при одновременной модификации одного файла двумя пользователями или программами и сгенерировать файл, являющийся результатом слияния двух версий относительно базовой с предупреждениями о возникших конфликтах. GNU Diffutils работают преимущественно с текстовыми файлами, для бинарных файлов они могут лишь констатировать факт сходства или различия. Пакет JojoDiff включает набор утилит для сравнения бинарных файлов: jdiff — сравнивает два файла, jpatch — реконструирует второй файл из первого на основе списка изменений, сгенерированного jdiff, jsync — синхронизирует файлы или директории между двумя компьютерами. Приложения KDiff, Xxdiff, gtkdiff, tkdiff, Diffstat, ExamDiff предоставляют средства визуального сравнения произвольных текстовых файлов. Известная утилита UN*X dirdiff позволяет сравнивать деревья каталогов и синхронизировать изменения между ними. Развитые графические средства сравнения текстовых файлов, а также синхронизации директорий включает в себя популярный файловый менеджер TotalCommander. Перечисленные программные средства производят сравнение произвольных текстовых или бинарных файлов без учета семантики их содержимого. Ряд программных утилит и приложений позволяет сравнивать документы в различных текстовых или бинарных форматах, HTML, XML, Multimedia данные. В частности, учитывая типизацию отдельных термов в текстовых данных, утилита spiff позволяет адекватно сравнивать числовые данные с плавающей точкой, а также исходные тексты на популярных языках программирования. Набор приложений CSDiff/CS-ExcelDiff/CS-HTMLDiff позволяет установить и отобразить изменения документов, представленных в форматах Microsoft Word, Microsoft Excel, HTML. Программы ExamDiff Pro, Compare Suite, Diff Doc поддерживают сравнение документов в различных форматах популярных офисных приложений, PDF, HTML, в том числе документов, хранящихся в архивах. Интегрированные пакеты Microsoft Office 2003 и 2007 имеют развитые встроенные средства сравнения и слияния версий документов. Наборы средств diffxml, 3dm, XMLComparator обеспечивают сравнение, коррекцию и слияние документов в XML разметке. Программы Image Comparer, ImageDupeless специализируются на сравнении графической, а Similarity — звуковой информации, хранимой в файлах наиболее распространенных форматов. 182
Средства сравнения реляционных данных позволяют определить изменения схем и содержимого популярных баз данных. В частности, программа DataDiff находит отличающиеся записи в альтернативных базах данных под управлением MySQL, утилита mysqldiff — находит отличия в определениях таблиц СУБД MySQL, утилита MDBDiff позволяет выявить структурные изменения в базах данных Microsoft Access, Oracle SchemaDiff — изменения в схемах Oracle, pgdiff — изменения в реляционных таблицах двух баз данных под управлением PostgreSQL с возможной генерацией команд конвертации схемы одной базы данных в другую. Универсальная программа с webинтерфейсом SQLDiff устанавливает и показывает отличия между двумя SQL таблицами для распространенных реляционных СУБД (Oracle, DB2, PostgreSQL). Ряд программных компонентов сравнения обеспечивает работу с объектными моделями. В частности, библиотека difflib, реализованная на Python, позволяет вычислять изменения в объектных данных. Программный модуль UMLDiff [4], функционирующий в среде программной инженерии Eclipse, определяет структурные изменения статических UML моделей. Более подробные сведения о вышеперечисленных утилитах и библиотеках, а также об аналогичных программных средствах сравнения данных и документов можно найти в Интернет-обзорах и каталогах [5–8]. Несмотря на разнообразие приложений, в которых математические методы и программные средства сравнения находят содержательное применение, следует отметить строгую функциональную специализацию и определенную ограниченность перечисленных выше средств. Главной причиной этого является ориентация на конкретные классы приложений с предопределенной семантикой. Безусловно, подобная направленность делает данные средства чрезвычайно полезными при решении конкретных прикладных задач, однако не дает возможность распространить их на нетривиальные масштабные междисциплинарные информационные модели, описываемые сотнями (иногда тысячами) типами данных и ограничений. Более того, часто и в простых случаях результаты сравнения оказываются неадекватными решаемой задаче. Например, сравнение документов в разметке XML позволяет идентифицировать изменения, связанные с появлением, удалением, модификацией элементов, однако не фиксирует изменения положения элементов относительно друг друга. Приложения, для которых порядок следования элементов данных существенен, не могут доверительно использовать результаты сравнения. Например, приложение, фиксирующее изменения в рейтинговом списке ученых, упорядоченном в соответствии с показателем результативности научной деятельности, не может корректно интерпретировать результаты сравнения и нуждается в более адекватном способе представления изменений в соответствии с семантикой модели данных. 183
В обобщенной постановке задача сравнения обычно не рассматривается, хотя все упомянутые выше виды документов, файлов, схем баз данных могли бы быть прозрачно представлены информационными моделями/метамоделями, и их сравнение могло бы быть проведено на более общей методологической и инструментальной основе. Подобная постановка является критично важной для приложений семантической реконсиляции, оперирующих произвольными типами данных при заданной прикладной модели с формально описанными структурой и алгебраическими ограничениями. В подходе, предложенном и развитом в наших работах [9–12], вычисление изменений данных рассматривается в качестве ключевого элемента реконструкции конкурентных транзакций и их семантически согласованной реконсиляции на основе заданной информационной модели. Учет семантики модели позволяет на формальной, математически строгой основе провести сравнительный анализ реплицируемых данных и выработать непротиворечивые (семантически корректные) и содержательные (обеспечивающие полноту итоговой транзакции) политики реконсиляции. В определенном смысле подход следует важному доминирующему направлению информационных технологий, предполагающему активное использование моделей на всех этапах программной инженерии и анализа информации. Деятельность международных сообществ по разработке соответствующих информационных стандартов и многофакторных прикладных моделей, прежде всего, в рамках ISO 10303 STEP [13] и OMG MDA [14] также следует этой тенденции. В настоящей работе обсуждаются проблемы сравнения коллекций — стандартных типов данных, поддерживаемых популярными языками моделирования и программирования, повсеместно используемых в приложениях и представляющих собой наиболее сложные и интересные конструкции для рассматриваемых задач реконсиляции. Главным лейтмотивом статьи является вопрос об адекватности способов представления изменений типам коллекций, а также о выборе эффективных алгоритмов нечеткого сравнения коллекций в соответствии с их семантикой.
2. Задача нечеткого сравнения семантической реконсиляции
в
приложениях
Обсудим особенности постановки задачи нечеткого сравнения коллекций в приложениях семантической реконсиляции. Напомним, что традиционная задача сравнения последовательностей обычно формулируется как задача отыскания минимального скрипта редактирования (последовательности элементарных команд, обеспечивающей преобразование исходной строки в заданную другую строку) [20, 21]. Множество найденных команд при соответствующей интерпретации может служить представлением изменений, внесенных в модифицированную версию коллекции относительно исходной. Отыскание наибольшей подпоследовательности также решает задачу 184
нечеткого сравнения и позволяет представить изменения модифицированной коллекции в виде добавленных и удаленных фрагментов строк, дополняющих найденную общую подпоследовательность до заданных строк. В отличие от традиционных постановок задач сравнения последовательностей, приложения реконсиляции оперируют с произвольными типами коллекций, и для адекватной реконструкции изменений требуется детальный семантический анализ. Другой важной особенностью рассматриваемой постановки является необходимость декомпозиции долгих транзакций на группы операций, которые могли бы быть представлены и применены независимым друг от друга образом. Это означает, что в ходе реконсиляции одна часть выявленных изменений может быть принята в итоговом представлении при отмене другой без каких-либо дополнительных ограничений. Выбранный базис элементарных операций должен удовлетворять данному требованию. Итак, пусть X , X ′, X ′′ ∈ collection < T > — некоторые версии коллекции элементов типа T, причем X — базовая версия, а X ′ , X ′′ — версии, полученные в результате ее одновременной модификации в двух параллельных ветвях. Задача реконсиляции в наиболее распространенной постановке заключается в вычислении соответствующих изменений модифицированных версий относительно базовой Δ ′ = Diff ( X ′, X ) , Δ ′′ = Diff ( X ′′, X ) и в консолидации изменений Δ* = Merge(Δ ′, Δ ′′) таким образом, чтобы сформировать итоговое представление коллекции X * = Apply ( X , Δ* ) как результат применения согласованных изменений к базовой версии. Это, так называемая, классическая “3-way Merge” схема реконсиляции в отличие от схем “2-way Merge” и “4-way Merge”, имеющих более узкое применение [15, 16]. Например, если при одновременной модификации текстового файла в ходе выполнения одной транзакции была вставлена новая строка, а в ходе выполнения другой транзакции одна из строк была удалена, результатом консолидации изменений должен стать итоговый документ с внесенными общими изменениями (см. рис. 1), дополняющий его существующие версии новым согласованным вариантом. Корректная идентификация изменений и реализация функции исполнения предполагают, что соответствующие тождества X ′ = Apply ( X , Δ ′) , X ′′ = Apply ( X , Δ ′′) необходимо удовлетворены.
185
Merged document Line1 New Line1 Line3
Version1 Line1 New Line1 Line2 Line3
Version2 Line1 Line3
Original document Line1 Line2 Line3
Рис. 1. Пример консолидации изменений в итоговом текстовом документе Тривиальными случаями реализации функции реконсиляции являются Merge(Δ ′, Δ ′′) = ∅ , Merge(Δ ′, Δ ′′) = Δ ′ и Merge(Δ ′, Δ ′′) = Δ ′′ , приводящие к уже известным версиям коллекции X , X ′ и X ′′ соответственно. Содержательная реализация функции реконсиляции Merge(Δ ′, Δ ′′) нетривиальна, поскольку Δ ′ , Δ ′′ могут представлять собой иерархически структурированные изменения Δ ′ = {δ 1′ , δ 2′ ,...δ n′′ } , Δ ′′ = {δ 1′′, δ 2′′ ,...δ n′′′′ } , δ i′ = δ i′,1 , δ i′, 2 ,...δ i′,ni′ ,
{
δ i′′ = δ i′′,1 , δ i′′, 2 ,...δ i′′,ni′′
}
{
и т.д. По существу, дельты
Δ′ ,
}
Δ ′′
являются
реконструируемым представлением конкурентных транзакций, примененных к исходным данным X и имеющих результатом X ′ и X ′′ соответственно. В силу указанных причин реализация данной функции должна обеспечивать консолидацию как можно большего числа изменений при условии сохранения частичного порядка операций, индуцируемого семантикой приложений, и их бесконфликтности. В случаях, когда все конфликты разрешаются путем принятия изменений из первой версии, функция реконсиляции строится следующим образом: Merge(Δ ′, Δ ′′) = {δ ′ | δ ′ ∈ Δ ′} ∪ {δ ′′ | δ ′′ ∈ Δ ′′ & ¬isConflict (δ ′′, δ ′), δ ′ ∈ Δ ′} , 186
где логическая функция isConflict (δ ′′, δ ′) истинна в случае конфликта между изменениями δ ′ , δ ′′ . Является открытым вопрос о том, какие ситуации следует считать конфликтными, и каким образом они могут быть разрешены: отменой всех операций, выделением и принятием бесконфликтного подмножества операций, или путем их коррекции. В дальнейшем под конфликтом будем понимать бинарное или множественное отношение между наборами или последовательностями операций в δ i′1 , δ i′2 ,..., δ i′n′ ∈ Δ ′ , конкурентных транзакциях δ ′j′1 , δ ′j′2 ,...δ ′j′n′′ ∈ Δ ′′ ,
Merged document1 Line1 New Line1 New Line2 Line2 Line3
Merged document2 Line1 New Line2 New Line1 Line2 Line3
одновременное принятие которых приводит к некорректной интерпретации операций и неоднозначности результата, или к нарушению семантических ограничений, накладываемых на результирующее представление данных X * = Apply ( X , Δ* ) , где Δ* = δ i′1 , δ i′2 ,..., δ i′n′ , δ ′j′1 , δ ′j′2 ,...δ ′j′n′′ .
Version1 Line1 New Line1 Line2 Line3
Version2 Line1 New Line2 Line2 Line3
{
}
Стандартный способ разрешения конфликта состоит в принятии одной из опций Options = ∅; δ i′1 , δ i′2 ,..., δ i′n′ ; δ ′j′1 , δ ′j′2 ,...δ ′j′n′′ . При условии, что ориги-
{
}
нальные транзакции приводят к корректным версиям X ′ и X ′′ , а принимаемые операции не конфликтуют друг с другом, результирующая транзакция также будет корректна. В случае выявления конфликтов они разрешаются вплоть до достижения корректности итоговой транзакции. Заметим, что решение всегда существует, поскольку в качестве итоговой транзакции могут быть приняты ∅, Δ ′ или Δ ′′ . Однако более содержательным была бы консолидация операций из обеих конкурентных транзакций. Важной особенностью задачи нечеткого сравнения коллекций в приложениях реконсиляции является учет частичного порядка между операциями. Например, если при одновременном редактировании текста в одну и ту же позицию были добавлены отличные строки, то конфликт связан не с нарушением какого-либо ограничения для результирующего документа, а с неоднозначностью исполнения соответствующих операций δ ′ , δ ′′ и с неопределенностью ожидаемого результата X * = Apply ( X , Merge(δ ′, δ ′′)) . Возможным способом разрешения конфликта в данном случае являлась бы одна из опций: Options = {∅; δ ′; δ ′′; δ ′∠δ ′′; δ ′′∠δ ′} , где символ ∠ означает отношение предшествования между исполняемыми операциями. Результатом может стать исходный документ, одна из его модифицированных версий либо документ с двумя возможными вариантами вставки строк (см. рис. 2). Заметим, что совпадение добавленных строк в обеих версиях модифицируемого документа не приводит к конфликту, и вариантами реконсиляции являются тривиальные решения Options = {∅; δ ′; δ ′′} , приводящие к уже существующим версиям документа.
187
Original document Line1 Line2 Line3
Рис. 2. Пример многовариантной реконсиляции с учетом частичного порядка операций редактирования текстового документа Приведем вариант предыдущего примера редактирования текстового документа, иллюстрирующего важность учета семантики модели данных при дополнительном ограничении уникальности элементов коллекции. Предположим, что документ представляет собой персонофицируемый список имен, формируемый путем согласования альтернативных рейтинговых показателей в параллельных версиях (см. рис. 3). В первой модифицируемой версии документа между персонами на первых двух позициях вставляется новое имя, после чего меняется порядок их следования. Дельта представляется следующим образом: Δ ′ = {δ 1′ (insert , " NewPerson" ,2), δ 2′ (transpose,1,3)} , где δ1′ — операция вставки нового элемента в соответствующую позицию коллекции, а δ 2′ — операция транспозиции пары элементов в заданных позициях коллекции. Во второй версии документа то же самое имя вставляется на позицию между последними персонами, а также меняется порядок их следования: Δ ′ = {δ 1′′(insert , " NewPerson" ,3), δ 2′′ (transpose,2,4)} . Заметим, что изменение порядка следования элементов коллекции и вставка новых элементов согласно репродуцируемой семантике множества не должна приводить к дублированию 188
имен в итоговом документе. Поэтому семантически корректными являются результаты Options = {∅; δ 1′∠δ 2′ ; δ 1′′∠δ 2′′ ; δ 1′; δ 2′ ; δ 1′′ ; δ 2′′ ; δ 1′∠δ 2′′ ; δ 2′′∠δ 2′ } , приводящие, соответственно, к оригинальным версиям Original document, Version1, Version2, версиям Version1-1, Version1-2, Version2-1, Version2-2, полученным частичным принятием операций одной из транзакций, и версиям документа Merged document1, Merged document2, полученным возможной консолидацией операций из двух конкурентных транзакций. Version1-1 Person2 Person1 Person3
Version1-2 Person1 New Person Person2 Person3
Version2-1 Person1 Person3 Person2
Merged document1 Person1 New Person Person3 Person2
Merged document2 Person2 Person1 New Person Person3
Version1 Person2 New Person Person1 Person3
Version2 Person1 Person3 New Person Person2
Version2-2 Person1 Person2 New Person Person3
Original document Person 1 Person 2 Person 3
Рис. 3. Пример многовариантной реконсиляции с учетом семантики модели коллекции
3. Классификация коллекций Коллекции — фундаментальный тип данных, поддерживаемый популярными языками моделирования и программирования для спецификации и реализации в приложениях агрегированных структурированных данных. Как иллюстрируют примеры предыдущего раздела, сравнение коллекций должно проходить в строгом соответствии с их семантикой. В противном случае результаты рискуют быть неадекватными исходной проблеме и теряют смысл для целевого приложения и пользователя. 189
Сравнение коллекций может рассматриваться в качестве частной задачи более общей проблемы семантического сопоставления (matching) и сравнения (differencing) расходящихся реплик структурированных данных, например, популяций объектов, заданных некоторой объектно-ориентированной моделью. Несмотря на многообразие частных типов коллекций, встречаемых в приложениях, можно выделить несколько фундаментальных свойств, в соответствии с которыми их анализ может проводиться содержательным образом. К таким свойствам мы относим уникальность элементов коллекции, упорядочение, возможную сортировку элементов коллекции, а также ограниченный размер коллекции. Декларативные языки объектно-ориентированного моделирования, как правило, предоставляют некоторый набор абстрактных или конкретных типов коллекций с априори заданным набором семантических свойств. В производных пользовательских типах, определяемых на основе базовых, семантика коллекций может быть сохранена или уточнена. Однако выделенные фундаментальные свойства остаются принципиальными для содержательного анализа коллекций. Так, декларативный язык ограничений OCL [17] определяет абстрактный интерфейс коллекций Collection и четыре конкретных класса Bag, Set, Sequence и OrderedSet для представления мультимножеств, множеств, последовательностей и упорядоченных множеств соответственно. Все виды коллекций задаются в обобщенном виде с возможностью параметризации типом элементов. Set — коллекция, по семантике соответствующая математическому понятию множества. Она не допускает дупликации элементов. OrderedSet — специализация данного типа для упорядоченных множеств. Ограничение уникальности необходимо поддерживается данным типом коллекции. Bag — мультимножество с возможным повторением элементов. Sequence — упорядоченное мультимножество или последовательность, допускающая повторение элементов. Таким образом, виды коллекций OCL могут быть классифицированы в соответствии с таблицей 1. Язык моделирования EXPRESS [18] предоставляет иной набор типов коллекций, а именно: Aggregate, Bag, Set, Array и List. Абстрактный тип Aggregate определяет базовый набор методов оперирования с элементами коллекций. Bag — специализация данного типа для представления мультимножеств. Set — специализация типа Aggregate для произвольных множеств, исключающая дупликацию элементов и игнорирующая их порядок. Тип данных List применяется для представления последовательностей. Допустимое количество элементов в списках, множествах и мультимножествах задается дополнительными ограничениями. Коллекции имеют строго фиксированный размер в тех случаях, когда нижний и верхний пределы их размера совпадают. Array — специализация типа Aggregate для массивов фиксированной длины. С учетом индексации порядок элементов в 190
массивах имеет существенное значение. Возможна организация разреженных массивов с неустановленными значениями элементов при помощи специального спецификатора. Также возможно определение производных типов массивов и списков с наложенным ограничением уникальности элементов. Базовые типы коллекций, предоставляемые языком моделирования EXPRESS, приведены в таблице 1. UNIQUE
ORDERED
SORTED
FIXED
+ + -
+
-
+ + -
+
+
-
-
-
+
-
+
+
+
-
+
-
+
+
-
+
+
+
-
-
+
+
+
+
+
+
+
Используемые Коллекции Коллекции сокращения OCL EXPRESS BAG BAG BAG SET SET SET FIXED BAG FIXED SET SEQUENCE LIST LIST ORDERED UNIQUE ORDERED SET SET LIST ARRAY ARRAY UNIQUE UNIQUE ARRAY ARRAY SORTED LIST SORTED SET SORTED ARRAY SORTED UNIQUE ARRAY
Таблица 1. Классификация базовых типов коллекций в языках моделирования EXPRESS и OCL Базовые типы могут переопределяться пользователем с учетом семантики приложения путем задания дополнительных ограничений с использованием всего репертуара конструкций декларативных языков OCL и EXPRESS. В частности, может быть уточнено допустимое число элементов коллекции и способ их индексации, задан частичный или полный порядок на множестве элементов коллекции, определены свойства корреляции значений элементов и т.п. Рассмотрим вопросы представления, вычисления изменений и исполнения соответствующих операций над коллекциями, следуя приведенной выше классификации в соответствии с выделенными семантическими свойствами. 191
4. Сравнение множеств Начнем рассмотрение с наиболее простого типа коллекций X ∈ set < T > — множества элементов типа T, предполагающего неявное задание и выполнение единственного семантического ограничения уникальности элементов ∀x, y ∈ X → x ≠ y . Дельта для двух версий множества X , X ′ ∈ set < T > может быть естественным образом представлена в виде неупорядоченного набора операций добавления и удаления соответствующих элементов коллекции: Δset < T > ( X ′, X ) = {ins ( x) | x ∈ ( X ′ \ X )} ∪ {del ( x) | x ∈ ( X \ X ′)} Корректное представление дельты Δ = Δset < T > ( X ′, X ) предполагает, что выполняется условие ∀ins ( x1 ) ∈ Δ ∀del ( x 2 ) ∈ Δ → x1 ≠ x 2 , означающее, что один и тот же элемент не может одновременно участвовать в операции добавления и удаления. Более того, будем исключать повторение операций добавления и удаления с одним и тем же элементом, которое при исполнении операций противоречило бы определению множества: ∀ins1 ( x1 ) ∈ Δ ∀ins 2 ( x 2 ) ∈ Δ → x1 ≠ x 2 и ∀del1 ( x1 ) ∈ Δ ∀del 2 ( x 2 ) ∈ Δ → x1 ≠ x 2 . Применение операций, определяемых дельтой, довольно прозрачно. К заданному исходному множеству добавляются элементы, определяемые операциями ins, и удаляются элементы, определяемые операциями del. Тем самым, гарантируется тождественность условия Apply ( X , Δset < T > ( X ′, X )) = X ′ , в котором функция Apply ( X , Δ ) возвращает модифицированное представление коллекции, полученное в результате применения дельты к заданному представлению коллекции X. Две конкурентные транзакции Δ ′, Δ ′′ могут оказаться конфликтными в случае isConflict (ins ′( x), del ′′( x)) = true , когда в одной транзакции некоторый элемент добавляется в коллекцию, а в другой транзакции тот же самый элемент удаляется. Однако, если обе дельты вычислялись относительно общей версии коллекции в рамках распространенной схемы слияния “3-way Merge”: Δ ′ = Δset < T > ( X ′, X ) , Δ ′′ = Δset < T > ( X ′′, X ) , то подобные конфликты исключены в силу того, что удаляемый элемент обязан принадлежать базовой версии коллекции X и не может быть добавлен в нее повторно вследствие ограничения уникальности элементов множества. В случае иных схем слияния с участием нескольких базовых версий, например, схемы “4-way Merge”, подобные конфликты должны идентифицироваться и корректно разрешаться. Тривиальными способами разрешения являются следующие опции Options = {∅; ins ′( x); del ′′( x)} , предполагающие игнорирование обеих конфликтных операций или принятие одной из них. 192
Сложность вычисления дельты Δset < T > определяется, прежде всего, способом представления исходных множеств (список, массив, сбалансированное дерево), а также алгоритмами поиска добавляемых и удаляемых элементов. Вычислительная сложность наивной реализации, основанной на простом поиске элемента в неупорядоченном списке и не требующей определения на элементах множеств полного порядка может быть оценена как O(| X | ⋅ | X ′ |) . Сложность оптимальной реализации, основанной на предварительной сортировке исходных множеств, например, методом пирамидальной сортировки или методом слияния списков, можно оценить как O(| X | ⋅ ln | X | + | X ′ | ⋅ ln | X ′ |) . А в случае использования для работы с множеством сбалансированного дерева, хранящего элементы в уже отсортированном порядке, сложность операции построения дельты оценивается как O(| X | + | X ′ |) . Понятно, что последняя оценка не может рассматриваться как реальная оценка, поскольку в данном случае основная часть затрат перенесена на стадию формирования исходных множеств. Наиболее корректной оценкой в данном случае является также O(| X | ⋅ ln | X | + | X ′ | ⋅ ln | X ′ |) . Ниже приведен пример сравнения двух версий множества натуральных чисел X , X ′ ∈ set < N > . Пусть X , X ′ — основная и модифицированная версии коллекции, содержащие следующие элементы: X = {1,2,3,6,8} , X ′ = {1,2,4,5,7,9} . Тогда дельта, вычисленная путем сравнения двух версий множества, представляется как Δ ( X ′, X ) = {ins (4), ins (5), ins (7), ins (9), del (3), del (6), del (8)} .
5. Сравнение мультимножеств Перейдем к задаче сравнения мультимножеств X ∈ multiset < T > с функцией cardinality ( x, X ) для подсчета числа вхождений элемента x ∈ T в коллекцию X . При модификации коллекции данного типа дельта представляется как неупорядоченный набор множественного добавления и удаления элементов. Пусть X , X ′ ∈ multiset < T > — две версии мультимножества, тогда дельта может быть сформирована как ⎧ card ( x , n ) | n = cardinalit y ( x , X ′) − ⎫ Δ multiset < T > ( X ′, X ) = ⎨ ⎬ ⎩ cardinalit y ( x , X ), n ∈ Z , n ≠ 0 ⎭ В используемых обозначениях Z — множество целых чисел. Положительное значение параметра n в операции изменения кардинальности card(x,n) указывает на добавление элемента в коллекцию соответствующее число раз, отрицательное значение — на удаление элемента из коллекции. Нулевое значение n не содержательно для представления дельты, поскольку означает, что количество экземпляров элемента в коллекции не изменилось. В 193
корректно сформированном представлении дельты Δ = Δmultiset < T > ( X ′, X ) предполагается, что элемент мультимножества не может одновременно участвовать в нескольких операциях ∀card1 ( x1 , n1 ) ∈ Δ ∀card 2 ( x 2 , n 2 ) ∈ Δ → x1 ≠ x 2 . В противном случае сравнение идентичных версий коллекции могло бы привести к неожиданному результату, отличному от пустого представления дельты, например: card i ( x, ni ) | ni = 0 ≠ ∅ .
{
∑
}
Применение базовой операции дельты card(x,n) состоит в кратном добавлении соответствующего элемента x при положительном значении параметра n и в его кратном удалении при отрицательном значении параметра. Это гарантирует выполнение необходимого тождества Apply ( X , Δmultiset < T > ( X ′, X )) = X ′ . Две операции card ′( x, n ′) ∈ Δ ′, card ′′( x, n ′′) ∈ Δ ′′ конфликтуют друг с другом, если параметры кратности перемещения экземпляров одного и того же элемента в конкурентных транзакциях отличаются друг от друга n′ ≠ n ′′ . Тривиальные способы разрешения конфликта состоят в выборе одной из опций Options = {∅; card ′( x, n ′); card ′′( x, n ′′)} . Вместе с тем, логичным представляется расширение возможных опций путем назначения параметру кратности всего интервала значений, порождающего конфликтную ситуацию: Options = {∅; card ( x, min(n ′, n ′′));...; card ( x, max(n ′, n ′′))} . Это означает, что может быть выбрано любое число кратности для операции перемещения, лежащее в интервале между соответствующими параметрами конфликтных операций. В случаях, когда обе конфликтные операции являются родственными, конфликт целесообразно разрешать, исходя из промежуточных значений. В случаях, когда одна из конфликтных операций — операция добавления, а другая — операция удаления, допустимым является также весь диапазон значений параметра кратности, означая удаление или добавление числа элементов, не превышающего используемое значение соответствующей операции. Сложность построения Δmultiset < T > определяется теми же факторами, что и сложность вычисления дельты обычного множества. Имеет место незначительное увеличение числа операций, однако это не влияет на асимптотическую оценку, которая в среднем выражается как O(| X | ⋅ ln | X | + | X ′ | ⋅ ln | X ′ |) . В качестве примера рассмотрим процедуру сравнения двух версий мультимножества символов. Пусть X , X ′ ∈ multiset < N > — основная и модифицированная версии коллекции, содержащие следующие натуральные X = {g , a, b, c, c, f , b, c} , X ′ = {b, e, a, d , e, e} . Тогда дельта, элементы вычисленная в результате сравнения двух версий коллекции, представляется 194
как Δ( X ′, X ) = {card (b,−1), card (c,−3), card (d ,1), card (e,3), card ( f ,−1), card ( g ,−1)} .
целью. Все значения индексов позиций относительно исходных версий коллекции.
6. Сравнение списков
Корректное представление дельты Δ = Δlist < T > ( X ′, X ) предполагает, что выполняются следующие условия:
Для сравнения списков (или последовательностей) могут быть задействованы классические алгоритмы минимального редакторского расстояния (editdistance (ed)) и алгоритмы нахождения наибольшей общей последовательности (longest-common-subsequence (lcs)), нашедшие применение в самых разных приложениях [20, 21]. Поскольку данные семейства алгоритмов достаточно хорошо проработаны и изучены, мы ограничимся вопросами их использования в общем контексте решения задач сравнения и слияния коллекций на основе семантики модели. Пусть элементы списка X ∈ list < T > предварительно последовательно пронумерованы, начиная с единицы, и каждый элемент x(i ) ∈ X , i = 1.. | X |, x(i ) ∈ T индексируется в соответствии с положением в списке. Далее x(i ) обозначается i-ый элемент коллекции, | X | — число элементов коллекции и x[i, j ] — упорядоченное подмножество элементов коллекции X , начинающееся в позиции i и заканчивающееся в позиции j ≥ i . Тогда дельта, полученная в результате сравнения двух версий коллекции X , X ′ ∈ list < T > , может быть представлена множеством операций вставки новых элементов в соответствующие позиции исходного списка и удаления элементов с соответствующих позиций подобно тому, как это осуществляется в алгоритмах минимального редакторского расстояния: Δlist < T > ( X ′, X ) = {ins (i, x ′[k , l ]) | i = 1.. | X ′ | +1, x'[k , l ] ⊆ X ′} ∪ {del ( x[i, j ]) | x[i, j ] ⊆ X } ∪ {skip( x[i, j ]) | x[i, j ] ⊆ X , x ′[k , l ] ⊆ X ′, x[i, j ] = x ′[k , l ]}
Здесь операция ins (i, x ′[k , l ]) вставляет упорядоченный набор элементов модифицированного списка X ′ с индексами в отрезке [k , l ] в позицию i исходного списка X . Операция del ( x[i, j ]) удаляет элементы исходного списка с индексами, принадлежащими отрезку [i, j ] , и, наконец, операция skip( x[i, j ]) переносит элементы с индексами в отрезке [i, j ] в модифицируемый список без изменений. Последний тип операций избыточен при практической реализации, поскольку подмножество переносимых элементов может быть вычислено путем анализа интервалов индексов для удаляемых элементов. Тем не менее, здесь они используются с методической 195
элементов
рассчитываются
∀ins1 (i1 , x ′[k1 , l1 ]) ∈ Δ ∀ins 2 (i2 , x ′[k 2 , l 2 ]) ∈ Δ → i1 ≠ i 2 ∀ins1∀ins2 ins1(i1, x′[k1, l1]),ins2 (i2 , x′[k2 , l2 ]) ∈ Δlist < T > ( X ′, X ) → [k1, l1] ∩ [k2 , l2 ] = ∅ ∀del1 ( x[i1 , j1 ]) ∈ Δ ∀del 2 ( x[i 2 , j 2 ]) ∈ Δ → [i1 , j1 ] ∩ [i2 , j 2 ] = ∅
означающие, что индексы позиций вставки элементов не повторяются, а интервалы вставляемых и удаляемых элементов не пересекаются в разных операциях. Будем считать также, что аналогичное условие выполняется для операций переноса элементов, индексы которых в исходном списке дополняют индексы удаляемых элементов: ∀skip1 ( x[i1 , j1 ]) ∈ Δ ∀skip 2 ( x[i2 , j 2 ]) ∈ Δ → [i1 , j1 ] ∩ [i2 , j 2 ] = ∅ ∀skip1 ( x[i1 , j1 ]) ∈ Δ ∀del 2 ( x[i 2 , j 2 ]) ∈ Δ → [i1 , j1 ] ∩ [i 2 , j 2 ] = ∅ ∀i ∈ [1, | X |] ∃del ( x[i1 , j1 ]) ∈ Δlist < T > ( X ′, X ) i ∈ [i1 , j1 ] ∧
∃skip( x[i1 , j1 ]) ∈ Δlist < T > ( X ′, X ), i ∈ [i1 , j1 ] Применение дельты предполагает предварительное упорядочение операций по индексам вставки и удаления элементов в исходной коллекции X таким образом, что при совпадении индексов операции вставки предшествуют соответствующим операциям удаления. Сами операции последовательно выполняются со значениями индексов, скорректированными с учетом предшествующих результатов. При подобной интерпретации каждая операция вставки элементов ins (i, x' [k , l ]) может рассматриваться в качестве композиции элементарных операций вставки отдельных элементов ins (i, x' (k ))∠...∠ins (i, x' (m))∠...∠ins (i, x' (l )) с наложенными отношениями предшествования между ними. Используемый символ отношения ins1 (i, x'1 )∠ins 2 (i, x' 2 ) означает, что операции ins1 (i, x'1 ) , ins 2 (i, x' 2 ) должны выполняться таким образом, чтобы в результирующем представлении коллекции элемент x'1 предшествовал элементу x' 2 при условии, что обе операции применяются. Последнее замечание существенно, поскольку одна из операций может быть не включена в результирующую транзакцию. Тем не менее, транзитивные отношения предшествования определяют частичный 196
порядок между операциями транзакции, который должен соблюдаться независимо от того, какие операции применяются, а какие — нет. Таким образом, установленные отношения предшествования гарантируют, что элементы x'[k , l ] будут вставлены в результирующий список, не нарушая исходный порядок. Подобные отношения могут конструктивно использоваться при выполнении операций. Например, если операция реализуется как вставка в указанную позицию списка, то при условии ins1 (i, x'1 )∠ins 2 (i, x' 2 ) операция ins 2 (i, x' 2 ) должна применяться до операции ins1 (i, x'1 ) . Две конкурентные операции с пересекающимися значениями интервалов индексов могут приводить к ситуациям, допускающим неоднозначное решение. Две операции удаления del ′( x[k ′, l ′]) ∈ Δ ′ и del ′′( x[k ′′, l ′′]) ∈ Δ ′′ с пересекающимися интервалами индексов [k ′, l ′] ∩ [k ′′, l ′′] ≠ ∅ допускают консолидированное исполнение в виде del ( x[k ′, l ′] ∪ [k ′′, l ′′]) . Однако полный перечень опций применения определяется как множество всех простых сочетаний (сочетаний без повторений) операций удаления, включая пустое множество: Options = SimpleCombinations{(del ( x(min(k ′, k ′′)),..., del ( x(max(l ′, l ′′))))} . Число возможных сочетаний может быть слишком велико для выбора необходимого варианта в ходе интерактивной сессии. Поэтому более естественным может оказаться представление конкурентных операций в более компактном виде с меньшим числом альтернатив выбора, а именно: ⎧(del ( x[k ′, l ′] ∩ [k ′′, l ′′]), ⎫ ⎪ ⎪ Options = SimpleCombinations ⎨del ( x[k ′, l ′] \ ( x[k ′, l ′] ∩ [k ′′, l ′′])),⎬ . ⎪del ( x[k ′′, l ′′] \ ( x[k ′, l ′] ∩ [k ′′, l ′′]))⎪ ⎩ ⎭
Отметим, что агрегированная операция удаления общих элементов del ( x[k ′, l ′] ∩ [k ′′, l ′′]) может рассматриваться как консолидированное действие, не требующее в большинстве случаев дополнительного согласования. del ( x[k ′, l ′] \ ( x[k ′, l ′] ∩ [k ′′, l ′′])) и Альтернативы удаления del ( x[k ′′, l ′′] \ ( x[k ′, l ′] ∩ [k ′′, l ′′])) дополняют общую операцию до соответствующих действий в каждой транзакции и могут приниматься в произвольном сочетании с двумя другими операциями. Две конкурентные операции вставки и удаления элементов, пересекающиеся по индексам: ins ′(i ′, x ′[k , l ]) ∈ Δ ′ , del ′′( x[i, j ]) ∈ Δ ′′ , где i ′ ∈ [i, j ] , следует считать неконфликтными, поскольку операции удаления могут всегда быть корректно исполнены последовательно или вместе с соответствующими операциями вставки. 197
ins ′(i, x ′[k ′, l ′]) ∈ Δ ′ и Для конфликтных операций вставки ins ′′(i, x ′′[k ′′, l ′′]) ∈ Δ ′′ , добавляющих неэквивалентные списки элементов x ′[k ′, l ′] ≠ x ′′[k ′′, l ′′] в одну и ту же позицию i исходного списка, пользователь должен принять решение относительно способа формирования консолидированного списка вставляемых элементов. Потенциально, любое размещение элементов альтернативных списков может считаться допустимым при выполнении следующих двух условий: оригинальный порядок элементов не изменяется и исключается дублирование эквивалентных элементов из разных версий списка в смежных позициях результирующего списка. Таким образом, возможные варианты консолидации представляются следующим образом: ⎧ins′(i, x′( k ′)),...ins′(i, x′(l ′)), ins′′(i, x′′( k ′′)),...ins′′(i, x′′(l ′′)) ⎫ ⎪ ⎪| ins′(i, x′(k ′))∠...∠ins′(i, x′(l ′)), ⎪ ⎪ Options = Arrangemen ts ⎨ ⎬ ⎪ ⎪| ins′′(i, x′′(k ′′))∠...∠ins′′(i, x′′(l ′′)), ⎪⎭ ⎪⎩| ins′(i, x′(m′)) <> ins′′(i, x′′(m′′)) → x′( m′) ≠ x′′( m′′)
Первые два условия гарантируют, что исходный порядок элементов при консолидации не будет нарушен. Третье условие, связанное с непосредственным предшествованием операций ins ′(i, x ′(m ′)) <> ins ′′(i, x ′′(m ′′)) в итоговой транзакции, обеспечивает исключение тождественных элементов при вставке в соседние позиции из разных списков. Возможный способ реализовать подобную стратегию заключается в применении упомянутых выше методов сравнения к консолидируемым последовательностям. Пусть вспомогательные списки Y ′ ∈ list < T > , Y ′′ ∈ list < T > представляют собой соответствующие последовательности вставки элементов Y ′ = x ′[k ′, l ′] и Y ′′ = x ′′[k ′′, l ′′] . Тогда их дельта представима в виде: Δlist < T > (Y ′′, Y ′) = {ins (i, y ′′[k , l ]) | i = 1.. | Y ′ | +1, y ′′[k , l ] ⊆ Y ′′} ∪ {del ( y ′[i, j ]) | y ′[i, j ] ⊆ Y ′} ∪
{skip( y ′[i, j ]) | y ′[i, j ] ⊆ Y ′, y ′′[k , l ] ⊆ Y ′′, y ′[i, j ] = y ′′[k , l ]}
Способы консолидации элементов из альтернативных списков задаются на основе Δ = Δlist < T > (Y ′′, Y ′) следующими размещениями:
⎧ins(i, y′[k , l ]) ⎫ ⎪ ⎪ Options = Arrangements ⎨| ∃skip( y′[k , l ]) ∈ Δ ∧ ∃del( y′[k , l ]) ∈ Δ ∧ ∃ins(i, y′′[k , l ]) ∈ Δ⎬ ⎪| ∀ins (i , y′[k , l ])∠ins (i , y′[k , l ]) → i < i , l < k ⎪ 1 1 1 1 2 2 2 2 1 2 1 2 ⎩ ⎭ Размещения предполагают предшествование операций, определяемое естественным порядком позиций вставки и индексных интервалов в исходных операциях сформированной дельты. Тем самым, оперируя с альтернативными 198
последовательностями вставки и выделяя отличия между ними, удается определить конструктивный способ разрешения данного рода конфликтов. Рассмотрим следующий пример. Пусть X , X ′, X ′′ ∈ list < S > — основная и модифицированные версии некоторого списка символов: X = {a, b, b, c, c, d , e, h, j} , X ′ = {a, b, c, d , e, f , g , h, j} , X ′′ = {a, b, c, d , e, h, j , k , l} . Тогда дельты, вычисленные в результате сравнения соответствующих модифицированных версий с базовой, представляются как Δ ′( X ′, X ) = {skip ′(1,2), del ′(3,4), skip ′(5,7), ins ′(8, [6,7]), skip ′(8,9)} и Δ ′′( X ′′, X ) = {skip ′′(1,2), del ′′(3,4), skip ′′(5,9), ins ′′(10, [8,9])} . В данном случае изменения не содержат конфликтов и могут быть консолидированы, приводя к результирующему представлению списка X * = {a, b, c, d , e, f , g , h, j , k , l} . Оценка вычислительной сложности классического алгоритма минимального редакторского расстояния с использованием метода динамического программирования составляет O (| X | ⋅ | X ′ |) . Этой же оценкой определяется общая сложность формирования дельты для списков. При неоптимальном формировании дельты, например, путем нахождения наибольшей общей последовательности и определения дополняющих операций, оценка вычислительной сложности может быть улучшена до O (| X | ⋅ ln | X | + | X ′ | ⋅ ln | X ′ |) , однако количество элементарных операций в полученном представлении дельты может оказаться высоким. Более детальная систематизация алгоритмов и сравнительный анализ их вычислительной сложности приводятся в [20, 21].
7. Сравнение упорядоченных множеств Упорядоченные множества являются одновременно специализацией и множеств, и списков, поэтому процедуры сравнения, рассмотренные выше, могли бы применяться и для этого случая. Однако в силу сочетания свойств упорядочения и уникальности элементов, видится более содержательный способ представления и расчета изменений для данного типа коллекций не только в виде операций вставки и удаления, но и операций перестановок. Пусть X , X '∈ ordered set < T > — исходная и модифицированная версии упорядоченного множества. Подобно спискам предполагается, что элементы коллекции последовательно перенумерованы, начиная с единицы, и с каждым элементом ассоциирован соответствующий индекс позиции. Тогда дельта может быть представлена как множество операций циклических перестановок, вставок новых элементов и удалений существующих элементов: Δ ordered set < T > ( X ' , X ) = { prm(i1 , i 2 ,...in ) | x(i1 ), x(i2 ),..., x(in ) ∈ ( X ∩ X ' )} ∪ {ins (i, [k , l ]) | x[k , l ] ∈ ( X '\ X ), i = 1.. | X | +1} ∪ {del ([i, j ]) | x[i, j ] ∈ ( X \ X ' )}
199
Операция перестановки prm(i1 , i 2 ,...in ) однократно циклически переставляет элементы исходного множества x(i1 ), x (i 2 ),...x (i n ) , приводя к следующему результату: x(i 2 ), x(i3 ),...x(i1 ) . Операция ins (i, [k , l ]) вставляет упорядоченный набор элементов модифицированного списка X ' с индексами в отрезке [k , l ] в позицию i-ого элемента исходного множества X . Операция del ([i, j ]) удаляет элементы исходного множества X с индексами, принадлежащими отрезку [i, j ] . Определим более точно семантику операций, исходя из требований предшествования группы новых элементов элементу исходного множества, в позицию которого происходит вставка, сохранения порядка вставляемых элементов относительно друг друга в соответствии с их позициями, а также непрерывности их следования в результирующем представлении множества независимо от применения или неприменения всех других операций дельты. Использование перестановок в представлении дельты упорядоченного множества позволяет изменить порядок элементов без их парных удалений и вставок, как это осуществлялось в случае списков. Более содержательный способ структуризации изменений, отражающий семантику данных, является принципиальным с учетом сложности приложений, оперирующих масштабными междисциплинарными информационными моделями. Все значения индексов позиций указываются в представлении дельты относительно исходных версий коллекции. При выполнении дельты индексные параметры всех последующих операций должны корректироваться с учетом ранее примененных. Данная корректировка заключается в увеличении или уменьшении индексов элементов исходного множества на количество выполненных вставок или удалений. При выполнении перестановок корректировка состоит в циклическом распространении индекса для каждого последующего элемента группы перестановки. Любое изменение порядка элементов в упорядоченном множестве может быть представлено композицией циклических перестановок. Причем при отсутствии пересечений по индексам перестановки удовлетворяют требованию коммутативности и могут применяться в произвольном порядке независимым друг от друга образом [19]. Тем самым удовлетворяется требование конструктивной декомпозиции и реконсиляции транзакций, связанное с возможностью независимого применения их отдельных операций. При этом наличие одного и того же индекса в разных группах перестановок дельты Δ = Δ ordered set < T > ( X ' , X ) должно быть запрещено: ∀prm1 (i1 , i 2 ,..in ) ∈ Δ ∀prm 2 ( j1 , j 2 ,.. j m ) ∈ Δ → (i1 , i 2 ,..i n ) ∩ ( j1 , j 2 ,.. j m ) = ∅
Данное условие не является обременительным, поскольку существует четкая методика разложения перестановки произвольного упорядоченного множества на группы циклических перестановок. Данная методика 200
приводится в [19] как доказательство теоремы о единственности специально заданного соединительного произведения перестановки линейно упорядоченного мультимножества. Для корректного применения дельты Δ = Δ ordered set < T > ( X ' , X ) операции могут быть частично упорядочены подобно тому, как это делалось для операций со списками. Предшествование операций циклической перестановки операциям вставки, а тех, в свою очередь, операциям удаления позволяет упростить реализацию применения дельты: ∀prm(i1 , i2 ,...in ) ∈ Δ ∀ins(i, [k , l ]) ∈ Δ ∀del ([i, j ]) ∈ Δ → prm(i1 , i 2 ,...in ) ∠ins (i, [k , l ])∠del ([i, j ])
del ([2,2]) , del ([6,6]) удаляют элементы b и f, приводя к окончательному представлению модифицированной версии коллекции X ′ . Опишем возможный способ формирования дельты двух упорядоченных множеств в соответствии с перечисленными выше условиями: 1. Поиск идентичных подмножеств Y ⊆ X и Y ′ ⊆ X ′ исходных Y , X , Y ′, X ′ ∈ ordered set < T > , сохраняющих множеств, оригинальный порядок следования элементов:
Y = {y (i ) | y (i ) ∈ X ∩ X ' , ∀ y (i ) ∀ y ( j ) y (i ) = x ( k ), y ( j ) = x (l ), i < j → k < l } , Y ′ = {y ′(i ) | y ′(i ) ∈ X ∩ X ' , ∀y ′(i ) ∀y ′( j ) y ′(i ) = x ′(k ), y ′( j ) = x ′(l ), i < j → k < l}
Данное требование связано с принятой семантикой операций, допускающей непосредственную индексацию элементов в исходных коллекциях. Вставка группы элементов неявно подразумевает задание ограничения предшествования добавляемых элементов элементу, в позицию которого осуществляется вставка. Однако следующая за вставкой операция перестановки может нарушить это условие. Для того чтобы удовлетворить условие и обеспечить неразрывность семантически связанных групп элементов, видятся два простых решения: 1. 2.
обобщение операции перестановки таким образом, чтобы обеспечить циклическую перестановку не отдельных элементов, а целых групп; соблюдение условия предшествования операций перестановки операциям вставки.
2.
На наш взгляд, второй способ более предпочтителен, поскольку контроль частичного порядка операций при выполнении не вызывает дополнительных сложностей в реализации в отличие от обобщенных перестановок. Проиллюстрируем вычисление и применение дельты для упорядоченных множеств на следующем примере. Пусть X , X ′ ∈ ordered set < S > — основная и модифицированная версии коллекции символов, представленные X = {a, b, c, d , e, f } , следующими последовательностями элементов: X ' = {e, g , h, k , l , d , c, m, a}. Тогда дельта, вычисленная в соответствии с вышеописанной семантикой операций, представляется как Δ orderedset ( X ' , X ) = {prm(1,5), prm(3,4), ins(4, [2,5]),ins(1, [8,8]), del([2,2]), del([6,6])}
3.
4.
В ходе применения дельты к основной версии X операции prm(1,5), prm(3,4) переставляют элементы a, e и c, d исходного множества, приводя к промежуточным представлениям коллекции {e, b, c, d , a, f } и {e, b, d , c, a, f } соответственно. Операции ins (4, [2,5]), ins (1, [8,8]) добавляют элементы g, h, k, l перед d и элемент m перед a, формируя последовательности {e, b, g , h, k , l , d , c, a, f } и {e, b, g , h, k , l , d , c, m, a, f } . Наконец, операции 201
5.
202
Возможная алгоритмическая реализация данного этапа состоит в предварительной сортировке элементов исходных множеств (при наличии отношения полного порядка) и последовательном просмотре и идентификации элементов как удаленных, вставленных или перенесенных без изменений. Альтернативный способ заключается в непосредственном использовании процедуры поиска для установления факта наличия или отсутствия элементов в исходных множествах и в параллельном формировании структур соответствия индексов элементов. Подобные структуры обеспечивают быстрое преобразование индексов, необходимое для эффективной реализации следующих этапов. Поиск циклической перестановки элементов множества Y , приводящей к последовательности элементов множества Y ' . Наиболее простым способом реализации данного этапа видится предварительная замена алфавита исходного множества Y на последовательность натуральных чисел (1,2,... | Y |) и применение методики, применяемой в доказательстве теоремы о единственности специальной формы соединительного произведения перестановки линейно упорядоченного мультимножества [19]. {ins(...)} , Определение множества операторов вставки упорядоченного по индексам вставки элементов, используя структуры соответствия индексов элементов множеств X ' и Y ' . {del (...)} , Определение множества операторов удаления упорядоченного по индексам удаляемых элементов, используя структуры соответствия индексов элементов множеств X и Y . Формирование единого списка операций дельты в соответствии с принятым порядком исполнения: сначала следуют операции перестановок, затем — операции вставок и в конце — операции удаления.
Сформированная таким образом дельта состоит из множества независимых операций, каждая из которых может быть принята или отклонена в рамках результирующей транзакции независимо от статуса остальных операций. Вычислительная сложность построения дельты определяется в первую очередь сложностью алгоритмов сортировки, применяемых в ходе реализации. Все остальные элементы, включая алгоритм разложения перестановки на множество циклических, имеют асимптотически линейную сложность при условии использования соответствующих структур быстрого преобразования индексов. Например, описанный метод формирования дельты с использованием сортировки на основе слияния списков имеет асимптотическую оценку вычислительной сложности O (| X | ⋅ ln | X | + | X ′ | ⋅ ln | X ′ |) . Перечислим конфликтные ситуации, возникающие при реконсиляции конкурентных операций над упорядоченными множествами, а также возможные способы их разрешения. Основные конфликты сводятся к следующим содержательным случаям (опустим для краткости математическую нотацию, примеры и комментарии подобно тому, как это делалось в предыдущей главе): 1. 2. 3. 4.
Вставка тождественных элементов в разные позиции. Стандартным способом разрешения конфликта является принятие одной из операций или отмена обеих. Вставка нетождественных последовательностей элементов в одну и ту же позицию. Варианты разрешения — те же самые, что и при сравнении списков. Удаление элемента в одной транзакции при его перестановке в другой. Способ разрешения — стандартный. Неэквивалентная перестановка одного и того же элемента в разных транзакциях. Принятие одной из циклических перестановок, в которых участвует элемент, является наиболее простым и очевидным способом разрешения подобного конфликта. Альтернативой ему может служить решение, основанное на декомпозиции конфликтных операций перестановки на элементарные транспозиции и выделение неэквивалентных цепочек транспозиций для заданного элемента. В этом случае разрешение конфликта сводится к отмене одной из конфликтных транспозиций и формированию итоговой консолидирующей перестановки.
Очевидно, что среда для согласования версий должна предусматривать возможность интерактивной работы для разрешения описанных видов конфликтов. При этом пользователю должны предлагаться уже сформированные, семантически корректные и наглядные варианты имеющихся альтернатив. 203
8. Сортированные последовательности Наличие свойств сортировки для последовательностей элементов позволяет существенно ускорить процедуры сравнения коллекций X , X ′ ∈ sorted list < T >⊂ list < T > и применения соответствующих операций дельты Δsorted list < T > ( X ′, X ) . Способ представления дельты в этих случаях повторяет ранее описанный для произвольных списков, однако методы ее вычисления допускают оптимизацию с учетом свойств порядка. Вместо вычислительно сложных алгоритмов минимального редакторского расстояния и наибольшей общей последовательности может эффективно применяться алгоритм линейной сложности O (| X | + | X ′ |) , осуществляющий последовательный просмотр элементов версий коллекции в сортированном порядке и фиксирующий изменения сразу по ходу их просмотра. Аналогичным образом может быть оптимизирована процедура применения операций дельты, допускающая эффективный поиск элементов по индексам.
9. Последовательности фиксированной длины Наличие у последовательности элементов статически фиксированной длины вносит коррективы в способ представления дельты, наиболее адекватно отражающий ее семантику. Коллекции данного типа будем называть статическими массивами. Для подобных случаев X , X ′ ∈ array < T >⊂ list < T > будем использовать следующее представление дельты: Δarray < T > ( X ′, X ) = {alt (i, x ′[i]) | i = 1.. | X |, x ′[i] ∈ X ′} , фиксирующее индексы измененных элементов. Здесь операция alt (i, x ′[i ]) заменяет значение элемента исходного массива X с индексом i значением соответствующего элемента модифицируемого массива X ′ . Корректное представление дельты Δ = Δarray < T > ( X ′, X ) предполагает, что индексы модифицируемых элементов не повторяются в разных операциях: ∀alt1 (i1 , x ′[i1 ]) ∈ Δ ∀alt 2 (i2 , x ′[i2 ]) ∈ Δ → i1 ≠ i 2 Две конкурентные операции alt ′(i ′, x ′[i ′]) ∈ Δ ′ и alt ′′(i ′′, x ′′[i ′′]) ∈ Δ ′′ в Δ ′ = Δarray < T > ( X ′, X ) и соответствующих транзакциях Δ ′′ = Δarray < T > ( X ′′, X ) оказываются конфликтными в тех случаях, когда присваивают разные значения элементу с одним и тем же индексом: i ′ = i ′′ , x ′[i ′] ≠ x ′′[i ′′] , x ′[i ′] ⊆ X ′ , x ′′[i ′′] ⊆ X ′′ , i ′, i ′′ = 1.. | X | . Способ разрешения конфликтов подобного рода тривиален и состоит в игнорировании обеих конкурентных операций или принятии одной из них: Options = {∅; alt ′(i, x ′[i ]); alt ′′(i, x ′′[i ])} . В частном случае x ′[i ] = x ′′[i ] операции эквивалентны и не конфликтуют друг с другом. 204
Рассмотрим следующий пример согласования изменений в массиве. Пусть X , X ′, X ′′ ∈ array < S > — базовая и модифицированные версии массива символов: X = {a, b, b, d , d } , X ′ = {a, b, c, h, i} , X ′′ = {a, b, c, d , e} . Тогда соответствующие дельты представляются как Δ ′( X ′, X ) = {alt ′(3, c), alt ′(4, h), alt ′(5, i )} , Δ ′′( X ′′, X ) = {alt ′′(3, c), alt ′′(5, e)} . В данном случае операции alt ′(3, c) ∈ Δ ′ и alt ′′(3, c) ∈ Δ ′′ эквивалентны и, следовательно, в результат включается одна из них. Операция alt ′(3, c) ∈ Δ ′ переносится без изменений. Операции alt ′(5, i ) ∈ Δ ′ и alt ′′(5, e) ∈ Δ ′′ конфликтны, поэтому лишь одна из них может быть включена в результирующую дельту. В случае принятия операции второй транзакции дельта приобретает вид Δ* = {alt ′(3, c), alt ′(4, h), alt ′′(5, e)} , а итоговый массив — X * = {a, b, c, h, e} .
10. Коллекции с ограниченной мощностью Наконец, особым образом должны реализовываться процедуры применения дельты для коллекций с ограниченной мощностью. В предположении, что исходная и модифицируемая версия коллекции были корректны и удовлетворяли необходимым семантическим ограничениям, частичное применение операций дельты также должно удовлетворять наложенным ограничениям мощности (размера коллекции). Способы представления и вычисления дельты при этом не меняются и определяются иными семантическими свойствами (см. предыдущие разделы), однако применение отдельных операций удаления и добавления элементов должно проводиться в соответствии с дополнительными логическими отношениями, индуцируемыми соответствующими ограничениями. Пусть задано следующее ограничение мощности коллекции: | X |∈ [ p, q] , где p, q ∈ N . Частный случай p = q соответствует коллекции фиксированной мощности. Если Δ ins ⊂ Δ и Δ del ⊂ Δ — соответствующие подмножества операций вставки и удаления исходного представления дельты Δ = Δcollection < T > ( X ′, X ) , то должно выполняться следующее дополнительное условие: p ≤| X | + | Δ ins | − | Δ del |≤ q . Очевидно, что в случае фиксированной мощности p = q количество операций вставки и удаления в транзакции должно совпадать. Для мультимножеств Δmultiset < T > ( X ′, X ) = {card i ( xi , ni )} условие представления дельты приобретает вид p ≤| X | +
∑n
i
≤ q.
205
В случае конкурентных транзакций Δ ′, Δ ′′ приведенные выше условия должны выполняться для консолидированной дельты Δ* = Merge ( Δ ′, Δ ′′) и итогового представления коллекции X * = Apply ( X , Δ* ) . В случае | X * |< p конфликт вызывает преобладание операций удаления над операциями вставки, в случае | X * |> q — преобладание операций вставки. Логичным способом разрешения подобных конфликтов является исключение такого количества преобладающих операций, чтобы мощность итоговой коллекции удовлетворяла наложенному ограничению: | X * |∈ [ p, q ] . Очевидно, что при выборе исключаемых операций следует учитывать и другие ограничения, наложенные на коллекцию. Например, в случае ограничения уникальности операции вставки и удаления элемента с одним и тем же значением должны быть включены или исключены совместно. Рассмотрим следующий пример. Пусть X , X ′, X ′′ ∈ multiset < S > — базовая и модифицированные версии мультимножества символов с ограниченной мощностью | X |∈ [2,6] : X = {a, b, c} , X ′ = {a, b, d , e, e} , X ′′ = {a, b, e, e, e, e} . Тогда дельты, вычисленные путем сравнения модифицированных версий с базовой, представляются как Δ ′( X ′, X ) = {card ′(c,−1), card ′(d ,1), card ′(e,2)} , Δ ′′( X ′′, X ) = {card ′′(c,−1), card ′′(e,4)} . Операции card ′(c,−1) и card ′′(c,−1) эквивалентны, поэтому в результирующую дельту следует включить любую из них. Операция card ′(d ,1) не конфликтует ни с одной другой операцией, поэтому переносится в результат без изменений. Наконец, операции card ′(e,2) и card ′′(e,4) конфликтуют друг с другом. Согласно рассмотренным выше методам согласования изменений для мультимножеств, конфликт можно разрешить выбором кратности вхождения элемента e из интервала [2, 4]. Однако выбор значения кратности, равного 4, приводит к нарушению ограничения мощности результирующего мультимножества, в которое в таком случае войдет 7 элементов. Следовательно, допустимыми значениями кратности вхождения элемента e в итоговую коллекцию являются 2 и 3. В случае принятия второго значения результирующая дельта приобретает вид: Δ* = {card ′(c,−1), card ′(d ,1), card (e,3)} , а итоговое семантически корректное представление мультимножества — X * = {a, b, d , e, e, e} .
11. Коллекции прямых и инверсных ассоциаций Коллекции могут использоваться для реализации множественных ассоциативных связей между объектными типами. В языках объектноориентированного моделирования имеется возможность для каждой прямой ассоциации объявить соответствующую инверсную, которая представляется, как правило, неупорядоченным множеством или мультимножеством 206
объектных ссылок и налагает дополнительные семантические ограничения (уникальности или мощности множественной ассоциации) на исходную модель. Пусть C1 и C 2 — объектные типы, X ∈ collection < C 2 > — прямая множественная ассоциация, Y ∈ collection < C1 > — соответствующая ей инверсная. Будем считать, что в качестве прямой ассоциации может быть использована произвольная коллекция, в качестве инверсной — set или multiset. Поскольку модификация прямой ассоциации подразумевает симметричную коррекцию инверсной, операции установления и отмены ассоциативных отношений связаны логической эквивалентностью следующим образом: ins ( x) ~ ins ( y ) , del ( x) ~ del ( y ) , x ∈ X , y ∈ Y . Поэтому данные операции обязаны совместно участвовать в итоговой транзакции. Если инверсная ассоциация представляется множеством, то дополнительно устанавливается ограничение уникальности инверсного отношения. При сочетании в качестве прямой и инверсных ассоциаций различных коллекций более строгое ограничение уникальности в итоге распространится на обе ассоциативные связи. Таким образом, возможны следующие варианты сочетания прямых и инверсных коллекций: «set–set», «multiset–multiset», «list– multiset», «ordered set–set». Нарушение ограничения уникальности прямой ассоциации автоматически приводит к аналогичному нарушению на стороне инверсной. Поэтому наличие инверсного ассоциативного отношения с уникальными элементами не вносит дополнительных корректив в способы представления и формирования дельты, а также в методы разрешения конфликтов, описанные в предыдущих разделах. Более интересным с этой точки зрения представляются ограничения мощности множественной ассоциации | X |∈ [m, n] , | Y |∈ [ p, q ] , m, n, p, q ∈ N . В данном случае корректное представление дельты предполагает выполнение следующих условий:
m ≤| X | + | Δ ins ( x ) | − | Δ del ( x ) |≤ n p ≤| Y | + | Δ ins ( y ) | − | Δ del ( y ) |≤ q В силу отношений логической эквивалентности операций над прямыми и обратными ассоциациями, условия приобретают вид:
max(m− | X |, p − | Y |) ≤| Δ ins ( x ) | − | Δ del ( x ) |≤ min(n− | X |, q − | Y |) В случае конкурентных транзакций
Δ ′, Δ ′′
данные условия должны
выполняться также для консолидированной дельты Δ* = Merge ( Δ ′, Δ ′′) .
207
12. Заключение Таким образом, рассмотрены основные типы коллекций, поддерживаемые популярными языками объектно-ориентированного моделирования. Для них определены способы представления, журнализации, вычисления, принятия и согласования изменений. Для каждого выделенного типа дается строгая, семантически состоятельная интерпретация конфликтов и предлагается конструктивный метод их идентификации и разрешения. Результаты предполагается использовать при создании универсальной, основанной на модельном представлении среды коллективной инженерии с развитыми возможностями семантически корректной и функционально содержательной реконсиляции дивергентных реплик данных. Литература [1] Y. Saito, M. Shapiro. Optimistic Replication // In ACM Computing Surveys, Vol. 37, No. 1, March 2005, pp. 42–81. [2] Better SCM Initiative: Version Control System Comparison, http://betterscm.berlios.de/comparison/comparison.html [3] Diffutils — GNU Project — Free Software Foundation (FSF), http://www.gnu.org/software/diffutils/diffutils.html [4] Z. Xing, E. Stroulia. UMLDiff: an algorithm for object-oriented design differences. // Proceedings of the 20th IEEE/ACM international Conference on Automated software engineering, Long Beach, CA, USA, 2005, pp. 54–65. [5] Open Testware Reviews — Data Comparator Survey, http://tejasconsulting.com/opentestware/feature/data-comparator-survey.html [6] Comprehensive List of File and Folder Comparison and Synchronization Tools, http://www.foldermatch.com/fmcompetitors.htm [7] Сравнение файлов — Soft Софт каталог, http://www.softsoft.ru/search/19709/index.htm [8] Google directory — Computers > Software > File Management > File Comparison, http://www.google.com/Top/Computers/Software/File_Management/File_Comparison [9] Semenov V.A., Karaulov A.A. Semantic-Based Decomposition of Long-Lived Transactions in Advanced Collaborative Environments. // Proceedings of 6 European Conference on product and process modeling, ECPPM 2006, Spain, Valencia, September 11–15, 2006, pp.223–232. [10] Семенов В.А., Ерошкин С.Г., Караулов А.А., Энкович И.В. Семантическая реконсиляция прикладных данных на основе моделей. // Труды Института системного программирования: т. 13, ч. 2. / Под ред. В.П. Иванникова — М.: ИСП РАН, 2007, с. 141–164. [11] Semenov V.A. Collaborative Software Engineering Using Metamodel-Driven Approach. // Proceedings 16th IEEE International Workshops on Enabling Technologies: Infrastructure for Collaborative Enterprises, WET ICE 2007, IEEE Computer Society Conference Publishing Services, 2007, pp. 178–179. [12] Semenov V.A. Semantics-Based Reconciliation of Divergent Replicas in Advanced Concurrent Engineering Environments. // Complex Systems Concurrent Engineering: Collaboration, Technology Innovation and Sustainability, Springer-Verlag, 2007, pp. 557–564.
208
[13] ISO 10303: 1994, Industrial automation systems and integration — Product data representation and exchange. [14] OMG. Model Driven Architecture: How systems will be built, http://www.omg.org/mda. [15] T. Lindholm. XML three-way merge as a reconciliation engine for mobile data. // Proceedings of the 3d ACM international workshop on data engineering for wireless and mobile access, San Diego, CA, USA, 2003, pp. 93–97. [16] J. Katajainen and J. L. Träff. A Meticulous Analysis of Mergesort Programs. // Lecture Notes In Computer Science, vol. 1203, 1997, pp. 217–228. [17] Object Constraint Language Specification, Version 2.0, http://www.omg.org/technology/documents/formal/ocl.htm [18] ISO 10303-11: 2004, Industrial automation systems and integration — Product data representation and exchange — Part 11: Description methods: The EXPRESS language reference manual. Edition 2. [19] Д. Кнут. Искусство программирования, том 3. Сортировка и поиск, 2-е изд. — М.: Издательский дом «Вильямс», 2000. [20] Д. Гасфилд. Строки, деревья и последовательности в алгоритмах: Информатика и вычислительная биология. — СПб.: Невский Диалект; БХВ-Петербург, 2003. [21] G. Navarro. A Guided Tour to Approximate String Matching. // ACM Computing Surveys, vol. 33, no. 1, March 2001, pp. 31–88.
209