stephen.pfeiffer

13 Posts

In an earlier posting, I showed what the Test-Driven Development cycle might look like in the ABAP Workbench (SE80) in NetWeaver Release 7.01. Unfortunately, the TDD experience in that Release was marred by a hurdle right at the start of the process of implementing an ABAP class. That hurdle has been removed, and there are three other important improvements to ABAP Unit.

With NetWeaver 7.0 EHP2 (Release 7.02), the process has become a little bit more graceful. This weblog reviews what ABAP TDD looks like in practice in Release 7.02.  (Look for even more improvements in support for ABAP TDD in the Customer Engagement: Evaluation of the Pilot Version of ABAP in Eclipse.)

Start by assuming that you have just created a new, empty class in transaction SE80. You are looking at the form-based class builder, in change mode.   

  1. You can now practice TDD orthodoxy right from the start: You can create ABAP Unit test methods before defining any methods in your global class under test. This means that you can evolve the signatures of your production methods in the course of testing from your test methods. This was not possible in Release 7.01.

    From the form-based class editor, choose Goto > Local Definitions/Implementations > Local Test Classes.

    The source code editor opens with the Local Test Classes include.

    Note: The ABAP Unit wizard is still available. As in 7.01, you can define the signatures of the methods of your global class first and then generate the ABAP Unit test methods from the signatures. Call the ABAP Unit wizard with Utilities > Test Classes > Generate (or Utilities > Test Class Generation, if you are working in the source-code based class builder).

  2. Type in the definition of your ABAP Unit test class.

    Do you remember the ugly old pseudo comments for specifying the risk level and duration of ABAP Unit tests in Release 7.01?  In 7.02, these are replaced by arguments of the ABAP CLASS statement. This also means that you can use code completion to help with the definition of your ABAP Unit class:

    CLASS lct_test_it DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT.
      PRIVATE SECTION.
        METHODS test_add_it_up FOR TESTING.
    ...

  3. In your test methods, type in the call to a method-under-test as you think it should be defined.

    With a double-click on the method name, you can define it via forward navigation in the global class that you are testing.

      method test_add_it_up.

        data result type i.

        result = cut->add_it_up(  “double-click on add_it_up
            addend_1 = 1          “to define the method in the
            addend_2 = 1 ).       “class under test

      endmethod.

    Forward navigation unfortunately does not automatically fill in the parameters of your new method.

    Even so, in 7.02 you can drive all of your development from your test methods, right from the start.

  4. In your test method, assert the result that you want to see.  There is a new and improved standard class for this in Release 7.02, CL_ABAP_UNIT_ASSERT. Here’s an example:

       cl_abap_unit_assert=>assert_equals( act = result exp = 2 ).

  5. Run your ABAP Unit test(s). The same convenient mechanism exists as in 7.01. From the ABAP Unit source code editor, choose Local Test Classes > Unit Test

    If the tests all run successfully, you see a success message in the status line of the SAP GUI.

    Otherwise, the ABAP Unit Result Display opens. You can navigate to the test method and then farther to the method under test, so that you can correct its implementation.

Mass Testing with the ABAP Unit Browser

Running a whole lot of ABAP Unit tests at once may not strictly be part of the TDD process. But it sure is useful for regression testing and verifying that your new code is still okay after you have transported it to the integration or consolidation system.

The ABAP Unit Browser – new with Release 7.02 – helps you with mass testing, both in the development system and in the consolidation or quality-testing system.

Activate the ABAP Unit Browser by selecting it in transaction SE80, in Utilities > Settings, on the Workbench (General) tab.  Among other options, you can run all ABAP Unit tests in a package or in the programs of a particular user. You can also store ‘favorites’, or defined collections of ABAP programs.  See the standard documentation for more information on the ABAP Unit Browser.

The New ABAP Debugger, introduced with NetWeaver 2004s, came with a Table tool for displaying the contents of internal tables.

With NetWeaver 7.0 EHP2 and with Netweaver 7.30, the Table tool has been re-worked and expanded to make it easier to use and more flexible – a truly comfortable tool for examining internal tables, no matter how large or complex they might be.

In this weblog, we’ll look at the new Table tool in the New ABAP Debugger. We’ll show you the following:

  • New and comfortable ways to change the column layout of a table so that you can see the columns you want to see
  • How to include fields from nested structures and tables and attributes of referenced objects in the display of an internal table
  • How to save the layout that you have created for a table for re-use
  • How to add or delete lines from an internal table or change the values in particular fields in a table
  • How to table layouts for re-use.   

Here is a short film that shows you how to use the Table Tool: 

Starting the Table Tool

Start the Table tool from the New ABAP Debugger when you need to look at the contents of an internal table while you are in the debugger:

Starting the Table Tool

Arranging the Table Display and Using the Column Configurator

Most internal tables were created for reading by programs, not by people. The biggest problem in analyzing the contents of an internal table is therefore usually arranging the table display for best readability.   If you are working with a simple table, you can simply drag and drop columns to the proper positions.

If a table is wide or complex, then the Table tool offers the Column Configurator for optimizing the layout of a table. Start the Column Configurator by clicking on the Columns button.

 Arranging the Table Display

In the Column Configurator, you can move columns around, delete columns that you do not need, and mark object attributes or fields in nested structures or internal tables for display in the table tool.

Using the Column Configurator

 

Displaying Object Attributes and Fields in Nested Data Objects

As the screen shot above suggests, you can add data entities from nested objects to the Table Tool display.  Before NetWeaver 7.0 EHP2, you needed to click your way into the object or nested data structure in each row of an internal table in order to see the data.

With the new Column Configurator, you can have the data pulled up into the normal table display.  Here in more detail is how to display concealed data:

  1. You start in the Table tool in the New ABAP Debugger with a table like this one, which contains references to instances of ABAP Objects classes. You want to see the customer address information that is hidden in the objects.

    A complex table

  2. Click on the Columns button. In the Column Configurator, the Insert Subcomponents icon marks columns that contain data objects with nested attributes or fields.  In this case the CUSTOMER (references to instances of ABAP class CUSTOMER) and DREF columns (anonymous data references) contain such objects.

    Clicking on Insert Subcomponents at the CUSTOMER column opens a list of the attributes of the CUSTOMER class. We select all three for inclusion in the column configuration.

     

  3. The Columns Configurator recognizes recursive data objects. The ACCOUNT, ADDRESS, and ID subcomponents have all been added to the column configuration.  ADDRESS itself contains further subcomponents that we want to add to the display.

    Displaying Subcomponents

  4. We move the ADDRESS subcomponents to the front of the layout in the Column Configurator for easier viewing.

    Displaying Subcomponents

  5. When we press the Apply button at the bottom of the Column Configurator, the hidden class attributes are added to the Table display for all of the rows of the table. There is no need to drill down into each separate class object to view the data.

    Subcomponents are displayed

Saving a Table Layout for Re-Use

So that you do not need to adjust a table’s layout each time you start the debugger, you can save a table layout.  You can also apply it automatically to other tables that have the same type definition.

Saving a layout affects only your view of a table – you do not affect other users who might need to analyze the same table.

Sharing a Table Layout

You want to share your great layout for an important table with someone else?  No problem.  Just do the following:

  1. Open the Column Configurator.
  2. Mark the columns in the Configurator that make up the layout to share (most likely, you will simply want to mark all entries).
  3. Copy the marked fields to the clipboard with Ctrl-C or the context menu.
  4. Paste the clipboard contents into a mail or .txt file for your colleague.
  5. To apply the layout, your colleague simply copies and pastes the column layout into the column configurator in their own debugger session.

Saving a table configuration

Adding or Deleting Table Rows or Changing Values

Modifying a table or the values in table fields has also been made easier. To delete rows, for example, just mark the rows to be discarded and open the context menu with a right mouse click.

 Adding or deleting rows

Even More Table Services…

To see the full range of operations that you can perform on a table, just click on the Services of the Tool button at the right of the Tables workspace. Among other convenient functions for displaying the contents of internal tables, you find things like Position on Column, which moves the column that you mark to the left-most position in the display or Optimize Column Width, which performs an Excel- or OpenOffice-Calc-like adjustment of the column widths for best display.

 Additional table services

In the old R/3 days, ABAP programmers rarely needed to analyze memory consumption in their programs.  The simple and robust ABAP memory model made it very hard for anyone to program a serious ABAP memory leak.

With the addition of more types of dynamic memory objects – not just internal tables, but also strings, anonymous data objects, boxed components – and a trend toward transactions that live longer, it’s now possible to write an ABAP program that can run into memory problems.

Since ABAP programmers can no longer ignore memory consumption, the ABAP Workbench has greatly enhanced the memory analysis tools in the New ABAP Debugger and in the Memory Inspector (transaction SMI or S_MEMORY_INSPECTOR).

This weblog showcases the enhancements that come with EHP2:

  • The Dominator Tree (or keep-alive tree) for containment hierarchies in memory objects – which runtime entity is keeping which memory object alive. This new analysis tool is offered in both the New ABAP Debugger and in the Memory Inspector. 
  • The Memory Object Explorer in the New ABAP Debugger, for navigating up and down through the memory objects in the keep-alive hierarchy of objects. Starting from any memory object, you can navigate up to its parents or down to its children, and you can take a look at the contents of each memory object.
  • A separate weblog shows you the new Application-specific memory analysis tools in the New ABAP Debugger – for looking at the memory consumption of ABAP Web Dynpro applications. There is also a similar tool for analyzing Web Service / HTTP applications.

You may also notice that doing memory analysis in the debugger is much more comfortable, and that we have made improvements in the user interface in the Memory Inspector as well. But we do not go into these UI changes in detail, nor will we look at the Memory Inspector transaction in this blog.

The New Features in Practice in the Debugger

You do memory analysis usually for one of two purposes:

  • You want to see how big your program – or objects in it - are in terms of memory consumption. Is it bigger than you expect?  Usually you’re in the New ABAP Debugger when you do this.
  • You want to see whether you have a memory leak. Does the memory consumption of your program change over time?  Or – how did it get so big that it dumped because of lack of memory – this question also sometimes comes up.

Usually, you’re comparing memory snapshots in the Memory Inspector (transaction SMI) when you’re doing this type of analysis.

Let’s see how the new memory analysis features work if you are in the debugger, just checking how much memory your program uses. Maybe you’re just being cautious in doing this checking – or maybe you’ve learned:  a long-running transaction or service with lots of dynamic memory objects means, you better check the memory consumption before your customers find out about it for you.  What you see here on the new memory analysis features applies just as well in the separate Memory Inspector transaction.

A Comfortable Quick Look at Memory Consumption – The Memory Analysis Tab

You don’t have to switch to a memory analysis tool anymore to see how much memory objects in your program are using.  There’s a new Memory Analysis tab in the new ABAP Debugger, on the Variable Fast Display. Just switch to the tab, click on a variable in your program, and you can see how much memory the object uses.

The Memory Analysis Tab in the ABAP Debugger 

Here we have two tables that have the same number of rows, and we happen to know that they contain the same data - except that IT_CUSTOMERS contains extra data.  Even so, IT_CUSTOMERS is smaller in memory.  How is the difference explained?   Does IT_CUSTOMERS offer a more efficient way to organize the table?  A closer look shows that we don’t understand how IT_CUSTOMERS is organized. The table statistics don’t seem to match the memory size.

  • The size of table IT_SCUSTOM is okay – 4637 rows of 16 fields with a row-length of 464 bytes is close to the Bound Used Memory (bound memory is the memory that would be freed if the table were cleared) plus some management overhead.   The table simply is that big.
  • The size of IT_CUSTOMERS is puzzling. It has the same number of rows as IT_SCUSTOM but a row length of 32 bytes. No way that the table body itself comes close to 1.7 million bytes of bound memory.

Analyzing Table IT_CUSTOMERS with the Memory Analysis Tool

To study table IT_CUSTOMERS, you activate the Memory Analysis Tool by choosing the New Tool or Replace Tool button, opening the Memory Management folder, and clicking on Memory Analysis.

 image

