Anda di halaman 1dari 41

---------------------------------------------------------------------------------- File name: snapper.sql -- Purpose: An easy to use Oracle session-level performance snapshot utility --NB!

This script does NOT require creation of any database object s! --This is very useful for ad-hoc performance diagnosis in environm ents -with restrictive change management processes, where creating -even temporary tables and PL/SQL packages is not allowed or woul d -take too much time to get approved. --All processing is done by few sqlplus commands and an anonymous -PL/SQL block, all that's needed is SQLPLUS access (and if you wa nt -to output data to server-side tracefile then execute rights on -DBMS_SYSTEM). --The output is formatted the way it could be easily post-processe d -by either Unix string manipulation tools or loaded to spreadshee t. ---- Author: Tanel Poder -- Copyright: (c) E2SN - http://tech.e2sn.com - All rights reserved. ---- Thanks to: Adrian Billington and Jamey Johnston for sending some useful fix es ----------------------------------------------------------------------------------- The Session Snapper v3.15 -- (c) Tanel Poder ( http://tech.e2sn.com ) ---+-----=====O=== Welcome to The Session Snapper! (Yes, you are looking at a cheap ASCII -- / imitation of a fish and a fishing rod. -- | Nevertheless the PL/SQL c ode below the -- | fish itself should be hel pful for quick -- | catching of relevant Orac le performance -- | information. -- | So I wish you happy... um ... snapping? -- | ) -- | ...... -- | iittii,,.... -- iiffffjjjjtttt,, -..;;ttffLLLLffLLLLLLffjjtt;;.. -..ttLLGGGGGGLLffLLLLLLLLLLLLLLffjjii,, ..ii ,,

-ffGGffLLLLLLjjttjjjjjjjjffLLLLLLLLLLjjii.. ..iijj;; .... -ffGGLLiittjjttttttiittttttttttffLLLLLLGGffii.. ;;LLLLii;; ;;.. -ffEEGGffiittiittttttttttiiiiiiiittjjjjffLLGGLLii.. iiLLLLLLttiiii ,, -;;ffDDLLiiiitt,,ttttttttttttiiiiiiiijjjjjjffLLLLffttiiiiffLLGGLLjjtttt;; .. -..ttttjjiitt,,iiiiiittttttttjjjjttttttttjjjjttttjjttttjjjjffLLDDGGLLttii.. -iittiitttt, ;;iittttttttjjjjjjjjjjttjjjjjjffffffjjjjjjjjjjLLDDGGLLtt;;.. -jjjjttttii:. ..iiiiffLLGGLLLLLLLLffffffLLLLLLLLLLLLLLLLffffffLLLLLLfftt,, -iittttii,,;;,,ttiiiiLLLLffffffjjffffLLLLLLLLffLLffjjttttttttttjjjjffjjii.. -,,iiiiiiiiiittttttiiiiiiiiiijjffffLLLLLLLLffLLffttttttii;;;;iiiitttttttt;; .. -..iittttttffffttttiiiiiiiiiittttffjjjjffffffffttiittii:: ....,,;;iittii ;; -..;;iittttttttttttttttiiiiiittttttttttjjjjjjtttttt;; ..;;ii ;;.. -..;;;;iittttttjjttiittttttttttttttjjttttttttii.. .. .. -....;;;;ttjjttttiiiiii;;;;;;iittttiiii.. -..;;ttttii;;.... ..;;;;.... -..iiii;;.. -..;;,, -.... ---- Usage: --snapper.sql <ash[1-3]|stats|all>[,out][,trace][,pagesize=X][,gather=[s][ t][w][l][e][b][a]]> <seconds_in_snap> <snapshot_count> <sid(s)_to_snap> --ash - sample session activity ASH style, waits and SQL_IDs from v$session and -print a TOP SQL/wait report from these samples (this is t he default from -Snapper 3.0). The columns chosen for TOP calculation are defined in CONFIG -section below. --ash=sql_id+event+wait_class -- the above example illustrates that you can also specify t he v$session -columns for TOP report yourself. The above example will s how a TOP -activity report grouped by SQL_ID + EVENT + WAIT_CLASS -Note that the columns are separated by + (as comma is a s napper parameter -separator, not column separator) --ash1 -ash2 -ash3 - in addition to "ash" report you can have 3 more reported during the same -snapper sampling snapshot. Just include ash1=col1+col2,as h2=col3+col4,... -parameters if you want multiple TOP reports per Snapper s napshot --stats - sample v$sesstat,v$sess_time_model,v$session_event perfor

mance counters -and report how much these stats increased (deltas) during Snapper run -all - report both ASH and stats sections --out - use dbms_output.put_line() for output. output will be see n only when -Snapper run completes due to dbms_output limitations. Thi s is the default. -trace - write output to server process tracefile -(you must have execute permission on sys.dbms_system.ksdw rt() for that, -you can use both out and trace parameters together if yo u like ) --pagesize - display header lines after X snapshots. if pagesize=0 don 't display -any headers. pagesize=-1 will display a terse header only once --gather - if omitted, gathers s,t,w statistics (see below) -- if specified, then gather following: --Session-level stats: -s - Session Statistics from v$sesstat -t - Session Time model info from v$sess_time_model -w - Session Wait statistics from v$session_event and v$session_wait --Instance-level stats: -l - instance Latch get statistics ( gets + immediate _gets ) -e - instance Enqueue lock get statistics -b - buffer get Where statistics -- useful in version s up to 10.2.x -a - All above --sinclude - if specified, then show only V$SESSTAT stats which match the -LIKE pattern of sinclude (REGEXP_LIKE in 10g+) -linclude - if specified, then show only V$LATCH latch stats which ma tch the -LIKE pattern of linclude (REGEXP_LIKE in 10g+) -tinclude - if specified, then show only V$SESS_TIME_MODEL stats whic h match the -LIKE pattern of tinclude (REGEXP_LIKE in 10g+) -winclude - if specified, then show only V$SESSION_EVENT wait stats w hich match the -LIKE pattern of winclude (REGEXP_LIKE in 10g+) --you can combine above parameters in any order, separate them by comm as -!!!don't use spaces as otherwise they are treated as next parameters by sqlplus !!! -!!!if you want to use spaces, enclose the whole sqlplus parameter in doublequotes !!! --<seconds_in_snap> - the number of seconds between taking snapshots -<snapshot_count> - the number of snapshots to take ( maximum value is p ower(2,31)-1 )

--<sids_to_snap> can be either one sessionid, multiple sessionids separate d by -commas or a SQL statement which returns a list of SIDs (if you need spac es -in that parameter text, enclose it in double quotes). --if you want to snap ALL sids, use "all" as value for -<sids_to_snap> parameter --alternatively you can used "select sid from v$session" as value for <sid s_to_snap> -parameter to capture all SIDs. you can write any query (with multiple an d/or) -conditions to specify complex rules for capturing only the SIDs you want --starting from version 3.0 there are further session_id selection options available in -instead of sid you can write such expressions for snapper's <sids_to_sna p> parameter: --sid=123 -- take sid 123 only (the same as just writing 123) -user=tanel -- take all sessions where username is 'tanel' (case inse nsitive) --- this is the same as writing following subquery for the --- <sids_to_snap> parameter: -select sid from v$session where lower(username) li ke lower('tanel') --user=tanel% -- take all sessions where username begins with 'tanel%' (case insensitive) --- the = means actually LIKE in SQL terms in this script --spid=1234 -- all these 3 parameters do the same thing: -ospid=1234 -- they look up the sessions(s) where the processes OS PI D=1234 -pid=1234 -- this is useful for quickly looking up what some OS pro cess is doing --- if it consumes too much of some resource -qc=123 -qcsid=123 -- show query coordinator and all PX slave sessions --program=sqlplus% -- the following examples filter by correspondin g v$session coulmns -machine=linux01 -- machine -osuser=oracle -- os username -module=HR -- module -"action=Find Order" -- note the quotes because there is a space insi de the parameter --- value -client_id=tanelpoder -- show only sessions where client_identifier is set to tanelpoder --- this is very useful in cases with (properly i nstrumented) --- connection pools ---Note that if you want to change some "advanced" snapper configuration pa rameters

-or default values then search for CONFIG in this file to see configurabl e -variable section ---- Examples: -NB! Read the online examples, these are more detailed and list script ou tput too! --http://tech.e2sn.com/oracle-scripts-and-tools/session-snapper --@snapper ash,stats 1 1 515 -(Output one 1-second snapshot of session 515 using dbms_output and exit -Wait, v$sesstat and v$sess_time_model statistics are reported by defaul t -Starting from V3 the ASH style session activity report is shown as well ) --@snapper stats,gather=w 1 1 515 -(Output one 1-second snapshot of session 515 using dbms_output and exit -only Wait event statistics are reported, no ASH) --@snapper ash,gather=st 1 1 515 -(Output one 1-second snapshot of session 515 using dbms_output and exit -only v$sesstat and v$sess_Time_model statistics are gathered + ASH) --@snapper trace,ash,gather=stw,pagesize=0 10 90 117,210,313 -(Write 90 10-second snapshots into tracefile for session IDs 117,210,313 -all statistics are reported, do not print any headers) --@snapper trace,ash 900 999999999 "select sid from v$session" -(Take a snapshot of ALL sessions every 15 minutes and write the output t o trace, -loop (almost) forever ) --@snapper out,trace 300 12 "select sid from v$session where username='APP S'" -(Take 12 5-minute snapshots of all sessions belonging to APPS user, writ e -output to both dbms_output and tracefile) --- Notes: --Snapper does not currently detect if a session with given SID has -ended and been recreated between snapshots, thus it may report bogus -statistics for such sessions. The check and warning for that will be -implemented in a future version. --------------------------------------------------------------------------------set termout off tab off verify off linesize 299 -- Get parameters define snapper_options="&1" define snapper_sleep="&2" define snapper_count="&3" define snapper_sid="&4"

