Wednesday, May 12, 2021

Oracle 19c new shared pool "SO private sga" and "SO private so latch" Performance Impacts

Oracle 19c introduced new shared pool component "SO private sga" and latch "SO private so latch". In this Blog, we will make tests to reveal their behaviours and performance impacts.

Normally Oracle can have up to 7 subpools in shared pool. However shared pool dump shows that "SO private sga" is only allocated to one single subpool. In such case, "SO private sga" can be one of the TOP 5 memory components, and hence high memory pressure on that particular subpool. Additionally "alter system flush shared_pool" cannot force the release of "SO private sga" memory.

The new 19c latch "SO private so latch" is positioned in LEVEL#=9, and "shared pool" latch LEVEL# is increased to 10 from 7 in 18c (latch ordering rule: Once a process acquires a latch of level x, it can only acquire another latch of level higher than x). To update "SO private sga" (kss_grow_from_global_cache), "SO private so latch" (kslgetl immediate_gets) is requested at first, which again requests library cache lock (kglLock) and mutex (kglGetMutex).

Note: Tested in Oracle 19.10

Update (08Jul2021): With test code of this Blog, Oracle delivered fix:
     Bug 32940955 : ORA-4031 DUE TO LARGE "SO PRIVATE SGA" ALLOCATION IN ONE SHARED POOL SUBPOOL


1. Test Setup


At first, we elaborate a standalone test, which can touch both "SO private sga" and "SO private so latch" for each new execution in a new Oracle connection.

create or replace package so_private_pkg as
  s_ts      timestamp with time zone;
  procedure proc1(p_cnt number);
end;
/

create or replace package body so_private_pkg as
  procedure proc1(p_cnt number) as
  begin
    s_ts  := systimestamp;   -- key to kss_grow_from_global_cache 
    dbms_session.set_nls('nls_territory',           'AMERICA');
    dbms_session.set_nls('nls_language',            'AMERICAN');
    dbms_session.set_nls('nls_sort',                'BINARY');
    dbms_session.set_nls('nls_numeric_characters',  '''.,''');
    commit;
    dbms_session.set_nls('nls_timestamp_format','''YYYY-MM-DD"T"HH24:MI:SS''');
  end;
end;
/

create or replace procedure K.start_job_so(p_count number) as
begin
  for i in 1..p_count loop
    dbms_scheduler.create_job (
      job_name        => 'TEST_JOB_SO_'||i,
      job_type        => 'PLSQL_BLOCK',
      job_action      =>
        'begin
           dbms_lock.sleep(29);
           so_private_pkg.proc1('||i||');
           --test_proc_dynamic(100, 1000);
           dbms_lock.sleep(1);
        end;',
      start_date      => systimestamp,
      repeat_interval => 'systimestamp',
      auto_drop       => true,
      enabled         => true);
  end loop;
end;
/


2. "SO private sga" and "SO private so latch"


Login to a 19c DB, we can see the new shared pool component "SO private sga":

select * from v$sgastat where name= 'SO private sga';

POOL           NAME                  BYTES   CON_ID
-------------- ---------------- ---------- --------
shared pool    SO private sga     40027648        0
If we make a shared pool dump, we can see "SO private sga" is only allocated to one single subpool (2) as TOP memory Subheap.

5 LARGEST SUB HEAPS for heap name="sga heap(2,0)"   desc=0x6015dc18
  Subheap ds=0x60006c98  heap name=  SO private sga  size=        40029040
   owner=(nil)  latch=(nil)
  Subheap ds=0x6000a060  heap name=  KSFD SGA I/O b  size=         4190424
   owner=(nil)  latch=(nil)
  Subheap ds=0x729ff880  heap name=   SQLA^15b219ae  size=         1242144
   owner=0x729ff730  latch=(nil)
  Subheap ds=0x9fd7ae80  heap name=   SQLA^9bd0be72  size=         1046128
   owner=0x9fd7ad30  latch=(nil)
  Subheap ds=0x8d9ebcf8  heap name=  PLMCD^e38f5ee0  size=         1038512
   owner=0x8dbbf2e0  latch=(nil)
Make a new connection and run test below to show latch usage:
(Note: For each new Connection, DO NOT RUN any script like glogin.sql (Site Profile file), login.sql (User Profile))

$ sqlplus /nolog

SQL> conn k/s@db19c
Connected.

SQL> select name, sum(gets), sum(immediate_gets) from v$latch_children where name in('shared pool', 'SO private so latch') group by name;

NAME                  SUM(GETS) SUM(IMMEDIATE_GETS)
-------------------- ---------- -------------------
shared pool            68985540               15463
SO private so latch     2793045             2808678

SQL> exec so_private_pkg.proc1(1);

PL/SQL procedure successfully completed.

SQL> select name, sum(gets), sum(immediate_gets) from v$latch_children where name in('shared pool', 'SO private so latch') group by name;

NAME                  SUM(GETS) SUM(IMMEDIATE_GETS)
-------------------- ---------- -------------------
shared pool            68985562               15463
SO private so latch     2793045             2808679
We can see "shared pool" SUM(GETS) increased 22 (68985562-68985540).
"SO private so latch" SUM(IMMEDIATE_GETS) increased 1 (2808679-2808678).

By the way, referring to x$ksmsp, we can see that Oracle shared pool memory is organized in 5 levels:

   shared_pool -> subpool (ksmchidx) -> component (ksmchcom) -> area (ksmchpar) -> chunk (ksmchptr)

select * from x$ksmsp where ksmchcom in ('SO private sga') and rownum <= 2;
 
  ADDR         INDX INST_ID CON_ID KSMCHIDX KSMCHDUR KSMCHCOM       KSMCHPTR KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR
  ------------ ---- ------- ------ -------- -------- -------------- -------- -------- -------- -------- --------
  7FBF49B7AF38    4       1      0        3        1 SO private sga 9FEFCBE8  1048536 recr         4095 9E29C270
  7FBF49B65BE8  837       1      0        3        1 SO private sga 9BFBDDA8   253976 freeabl         0 9E29C270
Following two queries list all memory components which are only allocated into one single subpool.

---- x$ksmsp lists each memomy chunk (ksmchptr, minimum unit) in each area (ksmchpar) for each component (ksmchcom) in subpool (ksmchidx)
---- x$ksmsp does not contain reserved extents
 
select ksmchcom, count(cnt) subpool_cnt, sum(siz) subpool_size
from (select ksmchidx, ksmchcom, count(*) cnt, sum(ksmchsiz) siz from x$ksmsp group by ksmchidx, ksmchcom)
  --where ksmchcom in ('SO private sga')
group by ksmchcom
having count(cnt) = 1
order by subpool_size desc; 
 
 
---- x$ksmss (v$sgastat) is about stats of SGA component (ksmssnam) in each subpool (ksmdsidx)
---- ksmdsidx = 0 is for reserved extents
 
select ksmssnam, count(cnt) subpool_cnt, sum(siz) subpool_siz 
  from (select ksmdsidx, ksmssnam, count(*) cnt, sum(ksmsslen) siz from x$ksmss group by ksmdsidx, ksmssnam)
  --where ksmssnam in ('SO private sga')
group by ksmssnam
having count(cnt) = 1
order by subpool_siz desc; 


3. SO private Activity Tracing


At frist, we get latch address, and then we compose one gdb script with those address (see Appendix "gdb_latch_script_3.txt")
(Note: the test DB is set with "_kghdsidx_count"=3 to create 3 subpools in shared pool)

select addr, latch#, child#, level#, name, gets, immediate_gets 
  from v$latch_children where name in ('shared pool', 'SO private so latch') and child# <=3 order by name, child#;

ADDR         LATCH#     CHILD#     LEVEL#  NAME                       GETS IMMEDIATE_GETS
-------- ---------- ---------- ----------  -------------------- ---------- --------------
B626A638         42          1          9  SO private so latch      931312         936136
B626A6F0         42          2          9  SO private so latch      930302         935888
B626A7A8         42          3          9  SO private so latch      931444         936680
60560A38        619          1         10  shared pool            22816547           5894
60560AD8        619          2         10  shared pool            22630853           5199
60560B78        619          3         10  shared pool            23540102           4370
Each time when making a test, we start a new connection:

SQL> conn k/s@db19c
Connected.
Get its UNIX process id: 123

Start tracing with the composed script:

gdb -x gdb_latch_script_3.txt -p 123
Run the test:

SQL> exec so_private_pkg.proc1(1);
Here the tracing log:

===== Library Cache Lock (1) <<< kgllkhdl: 879D79B0, kgllkmod 1, kglnaobj: BEGIN so_private_pkg.proc1(1); END;
===== Library Cache Lock (2) <<< kgllkhdl: 879DDD28, kgllkmod 1, kglnaobj: >>>=====
===== Library Cache Lock (3) <<< kgllkhdl: 6E851DD0, kgllkmod 1, kglnaobj: SO_PRIVATE_PKGK>>>=====
===== Library Cache Lock (4) <<< kgllkhdl: 707490A8, kgllkmod 1, kglnaobj: SO_PRIVATE_PKGK>>>=====
===== Library Cache Lock (5) <<< kgllkhdl: A2EA2A50, kgllkmod 1, kglnaobj: STANDARDSYS>>>=====
===== Library Cache Lock (6) <<< kgllkhdl: A5B6FA50, kgllkmod 1, kglnaobj: STANDARDSYS>>>=====
===== Library Cache Lock (7) <<< kgllkhdl: 87E9EC90, kgllkmod 1, kglnaobj: DBMS_SESSIONPUBLIC>>>=====

Breakpoint 9, 12d7f230 in kglGetMutex ()
=====--- kglGetMutex (30) ---> Mutex addr (rsi): A127C888, Location(r8d): 106
#0  12d7f230 in kglGetMutex ()
#1  12d74ea2 in kglhdgn ()

Breakpoint 10, 12da4830 in kgxExclusive ()
=====----- kgxExclusive (20) ---> Mutex addr (rsi): A127C888

Breakpoint 8, 12d76c90 in kgllkal ()
===== Library Cache Lock (8) <<< kgllkhdl: A127C738, kgllkmod 1, kglnaobj: DBMS_SESSIONSYS>>>=====
#0  12d76c90 in kgllkal ()
#1  12d72627 in kglLock ()

Breakpoint 1, 125c9ba0 in kslgetl ()
===== kslgetl shared latch (1) <<< Addr(rdi): 60560A38, Imget: 1, Why: 0, Where: 6293 >>>=====
#0  125c9ba0 in kslgetl ()
#1  1259da43 in ksfglt ()
#2  12d36f13 in kghalo ()
#3  12d33c24 in kghgex ()
#4  12d38442 in kghfnd ()
#5  12d36ae2 in kghalo ()
#6  12d7e6f8 in kglGetSO ()
#7  12d76dbf in kgllkal ()

Breakpoint 2, 125cf6c0 in kslfre ()
===== kslfre shared latch (1) <<< Addr(rdi): 60560A38 >>>=====
#0  125cf6c0 in kslfre ()
#1  1259dded in ksfflt ()

Breakpoint 11, 12da5850 in kgxRelease ()
=====----- kgxRelease (20) ---> Mutex addr (r15): A127C888 
$20 = {0, 909, 7105784, 253203}

......

===== Library Cache Lock (9) <<< kgllkhdl: 9EF37618, kgllkmod 1, kglnaobj: DBMS_SESSIONSYS>>>=====
===== Library Cache Lock (10) <<< kgllkhdl: 9EF30B90, kgllkmod 1, kglnaobj: alter session set nls_territory = AMERICA>>>=====
===== Library Cache Lock (11) <<< kgllkhdl: 9FAF9F50, kgllkmod 1, kglnaobj: alter session set nls_language = AMERICAN>>>=====
===== Library Cache Lock (12) <<< kgllkhdl: 9FAF8A28, kgllkmod 1, kglnaobj: alter session set nls_sort = BINARY>>>=====
===== Library Cache Lock (13) <<< kgllkhdl: A069FC50, kgllkmod 1, kglnaobj: alter session set nls_numeric_characters = '.,'>>>=====

......

Breakpoint 9, 12d7f230 in kglGetMutex ()
=====--- kglGetMutex (51) ---> Mutex addr (rsi): A1CA99D0, Location(r8d): 57
#0  12d7f230 in kglGetMutex ()
#1  04c9de7c in kglLockCursor ()

Breakpoint 10, 12da4830 in kgxExclusive ()
=====----- kgxExclusive (37) ---> Mutex addr (rsi): A1CA99D0

Breakpoint 8, 12d76c90 in kgllkal ()
===== Library Cache Lock (14) <<< kgllkhdl: A1CA9880, kgllkmod 1, kglnaobj: COMMIT>>>=====
#0  12d76c90 in kgllkal ()
#1  04c9df43 in kglLockCursor ()

Breakpoint 7, 012a0700 in kss_grow_from_global_cache ()
===== kss_grow_from_global_cache (1) <<  (r14): B7F27168, kss private so Chunk Addr (r14-4112): B7F26158 >>>=====
#0  012a0700 in kss_grow_from_global_cache ()
#1  1260c5ca in kss_add_child ()

Breakpoint 3, 125c9ba0 in kslgetl ()
===== kslgetl so private (1) <<< Addr(rdi): B626A6F0, Imget: 0, Why: 0, Where: 290 >>>=====
#0  125c9ba0 in kslgetl ()
#1  012a0844 in kss_grow_from_global_cache ()
#2  1260c5ca in kss_add_child ()
#3  12d7e4a5 in kglGetSO ()
#4  12d76dbf in kgllkal ()
#5  04c9df43 in kglLockCursor ()
#6  035b5e3f in kkspbd0 ()
#7  12a3bc9a in kksParseCursor ()

Breakpoint 4, 125cf6c0 in kslfre ()
===== kslfre so private (1) <<< Addr(rdi): B626A6F0 >>>=====
#0  125cf6c0 in kslfre ()
#1  012a088b in kss_grow_from_global_cache ()

Breakpoint 11, 12da5850 in kgxRelease ()
=====----- kgxRelease (37) ---> Mutex addr (r15): A1CA99D0 
$37 = {0, 909, 1253713, 34977169}

......

Breakpoint 8, 12d76c90 in kgllkal ()
===== Library Cache Lock (15) <<< kgllkhdl: 95589568, kgllkmod 1, kglnaobj: alter session set nls_timestamp_format = 'YYYY-MM-DD"T"HH24:MI:SS'>>>=====
#0  12d76c90 in kgllkal ()
#1  12d72627 in kglLock ()
The above output shows that latch get/free is fully contained within mutex get/release:

(1). kgxExclusive mutex get   A127C888
            kslgetl latch get   60560A38 (Imget: 1 for non IMMEDIATE_GETS)
            kslfre  latch free  60560A38
     kgxRelease mutex release A127C888 