The tool that was available before EHP2 was the Memory Objects view, shown below in its form in NetWeaver 7.0 EHP2. This tool shows you the memory objects of the program (the representations of dynamic variables in the memory management system) ranked by size. 

Memory Objects also shows you which variables reference each memory object.  But the view does not help us to understand the differences between the IT_CUSTOMERS and IT_SCUSTOM tables. Essentially we just have an unstructured list of memory objects ranked by size.

 image

Here’s where the new Dominator Tree view (below) shows how useful it is.  In this view, it’s immediately clear how IT_CUSTOMERS is structured. The rows are so short because they contain only an object reference. The CUSTOMER objects in the table contain, by the way, a further object, ACCOUNT.  The CUSTOMER objects are also keeping several strings – variables NAME, ADDRESS, and CITY - alive.  The bound storage of the table is so high because the table is keeping all of the customer object hierarchies alive.  (Without the references from IT_CUSTOMER, the ABAP garbage collector would clear the objects away and free the memory.)

image 

The organization of the table is now clear. Where does the advantage in memory use of the IT_CUSTOMERS table come from, compared to table IT_SCUSTOM? 

To find the answer, you could copy the name of a CUSTOMER object into the clipboard, and start the Memory Object Explorer (available only in the debugger, not in the Memory Inspector). In the Memory Object Explorer (below), we can follow edges from the CUSTOMER object to its children, all of the objects that it references.

The Dominator Tree shows only strings and instances of classes that belong to the bound storage of each CUSTOMER class object. These are the objects that CUSTOMER keeps alive. The Memory Object Explorer, by contrast, also shows objects that are referenced by more than one CUSTOMER instance, memory objects that are in effect shared. Can it be that sharing of objects accounts for the reduced storage use?

We follow a CUSTOMER object to its referenced objects and then check some of the referenced objects themselves in the Explorer. COUNTRY looks like a good variable to check for sharing among multiple CUSTOMER objects. So we go from string {S:S9} ADDRESS-COUNTRY up the memory hierarchy – Higher-Level Memory Objects - to see the parents of this string, objects that reference this string.

image 

And in fact, as you can see below, the more efficient storage use in table IT_CUSTOMERS than in IT_SCUSTOM could be due to massive sharing of string objects in memory.  There are many CUSTOMER objects from our table that reference the same COUNTRY string.

image 