-- The following code is required for making this script "dynamic" as due -- different Oracle versions, script parameters or granted privileges some -- statements might not compile if not adjusted properly. define define define define define define (from define define 10.2) define col col col col col col col col col col _IF_ORA10_OR_HIGHER="--" _IF_ORA11_OR_HIGHER="--" _IF_LOWER_THAN_ORA11="--" _IF_DBMS_SYSTEM_ACCESSIBLE="/* dbms_system is not accessible" /*dummy*/ _IF_X_ACCESSIBLE="--" _YES_PLSQL_OBJ_ID="--" -- plsql_object_id columns available in v$session 10.2.0.3) _NO_PLSQL_OBJ_ID="" _YES_BLK_INST="--" -- blocking_instance available in v$session (from _NO_BLK_INST="" noprint noprint noprint noprint noprint noprint noprint noprint noprint noprint new_value new_value new_value new_value new_value new_value new_value new_value new_value new_value _IF_ORA9 _IF_ORA10_OR_HIGHER _IF_ORA11_OR_HIGHER _IF_LOWER_THAN_ORA11 _IF_DBMS_SYSTEM_ACCESSIBLE _IF_X_ACCESSIBLE _NO_PLSQL_OBJ_ID _YES_PLSQL_OBJ_ID _NO_BLK_INST _YES_BLK_INST

snapper_ora9 snapper_ora10higher snapper_ora11higher snapper_ora11lower dbms_system_accessible x_accessible no_plsql_obj_id yes_plsql_obj_id no_blk_inst yes_blk_inst

col snapper_sid

noprint new_value snapper_sid

-- this block determines whether dbms_system.ksdwrt is accessible to us -- dbms_describe is required as all_procedures/all_objects may show this object -- even if its not executable by us (thanks to o7_dictionary_accessibility=false ) var v var x declare o p l a dty def inout len prec scal rad spa tmp begin begin execute immediate 'select count(*) from x$kcbwh where rownum = 1' into t sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.varchar2_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; sys.dbms_describe.number_table; number; varchar2(100) varchar2(10)

mp; :x:= ' '; -- x$ tables are accessible, so dont comment any lines out exception when others then null; end; sys.dbms_describe.describe_procedure( 'DBMS_SYSTEM.KSDWRT', null, null, o, p, l, a, dty, def, inout, len, prec, scal, rad, spa ); -- we never get to following statement if dbms_system is not accessible -- as sys.dbms_describe will raise an exception :v:= '-- dbms_system is accessible'; exception when others then null; end; / -- this is here for a reason -- im extracting the first word of the snapper_sid (if its a complex expression, not just a single SID) -- by relying on how DEF and & assignment treat spaces in strings def ssid_begin=&snapper_sid select * from ( select case when trim(lower('&ssid_begin')) like 'sid=%' then trim(replace('&ssi d_begin','sid=','')) when trim(lower('&ssid_begin')) like 'user=%' then 'select sid from v$session where lower(username) like '''||lower(trim(replace('&ssid_begin','user =','')))||'''' when trim(lower('&ssid_begin')) like 'username=%' then 'select sid f rom v$session where lower(username) like '''||lower(trim(replace('&ssid_begin',' username=','')))||'''' when trim(lower('&ssid_begin')) like 'machine=%' then 'select sid fr om v$session where lower(machine) like '''||lower(trim(replace('&ssid_begin','ma chine=','')))||'''' when trim(lower('&ssid_begin')) like 'program=%' then 'select sid fr om v$session where lower(program) like '''||lower(trim(replace('&ssid_begin','pr ogram=','')))||'''' when trim(lower('&ssid_begin')) like 'service=%' then 'select sid fr om v$session where lower(service_name) like '''||lower(trim(replace('&ssid_begin ','service=','')))||'''' when trim(lower('&ssid_begin')) like 'module=%' then 'select sid fro m v$session where lower(module) like '''||lower(trim(replace('&ssid_begin','modu le=','')))||'''' when trim(lower('&ssid_begin')) like 'action=%' then 'select sid fro m v$session where lower(action) like '''||lower(trim(replace('&ssid_begin','acti on=','')))||'''' when trim(lower('&ssid_begin')) like 'osuser=%' then 'select sid fro m v$session where lower(osuser) like '''||lower(trim(replace('&ssid_begin','osus er=','')))||'''' when trim(lower('&ssid_begin')) like 'client_id=%' then 'select sid from v$session where lower(client_identifier) like '''||lower(trim(replace('&ssi d_begin','client_id=','')))||'''' when trim(lower('&ssid_begin')) like 'spid=%' then 'select sid from v$session where paddr in (select addr from v$process where spid in ('||lower(tri

m(replace('&ssid_begin','spid=','')))||'))' when trim(lower('&ssid_begin')) like 'ospid=%' then 'select sid from v$session where paddr in (select addr from v$process where spid in ('||lower(tr im(replace('&ssid_begin','ospid=','')))||'))' when trim(lower('&ssid_begin')) like 'pid=%' then 'select sid from v $session where paddr in (select addr from v$process where spid in ('||lower(trim (replace('&ssid_begin','pid=','')))||'))' when trim(lower('&ssid_begin')) like 'qcsid=%' then 'select sid from v$px_session where qcsid in ('||lower(trim(replace('&ssid_begin','qcsid=',''))) ||')' when trim(lower('&ssid_begin')) like 'qc=%' then 'select sid from v$ px_session where qcsid in ('||lower(trim(replace('&ssid_begin','qc=','')))||')' when trim(lower('&ssid_begin')) = 'all' then 'select sid from v$sess ion' when trim(lower('&ssid_begin')) = 'bg' then 'select sid from v$sessi on where type=''BACKGROUND''' when trim(lower('&ssid_begin')) = 'fg' then 'select sid from v$sessi on where type=''USER''' when trim(lower('&ssid_begin')) = 'lgwr' then 'select sid from v$ses sion where program like ''%(LGWR)%''' when trim(lower('&ssid_begin')) = 'dbwr' then 'select sid from v$ses sion where program like ''%(DBW%)%''' when trim(lower('&ssid_begin')) like 'select%' then null when trim(lower('&ssid_begin')) like 'with%' then null else null end snapper_sid -- put the result back to snapper_sid sqlplus value (if its not null) from dual ) where snapper_sid is not null -- snapper_sid sqlplus variable value will not be re placed if this query doesnt return any rows / -- this query populates some sqlplus variables required for dynamic compilation used below with mod_banner as ( select replace(banner,'9.','09.') banner from v$version where rownum = 1 ) select decode(substr(banner, instr(banner, 'Release ')+8,2), '09', '', '--') snapp er_ora9, decode(substr(banner, instr(banner, 'Release ')+8,1), '1', '', '--') snap per_ora10higher, decode(substr(banner, instr(banner, 'Release ')+8,2), '11', '', '--') snap per_ora11higher, decode(substr(banner, instr(banner, 'Release ')+8,2), '11', '--', '') snap per_ora11lower, nvl(:v, '/* dbms_system is not accessible') dbms_system_accessible, nvl(:x, '--') x_accessible, case when substr( banner, instr(banner, 'Release ')+8, instr(substr(banner,i nstr(banner,'Release ')+8),' ') ) >= '10.2' then '' else '--' end yes_blk_ inst,

case when substr( banner, instr(banner, 'Release nstr(banner,'Release ')+8),' ') ) >= '10.2' then nst, case when substr( banner, instr(banner, 'Release nstr(banner,'Release ')+8),' ') ) >= '10.2.0.3' then l_obj_id, case when substr( banner, instr(banner, 'Release nstr(banner,'Release ')+8),' ') ) >= '10.2.0.3' then _obj_id from mod_banner /

')+8, instr(substr(banner,i '--' else '' end no_blk_i ')+8, instr(substr(banner,i '' else '--' end yes_plsq ')+8, instr(substr(banner,i '--' else '' end no_plsql

set termout on serverout on size 1000000 format wrapped prompt Sampling SID &4 with interval &snapper_sleep seconds, taking &snapper_cou nt snapshots... -- main() -- let the Snapping start!!! declare -- forward declarations procedure output(p_txt in varchar2); procedure fout; function tptformat( p_num in number, p_stype in varchar2 default 'STAT', p_precision in number default 2, p_base in number default 10, p_grouplen in number default 3 ) return varchar2; function getopt( p_parvalues in varchar2, p_extract in varchar2, p_delim in varchar2 default ',' ) return varchar2; -- type, constant, variable declarations -- trick for holding 32bit UNSIGNED event and stat_ids in 32bit SIGNED PLS_I NTEGER pls_adjust constant number(10,0) := power(2,31) - 1; type srec is record (stype varchar2(4), sid number, statistic# number, value number ); type stab is table of srec index by pls_integer; s1 stab; s2 stab; type snrec is record (stype varchar2(4), statistic# number, name varchar2(10 0)); type sntab is table of snrec index by pls_integer; sn_tmp sntab; sn sntab; type sestab is table of v$session%rowtype index by pls_integer; g_sessions sestab;

g_empty_sessions lue

sestab;

type hc_tab is table of number index by pls_integer; -- index is sql hash va type ses_hash_tab is table of hc_tab index by pls_integer; -- index is SID g_ses_hash_tab ses_hash_tab; g_empty_ses_hash_tab ses_hash_tab; -- dbms_debug_vc2coll is a built-in collection present in every oracle db g_ash sys.dbms_debug_vc2coll := new sys.dbms_debug_vc2coll(); g_empty_ash sys.dbms_debug_vc2coll := new sys.dbms_debug_vc2coll(); g_ash_samples_taken number := 0; g_count_statname number; g_count_eventname number; g_mysid i number; a number; b number; c number; delta number; changed_values number; pagesize number:=99999999999999; missing_values_s1 number := 0; missing_values_s2 number := 0; disappeared_sid number := 0; d1 date; d2 date; ash_date1 date; ash_date2 date; lv_gather varchar2(1000); gv_header_string varchar2(1000); lv_data_string varchar2(1000); lv_ash lv_stats gather_stats gather_ash varchar2(1000); varchar2(1000); number := 0; number := 0; number;

-- CONFIGURABLE STUFF --- this sets what are the default ash sample TOP reporting group by colu mns g_ash_columns '; g_ash_columns1 g_ash_columns2 g_ash_columns3 id + sql_id'; varchar2(1000) := 'event + wait_class'; varchar2(1000) := 'sid + username + sql_id'; varchar2(1000) := 'plsql_object_id + plsql_subprogram_ varchar2(1000) := 'sql_id + child + event + wait_class

-- output column configuration output_header number := 0; -- 1=true 0=false output_username number := 1; -- v$session.username

output_sid output_time output_seconds each snapshot too) output_stype ,...) output_sname output_delta output_delta_s output_hdelta output_hdelta_s econd output_percent output_pcthist

number := 1; -- sid number := 0; -- time of snapshot start number := 0; -- seconds in snapshot (shown in footer of number := 1; -- statistic type (WAIT,STAT,TIME,ENQG,LATG number number number number number := := := := := 1; 0; 0; 1; 1; -----statistic name raw delta raw delta normalized to per second human readable delta human readable delta normalized to per s

number := 1; -- percent of total time/samples number := 1; -- percent of total visual bar (histogram) 6; 20; 20; 20; 35; 25; 15; 8; 16; 16; 16; 10; 6; 10; 6; 15; 12; 12; 12; 15; 10; 10; 10; 10; 10; 25; 25; 25; 25;

-- column widths in ASH report output w_sid number := w_username number := w_machine number := w_terminal number := w_program number := w_event number := w_wait_class number := w_state number := w_p1 number := w_p2 number := w_p3 number := w_row_wait_obj# number := w_row_wait_file# number := w_row_wait_block# number := w_row_wait_row# number := w_blocking_session_status number := w_blocking_instance number := w_blocking_session number := w_sql_hash_value number := w_sql_id number := w_sql_child_number number := w_plsql_entry_object_id number := w_plsql_entry_subprogram_id number := w_plsql_object_id number := w_plsql_subprogram_id number := w_module number := w_action number := w_client_identifier number := w_service_name number := w_activity_pct -- END CONFIGURABLE STUFF --

number := 7;

-- constants for ash collection extraction from the vc2 collection s_sid constant number := 1 ; s_username constant number := 2 ; s_machine constant number := 3 ; s_terminal constant number := 4 ; s_program constant number := 5 ; s_event constant number := 6 ; s_wait_class constant number := 7 ; s_state constant number := 8 ; s_p1 constant number := 9 ;

s_p2 s_p3 s_row_wait_obj# s_row_wait_file# s_row_wait_block# s_row_wait_row# s_blocking_session_status s_blocking_instance s_blocking_session s_sql_hash_value s_sql_id s_sql_child_number s_plsql_entry_object_id s_plsql_entry_subprogram_id s_plsql_object_id s_plsql_subprogram_id s_module s_action s_client_identifier s_service_name

constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant constant

number number number number number number number number number number number number number number number number number number number number

:= := := := := := := := := := := := := := := := := := := :=

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

-- constants for ash collection reporting, which columns to show in report c_sid constant number := power(2, s_sid ); c_username constant number := power(2, s_username ); c_machine constant number := power(2, s_machine ); c_terminal constant number := power(2, s_terminal ); c_program constant number := power(2, s_program ); c_event constant number := power(2, s_event ); c_wait_class constant number := power(2, s_wait_class ); c_state constant number := power(2, s_state ); c_p1 constant number := power(2, s_p1 ); c_p2 constant number := power(2, s_p2 ); c_p3 constant number := power(2, s_p3 ); c_row_wait_obj# constant number := power(2, s_row_wait_obj# ); c_row_wait_file# constant number := power(2, s_row_wait_file# ); c_row_wait_block# constant number := power(2, s_row_wait_block# ); c_row_wait_row# constant number := power(2, s_row_wait_row# ); c_blocking_session_status constant number := power(2, s_blocking_sessio n_status ); c_blocking_instance constant number := power(2, s_blocking_instan ce ); c_blocking_session constant number := power(2, s_blocking_sessio n ); c_sql_hash_value constant number := power(2, s_sql_hash_value );

c_sql_id ); c_sql_child_number r ); c_plsql_entry_object_id ect_id ); c_plsql_entry_subprogram_id program_id); c_plsql_object_id ); c_plsql_subprogram_id m_id ); c_module ); c_action ); c_client_identifier er ); c_service_name );

