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.