The IT_CUSTOMERS table design takes advantage of the fact that string variables that have the same content share a single memory object.  (See ‘value semantic’ in the ABAP Online Help (transaction ABAPHELP or at help.sap.com).  ABAP gives a string or other value-semantic object its own memory object only on an as-needed basis – if the string is changed.

In many applications, a table like this list of customers is rarely changed, but is used instead to guide processing. In this case, it may make sense to make use of memory object sharing to reduce storage consumption.  In this application, CUSTOMER attributes like the country-of-residence reference the same string, as long as the same country is being referenced.  You can check out whether the strategy is really worthwhile – when filling a table, when reading a table, in the event that object attributes are changed – by doing some testing of the alternatives in the ABAP Runtime Analysis, transaction SAT. (See the weblogs by Olga Dolinskaja on the new SAT.)

An advantage of doing memory analysis in the debugger is that you can see what the value of the variables is. This is something that you cannot do when you analyze memory snapshots in the Memory Inspector (transaction SMI).  A double click on string {S:S9} above shows us what country all of those customers inhabit:

 Displaying the Value of a Memory Object in the ABAP Debugger

But the Memory Inspector, in turn, has the advantage of being able to compare memory snapshots and show changes in memory use over time. That’s an invaluable capability for checking for leaks in a long-running ABAP application.

Before NetWeaver 7.0 EHP2, it used to be difficult to analyze memory problems that were really specific to an ABAP Web Dynpro application and not just related to some mammoth internal table deep down in the application logic.

The problem was that it was hard to see the forest for the trees in the Memory Objects ranking lists:

  • What objects belong to the ABAP Web Dynpro runtime?
  • What objects belong to the Web Dynpro application?  
  • What belongs to the backend infrastructure? 

The old memory analysis functions could not tell the difference between one type of object and another.   

Here’s an example:

 What memory objects belong to ABAP Web Dynpro?

In the New ABAP Debugger, the application-specific memory analysis tool for ABAP Web Dynpro brings some order to the situation and lets you analyze the memory consumption of your ABAP Web Dynpro application much more easily and efficiently.

 Memory Analysis Views for ABAP Web Dynpro

There is also an extra filter option that lets you hide memory objects that do not pertain to the Web Dynpro application and runtime. In this case, that includes objects that belong to the ESI framework (web service infrastructure).  Here, you can see the effect of the filter – it lets you concentrate exclusively on memory objects that belong to ABAP Web Dynpro.

 Memory Analysis Objects filtered for ABAP Web Dynpro

It’s easy to write your own ABAP Debugger Scripts in NetWeaver 7.0 EHP2, either for ad hoc use directly in the debugger or for re-use by you or your colleagues.  An ABAP Debugger Script is an ABAP Objects program that runs under the control of the debugger.  The script automates your activities in the debugger by calling the services offered by the ABAP Debugger. In this weblog, we show you how to write a script, whereby the most important topic is how to use the Script Wizard to call the services of the ABAP Debugger from a debugger script.  We also demonstrate some sample scripts for tracing, creating individualized breakpoints, and extracting data for display from complex data objects. For a quick introduction to the Script workplace and ABAP Debugger Scripting, see also the weblog "ABAP Debugger Scripting: Basics". Here is a {code:html}video{code} on advanced debugger scripts. h3. Where to Work on Scripts:  The Debugger or Transaction SAS You can write scripts on-the-fly, while you are in the debugger. But writing scripts directly in the debugger ties up a Dialog work process for as long as you are in the debugger. If you are preparing anything other than a simple script, then transaction SAS, +ABAP Debugger Scripting and Tracing+, offers a more comfortable and efficient place to work. Transaction SAS also lets you view traces that you have created as well as those created by other users (you can easily share a trace with a colleague). More importantly for our present purposes, it also provides the same script editing facilities as the +Script+ tab in the debugger (only the +Start Script+ button is missing). Transaction SAS script workplace h3. Calling Debugger Services from a Debugger Script In this section, we’ll show you how to access debugger services from your scripts. When you write a script, you modify a script template that the Script tool automatically loads into the script editor.  Of course, you can use almost any ABAP OO statements or constructs that you can think of in a script.  But the real meat of the script is in the calls to debugger services (via the ADI – ABAP Debugger Interface) that you can insert into your script.  You insert debugger service calls into your scripts with the *Script Wizard*.  To add code from the wizard, position the cursor in the script editor at the point at which the wizard code should be inserted. Then do the following: * Choose the code snippet that you need from the published ADI functions. As you can see, a wide range of method calls are ready for you to use. The Script Wizard Complete the method call that the wizard inserts into your script. Then you can add more code of your own to evaluate the comparison of variables, and perhaps another ADI call to trace the results. Edit an ADI method call h3. A Script for Tracing the Call Hierarchy Having a call hierarchy is useful if you want to use layer-aware debugging, but you aren’t sure what to include in the software layer that is to be visible in the debugger. This simple script generates a trace of the call hierarchy. It is so easy to implement that you can set it up directly in the debugger – no need to use transaction SAS. Here’s how to trace the call hierarchy: 1. Stop the program that you want to trace in the debugger. It does not matter how you stop the program in the debugger. It does not matter where you are in the program.  (It can be useful to trace the call hierarchy just for the part of the program in which you are interested – between two breakpoints that you set in the source code editor, for example). 2. 3. Click on the +Script+ tab, the very last tab (for now) in the tool set of the New ABAP Debugger. 0.1. 0.2. In the script editor, scroll down to the SCRIPT method, which is where the main part of your script should be implemented.  You’ll see the comment +*** insert your script code here +together with a default debugger call, +me->break( )+ – which calls a breakpoint when the SCRIPT method runs. Comment out the +me->break( )+ method – you probably don’t want to stop in the debugger with every new call in the call hierarchy, right? Before continuing, let’s take a moment to review the four pre-defined methods in class LCL_DEBUGGER_SCRIPT. Chances are that you will need only the SCRIPT method, but even so... 0.1. +PROLOGUE+: In this method, the Debugger Script infrastructure generates the +ABAP_SOURCE+ handler for accessing the code and variables in the program that is being debugged. Your own initialization code, if any, should go in the +INIT+ method.  This method runs only once, the first time that a script runs. +ABAP_SOURCE+ is an instance of the class +CL_TPDA_SCRIPT_ABAPDESCR+. +ABAP_SOURCE+ is used by predefined methods and scripts to get information about the source code that is running in the debugger. You can also use the methods exposed by +ABAP_SOURCE+ yourself, should you need to do so. 0.1. +INIT+: In this method, you can do any initial setup that you need to do, for example, collecting such interactive input as the name of an object to test or the value to assign to a variable. (Yes, any UI elements permitted in ABAP Objects can be used in a Debugger script.) Like the +PROLOGUE+ method, the +INIT+ method runs only once, the first time that a script is started. 0.1. +SCRIPT+: You pack your operational script code in this method.  Should you wish to, you can add your own methods to +LCL_DEBUGGER_SCRIPT+ that are called from the +SCRIPT+ method. The Debugger Infrastructure runs the +SCRIPT+ method every time that the script should be started.  For example, if you specify that the script should start with every +Debugger Single Step+, then the very first time that the script runs, +PROLOGUE+ and +INIT+ will be executed.  The +SCRIPT+ method will also be run. With every subsequent +Single Debugger Step+, the infrastructure runs the +SCRIPT+ method only. When you end the execution of the script, then the +END+ method is run. 0.1. +END+: In this method, you can do any clean-up or post-processing that is needed. For example, The debugger runs +END+ only once, when you press the +Exit Script+ button. The +END+ method is not run in the event that a script is aborted because of an error or in the event that the debugger itself is  terminated through user action.0.1. 0.2. Get the method call for tracing the method hierarchy. Click on the +Script Wizard+ button. Then open the +Write Trace+ folder.  And finally double-click on +Trace of the Call Hierarchy (Current Event)+ entry to insert the call to method ADD_EVENT_INFO() of IF_TPDA_SCRIPT_TRACE_WRITE. Add a method call from the Script Wizard 0.1. 0.2. Set the trigger condition to +Breakpoint Reached+.  Then click on the +Change+ icon to open the +Script Breakpoints+ tool.    Set the start condition for the script Remember that scripts start only for breakpoints set from the +Script+ workplace. Breakpoints that you set for example in the source code editor do not cause a script to be run.  This arrangement lets you separate breakpoints and watchpoints for triggering a script from those that you set for your own use. 0.1. Create a stack-change breakpoint. Among other new types of breakpoint introduced with NetWeaver 7.02 EHP2 there’s one just for this purpose. Create a breakpoint for a debugger script 0.1. 0.2. Tell the ABAP Debugger to start running the script by pressing +Start Script+. Note: The debugger runs the debuggee-program as if you had pressed the +Continue (F8)+ button in the tool bar of the New ABAP Debugger.  At each change in the stack, the +Trigger+ condition that you set causes the ABAP Debugger to run your script.  The Debugger itself does not stop execution at the script breakpoints.  In order to stop the debugger and return control to you, the script itself must request a breakpoint.  Or you must encounter a breakpoint that you set for yourself, not as a trigger in the script workplace. 0.1. Tell the New ABAP Debugger to stop running the script so that you can take a look at the trace. If you have arrived at the next non-script breakpoint in your code, then the debugger has control.  You can tell the debugger to keep running the script or to stop running the script directly. Stop the script If control has returned to the program that you are debugging, at screen output, for example, then you need to return control to the debugger so that you can stop the script. Returning control to the debugger The debugger returns to the foreground, and you can click on +Exit Script+ or +Continue Script+. 0.1. Display the trace in the debugger session or in an internal session by clicking on +Display+ or +Start Analysis in New Session+. Display the call stack trace Here is what the call hierarchy trace might look like. You can use such a trace, for example, to find out what packages or other objects belong in a software layer for layer-aware debugging.  You can also add logic to test for errors and issue special trace messages or break execution as your script traverses the call hierarchy. The call stack trace h3. A Script for Checking the Results of RFC Calls We recently had the following problem: In a large customer landscape, one or more programs that were called remotely with RFC were returning garbage. You can’t easily use a conditional breakpoint or a watchpoint to detect the RFC destinations that are returning corrupted data. With a script, you can find the bad destinations quickly.  Here is how you could build such a script: 1. You would want to work in transaction SAS, in the +Script Editor+ tab, since the script will be relatively long and can be modified and re-used for analyzing other RFC problems.0.1. After +Creating+ and naming the script, you can first set the +Trigger+ condition.  Set the +Trigger+ condition to +Breakpoint+ +Reached+ and then create a script breakpoint. Click on the +Change+ icon that appears next to +Breakpoint Reached +(below).  The trigger condition is saved with the script and is set automatically when you load the script in the debugger.

 Setting a breakpoint for running scripts

Create an RFC breakpoint.  Setting a breakpoint on the ABAP Command ‘RFC’ tells the Debugger to run the script every time an RFC call is made.

Setting a breakpoint for running scripts

  • Then switch to the editor and position the cursor in the SCRIPT method, where you see

+*** insert your script code here+   me->break( ). You’ll probably want to comment out the BREAK instruction. If you wish to, you can have the script run it when it encounters bad data returned by an RFC. You can stop the program that you are debugging and use the debugger to look at the problem more closely. You could call that an ‘intelligent Debugger Script breakpoint’. Since the script runs whenever an RFC call is to be made, we’ll need this logic: 0.1. Run a debugger step to execute the RFC call 0.2. Read the variable or variables that we want to check 0.3. Apply tests to the value of the variable(s) – our own ‘application logic’, so to speak. 0.4. If we find bad data, write a user-defined trace message, stop with a debugger breakpoint, or do both actions. Insert the first instruction – running the RFC function call – by using the Script Wizard to insert this debugger method call:  Add a method call from the Script Wizard As you can see, the method signature lists the constants for the different types of debugger steps and jumps. We also uncomment the TRY and CATCH statements inserted by the Wizard. +*  me-> break( ).+
+*************************************************+
+* debugger commands (p_command):+ +* Step into(F5)   -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_INTO+ +* Execute(F6)     -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_OVER+ +* Return(F7)      -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_STEP_OUT+ +* Continue(F8)    -> CL_TPDA_SCRIPT_DEBUGGER_CTRL=>DEBUG_CONTINUE+
+*************************************************+
+****************************************************************+
+*Interface (CLASS = CL_TPDA_SCRIPT_DEBUGGER_CTRL / METHOD = DEBUG_STEP )+
+*Importing+
+*        REFERENCE( P_COMMAND ) TYPE I+
+****************************************************************+     TRY.         CALL METHOD debugger_controller->debug_step           EXPORTING             p_command = cl_tpda_script_debugger_ctrl=>debug_step_over.       CATCH cx_tpda_scr_rtctrl_status .       CATCH cx_tpda_scr_rtctrl .     ENDTRY. 0.1. Then add the second element of the script logic: A debugger method call to read a variable that is to be checked. Here, we use a variable from the RFCSI structure returned by the special RFC call RFC_SYSTEM_INFO, which requires no logon. This is a convenient way to simulate the error that occurred at the customer site. Again, most of what we need is a debugger wizard call from the +Script Wizard+. Add a method call from the Script Wizard We need to add a variable declaration to receive the value read by the debugger. The type information for the declaration is given to us in the method signature inserted by the Wizard. Note that the names of variables in the debugged program must be in capital letters.      DATA rfc_host TYPE tpda_var_value.     TRY.         CALL METHOD cl_tpda_script_data_descr=>get_simple_value           EXPORTING             p_var_name  = 'SYSTEM_INFO-RFCHOST2'           RECEIVING             p_var_value = rfc_host.       CATCH cx_tpda_varname .       CATCH cx_tpda_script_no_simple_type .     ENDTRY. We might want to report exceptions with trace method calls after the CATCH instruction, for example if we wrote the variable name incorrectly in P_VAR_NAME. 0.1. The third element of the script is a little bit of logic to check whether SYSTEM_INFO-RFCHOST2 contains valid data. That might look like this:     DATA length_of_hostname TYPE i.     DATA target_destination TYPE rfcdest.     DATA my_trace TYPE tpda_trace_custom.     length_of_hostname = strlen( rfc_host ).     IF length_of_hostname = 0 OR        contains( val = rfc_host(length_of_hostname) regex = '[^[:word:]]' ). We want to check that a hostname was returned and then that the hostname contains only the alphanumeric characters that we expect. By the way, the program DEMO_REGEX_TOY, featured in a weblog by Horst Keller, is a useful tool for testing your REGEX expressions. 0.1. The final element of the script is a trace message in the event that the script finds bad data returned by an RFC call. We could also insert an me->break( ) to stop at the scene of the crime as well. Add a method call from the Script Wizard We would want to add a second GET_SIMPLE_VALUE() call in order to report the name of the RFC destination, if we are not stopping to examine the situation directly. The user-specific trace message – written with method ADD_CUSTOM_INFO() requires only text in the VALUE field of the TPDA_TRACE_CUSTOM structure required by the method.       TRY.           CALL METHOD cl_tpda_script_data_descr=>get_simple_value             EXPORTING               p_var_name  = 'LS_ALSYSTEMS-DESTINAT'             RECEIVING               p_var_value = target_destination.           CONCATENATE 'This destination returns bad data:'                       target_destination                       INTO my_trace-value SEPARATED BY space.           CALL METHOD trace->add_custom_info             EXPORTING               p_trace_entry = my_trace.         CATCH cx_tpda_varname cx_tpda_script_no_simple_type.           my_trace-value = 'Error in script'.           CALL METHOD trace->add_custom_info             EXPORTING               p_trace_entry = my_trace.       ENDTRY.     ENDIF.   ENDMETHOD.                    "script 0.1. Save the script (remember to use a naming convention so that you can load the script easily).  It is ready to use in the debugger.  The output might look like this:  Trace of bad RFC calls You can also download the script to file so that it is available for uploading into other systems, should RFC problems occur elsewhere. h3. Script-Writing Notes Here are some tips that may help you to create scripts more quickly and efficiently. h5. Short Dumps The ABAP debugger infrastructure intercepts fatal errors in debugger scripts. A short dump is reported as a message in the status line of the debugger and does not propagate to a full-fledged short dump. For example, the script coding:    DATA my_quotient TYPE I.   my_quotient = 1 / 0. …produces this message rather than a short dump.   Dump message

Now that NetWeaver 7.0 EHP2 is available as a Mini-Basis System, we can tell you about some of its new features.

One of the coolest of these new features for problem analysis is ABAP scripting in the New ABAP Debugger. In this weblog, we explain what this feature is for, what the script workplace looks like, and how to run a couple of first scripts to whet your appetite.  You’ll see how to do a complete trace of executed statements from the script overview, and how to see where particular statements occur in your (executed) code.

Here is the video on ABAP Debugger Scripting Basics.

What is Debugger Scripting For?

The new scripting capability lets you automate anything that you can do by hand in the New ABAP Debugger. Debugger Scripts also make some things possible – like all kinds of tracing – that aren’t possible by hand.

With ABAP Debugger Scripts, you can:

  • Analyze and modify the contents of variables and objects in the program you are debugging.

    You can display or change the value of a variable by hand in the debugger, and you can do the same thing with a script.  The classical example: You can skip over failed AUTHORITY-CHECK statements with a script by stopping at each AUTHORITY-CHECK, running it, and resetting SY-SUBRC to 0.  (This is still recorded in the System Log….)

  • Perform any imaginable tracing that you might want to do.

    With scripts, you can

    • Do the first automated absolutely complete trace of executed ABAP statements possible (SAT – the new ABAP Runtime Analysis - cannot capture every executed statement.)

    • Trace the call stack in an ABAP application or in any part of an application – very useful if you want to find out what to include in a profile for ABAP Layer-Aware Debugging (See Layer-Aware Debugging in NW 7.0 EHP2.)

    • Define your own traces with any trace message content that you can imagine. You can for example trace the value of a parameter that is passed up or down the call stack in your application to find out where it goes bad.  

    • Implement your own breakpoints and watchpoints, should the extended set introduced in EHP2 not meet your needs.

      You can combine the powers of the new ABAP breakpoints and watchpoints with the intelligence of scripts.  Some system in your landscape is returning trash in an RFC data structure?  No problem. Have your script run whenever the program in the debugger does an RFC (breakpoint at RFC), execute the RFC call, check the returned data in your script, write a trace or break execution when you find the bad data return.

    • Analyze and display data from complex data structures (internal tables with nested objects, for example)

      The revised Table tool in the New ABAP Debugger makes it a lot easier to look at data in a complex data structure (look for the upcoming weblog). But for tracing data generation or checking data consistency as your debuggee runs, a script is the better tool. The classical example is an internal table with nested objects whose attributes you want to check. A script can read those nested attributes row by row, check or trace them as you wish, and even stop execution if something goes wrong.

    • Program (interactive) tools for standard tasks, such as checking data consistency, changing variable values to test error handling, and so on.

      A lot of script programming is quick ad hoc work right in the debugger to help you with some analytical or data collection problem.

      But ABAP Debugger Scripting also lets you save scripts for re-use.  And transaction SAS offers a comfortable workplace for developing scripts away from the pressure of an active debugging session.  You can develop tools or utilities for debugging to suit your special needs, and you can share these scripts with others on the same or on other ABAP Systems.

    An ABAP script is simply a local ABAP Objects program that is run by the New ABAP Debugger. The script uses the ADI – ABAP Debugger Interface – to automate actions that you otherwise might perform by hand in the debugger – and to add capabilities that otherwise would not be available to you.  ABAP scripts take advantage of the two-process debugging infrastructure that SAP introduced in NetWeaver NW04s (7.0).

    Of course, what you can do with a debugger script is limited only by your requirements and imagination. But the point of ABAP Debugger Scripting is to increase your efficiency in analyzing and solving problems with the New ABAP Debugger.

    So the Debugger Script workplace includes a small selection of ready-to-run scripts (the Script Overview button in Load Script) that you can either use directly or modify for your own purposes. And there is also a script wizard for writing your own scripts (or modifying ready-to-run scripts).  The wizard lets you insert method calls to debugger services in your script. See the weblog on advanced scripts for more information.)

    The Debugger Script Desktop

    Here is what you see when you start a program in the New ABAP Debugger and then click on the very last Debugger Tool tab:  Script.

    Debugger Script Desktop

    As you can see, the source code editor is open so that you can edit a standard script template.  You can use any standard ABAP OO statements and constructs that you wish. Plus you can use the method calls from the Script Wizard; these let you make use of the services of the New ABAP Debugger. (See the weblog on advanced scripts).

    You can load a script from the library or from a collection of your own scripts (use a naming convention, so that you can find them again). You can easily move scripts between systems by downloading them to file and then uploading them again.

    To the left of the editor, you can specify when the ABAP Debugger should run the script – for example, after every single execution step in the debugger. The Trigger options have the following meanings:

    • Execute directly:  If you mark this option, the script runs only if you press the Start Script button. The debugger does not run the script automatically. This is the right option if you are using a script for a single specific task, like dumping the data from a complex data source to a trace. 

    • After Debugger Events Debugger Single Step: Once you tell the debugger to run the script (with Start Script), then the debugger automatically runs the script after every debugger single step (F5 – Single Step in manual debugging). After every statement is executed by the debugger, the script runs again. This is the Trigger setting for a full statement-level trace of execution.

    • After Debugger Events - Breakpoint Reached, Watchpoint Reached: With these options, the debugger runs the script every time a breakpoint or watchpoint is reached.

      Important: The script runs only when the debugger reaches breakpoints and watchpoints defined here, in the context of the script.  If you check one of these Trigger options, an icon appears that allows you to define breakpoints and/or watchpoints for triggering the script. The script does not run at breakpoints or watchpoints that you may have set separately, in the source code editor, for example.  This arrangement lets you separate breakpoints or watchpoints that are to trigger the script from those that you set for your own use in the debugger.

    At the bottom of the screen (not shown in the screen shot) is the Display Last Trace frame. After your script has stopped running, you can use the buttons in this frame to display the trace (if any) created by the script.

    Your First Script: A Complete Trace of Executed Statements

    The ABAP Runtime Analysis (transaction SAT) can trace almost to the level of all executed statements. But before ABAP scripting, you had to step through a program yourself in the debugger to see exactly what a program does.

    Here’s how to create a complete trace of executed statements using ABAP Debugger Scripts.

    1. Start a program in the New ABAP Debugger. It’s immaterial how you reach the debugger – by breakpoint, /h ok-code – you can start a debugger script from any point in a program’s execution.
    2. Click on the very last Debugger Tool tab: Script
    3. Click on the Load Script button and then on the Overview of Debugger Scripts icon.  
    4. On the list of ready-to-run predefined scripts, open the Traces folder and double-click on the last script, RSTPDA_SCRIPT_STATEMENT_TRACE.

      The script is inserted into the script editor – it’s ready to run.  You’ll notice that the predefined script has also set the Trigger frame correctly.  Trigger tells the New ABAP Debugger to run the script after each debugger single step (an F5 key press, in interactive use of the Debugger).

      Selecting a pre-defined script

    5. Tell the New ABAP Debugger to run the script by clicking on the Start Script button.

      What happens?  The New ABAP Debugger runs the program that you are debugging to the next breakpoint (including a stop required by Layer-Aware Debugging) or until control returns to the program. It’s as if you had pressed the F8 Continue button on the debugger tool bar. 

      At each single-step that the debugger makes, it runs the RSTPDA_SCRIPT_STATEMENT_TRACE script. The script writes each debuggee-statement that is to be executed to a log. 

      If the program is long, you will notice the trace activity in the form of extra waiting time. For that reason, you may want to run the trace script only between two breakpoints or in conjunction with layer-aware debugging. Set breakpoints around the code that especially interests you. Start the script at the first breakpoint. Stop the script and look at the trace when you reach the second breakpoint.

    6. Tell the New ABAP Debugger to stop running the script so that you can take a look at the trace.

      If you have arrived at the next breakpoint, then the debugger has control.  You can tell the debugger to keep running the script or to stop running the script directly.

      Restart or stop the script

      If control has returned to the program that you are debugging, at screen output for example, then you need to return control to the debugger so that you can stop the script.

      Returning control to the debugger

      The debugger returns to the foreground, and you can click on Exit Script or Continue Script.

    7. Display the trace in the debugger session or in an internal session by clicking on Display or Start Analysis in New Session.

      Display the statement trace

      The trace shows every statement that the debugger executed.  A double-click on a line lets you jump into the source code to see the statement in context.

      A statement trace

    8. Click on F8 Continue to run the debugger further and return control to the debuggee.

     

    Bonus: Your Second Script - Finding Out Where Particular Statements Occur

    Perhaps you want to find out where AUTHORITY-CHECK statements are executed, so that you can ask a customer for the required authorizations for debugging a problem. Or perhaps you want to see where your program is selecting data from the database. 

    With the statement trace script, these tasks are easy to accomplish.

    Proceed by doing the following:

    1. Load the RSTPDA_SCRIPT_STATEMENT_TRACE as shown above in steps 1 through 4.
    2. Change the Trigger setting from Debugger Single Step to Breakpoint Reached.  Click on the Change button, which has appeared next to Breakpoint Reached.

      Executing a script at breakpoints

    3. Create a breakpoint for the SELECT statement, or the AUTHORITY-CHECK statement, or any other statement you would like to trace.

      Setting a breakpoint for running scripts

    4. Run the script and take a look at the result, as shown above as of step 6.

      Trace of statements by type

      As you can see, you can quickly modify a standard script to perform special tasks in the debugger.  

