Capture 10046 Traces Upon User Login (without using a trigger) (文档 ID 371678.1)

In this Document
  Purpose
  Software Requirements/Prerequisites
  Configuring the Script
  Running the Script
  Caution
  Script
  Script Output
  References

APPLIES TO:

Oracle Server - Enterprise Edition - Version: 8.1.7.4 to 11.1.0.8 - Release: 8.1.7 to 11.1
Information in this document applies to any platform.

PURPOSE

This script will capture a 10046 trace for sessions of a particular user as soon as the session exists in the database and is detected by the script.  This script was created to capture traces for very transient connections that where generated by a web application.  This is an alternative to a logon trigger that is useful when a logon trigger is too risky to enable in a production environment.

Note:  There is some risk in using this script!  The script will turn on tracing for a specified number of users - if you trace too many users concurrently, there could be a performance impact.  Also, if you kill the script after it has enabled traces, the sessions will continue tracing until they logoff (or you manually turn off the tracing).

SOFTWARE REQUIREMENTS/PREREQUISITES

Works with Oracle 7.3.4 and higher (tested up to 9.2).  Requires SQLPlus.  

Most of the functionality of this script is available in 10g and above with the DBMS_MONITOR package - please use that instead if possible.

CONFIGURING THE SCRIPT

1.  Save the script listed here to a file (lets call it "tbu.sql" for the rest of this note).

2.  Grant the following access privileges:

SELECT ANY TABLE, EXECUTE ANY PROCEDURE or specific grants on:
   DBMS_LOCK, DBMS_SYSTEM, DBMS_OUTPUT, V_$SESSION, V_$PROCESS, V_$PARAMETER, V_$INSTANCE, V_$STATNAME, V_$SESSTAT

3.  Execute the script within SQL*Plus to create the table, function, and procedure.

RUNNING THE SCRIPT

Once the script's objects are created, you can run this procedure by passing in the following parameters (in this order):

   p_username - DB username to trace (can be upper or lowercase)
   p_num_of_traces : total number of traces to generate
   p_level - level of the 10046 trace (defaults to 8, trace sql and waitevents)
     level 0 = same as sql trace
     level 4 = same as 0 + bind variables
     level 8 = same as 0 + wait events
     level 12 = same as 0 + bind vars + wait events
   p_sample_interval - how often to check if user logged off (default is 2 sec)
   p_get_stats - TRUE/FALSE; collect session statistics for duration of trace
   p_stickydelay - how long to wait for another session by same name to come along before timing out
   p_misc_comment - some text comment

For example, to set username to TOBI, get two 10046 traces at level 8, set no delay in sampling, get stats, allow 30 secs
   between sessions, and associate w/comment = 'AddEmp' :

SQL> set serveroutput on size 1000000

SQL> exec coe$trace_session_by_user('tobi', 2, 8, 0, TRUE, 30,'AddEmp')

CAUTION

This script is provided for educational purposes only and not supported by Oracle Support Services. It has been tested internally, however, and works as documented. We do not guarantee that it will work for you, so be sure to test it in your environment before relying on it.

Proofread this script before using it! Due to the differences in the way text editors, e-mail packages and operating systems handle text formatting (spaces, tabs and carriage returns), this script may not be in an executable state when you first receive it. Check over the script to ensure that errors of this type are corrected.

SCRIPT

