天天看點

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

所謂條件斷點,就是設定在某行語句上的斷點,并不總是會觸發,而是僅當滿足一定條件時才觸發。

條件斷點的使用場合是什麼?

舉個簡單的例子,下圖第15行ADD語句設定一個斷點。因為它在一個具有1000行的内表循環體内,是以正常情況下會觸發1000次。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

假設我們在調試一個bug,這個bug當循環到第999次時才出現,那我們前998次的單步調試都是無效的。最高效的做法,就是借助條件斷點的概念,讓斷點在代碼執行到第999次循環時,觸發且僅觸發一次。

本文介紹實作ABAP條件斷點的三種方式。也歡迎大家分享自己最喜歡用的且本文尚未提到的條件斷點技術。

方法一:給ABAP斷點維護觸發條件

在ABAP調試器裡點選Break/Watchpoints面闆,建立一個斷點:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

在Free Condition Entry裡維護這個斷點的觸發條件。

回到我上面的例子,我的内表裡包含了從1遞增到1000的整數,總共1000條記錄,而我的觸發條件維護為<data> = 22. 顯然,這個斷點在第22次循環時,唯一觸發一次。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

維護完畢後,我們在斷點面闆裡看到了這個建立的斷點:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

按F8繼續調試,斷點有且僅觸發了一次,此時<data>的值為22,正好符合我們維護的觸發條件,成功。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

方法二:利用ABAP調試器裡的觀察點(Watchpoint)

打開 ABAP 的調試器,此處建立Watchpoint:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

我們知道在LOOP循環體内,系統變量sy-tabix會自動賦以目前的循環次數。是以我們在Watchpoint的觸發條件裡,維護成sy-tabix = 22, 也可以達到在第22次循環時觸發的目的。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

Watchpoint建立好之後顯示如下:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

按F8繼續調試,程式果然在第22次循環時觸發了:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

并且調試器裡彈出一條提示資訊:Watchpoint reached

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

方法三:ABAP Debugger Script

ABAP Debugger Script這項技術,在SAP研究院内部用的很廣泛。

回到上面的例子,我們将編寫一段簡單的ABAP代碼,去控制目标ABAP代碼的斷點觸發。

在ABAP調試器裡,點選Script标簽頁,建立一個新的ABAP腳本:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

我們想用ABAP腳本監控ABAP代碼裡某個簡單變量的值變化,是以使用腳本建立向導裡的Variable Value(for Simple Variables):

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

這個向導會自動幫我們生成ABAP腳本,其實也就是一段ABAP代碼了,這段代碼可以用程式設計的方式,在調試器激活的上下文裡,擷取某個ABAP變量的值。

下圖腳本的語義很清晰,擷取調試器裡field symbol <data>的值,存儲在臨時變量lv_result裡。如果該變量的值為22,就調用ABAP腳本的工具方法break,觸發斷點。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

把這段腳本通過上圖的Save As按鈕另存下來,取名ZJERRY_TEST.

然後重新執行我們的測試代碼, 使用Load Script加載剛才儲存的ABAP腳本:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

點選Start Script執行腳本:

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

斷點再次如期觸發.

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

這個 script 的源代碼如下:

*---------------------------------------------------------------------*
*       CLASS lcl_debugger_script DEFINITION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS lcl_debugger_script DEFINITION INHERITING FROM  cl_tpda_script_class_super  .

  PUBLIC SECTION.
    METHODS: prologue  REDEFINITION,
      init    REDEFINITION,
      script  REDEFINITION,
      end     REDEFINITION.
    INTERFACES: if_tpda_script_w_input,
      if_tpda_script_w_output.

  PRIVATE SECTION.
    DATA: entity_name TYPE string.
    DATA: value TYPE string.
    DATA: output TYPE tpda_transfer_it_unsorted.
    DATA: bol_object_name TYPE crmt_ext_obj_name.
    METHODS get_attribute
      IMPORTING io_oref_descr     TYPE REF TO cl_tpda_script_orefdescr
                iv_attribute_name TYPE string
      RETURNING VALUE(ro_descr)   TYPE REF TO cl_tpda_script_data_descr.

