所谓条件断点,就是设置在某行语句上的断点,并不总是会触发,而是仅当满足肯定条件时才触发。

条件断点的应用场合是什么?

举个简略的例子,下图第15行ADD语句设置一个断点。因为它在一个具备1000行的内表循环体内,所以失常状况下会触发1000次。

假如咱们在调试一个bug,这个bug当循环到第999次时才呈现,那咱们前998次的单步调试都是有效的。最高效的做法,就是借助条件断点的概念,让断点在代码执行到第999次循环时,触发且仅触发一次。

本文介绍实现ABAP条件断点的三种形式。也欢送大家分享本人最喜爱用的且本文尚未提到的条件断点技术。

办法一:给ABAP断点保护触发条件

在ABAP调试器里点击Break/Watchpoints面板,新建一个断点:

在Free Condition Entry里保护这个断点的触发条件。

回到我下面的例子,我的内表里蕴含了从1递增到1000的整数,总共1000条记录,而我的触发条件保护为<data> = 22. 显然,这个断点在第22次循环时,惟一触发一次。

保护结束后,咱们在断点面板里看到了这个新建的断点:

按F8持续调试,断点有且仅触发了一次,此时<data>的值为22,正好合乎咱们保护的触发条件,胜利。

办法二:利用ABAP调试器里的观察点(Watchpoint)

关上 ABAP 的调试器,此处创立Watchpoint:

咱们晓得在LOOP循环体内,零碎变量sy-tabix会主动赋以以后的循环次数。因而咱们在Watchpoint的触发条件里,保护成sy-tabix = 22, 也能够达到在第22次循环时触发的目标。

Watchpoint创立好之后显示如下:

按F8持续调试,程序果然在第22次循环时触发了:

并且调试器里弹出一条提示信息:Watchpoint reached

办法三:ABAP Debugger Script

ABAP Debugger Script这项技术,在SAP研究院外部用的很宽泛。

回到下面的例子,咱们将编写一段简略的ABAP代码,去控制目标ABAP代码的断点触发。

在ABAP调试器里,点击Script标签页,创立一个新的ABAP脚本:

咱们想用ABAP脚本监控ABAP代码里某个简略变量的值变动,所以应用脚本创立向导里的Variable Value(for Simple Variables):

这个向导会主动帮咱们生成ABAP脚本,其实也就是一段ABAP代码了,这段代码能够用编程的形式,在调试器激活的上下文里,获取某个ABAP变量的值。

下图脚本的语义很清晰,获取调试器里field symbol <data>的值,存储在长期变量lv_result里。如果该变量的值为22,就调用ABAP脚本的工具办法break,触发断点。

把这段脚本通过上图的Save As按钮另存下来,取名ZJERRY_TEST.

而后从新执行咱们的测试代码, 应用Load Script加载方才保留的ABAP脚本:

点击Start 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_attributeENDCLASS.                    "lcl_debugger_script IMPLEMENTATION

咱们晓得,像如图一这品种的动态属性,因为不属于类的实例所有,因而调试到这个类的办法外部时,只能通过图二演示的两种形式在调试器显示该属性的值。而一旦调试到该类办法的内部,通常就只能通过"类名=>属性名"的形式来显示动态属性值(图三)。其实还有一种形式,如图四和图五所示。

图一:ABAP类的动态属性

图二:如何在ABAP调试器里查看类的动态属性

图三:在调试器里跳出类的办法之后,如何查看动态属性

图四和图五在调试器的Objects面板里,手动输出{C:ZCL_STATIC}, 这里的ZCL_STATIC替换成其余蕴含有动态属性的类名,回车即可查看。

可能有些敌人感觉这个小技巧没啥用吧,我以前在调试很多用单例模式(Singleton)实现的框架代码时常常用。当排错须要查看一个用单例模式实现的类的多个动态属性时,如果用图三介绍的"类名=>属性名"的形式,要反复敲很多字符,敲击键盘的工夫复杂度为o(n), n为动态属性的个数。用Object面板这种技巧,敲击键盘的工夫复杂度一下子降到o(1), 进步了排错效率。

总结

所谓条件断点,就是设置在某行语句上的断点,并不总是会触发,而是仅当满足肯定条件时才触发。本文首先介绍了 ABAP 条件断点的应用场合,接着应用了一个蕴含循环的 ABAP 程序,分享了三种不同的条件断点的应用形式。灵活运用条件断点,能大大提高开发人员的调试效率。