Wednesday, November 7, 2018

Oracle row cache objects Event: 10222, Dtrace Scripts (I)

You don’t understand anything until you learn it more than one way.
Marvin Minsky


(I)-Tracing Methods       (II)-Object Type(Part-1)      (III)-Object Type(Part-2)       (IV)-Sql Executions (IV)      (V)-Contentions and Scalability


This small Oracle Row Cache Cookbook is made of a series of 5 Blogs:
Oracle row cache objects Event: 10222, Dtrace Script (I)
Row Cache Objects, Row Cache Latch on Object Type: Plsql vs Java Call (Part-1) (II)
Row Cache Objects, Row Cache Latch on Object Type: Plsql vs Java Call (Part-2) (III)
Row Cache and Sql Executions (IV)
Latch: row cache objects Contentions and Scalability (V)


This Blog will present two tracing methods of row cache objects Gets, which provide more deep insights than ordinary Oracle dynamic performance views (v$rowcache, v$latch, etc.).

At first we explore Event: 10222 to trace row cache objects Gets, and then create a PL/SQL script to import and analyse the generated trace files.

As a second approach, three Dtrace scripts are composed and tested:
   -. rco_dtrace_cache: trace each Row Cache Object Get with its arguments.
   -. rco_dtrace_latch: printout each Row Cache Latch Get call stack.
   -. rco_dtrace_std:   draw a State Transition Diagram for Object and Latch Gets.
The difference of three methods are their precision of perceiving granularity.
   (1). dynamic performance views: statement
   (2). 10222 trace:               row cache
   (3). dtrace:                    latch (atomic locking mechanism)
Note: all tests are done in Oracle 12cR1 (12.1.0.2.0), and 12cR2 (12.2.0.1.0).


1. Trace Event: 10222


Perform the tracing on the same query as Blog: nls_database_parameters, dc_props, latch: row cache objects, and compare with the output of dynamic performance views in that Blog.

More details are revealed by increasing the tracing Level.


1.1. Level 4


alter session set max_dump_file_size = UNLIMITED;

alter session set tracefile_identifier = 'row_cache_10222_4';
alter session set events '10222 trace name context forever, level 4';  
select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
alter session set events '10222 trace name context off';

---------------------- Output ----------------------

kqrpre1 : found po=18f058b68 flg=2 hash=5b42c02e .
kqrpre1 : done po=18f058b68 cid=15 flg=2 hash=5b42c02e 0 eq=13feab2b0 .

kqrpre1 : found po=142020320 flg=2 hash=2155f5c0 .
kqrpre1 : done po=142020320 cid=15 flg=2 hash=2155f5c0 0 eq=13feab2b0 .

1.2. Level 255


alter session set tracefile_identifier = 'row_cache_10222_255';
alter session set events '10222 trace name context forever, level 255';  
select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
alter session set events '10222 trace name context off';

---------------------- Output ----------------------

kqrpre1 : found po=18f058b68 flg=2 hash=5b42c02e time=2235042744
kqrmpin : kqrLockAndPinPo Pin po 18f058b68 cid=15 flg=2 hash=5b42c02e time=2235042857
kqrpre1 : done po=18f058b68 cid=15 flg=2 hash=5b42c02e 0 eq=13fea3cd0 time=2235042872
kqrmupin : kqrpspr2 Unpin po 18f058b68 cid=15 flg=2 hash=5b42c02e time=2235042890

kqrpre1 : found po=142020320 flg=2 hash=2155f5c0 time=2235042919
kqrmpin : kqrLockAndPinPo Pin po 142020320 cid=15 flg=2 hash=2155f5c0 time=2235042945
kqrpre1 : done po=142020320 cid=15 flg=2 hash=2155f5c0 0 eq=13fea3cd0 time=2235043046
kqrmupin : kqrpspr2 Unpin po 142020320 cid=15 flg=2 hash=2155f5c0 time=2235043061

1.3. Level 4294967295


alter session set tracefile_identifier = 'row_cache_10222_4294967295';
alter session set events '10222 trace name context forever, level 4294967295';  
select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
alter session set events '10222 trace name context off';

---------------------- Output ----------------------

kqrpre1 : found po=18f058b68 flg=2 hash=5b42c02e time=2271264535
kqrmpin : kqrLockAndPinPo Pin po 18f058b68 cid=15 flg=2 hash=5b42c02e
time=2271264669
kqrpre1 : done po=18f058b68 cid=15 flg=2 hash=5b42c02e 0 eq=13fea57f8 SQL=select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET' time=2271264687
kqrmupin : kqrpspr2 Unpin po 18f058b68 cid=15 flg=2 hash=5b42c02e time=2271264704
kqrmupin : free po=18f058b68 flg=2
kqrfrpo: Free po 18f058b68
 KQR Parent Cache Object:
 pob = 18f058b68 flg = 00000002 cid = 15 xcb = 0 hbk = 18f05ac38
 hbk=[f84f96e8,151186580] 
 own=18f058c38[18f058c38,18f058c38] wat=18f058c48[18f058c48,18f058c48] mod=0
kqrfrpo : freed to heap po=18f058b68 time=2271264814

