Работа с файлами в COBOL на первый взгляд кажется обманчиво простой. Вы объявляете файл в разделе ENVIRONMENT, описываете структуру его записей в разделе DATA и используете команды OPEN, READ или WRITE, а также CLOSE в разделе PROCEDURE. Четыре команды, один жизненный цикл. Проблема в том, что программы на COBOL редко остаются простыми: за десятилетия поддержки они накапливают логику, пути обработки исключений, условные переходы и вызовы PERFORM. То, что начинается как чистый шаблон «одно открытие — одно закрытие», незаметно трансформируется в нечто более сложное: файлы открываются дважды в одном пути выполнения, операторы CLOSE выполняются, даже если файл не открыт, а ошибки приводят к выходу из программы, идущему дальше оператора CLOSE.
Ни одна из этих проблем не видна сразу. Программа компилируется. Она часто работает без ошибок на тестируемых входных данных. Она незаметно дает сбой на комбинациях входных данных, которые вы никогда не ожидали, или настолько постепенно ухудшает производительность пакетной обработки, что никто не может связать причину с следствием. Статический анализ обнаруживает их еще до того, как это произойдет.
Обнаружение утечек на всех языках
SMART TS XL отображает долгоживущие цепочки ссылок и неограниченные структуры данных по всей вашей кодовой базе.
ПодробнееЧетыре дефекта состояния файлов, которые стоит обнаружить
Прежде чем перейти к механизмам обнаружения, давайте рассмотрим четкую классификацию того, что вы ищете. Не все проблемы с файлами COBOL одинаковы.
| дефект | Что происходит во время выполнения программы? | Строгость |
|---|---|---|
| Двойное открытие | Открыть уже открытый файл → Состояние файла 41 или аварийное завершение работы. | Высокий |
| ЗАКРЫТО без предварительного ОТКРЫТИЯ | Закрыть файл, который никогда не был открыт → Состояние файла 42 или аварийное завершение работы. | Высокий |
| Отсутствует кнопка CLOSE при обычном выходе. | Файл, оставленный открытым при завершении выполнения (STOP RUN → ОС закрывает его), но не сброшенные буферы создают риск потери данных. | Средний |
| Отсутствует команда CLOSE при завершении работы программы из-за ошибки. | Программа завершается с ошибкой при открытии файла → возможное повреждение записей в файлах VSAM | Высокий |
| Избыточное использование операций ОТКРЫТЬ/ЗАКРЫТЬ в цикле | Файл открывается и закрывается на каждой итерации → значительные накладные расходы на ввод-вывод | Средне-высокая |
Главный вывод: наиболее серьезные дефекты, такие как двойное открытие и закрытие без открытия, приводят к немедленным сбоям во время выполнения или угрозам целостности данных. Дефекты производительности (открытие/закрытие на уровне цикла) не приводят ни к каким ошибкам, а лишь замедляют выполнение пакетных заданий, причину которых никто не смог установить.
Почему это сложно найти вручную?
Сложность заключается не в выявлении закономерности. Любой разработчик знает, что нельзя открывать файл дважды. Сложность в том, что поток управления в COBOL нелинейный, что делает ручную трассировку ненадежной.
Рассмотрим программу со следующей структурой:
кобол
PROCEDURE DIVISION.
MAIN-LOGIC.
PERFORM INITIALIZATION
PERFORM PROCESS-RECORDS
PERFORM TERMINATION
STOP RUN.
INITIALIZATION.
OPEN INPUT CUSTOMER-FILE
OPEN OUTPUT REPORT-FILE
MOVE 0 TO WS-ERROR-FLAG.
PROCESS-RECORDS.
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END MOVE 1 TO WS-EOF-FLAG
END-READ
IF WS-ERROR-FLAG = 1
PERFORM ERROR-EXIT
END-IF
PERFORM UNTIL WS-EOF-FLAG = 1
PERFORM PROCESS-ONE-RECORD
READ CUSTOMER-FILE INTO WS-CUSTOMER-REC
AT END MOVE 1 TO WS-EOF-FLAG
END-EXEC
END-PERFORM.
ERROR-EXIT.
DISPLAY 'Error in processing'
CLOSE CUSTOMER-FILE *> CLOSE here...
STOP RUN.
TERMINATION.
CLOSE CUSTOMER-FILE *> ...and CLOSE here too
CLOSE REPORT-FILE
STOP RUN.
Можно ли обнаружить дефект, не отслеживая каждый путь выполнения? Если WS-ERROR-FLAG внутри установлено значение 1 PROCESS-ONE-RECORD (возможно, посредством вызываемой подпрограммы), потоки управления к ERROR-EXIT, что закрывает файл и останавливает работу. Это правильно. Но если WS-ERROR-FLAG is не установлен, цикл завершается, PROCESS-RECORDS возвращается, и TERMINATION бега, который также закрывается CUSTOMER-FILEДва закрытия, одно открытие. Состояние файла во время выполнения 42.
Это простая программа из трёх секций. В реальных программах двадцать секций, условные вызовы PERFORM, операторы GO TO в устаревшем коде и процедуры обработки исключений, которые, в свою очередь, вызывают другие процедуры. Ручная трассировка не только чревата ошибками, но и не поддаётся контролю.
Как статический анализ моделирует состояние файлов
Статический анализ выявляет эти дефекты путем построения графа потока управления программы и распространения состояния файла по каждому пути.
В ходе анализа каждому файлу присваивается переменная состояния с тремя возможными значениями:
CLOSEDФайл не был открыт или был успешно закрыт.OPENФайл открыт, но ещё не закрыт.UNKNOWNСостояние невозможно определить статически (например, условное открытие в ветви, которая не была полностью проанализирована).
При каждом операторе OPEN анализ проверяет: находится ли этот файл уже в текущем состоянии. OPEN? Если да → двойной дефект открытия.
При каждом операторе CLOSE: находится ли этот файл в текущем состоянии? CLOSED or UNKNOWNЕсли ЗАКРЫТО → закрыть без открытого дефекта.
При каждом вызове команд STOP RUN или EXIT PROGRAM: находится ли какой-либо файл в текущем состоянии? OPENЕсли да, то отсутствует дефект, препятствующий устранению проблемы.
Для циклов анализ проверяет, содержит ли оператор OPEN или CLOSE заголовок цикла, то есть выполняется ли он на каждой итерации.
Осторожно: Самые сложные случаи для любого анализатора — это вызовы функции PERFORM в общие подпрограммы. Если
ERROR-HANDLERФункция PERFORM вызывается из двенадцати разных мест в программе, и состояние файла в момент вызова PERFORM определяет, следует ли закрывать файл внутри неё.ERROR-HANDLERявляется правильным или ошибочным. Анализатор, который не передает состояние через цепочки PERFORM, будет выдавать ложноотрицательные результаты (пропуск реальных дефектов) или ложноположительные результаты (отметка правильного кода).
Открытие/закрытие на уровне цикла: дефект производительности без кода ошибки.
Этот алгоритм не приводит к ошибкам во время выполнения и аномалиям в состоянии файла. Он просто работает медленно, потенциально на порядки медленнее, чем необходимо.
кобол
*> PROBLEMATIC: file opened and closed on every iteration
PROCESS-ALL-REGIONS.
PERFORM VARYING WS-REGION-ID FROM 1 BY 1
UNTIL WS-REGION-ID > 10
OPEN INPUT CUSTOMER-FILE
PERFORM PROCESS-REGION-RECORDS
CLOSE CUSTOMER-FILE
END-PERFORM.
Если файл не разбит физически на регионы, такой подход приводит к излишним накладным расходам. На практике лучше открыть файл один раз, прочитать все записи и применить фильтрацию в памяти или с помощью логики.
Каждое открытие файла VSAM включает в себя поиск в каталоге, инициализацию ACB и выделение буферного пула. Каждое закрытие очищает буферы, обновляет каталог и освобождает ACB. Для файла с десятью регионами это происходит десять раз вместо одного. Для файла с 10 000 записей и тысячей регионов вычисления становятся очень сложными.
кобол
*> CORRECT: open once, filter inside the loop
PROCESS-ALL-REGIONS.
OPEN INPUT CUSTOMER-FILE
PERFORM VARYING WS-REGION-ID FROM 1 BY 1
UNTIL WS-REGION-ID > 10
PERFORM PROCESS-REGION-RECORDS
END-PERFORM
CLOSE CUSTOMER-FILE.
Статический анализ обнаруживает это, выявляя операторы OPEN и CLOSE, которые доминируют в цикле, то есть каждое выполнение тела цикла включает в себя выполнение операторов OPEN или CLOSE. Для обнаружения требуется, чтобы граф потока управления представлял структуру цикла, а не просто последовательность операторов.
СОСТОЯНИЕ ФАЙЛА: Сигнал состояния файла вашей программы, если вы его отметите.
Каждая операция с файлом в COBOL устанавливает поле FILE STATUS, определенное в записи FILE-CONTROL. Программа, которая проверяет FILE STATUS после каждого открытия, чтения, записи и закрытия, может обнаруживать и реагировать на любые аномалии состояния файла во время выполнения. Программа, которая игнорирует FILE STATUS, работает вслепую.
Контрольный список: Коды состояния файлов, которые должен знать каждый разработчик COBOL.
00Успешное завершение10Конец файла (ЧИТАТЬ: больше нет записей)35Файл не найден (OPEN INPUT: файл не существует)41Файл уже открыт (OPEN для файла, находящегося в состоянии OPEN)42Файл не открыт (CLOSE или READ для файла, находящегося в состоянии CLOSED)47Попытка чтения файла, не открытого (вход или ввод/вывод).48Попытка записи в неоткрытый файл OUTPUT, IO или EXTEND97Файл успешно открыт (специфично для IBM, в некоторых средах).
кобол
FILE-CONTROL.
SELECT CUSTOMER-FILE
ASSIGN TO CUSTFILE
FILE STATUS IS WS-CUST-FILE-STATUS.
WORKING-STORAGE SECTION.
01 WS-CUST-FILE-STATUS PIC XX.
PROCEDURE DIVISION.
OPEN INPUT CUSTOMER-FILE
IF WS-CUST-FILE-STATUS NOT = '00'
DISPLAY 'OPEN failed: ' WS-CUST-FILE-STATUS
PERFORM ABEND-ROUTINE
END-IF.
Осторожно: Распространенная ошибка при обслуживании заключается в указании параметра FILE STATUS в записи FILE-CONTROL, но без ссылки на него.
WS-CUST-FILE-STATUSВ разделе ПРОЦЕДУР. Это поле заполняется после каждой операции с файлом, но если его не проверяет никакой код, программа проходит мимо ошибок молча. Статический анализ может выявить такой шаблон: СТАТУС ФАЙЛА объявлен, но никогда не используется в условной логике.
Три реальных примера ошибок в COBOL, приводящих к их возникновению.
Шаблон 1: Проблема совместного завершения обработки ошибок
Программа имеет несколько секций обработки, каждая со своим обработчиком ошибок. Несколько обработчиков ошибок закрывают файл перед возвратом. Обычная процедура завершения также закрывает файл. Если возникает ошибка и восстановление продолжается после обработки обработчиком ошибок, файл закрывается дважды.
кобол
VALIDATE-RECORDS.
READ CUSTOMER-FILE INTO WS-REC
AT END MOVE 1 TO WS-EOF
END-READ
IF WS-CUST-FILE-STATUS NOT = '00' AND '10'
CLOSE CUSTOMER-FILE *> closes here on read error
PERFORM WRITE-ERROR-LOG
GO TO TERMINATION *> skips to close in TERMINATION?
END-IF.
TERMINATION.
CLOSE CUSTOMER-FILE *> double close if GO TO reached here
CLOSE REPORT-FILE
STOP RUN.
GO TO TERMINATION передает управление непосредственно в TERMINATION, которая выполняет свои собственные CLOSE CUSTOMER-FILE. СТАТУС ФАЙЛА 42.
Решение: Используйте единую централизованную процедуру закрытия. Каждый путь выхода вызывает одну и ту же процедуру, которая проверяет, открыт ли файл, прежде чем закрыть его.
Шаблон 2: Условное открытие
Файл открывается условно, только когда активен определенный режим обработки. Но команда CLOSE является безусловной и выполняется на каждом этапе выполнения, включая те, где файл никогда не открывался.
кобол
INITIALIZATION.
IF WS-PROCESSING-MODE = 'FULL'
OPEN OUTPUT AUDIT-FILE *> only opened in FULL mode
END-IF.
TERMINATION.
CLOSE AUDIT-FILE *> ALWAYS closes -- FILE STATUS 42 in PARTIAL mode
STOP RUN.
Этот дефект незаметен при тестировании, если тесты всегда запускаются в ПОЛНОМ режиме. В производственной среде он проявляется при первом запуске в ЧАСТИЧНОМ режиме.
Шаблон 3: ВЫПОЛНЕНИЕ во всех модулях компиляции
В программах, использующих вызов CALL для запуска подпрограмм, файл, открытый в основной программе, может быть передан подпрограмме, которая его закрывает, после чего основная программа снова его закрывает.
кобол
*> Main program
CALL 'SUBPROG1' USING CUSTOMER-FILE-STATUS
*> SUBPROG1 closes CUSTOMER-FILE internally
CLOSE CUSTOMER-FILE *> double close
Для выявления этой закономерности требуется межпроцедурный анализ , отслеживающий состояние файла через границу CALL до поведения подпрограммы, что невозможно обнаружить с помощью простого внутрипрограммного анализа.
Что должен делать статический анализатор, чтобы хорошо справляться с этой задачей?
Не все инструменты статического анализа обрабатывают состояние файлов COBOL с одинаковой глубиной. Вот контрольный список для оценки возможностей:
Обрабатывает ли инструмент цепочки вызовов PERFORM? Программы на COBOL используют PERFORM для вызова именованных абзацев и разделов. Файловые операции внутри цели PERFORM должны быть доступны для отслеживания состояния вызывающей стороны.
Обрабатывает ли инструмент встроенные команды PERFORM VARYING (циклы)? Обнаружение циклов необходимо для идентификации операций OPEN/CLOSE, в которых преобладают циклы.
Отслеживает ли он состояние при переходе по узлу GO TO? В устаревших программах COBOL широко используется GO TO. Анализатор, который не может отслеживать ребра GO TO в графе потока управления, пропустит целые классы дефектов.
Обрабатывает ли он операции COPY и REPLACE? Код обработки файлов часто находится в членах COPY. Анализатор, который не разворачивает члены COPY перед анализом, пропустит операции, определенные во включаемом коде.
Проверяет ли система использование параметра FILE STATUS? Полный анализ проверяет не только то, объявлен ли параметр FILE STATUS, но и то, проверяется ли он фактически после каждой операции.
Выполняет ли он межпроцедурный анализ? Вызванные подпрограммы, выполняющие файловые операции, создают зависимости состояния между программами. Для получения истинного покрытия требуется прослеживание цепочек вызовов программ.
Главный вывод: инструмент, проверяющий только один абзац или раздел, пропустит большинство реальных дефектов. Дефекты, имеющие значение в производственной среде, те, на выявление которых ушли годы, а на диагностику — часы, — это те, которые затрагивают несколько разделов, условные ветвления и вызываемые процедуры.
Что делать с результатами: структура приоритезации
Когда статический анализ выявляет дефекты файлов, не все обнаруженные ошибки одинаковы. Вот как их классифицировать:
Исправьте немедленно (перед следующим запуском партии):
- Двойное открытие файла (OPEN) в любом пути выполнения, где статус файла 41 может привести к аварийному завершению программы.
- Закрывать без открытия в путях, которые выполняются в производственной среде.
- Отсутствует оператор CLOSE в путях завершения обработки ошибок для файлов VSAM (риск нарушения целостности записи).
Расписание следующего спринта:
- Циклически доминирующие функции ОТКРЫТЬ/ЗАКРЫТЬ с измеримым влиянием на окно пакета
- Отсутствуют проверки статуса файла для критически важных файлов (аудит, транзакция, отчет).
- Условное открытие с безусловным закрытием в многорежимных программах
Отслеживать и решать проблемы во время следующего этапа модернизации:
- Отсутствуют заявления о состоянии файла.
- Функция CLOSE активируется только при обычных путях выхода (это обрабатывается операционной системой, но это плохая практика).
- Межпроцедурные дефекты, исправление которых требует координации изменений в нескольких программах.
Как SMART TS XL Анализирует состояние файла COBOL
SMART TS XLАвтора статический анализ кода Создает модель потока управления для каждой программы COBOL в среде, разворачивая члены COPY, отслеживая цепочки PERFORM, трассируя связи GO TO и распространяя состояние файла через каждую ветвь каждого условного оператора. Это не сканирование с сопоставлением шаблонов; это структурная модель того, что программа фактически делает в каждой доступной точке своего выполнения.
Возможность отображения зависимостей приложений расширяет это на межпроцедурное измерение: когда программа COBOL вызывает подпрограмму, выполняющую операции с файлами, карта зависимостей представляет эту взаимосвязь, позволяя проводить анализ, выходящий за пределы единиц компиляции. Двойные ошибки OPEN, затрагивающие основную программу и вызываемую подпрограмму, видны в структурной модели таким образом, каким они не видны при внутрипрограммном анализе.
Функция корпоративного поиска позволяет масштабно использовать полученные результаты: за считанные секунды можно найти каждую программу в портфеле, которая открывает определенный набор данных, каждый элемент COPY, содержащий оператор CLOSE, каждую программу, у которой целевой объект PERFORM содержит оператор OPEN, по миллионам строк кода COBOL. Для команды модернизации, готовящейся к миграции пакетной обработки данных в облако, эта функция поиска превращает многонедельный ручной аудит в целенаправленный запрос.
Возможность расширения JCL добавляет оперативный контекст: какие шаги задания JCL вызывают каждую программу, на какие наборы данных ссылается каждый оператор DD и как обработка файлов в коде COBOL связана с физическими наборами данных, определенными в JCL. Дефект CLOSE в файле, который JCL определяет как критически важный выходной набор данных, имеет более высокий приоритет, чем тот же дефект во временном рабочем файле, и для определения приоритета необходимо знать контекст JCL каждой программы COBOL.
Главный вывод: состояние файла — это лишь одно измерение.
Избыточные операции с файлами — это частный случай более общей проблемы: программы на COBOL, развивавшиеся на протяжении десятилетий, содержат зависящее от состояния поведение — состояние файла, состояние переключателя, состояние счетчика, — которое можно правильно понять только путем отслеживания каждого возможного пути выполнения через полный граф потока управления.
Проверка такого рода кода человеком — медленный, неполный и непоследовательный процесс. Разные разработчики находят разные дефекты. Один и тот же разработчик, дважды проверивший одну и ту же программу, обнаружит разные дефекты. Статический анализ — это систематический процесс: он применяет одни и те же правила к каждому пути в каждой программе, каждый раз, без усталости и предположений.
Для команд, управляющих большими портфелями COBOL-приложений, будь то для текущего сопровождения, программ модернизации устаревших систем или для аудитов соответствия, отдача от систематического анализа состояния файлов заключается не только в обнаруженных дефектах. Это уверенность в том, что портфель был проанализирован полностью и что оставшиеся дефекты известны, а не скрыты.