Using the CTF Library Routines

Ordinarily, you would configure CTF using the MFTRACE_CONFIG variable, to output Run-Time System events, which can be useful for debugging issues and understanding code behaviors. However, if you are developing a large application, you may also want to add your own events to trace high-level code paths, or change CTF tracer and emitter properties at run time. The CBL_CTF_* library routines allow you to do this.

Note: The code excerpts used in this topic are from an example application of a graphics component behind a text editor, which contains three discrete sub-components: a keypress handler, a glyph shaper, and a glyph renderer. The excerpts displayed do not form a complete program; they show certain tracing techniques that you could incorporate into your own programs.

Enterprise Developer supplies a copybook, mfctf.cpy, which contains structures that make it easier to use the CBL_CTF_* library routines. The application also uses a constant to determine the current component name:

       copy "mfctf.cpy".
       78 THIS-COMPONENT               value "GRAPHICS".

The application also contains the following items in working-storage, used to store data such as the tracer handle. The ctf-trace-event-* structures store data to be associated with the custom trace events.

       78  MAX-TRACE-DATA              value 5.
 
       01  ctf-event-data.
           03  ctf-tracer-handle       pic x(4) comp-5.
           03  ctf-trace-event         cblt-trc-event.
           03  ctf-trace-event-lens    pic x(4) comp-5
                                       occurs MAX-TRACE-DATA.
           03  ctf-trace-event-types   pic x(4) comp-5
                                       occurs MAX-TRACE-DATA.
           03  ctf-trace-event-ptrs    pointer
                                       occurs MAX-TRACE-DATA.

The CBL_CTF_TRACER_GET routine obtains the tracer that corresponds to the component to be traced. If the tracer does not already exist, it is created. The ls-api-flags parameter can be used to indicate if the tracer name string is null- or space-terminated.

       move 78-CTF-FLAG-COMP-NAME-NULL-TERM to ls-api-flags
       move THIS-COMPONENT & x"00" to ls-comp-name

       call "CBL_CTF_TRACER_GET" using
                                 by value     ls-api-flags
                                 by reference ls-comp-name
                                 by reference ctf-tracer-handle

Properties for the tracer can be created in working-storage; these can represent different event types, sub-components, etc.... The properties are used to turn the related tracing of them on and off.

       78  PROPNAME-ALL                value "ALL"
       78  PROPNAME-EVENTS             value "EVENT_HANDLING".
       78  PROPNAME-GLYPHS             value "GLYPH_SHAPING".
       78  PROPNAME-RENDER             value "RENDERER".

A working storage array (ws-prop-name) contains the names of each property:

       78  PROPNAME-ALL                value "ALL".
       78  PROPNAME-EVENTS             value "EVENT_HANDLING".
       78  PROPNAME-GLYPHS             value "GLYPH_SHAPING".
       78  PROPNAME-RENDER             value "RENDERER".

       01  ws-prop-names.
           03                          pic x(15) value PROPNAME-ALL.
           03                          pic x(15) value PROPNAME-EVENTS.
           03                          pic x(15) value PROPNAME-GLYPHS.
           03                          pic x(15) value PROPNAME-RENDER.
           03                          pic x(15) value spaces.
       01  ws-prop-name                redefines ws-prop-names
                                       pic x(15) occurs 5.

To create these properties in the tracer, CBL_CTF_COMP_PROPERTY_GET is used, which generates properties that don't already exist. In this example, the ws-prop-names is iterated to create each property. To show that the properties will store integer values, the INT-VALUE flag is supplied.

           move 78-CTF-FLAG-PROP-INT-VALUE to ls-api-flags

           perform varying ls-prop-id from 1 by 1
                     until ws-prop-names(ls-prop-id) = spaces

               call "CBL_CTF_COMP_PROPERTY_GET" using
                              by value     ls-api-flags
                              by reference ctf-tracer-handle
                              by reference ws-prop-name(ls-prop-id)
                              by value     0
                              by reference ls-prop-value

           end-perform

To decide which sub-components to trace, CBL_CTF_COMP_PROPERTY_SET sets the required properties. This example enables output events for the RENDERER property:

           move 78-CTF-FLAG-PROP-INT-VALUE to ls-api-flags
           
           move 1 to ls-prop-value

           call "CBL_CTF_COMP_PROPERTY_SET" using
                              by value     ls-api-flags
                              by reference ctf-tracer-handle
                              by reference "RENDERER"
                              by reference ls-prop-value