Don't miss the presentation on Test-Driven Development in ABAP at TechEd 2010! 

At design time or when you refactor code, you can enable better unit testing by allowing dependencies to be injected into your code. Your ABAP Unit tests can then substitute test doubles for the production entities wherever you have enabled dependency injection.

This weblog shows how you might set up private injection of dependencies in ABAP. This is a useful pattern if you do not want to expose critical dependencies to users of a class, as you might do with Managing  Dependencies in TDD: Constructor Injection in ABAP in EHP1.

Private Dependency Injection

If you declare a local test class to be a LOCAL FRIEND of the class under test, then the test class can access protected and private components of the class. This is useful if the test class should test private and protected methods. But it also allows the test class to inject dependencies that are declared as private attributes of the class under test.

Private injection by a local friend allows you to break dependencies but still protect them from manipulation from outside the class under test.

Here is some sample code showing what private injection might look like:

*----------------------------------*
* In CLASS_UNDER_TEST...
*----------------------------------*

*----------------------------------*
* The dependency needed by
* CLASS_UNDER_TEST is instantiated
* in the CONSTRUCTOR method. The
* constructor sets the private variable
* MY_DEPENDENCY to reference the
* depended-on object.
*----------------------------------*
CLASS class_under_test DEFINITION CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS constructor.
    METHODS method_under_test
      RETURNING
        value(useful_value) TYPE i.
  PRIVATE SECTION.
    DATA my_dependency TYPE REF TO if_my_dependency.
ENDCLASS.

CLASS class_under_test IMPLEMENTATION.
  METHOD constructor.
    CREATE OBJECT my_dependency
      TYPE REF TO cl_my_dependency.
  ENDMETHOD.

  METHOD method_under_test.
    useful_value = my_dependency->calc_value( ).
  ENDMETHOD.
ENDCLASS.


*----------------------------------*
* In the local ABAP Unit include
* Goto -> Local Definitions/
* Implementations -> Local Test
* Classes...
*----------------------------------*

*----------------------------------*
* There is a local test double class
* that reimplements IF_MY_DEPENDENCY
* for use in testing.
*----------------------------------*
CLASS ltd_test_double DEFINITION.
   PUBLIC SECTION.
      INTERFACES if_my_dependency.
      ...
ENDCLASS.

CLASS ltd_test_double IMPLEMENTATION.
   METHOD if_my_dependency~calc_value.
       r_value = 1.
   ENDMETHOD.
ENDCLASS.

*----------------------------------*
* LTC_ABAP_UNIT_TESTS is declared as
* a LOCAL FRIEND of the class under
* test. As a LOCAL FRIEND, the test
* class can access protected and
* private members of CLASS_UNDER_TEST.
*----------------------------------*
CLASS ltc_abap_unit_tests DEFINITION DEFERRED.
CLASS class_under_test DEFINITION
   LOCAL FRIENDS ltc_abap_unit_tests.

*----------------------------------*
* In LTC_ABAP_UNIT_TESTS, the test
* method substitutes a test double for
* the production object referenced
* by the private attribute MY_DEPENDENCY
*----------------------------------*
CLASS ltc_abap_unit_tests DEFINITION FOR TESTING
"#AU DURATION SHORT
"#AU RISK LEVEL HARMLESS
PRIVATE SECTION.
  DATA:
     cut TYPE REF TO class_under_test.
  METHODS: calc_value_test FOR TESTING.
ENDCLASS.

CLASS ltc_abap_unit_tests IMPLEMENTATION.
  METHOD calc_value_test.
     DATA: useful_value TYPE i,
               test_double TYPE REF TO ltd_test_double.

     CREATE OBJECT test_double.
     CREATE OBJECT cut.

     cut->my_dependency = test_double.
     useful_value = cut->method_under_test( ).

     cl_abap_unit_assert=>assert_equals(
        act = useful_value
        exp = 2 ).
  ENDMETHOD.
ENDCLASS

Here is how the sample code works:

  • Instead of creating depended-on objects (dependencies) inline in its code, CLASS_UNDER_TEST in the sample does the following:
    • It declares dependencies as PROTECTED or PRIVATE attributes.
    • It instantiates these depended-on objects outside the methods that actually use them. In this case, the CONSTRUCTOR does the instantiation.

      The instantiation of depended-on objects is hidden from users of CLASS_UNDER_TEST.

  • A local helper class - LTD_TEST_DOUBLE in the sample code - provides a test version of the IF_MY_DEPENDENCY object used by CLASS_UNDER_TEST.
  • In the ABAP Unit test class - LTC_ABAP_UNIT_TESTS in the sample code - the test method creates an instance of the test double. It also creates an instance of the CLASS_UNDER_TEST, and substitutes the test double for the production MY_DEPENDENCY object for use in testing.

An alternative to cluttering up the test method with the instantiation of the test double and CUT test object would be to do these tasks in the ABAP Unit SETUP and TEARDOWN tasks.

Note: Don't miss the Session on Task-Driven Development in ABAP at TechEd 2010. 

Breaking dependencies on external code and resources is one of the keys to successful test-driven development. (A dependency, loosely defined, is the reliance of your program upon another program or resource. In common usage it also refers to the  'depended-on component' itself, as in 'injecting a dependency'. Injecting or breaking a dependency simply means 'making it possible to substitute a test entity for a depended-on component when doing unit testing'.) 

If the code under test in unit testing contains too many unbreakable dependencies, then the unit test becomes meaningless. Or at least, it is no longer a test of the discrete unit of code under test. You don't have the control to test the response of your code to different behaviors of the dependency. And your unit tests are no longer reliable tools for driving and validating your development work.

In NetWeaver 7.0 EHP1 (SAP_BASIS 7.01), the strategy for managing dependencies in ABAP is like this:


  • You need to ensure that ABAP objects in your code can be replaced with test doubles when ABAP Unit tests are running. That is, the dependencies represented by these objects must be 'injectable', so that the production object can be swapped out for a test object when testing.

  • You need to encapsulate non-ABAP Object dependencies such as the following in local or global ABAP classes:
    • Database operations
    • Calls to function modules and executable programs
    • Interaction with user interfaces
    • Authorization checks (the ABAP AUTHORITY-CHECK statement)
    • Use of files, system fields, and other resources.
  • For example, your code should not use the database or call a function module directly, but only by way of an encapsulating ABAP object. The object uses the database or calls a function for you.

    You need to ensure that these ABAP objects as well are 'injectable', so that the dependencies encapsulated by these objects can be broken for testing.

A Pattern for Constructor Injection

Constructor injection is a common pattern for breaking dependencies in all kinds of object-oriented languages. What could constructor injection look like in ABAP coding?   

In constructor injection of dependencies, you do not create a needed ABAP object in the code under test. Instead, you pass the object to the code under test as an argument of the CONSTRUCTOR method of the class.

In your ABAP Unit test methods, you can then substitute test doubles when you create the test object.

Here is what this pattern might look like in ABAP.

*------------------------------------------------*
* In CLASS_UNDER_TEST...
*------------------------------------------------*

*------------------------------------------------*
* The CONSTRUCTOR sets the private class
* variable MY_DEPENDENCY to reference
* the object of MY_INJECTED_DEPENDENCY.
*------------------------------------------------*
CLASS class_under_test DEFINITION CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS constructor
      IMPORTING my_injected_dependency TYPE REF TO if_my_dependency.
    METHODS method_under_test
      RETURNING
        value(useful_value) TYPE i.
  PRIVATE SECTION.
    DATA my_dependency TYPE REF TO if_my_dependency.
ENDCLASS.

CLASS class_under_test IMPLEMENTATION.
  METHOD constructor.
    my_dependency = my_injected_dependency.
  ENDMETHOD.

*------------------------------------------------*
* Method_under_test uses the dependency.
*------------------------------------------------*
  METHOD method_under_test.
    useful_value = me->my_dependency->calc_value( ).
  ENDMETHOD.
ENDCLASS.


*------------------------------------------------*
* In the local ABAP Unit test classes -
* Goto -> Local Test Classes...
*------------------------------------------------*