ENDCLASS.                    "lcl_debugger_script DEFINITION
*---------------------------------------------------------------------*
*       CLASS lcl_debugger_script IMPLEMENTATION
*---------------------------------------------------------------------*
*
*---------------------------------------------------------------------*
CLASS lcl_debugger_script IMPLEMENTATION.

  METHOD prologue.
*** generate abap_source (source handler for ABAP)
    super->prologue( ).
  ENDMETHOD.                    "prolog

  METHOD if_tpda_script_w_input~get_parameters.

    DATA lt_input      TYPE tpda_transfer_it.
    DATA ls_input      TYPE tpda_transfer_struc.

    ls_input-id   = 'ENTITY'.
    APPEND ls_input TO lt_input.
    p_parameters_it = lt_input.

  ENDMETHOD.                    "if_tpda_script_w_input~get_parameters

  METHOD if_tpda_script_w_input~set_parameter_values.

*   Tabelle mit Inputparameter und Wert
    DATA lt_input     TYPE tpda_transfer_it.
    DATA ls_input     TYPE tpda_transfer_struc.

    lt_input = p_parameter_values_it.
    LOOP AT lt_input INTO ls_input.
      IF ls_input-id = 'ENTITY'.
        entity_name = ls_input-value.
      ENDIF.
    ENDLOOP.

  ENDMETHOD.                    "if_tpda_script_w_input~set_parameter_values

  METHOD init.
*** insert your initialization code here
  ENDMETHOD.                    "init

  METHOD script.

    DATA lr_data_descr      TYPE REF TO cl_tpda_script_data_descr.
    DATA lr_struct_descr    TYPE REF TO cl_tpda_script_structdescr.
    DATA lr_cx              TYPE REF TO cx_root.
    DATA ls_quick           TYPE tpda_scr_quick_info.
    DATA lv_name            TYPE string.
    DATA lt_struct          TYPE tpda_scr_struct_comp_it.
    DATA ls_struct          TYPE tpda_scr_struct_comp.
    DATA ls_output          TYPE tpda_transfer_struc.
    DATA lr_symbsimple      TYPE REF TO tpda_sys_symbsimple.
    DATA ls_varinfo         TYPE tpda_quick_vars.

    FIELD-SYMBOLS: <lv_value> TYPE any.

    TRY.
        CLEAR output.

*        BREAK-POINT.

        ls_varinfo = cl_tpda_script_data_descr=>get_variable_info( 'LO_PRODUCT' ).

*       get object type name
        IF ls_varinfo-varvalue = 'OBJECT'.
*         class instance passed directly
          lv_name = entity_name && '-CONTAINER_PROXY->DATA_REF->OBJECT_NAME'.
        ELSE.
*         variable of class instance passed
          lv_name = ls_varinfo-varvalue && '-CONTAINER_PROXY->DATA_REF->OBJECT_NAME'.
        ENDIF.

        ls_quick = cl_tpda_script_data_descr=>get_quick_info( lv_name ).
        ASSIGN ls_quick-quickdata TO <lv_value>.
        lr_symbsimple ?= <lv_value>.
        bol_object_name = lr_symbsimple->valstring.