kqrpre1 : found po=142020320 flg=2 hash=2155f5c0 time=2271264844
kqrmpin : kqrLockAndPinPo Pin po 142020320 cid=15 flg=2 hash=2155f5c0
time=2271264871
kqrpre1 : done po=142020320 cid=15 flg=2 hash=2155f5c0 0 eq=13fea57f8 SQL=select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET' time=2271264885
kqrmupin : kqrpspr2 Unpin po 142020320 cid=15 flg=2 hash=2155f5c0 time=2271264904
kqrmupin : free po=142020320 flg=2
kqrfrpo: Free po 142020320
 KQR Parent Cache Object:
 pob = 142020320 flg = 00000002 cid = 15 xcb = 0 hbk = 1420223f0
 hbk=[1511862a0,18f077dd0] 
 own=1420203f0[1420203f0,1420203f0] wat=142020400[142020400,142020400] mod=0
kqrfrpo : freed to heap po=142020320 time=2271264992

1.4. Trace Level Discussion


Tests showed:
  Level 4 (5, 7, 15 are same) prints only kqrpre1 subroutine call. 
  Level 255 prints kqrpre1, kqrmpin, kqrmupin.
  Level 4294967295 prints kqrpre1, kqrmpin, kqrpre1, kqrmupin, kqrmupin, kqrfrpo, kqrfrpo.
where 4294967295 (2^32-1) seems the maximum tracing level with most details, including SQL statements.

"cid" denotes Cache ID; "kqrpre1" is for row cache object Read; "po" is for Parent Object.

There is also Event: 10205 which says:

  10205, 00000, "row cache debugging"
   // *Cause:
   // *Action:
   /  At level 1, KQR will dump out latch clean-up debugging traces.
   /  At level 2, KQR will dump out bootstrap debugging traces.
   /  At level 3, KQR will invalidate, remove from the cache and put 0xff's
   /              in row cache objects that are not currently locked.
But it is not able to generate trace file with 10205.


1.5. Are trace levels always bitmap additive ?


A common belief of trace levels is bitmap additive (all binary numbers are positive, different, power of 2).

If we run following Plsql, Level 516 output includes SQL statements. Since 516 is bitmap additive of 4 and 512:
    516 = 4 + 512 = 2^2 + 2^9 
Level 4 or Level 512 should contain SQL statements. But the test shows that Level 4 contains only "kqrpre1" in 12cR1 (and "kqreqd" in 12cR2); Level 512 does not generate any trace file. So Level 516 is not bitmap additive in this case, probably some hard-coded logic
(Level 513, 514 and 515 also not generate any trace file).

create or replace procedure test10222_trace_level as
  type         t_level_tab is table of pls_integer;
  l_level_tab  t_level_tab := new t_level_tab(4, 512, 513, 514, 515, 516);
  l_trc_prefix varchar2(256) := 'row_cache_10222_Level_';
  l_val        varchar2(256);
begin
  for i in 1..l_level_tab.count loop
    execute immediate q'[alter session set tracefile_identifier = ']' ||l_trc_prefix||l_level_tab(i)||q'[']';
    execute immediate q'[alter session set events '10222 trace name context forever, level ]'||l_level_tab(i)||q'[']';
    select value into l_val from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
    execute immediate q'[alter session set events '10222 trace name context off']';
  end loop;
end;
/

exec test10222_trace_level;


2. 10222 Trace Import and Analyse


2.1. Import


Import the trace files into DB via external files (see appended PL/SQL Script)

set serveroutput on

exec import_10222_trc_proc(1, 'testdb_ora_7920_row_cache_10222_4.trc');

exec import_10222_trc_proc(2, 'testdb_ora_7920_row_cache_10222_255.trc');

exec import_10222_trc_proc(3, 'testdb_ora_7920_row_cache_10222_4294967295.trc');

2.2. Analyze


Here a list of queries to analyze Trace Level 4294967295 (RUN=3).

select rownum line_nbr, text1 from raw_10222_trc_ext;

select * from import_10222_trc 
where run=3 
order by run, line_nbr;

select cid, cid_type, cid_subordinate#, cid_parameter, count(*) cnt 
from import_10222_trc 
where run=3 
group by cid, cid_type, cid_subordinate#, cid_parameter 
order by cnt desc;

CID  CID_TYPE     CID_SUBORDINATE#  CID_PARAMETER   CNT
---  -----------  ----------------  -------------   ---
15   PARENT                         dc_props        63
7    SUBORDINATE  0                 dc_users        14
17   PARENT                         dc_global_oids  4
11                                                  4
8    PARENT                         dc_objects      1
                                                    1

select ts_us - lag(ts_us) over(partition by run order by line_nbr) elapsed_us, t.*
from import_10222_trc t 
where run=3 
order by run, line_nbr;

select cid, cid_type, cid_subordinate#, cid_parameter, count(*) cnt, sum(elapsed_us) elapsed_us from (
  select ts_us - lag(ts_us) over(partition by run order by line_nbr) elapsed_us, t.*
  from import_10222_trc t
  where run=3 order by run, line_nbr)
group by cid, cid_type, cid_subordinate#, cid_parameter order by cnt desc;

-- only tested on Solaris (x86-64)
select * from import_10222_trc t, v$rowcache_parent r
where t.run=3 and '0000000'||t.po_address = r.address(+)
order by run, line_nbr;