/*   Name:    coe$trace_session_by_user

 Purpose: Wait for a user to logon then start a 10046 trace once user logs on...
   keep tracing until user logs off.

 Prequisites: SELECT ANY TABLE, EXECUTE ANY PROCEDURE or specific grants on:
   DBMS_LOCK, DBMS_SYSTEM, DBMS_OUTPUT, V_$SESSION, V_$PROCESS, V_$PARAMETER, V_$INSTANCE, V_$STATNAME, V_$SESSTAT

   !!!!!!!!!!!!!!!BE CAREFUL USING 10046 WITH MTS / SHARED SERVERS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
   By default, this script will trace only dedicated server sessions.
   In some versions of the database, tracing shared server sessions could cause the entire 
   database to be running w/10046 turned on after a while.  It's almost impossible to turn off tracing once this happens,
   requiring the database to be shutdown and restarted.
   If you are sure that it is OK to trace shared server sessions, you'll have to remove the restriction in the WHERE clause of 
   the query below ("server='DEDICATED'").
   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

 Parameters: 
       p_username - DB username to trace (can be upper or lowercase)
   p_num_of_traces : total number of traces to generate
   p_level - level of the 10046 trace (defaults to 8, trace sql and waitevents)
     level 0 = same as sql trace
     level 4 = same as 0 + bind variables
     level 8 = same as 0 + wait events
     level 12 = same as 0 + bind vars + wait events
   p_sample_interval - how often to check if user logged off (default is 2 sec)
   p_get_stats - TRUE/FALSE; collect session statistics for duration of trace
   p_stickydelay - how long to wait for another session by same name to come along before timing out
   p_misc_comment - some text comment

 Usage:  From SQLPlus:
   SQL>  SET SERVEROUTPUT ON SIZE 1000000
   SQL>  exec coe$trace_session_by_user('scott')

   To get 1 trace, set 10046 level 12 and wait for logoff at 10 second intervals:

   SQL>  exec coe$trace_session_by_user('scott', 1, 12, 10)

   To set username to TOBI, get 2 X 10046 at level 8, set no delay in sampling, get stats, allow 30 secs
   between sessions, and associate w/comment = 'AddEmp' :

   SQL> exec coe$trace_session_by_user('tobi', 2, 8, 0, TRUE, 30,'AddEmp')

   exec coe$trace_session_by_user('hpujol_us', 2, 12, 0, TRUE, 15,'home-1')

   Then, look for the trace file in user_dump_dest corresponding to OS PID

   Some useful queries:

   select
   ospid,cpu_overall, cpu_recursive, cpu_parse, cpu_other
   from coe$trace_by_user_results
   where misc_comment like 'start%'

   select
   a.trace_start, 'mlrep_ora_'||a.ospid, a.cpu_overall, a.misc_comment
   from coe$trace_by_user_results a
   where a.cpu_overall = (select max(b.cpu_overall)
        from coe$trace_by_user_results b
        where b.misc_comment like 'bug-srch2%')
    and a.misc_comment like 'bug-srch2%';

   select substr(misc_comment,1,length(misc_comment)-1) func, max(cpu_overall)
   from coe$trace_by_user_results 
   where trace_start >= to_date('06-11-2001','mm-dd-yyyy')
   group by substr(misc_comment,1,length(misc_comment)-1)
   order by 2;
   
   select distinct misc_comment
   from coe$trace_by_user_results 
   where trace_start >= to_date('06-11-2001','mm-dd-yyyy')

   select substr(a.mlf,1,length(a.mlf)-1) MLFunc, 
   sum(a.SumCPUOverall) / count(*) "Avg CPU Overall"
   ,sum(a.SumCPURec) / count(*) "Avg CPU Recursive"
   ,sum(a.SumCPUParse) / count(*) "Avg CPU Parse"
   ,sum(a.SumCPUOther) / count(*) "Avg CPU Other"
   from (select misc_comment MLF
   , sum(cpu_overall) SumCPUOverall
   , sum(cpu_recursive) SumCPURec
   , sum(cpu_parse) SumCPUParse
   , sum(cpu_other) SumCPUOther
   , count(*) Cnt
   from coe$trace_by_user_results
   where trace_start >= to_date('06-11-2001 08','mm-dd-yyyy hh24')
   and username = 'SCOTT'
   group by misc_comment ) a
   group by substr(a.mlf,1,length(a.mlf)-1)
   order by 2 desc;

   select substr(misc_comment,1,length(misc_comment)-1) MLFunction
   , avg(cpu_overall) "Avg CPU Overall"
   , avg(cpu_recursive) "Avg CPU Rec"
   , avg(cpu_other) "Avg CPU Other"
    from coe$trace_by_user_results
    where trace_start >= to_date('06-11-2001 08','mm-dd-yyyy hh24')
    and username = 'SCOTT'
    group by substr(misc_comment,1,length(misc_comment)-1) 
    order by 2 desc;

   select misc_comment, ospid, cpu_overall 
   from coe$trace_by_user_results
   where misc_comment like '&cmt%'

   delete from coe$trace_by_user_results where misc_comment='bug-srch-keywrd-3';

   exec coe$trace_session_by_user('SCOTT',2, 12,0,TRUE, 15,'tech_art-1')


------------------------------------------------------------------------------------------------------

*/
CREATE TABLE coe$trace_by_user_results (
 timestamp_record  DATE
,trace_start  DATE
,trace_end   DATE
,sid    NUMBER
,serial#   NUMBER
,username   VARCHAR2(100)
,ospid   VARCHAR2(10)
,program   VARCHAR2(100)
,cpu_overall  NUMBER
,cpu_recursive  NUMBER
,cpu_parse   NUMBER
,cpu_other   NUMBER
,misc_comment  VARCHAR2(200)
) TABLESPACE users;

 