*       get content
        IF ls_varinfo-varvalue = 'OBJECT'.
          lv_name = entity_name && '-CONTAINER_PROXY->DATA_REF->ATTRIBUTE_REF->*'.
        ELSE.
          lv_name = ls_varinfo-varvalue && '-CONTAINER_PROXY->DATA_REF->ATTRIBUTE_REF->*'.
        ENDIF.

        lr_data_descr = cl_tpda_script_data_descr=>factory( lv_name ).
        lr_struct_descr ?= lr_data_descr.

        lr_struct_descr->components(
          IMPORTING
*            p_components_it      =
            p_components_full_it =  lt_struct
        ).

        LOOP AT lt_struct INTO ls_struct.
          ls_output-id = ls_struct-compname.
          TRY.
              ASSIGN ls_struct-symbquick-quickdata TO <lv_value>.
              lr_symbsimple ?= <lv_value>.
              ls_output-value = lr_symbsimple->valstring.
            CATCH cx_root INTO lr_cx.
              ls_output-value = lr_cx->get_text( ).
          ENDTRY.
          APPEND ls_output TO output.
        ENDLOOP.

        DATA lt_col_alv                   TYPE tpda_script_service_source_tab.
        DATA ls_col_alv                   LIKE LINE OF lt_col_alv.
        ls_col_alv-fieldname = ls_col_alv-content = 'ID'.
        APPEND ls_col_alv TO lt_col_alv.
        ls_col_alv-fieldname = ls_col_alv-content = 'VALUE'.
        APPEND ls_col_alv TO lt_col_alv.

        CALL METHOD cl_tpda_script_data_display=>data_display
          EXPORTING
            p_list_header = 'Query Selection Parameters'
            p_column_it   = lt_col_alv
            p_popup       = 'X'
          CHANGING
            p_data_it     = output.

*        BREAK-POINT.
      CATCH cx_root INTO lr_cx.
        BREAK-POINT.                                       "#EC NOBREAK
        value = lr_cx->get_text( ).
    ENDTRY.
  ENDMETHOD.                    "script

  METHOD end.
*** insert your code which shall be executed at the end of the scripting (before trace is saved)
*** here

  ENDMETHOD.                    "end

  METHOD if_tpda_script_w_output~get_parameter_values.

    DATA lt_param TYPE tpda_transfer_it_unsorted.
    DATA ls_param TYPE tpda_transfer_struc.

    ls_param-id = 'VARIABLE'.
    ls_param-value = entity_name.
    APPEND ls_param TO lt_param.
    ls_param-id = 'OBJECT_NAME'.
    ls_param-value = bol_object_name.
    APPEND ls_param TO lt_param.

    APPEND INITIAL LINE TO lt_param.

    APPEND LINES OF output TO lt_param.

    p_parameter_values_it = lt_param.

  ENDMETHOD.                    "if_tpda_script_w_output~get_parameter_values

  METHOD get_attribute.

    DATA lr_oref_descr     TYPE REF TO cl_tpda_script_orefdescr.
    DATA lr_object_descr   TYPE REF TO cl_tpda_script_objectdescr.
    DATA ls_varinfo        TYPE tpda_quick_vars.
    DATA lv_longname       TYPE string.

    DATA lt_attributes TYPE tpda_script_object_attribut_it.

    lr_oref_descr   = io_oref_descr.
    lr_object_descr = lr_oref_descr->get_object_handle( ).

    lt_attributes = lr_object_descr->attributes( ).

    ro_descr = lr_object_descr->get_attribut_handle( lv_longname  ).

  ENDMETHOD.                    "get_oref_attribute

ENDCLASS.                    "lcl_debugger_script IMPLEMENTATION
           

我們知道,像如圖一這種類的靜态屬性,因為不屬于類的執行個體所有,是以調試到這個類的方法内部時,隻能通過圖二示範的兩種方式在調試器顯示該屬性的值。而一旦調試到該類方法的外部,通常就隻能通過"類名=>屬性名"的方式來顯示靜态屬性值(圖三)。其實還有一種方式,如圖四和圖五所示。

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

圖一:ABAP類的靜态屬性

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

圖二:如何在ABAP調試器裡檢視類的靜态屬性

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

圖三:在調試器裡跳出類的方法之後,如何檢視靜态屬性

實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結
實作 ABAP 條件斷點的三種方式分享方法一:給ABAP斷點維護觸發條件方法二:利用ABAP調試器裡的觀察點(Watchpoint)方法三:ABAP Debugger Script總結

總結

繼續閱讀