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.
Visual COBOL 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.