A callback can be posted when a property is updated. This allows a bitmask of the current tracer properties and tracer level to be automatically maintained, rather than having to call CBL_CTF_COMP_PROPERTY_GET each time to query a property. Note: this is only necessary to support dynamic CTF configuration.

To do this, a structure to hold the callback and tracer details is required. (This structure is defined in the mfctf.cpy copybook.)

           03  ls-install-param        cblt-trc-notif-install.

It must be Initialized to 0, then the tracer handle and a pointer to the callback function be placed into the structure, which then allows CBL_CTF_TRACER_NOTIFY to place the callback.

           move low-values to ls-install-param
           
           move ctf-tracer-handle to
                cblte-tni-handle of ls-install-param
                
           set cblte-tni-callback of ls-install-param to
               entry "ctf-tracer-notif-callback"
               
           call "CBL_CTF_TRACER_NOTIFY" using
                                        by value 0
                                        by reference ls-install-param

Additional flags are added, each one occupying a separate bit in the bitmask, before the callback is written. The bitmask can be quickly queried using ctf-trace-flags b-and TRACE_FLAGS-*.

           78  PROPID-ALL                        value 1.
           
           78  TRACE-FLAGS-RENDERER              value h'00000001'.
           78  TRACE-FLAGS-EVENT-HANDLER         value h'00000002'.
           78  TRACE-FLAGS-GLYPH-SHAPING         value h'00000004'.
           
           
           01  ctf-config-data.
                03  ctf-trace-level         pic x(4) comp-5.
                03  ctf-trace-flags         pic x(4) comp-5 value 0.              

Bitmask querying is covered again later when events start to be traced.

Additional structures are required, which are defined in the mfctf.cpy copybook.

       linkage section.
       01  lk-ctf-tracer-handle        pic x(4) comp-5.
       01  lk-ctf-notif-type           pic x(4) comp-5.

       01  lk-ctf-notif-param          pic x.
       01  lk-ctf-notif-param-level    redefines lk-ctf-notif-param
                                       pic x(4) comp-5.
       01  lk-ctf-notif-param-property redefines lk-ctf-notif-param
                                       cblt-trc-notif-prop-change.
       01  lk-ctf-property-name        pic x.
       01  lk-ctf-property-value       pic x.                

The callback determines whether the notification was for a change in the tracer level, or a change in the other properties, and then the callback can be written.

If the notification is for a change in one of the other properties (for example, ALL, RENDERER, EVENT-HANDLER, or GLYPH-SHAPING), a separate function is entered.

           entry "ctf-tracer-notif-callback" using
                                      by value     lk-ctf-tracer-handle
                                      by value     lk-ctf-notif-type
                                      by reference lk-ctf-notif-param.
           evaluate lk-ctf-notif-type
               when 78-TRC-NOTIF-TYPE-PROP-CHANGE
                   perform ctf-tracer-notif-property

               when 78-TRC-NOTIF-TYPE-LEVEL-CHANGE
                   move lk-ctf-notif-param-level to ctf-trace-level

               when other
                    continue
           end-evaluate

           goback
           .           

To work out which property was changed, the list of property names defined earlier is iterated, checking against the name of the property that changed. When there is a match, the new value is copied into the ls-prop-value variable, and apply-property-value is performed.

           ctf-tracer-notif-property section.
           set address of lk-ctf-property-name to
               cblte-tnpc-name of lk-ctf-notif-param-property

           perform varying ls-prop-id from 1 by 1
                     until ws-prop-name(ls-prop-id) = spaces


               if ws-prop-name(ls-prop-id) =
                  lk-ctf-property-name
                   (1:cblte-tnpc-namelen of lk-ctf-notif-param-property)

                   move cblte-tnpc-valint of lk-ctf-notif-param-property
                        to ls-prop-value

                   perform apply-property-value
                   exit perform
               end-if
           end-perform
           .           

Now that the property that changed and its new value is known, the bitmask can be updated accordingly. (There is a special case for the ALL property, which sets the whole bitmask to high values.)

           apply-property-value section.
           if ls-prop-id = PROPID-ALL
               if ls-prop-value not = 0
                  compute ctf-trace-flags = ctf-trace-flags b-or
                                            h'FFFFFFFF'
               else
                  move 0 to ctf-trace-flags
               end-if
           else
               compute ls-work-var = 2 ** (ls-prop-id - 2)

               if ls-prop-value not = 0
                   compute ctf-trace-flags = ctf-trace-flags b-or
                                             ls-work-var
               else
                   compute ctf-trace-flags = ctf-trace-flags b-and b-not
                                             ls-work-var
               end-if
           end-if
           .