*------------------------------------------------*
* There is a local test double class
* that reimplements IF_MY_DEPENDENCY
* for use in testing.
*------------------------------------------------*
  CLASS ltd_test_double DEFINITION.
    PUBLIC SECTION.
      INTERFACES if_my_dependency.
  ENDCLASS.
  CLASS ltd_test_double IMPLEMENTATION.
    METHOD if_my_dependency~calc_value.
      r_value = 1.
    ENDMETHOD.
  ENDCLASS.

*------------------------------------------------*
* In the ABAP Unit test class, the test
* method creates a test double for
* MY_INJECTED_DEPENDENCY and creates
* the test object using the double.
*------------------------------------------------*
CLASS ltc_abap_unit_tests DEFINITION FOR TESTING
  "#AU RISK LEVEL HARMLESS
  "#AU DURATION SHORT.
  PRIVATE SECTION.
    METHODS: test_method_under_test FOR TESTING ...
ENDCLASS.
     
CLASS ltc_abap_unit_tests IMPLEMENTATION.
  METHOD test_method_under_test.
    DATA cut TYPE REF TO class_under_test,
            test_double TYPE REF TO ltd_test_double,
            useful_value type i.
    CREATE OBJECT test_double.
    CREATE OBJECT cut
      IMPORTING
        my_injected_dependency = test_double.

    useful_value = cut->method_under_test( ).

    cl_abap_unit_assert=>assert_equals(
      act   = useful_value
      exp   = 1  ).
  ENDMETHOD.
ENDCLASS.

 

Here is how the sample code works:

  • Instead of creating depended-on objects (dependencies) inline in its code, CLASS_UNDER_TEST in the sample above imports depended-on objects with its CONSTRUCTOR method. This means that CLASS_UNDER_TEST can use separate production and test versions of its dependencies.

  • A local helper class - LTD_TEST_DOUBLE in the sample code - provides a test version of the IF_MY_DEPENDENCY object used by CLASS_UNDER_TEST.

  • In the ABAP Unit test class - LTC_ABAP_UNIT_TESTS in the sample code - the test method creates an instance of the test double. It also creates an instance of the CLASS_UNDER_TEST for use in the test methods in LTC_UNIT_TESTS.

    An alternative to cluttering up the test method with the instantiation of the test double and test object would be to do these tasks in the ABAP Unit SETUP method, which runs automatically before each test method.  You could also cleanup the 'test fixture' with the ABAP Unit TEARDOWN method, which automatically runs after each test method.  You just need to declare and implement these methods (or have Test Code Generation do part of this task for you).

  • When the ABAP Unit test methods in LTC_ABAP_UNIT_TESTS run, they use an instance of CLASS_UNDER_TEST that runs with a test double of IF_MY_DEPENDENCY.

    In this example, the test double functions as a 'responder', a mock or fake. It only returns a reliable test value. But you could easily make an 'observer' out of the test double by adding verification methods to it. A test method could then check for example the indirect output, or the data that the code under test provides to the test double.

 The ABAP Workbench and the Test-Driven Development Cycle in EHP1 is the first part of this weblog.

Perhaps you know some of the trademark slogans of test-driven development, like 'Red. Green. Refactor' or 'Test a little, implement a little, reflect a little.' 

In TDD, reduced to its basic work cycle, you write a unit test and run it. The test fails because the code under test is not implemented.  So you implement the code under test such that the unit test is green. Then you repeat the cycle until eventually the method under test and the entire class are completely developed, completely documented, and completely testable (using all of the unit tests you have developed along the way).

As a purely practical matter, how do you translate the TDD work cycle into ABAP development with ABAP Unit in the ABAP Workbench?  This weblog shows what ABAP TDD in the Workbench in NetWeaver Release 7.01 (7.0 EHP1) might look like. 

We aren't talking rocket science here, of course. But the idea is to save you some time and fumbling around the first time that you try to practice TDD in the Workbench.

If you don't know ABAP Unit, then you won't get very far with TDD until you know a little about it.  Here, you'll find a sample ABAP Unit implementation in the NetWeaver 7.0 documentation: http://help.sap.com/saphelp_nw70/helpdata/en/a2/8a1b602e858645b8aac1559b638ea4/frameset.htm

So what does practical TDD look like in the ABAP Workbench? We'll talk only about classes (you do develop only classes nowadays, right?  See ABAP Programming Guidelines, actually - Modern ABAP Programming - by Horst Keller and Wolf Hagen Thümmel.)

