Скрипты необходимы чтобы автоматизировать действия, производимые в отладчике вручную. К примеру, можно совершить 1000 шагов в цикле, изменять и получать значения переменных. Кроме того данный механизм позволит вам выполнять следующие действия:
- Изменять ход выполнения программы (путем изменения переменных)
- Анализировать значения переменных и ход выполнения программы
- Создавать журналы выполнения программы (трассировки)
- Создавать точки наблюдения и остановок, в зависимости от условий
Скрипты используют интерфейс отладчика и запускаются на его стороне. Они не могу непосредственно изменять код программы, но могут повлиять на ход его выполнения. Окно скриптов в отладчике:
- Редактор скрипта, как видно скрипт представляет собой класс с методами, через которые получает доступ к отладчику.
- Триггер – определяет событие, при котором будет вызван скрипт. Скрипт может быть вызван напрямую (Debugger Simple Step – execute directly), либо из точки наблюдения или остановки.
- В этой области как не трудно догадаться скрипт можно сохранить (как Z программу, либо отдельным файлом)
- С помощью кнопки Start Script происходит выполнение скрипта
- Script Wizard предоставляет доступ к заранее подготовленным шаблонам – использующим возможности интерфейса отладчика, например с его помощью можно вставить метод чтения значений переменной.
- Если вы создаете трассировки, то их список можно увидеть на вкладке – Traces Files. Список трассировок можно так же посмотреть отдельно в транзакции SAS, из нее можно редактировать и сохранять скрипты. Трассировки могут быть следующих видов: Statement Trace (Записывает информацию о текущей строке исходного кода, которая обрабатывается в скрипте), Trace of the Call Hierarchy (Запись иерархии вызовов), User Specific Trace (Определенный пользователем вид).
Далее рассмотрим созданный локальный класс скрипта. Скрипт наследуется от глобального класса — CL_TPDA_SCRIPT_CLASS_SUPER. По умолчанию для него создаются следующие методы:
- Prologue – техническая инициализация скрипта. Когда скрипт запускается, отладчик сначала выполняет данный метод. В реализации супер класса данного метода происходит обновление информации об исходном коде программы (super->abap_source), если вам не нужна данная информация, вы можете ускорить выполнение скрипта, закомментировав вызов метода prologue супер класса.
- Init – инициализация скрипта. Вызов происходит после prologue только один раз, Вы можете использовать его для инициализации внутренних данных либо для запроса у пользователя информации необходимой для скрипта.
- Script – метод вызывающийся при каждом срабатывании триггера, или прямом выполнении скрипта. В данный метод передается параметр p_trigger – обозначающий тип триггера, который его запустил. Кроме того у него есть два исключения, cx_tpda_stop_scripting_request – вызвав данное исключение вы прекратите срабатывание скрипта по данному триггеру, cx_tpda_script_continue – продолжает выполнение работы скрипта. Для того чтобы позволить пользователю выбрать хочет он продолжать работу скрипта далее или нет используйте метод — me->break( ).
- End – конец выполнения скрипта. Срабатывает когда пользователь отменяет работу скрипта, либо когда он сам ее завершает.
Супер класс поддерживает следующие сервисы:
- abap_source – данные об исходном коде программы, например получить имя программы можно следующим образом — currr_prog = abap_source->program( )
- bp – работа с точками остановок
- wp – работа с точками наблюдения
- debugger_control – позволяет управлять отладчиком на уровне шага, перейти на шаг вперед (назад)
- dynpro – информация о экранах
- specials – прочая системная информация (Работа с памятью, вызов commit work и пр.)
Автоматизация рутинных действий
Допустим, есть следующая программа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
REPORT zscripting_control. DATA: gv_rc TYPE char1. DO 1000 TIMES. PERFORM test USING gv_rc. IF gv_rc = abap_true. MESSAGE 'Error' TYPE 'E'. ENDIF. ENDDO. WRITE 'Все в порядке'. FORM test USING lv_rc TYPE char1. lv_rc = abap_true. ENDFORM. |
Как видно из кода, переменной gv_rc всегда будет присваиваться значение true, таким образом, чтобы пройти весь цикл нам необходимо в каждой итерации цикла подменять значение переменной, что само по себе накладно (для 1000 итераций J). Воспользуемся скриптами. Запустите программу в отладке и перейдите на закладку Scripts. В качестве триггера выберите Breakboint Reached, создайте точку остановки для процедуры Test.
В момент, когда процедура будет запущена, сработает метод Script. Нам необходимо чтобы при этом отладчик вышел из процедуры и подменил значение переменной gv_rc. Как уже упоминалось выше для контроля над процессом отладки в классе super, есть подкласс – debugger_controller. С помощью его метода – debug_step мы можем выполнить нужный нам шаг, в качестве команды задать — CL_TPDA_SCRIPT_DEBUGGER_CTRL=>debug_step_over. Далее с помощью помощника выберем изменение значений переменной. В итоге получиться следующий код:
1 2 3 4 5 6 7 8 9 10 |
METHOD script. me->debugger_controller->debug_step( CL_TPDA_SCRIPT_DEBUGGER_CTRL=>debug_step_over ). TRY. cl_tpda_script_data_descr=>change_value( p_new_value = '' p_varname = 'gv_rc' ). CATCH cx_tpda_varname . CATCH cx_tpda_scr_auth . ENDTRY. ENDMETHOD. "script |
Далее нажмите кнопку запуска скрипта, если все сделано правильно программа пройдет весь цикл, не выдав ошибочного сообщения, написав «Все в порядке».
Интерактивные скрипты
Допустим, необходимо чтобы при смене ABAP стека каждый раз, когда мы переходим в определенную программу, вызывался скрипт, но при этом мы хотим сделать скрипт более универсальным, т.е. имя программы необходимо задавать при его инициализации. Для запроса имени программы в методе init используем ФМ POPUP_GET_VALUES, имя программы будем хранить в глобальной переменной нашего класса. Поставим триггер на точку прерывания при смене ABAP стека.
В методе script сравним имя программы в текущем контексте отладчика с именем указанным при инициализации, если оно совпало, вызовем прерывание скрипта. Изменим предыдущую программу (создадим дополнительно новую – ZTMP3 с такой же процедурой, которая описана выше):
1 2 3 4 5 6 7 8 9 10 11 12 |
REPORT zscripting_control. DATA: gv_rc TYPE char1. DO 1000 TIMES. PERFORM test(ZTMP3) USING gv_rc. IF gv_rc = abap_true. MESSAGE 'Error' TYPE 'E'. ENDIF. ENDDO. WRITE 'Все в порядке'. |
Исходный код скрипта будет выглядеть следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
CLASS lcl_debugger_script DEFINITION INHERITING FROM cl_tpda_script_class_super . PUBLIC SECTION. METHODS: prologue REDEFINITION, init REDEFINITION, script REDEFINITION, end REDEFINITION. PRIVATE SECTION. DATA PROGRAM TYPE sy-repid. ENDCLASS. "lcl_debugger_script DEFINITION CLASS lcl_debugger_script IMPLEMENTATION. METHOD prologue. super->prologue( ). ENDMETHOD. "prologue METHOD init. DATA: l_dd03p_tab TYPE TABLE OF dd03p, l_key TYPE abap_keydescr, l_fields TYPE TABLE OF sval, l_field TYPE sval, l_rc(1) TYPE C. l_field-tabname = 'TPDA_DYNP_SRC_INFO'. l_field-fieldname = 'PROGRAM'. l_field-fieldtext = 'Programm'(prg). l_field-VALUE = SPACE. l_field-field_obl = abap_true. APPEND l_field TO l_fields. CALL FUNCTION 'POPUP_GET_VALUES' EXPORTING popup_title = 'Укажите имя программы'(hd1) IMPORTING RETURNCODE = l_rc TABLES FIELDS = l_fields EXCEPTIONS error_in_fields = 1 OTHERS = 2. READ TABLE l_fields INTO l_field INDEX 1. PROGRAM = l_field-VALUE. ENDMETHOD. "init METHOD script. IF PROGRAM = abap_source->program( ). me->break( ). ENDIF. ENDMETHOD. "script METHOD end. ENDMETHOD. "end ENDCLASS. "lcl_debugger_script IMPLEMENTATION |
После запуска скрипта, его остановка произойдет, как только мы изменим ABAP стек, вызвав процедуру из другой программы.
Динамическое создание точек прерывания и наблюдения
Допустим необходимо вызвать прерывание в момент, когда программа читает внутреннюю таблицу. Сделаем это используя встроенный механизм сканирования исходного кода. Исходный код программы:
1 2 3 4 5 6 7 8 |
REPORT zscripting_control. DATA: gt_mara TYPE STANDARD TABLE OF mara WITH HEADER LINE. SELECT matnr FROM mara INTO CORRESPONDING FIELDS OF TABLE gt_mara UP TO 10 ROWS. DELETE gt_mara INDEX 5. READ TABLE gt_mara INDEX 3. |
Исходный код скрипта для прерывания в момент чтения внутренней таблицы можно загрузить из стандартного подготовленного SAP скрипта — RSTPDA_SCRIPT_BP_READ_TAB. Его исходный код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
CLASS lcl_debugger_script DEFINITION INHERITING FROM cl_tpda_script_class_super . PUBLIC SECTION. METHODS: prologue REDEFINITION, init REDEFINITION, script REDEFINITION, end REDEFINITION. PRIVATE SECTION. DATA range_it TYPE tpda_range_it. ENDCLASS. "lcl_debugger_script DEFINITION CLASS lcl_debugger_script IMPLEMENTATION. METHOD prologue. *** generate abap_source (source handler for ABAP) super->prologue( ). ENDMETHOD. "prolog METHOD init. DATA l_cancel TYPE flag. CALL FUNCTION 'TPDA_SCRIPT_CALL_SEL_SCREEN' EXPORTING p_screen = if_tpda_sel_screens=>c_scr_itab IMPORTING p_range_it = range_it p_cancel = l_cancel. IF l_cancel = abap_true. MESSAGE s144(tpda) . RAISE EXCEPTION TYPE cx_tpda_stop_scripting_request. ELSEIF range_it IS INITIAL. MESSAGE s144(tpda) . RAISE EXCEPTION TYPE cx_tpda_stop_scripting_request. ENDIF. ENDMETHOD. "init METHOD script. DATA: l_itabs_it TYPE cl_tpda_script_scan_itab_read=>TY_IT_TABLES, l_itab LIKE LINE OF l_itabs_it, l_scan_object TYPE REF TO cl_tpda_script_scan_itab_read, l_curr_itab TYPE STRING. TRY. l_scan_object ?= cl_tpda_script_scan=>scan( p_program = abap_source->program( ) p_include = abap_source->include( ) p_line = abap_source->line( ) ). l_itabs_it = l_scan_object->IT_TABLES( ). LOOP AT l_itabs_it INTO l_itab. IF l_itab IN range_it. me->break( ). ENDIF. ENDLOOP. CATCH cx_sy_move_cast_error. MESSAGE s049(tpda_script). me->break( ). CATCH cx_tpda_script_scan_macro. MESSAGE s048(tpda_script). me->break( ). CATCH cx_tpda_script_scan. MESSAGE s049(tpda_script). me->break( ). CATCH cx_tpda_varname . MESSAGE s049(tpda_script). me->break( ). CATCH cx_tpda_script_no_simple_type . MESSAGE s049(tpda_script). me->break( ). CATCH CX_ROOT. MESSAGE s049(tpda_script). me->break( ). ENDTRY. ENDMETHOD. "script METHOD end. ENDMETHOD. "end ENDCLASS. "lcl_debugger_script IMPLEMENTATION |
Данный скрипт по умолчанию загружает точку остановки на моменте вызова READ TABLE для внутренних таблиц.
Использование трассировок
Как говорилось выше можно использовать разные виды трассировок. Следующий пример покажет, как использовать — Statement Trace. Задача: найти все вызовы проверки полномочий в стандартной транзакции – ME23N (во время запуска).
- Заходим в SE93, указываем ME23n, жмем – отладку (Тест – отладка).
- Переходим на вкладку скрипт, используя заранее подготовленный скрипт загрузим — RSTPDA_SCRIPT_STATEMENT_TRACE
- В качестве триггера создадим точку на вызов — AUTHORITY-CHECK
- Запускаем скрипт, жмем – Continue
- После запуска транзакции (нас интересовал только запуск) в командной строке набираем /h и жмем F3 – вылетит отладчик в котором необходимо завершить работу скрипта – EXIT SCRIPT.
- Запускаем транзакцию SAS (либо прямо в отладчике через закладку трассировок) – смотрим трассировку.
В итоге получится что-то вроде:
Добавление в журнал трассировок своих данных (User Specific Trace).
Задача: есть таблица с данными ОЗМ необходимо в журнал затащить все записи, у которых БЕИ отличается от M3. В журнале должен быть номер материала и его ЕИ. Пример программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
REPORT zscripting_control. DATA: gt_mara TYPE STANDARD TABLE OF mara WITH HEADER LINE, p_dummy TYPE C. FIELD-SYMBOLS: <fs_mara_line> TYPE mara. SELECT * FROM mara INTO CORRESPONDING FIELDS OF TABLE gt_mara UP TO 10 ROWS. LOOP AT gt_mara. ENDLOOP. WRITE 'TEST'. |
- Создаем точку наблюдения для переменной gt_mara-meins <> ‘M3’
- С помощью метода подкласса trace – add_custom_info добавляем свои данные.
- Метод script будет иметь следующий вид:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
METHOD script. DATA: cust_matnr TYPE tpda_var_value, cust_meins TYPE tpda_var_value, trace_entry TYPE tpda_trace_custom. trace->add_event_info( ). cust_matnr = cl_tpda_script_data_descr=>get_simple_value( p_var_name = 'GT_MARA-MATNR' ). cust_meins = cl_tpda_script_data_descr=>get_simple_value( p_var_name = 'GT_MARA-MEINS' ). IF cust_meins <> 'M3'. trace_entry-VALUE = 'Материал:' && SPACE && cust_matnr && ' ЕИ:' && SPACE && cust_meins. trace->add_custom_info( p_trace_entry = trace_entry ). ENDIF. ENDMETHOD. |
После этого если у вас были такие материалы, они отобразятся в трассировке:
Скажите — должна быть какая-то версия системы определенная? У меня в системе нет ФМ и программ, о которых упоминается в статье — ‘TPDA_SCRIPT_CALL_SEL_SCREEN’ например.
У нас стоит SAP NetWeaver 7.0 EhP2
Посмотрю отдельно с какой версии этот ФМ появился напишу.
Входит в SAP_BASIS версия 702, в 701 его нет
Делаю скрипт + точку прерывания на строку в коде
Записываю данные к-е есть в этом месте программы в журнал для SAS
Запускаю и скрипт и у меня каждый раз заходит в точку прерывания
Как сделать чтобы скрипт отработал и записал данные в журнал — не проваливаясь каждый раз в отладчик