Question Anna Golitsyna · Aug 7

Is there a simple, perhaps built in, way of recording sequence of function calls?

For example, you start debugging the AAA function, which calls the BBB function, then goes to the CCC^ABC function in a different routine and ends up while executing XXX^XYZ. No other routines or functions are visited in this example. What I would like is to get the record of AAA -> BBB -> CCC^ABC -> XXX^XYZ. No marking each function with recording code should be involved: too time consuming, too many functions in real code.

UPD: Iris has built in %SYS.Trace for this purpose but this class is apparently absent in Cache 2017. Did not find any substitutes yet.

Product version: Caché 2017.1

Comments

DC AI Bot · Aug 7 Bot

To record function calls dynamically without modifying all functions in code, you can use tools like %SYS.MONLBL, the TRACE utility, or external profiling tools. Here are some options:

  1. %SYS.MONLBL: This built-in utility is designed primarily for performance tracing. It can monitor all routines called in a process, including transitions between functions and routines. To use it:

    • Start your application in one terminal session.
    • Use another session to run %SYS.MONLBL, specifying the process ID of your application session.
    • Perform the operations you want to trace, then review the generated report in %SYS.MONLBL. Note that this might not fully work with deployed code and focuses on performance rather than debugging details [1].
  2. TRACE Utility: This tool can monitor the execution of scripts, including function calls. Refer to the InterSystems documentation or communities for more detailed setups of TRACE for function monitoring [1].

  3. Stack Inspection:

    • Use routines to save or display stack information dynamically, such as $Stack, which can reveal the execution path.
    • Stack information can be saved via a method like:
      ClassMethod StackToDisplay()
      {
        For loop = 0:1:$Stack(-1) {
            Write !, "Context level:", loop, ?25, "Context type: ", $Stack(loop)
            Write !, ?5, "Current place: ", $Stack(loop, "PLACE")
            Write !, ?5, "Current source: ", $Stack(loop, "MCODE")
            Write !
        }
      }
      

    This approach provides lightweight function call tracking without constantly modifying every function [2].

These methods allow dynamic call recording without hardcoding manual markers in each function.

Sources:

0
Anna Golitsyna  Aug 7 to Ashok Kumar T

As far as I understand, this is to examine the stack in a predetermined piece of code. I have used it in this capacity as well, but now I'd like a recording without explicitly calling stack in each involved function.

0
Robert Cemper  Aug 7 to Anna Golitsyna

If I understand it right, you do not just expect a tracking down to some point, 
but also a history of all up and down you did from a certain starting point.
Similar to a TCP traceroute 

0
Robert Cemper  Aug 8 to Anna Golitsyna

So  %SYS>d ^TRACE might be a useful start

This utility traces the execution of a process.  Traced processes write events
to a trace file with details about the routine line where the event occurred
and, if applicable, the global reference.  The events available for tracing
correspond to the metrics availabe in performance monitoring tools (PERFMON,
%SYS.MONLBL etc).  Raw data is written to iristrace_<pid>.txt in the
configured directory, and is then parsed and displayed by this utility.
 
 
Trace file directory:   not set
Trace file size limit: unlimited
Trace events selected:  RtnLoad, GloRef, BlockWait, NCacheMiss, DirBlkRd
   UpntBlkRd, BpntBlkRd, DataBlkRd, BdataBlkRd, MapBlkRd, OthBlkRd
 
1) Configure Trace
2) Trace Process (Foreground)
3) Start Background Trace
4) Stop Background Trace
5) Display Trace Files
6) Delete All Trace Files
 
Option?
0
Stuart Salzer · Aug 7

All the heavy lifting can be done with the $STACK() function. Here is a start:

ZTEST	; SRS 2025-08-07
	DO A
	QUIT
A	DO B
	QUIT
B	DO C
	QUIT
C	DO D
	QUIT
D	DO STACK
	QUIT
STACK	NEW
	FOR i=0:1:$STACK(-1)-1 {
	  SET s=$STACK(i,"PLACE")
	  SET retloc=$PIECE(s," ")
	  TRY { SET code=$TEXT(@retloc) } CATCH { SET code="n/a" }
	  WRITE !,retloc," (",$PIECE(s," ",2),") -> ",code
	}
	QUIT
0
Anna Golitsyna  Aug 8 to Stuart Salzer

Thanks, Stuart. Correct me if I am wrong but I would need to install such a function in multiple places and that's exactly what I am trying to avoid. I did use $STACK in less involved situations before and it is very useful in its own right.

0
Ali Nasser  Aug 8 to Anna Golitsyna

You only need to install this in one place, in the last method or routine call in your call stack. If your stack is like this AAA -> BBB -> CCC^ABC -> XXX^XYZ, then you need to put Stuart's code in a method called by XXX^XYZ

For example, assume you have these classmethods

ClassMethod GrandParent()
{
    do..Parent()
}
ClassMethod Parent()
{
    do..Child()
}
ClassMethod Child()
{
    do..Inspector()
}
ClassMethod Inspector()
{
    FOR i=0:1:$STACK(-1)-1 {
      SETs=$STACK(i,"PLACE")
      SET retloc=$PIECE(s," ")
      TRY { SET code=$TEXT(@retloc) } CATCH { SET code="n/a" }
      WRITE !,retloc," (",$PIECE(s," ",2),") -> ",code
    }
}

Then if you call GrandParent(), then it will print this:

@ (+1) -> n/a
GrandParent+1^Sample.Test.1 (+1) ->     do..Parent() }
Parent+1^Sample.Test.1 (+1) ->     do..Child() }
Child+1^Sample.Test.1 (+1) ->     do..Inspector() }
0

If you don't know what is getting execute exactly you can use MONLBL (ref : MOMLBL )
Its for performance diagnostics but will give you a statistic of routine code being called etc.

0
Anna Golitsyna  Aug 8 to Timo Lindenschmid

Thanks, Timo. I used this profiling utility before and it's very good for its purposes but now I am interested in sequence of function calls and MONLBL does not provide that.

0
Robert Cemper · Aug 8

Not sure for Caché 2017 
BUT
Cache for UNIX (Red Hat Enterprise Linux for x86-64) 2018.1.5 (Build 659U) Mon Mar 22 2021 07:12:43 EDT
Has ^TRACE in %SYS
with enough $v(..) and $ZU(..) to look promising 

W $ZU(5)%SYS%SYS>D ^TRACE
TRACE utility for Cache. You can signal a process to write a record of allprocedure, function, or subroutine calls to a file. Then use this utility to interpret and display the trace. The trace file is named: CacheTrace_'pid'.txtUse $$DIR^TRACE(dir) to set the directory where the file(s) should be written(process must have create/write access to the directory). $$DIR^TRACE() returnsthe current directory for trace files.Use $$ON^TRACE(pid) to start the trace and $$OFF^TRACE(pid) to end the recording. Then just run ^TRACE to see the results.Process ID for trace file: 
0
Anna Golitsyna  Aug 11 to Robert Cemper

Thanks, Robert. It is available in 2017, but I was not able to set it up yet. Something is still wrong the way I run it, please see below. I start debugging, stop at a breakpoint and input the $J value.
%SYS>D DIR^TRACE()
 
%SYS>D ^TRACE
 
TRACE utility for Cache. You can signal a process to write a record of all
procedure, function, or subroutine calls to a file. Then use this utility to
interpret and display the trace. The trace file is named: CacheTrace_'pid'.txt
 
Use $$DIR^TRACE(dir) to set the directory where the file(s) should be written
(process must have create/write access to the directory). $$DIR^TRACE() returns
the current directory for trace files.
 
Use $$ON^TRACE(pid) to start the trace and $$OFF^TRACE(pid) to end the
recording. Then just run ^TRACE to see the results.
 
Process ID for trace file: 5352
Output to: <0> TestAG.txt
 
Processing file ...
 
-1,Unable to open file CacheTrace_5352.txt
%SYS>

0
Robert Cemper  Aug 11 to Anna Golitsyna

1.  Use $$DIR^TRACE(dir)  you haven't set a suitable directory
2.  Use $$ON^TRACE(5352)  ; the job you want to trace ==> open file
3:  USE $$OFF ^TRACE(5352) to stop trace ==> close file
4.Now ^TRACE should show the file

I traced my own studio session:

START:1810291850,0,%SYS.BINDSRV,2,1
STACK: [ 0] D - SuperServer+112^%SYS.SERVER +1
STACK: [ 1] D - SuperConnect+34^%SYS.DBSRV +1
STACK: [ 2] D - Dispatch+14^%SYS.BINDSRV +1
DO:1812736542,0,%SYS.BINDSRV,39
ARG:[ 0] INT: 0
ARG:[ 1] INT: 1
ARG:[ 2] INT: 1
DO:1812736624,0,%SYS.BINDSRV,625
ARG:[ 0] INT: 0
ARG:[ 1] INT: 0
DO:1812736631,0,%SYS.BINDSRV,258
ARG:[ 0] INT: 0
ARG:[ 1] INT: 0
QUIT:1812736638,291
DO:1812736642,0,%SYS.BINDSRV,627
0
Anna Golitsyna  Aug 12 to Robert Cemper

It is very verbose but yes, I got it working now too. Thanks, Robert! It is interesting that the resultant file lists only routine names but not the labels. Instead of a label name, it lists the label line number.

0