CREATE OR REPLACE FUNCTION coe$trace_by_user_timeout (  p_username IN VARCHAR2
       ,p_level IN NUMBER DEFAULT 8
       ,p_sample_interval IN NUMBER DEFAULT 2
       ,p_get_stats IN BOOLEAN DEFAULT FALSE
       ,p_stickydelay IN NUMBER DEFAULT 60
       ,p_misc_comment IN VARCHAR2 DEFAULT NULL) 
RETURN BOOLEAN

IS

TYPE sesStatType IS TABLE OF NUMBER
  INDEX BY binary_integer;

BeginSesStat sesStatType;
EndSesStat   sesStatType;

vCPUStatNo  NUMBER;
vCPUBegin NUMBER := 0;
vCPUEnd NUMBER := 0;
vCPUDelta NUMBER := 0;

vRCPUStatNo NUMBER;
vRCPUBegin NUMBER := 0;
vRCPUEnd NUMBER := 0;
vRCPUDelta NUMBER := 0 ;

vPCPUStatNo NUMBER;
vPCPUBegin NUMBER := 0;
vPCPUEnd NUMBER := 0;
vPCPUDelta NUMBER := 0;

vOtherCPUDelta NUMBER := 0;
vGotStats  BOOLEAN := FALSE;

vnSID  v$session.sid%TYPE := -1;
vnSerial v$session.serial#%TYPE;
vnProgram v$session.program%TYPE;
vSPID  v$process.spid%TYPE;
vPADDR v$session.paddr%TYPE;

vUDump  v$parameter.value%TYPE;
VInstanceName v$instance.instance_name%TYPE;
vTraceFile_Name VARCHAR2(500);
vStartTime  VARCHAR2(20);
vEndTime  VARCHAR2(20);

vGotUser BOOLEAN := FALSE;
vKeepTracing BOOLEAN := TRUE;
vUsername  v$session.username%TYPE := UPPER(p_username);
  
vErrNum  NUMBER;
vErrMsg  VARCHAR2(100);

vSampleStartTime DATE;
vStickyDelay NUMBER := p_stickydelay /(60*1440);  -- secs -> fraction of a day to wait for another login by same name

BEGIN