3. Dtrace Script


3.1 Row Cache Object Gets


Run the same query in one Sqlplus Session (SID: 123, SPID: 7890), and at the same time, trace it by following trace Script (see appended Dtrace Script: rco_dtrace_cache).

SQL (123, 456) > select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';

sudo dtrace -n \
'pid$target::kqrLockAndPinPo:entry /execname == "oracle"/ {
  @CID_ADDR_CNT[arg0, arg1] = count(); 
  @CID_CNT[arg0] = count();
  printf("\nCID = %-4d ADDR = %-16X\n", arg0, arg1);}
END {
  printf("\n----- CID_ADDR CNT -----\n"); printa("CID = %-4d ADDR = %-16X CNT = %-10@d\n", @CID_ADDR_CNT);
  printf("\n----- CID CNT -----\n"); printa("CID = %-4d CNT = %-10@d\n", @CID_CNT);
  printf("\n*** all except 1st counting doubled ***\n");}
' -p 7890

 CPU     ID                    FUNCTION:NAME
...
   5  80618            kqrLockAndPinPo:entry
CID = 15   ADDR = 18F058B68
...
   5  80618            kqrLockAndPinPo:entry
CID = 15   ADDR = 142020320
...

----- CID_ADDR CNT -----
CID = 15   ADDR = 18F058B68        CNT = 1
CID = 15   ADDR = 142020320        CNT = 1
...

----- CID CNT -----
CID = 11   CNT = 3
CID = 17   CNT = 3
CID = 15   CNT = 60
We can see that ADDR matches 10222 Trace Event output, for example, po=18f058b68.


3.2 State Transition Diagram


Run Dtrace Script: rco_dtrace_std to draw a State Transition Diagram of latch Get (kslgetl) and Latch Free (kslfre) for each Row Cache Object GET. (Note: kslgetl is exClusive latch get, whereas ksl_get_shared_latch is Shared latch get(in S or X Mode))

Here the output for one Row Cache Object GET. It shows 3 kslgetl Calls at 3 different Locations ("where"), all are prefixed with "Transition", followed by one kslfre
(note the indented function entry and return).

Those are probably the 3 unclear "latch: row cache objects" GETs, discussed in Oracle Core: Essential Internals for DBAs and Developers Page 167. (see Blog: nls_database_parameters, dc_props, latch: row cache objects )

-> kqrpre1
---------Step 1 at 2018 Oct 18 08:50:25 (Elapsed-ns=1491587737)---------
  -> kslgetl
---------Step 2 at 2018 Oct 18 08:50:25 (Elapsed-ns=104489)---------
            Entry State(nproc_pid=>0x0, flag=>0x0, gets=>0x4A04F, latch=>0x19B, level=>0x4, where=>0x1171)
            Transition > kslgetl:entry(Addr=>0x182D98958, Immed_Get=>1, Why=>0, Where=>4441(0x1159))
  <- kslgetl
---------Step 3 at 2018 Oct 18 08:50:25 (Elapsed-ns=32606)---------
            Return State(nproc_pid=>0x1B, flag=>0x0, gets=>0x4A050, latch=>0x19B, level=>0x4, where=>0x1159)
  -> kqrLockAndPinPo
---------Step 4 at 2018 Oct 18 08:50:25 (Elapsed-ns=36536)---------
            CID = 15   ADDR = 171999DD8
  <- kqrLockAndPinPo
---------Step 5 at 2018 Oct 18 08:50:25 (Elapsed-ns=24430)---------
  -> kslfre
---------Step 6 at 2018 Oct 18 08:50:25 (Elapsed-ns=8813)---------
            Transition > kslfre:entry(Addr=>0x182D98958) ===Latch freed===
  <- kslfre
---------Step 7 at 2018 Oct 18 08:50:25 (Elapsed-ns=14267)---------
<- kqrpre1
---------Step 8 at 2018 Oct 18 08:50:25 (Elapsed-ns=11490)---------
-> kslgetl
---------Step 9 at 2018 Oct 18 08:50:25 (Elapsed-ns=6415)---------
            Entry State(nproc_pid=>0x0, flag=>0x0, gets=>0x4A050, latch=>0x19B, level=>0x4, where=>0x1159)
            Transition > kslgetl:entry(Addr=>0x182D98958, Immed_Get=>1, Why=>0, Where=>4464(0x1170))
<- kslgetl
---------Step 10 at 2018 Oct 18 08:50:25 (Elapsed-ns=6092)---------
            Return State(nproc_pid=>0x1B, flag=>0x0, gets=>0x4A051, latch=>0x19B, level=>0x4, where=>0x1170)
-> kslfre
---------Step 11 at 2018 Oct 18 08:50:25 (Elapsed-ns=10545)---------
            Transition > kslfre:entry(Addr=>0x60164110) ===Latch freed===
<- kslfre
---------Step 12 at 2018 Oct 18 08:50:25 (Elapsed-ns=4733)---------
-> kslgetl
---------Step 13 at 2018 Oct 18 08:50:25 (Elapsed-ns=4412)---------
            Entry State(nproc_pid=>0x0, flag=>0x0, gets=>0x4A051, latch=>0x19B, level=>0x4, where=>0x1170)
            Transition > kslgetl:entry(Addr=>0x182D98958, Immed_Get=>1, Why=>0, Where=>4465(0x1171))