(2). kgxExclusive mutex get   A1CA99D0
            kslgetl latch get   B626A6F0 (Imget: 0 for IMMEDIATE_GETS)
            kslfre  latch free  B626A6F0
     kgxRelease mutex release A1CA99D0 
The first latch get 60560A38 is triggered by kghalo to allocate shared pool memory.
Between kgxExclusive (mutex get) and kslgetl (latch get), there is one kgllkal (Library Cache Lock Allocate).
So the call sequence to allocate shared pool memory is kgxExclusive -> kgllkal -> kslgetl.

The second latch get B626A6F0 is triggered by kss_grow_from_global_cache to update (or insert) "SO private sga" memory component.

With following query, we can find the touched memory chunk for kss_grow_from_global_cache (r14): B7F27168 in "SO private sga".

select v.*, to_number('B7F27168', 'xxxxxxxx') - to_number(ksmchptr, 'xxxxxxxxxxxxxxxx') offset
  from x$ksmsp v 
 where ksmchcom in ('SO private sga') 
   and to_number('B7F27168', 'xxxxxxxx') between to_number(ksmchptr, 'xxxxxxxxxxxxxxxx') 
   and to_number(ksmchptr, 'xxxxxxxxxxxxxxxx') + ksmchsiz-1;
   
ADDR               INDX INST_ID  CON_ID KSMCHIDX   KSMCHDUR KSMCHCOM        KSMCHPTR         KSMCHSIZ KSMCHCLS KSMCHTYP KSMCHPAR         OFFSET
---------------- ------ ------- ------- -------- ---------- -------------- ----------------- -------- -------- -------- ---------------- -------
00007EFEA3952810 117341       1       0        2          1 SO private sga  00000000B7EF4878  1048536 recr     4095     0000000060006C98  207088
The trace log also contains Library Cache Locks/Pins (kglLock/kglpin), Mutex Gets/Releases (kgxExclusive and kgxRelease). For details, see Blog: Oracle PLITBLM "library cache: mutex X".


4. "latch: shared pool" and "library cache: mutex X" Blocking Test


Based on above tracing output, we can make two blocking wait tests.
One is with kslfre to block "latch: shared pool" get,
another is with kss_grow_from_global_cache to block "SO private sga" update.


4.1 kslfre Blocking Test


In this blocking test, we will show two wait events: "latch: shared pool", "library cache: mutex X", and then look their respective code path.

First we start 4 Job sessions:

exec start_job_so(4);
Then make a new DB connection.

SQL> conn k/s@db19c
Connected.
Get its UNIX process id: 9558 (Oracle session id: 909)

Start tracing and set a breakpoint

gdb -p 9558

break kslfre if $rdi==0x60560A38 || $rdi==0x60560AD8 || $rdi==0x60560B78
Run the test:

SQL> exec so_private_pkg.proc1(1);
Resume process running in gdb. After a few seconds, we reached the breakpoint, and display call stack.

(gdb) c
Continuing.

Breakpoint 1, 0x125cf6c0 in kslfre ()
(gdb) bt 16
#0  0x125cf6c0 in kslfre ()
#1  0x1259dded in ksfflt ()
#2  0x12d3605c in kghalo ()
#3  0x12d33c24 in kghgex ()
#4  0x12d38442 in kghfnd ()
#5  0x12d36ae2 in kghalo ()
#6  0x12d7e6f8 in kglGetSO ()
#7  0x12d76dbf in kgllkal ()
#8  0x12d72627 in kglLock ()
#9  0x12d6d4b5 in kglget ()
#10 0x04c86f19 in kglgob ()
#11 0x04c878bd in kglgob ()
#12 0x12d9b0c0 in kgiind ()
#13 0x056d98ec in pfri8_inst_spec ()
#14 0x056d9714 in pfri1_inst_spec ()
#15 0x12daed50 in pfrrun ()
We can see that all Job sessions are blocked with "latch: shared pool" or "library cache: mutex X" by session 909. So one session can cause two different wait events in the blocked sessions. (Note: we started 4 Job sessions, but v$session shows 6 due to dbms_scheduler job delayed start/stop cleanup).

v$mutex_sleep_history shows mutex sleeping stats by BLOCKING_SESSION 909.

v$latchholder shows that SID 909 is holding shared pool CHILD# 1 latch 60560A38.

SQL> select program, event, sid, serial#, p1, p2raw, p3raw, final_blocking_session
    from v$session
    where lower(program) like '%sql%' or lower(program) like '%j0%'
    order by program;

PROGRAM                        EVENT                      SID    SERIAL#  P1         P2RAW            P3RAW            FINAL_BLOCKING_SESSION
------------------------------ ------------------------- ------ --------- ---------- ---------------- ---------------- ----------------------
oracle@db19c (J000)         latch: shared pool            1011    58459   1616251448 000000000000026B 0000000093FD6BD0                    909
oracle@db19c (J001)         library cache: mutex X         426    51552   3168695887 0000038D00000000 0000130A0001006A                    909
oracle@db19c (J002)         library cache: mutex X         372     7360   3168695887 0000038D00000000 0000130A0001006A                    909
oracle@db19c (J003)         latch: shared pool             122    30477   1616251448 000000000000026B 000000008C6E1038                    909
oracle@db19c (J004)         library cache: mutex X         277     6387   3168695887 0000038D00000000 0000130A0001006A                    909
oracle@db19c (J005)         library cache: mutex X         408    13814   3168695887 0000038D00000000 0000130A0001006A                    909
sqlplus@db19c (TNS V1-V3)   SQL*Net message from client    909    33954   1413697536 0000000000000001 00
        
SQL> select mutex_identifier, sleep_timestamp, mutex_type, gets, sleeps, requesting_session, blocking_session, mutex_value, p1raw, location
     from v$mutex_sleep_history
     where sleep_timestamp > sysdate -2/1440 order by sleep_timestamp desc;

MUTEX_IDENTIFIER SLEEP_TIMESTAMP MUTEX_TYPE         GETS  SLEEPS REQUESTING_SESSION BLOCKING_SESSION MUTEX_VALUE      P1RAW            LOCATION
---------------- --------------- --------------- ------- ------- ------------------ ---------------- ---------------- ---------------- ------------
      3168695887 01:10:44        Library Cache   7108586   41476                277              909 0000038D00000000 00000000A127C738 kglhdgn2 106
      3168695887 01:10:44        Library Cache   7108586   41457                426              909 0000038D00000000 00000000A127C738 kglhdgn2 106
      3168695887 01:10:44        Library Cache   7108586   41470                408              909 0000038D00000000 00000000A127C738 kglhdgn2 106
      3168695887 01:10:44        Library Cache   7108586   41447                372              909 0000038D00000000 00000000A127C738 kglhdgn2 106
              15 01:10:38        Row Cache         46638       1                 76              360 0000016800000000 00               [19] kqrpre

  -- 38D in MUTEX_VALUE and P2RAW are blocking session id: 909 (=0x38D).

SQL> select * from v$latchholder;

 PID   SID LADDR     NAME                            GETS  CON_ID
---- ----- --------- --------------------------- -------- -------
  35   909 60560A38  shared pool                 22844512       0
  47  1011 6005F500  parameter table management  17143486       0
For "library cache: mutex X", query below finds the library object (P1RAW and P1 in above query output). ADDR is its object_handle (see Blog: Oracle PLITBLM "library cache: mutex X").

SQL > select hash_value, addr, owner, name, namespace, type from v$db_object_cache where hash_value in (3168695887);

HASH_VALUE ADDR             OWNER  NAME         NAMESPACE            TYPE
---------- ---------------- ------ ------------ -------------------- ----------
3168695887 00000000A127C738 SYS    DBMS_SESSION TABLE/PROCEDURE      PACKAGE
Now we can have a further look of two different wait events.


4.1.1 "library cache: mutex X" Wait


Above gdb trace shows that shared pool latch Get/Free is fully contained within mutex Get/Release. If the blocked session comes in the same code path, it is blocked by "library cache: mutex X" because it first invokes kglGetMutex (kgxExclusive) to get the same mutex.

Here is what diag LWS db19c_dia0_30593_lws_1.trc showed for Session ID 277. The Short stack dump shows kglGetMutex and kgxExclusive calls.

*** 2021-05-12T01:04:22.093264+02:00
HM: Early Warning - Session ID 277 serial# 6387 OS PID 5888 (J004)
     is waiting on 'library cache: mutex X' for 32 seconds, wait id 62
     p1: 'idn'=0xbcde764f, p2: 'value'=0x38d00000000, p3: 'where'=0x130a0001006a
    Final Blocker is Session ID 909 serial# 33954 on instance 1
     which is 'not in a wait' for 60 seconds
 
 Total  Self-         Total  Total  Outlr  Outlr  Outlr           
  Hung  Rslvd  Rslvd   Wait WaitTm   Wait WaitTm   Wait           
  Sess  Hangs  Hangs  Count   Secs  Count   Secs  Count Wait Event
------ ------ ------ ------ ------ ------ ------ ------ -----------
  2632      0      0 812304 7729347  12122 7695264      0 library cache: mutex X
 