An 'event level' is used to denote the importance of an event. Setting the 'tracer level' (in MFTRACE_CONFIG) determines the minimum importance an event must have for it to be traced. The permissible tracer levels are INFO, WARNING, ERROR, and FATAL.

If dynamic CTF configuration is not required, simply get the tracer properties at the start of the program using CBL_CTF_TRACER_LEVEL_GET and CBL_CTF_COMP_PROPERTY_GET; for example:

           move 78-CTF-FLAG-PROP-INT-VALUE to ls-api-flags

           move 1 to ls-prop-id

           call "CBL_CTF_COMP_PROPERTY_GET" using
                              by value     ls-api-flags
                              by reference ctf-tracer-handle
                              by reference ws-prop-name(ls-prop-id)
                              by value     0
                              by reference ls-prop-value                             

and

           move 0 to ls-api-flags

           call "CBL_CTF_TRACER_LEVEL_GET" using
                                       by value     ls-api-flags
                                       by reference ctf-tracer-handle
                                       by reference ctf-trace-level

At this point, trace events can be produced. A renderer event can be created, which should only be traced if the RENDERER or ALL properties are set. The event should also only appear if the tracer level is INFO or lower (that is, this particular trace event is not very important - it just supplies general information about what the program is doing). The following constants can be created to represent event IDs.

           78  EVENT-RENDERER-DRAW      value 0
           78  EVENT-GLYPH-SHAPE        value 1
           78  EVENT-EVENT-HANDLED      value 2

With these constants, the bitmask can be queried to see if the RENDERER property is set, and if its current tracer level is INFO or lower. If both queries are true, the event can be created and sent to the tracer. (The following example makes use of the ctf-event-data structure listed at the start of this topic.)

           if (TRACE-FLAGS-RENDERER b-and ctf-trace-flags) not = 0
           and 78-CTF-FLAG-LEVEL-INFO >= ctf-trace-level
               move EVENT-RENDERER-DRAW to ls-trace-event
               move 78-CTF-FLAG-LEVEL-INFO to ls-trace-level
               perform trace-event
           end-if           

When creating the event, the event name and level is stored in the ctf-trace-event structure. Using an evaluate statement, specific functions can be called, for example, to load extra data into the event, such as the value stored in a variable. In the following example, the GLYPH-SHAPE event outputs the text to be processed. The RENDERER-DRAW event doesn't require any extra data, so there is no helper function for that: simply leave ls-trace-data-count at its default value of 0.

           trace-event section.
           move 78-TRACE-EVENT-FLAGS-NONE to
                cblte-trcevt-flags of ctf-trace-event
           move ls-trace-event to
                cblte-trcevt-event-id of ctf-trace-event
           move ls-trace-level to
                cblte-trcevt-level of ctf-trace-event

           move 0 to ls-trace-data-count

           evaluate ls-trace-event
               
               when EVENT-GLYPH-SHAPE
                   perform trace-event-glyph-shape

               when EVENT-RENDERER-DRAW
               ...
                   continue
           end-evaluate

           move ls-trace-data-count to
                cblte-trcevt-data-count of ctf-trace-event

           call "CBL_CTF_TRACE" using by value     0
                                      by reference ctf-tracer-handle
                                      by reference ctf-trace-event

           end-if
           .           

For trace events that include some data, a helper function can be called to set up the variables. The ctf-event-data structure mentioned earlier can store up to MAX-TRACE-DATA values; in this example, that means that each event can have up to five extra data items. The tracer needs to know what kind of data is being provided (ctf-trace-event-types) and how large it is (ctf-trace-event-lens).

           03  ls-trace-data-desc      pic x(40).
           
           
           trace-event-glyph-shape section.
           
           move "Blah blah blah" to ls-trace-data-desc

           set ctf-trace-event-ptrs(1) to 
               address of ls-trace-data-desc

           move 14 to ctf-trace-event-lens(1)

           move 78-TRACE-EVENT-TYPE-TEXT to
                ctf-trace-event-types(1)

           add 1 to ls-trace-data-count
           .           

Now that the event has been set up so that the trace-event-glyph-shape function is returned, a call to CBL_CTF_TRACE sends the event to the tracer for the component. The tracer sends the data to any emitters that are enabled, which in turn outputs the trace events to a file on disk.