<- kslgetl
---------Step 14 at 2018 Oct 18 08:50:25 (Elapsed-ns=5110)---------
            Return State(nproc_pid=>0x1B, flag=>0x0, gets=>0x4A052, latch=>0x19B, level=>0x4, where=>0x1171)
-> kslfre
---------Step 15 at 2018 Oct 18 08:50:25 (Elapsed-ns=4445)---------
            Transition > kslfre:entry(Addr=>0x182D98958) ===Latch freed===
<- kslfre
The 3 consecutive latch Gets for one Row Cache Object GET are at 3 different Locations ("Where"):
  Where=>4441(0x1159): kqrpre: find obj   -- latch Get at 1st Location
  Where=>4464(0x1170): kqreqd             -- latch Get at 2nd Location
  Where=>4465(0x1171): kqreqd: reget      -- latch Get at 3rd Location
which can be found by sql below:

CREATE OR REPLACE FORCE VIEW X_GV$LATCH_MISSES as
SELECT t1.indx              INDX,
       t1.inst_id           INST_ID,       
       t1.ksllasnam         PARENT_NAME,   
       t2.ksllwnam          "WHERE",       
       t1.kslnowtf          NWFAIL_COUNT,  
       t1.kslsleep          SLEEP_COUNT,   
       t1.kslwscwsl         WTR_SLP_COUNT, 
       t1.kslwsclthg        LONGHOLD_COUNT,
       t2.ksllwnam          LOCATION,      
       t1.con_id            CON_ID         
  FROM x$ksllw t2, x$kslwsc t1
 WHERE t2.indx = t1.indx;
 
select * from sys.X_GV$LATCH_MISSES 
where parent_name in ('row cache objects') 
  and indx in (4441, 4464, 4465); 
  
INDX  PARENT_NAME        WHERE             NWFAIL_COUNT  SLEEP_COUNT  WTR_SLP_COUNT  LONGHOLD_COUNT  LOCATION        
----- ----------------------  -----------------------------  ------------  -----------  -------------  --------------
4441  row cache objects  kqrpre: find obj  0             2            8              0               kqrpre: find obj
4464  row cache objects  kqreqd            0             0            0              0               kqreqd          
4465  row cache objects  kqreqd: reget     0             6            0              0               kqreqd: reget 


3.3 Call Stack and Call Sequence (Update 2020-Aug-03)


There are 3 latch gets, the first call is from kqrpre1 with Call Stack:

  kslgetl ()
  kqrpre1 ()
  kkdlpftld ()
  qerfxFetch ()
  opifch2 ()
the second and third calls are from kqreqd with Call Stack:

  kslgetl ()
  kqreqd ()
  kqrprl ()
  kkdlpftld ()
  qerfxFetch ()
kqrpre1 calls latch get/free (kslgetl/kslfre) with Call Sequence (latch location 0x1159):

  <kgghash>
  <kslgetl>      -- Where=>4441(0x1159)
  <kqrCacheHit>
  <kqrLockAndPinPo>
  <kslfre>
kqreqd calls latch get/free (kslgetl/kslfre) twice, with Call Sequence (latch location 0x1170 and 0x1171):

  <kslgetl>     -- Where=>4464(0x1170)
  <KGHISPIR>    -- "pin recreatable" 
  <kghupr>      -- "unpin recreatable"
  <kslfre>
  
  <kslgetl>     -- Where=>4465(0x1171)
  <kglFreeSO>   -- "free state object"
  <kslfre>
To find RowCache object, kqrpre1 first calls <kgghash> to compute a hash value and map to po (Rowcache Parent Object) value as shown in above 10222 trace:

  kqrpre1 : found po=0x9675c390 flg=2 hash=819e131 pso=0x9511def8 dur=CALL


4. 12cR2 10222 Trace Event


Make the same trace in Oracle 12cR2 for 3 different Levels, here the output:


4.1. Level 4


kqrpre1 : found po=0x9675c390 flg=2 hash=819e131 pso=0x9511def8 dur=CALL .
kqrpre1 : done po=0x9675c390 cid=15 flg=2 hash=819e131 0 eq=0x63dfbd98 pso=0x9511def8 dur=CALL .
kqreqd: eq: 0x63dfbd98
kqreqd freeing enqeue: eq: 0x63dfbd98

4.2. Level 255


kqrmpin : kqrpre: found po Pin po 0x9675c390 cid=15 flg=2 hash=819e131
time=1980227738
kqrpre1 : found po=0x9675c390 flg=2 hash=819e131 pso=0x9511def8 dur=CALL time=1980227746
kqrpre1 : done po=0x9675c390 cid=15 flg=2 hash=819e131 0 eq=0x63dfc580 pso=0x9511def8 dur=CALL time=1980227754
kqrprl: eq=0x63dfc580 fpInUse=FALSE
ksedsts()+346<-kqrprl()+1221<-kkdlpExecSqlCbk()+570<-kkdlpExecSql()+114<-kkdlpGet()+317<-kkdlpftld()+229<-qerfxFetch()+6519<-opifch2()+3267<-kpoal8()+3436<-opiodr()+1229<-ttcpip()+1257<-opitsk()+1940<-opiino()+941<-opiodr()+1229<-opidrv()+1021<-sou2o()+145
<-opimai_real()+455<-ssthrdmain()+417<-main()+262<-__libc_start_main()+256.
kqreqd: eq: 0x63dfc580
kqrmupin : kqrReleaseLock Unpin po 0x9675c390 cid=15 flg=2 hash=819e131 time=1980227875
kqreqd freeing enqeue: eq: 0x63dfc580