HM: Dumping Short Stack of pid[61.5888] (sid:277, ser#:6387)
Short stack dump: 
ksedsts()+426<-ksdxfstk()+58<-ksdxcb()+872<-sspuser()+200<-__sighandler()<-semtimedop()+10<-skgpwwait()+187
<-ksliwat()+2224<-kslwaitctx()+188<-kgxWait()+1304<-kgxExclusive()+712<-kglGetMutex()+151<-kglhdgn()+898
<-kglLock()+562<-kglget()+293<-kglgob()+281<-kglgob()+2749<-kgiind()+4256<-pfri8_inst_spec()+140<-pfri1_inst_spec()+68
<-pfrrun()+544<-plsql_run()+752<-peicnt()+279<-kkxexe()+720<-opiexe()+25325<-kpoal8()+2387<-opiodr()+1202
<-kpoodr()+689<-upirtrc()+2760<-kpurcsc()+100<-kpuexec()+10994<-OCIStmtExecute()+41<-jslvec_execcb()+2537
<-jslvswu()+409<-jslvCDBSwitchUsr()+672<-jslve_execute0()+6761<-jslve_execute()+1529<-jslve_cdb_execute()+112
<-rpiswu2()+2004<-kkjex1e_cdb()+222<-kkjsexe()+2333<-kkjrdp()+1588<-opirip()+889<-opidrv()+581<-sou2o()+165
<-opimai_real()+173<-ssthrdmain()+417<-main()+256<-__libc_start_main()+245
Here oradebug short_stack for 'library cache: mutex X' wait:

SQL> oradebug setorapid 61  
Oracle pid: 61, Unix process pid: 5888, image: oracle@db19c (J004)

SQL> oradebug short_stack
ksedsts()+426<-ksdxfstk()+58<-ksdxcb()+872<-sspuser()+200<-__sighandler()<-semtimedop()+10<-skgpwwait()+187<-ksliwat()+2224
<-kslwaitctx()+188<-kgxWait()+1304<-kgxExclusive()+712<-kglGetMutex()+151<-kglhdgn()+898<-kglLock()+562<-kglget()+293<-kglgob()+281
<-kglgob()+2749<-kgiind()+4256<-pfri8_inst_spec()+140<-pfri1_inst_spec()+68<-pfrrun()+544<-plsql_run()+752<-peicnt()+279
<-kkxexe()+720<-opiexe()+25325<-kpoal8()+2387<-opiodr()+1202<-kpoodr()+689<-upirtrc()+2760<-kpurcsc()+100<-kpuexec()+10994
<-OCIStmtExecute()+41<-jslvec_execcb()+2537<-jslvswu()+409<-jslvCDBSwitchUsr()+672<-jslve_execute0()+6761<-jslve_execute()+1529
<-jslve_cdb_execute()+112<-rpiswu2()+2004<-kkjex1e_cdb()+222<-kkjsexe()+2333<-kkjrdp()+1588<-opirip()+889<-opidrv()+581
<-sou2o()+165<-opimai_real()+173<-ssthrdmain()+417<-main()+256<-__libc_start_main()+245


4.1.2 "latch: shared pool" Wait


There is also other code path, which directly requests 'latch: shared pool' and does not require mutex get. Here is what diag LWS db19c_dia0_30593_lws_1.trc showed for Session ID 122. The Short stack dump does not have kglGetMutex and kgxExclusive calls.

*** 2021-05-12T01:04:43.183727+02:00
HM: Early Warning - Session ID 122 serial# 30477 OS PID 5785 (J003)
     is waiting on 'latch: shared pool' for 52 seconds, wait id 61
     p1: 'address'=0x60560a38, p2: 'number'=0x26b, p3: 'why'=0x8c6e1038
    Final Blocker is Session ID 909 serial# 33954 on instance 1
     which is 'not in a wait' for 61 seconds 
                                                     IO           
 Total  Self-         Total  Total  Outlr  Outlr  Outlr           
  Hung  Rslvd  Rslvd   Wait WaitTm   Wait WaitTm   Wait           
  Sess  Hangs  Hangs  Count   Secs  Count   Secs  Count Wait Event
------ ------ ------ ------ ------ ------ ------ ------ -----------
    20      0      0  36534   9032     19   8544      0 latch: shared pool
 
HM: Dumping Short Stack of pid[60.5785] (sid:122, ser#:30477)
Short stack dump: 
ksedsts()+426<-ksdxfstk()+58<-ksdxcb()+872<-sspuser()+200<-__sighandler()<-semop()+7<-skgpwwait()+187<-kslges()+1534
<-kslgetl()+2489<-ksfglt()+163<-kghfre()+3985<-ksuxds()+1061<-kss_del_cb()+218<-kssdel()+216<-ksudel_int()+280
<-ksudel()+68<-kkjrdp()+2207<-opirip()+889<-opidrv()+581<-sou2o()+165<-opimai_real()+173<-ssthrdmain()+417
<-main()+256<-__libc_start_main()+245
Here oradebug short_stack for 'latch: shared pool' wait:

SQL> oradebug setorapid 60 
Oracle pid: 60, Unix process pid: 5785, image: oracle@db19c (J003)
SQL> oradebug short_stack
ksedsts()+426<-ksdxfstk()+58<-ksdxcb()+872<-sspuser()+200<-__sighandler()<-semop()+7<-skgpwwait()+187<-kslges()+1534<-kslgetl()+2489
<-ksfglt()+163<-kghfre()+3985<-ksp_param_handle_free()+779<-kspdesc()+142<-ksmugf()+208<-ksuxds()+3727<-kss_del_cb()+218
<-kssdel()+216<-ksudel_int()+280<-ksudel()+68<-kkjrdp()+2207<-opirip()+889<-opidrv()+581<-sou2o()+165<-opimai_real()+173
<-ssthrdmain()+417<-main()+256<-__libc_start_main()+245
In fact, if we use the same gdb script to trace one Job session, we can see the occurrence of kslgetl / kslfre on 60560A38 which is not contained within kglGetMutex kgxExclusive / kgxRelease.

Breakpoint 2, 0x125cf6c0 in kslfre ()
===== kslfre shared latch (14) <<< Addr(rdi): 60560B78 >>>=====
#0  0x125cf6c0 in kslfre ()
#1  0x1259dded in ksfflt ()

Breakpoint 1, 0x125c9ba0 in kslgetl ()
===== kslgetl shared latch (15) <<< Addr(rdi): 60560A38, Imget: 1, Why: 7165CBE8, Where: 6298 >>>=====
#0  0x125c9ba0 in kslgetl ()
#1  0x1259da43 in ksfglt ()
#2  0x12d2c341 in kghfre ()
#3  0x011380eb in ksp_param_handle_free ()
#4  0x01137d7e in kspdesc ()
#5  0x010f7990 in ksmugf ()
#6  0x1260e4cf in ksuxds ()
#7  0x1260503a in kss_del_cb ()

Breakpoint 2, 0x125cf6c0 in kslfre ()
===== kslfre shared latch (15) <<< Addr(rdi): 60560A38 >>>=====
#0  0x125cf6c0 in kslfre ()
#1  0x1259dded in ksfflt ()

Breakpoint 1, 0x125c9ba0 in kslgetl ()
===== kslgetl shared latch (16) <<< Addr(rdi): 60560AD8, Imget: 1, Why: 7D84F9C0, Where: 6298 >>>=====
db19c_dia0_30593_vfy_12.trc shows blocking graph for both wait events of two above sessions, which are blocked by session id: 909, and session id: 909 itself is in state 'CPU or Wait CPU' (although the fact is that session id: 909 is suspended by our manual breakpoint and in Process Status: t (stopped by debugger during trace) with %CPU=0.0, which does not consume any CPU).

*** 2021-05-12T01:05:52.275135+02:00
-------------------------------------------------------------------------------
Chain 1:
-------------------------------------------------------------------------------
    Oracle session identified by:
    {
                   os id: 5785
              process id: 60, oracle@db19c (J003)
              session id: 122
    }
    is waiting for 'latch: shared pool' with wait info:
    {
                      p1: 'address'=0x60560a38
                      p2: 'number'=0x26b
                      p3: 'why'=0x8c6e1038
            time in wait: 2 min 0 sec           
    }
    and is blocked by
 => Oracle session identified by:
    {
                   os id: 9558
              process id: 35, oracle@db19c
              session id: 909
             module name: 0 (SQL*Plusdb19c (TNS V1-V3))
    }
    which is on CPU or Wait CPU:
    {
               last wait: 2 min 30 sec ago
                blocking: 7 sessions
    }
 
Chain 1 Signature: 'CPU or Wait CPU'<='latch: shared pool'
===============================================================================
Chain 2:
-------------------------------------------------------------------------------
    Oracle session identified by:
    {
                   os id: 5888
              process id: 61, oracle@db19c (J004)
              session id: 277
    }
    is waiting for 'library cache: mutex X' with wait info:
    {
                      p1: 'idn'=0xbcde764f
                      p2: 'value'=0x38d00000000
                      p3: 'where'=0x130a0001006a
            time in wait: 2 min 1 sec
           timeout after: never
    }
    and is blocked by 'instance: 1, os id: 9558, session id: 909',
    which is a member of 'Chain 1'.


4.2 "SO private sga" kss_grow_from_global_cache Blocking Test


In this blocking test, only wait event: "library cache: mutex X" can be observed. We will also look its code path.

Make a new DB connection.

SQL> conn k/s@db19c
Connected.
Get its UNIX process id: 789 (Oracle session id: 909)

Start tracing and set a breakpoint

gdb -p 789

break kss_grow_from_global_cache
Run the test:

SQL> exec so_private_pkg.proc1(1);
Resume process running. After a few seconds, we reached the breakpoint, and display call stack.

(gdb) c
Continuing.

Breakpoint 1, 0x12a0700 in kss_grow_from_global_cache ()
(gdb) bt 21
#0  0x012a0700 in kss_grow_from_global_cache ()
#1  0x1260c5ca in kss_add_child ()
#2  0x12d7e4a5 in kglGetSO ()
#3  0x12d76dbf in kgllkal ()
#4  0x04c9df43 in kglLockCursor ()
#5  0x035b5e3f in kkspbd0 ()
#6  0x12a3bc9a in kksParseCursor ()
#7  0x12c11736 in opiosq0 ()
#8  0x129a7280 in opipls ()
#9  0x12990c52 in opiodr ()
#10 0x12aae556 in rpidrus ()
#11 0x12d501a1 in skgmstack ()
#12 0x12aae0d4 in rpidru ()
#13 0x12aad12f in rpiswu2 ()
#14 0x12aac4d2 in rpidrv ()
#15 0x12a89a83 in psddr0 ()
#16 0x12a88eb0 in psdnal ()
#17 0x12dbcfe2 in pevm_EXECC ()
#18 0x12db1a68 in pfrinstr_EXECC ()
#19 0x12db0544 in pfrrun_no_tool ()
#20 0x12daeeb6 in pfrrun ()
We can see that all Job sessions are blocked with "library cache: mutex X" by session 909. v$mutex_sleep_history shows mutex sleeping stats by BLOCKING_SESSION 909. (v$latchholder returns no rows because kslgetl is not yet invoked).

SQL> select program, event, sid, serial#, p1, p2raw, p3raw, final_blocking_session
    from v$session
    where lower(program) like '%sql%' or lower(program) like '%j0%'
    order by program;

PROGRAM                        EVENT                 SID   SERIAL#   P1        P2RAW            P3RAW            FINAL_BLOCKING_SESSION
------------------------------ -------------------- ------ --------- --------- ---------------- ---------------- ----------------------
oracle@db19c (J000)         library cache: mutex X    996     38685  255718823 0000038D00000000 0F3DF5A700000039                    909
oracle@db19c (J002)         library cache: mutex X    372     57666  255718823 0000038D00000000 0F3DF5A700000039                    909
oracle@db19c (J003)         library cache: mutex X    122     13135  255718823 0000038D00000000 0F3DF5A700000039                    909
oracle@db19c (J007)         library cache: mutex X   1011     38406  255718823 0000038D00000000 0F3DF5A700000039                    909
sqlplus@db19c (TNS V1-V3)   PGA memory operation      909     25675      65536 0000000000000001 00               

SQL> select mutex_identifier, sleep_timestamp, mutex_type, gets, sleeps, requesting_session, blocking_session, mutex_value, p1raw, location
     from v$mutex_sleep_history
     where sleep_timestamp > sysdate -2/1440 order by sleep_timestamp desc;

MUTEX_IDENTIFIER SLEEP_TIMESTAMP MUTEX_TYPE        GETS  SLEEPS REQUESTING_SESSION BLOCKING_SESSION MUTEX_VALUE      P1RAW            LOCATION
---------------- --------------- -------------  ------- ------- ------------------ ---------------- ---------------- ---------------- ---------------
       255718823 01:18:57        Library Cache  1254886    5155               1011              909 0000038D00000000 00000000A1CA9880 kgllkc1   57
       255718823 01:18:57        Library Cache  1254886    5145                122              909 0000038D00000000 00000000A1CA9880 kgllkc1   57
       255718823 01:18:57        Library Cache  1254886    5136                996              909 0000038D00000000 00000000A1CA9880 kgllkc1   57
       255718823 01:18:57        Library Cache  1254886    5141                372              909 0000038D00000000 00000000A1CA9880 kgllkc1   57
               0 01:17:36        Row Cache      4330026       1                122             1011 000003F300000000 00               [19] kqrpre

SQL> select * from v$latchholder;
  no rows selected


4.3 AWR Report


In AWR Section - Latch Sleep Breakdown, we can see stats of both "shared pool" and "SO private so latch".


Latch Sleep Breakdown

Latch Name                         Get Requests	 Misses  Sleeps Spin Gets
---------------------------------- ------------ ------- ------- ---------
cache buffers chains                 20,218,430   3,052     379     2,980
shared pool                             756,911   2,208     364     1,849
SO private so latch                      30,950      34       8	       27
kokc descriptor allocation latch            534       5       7         4
In Section - Latch Miss Sources, "SO private so latch" is displayed with correct Latch Name.

However, no Latch Name "shared pool" can be found. Probably it is re-named as "unknown". The location "Where" clearly shows that "kghalo" and "kghfre" (heap manager allocation/free). The sum of Sleeps for "unknown latch" is almost same as Sleeps (364) in above Section - Latch Sleep Breakdown.


Latch Miss Sources

Latch Name            Where                       NoWait Misses    Sleeps   Waiter Sleeps
--------------------- --------------------------- --------------  --------  -------------
SO private so latch   kss_grow_from_global_cache               0         7              0
SO private so latch   kss_shrink_private_so_list               0         1              8

unknown latch         kghalo                                   0       303            264
unknown latch         kghfre                                   0        31             78
unknown latch         kghupr1                                  0        15             12
unknown latch         kghalp                                   0         5              9
unknown latch         kgh_heap_sizes                           0         5              1
unknown latch         kghfnd: req scan                         0         2              0
Here the shared pool Child Latch Stats:

Child Latch Statistics

Latch Name    Child Num   Get Requests   Misses   Sleeps   Spin & Sleeps 1->3+
-----------  ----------  -------------  -------  -------  --------------------
shared pool           3        265,194      823      127      696/0/0/0
shared pool           2        256,577      775      149      629/0/0/0
shared pool           1        235,275      610       88      524/0/0/0
For other discussions of latch stats ("Get Requests", "Misses", "Sleeps", "Spin Gets"), see Blog: Is latch misses statistic gathered or deduced ?


4.4 "row cache mutex" Wait


diag LWS db19c_dia0_30593_lws_1.trc also shows "row cache mutex" Wait for 'cache id'=0xa (dc_users), which also involves 'latch: shared pool' ('address'=0x60560a38)

*** 2021-05-12T06:48:46.642290+02:00
HM: Early Warning - Session ID 277 serial# 6298 OS PID 7016 (J005)
     is waiting on 'row cache mutex' for 37 seconds, wait id 277
     p1: 'cache id'=0xa, p2: 'where requested'=0x13, p3: ''=0x0
    Blocked by Session ID 996 serial# 21988 on instance 1
     which is waiting on 'latch: shared pool' for 31 seconds
     p1: 'address'=0x60560a38, p2: 'number'=0x26b, p3: 'why'=0x0
    Final Blocker is Session ID 599 serial# 13104 on instance 1
     which is 'not in a wait' for 32 seconds
    Session ID 277 is blocking 2 sessions
    Blocking Session ID 765 serial# 16220 on instance 1
     which is waiting on 'row cache mutex' for 21 seconds
     p1: 'cache id'=0xa, p2: 'where requested'=0x13, p3: ''=0x0
                                                     IO           
 Total  Self-         Total  Total  Outlr  Outlr  Outlr           
  Hung  Rslvd  Rslvd   Wait WaitTm   Wait WaitTm   Wait           
  Sess  Hangs  Hangs  Count   Secs  Count   Secs  Count Wait Event
------ ------ ------ ------ ------ ------ ------ ------ -----------
    51      0      0 473776  30128    198  19008      0 row cache mutex
------------------------------------------

HM: Dumping Short Stack of pid[61.7016] (sid:277, ser#:6298)
Short stack dump: 
ksedsts()+426<-ksdxfstk()+58<-ksdxcb()+872<-sspuser()+200<-__sighandler()<-semtimedop()+10<-skgpwwait()+187<-ksliwat()+2224
<-kslwaitctx()+188<-kgxWait()+1304<-kgxExclusive()+712<-kqrGetPOMutexInt()+195<-kqrpre1()+792<-jsksGetDBObjectName()+771
<-jslvepost_exec_post()+1020<-jslvsst_session_stop()+5631<-jslve_execute0()+7742<-jslve_execute()+1529<-jslve_cdb_execute()+112
<-rpiswu2()+2004<-kkjex1e_cdb()+222<-kkjsexe()+2333<-kkjrdp()+1588<-opirip()+889<-opidrv()+581<-sou2o()+165<-opimai_real()+173
<-ssthrdmain()+417<-main()+256<-__libc_start_main()+245


5 Appendix gdb_latch_script_3.txt



set pagination off
set logging file latch_output_3.log
set logging overwrite on
set logging on
set $socg = 0
set $shcg = 0
set $socf = 0
set $shcf = 0
set $spec_lck = 0
set $spec_pin = 0
set $body_lck = 0
set $body_pin = 0
set $mutex_get = 0
set $mutex_ex = 0
set $mutex_fr = 0
set $mutex_addr = 0x0
set $body_locked = 0
set $sogrow = 0
set $i = 0

# Adjust beakpoint conditions
#   select name, listagg('$rdi==0X'||trim(leading 0 from addr), ' || ') within group (order by child#) addr
#    from v$latch_children where name in ('shared pool', 'SO private so latch') and child# <=3 group by name;
#   NAME                 ADDR
#   -------------------- ----------------------------------------------------
#   shared pool          $rdi==0X60560A38 || $rdi==0X60560AD8 || $rdi==0X60560B78
#   SO private so latch  $rdi==0xB626A638 || $rdi==0xB626A6F0 || $rdi==0xB626A7A8

# -- Usage: (1). make a trace new connection, (2). start gdb trace, (3). run a sql command, (4). stop gdb trace, (5). look trace output
# SQL> conn k/s@testdb
#      Connected.
# -- get spid (17191), gdb -x gdb_latch_script_3.txt -p 17191
# SQL> exec so_private_pkg.proc1(1);


break kslgetl if $rdi==0x60560A38 || $rdi==0x60560AD8 || $rdi==0x60560B78
commands
printf "===== kslgetl shared latch (%i) <<< Addr(rdi): %X, Imget: %i, Why: %X, Where: %i >>>=====\n", ++$shcg, $rdi, $rsi, $rdx, $rcx
backtrace 8
continue
end

break kslfre if $rdi==0x60560A38 || $rdi==0x60560AD8 || $rdi==0x60560B78
commands
printf "===== kslfre shared latch (%i) <<< Addr(rdi): %X >>>=====\n", ++$shcf, $rdi
backtrace 2
continue
end

break kslgetl if $rdi==0xB626A638 || $rdi==0xB626A6F0 || $rdi==0xB626A7A8
commands
printf "===== kslgetl so private (%i) <<< Addr(rdi): %X, Imget: %i, Why: %X, Where: %i >>>=====\n", ++$socg, $rdi, $rsi, $rdx, $rcx
backtrace 8
continue
end

break kslfre if $rdi==0xB626A638 || $rdi==0xB626A6F0 || $rdi==0xB626A7A8
commands
printf "===== kslfre so private (%i) <<< Addr(rdi): %X >>>=====\n", ++$socf, $rdi
backtrace 2
continue
end
    
break ksl_get_shared_latch if $rdi==0x60560A38 || $rdi==0x60560AD8 || $rdi==0x60560B78
commands
printf "===== ksl_get_shared_latch shared latch (%i) <<< addr(rdi): %X, Imget: %i, Why: %X, Where: %i, Mode: %X >>>=====\n", ++$i, $rdi, $rsi, $rdx, $rcx, $r8
backtrace 8
continue
end

break ksl_get_shared_latch if $rdi==0xB626A638 || $rdi==0xB626A6F0 || $rdi==0xB626A7A8
commands
printf "===== ksl_get_shared_latch SO latch (%i) <<< addr(rdi): %X, Imget: %i, Why: %X, Where: %i, Mode: %X >>>=====\n", ++$i, $rdi, $rsi, $rdx, $rcx, $r8
backtrace 8
continue
end

# Most "kss private so " has size 5136 in shared_poo dump like:   0b7f17b18 sz=     5136    cprm      "kss private so "
# Find Chunk which covers $r14. Offset 4112 to $r14 is only an example.
break kss_grow_from_global_cache
commands
printf "===== kss_grow_from_global_cache (%i) <<  (r14): %X, kss private so Chunk Addr (r14-4112): %X >>>=====\n", ++$sogrow, $r14, ($r14-4112)
backtrace 10
continue
end

break kgllkal 
#break kgllkal if $rdx==0X9FB08E08 || $rdx==0XA043B758
commands
printf "===== Library Cache Lock (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s>>>=====\n", ++$body_lck, $rdx, $rcx, ($rdx+0x1c0)
backtrace 8 
continue
end

break kglGetMutex if $body_lck > 0 
command 
printf "=====--- kglGetMutex (%i) ---> Mutex addr (rsi): %X, Location(r8d): %d\n", ++$mutex_get, $rsi, $r8d
backtrace 4
continue
end

break kgxExclusive if $body_lck > 0 
#break kgxExclusive if $r9==0X9EC403D0 && $body_lck > 0 
command 
printf "=====----- kgxExclusive (%i) ---> Mutex addr (rsi): %X\n", ++$mutex_ex, $rsi
#backtrace 4
set $mutex_addr = $rsi  
continue
end

break kgxRelease if $r15==$mutex_addr && $body_lck > 0 
command 
printf "=====----- kgxRelease (%i) ---> Mutex addr (r15): %X \n", ++$mutex_fr, $r15 
p/d (int[4])*$r15
# x/4dw $r15     
continue
end

Sunday, April 11, 2021

Oracle PLITBLM "library cache: mutex X"

PLITBLM is Oracle kernel package for the operations of collections (associative array, nested table and varray array).

In this Blog, we first show Oracle 19c new behaviour of PLITBLM on library cache locks and pins in comparing to Oracle 12c, and then demonstrate its consequence of "library cache: mutex X" in Oracle 19c.

After the tests, we first compose gdb scripts to trace PLITBLM Library Cache Locks and Pins (kglLock and kglpin). Then we further compose gdb scripts to reveal the calls of the triggered kgxExclusive and kgxRelease for mutex request and release.

We conclude the Blog with some rough estimations of PLITBLM execution duration and throughput.

As a further study, we also try to list all the calls of Mutex kgxExclusive and kgxRelease.

Note: Tested in Oracle 19c (19.9) and 12c(12.1.0.2.0).

Update (29Jun2021): With test code of this Blog, Oracle delivered fix: "Patch 32831855: NESTED TABLE CONSUMES MORE TIME IN 19C".


1. Test Setup


We create two procedures, one is coded in static, another in dynamic with execute immediate. Both are using a simple nested table.

create or replace procedure test_proc_static(p_cnt_1 number, p_cnt_2 number := 100) as
begin
  for i in 1..p_cnt_1 loop
	  declare
	    type t_num_tab   is table of number;
	    l_num_tab        t_num_tab   := t_num_tab();
	    l_val            number;
	    l_bool           boolean;
	  begin
	    for i in 1..p_cnt_2 loop
	      l_num_tab.extend;
	      l_num_tab(i) := i;   
	      l_val        := l_num_tab.count;
	      l_val        := l_num_tab(i);
	      l_val        := l_num_tab.first;
	      l_val        := l_num_tab.prior(i);
	      l_bool       := l_num_tab.exists(i);
	    end loop;
	      l_num_tab.delete(2);
	  end;
	end loop;
end;
/

create or replace procedure test_proc_dynamic(p_cnt_1 number, p_cnt_2 number := 100) as
begin
  for i in 1..p_cnt_1 loop
    execute immediate '
	    declare
	      type t_num_tab   is table of number;
	      l_num_tab        t_num_tab   := t_num_tab();
	      l_val            number;
	      l_bool           boolean;
	    begin
	      for i in 1..' ||p_cnt_2|| ' loop
	        l_num_tab.extend;
	        l_num_tab(i) := i;   
	        l_val        := l_num_tab.count;
	        l_val        := l_num_tab(i);
	        l_val        := l_num_tab.first;
	        l_val        := l_num_tab.prior(i);
	        l_bool       := l_num_tab.exists(i);
	      end loop;
	        l_num_tab.delete(2);
	    end;' ;
	end loop;
end;
/
And also create a job launcher for later "library cache: mutex X" concurrency test.

create or replace procedure test_job(p_type varchar2, p_job_cnt number, p_cnt_1 number :=10, p_cnt_2 number := 100, p_job_start_nr number := 0) as
  l_call_proc varchar2(20);
begin
  case p_type 
    when 'static'  then l_call_proc := 'test_proc_static';
    when 'dynamic' then l_call_proc := 'test_proc_dynamic';
  end case;
  
  for i in 1..p_job_cnt loop
    dbms_scheduler.create_job (
      job_name        => 'TEST_JOB_'||p_type||'_'||(p_job_start_nr + i),
      job_type        => 'PLSQL_BLOCK',
      job_action      => 'begin '||l_call_proc|| '('||p_cnt_1||','||p_cnt_2||'); end;',
      start_date      => systimestamp,
      repeat_interval => 'systimestamp',
      auto_drop       => true,
      enabled         => true);
  end loop;
end;
/


2. Library Cache Locks and Pins Test


First we run a small test and look the objects named as "PLITBLM":

exec test_proc_static(1);

select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
from v$db_object_cache v 
where name like 'PLITBLM' order by source;

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
APP.PLITBLM     TABLE/PROCEDURE  CURSOR     1153620443          0          0                1        1
PUBLIC.PLITBLM  TABLE/PROCEDURE  SYNONYM    1353169941          0          0               51       51
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          3          0               56       69
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               66       66
"APP.PLITBLM" is one non-existent object in application schema: APP.
"PUBLIC.PLITBLM" is a synonym.
Two "SYS.PLITBLM" are package spec and body, with HASH_VALUE: 4251528005 and 4039937844, which are the two main objects we are focused on.


2.1 Oracle 19c


In an Oracle 19c DB, we run both test_proc_static and test_proc_dynamic, and get library cache lock and pins stats:

select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
from v$db_object_cache v where hash_value in (4251528005, 4039937844);

exec test_proc_static(1e6, 1);  

select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
from v$db_object_cache v where hash_value in (4251528005, 4039937844);

exec test_proc_dynamic(1e6, 1); 

select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
from v$db_object_cache v where hash_value in (4251528005, 4039937844);
Here the output:

SQL(19c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
          from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               67       67
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          3          0               56       70

SQL(19c)> exec test_proc_static(1e6, 1);

  Elapsed: 00:00:00.84

SQL(19c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
          from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               70       70
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          3          0               57       74

SQL(19c)> exec test_proc_dynamic(1e6, 1);

  Elapsed: 00:00:12.75

SQL(19c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
          from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0        1,000,072     1,000,072
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          3          0               58     1,000,077
We can see that test_proc_static has locked_total and pinned_total almost not increased.

However test_proc_dynamic has noticeable differences:
  -. for PLITBLM BODY, locked_total and pinned_total increased (1,000,072 - 70), almost same as the number of run count (1e6).
  -. for PLITBLM SPEC, only PINNED_TOTAL increased (1,000,077 - 74), almost same as the number of run count (1e6).
  -. test_proc_static vs. test_proc_static elpased time are (00.84 vs. 12.75).


2.2 Oracle 12c


Repeat the same test in Oracle 12c. Here the output:

SQL(12c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               49       49
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          4          0               51       56

SQL(12c)> exec test_proc_static(1e6, 1);

  Elapsed: 00:00:00.57

SQL(12c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               51       51
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          4          0               53       59

SQL(12c)> exec test_proc_dynamic(1e6, 1);

  Elapsed: 00:00:07.46

SQL(12c)> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v where hash_value in (4251528005, 4039937844);

SOURCE          NAMESPACE        TYPE       HASH_VALUE      LOCKS       PINS     LOCKED_TOTAL     PINNED_TOTAL
--------------- ---------------- ---------- ---------- ---------- ---------- ---------------- ----------------
SYS.PLITBLM     BODY             CURSOR     4039937844          0          0               52       52
SYS.PLITBLM     TABLE/PROCEDURE  PACKAGE    4251528005          4          0               54       61
We can see that both test_proc_static and test_proc_dynamic have similar behaviour in Oracle 12c, in which locked_total and pinned_total almost not increased. test_proc_static vs. test_proc_static elpased time are (00.57 vs. 07.46).


3. "library cache: mutex X" Test


Now we will use dbms_scheduler to start a few job sessions and watch "library cache: mutex X" concurrency.


3.1 Oracle 19c


We start 64 job sessions with test_proc_dynamic, and look "library cache: mutex X".

exec test_job('dynamic', 64, 1e7, 1e4);
Here some monitoring queries and the output:

SQL(19c)> select sid, event, p1, p2, p3 
   from v$session where event in ('library cache: mutex X', 'library cache: bucket mutex X');

  SID EVENT                                   P1                P2                      P3
----- ------------------------------ ----------- ----------------- -----------------------
   14 library cache: mutex X          4251528005     1632087572480          20383914852447
   15 library cache: mutex X          4251528005      854698491904          20383914852447
   16 library cache: mutex X          4039937844      854698491904    18446744069414715498
   17 library cache: bucket mutex X        36660       98784247808                      62
   18 library cache: mutex X          4039937844      833223655424    18446744069414715498
  ...
  918 library cache: mutex X          4039937844      854698491904    18446744069414715498
  919 library cache: bucket mutex X        36660       98784247808                      62

48 rows selected.
We can see that "library cache: mutex X" P1: 4039937844(0XF0CC8F34) is mapped to "library cache: bucket mutex X" P1: 36660 (0X8F34) by hash function:

  mod(4039937844, power(2, 17)) = 36660
Each "bucket mutex X" is protecting 32768 (=power(2, 32)/power(2, 17)) "library cache mutex".

  _kgl_bucket_count         Library cache hash table bucket count (2^_kgl_bucket_count * 256)        Default: 9
  (_kgl_bucket_count = 2^(9+8) = 2^17).

-- Only top 5 showed
SQL(19c)> select * from v$mutex_sleep where mutex_type in ('Library Cache') order by sleeps desc;

MUTEX_TYPE       LOCATION             SLEEPS  WAIT_TIME     CON_ID
---------------- ---------------- ---------- ---------- ----------
Library Cache    kglhdgn2 106          37602 1280758050          0
Library Cache    kglpndl1  95          33147 1243018303          0
Library Cache    kglhdgn1  62           4129  129689067          0
Library Cache    kglpin1   4            1085   75769882          0
Library Cache    kglpnal1  90            270   25476440          0

SQL(19c)> select mutex_identifier, sleep_timestamp, mutex_type, gets, sleeps
                ,requesting_session, blocking_session, location
   from v$mutex_sleep_history
  where mutex_identifier in (4251528005, 4039937844) and sleep_timestamp > sysdate-1/1440 and sleeps > 3
  order by sleep_timestamp desc, location;

MUTEX_IDENTIFIER SLEEP_TIME MUTEX_TYPE           GETS  SLEEPS REQUESTING_SESSION BLOCKING_SESSION LOCATION
---------------- ---------- -------------- ---------- ------- ------------------ ---------------- ------------
      4039937844 21:27:36   Library Cache    11363330       4                 15              555 kglhdgn2 106
      4251528005 21:27:33   Library Cache     6807823       4                913              201 kglpndl1  95
      4251528005 21:27:33   Library Cache     6807823       4                912              201 kglpin1   4
      4251528005 21:27:33   Library Cache     6807823       4                914              201 kglpndl1  95
      4251528005 21:27:33   Library Cache     6807823       4                193              201 kglpndl1  95
      4039937844 21:27:19   Library Cache    11262862       5                919              560 kglhdgn2 106
      4039937844 21:27:19   Library Cache    11262705       4                 20               19 kglhdgn2 106
      4251528005 21:27:16   Library Cache     6749768       4                555               24 kglpndl1  95
      4251528005 21:27:16   Library Cache     6749768       4                918               24 kglpndl1  95

SQL(19c)> select chain_signature, sid, p1 
   from v$wait_chains where p1 in (4251528005, 4039937844) or chain_signature = 'library cache: mutex X';

CHAIN_SIGNATURE                  SID         P1
------------------------- ---------- ----------
'library cache: mutex X'          23 4251528005
'library cache: mutex X'         201 4039937844
'library cache: mutex X'         382 4251528005
'library cache: mutex X'         385 4251528005
'library cache: mutex X'         561 4251528005
'library cache: mutex X'         562 4251528005
After the test, we stop all jobs by (see appended code):

exec clearup_test;  
If we also start 64 job sessions with test_proc_static, there is no "library cache: mutex X" observed.

exec test_job('static', 64, 1e7, 1e4);
With following query, we can list mutex 'BLOCKING_SESSION' and 'REQUESTING_SESSION':

select 'BLOCKING_SESSION'   sess, program, event, mod(s.p1, power(2, 17)) "buckt muext(child_latch)", s.p1, s.p2, s.p3, s.sql_id, q.sql_text, m.*, s.*, q.*
from v$session s, v$mutex_sleep_history m, v$sqlarea q
where s.sid = m.blocking_session and s.sql_id = q.sql_id and m.sleep_timestamp > sysdate-5/1440 and m.sleeps > 3
union all
select 'REQUESTING_SESSION' sess, program, event, mod(s.p1, power(2, 17)) "buckt muext(child_latch)", s.p1, s.p2, s.p3, s.sql_id, q.sql_text, m.*, s.*, q.*
from v$session s, v$mutex_sleep_history m, v$sqlarea q
where s.sid = m.requesting_session and s.sql_id = q.sql_id and m.sleep_timestamp > sysdate-5/1440 and m.sleeps > 3;
The query below groups ‘'library cache: bucket mutex X' blocked 'library cache: mutex X' sessions:

select bs.session_id, bs.session_serial#, bs.program, bs.event, bs.p1, bs.blocking_session, bs.blocking_session_serial#, bs.sql_id
      ,s.sample_time, s.session_id, s.session_serial#, s.program, s.event, s.p1, s.blocking_session, s.blocking_session_serial#, s.sql_id
  from v$active_session_history bs, v$active_session_history s
where s.event  = 'library cache: mutex X'
  and bs.event = 'library cache: bucket mutex X'
  and s.sample_time = bs.sample_time
  and mod(s.p1, power(2, 17)) = bs.p1
  and s.session_id != bs.session_id
  and s.sample_time > sysdate-5/1440
order by s.sample_time desc, bs.session_id, s.session_id;


3.2 Oracle 12c


In Oracle 12c, we start 64 job sessions with test_proc_dynamic, and look "library cache: mutex X".

exec test_job('dynamic', 64, 1e7, 1e4);
Run the same monitoring queries. There is no sessions in 'library cache: mutex X' wait event.

SQL(12c)> select sid, event, p1, p2, p3 from v$session where event in ('library cache: mutex X', 'library cache: bucket mutex X');

no rows selected
After the test, we stop all jobs by (see appended code):

exec clearup_test;  
If we also start 64 job sessions with test_proc_static, there is also no "library cache: mutex X" observed.

exec test_job('static', 64, 1e7, 1e4);


4. Library Cache Locks and Pins (added in 25-Apr-2021)


We will use the findings in Blog:
      Tracing Library Cache Locks 3: Object Names
and follow the similar approach in Blog:
      Row Cache Object and Row Cache Mutex Case Study
to compose gdb scripts to reveal Library Cache Locks and Pins (kgllkal, kglpnal) activities.


4.1. 19c Library Cache Locks and Pins


At first, we run query below to list meta info of PLITBLM and our two test procedures in library objects of 19c DB. V$DB_OBJECT_CACHE.addr is the object_handle address, which will be used in gdb script.
(Note: object_handle address is changed after each DB restart or shared_pool flush).

select owner, name, namespace, type, hash_value, addr, to_number(addr, 'xxxxxxxxxxxxxxxx') addr_nr
  from v$db_object_cache v 
 where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC', 'TEST_PROC_DYNAMIC')
 order by v.name desc;

OWNER  NAME                   NAMESPACE           TYPE       HASH_VALUE  ADDR      ADDR_NR
------ ---------------------- ------------------- ---------- ---------- --------- -----------
K      TEST_PROC_STATIC       TABLE/PROCEDURE     PROCEDURE  1186232128  9DEB95E0  2649462240
K      TEST_PROC_DYNAMIC      TABLE/PROCEDURE     PROCEDURE   313204667  9DD7BC98  2648161432
SYS    PLITBLM                TABLE/PROCEDURE     PACKAGE    4251528005  A454FEB0  2757033648
SYS    PLITBLM                BODY                CURSOR     4039937844  9EC403D0  2663646160
Then we can compose a gdb script: "gdb_script_kgl_19c" (see Appendix 7.1).


4.1.1 19c Static Call


As first test, we run static call with 10 executions, and trace kgllkal and kglpnal with the script.

Here the output of Sqlplus:

ORA19C> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC') order by source;

SOURCE                  NAMESPACE        TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ---------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_STATIC      TABLE/PROCEDURE  PROCEDURE  1186232128      1     0            70           126
SYS.PLITBLM             TABLE/PROCEDURE  PACKAGE    4251528005      8     0           343         1,771
SYS.PLITBLM             BODY             CURSOR     4039937844      0     0           978           978

ORA19C> exec test_proc_static(10, 1);
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.00

ORA19C> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC') order by source;

SOURCE                  NAMESPACE        TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ---------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_STATIC      TABLE/PROCEDURE  PROCEDURE  1186232128      1     0            70           127
SYS.PLITBLM             TABLE/PROCEDURE  PACKAGE    4251528005      8     0           343         1,774
SYS.PLITBLM             BODY             CURSOR     4039937844      0     0           981           981
Here the output of gdb script:

Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (-2) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (-2) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (-2) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004c57ff0 in kglpnal ()
===== Proc Pin (0) <<< kgllkhdl: 9DEB95E0, kgllkmod 2, kglnaobj: TEST_PROC_STATICK >>>=====
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c2307c in kglpnp ()
#3  0x0000000012b6e545 in kgiind ()
#4  0x000000000569b7dc in pfri8_inst_spec ()
#5  0x000000000569b604 in pfri1_inst_spec ()
#6  0x0000000012b7d020 in pfrrun ()
#7  0x0000000012b86bab in plsql_run ()

Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (-1) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (-1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (-1) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (0) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (0) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (0) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c2307c in kglpnp ()
#3  0x0000000012b6e545 in kgiind ()
#4  0x000000000569b7dc in pfri8_inst_spec ()
#5  0x000000000569b604 in pfri1_inst_spec ()
#6  0x0000000012b7d020 in pfrrun ()
#7  0x0000000012b86bab in plsql_run ()
===== Spec PIN (1) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
#0  0x0000000004c504b0 in kgllkal ()
#1  0x0000000004c33e9d in kglLock ()
#2  0x0000000004c22e0c in kglget ()
#3  0x0000000004c1cb78 in kglgob ()
#4  0x0000000004de210f in kgiinbgob_swcb ()
#5  0x0000000004de115e in kgiinb ()
#6  0x000000000569d1bb in pfri7_inst_body_common ()
#7  0x000000000569cf38 in pfri3_inst_body ()
====== Body LOCK (1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c1cd80 in kglgob ()
#3  0x0000000004de210f in kgiinbgob_swcb ()
#4  0x0000000004de115e in kgiinb ()
#5  0x000000000569d1bb in pfri7_inst_body_common ()
#6  0x000000000569cf38 in pfri3_inst_body ()
#7  0x0000000012b7d0a6 in pfrrun ()
===== Body PIN (1) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
In gdb script, for both kgllkal and kglpnal, we start the counting from -2 to skip the first warmup (-2) run and two additional runs (-1, 0), so the last counter is "(1)".

In third run (0), we print out Call Stack (backtrace) of "Proc Pin TEST_PROC_STATICK". in the last run (1), we print out Call Stack (backtrace) for "Spec PIN PLITBLMSYS", "Body LOCK PLITBLMSYS", and "Body PIN PLITBLMSYS".

The above two outputs showed:

   -. Sqlplus TEST_PROC_STATIC.PINNED_TOTAL increased 1 (127-126). Script has one "Proc Pin TEST_PROC_STATICK".
   -. Sqlplus Spec PLITBLM.PINNED_TOTAL increased 3 (1,774-1,771). Script has 3 "Spec PIN PLITBLMSYS" (exclude first warmup).
   -. Sqlplus Body PLITBLM.LOCKED_TOTAL increased 3 (981-978). Script has 3 "Body LOCK PLITBLMSYS" (exclude first warmup).
   -. Sqlplus Body PLITBLM.PINNED_TOTAL increased 3 (981-978). Script has 3 "Body PIN PLITBLMSYS" (exclude first warmup).
kgllkmod is documented in V$LIBCACHE_LOCKS.


4.1.4.1.2 19c Dynamic Call2 19c Dynamic Call


Now we run dynamic call with 10 executions, and trace kgllkal and kglpnal with the same script.

Here the output of Sqlplus:

ORA19C> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_DYNAMIC') order by source;

SOURCE                  NAMESPACE        TYPE       HASH_VALUE  LOCKS  PINS LOCKED_TOTAL  PINNED_TOTAL
----------------------- ---------------- ---------- ---------- ------ ----- ------------ -------------
K.TEST_PROC_DYNAMIC     TABLE/PROCEDURE  PROCEDURE   313204667      1     0           20            56
SYS.PLITBLM             TABLE/PROCEDURE  PACKAGE    4251528005      8     0          343         1,775
SYS.PLITBLM             BODY             CURSOR     4039937844      0     0          982           982

ORA19C> exec test_proc_dynamic(10, 1);
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.03

ORA19C> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_DYNAMIC') order by source;

SOURCE                  NAMESPACE        TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ---------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_DYNAMIC     TABLE/PROCEDURE  PROCEDURE   313204667      1     0            20            57
SYS.PLITBLM             TABLE/PROCEDURE  PACKAGE    4251528005      8     0           343         1,787
SYS.PLITBLM             BODY             CURSOR     4039937844      0     0           994           994
Here the output of gdb script:

Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (-2) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (-2) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (-2) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004c57ff0 in kglpnal ()
===== Proc Pin (0) <<< kgllkhdl: 9DD7BC98, kgllkmod 2, kglnaobj: TEST_PROC_DYNAMICK >>>=====
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c2307c in kglpnp ()
#3  0x0000000012b6e545 in kgiind ()
#4  0x000000000569b7dc in pfri8_inst_spec ()
#5  0x000000000569b604 in pfri1_inst_spec ()
#6  0x0000000012b7d020 in pfrrun ()
#7  0x0000000012b86bab in plsql_run ()

Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (-1) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (-1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (-1) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (0) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (0) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (0) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c2307c in kglpnp ()
#3  0x0000000012b6e545 in kgiind ()
#4  0x000000000569b7dc in pfri8_inst_spec ()
#5  0x000000000569b604 in pfri1_inst_spec ()
#6  0x0000000012b7d020 in pfrrun ()
#7  0x0000000012b86bab in plsql_run ()
===== Spec PIN (1) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
#0  0x0000000004c504b0 in kgllkal ()
#1  0x0000000004c33e9d in kglLock ()
#2  0x0000000004c22e0c in kglget ()
#3  0x0000000004c1cb78 in kglgob ()
#4  0x0000000004de210f in kgiinbgob_swcb ()
#5  0x0000000004de115e in kgiinb ()
#6  0x000000000569d1bb in pfri7_inst_body_common ()
#7  0x000000000569cf38 in pfri3_inst_body ()
====== Body LOCK (1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c1cd80 in kglgob ()
#3  0x0000000004de210f in kgiinbgob_swcb ()
#4  0x0000000004de115e in kgiinb ()
#5  0x000000000569d1bb in pfri7_inst_body_common ()
#6  0x000000000569cf38 in pfri3_inst_body ()
#7  0x0000000012b7d0a6 in pfrrun ()
===== Body PIN (1) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (2) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (2) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (2) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (3) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (3) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (3) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====

....

Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (9) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (9) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (9) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x0000000004c57ff0 in kglpnal ()
===== Spec PIN (10) <<< kgllkhdl: A454FEB0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (10) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x0000000004c57ff0 in kglpnal ()
===== Body PIN (10) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
In gdb script, for both kgllkal and kglpnal, we start the counting from -2 to skip the first warmup (-2) run and two additional runs (-1, 0), so the last counter is "(10)".

In third run (0), we print out Call Stack (backtrace) of "Proc Pin TEST_PROC_DYNAMICK". in run (1), we print out Call Stack (backtrace) for "Spec PIN PLITBLMSYS", "Body LOCK PLITBLMSYS", and "Body PIN PLITBLMSYS".

The above two outputs showed:

   -. Sqlplus TEST_PROC_DYNAMICK.PINNED_TOTAL increased 1 (57-56). Script has one "Proc Pin TEST_PROC_DYNAMICK".
   -. Sqlplus Spec PLITBLM.PINNED_TOTAL increased 12 (1,787-1,775). Script has 12 "Spec PIN PLITBLMSYS" (exclude first warmup).
   -. Sqlplus Body PLITBLM.LOCKED_TOTAL increased 12 (994-982). Script has 12 "Body LOCK PLITBLMSYS" (exclude first warmup).
   -. Sqlplus Body PLITBLM.PINNED_TOTAL increased 12 (994-982). Script has 12 "Body PIN PLITBLMSYS" (exclude first warmup).
We can see all PIN in kgllkmod 2, whereas LOCK in kgllkmod 1 (kgllkmod is documented in V$LIBCACHE_LOCKS).


4.2. 12c1 (12.1.0.2.0) Library Cache Locks and Pins


First run query below to list meta info of PLITBLM and our two test procedures in library objects of 12c1 DB. V$DB_OBJECT_CACHE.addr is the object_handle address, which will be used in gdb script. (Note: object_handle address is changed after each DB restart or shared_pool flush).

select owner, name, namespace, type, hash_value, hash_value, addr, to_number(addr, 'xxxxxxxxxxxxxxxx') addr_nr
  from v$db_object_cache v 
 where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC', 'TEST_PROC_DYNAMIC')
 order by v.name desc;

OWNER  NAME                   NAMESPACE         TYPE       HASH_VALUE HASH_VALUE  ADDR       ADDR_NR
------ ---------------------- ----------------- ---------- ---------- ----------  ---------  ----------
K      TEST_PROC_STATIC       TABLE/PROCEDURE   PROCEDURE  1186232128 1186232128  1565EBC10  5744016400
K      TEST_PROC_DYNAMIC      TABLE/PROCEDURE   PROCEDURE   313204667  313204667  15756B220  5760266784
SYS    PLITBLM                TABLE/PROCEDURE   PACKAGE    4251528005 4251528005  15D175E20  5856779808
SYS    PLITBLM                BODY              CURSOR     4039937844 4039937844  15CA06D80  5848984960
Then we can compose a gdb script: "gdb_script_kgl_12c1" (see Appendix 7.2).


4.2.1 12c1 Static Call


First we run static call with 10 executions, and trace kgllkal and kglpnal with the script.

Note that each time we need to run the test in a new Sqlplus connection, the second and later runs will not increase LOCKED_TOTAL and PINNED_TOTAL (that is probably why PLITBLM "library cache: mutex X" contention is in 19c. Whereas in 12c1, it is cached).

Here the output of Sqlplus:

ORA12C1> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC') order by source;

SOURCE                  NAMESPACE         TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ----------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_STATIC      TABLE/PROCEDURE   PROCEDURE  1186232128      0     0            82            95
SYS.PLITBLM             TABLE/PROCEDURE   PACKAGE    4251528005      5     0           802           916
SYS.PLITBLM             BODY              CURSOR     4039937844      0     0           869           869

ORA12C1> exec test_proc_static(10, 1);
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01

ORA12C1> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_STATIC') order by source;

SOURCE                  NAMESPACE         TYPE       HASH_VALUE  LOCKS   PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ----------------- ---------- ---------- ------ ------ ------------- -------------
K.TEST_PROC_STATIC      TABLE/PROCEDURE   PROCEDURE  1186232128      1      0            83            96
SYS.PLITBLM             TABLE/PROCEDURE   PACKAGE    4251528005      5      0           803           919
SYS.PLITBLM             BODY              CURSOR     4039937844      0      0           871           871
Here the output of gdb script:

Breakpoint 4, 0x000000000cac5190 in kglpnal ()
===== Spec PIN (1) <<< kgllkhdl: 5D175E20, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x000000000cac18c0 in kgllkal ()
#0  0x000000000cac18c0 in kgllkal ()
#1  0x000000000cab9b8d in kglLock ()
#2  0x000000000cab2e62 in kglget ()
#3  0x000000000cab120f in kglgob ()
#4  0x0000000004b1961b in kgiinb ()
#5  0x0000000004c64ab9 in pfri3_inst_body ()
#6  0x000000000caf9a4b in pfrrun ()
#7  0x000000000cb02104 in plsql_run ()
====== Body LOCK (1) <<< kgllkhdl: 5CA06D80, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x000000000cac5190 in kglpnal ()
#0  0x000000000cac5190 in kglpnal ()
#1  0x000000000cab397d in kglpin ()
#2  0x000000000cab12f6 in kglgob ()
#3  0x0000000004b1961b in kgiinb ()
#4  0x0000000004c64ab9 in pfri3_inst_body ()
#5  0x000000000caf9a4b in pfrrun ()
#6  0x000000000cb02104 in plsql_run ()
#7  0x0000000004c4b345 in peicnt ()
===== Body PIN (1) <<< kgllkhdl: 5CA06D80, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 1, 0x000000000cac18c0 in kgllkal ()
===== Proc LOCK (1) <<< kgllkhdl: 565EBC10, kgllkmod 1, kglnaobj: TEST_PROC_STATICK >>>=====
#0  0x000000000cac18c0 in kgllkal ()
#1  0x000000000cab9b8d in kglLock ()
#2  0x000000000cab2e62 in kglget ()
#3  0x000000000cab120f in kglgob ()
#4  0x0000000004b17225 in kgiind ()
#5  0x0000000004c64072 in pfri1_inst_spec ()
#6  0x000000000caf99c3 in pfrrun ()
#7  0x000000000cb02104 in plsql_run ()

Breakpoint 2, 0x000000000cac5190 in kglpnal ()
===== Proc Pin (1) <<< kgllkhdl: 565EBC10, kgllkmod 2, kglnaobj: TEST_PROC_STATICK >>>=====
#0  0x000000000cac5190 in kglpnal ()
#1  0x000000000cab397d in kglpin ()
#2  0x000000000cab12f6 in kglgob ()
#3  0x0000000004b17225 in kgiind ()
#4  0x0000000004c64072 in pfri1_inst_spec ()
#5  0x000000000caf99c3 in pfrrun ()
#6  0x000000000cb02104 in plsql_run ()
#7  0x0000000004c4b345 in peicnt ()

Breakpoint 3, 0x000000000cac18c0 in kgllkal ()
#0  0x000000000cac18c0 in kgllkal ()
#1  0x000000000cab9b8d in kglLock ()
#2  0x000000000cab2e62 in kglget ()
#3  0x000000000cab120f in kglgob ()
#4  0x000000000cab1dc2 in kglgob ()
#5  0x0000000004b17225 in kgiind ()
#6  0x0000000004c64072 in pfri1_inst_spec ()
#7  0x000000000caf99c3 in pfrrun ()
===== Spec LOCK (1) <<< kgllkhdl: 5D175E20, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x000000000cac5190 in kglpnal ()
===== Spec PIN (2) <<< kgllkhdl: 5D175E20, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 4, 0x000000000cac5190 in kglpnal ()
===== Spec PIN (3) <<< kgllkhdl: 5D175E20, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x000000000cac18c0 in kgllkal ()
====== Body LOCK (2) <<< kgllkhdl: 5CA06D80, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x000000000cac5190 in kglpnal ()
===== Body PIN (2) <<< kgllkhdl: 5CA06D80, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
The above two outputs showed:

   -. Sqlplus TEST_PROC_STATICK.LOCKED_TOTAL and PINNED_TOTAL increased 1 (83-82, 96-95). 
      Script has one "Proc LOCK TEST_PROC_STATICK" and one "Proc Pin TEST_PROC_STATICK".
   -. Sqlplus Spec PLITBLM.LOCKED_TOTAL increased 1 (803-802). Script has 1 "Spec LOCK PLITBLMSYS".
   -. Sqlplus Spec PLITBLM.PINNED_TOTAL increased 3 (919-916). Script has 3 "Spec PIN PLITBLMSYS".
   -. Sqlplus Body PLITBLM.LOCKED_TOTAL increased 2 (871-869). Script has 2 "Body PIN PLITBLMSYS".
   -. Sqlplus Body PLITBLM.PINNED_TOTAL increased 2 (871-869). Script has 2 "Body LOCK PLITBLMSYS".


4.2.1 12c1 Dynamic Call


Now we run dynamic call with 10 executions, and trace kgllkal and kglpnal with the same script.

Here the output of Sqlplus:

ORA12C1> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_DYNAMIC') order by source;

SOURCE                  NAMESPACE         TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ----------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_DYNAMIC     TABLE/PROCEDURE   PROCEDURE   313204667      0     0            79            85
SYS.PLITBLM             TABLE/PROCEDURE   PACKAGE    4251528005      5     0           822           939
SYS.PLITBLM             BODY              CURSOR     4039937844      0     0           890           890

ORA12C1> exec test_proc_dynamic(10, 1);
PL/SQL procedure successfully completed.
Elapsed: 00:00:00.01

ORA12C1> select owner||'.'||name source, namespace, type, hash_value, locks, pins, locked_total, pinned_total
    from v$db_object_cache v
    where hash_value in (4251528005, 4039937844) and name like 'PLITBLM' or name in ('TEST_PROC_DYNAMIC') order by source;

SOURCE                  NAMESPACE         TYPE       HASH_VALUE  LOCKS  PINS  LOCKED_TOTAL  PINNED_TOTAL
----------------------- ----------------- ---------- ---------- ------ ----- ------------- -------------
K.TEST_PROC_DYNAMIC     TABLE/PROCEDURE   PROCEDURE   313204667      1     0            80            86
SYS.PLITBLM             TABLE/PROCEDURE   PACKAGE    4251528005      5     0           822           941
SYS.PLITBLM             BODY              CURSOR     4039937844      0     0           892           892
Here the output of gdb script:

Breakpoint 4, 0x000000000cac5190 in kglpnal ()
===== Spec PIN (1) <<< kgllkhdl: 5D175E20, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x000000000cac18c0 in kgllkal ()
#0  0x000000000cac18c0 in kgllkal ()
#1  0x000000000cab9b8d in kglLock ()
#2  0x000000000cab2e62 in kglget ()
#3  0x000000000cab120f in kglgob ()
#4  0x0000000004b1961b in kgiinb ()
#5  0x0000000004c64ab9 in pfri3_inst_body ()
#6  0x000000000caf9a4b in pfrrun ()
#7  0x000000000cb02104 in plsql_run ()
====== Body LOCK (1) <<< kgllkhdl: 5CA06D80, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x000000000cac5190 in kglpnal ()
#0  0x000000000cac5190 in kglpnal ()
#1  0x000000000cab397d in kglpin ()
#2  0x000000000cab12f6 in kglgob ()
#3  0x0000000004b1961b in kgiinb ()
#4  0x0000000004c64ab9 in pfri3_inst_body ()
#5  0x000000000caf9a4b in pfrrun ()
#6  0x000000000cb02104 in plsql_run ()
#7  0x0000000004c4b345 in peicnt ()
===== Body PIN (1) <<< kgllkhdl: 5CA06D80, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 1, 0x000000000cac18c0 in kgllkal ()
===== Proc LOCK (1) <<< kgllkhdl: 5756B220, kgllkmod 1, kglnaobj: TEST_PROC_DYNAMICK >>>=====
#0  0x000000000cac18c0 in kgllkal ()
#1  0x000000000cab9b8d in kglLock ()
#2  0x000000000cab2e62 in kglget ()
#3  0x000000000cab120f in kglgob ()
#4  0x0000000004b17225 in kgiind ()
#5  0x0000000004c64072 in pfri1_inst_spec ()
#6  0x000000000caf99c3 in pfrrun ()
#7  0x000000000cb02104 in plsql_run ()

Breakpoint 2, 0x000000000cac5190 in kglpnal ()
===== Proc Pin (1) <<< kgllkhdl: 5756B220, kgllkmod 2, kglnaobj: TEST_PROC_DYNAMICK >>>=====
#0  0x000000000cac5190 in kglpnal ()
#1  0x000000000cab397d in kglpin ()
#2  0x000000000cab12f6 in kglgob ()
#3  0x0000000004b17225 in kgiind ()
#4  0x0000000004c64072 in pfri1_inst_spec ()
#5  0x000000000caf99c3 in pfrrun ()
#6  0x000000000cb02104 in plsql_run ()
#7  0x0000000004c4b345 in peicnt ()

Breakpoint 4, 0x000000000cac5190 in kglpnal ()
===== Spec PIN (2) <<< kgllkhdl: 5D175E20, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 5, 0x000000000cac18c0 in kgllkal ()
====== Body LOCK (2) <<< kgllkhdl: 5CA06D80, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 6, 0x000000000cac5190 in kglpnal ()
===== Body PIN (2) <<< kgllkhdl: 5CA06D80, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
All LOCKED_TOTAL and PINNED_TOTAL for PLITBLM.LOCKED_TOTAL and PLITBLM are smilar to static call, Script output are also similar to static call.


4.3 Test Observations and Discussions


(a). Above tests showed that only dynamic call in Oracle 19c has the number of executions (almost) equal to:
         Spec PLITBLM.PINNED_TOTAL = Body PLITBLM.LOCKED_TOTAL = Body PLITBLM.PINNED_TOTAL.
         
     Therefore in case of dynamic call in Oracle 19c, 
     the number of kglLock and kglpin subroutine calls are linear to the number of executions.
     
(b). It seems that Oracle 19c has some new behaviour on "execute immediate" PL/SQL block.
     Probably dynamic parsed PL/SQL cursor is not cached, or cannot be re-used. 
     Hence it incurs one hard parsing for each execution.
     
(c). Here the Call Stack for kglpin & kglLock in Oracle 19c vs. 12c1.
     In 19c, pfri1_inst_spec invokes pfri8_inst_spec, pfri3_inst_body invokes pfri7_inst_body_common.
     Whereas in 12c1, they are not possible.
     
     It looks like PL/SQL engine changed.

       -------- 19c kglpin & kglLock --------------------      -------- 12c1 kglpin & kglLock --------
       
       #0  0x0000000004c57ff0 in kglpnal ()                    #0  0x000000000cac18c0 in kgllkal ()        
       #1  0x0000000004c1f8c4 in kglpin ()                     #1  0x000000000cab9b8d in kglLock ()        
       #2  0x0000000004c2307c in kglpnp ()                     #2  0x000000000cab2e62 in kglget ()         
       #3  0x0000000012b6e545 in kgiind ()                     #3  0x000000000cab120f in kglgob ()         
       #4  0x000000000569b7dc in pfri8_inst_spec ()            #4  0x0000000004b1961b in kgiinb ()         
       #5  0x000000000569b604 in pfri1_inst_spec ()            #5  0x0000000004c64ab9 in pfri3_inst_body ()
       #6  0x0000000012b7d020 in pfrrun ()                     #6  0x000000000caf9a4b in pfrrun ()         
       #7  0x0000000012b86bab in plsql_run ()                  #7  0x000000000cb02104 in plsql_run ()      
                                                   
       #0  0x0000000004c504b0 in kgllkal ()                    #0  0x000000000cac5190 in kglpnal ()        
       #1  0x0000000004c33e9d in kglLock ()                    #1  0x000000000cab397d in kglpin ()         
       #2  0x0000000004c22e0c in kglget ()                     #2  0x000000000cab12f6 in kglgob ()         
       #3  0x0000000004c1cb78 in kglgob ()                     #3  0x0000000004b1961b in kgiinb ()         
       #4  0x0000000004de210f in kgiinbgob_swcb ()             #4  0x0000000004c64ab9 in pfri3_inst_body ()
       #5  0x0000000004de115e in kgiinb ()                     #5  0x000000000caf9a4b in pfrrun ()         
       #6  0x000000000569d1bb in pfri7_inst_body_common ()     #6  0x000000000cb02104 in plsql_run ()      
       #7  0x000000000569cf38 in pfri3_inst_body ()            #7  0x0000000004c4b345 in peicnt ()         

(d). There does not exist PLITBLM PACKAGE BODY. It is defined as PRAGMA INTERFACE(C, ...).
     (See later Section: "6. Full List of Mutex kgxExclusive and kgxRelease").
     The new 19c call of pfri7_inst_body_common can be a problem.
In all above tests, we have not yet include kglUnLock and kglUnPin.


5. 19c Mutex kgxExclusive and kgxRelease from kglLock and kglpin


After looking kglLock and kglpin in the last section, now we can have a look of their triggered kgxExclusive and kgxRelease for mutex request and release.

We only look kglLock and kglpin on PLITBLM Body in dynamic call. The same approach can be applied on PLITBLM Spec.


5.1. 19c kglLock triggered Mutex kgxExclusive and kgxRelease


Run dynamic call for 10 executions, and trace kglLock mutex Request/Release with appended "gdb_script_mutex_19c_body_lock" (see Appendix 7.3)

exec test_proc_dynamic(10, 1); 
Here the gdb output:

Breakpoint 1, 0x0000000004c504b0 in kgllkal ()
====== Body LOCK (0) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 1, 0x0000000004c504b0 in kgllkal ()
#0  0x0000000004c504b0 in kgllkal ()
#1  0x0000000004c33e9d in kglLock ()
#2  0x0000000004c22e0c in kglget ()
#3  0x0000000004c1cb78 in kglgob ()
#4  0x0000000004de210f in kgiinbgob_swcb ()
#5  0x0000000004de115e in kgiinb ()
#6  0x000000000569d1bb in pfri7_inst_body_common ()
#7  0x000000000569cf38 in pfri3_inst_body ()
====== Body LOCK (1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (1) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (1) --->  Mutex addr (r15): 9EC40520 
$1 = {0, 551, 7207, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (2) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (2) --->  Mutex addr (r15): 9EC40520 
$2 = {0, 551, 7208, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (3) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (3) --->  Mutex addr (r15): 9EC40520 
$3 = {0, 551, 7209, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (4) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (4) --->  Mutex addr (r15): 9EC40520 
$4 = {0, 551, 7210, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (5) --->  Mutex addr (rsi): 9EC40520
Breakpoint 1, 0x0000000004c504b0 in kgllkal ()

====== Body LOCK (2) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (5) --->  Mutex addr (r15): 9EC40520 
$5 = {0, 551, 7211, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (6) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (6) --->  Mutex addr (r15): 9EC40520 
$6 = {0, 551, 7212, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (7) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (7) --->  Mutex addr (r15): 9EC40520 
$7 = {0, 551, 7213, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (8) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (8) --->  Mutex addr (r15): 9EC40520 
$8 = {0, 551, 7214, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (9) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (9) --->  Mutex addr (r15): 9EC40520 
$9 = {0, 551, 7215, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (10) --->  Mutex addr (rsi): 9EC40520
Breakpoint 1, 0x0000000004c504b0 in kgllkal ()

...

====== Body LOCK (10) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (45) --->  Mutex addr (r15): 9EC40520 
$45 = {0, 551, 7251, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (46) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (46) --->  Mutex addr (r15): 9EC40520 
$46 = {0, 551, 7252, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (47) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (47) --->  Mutex addr (r15): 9EC40520 
$47 = {0, 551, 7253, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (48) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (48) --->  Mutex addr (r15): 9EC40520 
$48 = {0, 551, 7254, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body LOCK kgxExclusive (49) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body LOCK kgxRelease (49) --->  Mutex addr (r15): 9EC40520 
$49 = {0, 551, 7255, 10791}
Above output shows:

  (a). There are 10 "Body LOCK PLITBLMSYS", each for one dynamic call execution.
  (b). For each "Body LOCK PLITBLMSYS", there are 5 pairs of kgxExclusive / kgxRelease on mutex address: 9EC40520.
For each kgxRelease, we show a tuple like:

      {0, 551, 7255, 10791}
   where
      551: session_id (SID) of dynamic call session (mutex holding session) = V$MUTEX_SLEEP_HISTORY.blocking_session
      7255: mutex gets = V$MUTEX_SLEEP_HISTORY.gets
      10791: mutex sleeps = V$MUTEX_SLEEP_HISTORY.sleeps
             
      (V$MUTEX_SLEEP_HISTORY.mutex_identifier = V$DB_OBJECT_CACHE.hash_value
       V$MUTEX_SLEEP_HISTORY.p1raw = V$DB_OBJECT_CACHE.adrr: object_handle addr)


5.1. 19c kglpin triggered Mutex kgxExclusive and kgxRelease


Run dynamic call for 10 executions, and trace kglpin mutex Rquests/Release with appended "gdb_script_mutex_19c_body_pin" (see Appendix 7.4)

exec test_proc_dynamic(10, 1); 
Here the gdb output:

Breakpoint 1, 0x0000000004c57ff0 in kglpnal ()
====== Body PIN (0) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 1, 0x0000000004c57ff0 in kglpnal ()
#0  0x0000000004c57ff0 in kglpnal ()
#1  0x0000000004c1f8c4 in kglpin ()
#2  0x0000000004c1cd80 in kglgob ()
#3  0x0000000004de210f in kgiinbgob_swcb ()
#4  0x0000000004de115e in kgiinb ()
#5  0x000000000569d1bb in pfri7_inst_body_common ()
#6  0x000000000569cf38 in pfri3_inst_body ()
#7  0x0000000012b7d0a6 in pfrrun ()
====== Body PIN (1) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (1) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (1) --->  Mutex addr (r15): 9EC40520 
$1 = {0, 551, 7263, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (2) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (2) --->  Mutex addr (r15): 9EC40520 
$2 = {0, 551, 7264, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (3) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (3) --->  Mutex addr (r15): 9EC40520 
$3 = {0, 551, 7265, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (4) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (4) --->  Mutex addr (r15): 9EC40520 
$4 = {0, 551, 7266, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (5) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (5) --->  Mutex addr (r15): 9EC40520 
$5 = {0, 551, 7267, 10791}

Breakpoint 1, 0x0000000004c57ff0 in kglpnal ()
====== Body PIN (2) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (6) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (6) --->  Mutex addr (r15): 9EC40520 
$6 = {0, 551, 7268, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (7) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (7) --->  Mutex addr (r15): 9EC40520 
$7 = {0, 551, 7269, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (8) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (8) --->  Mutex addr (r15): 9EC40520 
$8 = {0, 551, 7270, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (9) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (9) --->  Mutex addr (r15): 9EC40520 
$9 = {0, 551, 7271, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (10) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (10) --->  Mutex addr (r15): 9EC40520 
$10 = {0, 551, 7272, 10791}

...

Breakpoint 1, 0x0000000004c57ff0 in kglpnal ()
====== Body PIN (10) <<< kgllkhdl: 9EC403D0, kgllkmod 2, kglnaobj: PLITBLMSYS >>>=====
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (46) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (46) --->  Mutex addr (r15): 9EC40520 
$46 = {0, 551, 7308, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (47) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (47) --->  Mutex addr (r15): 9EC40520 
$47 = {0, 551, 7309, 10791}
Breakpoint 2, 0x0000000004deb0e0 in kgxExclusive ()
------------ Body PIN kgxExclusive (48) --->  Mutex addr (rsi): 9EC40520
Breakpoint 3, 0x0000000004dee690 in kgxRelease ()
------------ Body PIN kgxRelease (48) --->  Mutex addr (r15): 9EC40520 
$48 = {0, 551, 7310, 10791}
Above output shows:

  (a). There are 10 "Body PIN PLITBLMSYS", each for one dynamic call execution.
  (b). For each "Body PIN PLITBLMSYS", there are 5 pairs of kgxExclusive / kgxRelease on mutex address: 9EC40520.


5.3 Test Observations and Discussions: PLITBLM Execution Limit


In 19c, when we run:

  exec test_proc_dynamic(10, 1);
It makes 10 dynamic Pl/SQL anonymous block executions.

Each execution incurs:

  1 kglpin  on PLITBLM Spec
  1 kglLock on PLITBLM Body
  1 kglpin  on PLITBLM Body
Above tests showed that each kglpin or each kglLock incurs 5 pairs of mutex kgxExclusive / kgxRelease. So for each dynamic execution, there are 15 (3*5) pairs of mutex kgxExclusive / kgxRelease. For 10 dynamic executions in 19c, it triggers 150 (3*5*10) mutex kgxExclusive / kgxRelease.

Whereas in all other cases (19c static, 12c1 static/dynamic), they are below constant (less than 3*5*3=45), independent of the number of executions.

Assume each mutex kgxExclusive or each kgxRelease take 100 microseconds,

  the duration of one ksun_pickler_test_1d execution can be estimated as:
     
       Dur_Per_Exe = (3*5*2*100) = 3000
       
  The throughput of ksun_pickler_test_1d executions per second can be estimated as
  (each kgxExclusive / kgxRelease is independently performed):
     
       Exec_Per_Second = 1000,000/100 = 10,000
         
  if there is sufficient resource (specially CPU) and does not take account of queuing effect in concurrency system.
  
       where  
              3: 1 kglpin on PLITBLM Spec, 1 kglLock on PLITBLM Body, 1 kglpin on PLITBLM Body
              5: each kglpin/kglLock requires 5 pairs of mutex kgxExclusive / kgxRelease 
              2: 1 kgxExclusive and 1 kgxRelease
            100: mutex time in microsecond
If we markhot PLITBLM, it will create hot copy PLITBLM objects. Each hot copy object has a different object_handle, hence different mutex to protect it. (see Blog: "library cache: mutex X" and Application Context ). The maximum number of test_proc_dynamic executions per second can increase to:

   Exec_Per_Second * "_kgl_hot_object_copies"
   
      _kgl_hot_object_copies: controls the maximum number of copies, maximum value: 255

      (Note Oracle MOS: 
          Bug 19373224 - dbms_shared_pool.unmarkhot spins if _kgl_hot_object_copies is 255 (Doc ID 19373224.8)
       wrote:
          Executing dbms_shared_pool.unmarkhot() might have entered a spin under kglget() if 
             _kgl_hot_object_copies was set (or derived) to a value of 255.
          Workaround
             Reduce the value of _kgl_hot_object_copies to 254 or below).
Each object is protected by its own mutex (single point). Its performance (mutex time) is related to Processor Clock Speed and scheduler time slice.

Till now, we only looked part of mutex kgxExclusive/kgxRelease. In next Section (6. "Full List of Mutex kgxExclusive and kgxRelease"), we will dig further to list all pairs of mutex kgxExclusive/kgxRelease (So the above estimation of execution duration has to be increased). At the same time, we will show how to observe that kgxExclusive / kgxRelease is independently performed.

By the way, subroutine "kglLock" has Uppercase "L" for "lock", whereas subroutine "kglpin" has lowercase "p" for "pin", probably to avoid two adjacent lowercase "l" in "kgllock".

There seem some behaviour changes in Oracle 19c and latest 18c version. For example: Blog: The Third Test of ORA-04025: maximum allowed library object lock showed session_cached_cursors changes from 18c to 19c in the call of dbms_lock.allocate_unique_autonomous.

Blog: DBMS_SCHEDULER Job Not Running and Used Slaves showed DBMS_SCHEDULER changes from Oracle 18.10 to Oracle 19.8.


6. Full List of Mutex kgxExclusive and kgxRelease


In Section 5.1. "19c kglLock triggered Mutex kgxExclusive and kgxRelease", we use "gdb_script_mutex_19c_body_lock" (see Appendix 7.3) to collect Mutex kgxExclusive and kgxRelease triggered by kglLock on PLITBLM Body.

It only traced kglGetMutex on one mutex (Mutex addr: 9EC40520) for object handle (9EC403D0) of PLITBLM Body.

In this session, we will compose a new gdb script: "gdb_script_mutex_19c_body_lock_all" (see Appendix 7.5) to trace both kglGetMutex and kglGetBucketMutex on all mutexes triggered by kglLock on PLITBLM Body. (see Blog: Row Cache Object and Row Cache Mutex Case Study about Row Cache "BUCKET mutex").

Run dynamic call for 10 executions, and trace kglLock mutex Request/Release with appended "gdb_script_mutex_19c_body_lock_all" (see Appendix 7.5).

exec test_proc_dynamic(10, 1); 
Here the gdb output (unrelated content removed):

Breakpoint 1, 0x0000000004c504b0 in kgllkal ()
===Body LOCK (1) <<< kgllkhdl: 9EC403D0, kgllkmod 1, kglnaobj: PLITBLMSYS>>>=====
  #0  kgllkal ()
  #1  kglLock ()
  #2  kglget ()
  #3  kglgob ()
  #4  kgiinbgob_swcb ()
  #5  kgiinb ()
  #6  pfri7_inst_body_common ()
  #7  pfri3_inst_body ()
------kglGetMutex (1) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 4
  #0  kglGetMutex ()
  #1  kglpin ()
  #2  kglgob ()
  #3  kgiinbgob_swcb ()
----------kgxExclusive (1) ---> Mutex addr (rsi): 9EC40520
------------kgxRelease (1) ---> Mutex addr (r15): 9EC40520 
$1 = {0, 551, 11144, 17474}
------kglGetMutex (2) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 90
  #0  kglGetMutex ()
  #1  kglpnal ()
  #2  kglpin ()
  #3  kglgob ()
----------kgxExclusive (2) ---> Mutex addr (rsi): 9EC40520
------kglGetMutex (3) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 71
  #0  kglGetMutex ()
  #1  kglobpn ()
  #2  kglpim ()
  #3  kglpin ()
------kglGetMutex (4) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 72
  #0  kglGetMutex ()
  #1  kglobpn ()
  #2  kglpim ()
  #3  kglpin ()
------------kgxRelease (2) ---> Mutex addr (r15): 9EC40520 
$2 = {0, 551, 11145, 17474}
------kglGetMutex (5) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 95
  #0  kglGetMutex ()
  #1  kglpndl ()
  #2  kglUnPin ()
  #3  kgiinb ()
----------kgxExclusive (3) ---> Mutex addr (rsi): 9EC40520
------------kgxRelease (3) ---> Mutex addr (r15): 9EC40520 
$3 = {0, 551, 11146, 17474}
------kglGetMutex (6) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 85
  #0  kglGetMutex ()
  #1  kgllkdl ()
  #2  kglUnLock ()
  #3  kgiinb ()
----------kgxExclusive (4) ---> Mutex addr (rsi): 9EC40520
------kglGetMutex (7) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 86
  #0  kglGetMutex ()
  #1  kgllkdl ()
  #2  kglUnLock ()
  #3  kgiinb ()
------------kgxRelease (4) ---> Mutex addr (r15): 9EC40520 
$4 = {0, 551, 11147, 17474}
------kglGetMutex (8) ---> Mutex addr (rsi): A4550000, Location(r8d): 95
  #0  kglGetMutex ()
  #1  kglpndl ()
  #2  kss_del_cb ()
  #3  kssdel ()
----------kgxExclusive (5) ---> Mutex addr (rsi): A4550000
------------kgxRelease (5) ---> Mutex addr (r15): A4550000 
$5 = {0, 551, 11349, 51801}
------kglGetMutex (9) ---> Mutex addr (rsi): A4550000, Location(r8d): 4
  #0  kglGetMutex ()
  #1  kglpin ()
  #2  kglpnp ()
  #3  kgiind ()
----------kgxExclusive (6) ---> Mutex addr (rsi): A4550000
------------kgxRelease (6) ---> Mutex addr (r15): A4550000 
$6 = {0, 551, 11350, 51801}
------kglGetMutex (10) ---> Mutex addr (rsi): A4550000, Location(r8d): 90
  #0  kglGetMutex ()
  #1  kglpnal ()
  #2  kglpin ()
  #3  kglpnp ()
----------kgxExclusive (7) ---> Mutex addr (rsi): A4550000
------kglGetMutex (11) ---> Mutex addr (rsi): A4550000, Location(r8d): 71
  #0  kglGetMutex ()
  #1  kglobpn ()
  #2  kglpim ()
  #3  kglpin ()
------kglGetMutex (12) ---> Mutex addr (rsi): A4550000, Location(r8d): 72
  #0  kglGetMutex ()
  #1  kglobpn ()
  #2  kglpim ()
  #3  kglpin ()
------------kgxRelease (7) ---> Mutex addr (r15): A4550000 
$7 = {0, 551, 11351, 51801}
------kglGetBucketMutex (1) ---> BucketMutex hash_value (rsi): 36660, Location(r8d): 62
  #0  kglGetBucketMutex ()
  #1  kglhdgn ()
  #2  kglLock ()
  #3  kglget ()
------kglGetMutex (13) ---> Mutex addr (rsi): A614A230, Location(r8d): 62
  #0  kglGetMutex ()
  #1  kglhdgn ()
  #2  kglLock ()
  #3  kglget ()
----------kgxExclusive (8) ---> Mutex addr (rsi): A614A230
------------kgxRelease (8) ---> Mutex addr (r15): A614A230 
$8 = {0, 551, 4832, 0}
------kglGetMutex (14) ---> Mutex addr (rsi): 9EC40520, Location(r8d): 106
  #0  kglGetMutex ()
  #1  kglhdgn ()
  #2  kglLock ()
  #3  kglget ()
----------kgxExclusive (9) ---> Mutex addr (rsi): 9EC40520
------------kgxRelease (9) ---> Mutex addr (r15): 9EC40520 
$9 = {0, 551, 11148, 17474}
There are (about) 10 such output, each for one execution.

Above output shows:

(a). kglGetMutex
       8 kglGetMutex on Mutex addr (rsi): 9EC40520 in 8 different Location(r8d)
       5 kglGetMutex on Mutex addr (rsi): A4550000 in 5 different Location(r8d) 
       1 kglGetMutex on Mutex addr (rsi): A614A230 in 1 different Location(r8d) (from kglGetBucketMutex)
       
(b). kglGetBucketMutex
       1 kglGetBucketMutex with BucketMutex hash_value (rsi): 36660, Location(r8d): 62
            (v$db_object_cache.hash_value=36660)
       kglGetBucketMutex calls kglGetMutex, which then calls 
       
(c). kgxExclusive/kgxExclusive
       5 kgxExclusive/kgxRelease on Mutex addr (rsi): 9EC40520
       3 kgxExclusive/kgxRelease on Mutex addr (rsi): A4550000
       1 kgxExclusive/kgxRelease on Mutex addr (rsi): A614A230 (from kglGetBucketMutex)
       
       where:  Mutex addr (rsi): 9EC40520 is for PLITBLM Pacakge Body
               Mutex addr (rsi): A4550000 is for PLITBLM Pacakge Spec
               Mutex addr (rsi): A614A230 is fir BucketMutex
               
(d). Location
       multiple kglGetMutex trigger one kgxExclusive/kgxExclusive, for example,
         kglGetMutex on Mutex addr (rsi): 9EC40520 with Location: 71, 72, 95 trigger one kgxExclusive/kgxRelease. 
   
(e). Between kgxExclusive and kgxRelease for the same mutex addr, there is no other kgxExclusive (not interleaved).

(f). Each mutex addr has its own counter recorded in the form of four element tuple:
         {0,  holding session_id, mutex gets, mutex sleeps}
         
       9EC40520: from {0, 730, 11144, 17474} to {0, 551, 11148, 17474}, 5 tuples (gets counter from 11144 to 11148)
       A4550000: from {0, 551, 11349, 51801} to {0, 551, 11351, 51801}, 3 tuples (gets counter from 11349 to 11351)
       A614A230: from {0, 551, 4832, 0},                                1 tuple  (gets counter 4832)
In summary, one dynamic execution incurs one kglLock on PLITBLM Body, which requires 9 pairs of kgxExclusive/kgxRelease. The 9 pairs are distributed in 3 different mutex addr.

As showed in Section 5.3 "Test Observations and Discussions: PLITBLM Execution Limit", one dynamic execution also incurs one kglpin on PLITBLM Spec and one kglpin on PLITBLM Body, which have the similar requests on kgxExclusive/kgxRelease.

If we make a PROCESSSTATE (level 10) or SYSTEMSTATE (level 10) dump, we can see object handle: "LibraryHandle: Address=0xa454feb0":

  LibraryHandle:  Address=0xa454feb0 Hash=fd692b45 LockMode=N PinMode=0 LoadLockMode=0 Status=VALD 
    ObjectName:  Name=SYS.PLITBLM   
      FullHashValue=6e9b4ca99d9498e62b2af8bafd692b45 Namespace=TABLE/PROCEDURE(01) Type=PACKAGE(09) ContainerId=0 ContainerUid=0 Identifier=4746 OwnerIdn=0 
    Statistics:  InvalidationCount=0 ExecutionCount=2795763211 LoadCount=4 ActiveLocks=7 TotalLockCount=392941 TotalPinCount=13513812 
    Counters:  BrokenCount=1 RevocablePointer=1 KeepDependency=0 Version=0 BucketInUse=821 HandleInUse=821 HandleReferenceCount=0 
    Concurrency:  DependencyMutex=0xa454ff60(0, 747294, 0, 0) Mutex=0xa4550000(0, 42469882, 52549, 0) 
It contains tuple:

	   Mutex=0xa4550000(0, 42469882, 52549, 0)  
	     where   mutex gets:   42469882
	             mutex sleeps:    52549

      (By the way, in AIX, we saw heavy "library cache: mutex X" Wait Event with:
                 Mutex=70001067e219fb0(9859, 1144748, 67542, 6)
              	     where   mutex holder Session_ID:      9859
              	             mutex gets:                1144748
              	             mutex sleeps:                67542
              	             mutex block mode:                6
      )	      
However in PROCESSSTATE / SYSTEMSTATE dumps, we cannot find anything about PLITBLM PACKAGE BODY, such as:

    ObjectName:  Name=SYS.PLITBLM   
      ... Namespace=BODY(02) Type=PACKAGE BODY(11) 
because PLITBLM PACKAGE BODY is defined as PRAGMA INTERFACE(C, ...), and there is no PLITBLM PACKAGE BODY.

Following query also shows no PLITBLM PACKAGE BODY:

select owner, object_name, object_type from dba_objects where object_name in ('PLITBLM', 'STANDARD');

  OWNER    OBJECT_NAME  OBJECT_TYPE
  -------- ------------ ------------
  SYS      STANDARD     PACKAGE
  SYS      STANDARD     PACKAGE BODY
  SYS      PLITBLM      PACKAGE
  PUBLIC   PLITBLM      SYNONYM
In the above gdb output, "mutex gets" in all the tuples of one particular "mutex addr" are consecutive because we have only one single test session. In case of real applications, there are many concurrent sessions. For example, if we start the same jobs in Section 3. "library cache: mutex X" Test:

   exec test_job('dynamic', 64, 1e7, 1e4);
And then run the gdb script again, we can observe that "mutex gets" are jumping one after another (inconsecutive). By measuring the gap between two "mutex gets" (discontinuity), we can also estimate the degree of concurrency.

By the way, we can print outl kgl Mutex Locations listed in kglMutexLocations[] array. (similar to kqrMutexLocations[] array in Blog: Row Cache Object and Row Cache Mutex Case Study)

#Define Command to PrintkglMutexLocations
define PrintkglMutexLocations
  set $i = 0
  while $i < $arg0 + $arg0
    x /s *(&kglMutexLocations + $i)
    set $i = $i + 2
  end
end

(gdb) PrintkglMutexLocations 174


7. Appendix



7.1 gdb_script_kgl_19c



set pagination off
set logging file library_cache_kgl_19c.log
set logging overwrite on
set logging on
set $proc_lck = 0
set $proc_pin = 0
set $spec_lck = -2
set $spec_pin = -2
set $body_lck = -2
set $body_pin = -2
set $bt_print = -2

break kgllkal if $rdx==0X9DEB95E0 ||  $rdx==0X9DD7BC98
commands
printf "===== Proc LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====\n", $proc_lck++, $rdx, $rcx, ($rdx+0x1c0)
backtrace 8
continue
end

break kglpnal if $rdx==0X9DEB95E0 ||  $rdx==0X9DD7BC98
commands
printf "===== Proc Pin (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====\n", $proc_pin++, $rdx, $rcx, ($rdx+0x1c0)
backtrace 8
continue
end

break kgllkal if $rdx==0XA454FEB0
commands
while $spec_lck ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Spec LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $spec_lck++, $rdx, $rcx, ($rdx+0x1c0)
continue
end

break kglpnal if $rdx==0XA454FEB0
commands
while $spec_pin ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Spec PIN (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $spec_pin++, $rdx, $rcx, ($rdx+0x1c0)
continue
end

break kgllkal if $rdx==0X9EC403D0
commands
while $body_lck ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "====== Body LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_lck++, $rdx, $rcx, ($rdx+0x1c0)
continue
end

break kglpnal if $rdx==0X9EC403D0
commands
while $body_pin ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Body PIN (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_pin++, $rdx, $rcx, ($rdx+0x1c0)
continue
end


7.2 gdb_script_kgl_12c1



set pagination off
set logging file library_cache_kgl_12c1.log
set logging overwrite on
set logging on
set $proc_lck = 1
set $proc_pin = 1
set $spec_lck = 1
set $spec_pin = 1
set $body_lck = 1
set $body_pin = 1
set $bt_print = 1

break kgllkal if $rdx==0x1565ebc10 ||  $rdx==0x15756b220
commands
printf "===== Proc LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====\n", $proc_lck++, $rdx, $rcx, ($rdx+0x1b8)
backtrace 8
continue
end

break kglpnal if $rdx==0x1565ebc10 ||  $rdx==0x15756b220
commands
printf "===== Proc Pin (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====\n", $proc_pin++, $rdx, $rcx, ($rdx+0x1b8)
backtrace 8
continue
end

break kgllkal if $rdx==0x15d175e20
commands
while $spec_lck ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Spec LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $spec_lck++, $rdx, $rcx, ($rdx+0x1b8)
continue
end

break kglpnal if $rdx==0x15d175e20
commands
while $spec_pin ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Spec PIN (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $spec_pin++, $rdx, $rcx, ($rdx+0x1b8)
continue
end

break kgllkal if $rdx==0x15ca06d80
commands
while $body_lck ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "====== Body LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_lck++, $rdx, $rcx, ($rdx+0x1b8)
continue
end

break kglpnal if $rdx==0x15ca06d80
commands
while $body_pin ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "===== Body PIN (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_pin++, $rdx, $rcx, ($rdx+0x1b8)
continue
end


7.3 gdb_script_mutex_19c_body_lock



set pagination off
set logging file mutex_19c_body_lock.log
set logging overwrite on
set logging on
set $body_lck = 0
set $bt_print = -2
set $k = 1
set $r = 1
set $mutex_addr = 0x0


break kgllkal if $rdx==0X9EC403D0
commands
while $body_lck ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "====== Body LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_lck++, $rdx, $rcx, ($rdx+0x1c0)
continue
end

break kgxExclusive if $r9==0X9EC403D0 && $body_lck > 1 
command 
printf "------------ Body LOCK kgxExclusive (%i) --->  Mutex addr (rsi): %X\n", $k++, $rsi
set $mutex_addr = $rsi     
continue
end

break kgxRelease if $r15==$mutex_addr && $body_lck > 1  
command 
printf "------------ Body LOCK kgxRelease (%i) --->  Mutex addr (r15): %X \n", $r++, $r15 
p/d (int[4])*$r15
# x/4dw $r15     
continue
end


7.4 gdb_script_mutex_19c_body_pin



set pagination off
set logging file mutex_19c_body_pin.log
set logging overwrite on
set logging on
set $body_pin = 0
set $bt_print = -2
set $k = 1
set $r = 1
set $mutex_addr = 0x0


break kglpnal if $rdx==0X9EC403D0
commands
while $body_pin ==1 && $bt_print==0
  backtrace 8
  set $bt_print = 1
end
set $bt_print = 0
printf "====== Body PIN (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s >>>=====", $body_pin++, $rdx, $rcx, ($rdx+0x1c0)
continue
end

break kgxExclusive if $r9==0X9EC403D0 && $body_pin > 1 
command 
printf "------------ Body PIN kgxExclusive (%i) --->  Mutex addr (rsi): %X\n", $k++, $rsi
set $mutex_addr = $rsi     
continue
end

break kgxRelease if $r15==$mutex_addr && $body_pin > 1  
command 
printf "------------ Body PIN kgxRelease (%i) --->  Mutex addr (r15): %X \n", $r++, $r15 
p/d (int[4])*$r15
# x/4dw $r15     
continue
end


7.5 gdb_script_mutex_19c_body_lock_all



set pagination off
set logging file mutex_19c_body_lock_all.log
set logging overwrite on
set logging on
set $body_lck = 0
set $k = 1
set $r = 1
set $kget = 1
set $kbucketget = 1
set $mutex_addr = 0x0

break kgllkal if $rdx==0X9EC403D0
commands
printf "===Body LOCK (%i) <<< kgllkhdl: %X, kgllkmod %x, kglnaobj: %s>>>=====\n", $body_lck++, $rdx, $rcx, ($rdx+0x1c0)
backtrace 8 
set $bt_print = 1
continue
end

break kglGetMutex if $body_lck > 1 
command 
printf "------kglGetMutex (%i) ---> Mutex addr (rsi): %X, Location(r8d): %d\n", $kget++, $rsi, $r8d
backtrace 4
continue
end

break kglGetBucketMutex if $body_lck > 1 
command 
printf "------kglGetBucketMutex (%i) ---> BucketMutex hash_value (rsi): %d, Location(r8d): %d\n", $kbucketget++, $rsi, $r8d
backtrace 4
continue
end

break kgxExclusive if $body_lck > 1 
#break kgxExclusive if $r9==0X9EC403D0 && $body_lck > 1 
command 
printf "----------kgxExclusive (%i) ---> Mutex addr (rsi): %X\n", $k++, $rsi
#backtrace 4
set $mutex_addr = $rsi  
continue
end

break kgxRelease if $r15==$mutex_addr && $body_lck > 1 
command 
printf "------------kgxRelease (%i) ---> Mutex addr (r15): %X \n", $r++, $r15 
p/d (int[4])*$r15
# x/4dw $r15     
continue
end


#break kglReleaseMutex
#break kglReleaseBucketMutex


7.6 Test Cleanup


After test, we can cleanup the test by following procedure:

create or replace procedure clearup_test as
begin
  for c in (select * from dba_scheduler_jobs where job_name like '%TEST_JOB%') loop
    begin
      --set DBA_SCHEDULER_JOBS.enabled=FALSE
	    dbms_scheduler.disable (c.job_name, force => true, commit_semantics =>'ABSORB_ERRORS');
	    --set DBA_SCHEDULER_JOBS.enabled=TRUE, so that it can be scheduled to run (state='RUNNING')
	    --  dbms_scheduler.enable (c.job_name, commit_semantics =>'ABSORB_ERRORS');
	  exception when others then null;
	  end;
	end loop;
	
  for c in (select * from dba_scheduler_running_jobs where job_name like '%TEST_JOB%') loop
    begin
      --If force=FALSE, gracefully stop the job, slave process can update the status of the job in the job queue.
      --If force= TRUE, the Scheduler immediately terminates the job slave.
      --For repeating job with attribute "start_date => systimestamp" and enabled=TRUE, 
      --re-start immediate (state changed from 'SCHEDULED to 'RUNNING'), DBA_SCHEDULER_JOBS.run_count increases 1.
	    dbms_scheduler.stop_job (c.job_name, force => true, commit_semantics =>'ABSORB_ERRORS');
	  exception when others then null;
	  end;
	end loop;
	
  for c in (select * from dba_scheduler_jobs where job_name like '%TEST_JOB%') loop
    begin
      --If force=TRUE, the Scheduler first attempts to stop the running job instances 
      --(by issuing the STOP_JOB call with the force flag set to false), and then drops the jobs.
	    dbms_scheduler.drop_job (c.job_name, force => true, commit_semantics =>'ABSORB_ERRORS');
	  exception when others then null;
	  end;
	end loop;
end;
/

exec clearup_test;