DBMS_OUTPUT.PUT_LINE('IN coe$trace_by_user_timeout');

 -- Init Stat Gathering
 IF p_get_stats = TRUE THEN
  
  SELECT statistic# INTO vCPUStatNo FROM v$statname WHERE name = 'CPU used by this session';
  SELECT statistic# INTO vRCPUStatNo FROM v$statname WHERE name = 'recursive cpu usage';
  SELECT statistic# INTO vPCPUStatNo FROM v$statname WHERE name = 'parse time cpu';
 END IF;

 vSampleStartTime := SYSDATE;

 WHILE NOT vGotUser AND ( (SYSDATE - vSampleStartTime) <= vStickyDelay )

 LOOP

  FOR U in (select sid, serial#, paddr, program, sql_address, sql_hash_value
               from v$session
              where username=vUsername and server='DEDICATED')
  LOOP

   -- Caught the user, now we can exit
   vGotUser := TRUE;  
     vnSID := U.sid;
   vnSerial := U.serial#;
   vPADDR := U.PADDR;
   vnProgram := U.program;  

   -- Get Begin Stats
   IF p_get_stats = TRUE THEN
    BEGIN
       SELECT value INTO BeginSesStat(vCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vCPUStatNo;
       SELECT value INTO BeginSesStat(vRCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vRCPUStatNo;
       SELECT value INTO BeginSesStat(vPCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vPCPUStatNo;
       DBMS_OUTPUT.PUT_LINE('Got Stats');
    EXCEPTION
       WHEN NO_DATA_FOUND THEN
     DBMS_OUTPUT.PUT_LINE('Couldnt Get Begin Stats...too fast...keep looking for user');
     vGotUser := FALSE;
    END;
   END IF;
  END LOOP;

 END LOOP;

-- Start Tracing the Session

 IF vGotUser THEN

  SYS.DBMS_SYSTEM.SET_EV(vnSID, vnSerial,10046,p_level,'');

  BEGIN
   SELECT spid INTO vSPID FROM v$process WHERE addr = vPADDR;
   SELECT value INTO vUDump FROM v$parameter WHERE name = 'user_dump_dest';
   SELECT LOWER(instance_name) INTO VInstanceName FROM v$instance;
   vStartTime := TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS');
   vTraceFile_Name := VUDump||'/'||VInstanceName||'_ora_'||vSPID||'.trc';
  EXCEPTION 
    WHEN NO_DATA_FOUND THEN
    DBMS_OUTPUT.PUT_LINE('Couldnt get base info');
  END;

  DBMS_OUTPUT.PUT_LINE('--------- Session Info -----------');
  DBMS_OUTPUT.PUT_LINE('SID:     '||TO_CHAR(vnSID)); 
  DBMS_OUTPUT.PUT_LINE('Serial:  '||TO_CHAR(vnSerial));
  DBMS_OUTPUT.PUT_LINE('OS PID: '||vSPID);
  DBMS_OUTPUT.PUT_LINE('Program: '||vnProgram);
  DBMS_OUTPUT.PUT_LINE('----------------------------------');
  DBMS_OUTPUT.PUT_LINE('Started Trace of user '||UPPER(p_username)||' at '||vStartTime);

-- Loop around until the user is gone
 
  WHILE vKeepTracing 
  LOOP
   vKeepTracing := FALSE;
   FOR U in (select sid, serial# from v$session where sid=vnSID AND serial#=vnSerial)
   LOOP
    IF U.SID = vnSID AND U.SERIAL# = vnSerial THEN
     vKeepTracing := TRUE;
     IF p_get_stats = TRUE THEN
      -- Get latest stats
      BEGIN
         SELECT value INTO EndSesStat(vCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vCPUStatNo;
         SELECT value INTO EndSesStat(vRCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vRCPUStatNo;
         SELECT value INTO EndSesStat(vPCPUStatNo) FROM v$sesstat WHERE sid = vnSID AND statistic# = vPCPUStatNo;
         vGotStats := TRUE;
      EXCEPTION
         WHEN NO_DATA_FOUND THEN
       vKeepTracing := FALSE;
       DBMS_OUTPUT.PUT_LINE('Couldnt Get stat update.');
       DBMS_OUTPUT.PUT_LINE('User '||vUsername||' has logged off.');
       EXIT;
      END;
     END IF;
    END IF;

    IF p_sample_interval > 0 THEN
     DBMS_LOCK.SLEEP(p_sample_interval); 
    END IF;

   END LOOP;
  END LOOP;


  -- Stop the Trace

  SYS.DBMS_SYSTEM.SET_EV(vnSID, vnSerial,10046,0,''); 

  vEndTime := TO_CHAR(sysdate, 'DD-MON-YYYY HH24:MI:SS');
 
  DBMS_OUTPUT.PUT_LINE('Stopped Tracing at '||vEndTime);
  DBMS_OUTPUT.PUT_LINE(' ');
  DBMS_OUTPUT.PUT_LINE('Trace file: '||vTraceFile_Name);

  -- Print out Session Stats collected
  IF vGotStats = TRUE THEN
   vCPUDelta := EndSesStat(vCPUStatNo) - BeginSesStat(vCPUStatNo);
   vRCPUDelta := EndSesStat(vRCPUStatNo) - BeginSesStat(vRCPUStatNo);
   vPCPUDelta := EndSesStat(vPCPUStatNo) - BeginSesStat(vPCPUStatNo);
   vOtherCPUDelta := vCPUDelta - vRCPUDelta - vPCPUDelta;

   DBMS_OUTPUT.PUT_LINE('--------- Session CPU Statistics -----------');
   DBMS_OUTPUT.PUT_LINE('Delta Overall CPU = '||TO_CHAR(vCPUDelta/100)||' sec');
   DBMS_OUTPUT.PUT_LINE('Delta Recursive CPU = '||TO_CHAR(vRCPUDelta/100)||' sec');
   DBMS_OUTPUT.PUT_LINE('Delta Parse CPU = '||TO_CHAR(vPCPUDelta/100)||' sec');
   DBMS_OUTPUT.PUT_LINE('Delta Other CPU = '||TO_CHAR(vOtherCPUDelta/100)||' sec');
  ELSE
   DBMS_OUTPUT.PUT_LINE('Couldnt Get End Stats');
  END IF;

  -- Put results into table
  INSERT INTO coe$trace_by_user_results
  ( 
   timestamp_record 
  ,trace_start  
  ,trace_end    
  ,sid    
  ,serial#   
  ,username   
  ,ospid   
  ,program   
  ,cpu_overall 
  ,cpu_recursive  
  ,cpu_parse   
  ,cpu_other   
  ,misc_comment
  )
  VALUES
  (
   SYSDATE
  ,TO_DATE(vStartTime,'DD-MON-YYYY HH24:MI:SS')
  ,TO_DATE(vEndTime, 'DD-MON-YYYY HH24:MI:SS')
  ,vnSID
  ,vnSerial
  ,vUsername
  ,vSPID
  ,vnProgram
  ,vCPUDelta/100
  ,vRCPUDelta/100
  ,vPCPUDelta/100
  ,vOtherCPUDelta/100
  ,p_misc_comment
  );

  COMMIT;

  RETURN TRUE;  -- all done, success

 ELSE  -- Timeout waiting for user to login
  DBMS_OUTPUT.PUT_LINE('Timed out waiting for user '||vUsername);
  RETURN FALSE;
 END IF;

DBMS_OUTPUT.PUT_LINE('Leaving... coe$trace_session_by_user');

EXCEPTION
  WHEN OTHERS THEN
   vErrNum := SQLCODE;
   vErrMsg := SUBSTR(SQLERRM, 1, 100);

   DBMS_OUTPUT.PUT_LINE('ERROR = '||TO_CHAR(vErrNum));
   DBMS_OUTPUT.PUT_LINE('ERROR MSG = '||vErrMsg);

   RETURN FALSE;
END;
/
show errors

 


CREATE OR REPLACE PROCEDURE coe$trace_session_by_user  (  p_username IN VARCHAR2
       ,p_num_of_traces IN NUMBER DEFAULT 3
       ,p_level IN NUMBER DEFAULT 8
       ,p_sample_interval IN NUMBER DEFAULT 2
       ,p_get_stats IN BOOLEAN DEFAULT FALSE
       ,p_stickydelay IN NUMBER DEFAULT 60
       ,p_misc_comment IN VARCHAR2 DEFAULT NULL) 
IS

 vTraceFileCount NUMBER := 0;
 vSuccessful BOOLEAN := TRUE;

BEGIN

 DBMS_OUTPUT.ENABLE(1000000);

 DBMS_OUTPUT.PUT_LINE('IN coe$trace_session_by_user');

 LOOP
  DBMS_OUTPUT.PUT_LINE('*************************************************');

  vSuccessful := coe$trace_by_user_timeout(p_username, p_level, p_sample_interval, p_get_stats, p_stickydelay, p_misc_comment);
  
  IF vSuccessful THEN
   vTraceFileCount := vTraceFileCount + 1;
  END IF;
  
  EXIT WHEN vTraceFileCount = p_num_of_traces;
  EXIT WHEN NOT vSuccessful;

 END LOOP;

 DBMS_OUTPUT.PUT_LINE('Successfully Traced '||TO_CHAR(vTraceFileCount)||' sessions.');

END;
/

show errors

SCRIPT OUTPUT

SQL> exec coe$trace_session_by_user('SYSTEM',2, 12,1, true,10,'test2');
IN coe$trace_session_by_user
*************************************************
IN coe$trace_by_user_timeout
Got Stats
--------- Session Info -----------
SID:     10
Serial:  650
OS PID: 10391
Program: sqlplus@coehq2 (TNS V1-V3)
----------------------------------
Started Trace of user SYSTEM at 30-MAY-2006 17:15:17
Stopped Tracing at 30-MAY-2006 17:15:41
Trace file: /u01/app/oracle/product/DB9iR2/admin/DB9iR2/udump/db9ir2_ora_10391.trc
--------- Session CPU Statistics -----------
Delta Overall CPU = 1.48 sec
Delta Recursive CPU = .05 sec
Delta Parse CPU = .06 sec
Delta Other CPU = 1.37 sec
*************************************************
IN coe$trace_by_user_timeout
Got Stats
--------- Session Info -----------
SID:     10
Serial:  653
OS PID: 10395
Program: sqlplus@coehq2 (TNS V1-V3)
----------------------------------
Started Trace of user SYSTEM at 30-MAY-2006 17:15:49
Stopped Tracing at 30-MAY-2006 17:16:07
Trace file: /u01/app/oracle/product/DB9iR2/admin/DB9iR2/udump/db9ir2_ora_10395.trc
--------- Session CPU Statistics -----------
Delta Overall CPU = 1.38 sec
Delta Recursive CPU = 0 sec
Delta Parse CPU = 0 sec
Delta Other CPU = 1.38 sec
Successfully Traced 2 sessions.

PL/SQL procedure successfully completed.

REFERENCES

NOTE:352363.1 - LTOM - The On-Board Monitor User Guide
 
 

相关内容

   
 
 

产品

   
 
  • Oracle Database Products > Oracle Database > Oracle Database > Oracle Database - Enterprise Edition > RDBMS > Database Level Performance Issues (not SQL Tuning)
 

关键字

   
 
TRACE; LOGON; PERFORMANCE; SQL TRACING

你可能感兴趣的:(Capture 10046 Traces Upon User Login (without using a trigger) (文档 ID 371678.1))