4.3. Level 4294967295


kqrmpin : kqrpre: found po Pin po 0x9675c390 cid=15 flg=2 hash=819e131
time=1845554855
kqrpre1 : found po=0x9675c390 flg=2 hash=819e131 pso=0x9511def8 dur=CALL time=1845554916
found stack: ksedsts()+346<-kqrpre1()+4784<-kkdlpExecSqlCbk()+418<-kkdlpExecSql()+114<-kkdlpGet()+317<-kkdlpftld()+229<-qerfxFetch()+6519<-opifch2()+3267<-kpoal8()+3436<-opiodr()+1229<-ttcpip()+1257<-opitsk()+1940<-opiino()+941<-opiodr()+1229<-opidrv()+1021
<-sou2o()+145<-opimai_real()+455<-ssthrdmain()+417<-main()+262<-__libc_start_main()+256.
kqrpre1 : done po=0x9675c390 cid=15 flg=2 hash=819e131 0 eq=0x63dfc0e0 pso=0x9511def8 dur=CALL SQL=select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET' time=1845564279
done stack: ksedsts()+346<-kqrpre1()+2566<-kkdlpExecSqlCbk()+418<-kkdlpExecSql()+114<-kkdlpGet()+317<-kkdlpftld()+229<-qerfxFetch()+6519<-opifch2()+3267<-kpoal8()+3436<-opiodr()+1229<-ttcpip()+1257<-opitsk()+1940<-opiino()+941<-opiodr()+1229<-opidrv()+1021
<-sou2o()+145<-opimai_real()+455<-ssthrdmain()+417<-main()+262<-__libc_start_main()+256.
kqrprl: eq=0x63dfc0e0 fpInUse=FALSE
ksedsts()+346<-kqrprl()+1221<-kkdlpExecSqlCbk()+570<-kkdlpExecSql()+114<-kkdlpGet()+317<-kkdlpftld()+229<-qerfxFetch()+6519<-opifch2()+3267<-kpoal8()+3436<-opiodr()+1229<-ttcpip()+1257<-opitsk()+1940<-opiino()+941<-opiodr()+1229<-opidrv()+1021<-sou2o()+145
<-opimai_real()+455<-ssthrdmain()+417<-main()+262<-__libc_start_main()+256.
kqreqd: eq: 0x63dfc0e0
kqrmupin : kqrReleaseLock Unpin po 0x9675c390 cid=15 flg=2 hash=819e131 time=1845566287
kqreqd freeing enqeue: eq: 0x63dfc0e0

4.4. 12cR2 vs. 12cR1


Note that in Oracle 12.2.0.1.0 (12cR2), "row cache mutex" replaced 12.1.0.2.0 (12cR1) "latch: row cache objects". (See Blog: row cache mutex in Oracle 12.2.0.1.0 )

It seems that 10222 Trace event still generates similar output with more information, for example, call stacks.

Comparing the function calling sequence of Level 255 between 12cR1 and 12cR2:

  12cR1: kqrpre1(found) -> kqrmpin        -> kqrpre1(done) -> kqrmupin
  12cR2: kqrmpin        -> kqrpre1(found) -> kqrpre1(done) -> kqrmupin
12cR1: kqrpre1 and kqrmpin (kqrmupin) are interleaved, whereas 12cR2 shows that kqrpre1 is enclosed by kqrmpin (kqrmupin). So 12cR2 improved trace output.


5. Row Cache Internals: kqrpre1 and kqrfrpo


Pick one Row Cache Object GET from the output of above 10222 Level 4294967295, it shows that Row Cache is not able to cache it. Each time it needs to invoke "kqrpre1" (Row Cache Parent Read) to create a new DC row, use it, and then release it by "kqrfrpo : freed to heap". It took 279 us (2271264814-2271264535) for one such Read.

kqrpre1 : found po=18f058b68 flg=2 hash=5b42c02e time=2271264535
kqrmpin : kqrLockAndPinPo Pin po 18f058b68 cid=15 flg=2 hash=5b42c02e
time=2271264669
kqrpre1 : done po=18f058b68 cid=15 flg=2 hash=5b42c02e 0 eq=13fea57f8 SQL=select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET' time=2271264687
kqrmupin : kqrpspr2 Unpin po 18f058b68 cid=15 flg=2 hash=5b42c02e time=2271264704
kqrmupin : free po=18f058b68 flg=2
kqrfrpo: Free po 18f058b68
 KQR Parent Cache Object:
 pob = 18f058b68 flg = 00000002 cid = 15 xcb = 0 hbk = 18f05ac38
 hbk=[f84f96e8,151186580] 
 own=18f058c38[18f058c38,18f058c38] wat=18f058c48[18f058c48,18f058c48] mod=0