So, you create a brand new class in SE80 in the ABAP Class Builder. What next?

  1. Ideally, you would like to write your first ABAP Unit test method and then go from there.

    Here, we have to adapt TDD to the Workbench.  In NetWeaver 7.01 (7.0 EHP1), you'll first need to define the signature of a method to test.  There is no way to create an ABAP Unit include without a production method as a starting point. (This is fixed in EHP2.)

    So go ahead and define the signature of the first method in your class under test.  (You may want to keep on defining production method signatures first, since you can't define a method via forward navigation from a test method.)

  2. Create the ABAP Unit include by choosing Utilities -> Test Class Generation. You'll see the little dialog shown below. You can tell ABAP Unit to generate test methods for the production methods you specify. Choose all of the options - you'll save yourself some typing. 

    image 

  3. You'll find yourself in the class editor, ready to define your ABAP Unit test method.   

    After this initial create-include step, you can edit your ABAP Unit class by choosing Goto -> Local Test Classes.  Or, if you are lazy, you can call Test Class Generation again and tell it to create a test method for whatever new production method you have just defined.

  4. Implement your test class. Usually, ABAP Unit test classes are local classes in the class pool of a global class.
  5. What is an ABAP Unit test class?  Simply a class definition with the FOR TESTING option, together with method definitions that also have the FOR TESTING option. And then method implementations with calls to CL_AUNIT_ASSERT to evaluate the tests.

    CLASS abap_unit_testclass 
         DEFINITION FOR TESTING. 
         "#AU duration short      
         "#AU risk level harmless
      PRIVATE SECTION.
       METHODS test_toggle_value FOR TESTING.
       DATA m_ref TYPE REF TO cl_class_under_test.
       METHODS setup.      "Set up a fixture
       METHODS teardown. "Clear away a fixture.

    ENDCLASS.                    "abap_unit_testclass DEFINITION

    CLASS abap_unit_testclass IMPLEMENTATION.
      METHOD setup.
          CREATE OBJECT m_ref.
      ENDMETHOD.                   "setup

      METHOD test_toggle_value.
          DATA: test_value TYPE i.

          m_ref->toggle_value( CHANGING toggle_value = test_value ).
          cl_aunit_assert=>assert_equals( exp = 1 act = test_value ).

          m_ref->toggle_value( CHANGING toggle_value = test_value ).
          cl_aunit_assert=>assert_equals( exp = 0 act = test_value ).

     ENDMETHOD.                    "test_toggle_value
    ENDCLASS.                    "abap_unit_testclass IMPLEMENTATION

  6. The big moment has arrived. Activate the entire class. Then choose Class -> Unit Test or the appropriate entry from the class context menu. Or just Local Test Classes -> Unit Test from the test class editor to run the ABAP Unit tests. Of course, your empty TOGGLE_VALUE method will fail the unit test miserably, and ABAP Unit will branch to a results display like this one.
  7. ABAP Unit Error Display

    With F3 you can leave the ABAP Unit results display and return to the Class Builder.  Or you can jump from the ABAP Unit results display to the test method and from there to the production source code. There, you can implement TOGGLE_VALUE such that it passes the ABAP Unit test. And then you can start over, casting the next requirement on the method or the class in an ABAP Unit test method.

Coming up: Managing Dependencies for ABAP Unit testing

Software Layer-Aware Debugging (SLAD) in NetWeaver 7.0 EHP2

The Problem…

Have you ever had this problem?  You need to analyze a bug, or you want to find out how an application works. And you don’t know where exactly to set a breakpoint. So you start the ABAP Debugger and start stepping through the code.

And you keep on stepping through the code, endlessly, without reaching any of the application logic in which you are interested. ABAP applications often make use of application frameworks or technical infrastructures like ESI, Web Dynpro, or ALV. Finding your way through this infrastructure coding in the debugger to your own application coding can be time-consuming and inefficient.

… And The Solution

With NetWeaver 7.0 EHP2, which is delivered with Enhancement Package 5 of SAP ERP, ABAP brings you an elegant solution to the problem of reaching the code you want to debug:  layer-aware debugging.

The basic idea of layer-aware debugging is simple: you separate the code in the system into the code that you want to debug and all the rest that you don’t want to see. You tell the New ABAP Debugger about this, and the debugger lets you jump right to the code you want to see, skipping over (optionally) all the rest of the code.

Here’s a short film demonstrating Layer-Aware Debugging: Demo: ABAP Layer-Aware Debugging

You can define simple software layers either ad hoc, on the fly in the ABAP Debugger. Or you can define re-usable, more flexible layers as object sets in so-called SLAD profiles (Software Layer-Aware Debugging) in transaction SLAD.

You can vary software layers along these dimensions:

  • Granularity: You can define an object set broadly, as the list of software packages in a package component, for example. You can also define very narrow object sets, citing individual programs, classes, and function modules.
  • Visibility: You can specify in an object set whether code is visible in the debugger. You can also specify how the debugger should behave when it finds code from the object set – stop on entry, stop on exit.
  • Flexiblity: You can include multiple object sets in a profile, so you can modulate the behavior of the debugger with respect to software layers precisely.  And a profile can specify what the debugger should do with all the rest of the code that is not defined in object sets.

 Ad Hoc Layer-Aware Debugging

Here's how you can use layer-aware debugging on the fly in the New ABAP Debugger. Our example is an ABAP Web Dynpro application. We want to skip all of the ABAP Web Dynpro code and jump right to the GRMG application code in package DSWP_MON_GRMG.

Here’s how to proceed:

  1. Let's assume that you do not know where to set a user breakpoint for external debugging to capture the Web Dynpro application.  You can intercept the application using request-based debugging. Or you can use transaction SICF to have requests to the Web Dynpro application trigger the debugger. In either case, there is a lot of infrastructure code between your present location in the debugger and your own logic. 
  2. Once you are in the New ABAP Debugger, click on Configure Software Layer to define an ad hoc software layer. The debugger presents a screen in which you can do a direct definition of a layer (we’re doing this) or use a profile from transaction SLAD.

    Ad hoc layer aware debugging
  3. You’ll notice that Layer-Aware Debugging has added a new debugger control button, Next Object Set.  Click on this button to tell the debugger to skip ahead to the start of the first object in the software layer.

    Skipping unimportant layers
    The debugger responds by stopping at the start of the first object that it encounters in package DSWP_MON_GRMG.

    The debugger stops at entry to code in the defined layer
  4. If this is the right spot in the application logic, you can proceed with your debugging.  

  5. If there is still more non-layer code in the way, then you can continue to use Next Object Set to skip over the non-layer code.  The debugger will stop when it re-enters an object of package DSWP_MON_GRMG after processing a call or set of calls to code from packages that are not in the layer.
    Even if you knew in step 1 where to stop - where to set a user breakpoint for external debugging in order to capture the Web Dynpro application - layer-aware debugging can still be useful to you by letting you skip over extraneous coding.   

Defining Re-Usable Software Layers in Transaction SLAD

Direct, ad hoc layer definitions in the debugger are convenient for a quick debugging session. But often you can save yourself time by defining re-usable layer-aware object sets and profiles in the SLAD-Cockpit, transaction SLAD (Software Layer-Aware Debugging). 

Let’s say that it is your turn in support. With an SLAD profile, you do not need to enter a list of your packages in an ad-hoc software layer every time that you start the debugger. And only with an SLAD profile can you fine-tune the behavior of the debugger by referencing multiple object sets with differing visibility and debugger-behavior settings.  

Here’s how to define object sets and profiles in transaction SLAD.

  1. Start the SLAD-Cockpit by entering transaction SLAD in the Command field.  Here’s what the desktop looks like.

    SLAD desktop
  2. Toggle the Object Type field from Profile to Object Set, and enter a name for a new object set. (You can also work top-down from a profile and create object sets via forward navigation, if you wish). When you press the Create button, you’re asked to describe the object set and assign it to a component.

    Creating an object set
  3. Now you can specify the objects that belong to the object set.
  4. An object set is there to define the objects that belong to a software layer. But  an object set is actually made up of one or more Selection Sets. A selection set is where you actually identify the individual packages, classes, function modules or other objects that belong to an object set. Here, we have one selection set for the GRMG package itself, with any sub-packages. And we have a selection set for GRMG classes (classes in the GRMG package).

    Filling the object set
  5. Complete the object set by specifying how the selection sets are logically linked. You can link object sets with AND, OR, or NOT logic.
    Linking selection sets within object sets in this way lets you define elegant filters and conditions for jumping through the code in layer-aware debugging. But of course you can keep your object sets very simple as well.
  6. To stop at any object in any of the selection sets, just link the selection sets with OR in the Linking of Selection Sets editor. In this example, pressing Next Object Set in the debugger would stop the debugger in any object in the GRMG package AND ALSO (OR logic) any class in the GRMG package (class name specification CL_GRMG* in the selection set). This is a nice way to see all of the class implementations in GRMG in their context in programs or function modules.

    Linking selection sets - Or Logic
    Alternatively, you could stop in layer-aware debugging only at GRMG classes with AND. Or stop at all components in the GRMG package EXCEPT GRMG classes with AND NOT linking.

    Linking selection sets - AND logic
  7. Save the object set. The system asks you for a transport request for the object set – both object sets and profiles are ABAP Repository objects and therefore are transportable. You can transport setup work that you do in one system to other systems in your system landscape.

  8. Create an SLAD profile. Toggle the Object Type field from Object Set to Profile, and enter a name for the new profile.

  9. Once you have entered a description and – if applicable – the component to which the profile belongs, you can add object sets to the profile. Add object sets from the navigation pane on the left side of the screen – simply drag and drop objects sets into the profile pane.
  10. In the profile, you can specify the following for each object set:
    • Whether the object set is visible in the debugger or not
    • Whether system coding called by the objects in the object set should be visible in the debugger
    • Whether the debugger should stop at entry to the first object it encounters in the layer, when the debugger leaves the layer, or not at all.
    With the special object set <<%REST%>>, you can specify explicitly what the debugger should do with all of the code that DOES NOT belong to any of your object sets. Or with Remove Remainder, you can delete <<%REST%>> and let your objects sets determine how the debugger behaves. By default, <<%REST%>> code is not visible and is ignored by the debugger when layer-aware debugging is active.
    Here, the debugger should stop at entry to the first object it encounters in the GRMG_PACKAGE object set (according to the selection set and OR logic, that was any object in the GRMG package and also any GRMG class (classname CL_GRMG*).

    Specifying the visibility and handling of object sets
  11. Save the profile. It is ready to use in the debugger.
    As you may have seen above in ‘Ad Hoc Layer-Aware Debugging’, you turn on layer-aware debugging in the New ABAP Debugger by clicking on Configure Software Layer. You can then select your profile as the configuration for layer-aware debugging.

Notes: How to Find Out What to Include in a Software Layer


If you are really lucky, you know exactly where to set a breakpoint in order to debug a problem.  In this case, you don’t need layer-aware debugging. You can stop in the debugger exactly at the logic you want to see.

If you kind of lucky, then you are working with your own code or code that belongs to your project.   You probably know the packages and programs that contain your code.  And you can define a software layer consisting of these entities. This kind of positive list of interesting code is optimal; a software layer defined this way lets you jump right to your code and also lets you skip over infrastructure code that may be intermingled in your code.

But what do you do if you really don’t know the software that you are about to debug? How can you find out what to include in a software layer that you define for layer-aware debugging? Maybe you can ask someone who knows. Perhaps you are working on a problem involving a customer’s development – then you can ask about the packages and other components that the customer uses. 

But if you really are unlucky and do not even have a contact person who knows the code, here are two good ways to find out what to include in a ‘visible’ software layer:

  • Use the ABAP Runtime Analysis (transaction SAT) to trace the processing blocks in all or part of an application.  (See  the online documentation on SAT).

    SAT: Finding out what should be in a software layer
  • Use a simple pre-defined script in the New ABAP Debugger to trace all or part of the call hierarchy of a program in the debugger. (See  the online help for debugger scripts).

    Debugger scripts: Using a script to identify software layers

The information in these traces lets you assign trace entries to software components pretty easily. You can then define a software layer on the basis of package assignments or the objects in the traces themselves.  The traces may even show you exactly where to set a breakpoint.

Finding Packages with the SLAD Component to Development Package Mapping

If you know the component that you want to specify in a SLAD object set, then you can use the package select in the Object Set editor to find out which packages belong to the component.  Here’s how to do this:

  1. Starting from the Object Set editor, click on Display/Change Selection to open the package selector.

  2. Drill down to the component whose code you want to filter out in the debugger.  In this case, we go to the ABAP Web Dynpro Runtime component.

    Finding the component
  3. Double-Click on the component to see which packages are assigned to it.
  4. Here, you can see that we can hide the infrastructure code of ABAP Web Dynpro by making all packages that begin with SWDP* invisible in the debugger.

    Display the packages in the component
    Click on Adopt to move the packages that you mark to your object set.

The first part of this weblog did not quite manage to open a short dump as of Release NW04s 7.0 EHP1 for display. Instead it reviewed ways to extract contextual information from the short dump lists and elsewhere.

In this second part of the web log we, in the words of W. C. Fields, grab the bull by the tail and face the issue.  In a short dump, you want to answer these primary questions:

  • What exactly happened?
  • Where did it happen?
  • How can the problem be corrected? 

We will look at the diagnostic information and aids that the short dump offers for answering these questions.

From the Top: The Context of the Error

You have finally made it into one of the ABAP short dumps. You'll see a display that looks quite similar to this one.

ABAP Short Dump Display in Transaction ST22

Maybe the Short text , What happened, Error Analysis, and Source Code Extract will be enough to let you diagnose and correct the problem. That's often the case when a dump was caused by a relatively stupid programming error. But let's take it from the top and see what diagnostic help the short dump offers, just in case.

The Short Dump Heading

On a bold red background at the top of every dump you will find the short dump ID and the date and time at which the dump occurred.  Together with the Application Server and the WP Index from the dump list, you have all the information that you need to look for relevant messages in the ABAP System Log or Developer's Trace (see Part I of this weblog). 

If an exception occurred and the runtime error was cacheable, the exception that caused the dump is also shown. 

Together with the program name (ZSP_COMPLETE_FQDNS) from What happened?, you also already have enough information to search for a relevant OSS note.  The combination of dump ID or exception name and program name should find the right note, if it exists. You'll also find a more extensive list of search terms for the OSS in the How to correct the error section in the dump.

The System Environment: Context Information and Where Did That RFC Actually Come From?

You probably skip over the context information presented under System Environment. But there are some worthwhile nuggets of information in there.

Information on the Context of the Short Dump

If you plan to search for OSS notes and messages, then you will need the system release and SP levels, kernel patch level, and other facts on the ‘scene of the crime' to see whether notes or messages fit your problem.  If you plan to open an OSS message for SAP, then you can simply save and attach the entire short dump. (From the dump display in ST22, choose System -> List -> Save -> Local file.) That should help Support to respond quickly to the problem.

If you are analyzing the problem yourself, then here are three important bits of information:

  • At the bottom of the System environment list, you'll find a compact overview of the memory usage of the program at the time that it dumped. If you see that the program has allocated heap memory, then check to see in section Information on where terminated to see if the program was started in a background job. If the program was not running as a background job, then you might want to take a look at the memory consumption of the program in the Debugger with the Memory Analyzer or with the ABAP Runtime Analysis (transaction SAT).  A dialog program - one running interactively in a dialog process - gets heap memory - private, process-local memory - only if the memory resources of the Web AS have been exhausted. (Just to confuse things, background jobs manage memory differently, and get heap memory before getting ABAP Extended Memory.) If you see a dialog program with heap memory, then something is wrong with the program. Or the memory resources configured in the Web AS are inadequate.  Or possibly other processes are memory hogs and have forced this process into heap memory.
  • Check the User and Transaction section to see if the dump occurred while processing a dynpro screen. The User and Transaction list specifies the screen and ‘Screen Line' at which the dump occurred. Screen Line is actually the line in the flow logic of the dynpro at which the faulty module was called. You'll also get this information out of the Source Code Extract as well, but here, you won't have to piece together the information on which module in which dynpro in which program failed.
  • If you are dealing with an RFC problem in the RFC server, then the Server-Side Connection Information tells you where the RFC call came from. You can then find the short dump on the caller side, which may help you to understand the server-side dump.

image

And of course, the opposite is true. From a dump on the client side of an RFC interaction, you can find out where the call went.

What Happened Exactly:  Short Text, What Happened, Error Analysis

The key question is: what happened exactly?  You need to understand the problem in detail to be able to correct it. For this understanding, the Short text, What happened, and Error analysis sections are invaluable.

The Short text states what happened in a single line. In our MOVE_TO_LIT_NOTALLOWED_NODATA dump (above), the short text is this:

Short Text: The Short Dump Cause in One Line

Interesting - so I'm trying to overwrite a constant in my program? 

The What happened section of this dump adds the name of the program in which the error occurred. 

What Happened?: The Program Name and Sometimes the Complete Explanation

In the screen shot above, the identification of the faulty program is quite simple, since I was too lazy to write a faulty method that perhaps resided in a separate include.  But you may see more complicated explanations of the location of the error like this:  

  • The termination occurred in the ABAP program "SAPLSVIM" in "VIM_BC_LANGU_ADD". The main program was SAPLS_IMG_TOOL_5. The termination occurred in line 330 of the source code of program LSVIMF59. In this case, SAPLSVIM is the current program, in which the dump occurred, and the SAPL prefix indicates that we are actually talking about the function group SVIM. VIM_BC_LANGU_ADD is the processing block (function module, method, form routine) in which the dump occurred. LSVIMF59 is the name of the include in which VIM_BC_LANGU_ADD is located.
  • Or, if the crash occurred in a class, you might see something like this: The current ABAP program "CL_IM_SPROXY_BADI_CTS=========CP" had to be terminated because ..., where  CL_IM_SPROXY_BADI_CTS=========CP is the name of the class pool in which the dump occurred. You can enter the full name in SE80 or SE24 to display the class, but more commonly, you would simply enter CL_IM_SPROXY_BADI_CTS...

In many short dumps with few possible causes, the What happened section describes the error that occurred quite exactly.  The MOVE_TO_LIT_NOTALLOWED_NODATA dump, however, can arise out of many different circumstances.  It's not possible to say which transgression in the code produced the short dump, so detailed explanations are forced from What happened into the next very useful section, the Error Analysis.

Short dump texts are written by SAP kernel developers. The Error analysis sections often provide really detailed information about the possible causes of a short dump, which in turn reflects the detailed knowledge of the kernel that these developers have.  There is often a lot of text, but the time taken to read it through will be rewarded.

image

For example, we learned from the short text that my program dumped because it tried to overwrite a constant. I don't see any constant in the bad code below.  I just wanted to complete the fully-qualified domain names of a list of hosts.  Do you see the error in the code? 

The Transgressor: The Dumping Code

If you don't see where I try to overwrite a read-only field, then see the seventh point in the discussion in Error analysis, the one that begins "Accesses using field symbols..."  Experience has shown that a lot of people just skip over the explanations in What happened and Error analysis.  This may end up costing them more time than it saves.

Where Did It Happen:  Source Code Extract

The SAP Short Dump developers were right to put Source Code Extract in initial caps, because, if you are lucky, this is a really nice, helpful section of the dump.  You're shown exactly where the program was aborted.  A few people don't know that from here, you can jump right into source code in the ABAP Editor with a double-click.   In the dump that we have been following, it would be possible in the editor to branch to the definition of the internal table LT_CSMNSDIC, where you might notice that the CDNSNAME field has been declared as part of the key of the sorted internal table...

The Source Code Extract: Where the Code Broke

If you can reproduce the problem, then you can set a breakpoint right from the short dump in order to stop just before the short dump occurs. You can then use all of the tools of the new ABAP debugger to investigate the cause of the dump.

If the code line shown by the pointer doesn't seem to make any sense in the context of the dump, then take a look at the previous line of code.  Occasionally, the instruction counter may still advance even after a dump has been triggered, so that the >>>>>> pointer points at the line following the bad line of code. 

Where Did It Happen: Active Calls/Events

In program failures that involve infrastructure like Web Dynpro, or calls between components, or in which an uncaught exception has been passed up through the callers, the Active Calls/Events section may help you to understand the components involved in the crash.  This call stack is a useful supplement to the point of failure marked in the Source Code Extract, because in the stack you can see how you got to the point of failure.  

You read the Active Calls/Events list from the bottom up.  It shows all of the report events, dynpro modules, functions, methods and form routines through which the path of execution has come.  You can jump into the ABAP Editor at any level in the call stack. This means that you can set breakpoints all along the way to the dump if you think that a problem at a higher level resulted in the dump at the end of the stack. 

There are two things to remember about the ABAP call stack:

  • It's a call stack and not a complete history of calls. If the flow of execution returns from the last callee in the stack, that return from the callee is not shown in the stack.  If the short dump occurs in the caller, then you might wonder why the stack shows a different program as the end point of execution than the What happened section.
  • If ABAP dumped because of an incompatible call to a function module or method (CALL_FUNCTION_CONFLICT_GEN_TYP, CALL_FUNCTION_CONFLICT_LENG, CALL_METHOD_CONFLICT_TYPE, ...), then the called function or method will not appear as the last level in the call stack.  The call itself failed, so the callee is not shown in the stack.

Where Did It Happen: The Hard Way

Usually, the Source Code Extract shows where your error occurred.  But if you are unlucky, you may have to determine this vital piece of information the hard way. As a not so tragic example, if a short dump occurs in a macro, then the source code pointer will be set to the macro call, not to the statement in the macro that caused the problem. 

An error in the kernel may leave no information in the Source Code Extract at all. 

In cases like these, how can you find out where the short dump occurred?

Let's start with the no-source-code-its-a-macro case.  The Source Code Extract does show where the misbehaving macro was called.  Since you can jump into the ABAP Editor and then forward-navigate into the macro with a couple of clicks, you can first see if a good look at the macro code might reveal the problem. 

If you still can't see where in the source code the problem occurred, then the ABAP Control Blocks (CONT) section may help you to localize the problem.  The CONT table shows the CCBs - Control-Control-Blocks - which represent the ABAP statements to be executed in the processing blocks of an ABAP program. The short dump contains an extract of the CONT table showing the CCBs that lead up to the dump and the next few statements that were to be processed.  Read the list of CCBs from the top down.

Low-level as it is, the CONT does not care whether statements are in a macro or not - and it shows the short dump pointer that you know from the Source Code Extract. Unfortunately, a double-click on the CCB at the dump pointer still takes you only to point in the source code at which the bad macro was called.  But the halfway intelligible CCB names may be enough to show you at which line of code in the macro the problem occurred. 

First of all, if the macro is not too long, then clicking on the CCBs to jump into the ABAP Editor shows you where the macro started.  Then, with a little jumping back and forth between the CONT table and the ABAP Editor, you can start to equate the CCBs and the statements in the faulty code. 

In our case, the SQLS and PAR1 CCBs turn out to reference an SQL SELECT well before the macro call. CCB 68, BRAF, represents the start of an IF control structure in which the macro is called. The COND and PAR1 CCBs depict the macro statement that actually failed:  CONCATENATE &1 ‘.sap.corp' into &1.

image

In the case I-have-only-a-kernel-dump (SYSTEM_CORE_DUMPED, ABAP_ASSERT, etc.), the Source Code Extract section will really be empty.  In this case, the dump section Active Calls in SAP Kernel provides clues as to the location of the error.  But since no customer or ABAP application developer should have to read a kernel stack, we mention this only for the record. If you have a short dump that originated in the kernel and it is not simply because somebody pulled the plug on the ABAP AS, then all you need to do is provide the short dump with the Active Calls in SAP Kernel section to SAP Support.  

Other situations with no where-it-happened location:   Should you not have any luck in finding out exactly where the program went down the tubes, then a useful tip is to try to reproduce the problem in transaction SAT, the ABAP Runtime Analysis. In SAT, you can trace the execution of an ABAP program at the level of ABAP processing blocks. Run your program to its dump (provided that this does not take too long - a non-aggregated SAT trace can get large quickly).  Then check the SAT trace.  It may help you find out pretty exactly where to look for the problem, even if the dump occurred in a macro.

Also, you can use ST05, the Performance Analysis, to switch on (in a controlled fashion - for your user, for example) a detailed trace of program activity. Be aware that the trace will also include the writing of the short dump.  The dump processing starts where you find activity on DB table SNAP, so search for the problem area before that point. 

See help.sap.com for help with using SAT and ST05.

The Third Major Question:  What's the Solution? 

Naturally, the discussion that you will find in the How to correct the error section of a short dump tends to be a bit generic.  Developers are constantly finding new and inventive ways to repeat old errors, like the MOVE_TO_LIT_NOTALLOWED_NODATA error that we have been examining.  It's therefore not possible for How to correct the error to describe exactly what you should do to fix a dumping program.

Even so, the combination of the discussion in How to correct the error and taking a good look at the faulty code often leads to success in correcting the problem.  In the case of the MOVE_TO_LIT_NOTALLOWED_WA dump that we have been examining, the dump astutely remarks that ‘The field to be overwritten is a parameter or a field symbol.' If you were not aware that the sort keys of a sorted table may not be overwritten in a field symbol, then the tip that a field symbol may be involved might help you get onto the right analytical track. 

In the end, however, understanding and correcting the cause of a short dump rests on your shoulders. You will have to extract as much information from the short dump as possible, and use this information to illuminate what went wrong in the code. 

Gathering More Information

A short dump addresses more or less directly the journalistic questions of what went wrong where and what to do. Should these questions be addressed ‘less' rather than ‘more' in a dump, then it is good to know that a dump also includes a lot of additional supporting information that can help you in your analysis.

System Variables

As an ABAP program executes, it is accompanied by an entire swarm of system variables, like Jupiter with its cloud of little moons. Some of these variables are well-known, like SY-SUBRC, the return code set by many ABAP instructions or SY-TABIX, the counter in LOOP AT and READ TABLE internal table instructions. 

When a short dump occurs, ABAP preserves the state of the system variables at the time of the crash.  You can see the contents of these variables in the Contents of system fields section.  Here are some of the system variables that are most likely to be useful:

  • SY-SUBRC usually shows the last return code setting before the program crashed.  A non-zero SY-SUBRC from a method or function preceding an instruction that dumped may illuminate for you what went wrong.
  • SY-TABIX. In a short dump raised from within a LOOP AT table or after a READ TABLE instruction, SY-TABIX tells you what record from the internal table was being processed when the program failed.
  • SY-INDEX provides the same iteration-count information for DO and WHILE loops. 
  • SY-LINNO (number of lines in an ABAP list) and SY-COLNO (number of columns in an ABAP list) show how much memory a large ABAP list consumes, if you are having memory problems with a large list.
  • SY-MSGID and SY-MSGNO, if set, let you look up the last message issued by the failed program in transaction SE91.  SY-MSGV1 - 4 show any message variables that previously were set (not necessarily for use in the most recent message).
  • SY-DATUM and SY-UZEIT may show a more accurate and earlier time stamp for the initial program abort than the date and time associated with the short dump itself. If you are sifting through the System Log or Developer Traces (see the Part I of this weblog), then the few seconds difference that you may see can be important in establishing to chronology of events in a failure.

Program Variables

For the Chosen variables section, the short dump infrastructure takes a quick run through the collapsing program context  grabbing any program  and infrastructure variables it finds that are currently in scope.  The situation is a bit like the belated shopper running through a grocery just at closing time - there's no guarantee that the shopper will bring home everything that he or she was supposed to buy.  Even though the dump infrastructure may not capture everything, much more often than not you will find the variables and values that you want to see. 

Since SAP_BASIS Release 6.20, the short dump infrastructure has captured a separate set of Chosen variables for each level in the Active Events/Calls ABAP call stack.

image

If you are analyzing a data-related problem, then a careful look at the Chosen variables may clarify the problem. In one recent example, an OSS message reported a short dump because ABAP could not convert the character value 229812 to an integer (dump ID CONVT_NO_NUMBER). Since this is one of ABAP's easiest tricks, the dump is at first glance pretty mystifying.  A quick look at the character field in Chosen variables showed, however, that the character field held not ‘229812' but rather ‘229812##믆䀾##蠤䋒##p###'. The fact that the field was either not correctly initialized or was filled with non-character data explains the conversion failure, at the very least.

Chosen variables shows the size (here, one record with a length of 3440 bytes) of an internal table, as well as useful information such as the type of organization of the table (here, a sorted table).  The table display can be useful in analyzing the popular dump of type TSV_TNEW_PAGE_ALLOC_FAILED (no more memory available for an internal table), since you can see how much memory has been allocated to hold the rows of  each internal table.  (The amount of storage allocated for the rows may not, however, be the amount of storage used by the rows of the table. If, for example, a table holds only data references to objects, then storage for references may not be all the memory actually consumed by the table and its contents.  The references are relatively short. The objects may occupy much larger amounts of memory.)  

image

In an upcoming release, the table display will contain at least the start of the contents of each of the first five records of each internal table that is captured.

Finally, object references that have not been initialized (a favorite cause of OBJECTS_OBJREF_NOT_ASSIGNED_NO, and others...) are easy to pick out in Chosen variables.  Just use Ctrl - F to search for ‘:initial}'.

A Possible GETWA_NOT_ASSIGNED Trigger

Note that a random mouse click in the Chosen variables display switches the display from the relatively attractive formatted view to an unformatted view.  Don't be alarmed. Just click on F3 / Back to return to the formatted display.

An Ounce of Prevention... 

Is worth a pound of cure, as the old saying goes. 

Don't forget that ABAP offers logging and checkpoints that can be activated when needed (see help.sap.com).  With these, you can turn on switchable logging, breakpoints, and assertions to help you with diagnosis and trouble-shooting, should something go wrong in your program after it has reached your users.

And don't forget the suite of tools that the ABAP Workbench offers to help you find errors before your users do, starting with tools for static checking like the Code Inspector (Transaction SCI), continuing with the ABAP Unit Test facility, with which you can even go so far as to practice test-driver development.  The best ABAP short dump is the one that you never have to analyze.

 

- This weblog is based in part on Boris Gebhardt's Advanced ABAP Workshop: ABAP Analysis Tools. You can find more information on ABAP Test and Analysis Tools at help.sap.com and also in ABAP: Advanced Tools and Techniques, Volume 2, SAP Press 2009, ISBN 978-3-8362-1151-2.

 

ABAP ‘short dump' (German Kurzdump) is a misnomer. There is nothing ‘short' about an ABAP dump. Should the ABAP AS no longer be able to execute a program - because of an unhandled or unhandleable exception, a resource or system problem, or an error in coding - it may document the problem with 20 or more pages of diagnostic information.

 

 

Very often, a short dump contains not only the exact diagnosis of the problem that occurred but also the solution, or at least important pointers toward the solution of the problem. But experience has shown that developers often don't even read dumps - not even the highly useful Error analysis - much less make use of the diagnostic resources that short dumps offer.

 

The lack of attention to short dumps is understandable but regrettable.

Understandable  because the report that your program is dumping in a customer system is about the worst news you can get. It's time to drop everything and switch to emergency mode. 

Regrettable, because often developers who don't take a good look at the short dump waste a lot of time thrashing around in the debugger, trying to understand what went wrong. Taking a good look at the short dump is usually a better use of your time. And then there are the situations in which the dump is the only diagnostic resource that you have - when the dump occurred in a production system and is too sensitive to repeat, when the dump occurred several hours after the background job started, and so on.

In this pair of weblogs, we will take a quick tour through the ABAP short dump as of NetWeaver Release 7.0 EHP1, pointing out important analytic aids that it offers and how to make the best use of them.

In the first weblog, we will just get ready to analyze a dump. The weblog looks at the ABAP dump lists and how to get the most out of them, as well as at a couple of related sources of information.

Off to the Dump

If you aren't staring at a short dump on the screen in front of you, then the way to see any ABAP short dumps in your system is by starting transaction ST22.  As standard selections, ST22 lets you list dumps from yesterday and today, but also lets you select dumps by user, date and other parameters.

Adding Information to the List of Selected Runtime Errors

It is natural to want to hurry right to the display of a dump that you need to investigate. But hold on - have you ever noticed those three useful little options at the bottom of the ST22 start screen?  You might want to take a look at them before you rush off into a list of short dumps.

Options for Enhancing the Selected List of Runtime Errors

 

By default, the options are not set.  But:

With information on Exception/Short Text of Runtime Error extracts the data provided by the exception object, if a class-based exception triggered the dump.  The display of the exception object may show you useful information, such as the exception's error message. If the initial exception was caught by another exception, then you can see the chain of exceptions. 

!https://weblogs.sdn.sap.com/weblogs/images/251686928/blog_st22_exception_chain_scaled.jpg|height=326|alt=image|width=700|src=https://weblogs.sdn.sap.com/weblogs/images/251686928/blog_st22_exception_chain_scaled.jpg|border=0

0.1.

The program affected and Program and associated application components (long runtime) options add information to the List of Selected Runtime Errors. The name of the program in which the dump occurred is shown.  Just as importantly, the list displays the component (BC-CCM-MON, or FI-XXX-YYY or whatever) in which the dump occurred, if it can be found.

Sometimes the program and component may be misleading, if an application problem triggers a dump somewhere in infrastructure coding. But often, you can search for notes with the program and component. (The How to correct the error section in the dump long text shows a more extensive list of possible search terms.)

 

If you need to write an OSS message to SAP, you know the component in which to file the message.

 

 

If the component is not shown or you think that the component shown is misleading (the dump was finally triggered in infrastructure code), then you can find the component for an OSS message by following the path from the suspect program (for example, from the Active Calls/Events section) to the package of the program to the component.  In SE80, go from the attributes of the program to the package. In the package, the component is displayed.

 

Surveying the Scene of the Disaster

You've set those useful dump list options. Now you want to get on with looking at that dump. 

But once again - wait!  Especially if you are working on an ill-defined problem - you don't have a really good idea of what caused the dump - then it is wise to take a good look around the scene of the disaster before plunging into the details.  Otherwise, you may miss vital clues to external factors that contributed to or even caused your dump.

Start your survey with the dump lists that ST22 offers, which offer useful information to the keen-eyed investigator.

The List of Selected Runtime Errors

 

The main dump display is the List of Selected Runtime Errors. Just click on Today on the ST22 start screen to take a look at current short dumps.

 

The list of may look something like the screen shot below. (This is from a system that is dumping quite busily - a headache for the QMs.)

The Selected List of Runtime Errors in Transaction ST22

 

As you look at the list of dumps, ask yourself these questions:

0.1. Is your dump a mass phenomenon? 

0.2. When did the trouble with the dumps start?

0.3. Does the dump occur on only one server?  Or for one user? Or for one program?

0.4. What other dumps occurred around the same time as your dump?  Are there signs of system or database problems or problems in components which your program uses?

If your dump is a mass phenomenon, then you want to know this as quickly as possible  If there are patterns in the list of short dumps, then you want to find these, because they could signal that your dump is part of some larger problem with the system or one of its components.

The Dump Overview

 

As you look at the List of Runtime Errors above, you may have the feeling that you can't see the forest for the trees.  There are so many dumps. They are clustered by type, but none of the sets of dumps seem likely to share causal explanations or to fit the journalistic questions posed above.  What's going on?

 

 

In cases like this, the more orderly view of dump traffic offered by the Overview function may help.

 

 

Choose Goto -> Overview from the ST22 start screen. Skip over the following selection screen with Execute.  The system shows you what has been dumping in the system, sorted by dump category.   

 

The Dump Overview in Transaction ST22

 

The foci of dump activity make it clearer what is going on in the system. First,  the LOAD_PROGRAM_LOST short dumps tell us that we are in a development system, in which infrastructure source code (in this case) is being changed on the fly.

The UNCAUGHT_EXCEPTIONs and OBJECTS_OBJREF_NOT_ASSIGNED_NO may indicate that the developers have not cleanly implemented some programs as yet. Or perhaps some particular program or component is not working quite right.  The scattering of dump activity among unrelated programs is in this case also explained by the fact that the system is apparently a development system.  For a closer look, a double-click on one of the entries in the list selects the relevant short dumps for display.

The ABAP AS defines more than 1600 short dumps, all documented in loving detail by the kernel developers who are responsible for them.  Some of these - the ‘usual suspects' in the parlance of the film ‘Casablanca' - already indicate to the savvy investigator that something other than an ABAP error is at play in the system.   A list of the usual suspects might include these short dump IDs:

Short Dump ID

Cause and Significance

SYSTEM_CORE_DUMPED

The operating system sent the work process a signal. Perhaps the computer was shut down or the process was explicitly killed, perhaps a serious OS error occurred. 

 

No analysis or correction on the ABAP side is possible or necessary. The system core dump is prima facie evidence that other dumps occurring around the same time are related either directly or indirectly to the core dump.

DATASET_CANT_CLOSE

DATASET_CANT_OPEN

DATASET_NOT_OPEN

A file system error has occurred. Perhaps the file system is full, perhaps the file or directory did not exist. 

 

Most likely a file system or configuration problem has caused the dump.  Check the file system with ST06 or with OS tools.

INCL_NOT_ENOUGH_PAGING

INCL_NOT_ENOUGH_PXA

INCL_NOT_ENOUGH_ROLL

 

MEMORY_</p></td><td width="284" valign="top"><p>In many cases, overuse of configured ABAP AS memory resources through one or more work processes has occurred, or the memory parameters in the instance profile are simply not adequately dimensioned for the size and workload of the instance. <br /><br />The System Environment section of the dump shows you the memory consumption of the dumped program; you can check whether it in fact was the culprit (for example, it makes major use of Heap storage). <br /><br />Also: Use SM50 to look for processes in PRIV mode. Use ST02 to check the ABAP buffers and memory. Use the Memory Analyzer in the new ABAP Debugger to check suspect programs.</p></td></tr><tr><td width="284" valign="top"><p>DB_ERR_<DBS>

 

In many cases, a problem with the database (not necessarily provoked by misbehavior in an ABAP program) has occurred.

 

Use ST04 to check for database problems.

NI_HOST_TO_ADDR

NI_MY_HOSTNAME

The system cannot identify a remote server by name or cannot even identify itself by name.

 

Somebody has messed up the network configuration or a network failure has occurred. Use network tools to analyze the problem.

TSV_TNEW_PAGE_ALLOC_FAILED

Usually due to a poorly implemented internal table. In some cases, however, may be due to inadequately dimensioned memory parameters in the instance profile.

 

If a problem with an internal table seems unlikely, then use ST02 to check the ABAP buffers and memory.

One More Detour

You've looked at the dump lists. Perhaps you have taken a look at your dump as well. And you still aren't sure what has gone wrong.  One more detour - to the System Log - often provides additional useful clues before you finally get to the dump. 

You need to know the application server on which the dump occurred, which you can get from the dump lists.  Start the SysLog (transaction SM21) on the application server on which the dump occurred. 

Find your dump in the system log. As a search term with CTRL-F, ‘short dump' is useful.  (Be aware that the system log writes on a circular file, so the report of your dump can be overwritten.  Save the file if it is useful to you by downloading it.)  Then look for events before - even well before - your dump and for significant events thereafter.  

The Developer Traces

If even the System Log has not helped you to understand the dump, then you may need to look even further afield for more clues as to what went wrong.  In this situation, the ABAP developer traces may help you to find the missing clues.

 

The developer trace is the log that each kernel component (work processes, message server, gateway, and so on) writes. Even in the standard setting, it offers a detailed trace of the activity of each kernel component, whereby in the case of an ABAP dump in most cases only the work process traces are useful.  A work process dev-trace is written by each individual work process in an ABAP instance. To access the dev-trace, you therefore need both the name of the instance and also the number of the work process in which the dump occurred.  Fortunately, the Selected List of Runtime Errors shows both items of information, in the Application Server and WP Index fields, respectively. is the work process number that you found in field WP Index. There is a current dev-trace file, which was started when the instance was started, as well as a dev_wThe dump you want is older than today?  The ABAP AS regularly reorganizes dumps. By default, all dumps are deleted at the latest after seven days. But don't give up.  Enter the start and end dates in which you are interested in Own selection on the ST22 start screen. Often, especially if the system has not been dumping much, dumps up to a week old are still accessible.

 

If you need to hold on to a dump, be sure to lock it in the list of short dumps. It won't be deleted during reorganization.

!https://weblogs.sdn.sap.com/weblogs/images/251686928/blog_st22_locked_dump.jpg|height=167|alt=Locking a Dump to Keep It Safe from Reorganization|width=555|src=https://weblogs.sdn.sap.com/weblogs/images/251686928/blog_st22_locked_dump.jpg|border=0!</body>

 

Here’s a tip for those of you out there who are responsible for quality testing using ABAP Unit module tests.  You can automate ABAP Unit runs with the ABAP Code Inspector as of  Release NW04 (6.40).

 

Let me explain why this is a useful tip.  For most quality advocates, the first task is to get your developers to implement ABAP Unit tests.  

 

But once you have ABAP Unit tests in place, then a second, problematic task emerges:  Running the ABAP Unit tests regularly.  After all, you created the unit tests because you wanted to detect problems introduced into existing functions by changes in coding, and you wanted to be able to verify that new coding works as designed.

 

To achieve these desirable results, you need to run a perhaps quite large set of unit tests more or less regularly, when code arrives in your test system.  It is a bit tedious, however, to travel through the relevant classes, function groups, programs in the ABAP Object Navigator calling Test -> Unit Test off the context menu at each stop.   

 

Fortunately the Code Inspector of the ABAP Workbench provides a convenient way for you to automate executions – and even track results – of selected sets of ABAP Unit tests.  Here is how to do this: 

  1. Start the Code Inspector (transaction SCI) in your testing system.  
  2. Create an object set that specifies what ABAP Unit tests are to be run. 

    Create an Object Set
  3. Pick out the ABAP classes, programs, and function groups whose unit tests are to run under this object set. 

    Ideally, you can just specify the set of packages in which you are actively developing.  Leave Save Selections Only on to select objects according to object assignments fresh every time that you use the object set.

    Select the Objects Whose Unit Tess Should Run

    When you are ready, save the object set and return to the SCI start screen.
  4. Create a check variant.

    Create a Check Variant
     
  5. Mark only the Dynamic Tests option on the Check Variant screen.  Then save your check variant and return to the SCI start screen.

    Mark Dynamic Tests
  6. Now you can create an inspection that uses the object set and check variant that you just created.

    Create an Inspection

    Run the Inspection with Execute...
     
  7. The Execute button with opens a window in which you decide how to run the inspection.  As the screen shot shows, set the inspection to run as a background job. Whenever the job runs, all of the ABAP Unit tests in the object set will be run. The Code Inspector saves the results of each run.

    Schedule the Inspection to Run as a Job

    All of the repetition features of ABAP background processing are available to you – but if you import code on a regular schedule, then you probably need only schedule the job periodically.

    Here, the ABAP Units inspection is scheduled to run immediately as a background job and will be repeated weekly.

    Choose Periodic Execution for the Batch Job
     
  8. A finished inspection is marked with a green icon, as you can see here. Clicking on Possible Entries (F4) in the Inspection field shows you the status of the current and past inspections.  Click on an inspection to open it.

    Choose from the Available Results

    Then click on the Results button to see any messages issued by the ABAP Unit tests.

    Finally - The ABAP Unit Results

Filter Blog

By date:
By tag: