04 - Introduction To Tracing Classic Functions
04 - Introduction To Tracing Classic Functions
Contents
Whilst investigating some issues in X3, it may be advantageous to have insight into what code is being executed
during the running of a Classic Function – some typical instances are:
Sage X3 provides tracing mechanisms which can record the flow of execution - this can assist investigations into
types 1 and 2, and also function timing and SQL traces which can assist with type 3 issues.
Engine Traces
When starting an Engine Trace, you can specify a Flag value which denotes what type of event you wish to trace. The
following table shows what sorts of events you can ask for:
Please note that these values can be added together in order to trace more than one type of event.
Specifying flag-value 4 will only record events of type 4 – i.e. SQL Statements.
When looking at an Engine Trace file, you will see a “Channel” at the start of each line – this shows which sort of
event it relates to.
Global Traces
Engine Traces can be enabled for all sessions via the Administration > Usage > Logs > Engine Trace function
Provide a Flag-value and Volume for the tra-file.
This will enable Tracing for all sessions launched after it is submitted – when a Function is launched, it envokes an
adonix process and that is the numeric part of the Trace file’s name. This means, each browser session has its own
Trace File.
This can be done using Openlog/Closelog mechanism where tracing is initiated by the User from within a Classic
Function.
Openlog and Closelog are executed in the Help > Diagnosis > Calculator which is available in all Classic Functions.
This mechanism allows a User to specify the same Flag Values as are available in the Global Tracing, but it is the
responsibility of the User to start and stop the Tracing, and it is only for the current Session.
Once the Tracing has been started, it will continue and will record the events for all the Classic Functions used on a
Tab with the same adonix process until it is closed, or until a Closelog() is issued.
Note that the Tracing only applies to the Browser Tab in which it is initiated – if a User starts additional secondary
Tabs, then the Trace will not record any activity in any Classic Functions used in the Secondary Tabs.
Any activity on other Browsers, on the same or different Client Machines, will not be recorded.
This level of tracing could be used when you’re trying to trace a Function *before* the screens are loaded – you’re
not able to enable Tracing until the screens are loaded.
The procedure would be
This is the same as Session-level Tracing except that the Openlog and Closelog calls are done solely within a Classic
Function – i.e. the Closelog is done before the User exits the Function and no other Functions are launched during
the period of Tracing.
This table must be populated by the Script associated with the Report (as defined in Development > Script Dictionary
> Reports) – in this case RPTHDU.
RPTHDU.src has several conditions and routes to write to AREPORTM, so use Openlog to record what route the
program takes.
Using Flag 7, this shows basic SQL activity which can then be corelated with SQL Profiler and the Source-code.
Since data needs to be inserted into AREPORTM, look for Trbegin in the Trace-file (TrBegin stands for Transaction
Begin).
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(172) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(172) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(172) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(172) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(171) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(172) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(174) | | | | | | | | Link
<channel 2>@X3.TRT/RPTHDU$adx(180) | | | | | | | | Columns
<channel 2>@X3.TRT/RPTHDU$adx(182) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(183) | | | | | | | | Assign
<channel 3>@X3.TRT/RPTHDU$adx(184) | | | | | | | | Call DEBTRANS From GLOCK, tick:560661
<channel 2>@X3.TRT/GLOCK$adx(82) | | | | | | | | | Assign
<channel 2>@X3.TRT/GLOCK$adx(83) | | | | | | | | | Assign
<channel 2>@X3.TRT/GLOCK$adx(84) | | | | | | | | | Assign
<channel 2>@X3.TRT/GLOCK$adx(85) | | | | | | | | | Assign
<channel 2>@X3.TRT/GLOCK$adx(86) | | | | | | | | | End
<channel 1>@X3.TRT/RPTHDU$adx(184) | | | | | | | | End Call, tick:560661
<channel 2>@X3.TRT/RPTHDU$adx(185) | | | | | | | | Trbegin
<channel 2>@X3.TRT/RPTHDU$adx(189) | | | | | | | | For
<channel 4>Execution SQL on For clause in @X3.TRT/RPTHDU$adx at line 189, tick : 560663
<channel 4>Select HDU_.ROWID, HDU_.NUMHDU_0, HDU_.DATEVT_0, HDU_.TYP_0, HDU_.NUM_0, HDU_.FCY_0, HDU_.SAC_0,
HDU_.BPR_0, HDU_.ACCNUM_0, HDU_.DUDLIG_0, HDU_.BPRPAY_0, HDU_.UPDTICK_0, HAE_.CAT_0, HAE_.FLGREP_0, FGR_.CPY_0,
ACC_.CONSUL_0, AFF_.PRFCOD_0 From SEED.HISTODUD HDU_ JOIN SEED.GACCENTRY HAE_ ON ((HAE_.TYP_0 = HDU_.TYP_0) AND
(HAE_.NUM_0 = HDU_.NUM_0)) JOIN SEED.FACGROUP FGR_ ON ((FGR_.CPY_0 = ?) AND (FGR_.FCY_0 = HDU_.FCY_0)) LEFT OUTER
JOIN SEED.GRPSAC GSC_ ON ((GSC_.COA_0 = ?) AND (GSC_.GRU_0 = ?) AND (GSC_.SAC_0 = HDU_.SAC_0)) JOIN SEED.BPARTNER BPR_
ON ((BPR_.BPRNUM_0 = HDU_.BPR_0)) LEFT OUTER JOIN SEED.ACCES ACC_ ON ((ACC_.USR_0 = ?) AND (ACC_.CODACC_0 =
BPR_.ACS_0)) LEFT OUTER JOIN SEED.AFCTFCY AFF_ ON ((AFF_.FCY_0 = HDU_.FCY_0) AND (AFF_.PRFCOD_0 = ?) AND (AFF_.FNC_0 =
?)) Where FGR_.CPY_0 = ? And HDU_.DATEVT_0 <= ? And HAE_.FLGREP_0 <> ? And ACC_.CONSUL_0 = ? And AFF_.PRFCOD_0 <> ? And
((HAE_.CAT_0 = ?) Or (HAE_.CAT_0 = ?)) Order by HDU_.ACCNUM_0,HDU_.DUDLIG_0,HDU_.NUMHDU_0
<channel 4>Query end, tick : 560889
<channel 2>@X3.TRT/RPTHDU$adx(190) | | | | | | | | Raz
<channel 2>@X3.TRT/RPTHDU$adx(191) | | | | | | | | Filter
<channel 2>@X3.TRT/RPTHDU$adx(192) | | | | | | | | For
<channel 4>Execution SQL on For clause in @X3.TRT/RPTHDU$adx at line 192, tick : 560889
<channel 4>Select HD2_.ROWID, HD2_.* From SEED.HISTODUD HD2_ Where HD2_.ACCNUM_0 = ? And HD2_.DUDLIG_0 = ? And
((HD2_.DATEVT_0 != ?) Or (HD2_.FLGCLE_0 = ?)) Order by HD2_.ACCNUM_0,HD2_.DUDLIG_0,HD2_.NUMHDU_0
<channel 4>Query end, tick : 560905
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(244) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(244) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(244) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 3>@X3.TRT/RPTHDU$adx(247) | | | | | | | | Gosub TR1 , tick:560905
<channel 2>@X3.TRT/RPTHDU$adx(273) | | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(280) | | | | | | | | | Return
<channel 1>@X3.TRT/RPTHDU$adx(247) | | | | | | | | End Gosub, tick:560905
<channel 2>@X3.TRT/RPTHDU$adx(248) | | | | | | | | Filter
<channel 2>@X3.TRT/RPTHDU$adx(249) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(190) | | | | | | | | Raz
<channel 2>@X3.TRT/RPTHDU$adx(191) | | | | | | | | Filter
<channel 2>@X3.TRT/RPTHDU$adx(192) | | | | | | | | For
<channel 4>Execution SQL on For clause in @X3.TRT/RPTHDU$adx at line 192, tick : 560905
<channel 4>Select HD2_.ROWID, HD2_.* From SEED.HISTODUD HD2_ Where HD2_.ACCNUM_0 = ? And HD2_.DUDLIG_0 = ? And
((HD2_.DATEVT_0 != ?) Or (HD2_.FLGCLE_0 = ?)) Order by HD2_.ACCNUM_0,HD2_.DUDLIG_0,HD2_.NUMHDU_0
<channel 4>Query end, tick : 560905
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(244) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(197) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(200) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(201) | | | | | | | | Raz
<channel 2>@X3.TRT/RPTHDU$adx(202) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(203) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(204) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(205) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(206) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(207) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(208) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(209) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(210) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(211) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 2>@X3.TRT/RPTHDU$adx(195) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(215) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(233) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(234) | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(235) | | | | | | | | Assign
<channel 2>@X3.TRT/RPTHDU$adx(246) | | | | | | | | Next
<channel 3>@X3.TRT/RPTHDU$adx(247) | | | | | | | | Gosub TR1 , tick:560907
<channel 2>@X3.TRT/RPTHDU$adx(273) | | | | | | | | | If
<channel 2>@X3.TRT/RPTHDU$adx(274) | | | | | | | | | Write
<channel 2>@X3.TRT/ATRIFIL$adx(16) | | | | | | | | | | Char
Judging by the output, a Transaction is started at code line 185 and the actual action is taken in subprog TR1 –
potentially including or excluding data as appropriate.
If you have access to the code, this can be examined to see what conditions cause a record to be rejected from the
Report – if you don’t have access to the code, then it could be sent to Sage’s X3 Customer Services to provide more
information.
Please remember to do the Closelog() to turn the logging off after the task has been completed.
Flamegraph
Flamegraph is a mechanism for exploring the time/process consumption of a classic Function, showing the order in
which the Sub Programs are called, and also showing any nested Calls.
Flamegraphs are activated within the Administration > Usage > Session Management > Classic Client sessions
option.
Flamegraph also generates an x3diary tra-file in the folder’s TRA sub-directory, including the SQL queries that are
used
Please note that the tracing is only active for Classic Functions launched on the Browser which Activated the logging
– if three Classic Functions are opened on secondary Tabs of the Browser, then a Flamegraph-trace will be generated
for each one of those, but any Classic Functions on other Browser will not have Flamegraph-traces generated.
Flamegraph Traces will be generated for all Classic Functions on the Browser session until logging is Deactivated.
The Flamegraph is only available while the Classic Function is open – although the tra-file continues to exist after it is
closed - any attempt to access/refresh the Flamegraph will fail after the function is exited. Therefore, it is essential to
save a copy of the Flamegraph before closing the Classic Function in question – this can be done using the Browser’s
builtin “Save as…” feature which is made available on right-clicking within the body of the Flamegraph.
This Trace generates an x3diary tra-file for the information analysed in the Flameraph – it can be found in the
Folder’s TRA sub-directory – in this example, SEED\TRA\x3diary_usr1_8772.tra.
The consumption can be seen in graphical form by clicking the CPU Graph icon.
The Graph also shows any Database I/O in the context of the Subprog.
Since Flamegraph reads the x3diary tra-file during the generation of the Graph, you can see the Flamegraph change
during the lifecycle of the function being examined – the graph can be updated by refreshing the browser-tab with
the graph in it.
Thus, several snapshots of a graph can be taken and even saved in the svg format for inspection at a later date.
Snapshot 1:
Snapshot 2
Details of the time spent in each $-label and Subprog can be found by hovering over the appropriate bar, and this
will correspond to the section of tra-file recording progress through the code.
As mentioned, the Flamegraph is based on the x3diary tra-file. This means, you can pin-point a section of the
Flamegraph and then examine the corresponding section of the tra-file to see more detail.
For example, the section Call CONNECT From GESUSER (MENU:207) corresponds to the following lines from the
x3diary tra-file
The following is a manually generated and formatted representation of calls and sub-calls in a section of the
X3diary_admin_11216_0.tra file
You can see from this, that the pipe-symbol indicates the call-level for each Call/Gosub so these line can be
interpreted in the following way
Line Origin Level 1 Level 2 Level 3
Knowing this, you could import the x3diary file into Excel with pipe-delimiter and you’d get a spreadsheet with lines
like
As the Flamegraph is an SVG file, you can examine the page in a Browser and search for elements to see where they
appear in the flow by using the Browser’s built-in F12 Developer Tools.
When you search for a string in the body of the Graph, it will highlight it in the actual Graph (it pops-up a g.func_g
over it).
You can also do a View Source to see all the information about the calls in written form.
Timing Trace
X3 can record the duration of calls in a Classic Functions by selecting Help > Diagnostics > Activation timing on the
right-hand side of the Function
Once the steps under investigation have been carried-out, then select Reading timing and that will display a
“normal” tra-file with F-prefix which in the folder’s TRA sub-directory with a summary of the timings – the details are
in the specified file-name, defaulting to <user-name>.tra in runtime\tmp folder – for example,
…\runtime\tmp\ADMIN.tra – not the folder’s TRA sub-directory.
This feature could even be used to trace the running of a Report – just make sure the Log-file name is
unique/meaningful so it won’t overwrite or be overwritten by other traces.
Sage Support can carry-out additional analysis on the two tra-files generated by this Timing Trace feature – send
them in and ask for them to be run through X3Analyzer.
SQL trace
As with Timing Traces, these can be activated/deactivated via Help > Diagnosis > Activate SQL Server trace
The SQL Trace is output in the form of a SQL Profiler trc file of the format SQLTRC67-<timestamp>.trc in the
…\Folders\Database\trace directory – for example, d:\Sage\X3\Database\trace\SQLTRC67-2021-02-26T16-35-21-
667.trc.
After deactivating the trace, a tra-file is generated in the folder’s TRA sub-directory based on the information in the
trc-file.
The .trc file can be loaded into SQL Profiler where it’s easier to examine – you can search for particular tables and it
can also be saved to a Database Table and searched and filtered in SQL as well.
By default, “Standard” Recovery of results is used - the information recorded is basically the Execution Plans for
selects, inserts and updates performed during the trace-time – this will be familiar to you if you have used SQL
Profiler during investigations in the past. Unfortunately, the Execution Plans don’t include the data – they are
parameterised as seen in the following example:
Execution Tree
--------------
Table Insert(OBJECT:([x3erpv11].[X3].[AFCTCUR]), OBJECT:([x3erpv11].[X3].[AFCTCUR].[AFCTCUR_ROWID]),
OBJECT:([x3erpv11].[X3].[AFCTCUR].[AFCTCUR_AFU0]), SET:([x3erpv11].[X3].[AFCTCUR].[UPDTICK_0] = RaiseIfNullInsert([@P1]),[x3erpv11].[X3].[AFCTCUR].[F
CT_0] = RaiseIfNullInsert([Expr1004]),[x3erpv11].[X3].[AFCTCUR].[UID_0] = RaiseIfNullInsert([Expr1005]),[x3erpv11].[X3].[AFCTCUR].[USR_0] =
RaiseIfNullInsert([Expr1006]),[x3erpv11].[X3].[AFCTCUR].[LOGIN_0] = RaiseIfNullInsert([Expr1007]),[x3erpv11].[
X3].[AFCTCUR].[MODULE_0] = RaiseIfNullInsert([@P6]),[x3erpv11].[X3].[AFCTCUR].[CREDAT_0] =
RaiseIfNullInsert([Expr1008]),[x3erpv11].[X3].[AFCTCUR].[CREUSR_0] = RaiseIfNullInsert([Expr1009]),[x3erpv11].[X3].[AFCTCUR].[UPDUSR_0] =
RaiseIfNullInsert([Ex
pr1010]),[x3erpv11].[X3].[AFCTCUR].[CREDATTIM_0] = RaiseIfNullInsert([Expr1011]),[x3erpv11].[X3].[AFCTCUR].[UPDDATTIM_0] =
RaiseIfNullInsert([Expr1012]),[x3erpv11].[X3].[AFCTCUR].[AUUID_0] = RaiseIfNullInsert([@P12]),[x3erpv11].[X3].[AFCTCUR].[ROWID]
= [Expr1003]))
!--Compute Scalar(DEFINE:([Expr1004]=CONVERT_IMPLICIT(nvarchar(80),[@P2],0), [Expr1005]=CONVERT_IMPLICIT(nvarchar(12),[@P3],0),
[Expr1006]=CONVERT_IMPLICIT(nvarchar(5),[@P4],0), [Expr1007]=CONVERT_IMPLICIT(nvarchar(20),[@P5],0), [Expr1008]=CONVERT_
IMPLICIT(datetime,[@P7],0), [Expr1009]=CONVERT_IMPLICIT(nvarchar(5),[@P8],0), [Expr1010]=CONVERT_IMPLICIT(nvarchar(5),[@P9],0),
[Expr1011]=CONVERT_IMPLICIT(datetime,[@P10],0), [Expr1012]=CONVERT_IMPLICIT(datetime,[@P11],0)))
!--Compute Scalar(DEFINE:([Expr1003]=getidentity((98151445),(5),NULL)))
!--Constant Scan
If you select ”Tuning” for Recovery of results, you can get a bit more information – you get to see cursor-prepare
statements such as
declare @p1 int
set @p1=1073742628
declare @p2 int
set @p2=180161391
declare @p5 int
set @p5=16
declare @p6 int
set @p6=1
declare @p7 int
set @p7=0
exec sp_cursorprepexec @p1 output,@p2 output,N'@P1 nvarchar(16),@P2 nvarchar(21),@P3 smallint',N'Select TPT_.ROWID, TPT_.*
From SEED.TABPAYTERM TPT_
Where ( TPT_.PTE_0 = @P1 And TPT_.LEG_0 = @P2 And TPT_.PTELIN_0 = @P3 )
Order by TPT_.PTE_0,TPT_.LEG_0,TPT_.PTELIN_0
Option (FAST 1)',@p5 output,@p6 output,@p7 output,N'CH30NET',N'BRI',1
select @p1, @p2, @p5, @p6, @p7
And then you can search for subsequent occurrences of that cursor:
Finally, by selecting Recovery of results “Advanced”, you can stipulate event-types to trace – these are selected from
the “Advanced event parameters” section. Unfortunately, the event-types which are pre-ticked are hard-coded to
Standard and/or Tuning runs – in order to record any events during an Advanced run, they must be marked as “Yes”
in the “To take” column of the Advanced event parameters screen after clicking on the Advanced button in the
Recovery of results section.
For example F138641.tra/SQLTRC59-2021-03-01T17-25-07-497.trc – these show entries for multiple events such as
RPC:Starting and RPC_Completed events which include the parameter-values to the SQL statement, and Execution
Plan events which show the SQL and associated Execution Plans
[EventClass]=11 RPC:Starting
[StartTime]=2021-03-01 17:34:55
exec sp_cursorexecute 1073742362,@p2 output,@p3 output,@p4 output,@p5 output,N'ADMIN',N'AUTOSEL'
select @p2, @p3, @p4, @p5
[EventClass]=68 Execution Plan
Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000]))
!--Index Seek(OBJECT:([x3erpv11].[SEED].[ADOVALAUS].[ADOVALAUS_ADU1] AS [ADU_]),
SEEK:([ADU_].[PARAM_0]=[@P2] AND [ADU_].[CODUSR_0]=[@P1]) ORDERED FORWARD)
!--RID Lookup(OBJECT:([x3erpv11].[SEED].[ADOVALAUS] AS [ADU_]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP
ORDERED FORWARD)
[EventClass]=10 RPC:Completed
[StartTime]=2021-03-01 17:34:55
[EndTime]=2021-03-01 17:34:55
[Duration]=14263
exec sp_cursorexecute 1073742362,@p2 output,@p3 output,@p4 output,@p5 output,N'ADMIN',N'AUTOSEL'
select @p2, @p3, @p4, @p5
The trace-files will record multiple RPC:Starting, Execution Plan and RPC:Completed events for each execution of a
SQL Cursor – for example, Cursor 1073742170
[EventClass]=11 RPC:Starting
[StartTime]=2021-03-01 17:34:55
exec sp_cursorexecute 1073742170,@p2 output,@p3 output,@p4 output,@p5 output,N'ZA002'
[EventClass]=68 Execution Plan
[StartTime]=2021-03-01 17:34:55
Nested Loops(Inner Join, OUTER REFERENCES:([Bmk1000]))
!--Index Seek(OBJECT:([x3erpv11].[SEED].[BPCUSTOMER].[BPCUSTOMER_BPC0] AS [BPC_]),
SEEK:([BPC_].[BPCNUM_0]=[@P1]) ORDERED FORWARD)
!--RID Lookup(OBJECT:([x3erpv11].[SEED].[BPCUSTOMER] AS [BPC_]), SEEK:([Bmk1000]=[Bmk1000]) LOOKUP
ORDERED FORWARD)
[EventClass]=10 RPC:Completed
[StartTime]=2021-03-01 17:34:55
[EndTime]=2021-03-01 17:34:55
[Duration]=8514
[CPU]=15
[Reads]=5
[Writes]=0
exec sp_cursorexecute 1073742170,@p2 output,@p3 output,@p4 output,@p5 output,N'ZA002'
[EventClass]=10 RPC:Completed
[StartTime]=2021-03-01 17:34:57
[EndTime]=2021-03-01 17:34:57
[Duration]=367
[CPU]=0
[Reads]=3
[Writes]=0
exec sp_cursorexecute 1073742170,@p2 output,@p3 output,@p4 output,@p5 output,N'ZA011'
[EventClass]=10 RPC:Completed
[StartTime]=2021-03-01 17:34:57
[EndTime]=2021-03-01 17:34:57
[Duration]=407
[CPU]=0
[Reads]=3
[Writes]=0
exec sp_cursorexecute 1073742170,@p2 output,@p3 output,@p4 output,@p5 output,N'ZA002'
These three Events will record Inserts, Updates and Deletes as well:
[EventClass]=10 RPC:Completed
[StartTime]=2021-03-01 17:35:04
[EndTime]=2021-03-01 17:35:04
[Duration]=315
[CPU]=0
[Reads]=5
[Writes]=0
exec sp_prepexec @p1 output,N'@P1 int,@P2 nvarchar(81),@P3 nvarchar(13),@P4 nvarchar(6),@P5
nvarchar(21),@P6 tinyint,@P7 datetime2,@P8 nvarchar(6),@P9 nvarchar(6),@P10 datetime2,@P11
datetime2,@P12 binary(16)',N'INSERT INTO X3.AFCTCUR (UPDTICK_0, FCT
_0, UID_0, USR_0, LOGIN_0, MODULE_0, CREDAT_0, CREUSR_0, UPDUSR_0, CREDATTIM_0, UPDDATTIM_0,
AUUID_0) VALUES
(@P1,@P2,@P3,@P4,@P5,@P6,@P7,@P8,@P9,@P10,@P11,@P12)',1,N'SAIWRKPLN',N'1231',N'ADMIN',N'admin
',1,'1753-01-01 00:00:00',N'ADMIN',N'ADMIN','20
21-03-01 16:35:04','2021-03-01 16:35:04',0x6FB4F3C07CC83B4F9C79AFC74568B90F
Conclusions
• Engine Trace
• Use this to show what code is being executed.
• Use it in conjunction with SQL Profiler/Extended Events.
• Flamegraph
• Use this to see the code-flow “at a glance”.
• Timing Trace
• Use this to see what blocks of code are taking the time and should be used in conjunction with SQL
Profiler to see if particular SQL statements have long durations.
• SQL Trace
• Use this instead of SQL Profiler/Extended Events if they are not available.
• This only records SQL statements – you cannot see which piece of code is executing the statements – so
it is of interest when you are investigating exactly which data is involved with the Function.
Appendix 1: Another example of an Engine Trace
Engine Traces
• Engine Traces are good for investigating what code a function runs through, along with the basic SQL
statements.
• It would be unusual to use Global-level tracing as all sessions are recorded and it would be difficult to
manage and decide which tra-file to look at.
• Best to use Function-level Tracing if possible.
• Using Flag value 7 means you can see both the code and SQL statements – if you run SQL Profiler at the
same time, then you can figure-out the parameter-values for the SQL in the tra-file
• Use SQL Profiler in conjunction with Engine Trace to try to discover the full SQL-statements and put them in
context
• Enable Tracing for the shortest time possible.
Then search for “ADU_” which is the alias for the SEED.ADOVALAUS table – this may show you the value of
PARAM_0 in the above statement.
When looking for writes/re-writes in a x3diary tra-file search for Write/Rewrite. Unfortunately, the tra-file doesn’t
record which table is being updated, so you may have to refer to the code related to the Write
Engine Traces
Examples
There are also KBs on how to initiate an Engine Trace with X3 Code:
https://support.na.sage.com/selfservice/viewdocument.do?noCount=true&externalId=83553
How to enable Runtime Logging to create a debug trace in V7? - Sage X3 Support - Sage X3 - Sage City Community
Sage X3 Support North America - Running an Engine Trace - YouTube
https://support.na.sage.com/selfservice/viewdocument.do?noCount=true&externalId=97757
Online Help
https://online-help.sageerpx3.com/erp/12/ticket/how-to-generate-a-sql-server-trace/
https://online-help.sageerpx3.com/erp/12/staticpost/openlog/
Timing Traces
Examples
Online Help
https://online-help.sageerpx3.com/erp/12/ticket/how-to-activate-the-timing-trace-for-a-progsubprog/
SQL Tracing
Online Help
https://online-help.sageerpx3.com/erp/12/ticket/how-to-generate-a-sql-server-trace/
Flamegraph
https://support.na.sage.com/selfservice/viewdocument.do?noCount=true&externalId=79383
https://online-help.sageerpx3.com/erp/12/staticpost/flamegraph-presentation/
Please note that SQL Profiler is now deprecated and the advice is to use Extended Events in its place
https://docs.microsoft.com/en-us/sql/tools/sql-server-profiler/sql-server-profiler?view=sql-server-ver15 : SQL
Server Profiler
https://docs.microsoft.com/en-us/sql/relational-databases/extended-events/extended-events?view=sql-server-
ver15 : Extended Events Overview