kqrfrpo : freed to heap po=18f058b68 time=2271264814
We will look further in next Blog: Row Cache Objects, Row Cache Latch on Object Type: Plsql vs Java Call (Part-1) (II)


6. Discussions


Row Cache is a mechanisms analogue to CPU Cache:
  "CPU Cache" transfer "cache lines" between "Main Memory"  and "L1 Cache".

  "Row Cache" transfer "DC rows"     between "Buffer Cache" and "DC Cache".
Row Cache is more like a "software" cache because "Row Cache" and its hidden "Buffer Cache" are in SGA memory,
whereas "CPU Cache (L1)" and its underlying "Main Memory" are located in separate hardware.

Any cache miss results in a "Main Memory/Buffer" with much longer latency. The cache hit rate plays an important role in determining the performance.

Section 3.2 State Transition Diagram showed that each Row Cache Object GET is a cache miss.


7. 10222 Trace Import and Analyse Script


drop table raw_10222_trc_ext;

create table raw_10222_trc_ext
( text1 varchar2(1000)
)
organization external
(type oracle_loader
 default directory PLSHPROF_DIR
 access parameters
 (
   records delimited by newline CHARACTERSET AL32UTF8
   badfile aaa_db_io:'trc_ext.bad'
   logfile aaa_db_io:'trc_ext.log'
   discardfile aaa_db_io:'trc_ext.dsc'
   fields terminated by ','  OPTIONALLY ENCLOSED BY '"'
   missing field values are null
   ( text1 position(1:1000)
   )
 )
 location ('testdb_ksun_trc_row_cache_10222_15.trc')
) reject limit unlimited;

select rownum line_nbr, text1 from raw_10222_trc_ext;

drop table import_10222_trc;

create table import_10222_trc (run number, line_nbr number, cid number, ts_us number, po_address varchar2(32), 
  cid_type varchar2(12), cid_subordinate# number, cid_parameter varchar2(40), 
  obobn number, obname varchar2(100), sql varchar2(100), raw_lines varchar2(4000));

create or replace procedure import_10222_trc_proc (p_run number, p_trc_file varchar2, p_delete_old boolean := true) as 
  l_line_sep    varchar2(3) := '###';
  l_pos_1       number;
  l_pos_2       number;
  l_raw_lines   varchar2(32000);
  l_line_nbr    number           := 1;
  l_cid         number;
  l_ts_us       number;
  l_po_address  varchar2(32000);
  l_obobn       number;
  l_obname      varchar2(32000);
  l_sql         varchar2(32000);
begin
  -- setup trace file location
  execute immediate 'alter table raw_10222_trc_ext location (''' || p_trc_file || ''')';
  
  -- delete old RUN
  if p_delete_old then
    delete from import_10222_trc where run = p_run;
    commit;
  end if;
  
  -- concatenate same PO lines into one line
  for c in (select rownum line_nbr, text1 from raw_10222_trc_ext) loop
    dbms_output.put_line('Raw line_nbr: '||c.line_nbr ||'--text1: '||c.text1);
    if l_raw_lines is null then
      l_raw_lines := c.text1;
    else
      l_raw_lines := l_raw_lines ||l_line_sep||c.text1;
    end if;
    
    if c.text1 like 'kqrpre1 : found po=%' or c.text1 like 'kqrpad: new po%' then
      l_raw_lines := substr(l_raw_lines, 1, 4000);
      insert into import_10222_trc(run, line_nbr, raw_lines) values(p_run, l_line_nbr, l_raw_lines);
      commit;
      l_line_nbr  := c.line_nbr;
      l_raw_lines := c.text1;
    end if;
  end loop;
  
  -- insert last line
  if length(l_raw_lines) > 0 then
   l_raw_lines := substr(l_raw_lines, 1, 4000);
    insert into import_10222_trc(run, line_nbr, raw_lines) values(p_run, l_line_nbr, l_raw_lines);
    commit;
  end if;

 -- parse raw_lines
  for c in (select line_nbr, raw_lines from import_10222_trc
             where raw_lines like 'kqrpre1 : found po=%' or raw_lines like 'kqrpad: new po%') loop
    dbms_output.put_line('Parsing line_nbr: '||c.line_nbr||'--text1: '||c.raw_lines);
    l_cid        := regexp_substr(c.raw_lines, 'cid=(\d*)', 1, 1, 'i', 1);
    l_pos_1      := instr(c.raw_lines, 'time=', -1);
    if l_pos_1 > 0 then
      l_ts_us    := regexp_substr(c.raw_lines, 'time=(\d*)', l_pos_1, 1, 'i', 1);
    end if;
    l_po_address := substr(upper(regexp_substr(c.raw_lines, 'po=(\S*)', 1, 1, 'i', 1)), 1, 32);
    l_obobn      := regexp_substr(c.raw_lines, 'obobn=(\d*)', 1, 1, 'i', 1);
    l_obname     := substr(regexp_substr(c.raw_lines, 'obname=(\S*)', 1, 1, 'i', 1), 1, 100);
    l_pos_2      := instr(c.raw_lines, 'SQL=', 1) + 4;
    
    if (l_pos_2 > 4) then
     l_pos_1      := instr(c.raw_lines, 'time=', l_pos_2);
     if l_pos_1 = 0 then l_pos_1 := l_pos_2 + 100; end if;
      l_sql        := substr(substr(c.raw_lines, l_pos_2, l_pos_1 - l_pos_2), 1, 100);
    else
     l_sql := null;
    end if;
    
    update import_10222_trc 
       set cid        = l_cid
          ,ts_us      = l_ts_us
          ,po_address = l_po_address
          ,obobn      = l_obobn
          ,obname     = l_obname
          ,sql        = l_sql
     where run = p_run and line_nbr = c.line_nbr; 
    commit;
  end loop;

  -- add v$rowcache info
  update import_10222_trc t 
     set (cid_type, cid_subordinate#, cid_parameter) = 
         (select type, subordinate#, parameter from v$rowcache r where r.cache# = t.cid and rownum=1)
   where run = p_run;
  commit;
end;
/


8. Dtrace Scripts


8.1. rco_dtrace_cache


sudo dtrace -n \
'pid$target::kqrLockAndPinPo:entry /execname == "oracle"/ {
  @cid_addr_cnt[arg0, arg1] = count();
  @cid_cnt[arg0] = count();
  @cid_cnt_total = count();
  ustack(5, 0);
  printf("\nCID = %-4d ADDR = %-16X\n", arg0, arg1);}
END { 
   /** Only first call in new opened Sqlplus has not double counting **/
   printf("\n\n*************** CID Stats with Address (double counting) ****************");
   printa("\nCID = %-4d ADDR = %-16X CNT = %-10@d", @cid_addr_cnt);
   printf("\n*************** CID Stats ****************");
   printa("\nCID = %-4d CNT = %-10@d", @cid_cnt);
   printf("\n*************** CID Stats Summary ****************");
   printa("\nCNT = %-10@d", @cid_cnt_total); }
' -p $1

Update (30-May-2021)

Nenad (All-round Database Topics) found that double counting user calls can be avoided by specifying the module name, like

   pid$target:oracle:kqrLockAndPinPo:entry
instead of

   pid$target::kqrLockAndPinPo:entry
Here some tests in Oracle 19c
(Note: Oracle 12.2.0.1.0 (12cR2), "row cache mutex" replaced 12.1.0.2.0 (12cR1) "latch: row cache objects". See Blog: row cache mutex in Oracle 12.2.0.1.0 )

At first we list all kqr* functions:

SQL > select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';

$ sudo dtrace -n 'pid$target::kqr*:entry { @[probefunc] = count(); }' -p 25986

	dtrace: description 'pid$target::kqr*:entry ' matched 310 probes
		  kqrAllocateEnqueue        194
		  kqrCacheHit               194
		  kqrFreeEnqueue            194
		  kqrGetPOMutexInt          194
		  kqrLockPo                 194
		  kqrReleaseLock            194
		  kqreqd                    194
		  kqrget                    194
		  kqrhsh                    194
		  kqrmpin                   194
		  kqrmupin                  194
		  kqrpre1                   194
		  kqrpre2                   194
		  kqrprl                    194
		  kqrpspr                   194
		  
	*Note: kqrLockAndPinPo not listed, probably it is an Oracle 12.1 function
Then we list probes for above probe functions. There are two probe modules for each function (that is why we have the double counting), for example,

$ sudo dtrace -l -f kqrLockPo 
         ID   PROVIDER          MODULE           FUNCTION NAME
      90031   pid25986           a.out          kqrLockPo entry
      90032   pid25986          oracle          kqrLockPo entry
         
$ sudo dtrace -l -f kqrpre1    
         ID   PROVIDER          MODULE           FUNCTION NAME
      90029   pid25986           a.out            kqrpre1 entry
      90030   pid25986          oracle            kqrpre1 entry
Now we can use one of following probes to specify module and hence avoid double counting:

$ sudo dtrace -n 'pid$target:oracle:kqrpre1:entry {@[probefunc] = count();}' -p 25986
$ sudo dtrace -n 'pid$target:a.out:kqrpre1:entry  {@[probefunc] = count();}' -p 25986
$ sudo dtrace -i '90029 {@[probefunc] = count();}'
$ sudo dtrace -i '90030 {@ = count(); }'
We can also directly list each function with its probe module as follows:

SQL >select value from nls_database_parameters where parameter = 'NLS_CHARACTERSET';

$ sudo dtrace -n 'pid$target::kqr*:entry {@[probefunc, probemod] = count();}' -p 25986
	dtrace: description 'pid$target::kqr*:entry ' matched 310 probes
	    kqrAllocateEnqueue          a.out          97
	    kqrAllocateEnqueue          oracle         97
	    ......                      
	    kqrpre1                     a.out          97
	    kqrpre1                     oracle         97
	    ......                      
	    kqrpspr                     a.out          97
	    kqrpspr                     oracle         97

8.2. rco_dtrace_latch


sudo dtrace -F -n \
'
BEGIN {roread = 0; step = 1; self->func = "ne"; self->name = "na"; prev_ts = timestamp;}
pid$target::kqrpre1:entry /execname == "oracle" && self->func != probefunc / {roread = 1;
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt[probefunc] = count();}
pid$target::kqrpre1:return /roread ==1/ {roread = 0;
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;}
pid$target::kslgetl:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt[probefunc] = count();}
pid$target::kslgetl:return /self->func == probefunc && self->name != probename/{
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1;  prev_ts = timestamp;}
pid$target::kslfre:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt[probefunc] = count();}
pid$target::kslfre:return /self->func == probefunc && self->name != probename/{
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1;  prev_ts = timestamp;}
pid$target::kqrLockAndPinPo:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt[probefunc] = count();}
pid$target::kqrLockAndPinPo:return /self->func == probefunc && self->name != probename/{
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;}
' -p $1

8.3. rco_dtrace_std (@TODO improve it)


sudo dtrace -F -n \
'
typedef unsigned int ub4;

typedef struct latchstate /* latch state */ {
    ub4 nproc_pid;  /* Nproc or pid   */
    ub4 flag;       /* blocking flag  */
    ub4 gets;       /* number of gets */
    ub4 latch;      /* latch number   */
    ub4 level;      /* latch level    */
    ub4 where; } latchstate;

BEGIN {step = 1; self->func = "na"; self->name = "na"; prev_ts = timestamp;}
pid$target::kqrpre1:entry /execname == "oracle" && self->func != probefunc / {self->roread = 1;
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt_func[probefunc] = count();}
pid$target::kqrpre1:return /self->roread ==1/ {self->roread = 0;
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = "na"; self->name = probename; step = step + 1; prev_ts = timestamp;}
pid$target::kslgetl:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt_func[probefunc] = count();
   ls = ((latchstate *) copyin(arg0, sizeof(latchstate)));
   printf("\n            Entry State(nproc_pid=>0x%X, flag=>0x%X, gets=>0x%X, latch=>0x%X, level=>0x%X, where=>0x%X)"
           ,ls->nproc_pid, ls->flag, ls->gets, ls->latch, ls->level, ls->where);
   printf("\n            Transition > %s:%s(Addr=>0x%X, Immed_Get=>%d, Why=>%d, Where=>%d(0x%X))",
          probefunc, probename, arg0, arg1, arg2, arg3, arg3);
   self->addr = arg0; self->loc = arg3;}
pid$target::kslgetl:return /self->func == probefunc && self->name != probename/{
   @sum[self->addr, self->loc] = sum(timestamp - prev_ts);
   @avg[self->addr, self->loc] = avg(timestamp - prev_ts);
   @max[self->addr, self->loc] = max(timestamp - prev_ts);
   @cnt[self->addr, self->loc] = count();
   @sum_total = sum(timestamp - prev_ts);
   @cnt_total = count();
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1;  prev_ts = timestamp;
   ls = ((latchstate *) copyin(self->addr, sizeof(latchstate)));
   printf("\n            Return State(nproc_pid=>0x%X, flag=>0x%X, gets=>0x%X, latch=>0x%X, level=>0x%X, where=>0x%X)"
           ,ls->nproc_pid, ls->flag, ls->gets, ls->latch, ls->level, ls->where);}
pid$target::kslfre:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt_func[probefunc] = count();
   printf("\n            Transition > %s:%s(Addr=>0x%-X) ===Latch freed===", probefunc, probename, arg0);}
pid$target::kslfre:return /self->func == probefunc && self->name != probename/{
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1;  prev_ts = timestamp;}
pid$target::kqrLockAndPinPo:entry /step > 1 && self->func != probefunc / {
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;
   @cnt_func[probefunc] = count();
   @cid_addr_cnt[arg0, arg1] = count();
   @cid_cnt[arg0] = count();
   @cid_cnt_total = count();
   printf("\n            CID = %-4d ADDR = %-16X", arg0, arg1);}
pid$target::kqrLockAndPinPo:return /self->func == probefunc && self->name != probename/{
   printf("\n---------Step %d at %Y (Elapsed-ns=%d)---------", step, walltimestamp, (timestamp-prev_ts));
   self->func = probefunc; self->name = probename; step = step + 1; prev_ts = timestamp;}
END {
   trunc(@sum, 10); trunc(@avg, 10); trunc(@max, 10); trunc(@cnt, 10);
   printf("\n\n*************** Latch Stats (time in ns) ****************");
   printf("\nAddr           Where");
   printa("\n0x%-10X %6d sum = %10@d", @sum); printf("\n----------------------");
   printa("\n0x%-10X %6d avg = %10@d", @avg); printf("\n----------------------");
   printa("\n0x%-10X %6d max = %10@d", @max); printf("\n----------------------");
   printa("\n0x%-10X %6d cnt = %10@d", @cnt); printf("\n----------------------");
   printa("\nsum_total = %10@d", @sum_total);
   printa("\ncnt_total = %10@d", @cnt_total);

   /** @TODO not exact **/
   printf("\n\n*************** CID Stats with Address ****************");
   printa("\nCID = %-4d ADDR = %-16X CNT = %-10@d", @cid_addr_cnt);
   printf("\n*************** CID Stats ****************");
   printa("\nCID = %-4d CNT = %-10@d", @cid_cnt);
   printf("\n*************** CID Stats Summary ****************");
   printa("\nCNT = %-10@d", @cid_cnt_total);

   printf("\n\n*************** probefunc Stats ****************");
   printa("\n%-20s cnt = %10@d", @cnt_func);}
' -p $1