constant number := power(2, s_sql_id constant number := power(2, s_sql_child_numbe constant number := power(2, s_plsql_entry_obj constant number := power(2, s_plsql_entry_sub constant number := power(2, s_plsql_object_id constant number := power(2, s_plsql_subprogra constant number := power(2, s_module constant number := power(2, s_action constant number := power(2, s_client_identifi constant number := power(2, s_service_name

-- bitfield specifying which columns to group by in sampled session activity (ASH) g_ash_grouping number := 63; -- test

/*---------------------------------------------------- proc for outputting data to trace or dbms_output ---------------------------------------------------*/ procedure output(p_txt in varchar2) is begin if (getopt('&snapper_options', 'out') is not null) or (getopt('&snapper_options', 'out') is null and getopt('&snapper_optio ns', 'trace') is null) then dbms_output.put_line(p_txt); end if; -- The block below is a sqlplus trick for conditionally commenting out P L/SQL code &_IF_DBMS_SYSTEM_ACCESSIBLE if getopt('&snapper_options', 'trace') is not null then sys.dbms_system.ksdwrt(1, p_txt); sys.dbms_system.ksdfls; end if; -- */ end; -- output /*---------------------------------------------------- proc for outputting data, utilizing global vars ---------------------------------------------------*/ procedure fout is l_output_username VARCHAR2(100); begin -output( 'DEBUG, Entering fout(), b='||to_char(b)||' sn(s2(b).statistic#= '||s2(b).statistic# );

-output( 'DEBUG, In fout(), a='||to_char(a)||' b='||to_char(b)||' s1.coun t='||s1.count||' s2.count='||s2.count||' s2.count='||s2.count); if output_username = 1 then begin l_output_username := nvl( g_sessions(s2(b).sid).username, substr (g_sessions(s2(b).sid).program, instr(g_sessions(s2(b).sid).program,'(')) ); exception when no_data_found then l_output_username := 'error'; when others then raise; end; end if; output( CASE WHEN output_header || CASE WHEN output_sid ', ' END || CASE WHEN output_username = 1 THEN rpad(CASE s2(b).sid WHEN -1 T HEN ' ' ELSE l_output_username END, 10)||', ' END || CASE WHEN output_time = 1 THEN to_char(d1, 'YYYYMMDD HH24:MI :SS')||', ' END || CASE WHEN output_seconds = 1 THEN to_char(case (d2-d1) when 0 t hen &snapper_sleep else (d2-d1) * 86400 end, '9999999')||', ' END || CASE WHEN output_stype = 1 THEN s2(b).stype||', ' END || CASE WHEN output_sname = 1 THEN rpad(sn(s2(b).statistic#).nam e, 58, ' ')||', ' END || CASE WHEN output_delta = 1 THEN to_char(delta, '999999999999' )||', ' END || CASE WHEN output_delta_s = 1 THEN to_char(delta/(case (d2-d1) w hen 0 then &snapper_sleep else (d2-d1) * 86400 end),'999999999')||', ' END || CASE WHEN output_hdelta = 1 THEN lpad(tptformat(delta, s2(b).s type), 10, ' ')||', ' END || CASE WHEN output_hdelta_s = 1 THEN lpad(tptformat(delta/(case (d 2-d1) when 0 then &snapper_sleep else (d2-d1)* 86400 end ), s2(b).stype), 10, ' ')||', ' END || CASE WHEN output_percent = 1 THEN CASE WHEN s2(b).stype IN ('TI ME','WAIT') THEN to_char(delta/CASE (d2-d1) WHEN 0 THEN &snapper_sleep ELSE (d2d1) * 86400 END / 10000, '9999.9')||'%,' END END || CASE WHEN output_pcthist = 1 THEN CASE WHEN s2(b).stype IN ('TI ME','WAIT') THEN rpad(' '||rpad('|', ceil(round(delta/CASE (d2-d1) WHEN 0 THEN & snapper_sleep ELSE (d2-d1) * 86400 END / 100000,1))+1, '@'),12,' ')||'|' END END ); end; /*---------------------------------------------------- function for converting large numbers to human-readable format ---------------------------------------------------*/ function tptformat( p_num in number, p_stype in varchar2 default 'STAT', p_precision in number default 2, p_base in number default 10, -- for KiB/MiB formattin g use p_grouplen in number default 3 -- p_base=2 and p_groupl en=10 ) return varchar2 is begin if p_stype in ('WAIT','TIME') then = 1 THEN 'SID= ' END = 1 THEN to_char(s2(b).sid,'999999')||

return round( p_num / power( p_base , trunc(log(p_base,abs(p_num)))-trunc( mod(log(p_base,abs(p_num)),p_grouplen)) ), p_precision ) || case trunc(log(p_base,abs(p_num)))-trunc(mod(log(p_base,abs(p _num)),p_grouplen)) when 0 then 'us' when 1 then 'us' when p_grouplen*1 then 'ms' when p_grouplen*2 then 's' when p_grouplen*3 then 'ks' when p_grouplen*4 then 'Ms' else '*'||p_base||'^'||to_char( trunc(log(p_base,abs(p_nu m)))-trunc(mod(log(p_base,abs(p_num)),p_grouplen)) )||' us' end; else return round( p_num / power( p_base , trunc(log(p_base,abs(p_num)))-trunc( mod(log(p_base,abs(p_num)),p_grouplen)) ), p_precision ) || case trunc(log(p_base,abs(p_num)))-trunc(mod(log(p_base,abs(p _num)),p_grouplen)) when 0 then '' when 1 then '' when p_grouplen*1 then 'k' when p_grouplen*2 then 'M' when p_grouplen*3 then 'G' when p_grouplen*4 then 'T' when p_grouplen*5 then 'P' when p_grouplen*6 then 'E' else '*'||p_base||'^'||to_char( trunc(log(p_base,abs(p_nu m)))-trunc(mod(log(p_base,abs(p_num)),p_grouplen)) ) end; end if; end; -- tptformat /*---------------------------------------------------- simple function for parsing arguments from parameter string ---------------------------------------------------*/ function getopt( p_parvalues in varchar2, p_extract in varchar2, p_delim in varchar2 default ',' ) return varchar2 is ret varchar(1000) := NULL; begin -- dbms_output.put('p_parvalues = ['||p_parvalues||'] ' ); -- dbms_output.put('p_extract = ['||p_extract||'] ' ); if lower(p_parvalues) like lower(p_extract)||'%' or lower(p_parvalues) like '%'||p_delim||lower(p_extract)||'%' then

ret := nvl ( substr(p_parvalues, instr(p_parvalues, p_extract)+length(p_extract), case instr( substr(p_parvalues, instr(p_parvalues, p_extract)+length (p_extract) ) , p_delim ) when 0 then length(p_parvalues) else instr( substr(p_parvalues, instr(p_parvalues, p_extract)+length (p_extract) ) , p_delim ) - 1 end ) , chr(0) -- in case parameter was specified but with no valu e ); else ret := null; -- no parameter found end if; -- dbms_output.put_line('ret = ['||replace(ret,chr(0),'\0')||']'); return ret; end; -- getopt /*---------------------------------------------------- proc for getting session list with username, osuser, machine etc ---------------------------------------------------*/ procedure get_sessions is tmp_sessions sestab; begin select * bulk collect into tmp_sessions from v$session where sid in (&snapper_sid); g_sessions := g_empty_sessions; for i in 1..tmp_sessions.count loop g_sessions(tmp_sessions(i).sid) := tmp_sessions(i); end loop; end; -- get_sessions

/*---------------------------------------------------- function for getting session list with username, osuser, machine etc -- this func does not update the g_sessions global array but returns session info as return value ---------------------------------------------------*/ function get_sessions return sestab is tmp_sessions sestab; l_return_sessions sestab; begin select * bulk collect into tmp_sessions from v$session where sid in (&snapper_sid); for i in 1..tmp_sessions.count loop --output('get_sessions i='||i||' sid='||tmp_sessions(i).sid); l_return_sessions(tmp_sessions(i).sid) := tmp_sessions(i); end loop; return l_return_sessions; end; -- get_sessions

/*---------------------------------------------------- functions for extracting and converting v$session -- records to varchar2 ---------------------------------------------------*/ function sitem(p in varchar2) return varchar2 as begin return '<'||translate(p, '<>', '__')||'>'; end; -- sitem varchar2 function sitem(p in number) return varchar2 as begin return '<'||to_char(p)||'>'; end; -- sitem number function sitem(p in date) return varchar2 as begin return '<'||to_char(p, 'YYYY-MM-DD HH24:MI:SS')||'>'; end; -- sitem date function sitem_raw(p in raw) return varchar2 as begin return '<'||upper(rawtohex(p))||'>'; end; -- sitem_raw /*---------------------------------------------------- proc for resetting the snapper ash array ---------------------------------------------------*/ procedure reset_ash is

begin g_ash_samples_taken := 0; -- clear g_ash g_ash := new sys.dbms_debug_vc2coll(); end; -- reset_ash /*---------------------------------------------------- proc for getting ash style samples from v$session -- (and v$session_wait in 9i) ---------------------------------------------------*/ procedure extract_ash is ash_i number; s v$session%rowtype; begin -- keep track how many times we sampled v$session so we could calculate a verages later on g_ash_samples_taken := g_ash_samples_taken + 1; --output('g_sessions.count='||g_sessions.count); ash_i := g_sessions.first; while ash_i is not null loop s := g_sessions(ash_i); -- only extract active sessions, TODO: get rid of wait_class for 9i co mpatibility if -- active, on cpu (s.status = 'ACTIVE' and s.state != 'WAITING' and s.sid != g_mysid ) or -- active, waiting for non-idle wait (s.status = 'ACTIVE' and s.state = 'WAITING' and s.wait_class != ' Idle' and s.sid != g_mysid) then --output('extract_ash: i='||i||' sid='||s.sid||' hv='||s.sql_hash_v alue||' sqlid='||s.sql_id); -- if not actually waiting for anything, clear the past wait event details if s.state != 'WAITING' then s.state:='ON CPU'; s.event:='ON CPU'; s.wait_class:='ON CPU'; s.p1:=NULL; s.p2:=NULL; s.p3:=NULL; end if; g_ash.extend; -- max length 1000 bytes (due to dbms_debug_vc2coll) g_ash(g_ash.count) := substr( sitem(s.sid) -- 1 ||sitem(s.username) -- 2 ||sitem(s.machine) -- 3 ||sitem(s.terminal) -- 4 ||sitem(s.program) -- 5 ||sitem(s.event) -- 6 ||sitem(s.wait_class) -- 7 10g+

-------

30 64 30 48 64 64

bytes bytes bytes bytes bytes bytes,

&_NO_BLK_INST &_YES_BLK_INST

&_NO_PLSQL_OBJ_ID &_NO_PLSQL_OBJ_ID &_NO_PLSQL_OBJ_ID &_NO_PLSQL_OBJ_ID &_YES_PLSQL_OBJ_ID &_YES_PLSQL_OBJ_ID &_YES_PLSQL_OBJ_ID &_YES_PLSQL_OBJ_ID

, 1,

||sitem(s.state) -- 8 ||sitem(s.p1) -- 9 ||sitem(s.p2) -- 10 ||sitem(s.p3) -- 11 ||sitem(s.row_wait_obj#) -- 12 ||sitem(s.row_wait_file#) -- 13 ||sitem(s.row_wait_block#) -- 14 ||sitem(s.row_wait_row#) -- 15 ||sitem(s.blocking_session_status) -- 16 -- 10g+ ||sitem('N/A') -- 17 -- 10gR2+ ||sitem(s.blocking_instance) -- 17 -- 10gR2+ ||sitem(s.blocking_session) -- 18 -- 10g+ ||sitem(s.sql_hash_value) -- 19 ||sitem(s.sql_id) -- 20 ||sitem(s.sql_child_number) -- 21 -- 10g+ ||sitem('N/A') -- 22 ||sitem('N/A') -- 23 ||sitem('N/A') -- 24 ||sitem('N/A') -- 25 ||sitem(s.plsql_entry_object_id) -- 22 ||sitem(s.plsql_entry_subprogram_id) -- 23 ||sitem(s.plsql_object_id) -- 24 ||sitem(s.plsql_subprogram_id) -- 25 ||sitem(s.module) -- 26 -- 48 bytes ||sitem(s.action) -- 27 -- 32 bytes ||sitem(s.client_identifier) -- 28 -- 64 bytes ||sitem(s.service_name) -- 29 -- 64 bytes 1000);

end if; -- sample is of an active session ash_i := g_sessions.next(ash_i); end loop; exception when no_data_found then output('error in extract_ash(): no_data_found for item '||i); end; -- extract_ash /*---------------------------------------------------- proc for querying performance data into collections ---------------------------------------------------*/ procedure snap( p_snapdate in out date, p_stats in out stab ) is lv_include_stat nclude=' )), '%'); lv_include_latch nclude=' )), '%'); lv_include_time nclude=' )), '%'); lv_include_wait nclude=' )), '%'); begin p_snapdate := sysdate; varchar2(1000) := nvl( lower(getopt('&snapper_options', 'si varchar2(1000) := nvl( lower(getopt('&snapper_options', 'li varchar2(1000) := nvl( lower(getopt('&snapper_options', 'ti varchar2(1000) := nvl( lower(getopt('&snapper_options', 'wi

select * bulk collect into p_stats from ( select 'STAT' stype, sid, statistic# pls_adjust statistic#, value from v$sesstat where sid in (&snapper_sid) and (lv_gather like '%s%' or lv_gather like '%a%') and statistic# in (select /*+ no_unnest */ statistic# from v$statname where lower(name) li ke '%'||lv_include_stat||'%' &_IF_ORA10_OR_HIGHER , lv_include_stat, 'i') or regexp_like (name ) -union all select 'WAIT', sw.sid, en.event# + (select count(*) fro m v$statname) + 1 - pls_adjust, nvl(se.time_waited_micro,0) + ( decode(se.event||sw.state, sw.event||'WAITING', sw.seconds_in_wait, 0) * 1000000 ) value from v$session_wait sw, v$session_event se, v$event_name en where sw.sid = se.sid and se.event = en.name and se.sid in (&snapper_sid) and (lv_gather like '%w%' or lv_gathe r like '%a%') and event# in (select event# from v$ event_name where lower(name) li ke '%'||lv_include_wait||'%' &_IF_ORA10_OR_HIGHER or regexp_like (n ame, lv_include_wait, 'i') ) -&_IF_ORA10_OR_HIGHER union all &_IF_ORA10_OR_HIGHER select 'TIME' stype, sid, stat_id - pls _adjust statistic#, value &_IF_ORA10_OR_HIGHER from v$sess_time_model &_IF_ORA10_OR_HIGHER where sid in (&snapper_sid) &_IF_ORA10_OR_HIGHER and (lv_gather like '%t%' or lv_gathe r like '%a%') &_IF_ORA10_OR_HIGHER and stat_id in (select stat_id from v$s ys_time_model &_IF_ORA10_OR_HIGHER where lower(stat_nam e) like '%'||lv_include_time||'%' &_IF_ORA10_OR_HIGHER or regexp_like (s tat_name, lv_include_time, 'i') &_IF_ORA10_OR_HIGHER ) -union all select 'LATG', -1 sid, l.latch# + (select count(*) from v$statn ame) +

(select count(*) from v$event _name) + 1 - pls_adjust statistic#, l.gets + l.immediate_gets value from v$latch l where (lv_gather like '%l%' or lv_gather like '%a%') and latch# in (select latch# from v$lat chname where lower(name) like ' %'||lv_include_latch||'%' &_IF_ORA10_OR_HIGHER lv_include_latch, 'i') &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE name) + &_IF_X_ACCESSIBLE t_name) + &_IF_X_ACCESSIBLE h) + &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE er like '%a%') &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE name) + &_IF_X_ACCESSIBLE t_name) + &_IF_X_ACCESSIBLE h) + &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE her_wait value &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE sw.other_wait > 0 &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE er like '%a%') or regexp_like (name,

) -&_IF_LOWER_THAN_ORA11 union all &_IF_LOWER_THAN_ORA11 select 'BUFG', -1 sid, &_IF_LOWER_THAN_ORA11 s.indx + &_IF_LOWER_THAN_ORA11 (select count(*) from v$stat &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 &_IF_LOWER_THAN_ORA11 (select count(*) from v$even (select count(*) from v$latc 1 - pls_adjust statistic#, s.why0+s.why1+s.why2 value from x$kcbsw s, x$kcbwh w where s.indx = w.indx and s.why0+s.why1+s.why2 > 0 and (lv_gather like '%b%' or lv_gath

-&_IF_ORA11_OR_HIGHER union all &_IF_ORA11_OR_HIGHER select 'BUFG', -1 sid, &_IF_ORA11_OR_HIGHER sw.indx + &_IF_ORA11_OR_HIGHER (select count(*) from v$stat &_IF_ORA11_OR_HIGHER &_IF_ORA11_OR_HIGHER &_IF_ORA11_OR_HIGHER &_IF_ORA11_OR_HIGHER (select count(*) from v$even (select count(*) from v$latc 1 - pls_adjust statistic#, why.why0+why.why1+why.why2+sw.ot

&_IF_ORA11_OR_HIGHER from &_IF_ORA11_OR_HIGHER x$kcbuwhy why, &_IF_ORA11_OR_HIGHER x$kcbwh dsc, &_IF_ORA11_OR_HIGHER x$kcbsw sw &_IF_ORA11_OR_HIGHER where &_IF_ORA11_OR_HIGHER why.indx = dsc.indx &_IF_ORA11_OR_HIGHER and why.why0 + why.why1 + why.why2 + &_IF_ORA11_OR_HIGHER and &_IF_ORA11_OR_HIGHER and &_IF_ORA11_OR_HIGHER &_IF_ORA11_OR_HIGHER and -dsc.indx = sw.indx why.indx = sw.indx -- deliberate cartesian join (lv_gather like '%b%' or lv_gath

union all select 'ENQG', -1 sid, ascii(substr(e.eq_type,1,1))*256 + ascii(substr(e.eq_type,2,1)) + (select count(*) from v$statn ame) + (select count(*) from v$event _name) + (select count(*) from v$latch ) + &_IF_X_ACCESSIBLE ) + (select count(*) from x$kcbwh 1 - pls_adjust statistic#, e.total_req# value from v$enqueue_stat e where (lv_gather like '%e%' or lv_gathe r like '%a%') ) snapper_stats order by sid, stype, statistic#; end snap; /*---------------------------------------------------- proc for dumping ASH data out in grouped -- and ordered fashion ---------------------------------------------------*/ procedure out_ash( p_ash_columns in varchar2, p_topn in number := 10 ) as -- whether to print given column or not p_sid number := p_username number := p_machine number := p_terminal number := p_program number := p_event number := p_wait_class number := p_state number := p_p1 number := p_p2 number := p_p3 number := p_row_wait_obj# number := p_row_wait_file# number := p_row_wait_block# number := p_row_wait_row# number := p_blocking_session_status number := p_blocking_instance number := p_blocking_session number := p_sql_hash_value number := p_sql_id number := p_sql_child_number number := p_plsql_entry_object_id number := p_plsql_entry_subprogram_id number := p_plsql_object_id number := p_plsql_subprogram_id number := p_module number := p_action number := p_client_identifier number := p_service_name number := 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0; 0;

-- temporary variables for holding session details (for later formatting ) o_sid o_username o_machine o_terminal o_program o_event o_wait_class o_state o_p1 o_p2 o_p3 o_row_wait_obj# o_row_wait_file# o_row_wait_block# o_row_wait_row# o_blocking_session_status o_blocking_instance o_blocking_session o_sql_hash_value o_sql_id o_sql_child_number o_plsql_entry_object_id o_plsql_entry_subprogram_id o_plsql_object_id o_plsql_subprogram_id o_module o_action o_client_identifier o_service_name -- helper local vars l_ash_grouping l_output_line l_ash_header_line begin -- bail out if no ASH samples recorded if g_ash.count = 0 then output(' <No active sessions captured during the sampling period>') ; return; end if; l_ash_header_line := 'Active%'; -- ash,ash1,ash2,ash3 parameter column group tokenizer for s in ( SELECT LEVEL , SUBSTR ( TOKEN , DECODE(LEVEL, 1, 1, INSTR(TOKEN, DELIMITER, 1, LEVEL-1)+1) , INSTR(TOKEN, DELIMITER, 1, LEVEL) DECODE(LEVEL, 1, 1, INSTR(TOKEN, DELIMITER, 1, LEVEL-1)+1) ) TOKEN FROM ( SELECT REPLACE( LOWER(p_ash_columns) ,' ','')||'+' AS TOKEN , '+' AS DELIMITER varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); varchar2(100); number := 0; varchar2(4000); varchar2(4000);

FROM DUAL ) CONNECT BY INSTR(TOKEN, DELIMITER, 1, LEVEL)>0 ORDER BY LEVEL ASC ) loop case s.token -- actual column names in v$session when 'sid' then l_ash_grouping := l_ash_g c_sid ; l_ash_header_line := l_ash_header_line || lpad('SID' , w_sid , ' '); when 'username' then l_ash_grouping := l_ash_g c_username ; l_ash_header_line := l_ash_header_line || rpad('USERNAME' , w_username , ' '); when 'machine' then l_ash_grouping := l_ash_g c_machine ; l_ash_header_line := l_ash_header_line || rpad('MACHINE' , w_machine , ' '); when 'terminal' then l_ash_grouping := l_ash_g c_terminal ; l_ash_header_line := l_ash_header_line || rpad('TERMINAL' , w_terminal , ' '); when 'program' then l_ash_grouping := l_ash_g c_program ; l_ash_header_line := l_ash_header_line || rpad('PROGRAM' , w_program , ' '); when 'event' then l_ash_grouping := l_ash_g c_event ; l_ash_header_line := l_ash_header_line || rpad('EVENT' , w_event , ' '); when 'wait_class' then l_ash_grouping := l_ash_g c_wait_class ; l_ash_header_line := l_ash_header_line || rpad('WAIT_CLASS' , w_wait_class , ' '); when 'state' then l_ash_grouping := l_ash_g c_state ; l_ash_header_line := l_ash_header_line || rpad('STATE' , w_state , ' '); when 'p1' then l_ash_grouping := l_ash_g c_p1 ; l_ash_header_line := l_ash_header_line || rpad('P1' , w_p1 , ' '); when 'p2' then l_ash_grouping := l_ash_g c_p2 ; l_ash_header_line := l_ash_header_line || rpad('P2' , w_p2 , ' '); when 'p3' then l_ash_grouping := l_ash_g c_p3 ; l_ash_header_line := l_ash_header_line || rpad('P3' , w_p3 , ' '); when 'row_wait_obj#' then l_ash_grouping := l_ash_g c_row_wait_obj# ; l_ash_header_line := l_ash_header_line || rpad('ROW_WAIT_OBJ#' , w_row_wait_obj# , ' '); when 'row_wait_file#' then l_ash_grouping := l_ash_g c_row_wait_file# ; l_ash_header_line := l_ash_header_line || rpad('ROW_WAIT_FILE#' , w_row_wait_file# , ' '); when 'row_wait_block#' then l_ash_grouping := l_ash_g c_row_wait_block# ; l_ash_header_line := l_ash_header_line || rpad('ROW_WAIT_BLOCK#' , w_row_wait_block# , ' '); when 'row_wait_row#' then l_ash_grouping := l_ash_g c_row_wait_row# ; l_ash_header_line := l_ash_header_line || rpad('ROW_WAIT_ROW#' , w_row_wait_row# , ' '); when 'blocking_session_status' then l_ash_grouping := l_ash_g c_blocking_session_status ; l_ash_header_line := l_ash_header_line || rpad('BLOCKING_SESSION_STATUS' , w_blocking_session_status , ' '); when 'blocking_instance' then l_ash_grouping := l_ash_g

rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' || rouping + ' | ' ||

rouping + c_blocking_instance ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('BLOCKING_INSTANCE' , w_blocking_instance , ' '); when 'blocking_session' then l_ash_grouping := l_ash_g rouping + c_blocking_session ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('BLOCKING_SESSION' , w_blocking_session , ' '); when 'sql_hash_value' then l_ash_grouping := l_ash_g rouping + c_sql_hash_value ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_HASH_VALUE' , w_sql_hash_value , ' '); when 'sql_id' then l_ash_grouping := l_ash_g rouping + c_sql_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_ID' , w_sql_id , ' '); when 'sql_child_number' then l_ash_grouping := l_ash_g rouping + c_sql_child_number ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_CHILD_NUMBER' , w_sql_child_number , ' '); when 'plsql_entry_object_id' then l_ash_grouping := l_ash_g rouping + c_plsql_entry_object_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_ENTRY_OBJECT_ID' , w_plsql_entry_object_id , ' '); when 'plsql_entry_subprogram_id' then l_ash_grouping := l_ash_g rouping + c_plsql_entry_subprogram_id; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_ENTRY_SUBPROGRAM_ID' , w_plsql_entry_subprogram_id, ' '); when 'plsql_object_id' then l_ash_grouping := l_ash_g rouping + c_plsql_object_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_OBJECT_ID' , w_plsql_object_id , ' '); when 'plsql_subprogram_id' then l_ash_grouping := l_ash_g rouping + c_plsql_subprogram_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_SUBPROGRAM_ID' , w_plsql_subprogram_id , ' '); when 'module' then l_ash_grouping := l_ash_g rouping + c_module ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('MODULE' , w_module , ' '); when 'action' then l_ash_grouping := l_ash_g rouping + c_action ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ACTION' , w_action , ' '); when 'client_identifier' then l_ash_grouping := l_ash_g rouping + c_client_identifier ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('CLIENT_IDENTIFIER' , w_client_identifier , ' '); when 'service_name' then l_ash_grouping := l_ash_g rouping + c_service_name ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SERVICE_NAME' , w_service_name , ' '); -- aliases for convenience (only either real name or alias should be used together at the same time) , ' '); when 'user' then l_ash_grouping := l_ash_g rouping + c_username ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('USERNAME' , w_username , ' '); when 'obj' then l_ash_grouping := l_ash_g rouping + c_row_wait_obj# ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ROW_WAIT_OBJ#' , w_row_wait_obj# , ' '); when 'file' then l_ash_grouping := l_ash_g rouping + c_row_wait_file# ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ROW_WAIT_FILE#' , w_row_wait_file# , ' '); when 'block' then l_ash_grouping := l_ash_g rouping + c_row_wait_block# ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ROW_WAIT_BLOCK#' , w_row_wait_block# , ' '); when 'row' then l_ash_grouping := l_ash_g rouping + c_row_wait_row# ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ROW_WAIT_ROW#' , w_row_wait_row# , ' '); when 'bss' then l_ash_grouping := l_ash_g rouping + c_blocking_session_status ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('BLOCKING_SESSION_STATUS' , w_blocking_session_status , ' '); when 'bsi' then l_ash_grouping := l_ash_g rouping + c_blocking_instance ; l_ash_header_line := l_ash_header_line ||

' | ' || rpad('BLOCKING_INSTANCE' , w_blocking_instance , ' '); when 'bs' then l_ash_grouping := l_ash_g rouping + c_blocking_session ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('BLOCKING_SESSION' , w_blocking_session , ' '); when 'sql' then l_ash_grouping := l_ash_g rouping + c_sql_hash_value ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_HASH_VALUE' , w_sql_hash_value , ' '); when 'sqlid' then l_ash_grouping := l_ash_g rouping + c_sql_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_ID' , w_sql_id , ' '); when 'child' then l_ash_grouping := l_ash_g rouping + c_sql_child_number ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SQL_CHILD_NUMBER' , w_sql_child_number , ' '); when 'plsql_eoid' then l_ash_grouping := l_ash_g rouping + c_plsql_entry_object_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_ENTRY_OBJECT_ID' , w_plsql_entry_object_id , ' '); when 'plsql_esubpid' then l_ash_grouping := l_ash_g rouping + c_plsql_entry_subprogram_id; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_ENTRY_SUBPROGRAM_ID' , w_plsql_entry_subprogram_id, ' '); when 'plsql_oid' then l_ash_grouping := l_ash_g rouping + c_plsql_object_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_OBJECT_ID' , w_plsql_object_id , ' '); when 'plsql_subpid' then l_ash_grouping := l_ash_g rouping + c_plsql_subprogram_id ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('PLSQL_SUBPROGRAM_ID' , w_plsql_subprogram_id , ' '); when 'mod' then l_ash_grouping := l_ash_g rouping + c_module ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('MODULE' , w_module , ' '); when 'act' then l_ash_grouping := l_ash_g rouping + c_action ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('ACTION' , w_action , ' '); when 'cid' then l_ash_grouping := l_ash_g rouping + c_client_identifier ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('CLIENT_IDENTIFIER' , w_client_identifier , ' '); when 'service' then l_ash_grouping := l_ash_g rouping + c_service_name ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('SERVICE_NAME' , w_service_name , ' '); when 'wait_event' then l_ash_grouping := l_ash_g rouping + c_event ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('EVENT' , w_event , ' '); when 'wait_state' then l_ash_grouping := l_ash_g rouping + c_state ; l_ash_header_line := l_ash_header_line || ' | ' || rpad('STATE' , w_state , ' '); else null; -- raise_application_error(-20000, 'Invalid ASH column name'); end case; -- case s.token end loop; -- tokenizer output(' '); output(lpad('-',length(l_ash_header_line),'-')); output(l_ash_header_line); output(lpad('-',length(l_ash_header_line),'-')); -- this is needed for "easy" sorting and group by ops (without any custom stored object types!) for i in ( with raw_records as ( select column_value rec from table(cast(g_ash as sys.dbms_debug_vc2

coll)) ), ash_records as ( select substr(r.rec, instr(r.rec, '<', 1, 1)+1, instr instr(r.rec, '<', 1, 1)+1), '>')-1) sid , substr(r.rec, instr(r.rec, '<', 1, 2)+1, instr instr(r.rec, '<', 1, 2)+1), '>')-1) username , substr(r.rec, instr(r.rec, '<', 1, 3)+1, instr instr(r.rec, '<', 1, 3)+1), '>')-1) machine , substr(r.rec, instr(r.rec, '<', 1, 4)+1, instr instr(r.rec, '<', 1, 4)+1), '>')-1) terminal , substr(r.rec, instr(r.rec, '<', 1, 5)+1, instr instr(r.rec, '<', 1, 5)+1), '>')-1) program , substr(r.rec, instr(r.rec, '<', 1, 6)+1, instr instr(r.rec, '<', 1, 6)+1), '>')-1) event , substr(r.rec, instr(r.rec, '<', 1, 7)+1, instr instr(r.rec, '<', 1, 7)+1), '>')-1) wait_class , substr(r.rec, instr(r.rec, '<', 1, 8)+1, instr instr(r.rec, '<', 1, 8)+1), '>')-1) state , substr(r.rec, instr(r.rec, '<', 1, 9)+1, instr instr(r.rec, '<', 1, 9)+1), '>')-1) p1 , substr(r.rec, instr(r.rec, '<', 1, 10)+1, instr instr(r.rec, '<', 1, 10)+1), '>')-1) p2 , substr(r.rec, instr(r.rec, '<', 1, 11)+1, instr instr(r.rec, '<', 1, 11)+1), '>')-1) p3 , substr(r.rec, instr(r.rec, '<', 1, 12)+1, instr instr(r.rec, '<', 1, 12)+1), '>')-1) row_wait_obj# , substr(r.rec, instr(r.rec, '<', 1, 13)+1, instr instr(r.rec, '<', 1, 13)+1), '>')-1) row_wait_file# , substr(r.rec, instr(r.rec, '<', 1, 14)+1, instr instr(r.rec, '<', 1, 14)+1), '>')-1) row_wait_block# , substr(r.rec, instr(r.rec, '<', 1, 15)+1, instr instr(r.rec, '<', 1, 15)+1), '>')-1) row_wait_row# , substr(r.rec, instr(r.rec, '<', 1, 16)+1, instr instr(r.rec, '<', 1, 16)+1), '>')-1) blocking_session_status , substr(r.rec, instr(r.rec, '<', 1, 17)+1, instr instr(r.rec, '<', 1, 17)+1), '>')-1) blocking_instance , substr(r.rec, instr(r.rec, '<', 1, 18)+1, instr instr(r.rec, '<', 1, 18)+1), '>')-1) blocking_session , substr(r.rec, instr(r.rec, '<', 1, 19)+1, instr instr(r.rec, '<', 1, 19)+1), '>')-1) sql_hash_value , substr(r.rec, instr(r.rec, '<', 1, 20)+1, instr instr(r.rec, '<', 1, 20)+1), '>')-1) sql_id , substr(r.rec, instr(r.rec, '<', 1, 21)+1, instr instr(r.rec, '<', 1, 21)+1), '>')-1) sql_child_number , substr(r.rec, instr(r.rec, '<', 1, 22)+1, instr instr(r.rec, '<', 1, 22)+1), '>')-1) plsql_entry_object_id , substr(r.rec, instr(r.rec, '<', 1, 23)+1, instr instr(r.rec, '<', 1, 23)+1), '>')-1) plsql_entry_subprogram_id , substr(r.rec, instr(r.rec, '<', 1, 24)+1, instr instr(r.rec, '<', 1, 24)+1), '>')-1) plsql_object_id , substr(r.rec, instr(r.rec, '<', 1, 25)+1, instr instr(r.rec, '<', 1, 25)+1), '>')-1) plsql_subprogram_id , substr(r.rec, instr(r.rec, '<', 1, 26)+1, instr instr(r.rec, '<', 1, 26)+1), '>')-1) module , substr(r.rec, instr(r.rec, '<', 1, 27)+1, instr instr(r.rec, '<', 1, 27)+1), '>')-1) action , substr(r.rec, instr(r.rec, '<', 1, 28)+1, instr instr(r.rec, '<', 1, 28)+1), '>')-1) client_identifier

(substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec, (substr(r.rec,

, substr(r.rec, instr(r.rec, '<', 1, 29)+1, instr (substr(r.rec, instr(r.rec, '<', 1, 29)+1), '>')-1) service_name from raw_records r ) select * from ( select decode(bitand(l_ash_grouping, power(2, s_sid )), 0, chr(0), sid ) as sid , decode(bitand(l_ash_grouping, power(2, s_username )), 0, chr(0), username ) as username , decode(bitand(l_ash_grouping, power(2, s_machine )), 0, chr(0), machine ) as machine , decode(bitand(l_ash_grouping, power(2, s_terminal )), 0, chr(0), terminal ) as terminal , decode(bitand(l_ash_grouping, power(2, s_program )), 0, chr(0), program ) as program , decode(bitand(l_ash_grouping, power(2, s_event )), 0, chr(0), event ) as event , decode(bitand(l_ash_grouping, power(2, s_wait_class )), 0, chr(0), wait_class ) as wait_class , decode(bitand(l_ash_grouping, power(2, s_state )), 0, chr(0), state ) as state , decode(bitand(l_ash_grouping, power(2, s_p1 )), 0, chr(0), p1 ) as p1 , decode(bitand(l_ash_grouping, power(2, s_p2 )), 0, chr(0), p2 ) as p2 , decode(bitand(l_ash_grouping, power(2, s_p3 )), 0, chr(0), p3 ) as p3 , decode(bitand(l_ash_grouping, power(2, s_row_wait_obj# )), 0, chr(0), row_wait_obj# ) as row_wait_obj# , decode(bitand(l_ash_grouping, power(2, s_row_wait_file# )), 0, chr(0), row_wait_file# ) as row_wait_file# , decode(bitand(l_ash_grouping, power(2, s_row_wait_block# )), 0, chr(0), row_wait_block# ) as row_wait_block# , decode(bitand(l_ash_grouping, power(2, s_row_wait_row# )), 0, chr(0), row_wait_row# ) as row_wait_row# , decode(bitand(l_ash_grouping, power(2, s_blocking_session_status )), 0, chr(0), blocking_session_status ) as blocking_session_status , decode(bitand(l_ash_grouping, power(2, s_blocking_instance )), 0, chr(0), blocking_instance ) as blocking_instance , decode(bitand(l_ash_grouping, power(2, s_blocking_session )), 0, chr(0), blocking_session ) as blocking_session

, decode(bitand(l_ash_grouping, power(2, s_sql_hash_value )), 0, chr(0), sql_hash_value ) as sql_hash_value , decode(bitand(l_ash_grouping, power(2, s_sql_id )), 0, chr(0), sql_id ) as sql_id , decode(bitand(l_ash_grouping, power(2, s_sql_child_number )), 0, chr(0), sql_child_number ) as sql_child_number , decode(bitand(l_ash_grouping, power(2, s_plsql_entry_object_id )), 0, chr(0), plsql_entry_object_id ) as plsql_entry_object_id d d , decode(bitand(l_ash_grouping, power(2, s_plsql_entry_subprogram_i )), 0, chr(0), plsql_entry_subprogram_id ) as plsql_entry_subprogram_i , decode(bitand(l_ash_grouping, power(2, s_plsql_object_id )), 0, chr(0), plsql_object_id ) as plsql_object_id , decode(bitand(l_ash_grouping, power(2, s_plsql_subprogram_id )), 0, chr(0), plsql_subprogram_id ) as plsql_subprogram_id , decode(bitand(l_ash_grouping, power(2, s_module )), 0, chr(0), module ) as module , decode(bitand(l_ash_grouping, power(2, s_action )), 0, chr(0), action ) as action , decode(bitand(l_ash_grouping, power(2, s_client_identifier )), 0, chr(0), client_identifier ) as client_identifier , decode(bitand(l_ash_grouping, power(2, s_service_name )), 0, chr(0), service_name ) as service_name , count(*)/g_ash_samples_taken average_active_samples from ash_records a group by decode(bitand(l_ash_grouping, power(2, s_sid )), 0, chr(0), sid ) -- sid , decode(bitand(l_ash_grouping, power(2, s_username )), 0, chr(0), username ) -- username , decode(bitand(l_ash_grouping, power(2, s_machine )), 0, chr(0), machine ) -- machine , decode(bitand(l_ash_grouping, power(2, s_terminal )), 0, chr(0), terminal ) -- terminal , decode(bitand(l_ash_grouping, power(2, s_program )), 0, chr(0), program ) -- program , decode(bitand(l_ash_grouping, power(2, s_event )), 0, chr(0), event ) -- event , decode(bitand(l_ash_grouping, power(2, s_wait_class )), 0, chr(0), wait_class ) -- wait_class , decode(bitand(l_ash_grouping, power(2, s_state

)), 0, chr(0), state

) -- state

, decode(bitand(l_ash_grouping, power(2, s_p1 )), 0, chr(0), p1 ) -- p1 , decode(bitand(l_ash_grouping, power(2, s_p2 )), 0, chr(0), p2 ) -- p2 , decode(bitand(l_ash_grouping, power(2, s_p3 )), 0, chr(0), p3 ) -- p3 , decode(bitand(l_ash_grouping, power(2, s_row_wait_obj# )), 0, chr(0), row_wait_obj# ) -- row_wait_obj# , decode(bitand(l_ash_grouping, power(2, s_row_wait_file# )), 0, chr(0), row_wait_file# ) -- row_wait_file# , decode(bitand(l_ash_grouping, power(2, s_row_wait_block# )), 0, chr(0), row_wait_block# ) -- row_wait_block# , decode(bitand(l_ash_grouping, power(2, s_row_wait_row# )), 0, chr(0), row_wait_row# ) -- row_wait_row# , decode(bitand(l_ash_grouping, power(2, s_blocking_session_status )), 0, chr(0), blocking_session_status ) -- blocking_session_status , decode(bitand(l_ash_grouping, power(2, s_blocking_instance )), 0, chr(0), blocking_instance ) -- blocking_instance , decode(bitand(l_ash_grouping, power(2, s_blocking_session )), 0, chr(0), blocking_session ) -- blocking_session , decode(bitand(l_ash_grouping, power(2, s_sql_hash_value )), 0, chr(0), sql_hash_value ) -- sql_hash_value , decode(bitand(l_ash_grouping, power(2, s_sql_id )), 0, chr(0), sql_id ) -- sql_id , decode(bitand(l_ash_grouping, power(2, s_sql_child_number )), 0, chr(0), sql_child_number ) -- sql_child_number , decode(bitand(l_ash_grouping, power(2, s_plsql_entry_object_id )), 0, chr(0), plsql_entry_object_id ) -- plsql_entry_object_id d d , decode(bitand(l_ash_grouping, power(2, s_plsql_entry_subprogram_i )), 0, chr(0), plsql_entry_subprogram_id ) -- plsql_entry_subprogram_i , decode(bitand(l_ash_grouping, power(2, s_plsql_object_id )), 0, chr(0), plsql_object_id ) -- plsql_object_id , decode(bitand(l_ash_grouping, power(2, s_plsql_subprogram_id )), 0, chr(0), plsql_subprogram_id ) -- plsql_subprogram_id , decode(bitand(l_ash_grouping, power(2, s_module )), 0, chr(0), module ) -- module , decode(bitand(l_ash_grouping, power(2, s_action )), 0, chr(0), action ) -- action , decode(bitand(l_ash_grouping, power(2, s_client_identifier

)), 0, chr(0), client_identifier

) -- client_identifier

, decode(bitand(l_ash_grouping, power(2, s_service_name )), 0, chr(0), service_name ) -- service_name order by count(*)/g_ash_samples_taken desc ) where rownum <= p_topn ) loop l_output_line := ''; o_sid := CASE WHEN i.sid = chr(0) THEN null ELSE nvl(i.sid , ' ') END; o_username := CASE WHEN i.username = chr(0) THEN null ELSE nvl(i.username , ' ') END; o_machine := CASE WHEN i.machine = chr(0) THEN null ELSE nvl(i.machine , ' ') END; o_terminal := CASE WHEN i.terminal = chr(0) THEN null ELSE nvl(i.terminal , ' ') END; o_program := CASE WHEN i.program = chr(0) THEN null ELSE nvl(i.program , ' ') END; o_event := CASE WHEN i.event = chr(0) THEN null ELSE nvl(i.event , ' ') END; o_wait_class := CASE WHEN i.wait_class = chr(0) THEN null ELSE nvl(i.wait_class , ' ') END; o_state := CASE WHEN i.state = chr(0) THEN null ELSE nvl(i.state , ' ') END; o_p1 := CASE WHEN i.p1 = chr(0) THEN null ELSE nvl(i.p1 , ' ') END; o_p2 := CASE WHEN i.p2 = chr(0) THEN null ELSE nvl(i.p2 , ' ') END; o_p3 := CASE WHEN i.p3 = chr(0) THEN null ELSE nvl(i.p3 , ' ') END; o_row_wait_obj# := CASE WHEN i.row_wait_obj# = chr(0) THEN null ELSE nvl(i.row_wait_obj# , ' ') END; o_row_wait_file# := CASE WHEN i.row_wait_file# = chr(0) THEN null ELSE nvl(i.row_wait_file# , ' ') END; o_row_wait_block# := CASE WHEN i.row_wait_block# = chr(0) THEN null ELSE nvl(i.row_wait_block# , ' ') END; o_row_wait_row# := CASE WHEN i.row_wait_row# = chr(0) THEN null ELSE nvl(i.row_wait_row# , ' ') END; o_blocking_session_status := CASE WHEN i.blocking_session_status = chr(0) THEN null ELSE nvl(i.blocking_session_status , ' ') END; o_blocking_instance := CASE WHEN i.blocking_instance = chr(0) THEN null ELSE nvl(i.blocking_instance , ' ') END; o_blocking_session := CASE WHEN i.blocking_session = chr(0) THEN null ELSE nvl(i.blocking_session , ' ') END; o_sql_hash_value := CASE WHEN i.sql_hash_value = chr(0) THEN null ELSE nvl(i.sql_hash_value , ' ') END; o_sql_id := CASE WHEN i.sql_id = chr(0) THEN null ELSE nvl(i.sql_id , ' ') END; o_sql_child_number := CASE WHEN i.sql_child_number = chr(0) THEN null ELSE nvl(i.sql_child_number , ' ') END; o_plsql_entry_object_id := CASE WHEN i.plsql_entry_object_id = chr(0) THEN null ELSE nvl(i.plsql_entry_object_id , ' ') END; o_plsql_entry_subprogram_id := CASE WHEN i.plsql_entry_subprogram_id = chr(0) THEN null ELSE nvl(i.plsql_entry_subprogram_id , ' ') END; o_plsql_object_id := CASE WHEN i.plsql_object_id

= chr(0) THEN null ELSE nvl(i.plsql_object_id , ' ') END; o_plsql_subprogram_id := CASE WHEN i.plsql_subprogram_id = chr(0) THEN null ELSE nvl(i.plsql_subprogram_id , ' ') END; o_module := CASE WHEN i.module = chr(0) THEN null ELSE nvl(i.module , ' ') END; o_action := CASE WHEN i.action = chr(0) THEN null ELSE nvl(i.action , ' ') END; o_client_identifier := CASE WHEN i.client_identifier = chr(0) THEN null ELSE nvl(i.client_identifier , ' ') END; o_service_name := CASE WHEN i.service_name = chr(0) THEN null ELSE nvl(i.service_name , ' ') END; -- print the activity % as the first column l_output_line := lpad(to_char(round(i.average_active_samples*100))||'% ', w_activity_pct, ' '); -- loop through ash columns to find what to print and in which order for s in ( SELECT LEVEL , SUBSTR ( TOKEN , DECODE(LEVEL, 1, 1, INSTR(TOKEN, DELIMITER, 1, LEVEL-1 )+1) , INSTR(TOKEN, DELIMITER, 1, LEVEL) DECODE(LEVEL, 1, 1, INSTR(TOKEN, DELIMITER, 1, LEVEL-1)+1 ) ) TOKEN FROM ( SELECT REPLACE( LOWER(p_ash_columns) ,' ','')||'+' AS TOK EN , '+' FROM DUAL ) CONNECT BY INSTR(TOKEN, DELIMITER, 1, ORDER BY LEVEL ASC ) loop l_output_line := l_output_line case s.token -- actual column names when 'sid' , w_sid when 'username' , w_username when 'machine' , w_machine when 'terminal' , w_terminal when 'program' , w_program when 'event' , w_event when 'wait_class' , w_wait_class when 'state' , w_state when 'p1' , w_p1 when 'p2' AS DELIMITER

LEVEL)>0

|| ' | ' || in v$session then lpad(o_sid , ' ') then rpad(o_username , ' ') then rpad(o_machine , ' ') then rpad(o_terminal , ' ') then rpad(o_program , ' ') then rpad(o_event , ' ') then rpad(o_wait_class , ' ') then rpad(o_state , ' ') then rpad(o_p1 , ' ') then rpad(o_p2

, w_p2 when 'p3' , w_p3 bj#

, ' ') then rpad(o_p3 , ' ')

when 'row_wait_obj#' then rpad(o_row_wait_o , w_row_wait_obj# , ' ') when 'row_wait_file#' then rpad(o_row_wait_f ile# , w_row_wait_file# , ' ') when 'row_wait_block#' then rpad(o_row_wait_b lock# , w_row_wait_block# , ' ') when 'row_wait_row#' then rpad(o_row_wait_r ow# , w_row_wait_row# , ' ') when 'blocking_session_status' then rpad(o_blocking_s ession_status , w_blocking_session_status , ' ') when 'blocking_instance' then rpad(o_blocking_i nstance , w_blocking_instance , ' ') when 'blocking_session' then rpad(o_blocking_s ession , w_blocking_session , ' ') when 'sql_hash_value' then rpad(o_sql_hash_v alue , w_sql_hash_value , ' ') when 'sql_id' then rpad(o_sql_id , w_sql_id , ' ') when 'sql_child_number' then rpad(o_sql_child_ number , w_sql_child_number , ' ') when 'plsql_entry_object_id' then rpad(o_plsql_entr y_object_id , w_plsql_entry_object_id , ' ') when 'plsql_entry_subprogram_id' then rpad(o_plsql_entr y_subprogram_id , w_plsql_entry_subprogram_id, ' ') when 'plsql_object_id' then rpad(o_plsql_obje ct_id , w_plsql_object_id , ' ') when 'plsql_subprogram_id' then rpad(o_plsql_subp rogram_id , w_plsql_subprogram_id , ' ') when 'module' then rpad(o_module , w_module , ' ') when 'action' then rpad(o_action , w_action , ' ') when 'client_identifier' then rpad(o_client_ide ntifier , w_client_identifier , ' ') when 'service_name' then rpad(o_service_na me , w_service_name , ' ') -- aliases for convenience (only either real name or alias should be used together at the same time) when 'user' then rpad(o_username , w_username , ' ') when 'obj' then rpad(o_row_wait_o bj# , w_row_wait_obj# , ' ') when 'file' then rpad(o_row_wait_f ile# , w_row_wait_file# , ' ') when 'block' then rpad(o_row_wait_b lock# , w_row_wait_block# , ' ') when 'row' then rpad(o_row_wait_r ow# , w_row_wait_row# , ' ') when 'bss' then rpad(o_blocking_s ession_status , w_blocking_session_status , ' ') when 'bsi' then rpad(o_blocking_i nstance , w_blocking_instance , ' ') when 'bs' then rpad(o_blocking_s ession , w_blocking_session , ' ') when 'sql' then rpad(o_sql_hash_v alue , w_sql_hash_value , ' ') when 'sqlid' then rpad(o_sql_id

, w_sql_id , when 'child' number , w_sql_child_number , when 'plsql_eoid' y_object_id , w_plsql_entry_object_id , when 'plsql_esubpid' y_subprogram_id , w_plsql_entry_subprogram_id, when 'plsql_oid' ct_id , w_plsql_object_id , when 'plsql_subpid' rogram_id , w_plsql_subprogram_id , when 'mod' , w_module , when 'act' , w_action , when 'cid' ntifier , w_client_identifier , when 'service' me , w_service_name , when 'wait_event' , w_event , when 'wait_state' , w_state , else '' end; -- case s.token end loop; -- ash parameter tokenizer output(l_output_line); end loop; -- grouped ash samples end out_ash; -- and it begins!!! begin

' ') then rpad(o_sql_child_ ' ') then rpad(o_plsql_entr ' ') then rpad(o_plsql_entr ' ') then rpad(o_plsql_obje ' ') then rpad(o_plsql_subp ' ') then rpad(o_module ' ') then rpad(o_action ' ') then rpad(o_client_ide ' ') then rpad(o_service_na ' ') then rpad(o_event ' ') then rpad(o_state ' ')

-- get snappers own sid into g_mysid select sid into g_mysid from v$mystat where rownum = 1; pagesize := nvl( getopt('&snapper_options', 'pagesize=' ), pagesize); --output ( 'Pagesize='||pagesize ); lv_ash := getopt('&snapper_options', 'ash'); lv_stats := getopt('&snapper_options', 'stat'); if lv_ash is not null then gather_ash := 1; end if; if lv_stats is not null then gather_stats := 1; end if; --output('all='||case when getopt('&snapper_options', 'all') = chr(0) then ' chr(0)' when getopt('&snapper_options', 'all') is null then 'null' else (getopt( '&snapper_options','all')) end); -- some additional default value logic if getopt('&snapper_options', 'all') is not null then output('setting stats to all because of option = all'); gather_stats := 1; gather_ash := 1; else

if (lv_ash is null and lv_stats is null) then gather_stats := 0; gather_ash := 1; end if; end if; -- determine which performance counters and stats to collect lv_gather := case nvl( lower(getopt ('&snapper_options', 'gather=')), 'stw') when 'all' then 'stw' else nvl( lower(getopt ('&snapper_options', 'gather=')), 'st w') end; --lv_gather:=getopt ('&snapper_options', 'gather='); --output('lv_gather='||lv_gather);

if pagesize > 0 then output(' '); output('-- Session Snapper v3.15 by Tanel Poder @ E2SN ( http://tech.e2s n.com )'); output(' '); end if; -- initialize statistic and event name array -- fetch statistic names with their adjusted IDs select * bulk collect into sn_tmp from ( select 'STAT' stype, statistic# - pls_adjust st atistic#, name from v$statname where (lv_gather like '%s%' or lv_gather like ' %a%') -union all select 'WAIT', event# + (select count(*) from v$statnam e) + 1 - pls_adjust, name from v$event_name where (lv_gather like '%w%' or lv_gather like ' %a%') -&_IF_ORA10_OR_HIGHER union all &_IF_ORA10_OR_HIGHER select 'TIME' stype, stat_id - pls_adjust stati stic#, stat_name name &_IF_ORA10_OR_HIGHER from v$sys_time_model &_IF_ORA10_OR_HIGHER where (lv_gather like '%t%' or lv_gather like ' %a%') -union all select 'LATG', l.latch# + (select count(*) from v$statname) + (select count(*) from v$event_name) + 1 - pls_adjust statistic#, name from v$latch l where (lv_gather like '%l%' or lv_gather like '

%a%') &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE &_IF_X_ACCESSIBLE '%a%') -union all select 'ENQG', ascii(substr(e.eq_type,1,1))*256 + ascii( substr(e.eq_type,2,1)) + (select count(*) from v$statname) + (select count(*) from v$event_name) + (select count(*) from v$latch) + (select count(*) from x$kcbwh) + 1 - pls_adjust statistic#, eq_type from ( &_IF_ORA10_OR_HIGHER select es.eq_type ||' - '||lt.name eq_type, total_req# from v$enqueue_stat es , v$lock_type lt where es.eq_type = lt.type ) e where (lv_gather like '%e%' or lv_gather like ' %a%') ) snapper_statnames order by stype, statistic#; -- store these into an index_by array organized by statistic# for fast looku p --output('sn_tmp.count='||sn_tmp.count); --output('lv_gather='||lv_gather); for i in 1..sn_tmp.count loop -- output('i='||i||' statistic#='||sn_tmp(i).statistic#); sn(sn_tmp(i).statistic#) := sn_tmp(i); end loop; -- main sampling loop for c in 1..&snapper_count loop -- sesstat and other performance counter sampling if gather_stats = 1 then -- print header if required gv_header_string := CASE WHEN output_header END || CASE WHEN output_sid = 1 THEN ' SID,' -union all select 'BUFG', indx + (select count(*) from v$statname) + (select count(*) from v$event_name) + (select count(*) from v$latch) + 1 - pls_adjust statistic#, kcbwhdes name from x$kcbwh where (lv_gather like '%b%' or lv_gather like

&_IF_X_ACCESSIBLE

&_IF_ORA10_OR_HIGHER &_IF_ORA10_OR_HIGHER

= 1 THEN 'HEAD,'

END || CASE WHEN output_username = 1 THEN ' USERNAME ,' END || CASE WHEN output_time END || CASE WHEN output_seconds = 1 THEN ' SECONDS,' END || CASE WHEN output_stype END || CASE WHEN output_sname ')||',' END || CASE WHEN output_delta END || CASE WHEN output_delta_s = 1 THEN ' DELTA/SEC,' END || CASE WHEN output_hdelta END || CASE WHEN output_hdelta_s = 1 THEN ' HDELTA/SEC,' END || CASE WHEN output_percent = 1 THEN ' END || CASE WHEN output_pcthist = 1 THEN ' GRAPH END ; if pagesize > 0 and mod(c-1, pagesize) = 0 then output(rpad('-',length(gv_header_string),'-')); output(gv_header_string); output(rpad('-',length(gv_header_string),'-')); else if pagesize = -1 and c = 1 then output(gv_header_string); end if; end if; if c = 1 then get_sessions; snap(d1,s1); else get_sessions; d1 := d2; s1 := s2; end if; -- c = 1 -- output('snapper_sleep='||to_char(&snapper_sleep - (sysdate - d1)* 86400)); -- dbms_lock.sleep( (&snapper_sleep - (sysdate - d1)) ); -- dbms_lock.sleep( (&snapper_sleep - (sysdate - d1))*1000/1024 ); end if; -- gather_stats = 1 ' %TIME,' = 1 THEN ' HDELTA,' = 1 THEN ' DELTA,' = 1 THEN rpad(' STATISTIC',59,' = 1 THEN ' TYPE,' = 1 THEN ' SNAPSHOT START ,'

-- ASH style sampling ash_date1 := sysdate; if gather_ash = 1 then while sysdate < (ash_date1 + (&snapper_sleep/86400)) loop -- get active session records from g_sessions get_sessions; extract_ash(); -- sleep timeout backoff depending on the duration sampled (for u p to 10 seconds total sampling time will get max 100 Hz sampling) -- for longer duration sampling the algorithm will back off and f or long durations (over 100 sec) the sampling rate will stabilize -- at 1Hz dbms_lock.sleep( greatest(0.1,(least(1,&snapper_sleep*&snapper_co unt/100))) ); end loop; else dbms_lock.sleep( ((ash_date1+(&snapper_sleep/86400)) - sysdate)*8640 0 ); end if; ash_date2 := sysdate; -- sesstat new sample and delta calculation if gather_stats = 1 then get_sessions; snap(d2,s2); -- manually coded nested loop outer join for calculating deltas -- why not use a SQL join? this would require creation of PL/SQL -- collection object types, but Snapper does not require any changes -- to the database, so any custom object types are out! changed_values := 0; missing_values_s1 := 0; missing_values_s2 := 0; -- remember last disappeared SID so we woudlnt need to output a warn ing -- message for each statistic row of that disappeared sid disappeared_sid := 0; i :=1; -- iteration counter (for debugging) a :=1; -- s1 array index b :=1; -- s2 array index while ( a <= s1.count and b <= s2.count ) loop delta := 0; -- don't print case when s1(a).sid = s2(b).sid then case when s1(a).statistic# = s2(b).statistic# then delta := s2(b).value - s1(a).value;

if delta != 0 then fout(); end if; a := a + 1; b := b + 1; when s1(a).statistic# > s2(b).statistic# then delta := s2(b).value; if delta != 0 then fout(); end if; b := b + 1; when s1(a).statistic# < s2(b).statistic# then output('ERROR, s1(a).statistic# < s2(b).statisti c#, a='||to_char(a)||' b='||to_char(b)||' s1.count='||s1.count||' s2.count='||s2 .count||' s2.count='||s2.count); a := a + 1; b := b + 1; else output('ERROR, s1(a).statistic# ? s2(b).statisti c#, a='||to_char(a)||' b='||to_char(b)||' s1.count='||s1.count||' s2.count='||s2 .count||' s2.count='||s2.count); a := a + 1; b := b + 1; end case; -- s1(a).statistic# ... s2(b).statistic# when s1(a).sid > s2(b).sid then delta := s2(b).value; if delta != 0 then fout(); end if; b := b + 1; when s1(a).sid < s2(b).sid then if disappeared_sid != s2(b).sid then output('WARN, Session has disappeared during snapsho t, ignoring SID='||to_char(s2(b).sid)||' debug(a='||to_char(a)||' b='||to_char(b )||' s1.count='||s1.count||' s2.count='||s2.count||' s2.count='||s2.count||')'); end if; disappeared_sid := s2(b).sid; a := a + 1; else output('ERROR, Should not be here, SID='||to_char(s2(b). sid)||' a='||to_char(a)||' b='||to_char(b)||' s1.count='||s1.count||' s2.count=' ||s2.count||' s2.count='||s2.count); end case; -- s1(a).sid ... s2(b).sid i:=i+1; if delta != 0 then changed_values := changed_values + 1; end if; -- delta != 0

end loop; -- while ( a <= s1.count and b <= s2.count ) if pagesize > 0 and output('-- End 2, 'YYYY-MM-DD HH24:MI:SS')||', pper_sleep else round((d2-d1) * output(''); end if; output(' '); end if; -- gather_stats = 1 if gather_ash = 1 then -- get ASH sample grouping details g_ash_columns := nvl( getopt('&snapper_options', 'ash=' ), g_ash_c olumns ); -- optional additional ASH groupings g_ash_columns1 := case when getopt('&snapper_options', 'ash1' ) is n null when getopt('&snapper_options', 'ash1' ) = chr(0) then g_ash_colum getopt('&snapper_options', 'ash1=' ) end; g_ash_columns2 := case when getopt('&snapper_options', 'ash2' ) is n null when getopt('&snapper_options', 'ash2' ) = chr(0) then g_ash_colum getopt('&snapper_options', 'ash2=' ) end; g_ash_columns3 := case when getopt('&snapper_options', 'ash3' ) is n null when getopt('&snapper_options', 'ash3' ) = chr(0) then g_ash_colum getopt('&snapper_options', 'ash3=' ) end; -- group ASH records and print report out_ash( g_ash_columns, 10 ); -- group and print optional ASH reports if g_ash_columns1 is not null then out_ash( g_ash_columns1, 10 ); en d if; if g_ash_columns2 is not null then out_ash( g_ash_columns2, 10 ); en d if; if g_ash_columns3 is not null then out_ash( g_ash_columns3, 10 ); en d if; if pagesize > 0 then output(' '); output('-- End of ASH snap '||to_char(c)||', end='||to_char(ash _date2, 'YYYY-MM-DD HH24:MI:SS')||', seconds='||to_char(case (ash_date2-ash_date 1) when 0 then &snapper_sleep else round((ash_date2-ash_date1) * 86400, 1) end)| |', samples_taken='||g_ash_samples_taken); output(' '); end if; reset_ash(); end if; -- gather_ash = 1 end loop; -- for c in 1..snapper_count end; / changed_values > 0 then of Stats snap '||to_char(c)||', end='||to_char(d seconds='||to_char(case (d2-d1) when 0 then &sna 86400, 1) end));

ull then ns1 else ull then ns2 else ull then ns3 else

undefine snapper_oraversion undefine snapper_sleep undefine snapper_count undefine snapper_sid undefine ssid_begin -- undefine _IF_ORA10_OR_HIGHER -- undefine _IF_ORA9 -- undefine _IF_ORA11_OR_HIGHER -- undefine _IF_LOWER_THAN_ORA11 -- undefine _NO_BLK_INST -- undefine _YES_BLK_INST -- undefine _NO_PLSQL_OBJ_ID -- undefine _YES_PLSQL_OBJ_ID -- undefine _IF_DBMS_SYSTEM_ACCESSIBLE -- undefine _IF_X_ACCESSIBLE col snapper_ora10higher clear col snapper_ora11higher clear col snapper_ora11lower clear col dbms_system_accessible clear set serverout off