Interpreting Wait Events

What are Wait Events?
Common Wait Events
Collecting Wait Events
Tracing Wait Events
10g Wait Interface Enhanments
Event Wait Analysis with Statspack
How Wait Events can be used to improve performance (with good examples)
More examples on 10g

What are Wait Events?

"Wait Event" Defined

At any given moment, every Oracle process is either busy servicing a request or waiting for something specific to happen. By "busy" we mean that the process wishes to use the CPU. For example, a dedicated server process might be searching the buffer cache to see if a certain data block is in memory. This process would be said to be busy and not waiting. The ARC0 process, meanwhile, might be waiting for LGWR to signal that an online redo log needs archiving. In this case, the ARC0 process is waiting.
The kernel developers at Oracle have defined a list of every possible event that an Oracle process could be waiting for. At any moment, every Oracle process that is not busy is waiting for one of these events to occur. Suppose an application has submitted a COMMIT statement and the server process is waiting for the LGWR process to signal that the redo log buffer has been flushed to disk. This wait event is called "log file sync." Another dedicated server process might be waiting for a row-level lock on the INVOICES table to be freed so that a SELECT FOR UPDATE statement can continue. That wait event is called "enqueue."
By querying v$ views, we can see what events processes are waiting on to an amazing level of detail. For example, we might learn that a dedicated server process has been waiting 30 milliseconds for the operating system to read eight blocks from data file 42, starting at block 18042. We can also see summary information of how much time each Oracle process has spent waiting on each type of wait event for the duration of the process. In addition, we can direct an Oracle process to write detailed wait event data to a trace file for later analysis using TKPROF.

Why Wait Event Information is Useful

Using the wait event interface, you can get insights into where time is being spent. If a report takes four hours to complete, for example, the wait event interface will tell you how much of that four hours was spent waiting for disk reads caused by full table scans, disk reads caused by index lookups, latch contention, and so on.
The wait event interface provides much more information to work with than cache hit ratios do.You get data that can touch upon so many different areas of your database such as disk I/O, latches, parallel processing, network traffic, checkpoints, and row-level locking. At the same time, you get incredible detail such as the file number and block number of a block being read from disk, or the name of a latch being waited on along with the number of retries.
The wait event interface will not always give you all of the information you need in order to diagnose and solve a problem, but it will certainly point you in the right direction. You might think the buffer cache is too small because the cache hit ratio is only 70%, but in fact, the application's slow response time could be caused by latch contention in the shared pool, a bottleneck in the log writer, or any of a number of other things.

Prior to Oracle 10g, Oracle's default optimizer mode was called “choose.” In the choose optimizer mode, Oracle will execute the rule-based optimizer if there are no statistics present for the table; it will execute the cost-based optimizer if statistics are present. The danger with using the choose optimizer mode is that problems can occur in cases where one Oracle table in a complex query has statistics and the other tables do not.
Starting in Oracle 10g, the default optimizer mode is all_rows, favoring full-table scans over index access.  The all_rows optimizer mode is designed to minimize computing resources and it favors full-table scans.  Index access (first_rows) adds additional I/O overhead, but they return rows faster.
When only some tables contain CBO statistics, Oracle will use the cost-based optimization and estimate statistics for the other tables in the query at runtime. This can cause significant slowdown in the performance of the individual query.


Common Wait Event Names and What They Mean

Although there are many different types of wait events, the majority of them come up very infrequently or tend not to be significant. In practice, only a few dozen wait events tend to be of interest to most DBAs. You'll see different wait events surfacing in different environments based on which Oracle features have been implemented and which capabilities of the database are being taxed the most. For example, the PX wait events won't appear if you aren't using parallel query, and the virtual circuit status wait event won't appear if you are not using the multi-threaded server architecture (or shared server architecture as it is more recently called). Along those lines, the log file sync and enqueue wait events probably won't be prevalent in a primarily read-only system. Here are some of the most common wait events and what they mean:

Wait Event Description
buffer busy waits The session wants to access a data block that is either 1) currently not in memory, but another process has already issued an I/O request to read the block into memory, or 2) in memory but in an incompatible mode (current versus consistent, for example). For example another session is using that block via an insert, update or delete.
control file parallel write The session has issued multiple I/O requests in parallel to write blocks to all control files, and is waiting for all of the writes to complete.
control file sequential read The session is waiting for blocks to be read from a control file.
db file parallel read The session has issued multiple I/O requests in parallel to read blocks from data files into memory and is waiting for all requests to complete. This may occur during recovery or during regular activity when a session batches many single block I/O requests together and issues them in parallel.
db file parallel write The process, typically DBWR, has issued multiple I/O requests in parallel to write dirty blocks from the buffer cache to disk and is waiting for all requests to complete.
db file scattered read The session has issued an I/O request to read a series of contiguous blocks from a data file into the buffer cache and is waiting for the operation to complete. This typically happens during a full table scan or fast full index scan. Oracle reads up to DB_FILE_MULTIBLOCK_READ_COUNT consecutive blocks at a time and scatters them into buffers in the buffer cache
db file sequential read The session has issued an I/O request to read one block from a data file into the buffer cache and is waiting for the operation to complete. This typically happens during an index lookup or a fetch from a table by ROWID when the required data block is not already in memory. This call differs from "db file scattered read" in that a sequential read reads data into contiguous memory (whilst a scattered read reads multiple blocks and scatters them into different buffers in the SGA).
direct path read,
direct path write
The session has issued asynchronous I/O requests that bypass the buffer cache and is waiting for them to complete. These wait events often involve temporary segments, sorting activity, parallel query or hash joins.
enqueue The session is waiting on an enqueue (a lock you can see in v$lock). This commonly occurs when one user is trying to update a row in a table that is currently being updated by another user.
free buffer waits The session needs a free buffer so it can bring a data block into the buffer cache and is waiting for a buffer that is not dirty to become available. This can occur if DBWR is not writing dirty buffers to disk fast enough.
latch free The session is waiting for a latch held by another session. (This event does not apply to processes that are spinning while waiting for a latch; when a process is spinning, it is not waiting.)
library cache load lock The session is waiting for the opportunity to load an object or a piece of an object into the library cache. (Only one process can load an object or a piece of an object at a time.)
library cache pin The session wants to pin an object in memory in the library cache for examination, ensuring no other processes can update the object at the same time. This happens when you are compiling or parsing a PL/SQL object or a view.
log buffer space The session is waiting for space in the log buffer. (Space becomes available only after LGWR has written the current contents of the log buffer to disk.) This typically happens when applications generate redo faster than LGWR can write it to disk.
log file parallel write The session is waiting for blocks to be written to all online redo log members in one group. LGWR is typically the only process to see this wait event. It will wait until all blocks have been written to all members.
log file sequential read The session is waiting for blocks to be read from the online redo log into memory. This primarily occurs at instance startup and when the ARCH process archives filled online redo logs.
log file switch completion The session is waiting for a log file switch to complete, typically so more redo can be generated.
log file sync The session is waiting for LGWR to finish flushing the log buffer to disk. This occurs when a user commits a transaction. (A transaction is not considered committed until all of the redo to recover the transaction has been successfully written to disk). Probably you are commiting too often
undo segment extension The session is waiting for an undo segment to be extended or shrunk.
write complete waits The session is waiting for a requested buffer to be written to disk; the buffer cannot be used while it is being written.

There are several wait events that we call "idle events" because each of these wait events typically occurs when the Oracle process has nothing to do and is waiting for somebody to give it a task. Idle events are usually not very interesting from a tuning standpoint, so we usually overlook them when evaluating data extracted from the wait event interface. The common idle events are as follows:

Idle Events
client message PX Deq: Execute Reply
dispatcher timer PX Deq: Execution Msg
gcs for action PX Deq: Signal ACK
gcs remote message PX Deq: Table Q Normal
ges remote message PX Deque wait
i/o slave wait PX Idle Wait
jobq slave wait queue messages
lock manager wait for remote message rdbms ipc message
null event slave wait
parallel query dequeue smon timer
pipe get SQL*Net message from client
PL/SQL lock timer SQL*Net message to client
pmon timer SQL*Net more data from client
PX Deq Credit: need buffer virtual circuit status
PX Deq Credit: send blkd wakeup time manager


What Wait Event Information Does Not Provide

If an Oracle process has work to do but,  must wait for something to happen before it can continue, then the process will be waiting on a non-idle wait event. If a process has nothing to do, it will be waiting on an idle wait event. So what happens if a process has work to do and is busy doing it? When a process is busy, there will be no information in the wait event interface since the process is not waiting.

When we look at the wait event information extracted from an Oracle instance, we see detailed information about how many times and how much time was spent waiting for specific events to occur. But we do not see anything about the time periods in which the process requested use of the CPU. This means that the wait event interface is not able to provide information about the following:

This is important to keep in mind because there is an easy trap to fall into. You could be troubleshooting a very slow SELECT statement and learn from the wait event interface that the process does not have significant wait events when running the query. This could lead you to think that the statement is optimal, and that it just takes a long time to run. In fact, however, the query might be performing huge numbers of logical reads and the number of buffer gets could be reduced by rewriting the query.

When Oracle needs to access a data block and the block is already in the buffer cache, a logical read occurs with no physical read. The process is able to read the block without the occurrence of any wait events. Large amounts of CPU time could be consumed on significant numbers of logical reads, and the wait event interface will have nothing to show for the elapsed time.
Statement parsing and spinning while waiting for a latch to become available are two other examples of activities not accounted for by the wait event interface. An Oracle process uses CPU time while parsing a statement or spinning on a busy latch; since no waiting is involved, the wait event interface does not have anything to report.
Beginning in Oracle 9i, the portion of the Statspack report that lists wait event information also lists CPU usage. This is very helpful information. It allows us to easily compare time spent waiting to time spent processing, so we know where to focus our tuning efforts. However, it should be pointed out that the CPU usage information in this section of the Statspack report does not come from the wait event interface. Instead, Statspack merges data collected from the wait event interface with CPU usage information collected from the v$sysstat dynamic performance view.

A Note About the timed_statistics Parameter

The Oracle kernel is capable of timing many activities including wait events. In Oracle 9i Release 2 timed statistics are collected by default, and some timings are collected in units of microseconds. The timed_statistics instance parameter is used to enable and disable timed statistics collection. When timed_statistics is set to FALSE, all times in the wait event interface will appear as zero. You may enable timed statistics collection at the instance or the session level with the following commands:

You may enable timed statistics at the instance level on the next and all subsequent instance restarts by adding the following line to the instance parameter file:

        timed_statistics = true

In practice, the overhead of collecting timed statistics is extremely small. In most cases, the benefit you'll get from having timing information at your disposal will outweigh the performance overhead. For several years, many DBAs have been running their production systems with timed statistics enabled at all times. With Oracle 9i Release 2, this is now the default behavior.


Collecting Wait Event Information

There are two common ways of collecting wait event information: the Oracle extended trace (10046 event) and using V$ views; which is the main topic of this article. Any user with the SELECT ANY TABLE system privilege or the SELECT_CATALOG_ROLE role can query the v$ views. Only users who can connect to the database as SYSDBA or execute the DBMS_SUPPORT or DBMS_SYSTEM packages can activate wait event tracing in other database sessions. In a typical environment, DBAs have access to the wait event interface but general users do not.
Oracle provides a number of views to aid in tuning; a few are:
    * V$SYSTEM_EVENT, which gives you total system waits for an event since database startup.
    * V$SESSION_EVENT, which is at the session level.
    * V$SESSION_WAIT gives you what an active session is waiting on at that point in time.
    * V$SESSION, which gives you session information and has a column ROW_WAIT_OBJ# that tells you the object involved when joined with OBJECT_ID or DATA_OBJECT_ID in DBA_OBJECTS.
Version 10g has some more V$ views for tracking history called V$%_HISTORY, including V$ACTIVE_SESSION_HISTORY (ASH), which samples non-idle wait events every second and new columns in V$SESSION to combine information in V$SESSION_WAIT.

In addition to the four dynamic performance views, Oracle provides a tracing facility where extremely detailed wait event information is written to a trace file for later evaluation. In more recent releases of Oracle, a PL/SQL package is provided for activating the wait event tracing facility. In older releases of Oracle, the wait event tracing facility is activated by setting debug event number 10046 (not to be confused with a wait event).

The V$SYSTEM_EVENT View
The v$system_event view shows one row for each wait event name, along with the total number of times a process has waited for this event, the number of timeouts, the total amount of time waited, and the average wait time. All of these figures are cumulative for all Oracle processes since the instance started. Wait events that have not occurred at least once since instance startup do not appear in this view. The v$system_event view has the following columns:

        Name                            Null?    Type
------------------------------- -------- ----
EVENT VARCHAR2(64)
TOTAL_WAITS NUMBER
TOTAL_TIMEOUTS NUMBER
TIME_WAITED NUMBER
AVERAGE_WAIT NUMBER
TIME_WAITED_MICRO NUMBER

EVENT is the name of the wait event. You can see a list of all wait event names known to your Oracle kernel with the following query:
        SELECT name FROM v$event_name;
TOTAL_WAITS is the total number of times a process has waited for this event since instance startup.
TOTAL_TIMEOUTS is the total number of times a process encountered a timeout while waiting for an event. When a process begins to wait for an event, it specifies a timeout period after which the operating system should wake it up if the event has not yet transpired. For example, when an Oracle process issues an I/O request to read a block from a data file (the db file sequential read wait event), the process sets a timeout of one second. Usually the I/O request will complete in less than one second and no timeout will occur. But if the read should take longer than one second for whatever reason, a timeout will occur and the process will wake up. The process might do some minor housekeeping, but it will likely just begin another timeout period of one second and continue waiting for the same event.
TIME_WAITED and AVERAGE_WAIT show the cumulative (sum) and average time spent by processes waiting for this event, in centiseconds. Divide these figures by 100 in order to get the wait time in seconds. These two columns will show as zero if timed statistics are not enabled.
TIME_WAITED_MICRO is the same as TIME_WAITED, except that the time is in microseconds. Divide this figure by 1000000 in order to get the wait time in seconds.

Consider the following query from v$system_event:

        SELECT event, time_waited
FROM v$system_event
WHERE event IN ('SQL*Net message from client', 'smon timer',
'db file sequential read', 'log file parallel write');

EVENT TIME_WAITED(sec)
------------------------------------------------------ ------------
log file parallel write 1596.92
db file sequential read 286.57
smon timer 1306738.37
SQL*Net message from client 165289.89

Since instance startup, processes on this system have waited a total of 286.57 seconds while reading single data file blocks from disk, and over 1,596 seconds (26 minutes) while writing redo to the online redo logs. A huge amount of time has been spent waiting on the smon timer and SQL*Net message from client events, but these are both idle wait events so we don't worry about them. (The SMON process spends a lot of time sleeping between consecutive checks of the system, and many dedicated server processes spend a lot of their time waiting for the application to submit a SQL statement for processing.)

The V$SESSION_EVENT View
The v$session_event view is a lot like the v$system_event view, except that it shows separate rows of information for each Oracle process. As with v$system_event, event names do not appear in this view if the process has not waited for them at least once. Also, when an Oracle process terminates (as in the case of when a user logs off the database) all of the rows in v$session_event for that process permanently disappear. The v$session_event view has the following columns:

        Name                            Null?    Type
------------------------------- -------- ----
SID NUMBER
EVENT VARCHAR2(64)
TOTAL_WAITS NUMBER
TOTAL_TIMEOUTS NUMBER
TIME_WAITED NUMBER
AVERAGE_WAIT NUMBER
MAX_WAIT NUMBER
TIME_WAITED_MICRO NUMBER

SID indicates the session ID of the process waiting for the event. You can query v$session in order to determine the SID of the session whose wait events you want to investigate. The next five columns in the v$session_event view are the same as in the v$system_event view, except that now they pertain to the one specific process instead of all processes.
MAX_WAIT indicates the maximum amount of time the process had to wait for the event. Like TIME_WAITED and AVERAGE_WAIT, the unit of measure is centiseconds and will display as zero if timed statistics are not enabled.

Consider the following query which displays all wait event information for the current SQL*Plus session:

        SELECT event, total_waits, time_waited_micro, max_wait
FROM v$session_event
WHERE SID =
(SELECT sid FROM v$session
WHERE audsid = USERENV ('sessionid') );

EVENT TOTAL_WAITS TIME_WAITED_MICRO MAX_WAIT
------------------------------ ----------- ----------------- ----------
log file sync 1 19629 2
db file sequential read 3 26128 1
db file scattered read 678 1154632 8
SQL*Net message to client 76 352 0
SQL*Net more data to client 1 63 0
SQL*Net message from client 75 1118656279 26930
SQL*Net break/reset to client 4 22281 2

You can see that the Oracle process serving this session has spent 1.180760 seconds waiting for disk I/O. Although there have been 76 instances where the process waited for the networking software to allow it to send a message to the client (the SQL*Plus program), each of these waits was shorter than the 0.01 second resolution of the MAX_WAIT column in v$session_event. (All 76 waits combined added up to only 0.000352 seconds.) Far and away, the Oracle process has spent the vast majority of its time (over 18 minutes) waiting for a SQL statement to be entered at the SQL*Plus prompt.

If you run the query against v$session_event shown above and get back no rows, it is possible that you are encountering Oracle bug number 2429929. In some releases of Oracle 9i, including 9.2.0.1, the SID column in v$session_event actually shows the SID minus one instead of the SID.

The V$SESSION_WAIT View
The v$session_wait view shows one row for each Oracle process. The row indicates the name of the wait event and additional parameters that provide further information about exactly what the process is waiting for (or information about the most recent wait event if the process is not currently waiting). While the v$system_event and v$session_event views show cumulative wait event information, the v$session_wait view shows information as of the present moment only. The v$session_wait view has the following columns:

        Name                            Null?    Type
------------------------------- -------- ----
SID NUMBER
SEQ# NUMBER
EVENT VARCHAR2(64)
P1TEXT VARCHAR2(64)
P1 NUMBER
P1RAW RAW(8)
P2TEXT VARCHAR2(64)
P2 NUMBER
P2RAW RAW(8)
P3TEXT VARCHAR2(64)
P3 NUMBER
P3RAW RAW(8)
WAIT_TIME NUMBER
SECONDS_IN_WAIT NUMBER
STATE VARCHAR2(19)
SID - Session ID.
SEQ# - Sequence number that uniquely identifies the wait event. This sequence is incremented with each wait event completion.
EVENT - Name of wait event.
P1TEXT - A description of what the P1 value is used for. Not reliable, use PARAMETER(n) value from V$EVENT_NAME to determine.
P1 - This value definition is dependent on the event. Use PARAMETER1 value from V$EVENT_NAME to describe what this value is used for.
P1RAW - Same value as P1 except in hexadecimal format.
P2TEXT - A description of what the P2 value is used for. Not reliable, use PARAMETER(n) value from V$EVENT_NAME to determine.
P2 - This value definition is dependent on the event. Use PARAMETER2 value from V$EVENT_NAME to describe what this value is used for.
P2RAW - Same value as P2 except in hexadecimal format.
P3TEXT - A description of what the P3 value is used for. Not reliable, use PARAMETER(n) value from V$EVENT_NAME to determine.
P3 - This value definition is dependent on the event. Use PARAMETER3 value from V$EVENT_NAME to describe what this value is used for.
P3RAW - Same value as P3 except in hexadecimal format.
WAIT_CLASS_ID - ID of the wait class. Column is in 10g.
WAIT_CLASS# - Number of the class. Column is in 10g.
WAIT_CLASS - The name of the wait class (Idle, Network, System I/O, etc.). Column is in 10g.
WAIT_TIME - The last amount of time that the session waited for an event. A 0 value means the session is currently waiting on the event. This value of 0 will not increase until the session completes the wait event. Time is in centiseconds.
SECONDS_IN_WAIT - If WAIT_TIME equals 0, then this is the number of seconds spent waiting on the event so far. This can give you complicated results when a timeout occurs like with enqueues because it will reset to 0, but this is beyond the scope of this article.
STATE - State of the wait event: WAIT_TIME=0 - WAITING, the process is in the state of waiting on an event. WAIT_TIME=-1 - WAITED SHORT TIME, wait time completed in less than a centisecond. WAIT_TIME=-2 - WAITED UNKOWN TIME, duration of wait is unknown because TIME_STATISTICS was set to false. WAIT_TIME>0 - WAITED KNOWN TIME, wait completed. WAIT_TIME is the duration of the wait event in centiseconds.

The following query shows the parameters associated with the db file scattered read wait event:

SELECT *    
FROM v$event_name
WHERE name = 'db file scattered read';

EVENT# NAME PARAMETER1 PARAMETER2 PARAMETER3
---------- ---------------------- ------------ ----------- -----------
188 db file scattered read file# block# blocks

This tells us that when a process is waiting for a multi-block read from disk to complete (as in the case of a full table scan where the data blocks were not already in the buffer cache), we can see the file from which the blocks are being read as well as the starting block and number of blocks.

The following query was run while a session was performing a full table scan:

        set linesize 32000
SELECT sid,seq#,substr(event,1,35) event, p1text, p1, p2text, p2, p3text, p3, wait_time, seconds_in_wait, state
 FROM v$session_wait WHERE sid = 442;

SID SEQ# EVENT
---------- ---------- ------------------------------
P1TEXT P1 P1RAW
------------------------------ ---------- --------
P2TEXT P2 P2RAW
------------------------------ ---------- --------
P3TEXT P3 P3RAW WAIT_TIME SECONDS_IN_WAIT
------------------------------ ---------- -------- ---------- ---------------
STATE
-------------------
442 303 db file scattered read
file# 17 00000011
block# 2721 00000AA1
blocks 8 00000008 -1 0
WAITED SHORT TIME

You can see that the process was not waiting at the moment this query was run, but its last wait had been for an I/O request to read eight blocks from file 17 starting at block 2721. That I/O request had completed in less than 0.01 second. (Note the -1 in the WAIT_TIME column when the STATE is WAITED SHORT TIME.) Why did Oracle choose to read eight blocks at a time? Because the db_file_multiblock_read_count instance parameter was set to eight.
For most wait events this view is sufficient, but it is hardly a robust tuning tool for at least two important reasons:
    * The view is a snapshot of the present. When the waits cease to exist, the history of those waits experienced by the session earlier disappears too, making after-effect diagnosis difficult. V$SESSION_EVENT provides cumulative but not very detailed data.
    * V$SESSION_WAIT contains information only about wait events; for all other relevant information such as the userid and terminal you have to join it with the view V$SESSION.
In Oracle Database 10g, the wait interface has been radically redesigned to provide more information with less DBA intervention. In this article, we will explore those new features and see how they aid us in the diagnosis of performance problems. For most of the performance problems, you will get an extended analysis from Automatic Database Diagnostic Manager (ADDM), but for immediate problems not yet captured by ADDM, the wait interface provides valuable data for diagnosis.


The V$SESSION View
Session details, the only listing column used in the scope of this article.
ROW_WAIT_OBJ# - This is the object id of the object involved and is joined to the DBA_OBJECTS.OBJECT_ID or DBA_OBJECTS.DATA_OBJECT_ID column. This column is only populated in version 9i and above. A value that is not -1 means that the session is waiting on a lock for this object. Previous versions of the database will need to use P(n) values from V$SESSION_WAIT to find objects in DBA_EXTENTS.

The v$event_name View
The v$event_name view shows reference information rather than up-to-the-moment information about instance performance. This view shows one row for each wait event known to the Oracle kernel. Associated with each wait event are up to three parameters-additional pieces of information that provide more detail about a wait situation. This view displays the definition of P(n) columns from V$SESSION_WAIT and can be joined on the NAME column.
The v$event_name view has the following columns:

        Name                            Null?    Type
------------------------------- -------- ----
EVENT# NUMBER
NAME VARCHAR2(64)
PARAMETER1 VARCHAR2(64)
PARAMETER2 VARCHAR2(64)
PARAMETER3 VARCHAR2(64)

EVENT# - Number of the wait event.
NAME - Name of the event.
PARAMETER1 - Description of the value for P1.
PARAMETER2 - Description of the value for P2.
PARAMETER3 - Description of the value for P3.
WAIT_CLASS_ID - ID of the wait class. Column is in 10g.
WAIT_CLASS# - Number of the class. Column is in 10g.
WAIT_CLASS - The name of the wait class (Idle, Network, System I/O, etc.). Column is in 10g.

The v$system_event and v$session_event views show cumulative information about past waits in summary form, leaving out parameter information from each individual wait. As we will see in the next sections, wait event parameters come into play in the v$session_wait view and wait event trace files.



Tracing Wait Event Activity (SYSTEM EVENT 10046)
DBAs should be familiar with the SQL trace facility built into Oracle. By using the commands below, you can enable SQL trace for your session or another session:

When SQL trace is enabled for a session, the Oracle process writes detailed trace information (including timing data if timed statistics are enabled) to a trace file in a directory specified by the user_dump_dest instance parameter. These trace files are plain text files and human readable, but rather tedious and repetitive. You can optionally run trace files through a processor such as TKPROF instead of looking at them directly.
Oracle has the ability to direct a process to include additional information in the trace file, including wait event information. In earlier releases of Oracle, we activated the wait event tracing facility by setting debug event 10046. Debug event 10046 still works in Oracle 9i, but recent releases of Oracle include a PL/SQL package built-in that gives a more friendly means for activating wait event tracing.
Setting debug events allows a DBA to instruct an Oracle instance to take on a special, atypical behavior. Debug events can be used, for example, to cause Oracle to write a system level dump file whenever an ORA-0600 error occurs or skip over corrupted blocks in a table when performing a full table scan. Most debug events should never be set unless you are directed to do so by Oracle Support. Some debug events can put your database at risk.
Debug event 10046 affects the amount of information written to trace files. It is a very safe debug event, and one of the few that you are allowed to set without special permission from Oracle Support. Debug event 10046 can be set to the following values:

Debug Event Setting Effect
10046 trace name context forever, level 1 Enables ordinary SQL trace
10046 trace name context forever, level 4 Enables SQL trace with bind variable values included in trace file
10046 trace name context forever, level 8 Enables SQL trace with wait event information included in trace file
10046 trace name context forever, level 12 Equivalent of level 4 and level 8 together
10046 trace name context off Turns off tracing

You can set the 10046 debug event to trace your session and collect wait event information in the trace file with either of the following commands:

You can set the 10046 debug event to trace another session on the database with any of the following commands:
Find the session:
set linesize 150
column Name format a14
column SID format 9999
column PID format 99999
column TERM format a15
column OSUSER format a15
column Program format a15
column Stats format a10
column Logon_time format a20
select a.username Name, a.sid SID, a.serial#, b.spid PID,
       SUBSTR(A.TERMINAL,1,9) TERM, SUBSTR(A.OSUSER,1,9) OSUSER,
       substr(a.program,1,10) Program, a.status Status,
       to_char(a.logon_time,'MM/DD/YYYY hh24:mi') Logon_time
from v$session a, v$process b
where a.paddr = b.addr
  and a.serial# <> '1'
  and a.status = 'ACTIVE'
  and a.username like upper('%&user%') -- if you want to filter by username
order by a.logon_time;

Once you have the SID and SERIAL# of the session you can use some Oracle supplied packages.
-- set timed statistics at user session level if not set at system level.
execute sys.dbms_system.set_boo_param_in_session(&&SID, &&SERIAL,'timed_statistics',true);

--set max dump file size if not set at system level.
execute sys.dbms_system.set_in_param_in_session(&&SID, &&SERIAL, 'max_dump_file_size',10000000);
   
-- activate level 8 extended SQL tracing.
execute sys.dbms_system.set_ev(&&SID, &&SERIAL, 10046, 8, ' ');
or
execute sys.dbms_support.start_trace_in_session (
&&SID, &&SERIAL)


******* run all of your processing here *******

-- disables extended SQL tracing.
execute sys.dbms_system.set_ev(&&SID, &&SERIAL, 10046, 0, ' ');
or
execute sys.dbms_support.stop_trace_in_session (
&&SID, &&SERIAL)

Calling DBMS_SUPPORT as shown here is equivalent to activating debug event 10046 at level 8. You may include optional additional parameters in the procedure call to activate level 4 or 12. The DBMS_SUPPORT package is not installed in the database by default. You need to run the dbmssupp.sql script found in the rdbms/admin directory as SYS before you can call DBMS_SUPPORT.
It should also be pointed out that the SET_EV procedure in the DBMS_SYSTEM package is not officially supported by Oracle Corporation-they prefer that you use DBMS_SUPPORT instead. The reason is that DBMS_SYSTEM.SET_EV allows you to set any debug event in any session. As mentioned above some debug events can potentially be dangerous, and DBMS_SYSTEM.SET_EV lets you set such events in any session.
Unlike timed statistics, tracing consumes a significant amount of system resources. Therefore it is important to use the tracing facility sparingly. Trace only the sessions you need to trace, and only for as long as you need. Turn off tracing as soon as it is no longer needed with any of the following commands:

When you set debug event 10046 to a level of 8 or 12 (or call START_TRACE or START_TRACE_IN_SESSION in DBMS_SUPPORT), the Oracle process will write a line into the trace file every time it finishes waiting for an event. The line in the trace file will contain almost the same information that would have appeared in the v$session_wait view, but perhaps in a slightly less friendly format. You can also see in the trace file which cursor (and therefore which SQL statement) the wait event was associated with.
Here is an excerpt from a trace file generated by setting debug event 10046 to level 12 on an Oracle 8i database:

        =====================
PARSING IN CURSOR #1 len=80 dep=0 uid=502 oct=3 lid=502
tim=2293771931 hv=2293373707 ad='511dca20'
SELECT /*+ FULL */ SUM (LENGTH(notes))
FROM customer_calls
WHERE status = :x
END OF STMT
PARSE #1:c=0,e=0,p=0,cr=0,cu=0,mis=1,r=0,dep=0,og=0,tim=2293771931
BINDS #1:
bind 0: dty=2 mxl=22(22) mal=00 scl=00 pre=00 oacflg=03 oacfl2=0
size=24 offset=0
bfp=09717724 bln=22 avl=02 flg=05
value=43
EXEC #1:c=0,e=0,p=0,cr=0,cu=0,mis=0,r=0,dep=0,og=4,tim=2293771931
WAIT #1: nam='SQL*Net message to client' ela= 0 p1=675562835 p2=1 p3=0
WAIT #1: nam='db file scattered read' ela= 3 p1=17 p2=923 p3=8
WAIT #1: nam='db file scattered read' ela= 1 p1=17 p2=931 p3=8
WAIT #1: nam='db file scattered read' ela= 2 p1=17 p2=939 p3=8
WAIT #1: nam='db file sequential read' ela= 0 p1=17 p2=947 p3=1
WAIT #1: nam='db file scattered read' ela= 3 p1=17 p2=1657 p3=8
WAIT #1: nam='db file scattered read' ela= 1 p1=17 p2=1665 p3=8
WAIT #1: nam='db file scattered read' ela= 2 p1=17 p2=1673 p3=8
WAIT #1: nam='db file scattered read' ela= 0 p1=17 p2=1681 p3=8
WAIT #1: nam='db file scattered read' ela= 3 p1=17 p2=1761 p3=8
WAIT #1: nam='db file scattered read' ela= 1 p1=17 p2=1769 p3=8
WAIT #1: nam='db file scattered read' ela= 1 p1=17 p2=1777 p3=8
WAIT #1: nam='db file scattered read' ela= 0 p1=17 p2=1785 p3=8
WAIT #1: nam='db file scattered read' ela= 2 p1=17 p2=1841 p3=8
WAIT #1: nam='db file scattered read' ela= 1 p1=17 p2=1849 p3=8

The "ela=" figures indicate the duration of the wait. In trace files generated by Oracle 8i and earlier, the elapsed time is shown in centiseconds. Beginning in Oracle 9i, the elapsed time is shown in microseconds.
You can see that when the session executed the query, there was a wait shorter than one centisecond for a message to be sent to the client, followed by a bunch of waits for I/O requests against file 17. Most of the I/O requests were multi-block reads, reading eight blocks at a time while performing a full table scan of the CUSTOMER_CALLS table.
You can probably imagine how large and overwhelming trace files can get when anything but a trivial application is traced. This is why Oracle provides the TKPROF utility. TKPROF takes a trace file as input and generates a nicely formatted, easy to read report. Unfortunately, the TKPROF that comes with Oracle 8i and earlier releases ignores wait event information written in the trace file. To analyze the wait event information collected by a trace in an Oracle 8i or earlier environment, you will need to either pore through the trace file manually or write your own trace file parser and formatter. (We've heard of people writing perl scripts to do this, but we have never come across them.)
The TKPROF utility that comes with Oracle 9i is capable of reporting wait event information summarized by distinct statement. By default Oracle 9i TKPROF ignores wait event information in the trace file as earlier releases did. However, if the "waits=yes" command line argument is provided, a separate table of wait event statistics will appear in the report for each distinct statement. Here is a sample excerpt from a TKPROF report showing wait event information:

        ********************************************************************************
SELECT A.customer_id, A.customer_name, COUNT (*) purchases,
MAX (B.transaction_date) last_purchase_date
FROM customers A, purchase_history B
WHERE B.customer_id = A.customer_id
GROUP BY A.customer_id, A.customer_name
ORDER BY A.customer_id

call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.06 0.12 0 27 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 1461 21.41 25.80 7801 5905 5 21893
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 1463 21.47 25.93 7801 5932 5 21893

Misses in library cache during parse: 1
Optimizer goal: CHOOSE
Parsing user id: 17

Rows Row Source Operation
------- ---------------------------------------------------
21893 SORT GROUP BY
1525494 HASH JOIN
31212 TABLE ACCESS FULL CUSTOMERS
1525494 INDEX FAST FULL SCAN PURCHASE_HISTORY_PK (object id 7824)

Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 1461 0.00 0.00
db file sequential read 4 0.00 0.01
db file scattered read 750 0.07 2.25
direct path write 271 0.83 2.79
direct path read 254 0.00 0.01
SQL*Net message from client 1461 1.28 22.40
********************************************************************************

You can see that the query was parsed and executed once, and there were 1461 fetches to read 21893 rows. The Oracle process used 21.47 CPU seconds and 25.93 seconds of elapsed time to parse, execute, and fetch the query. The elapsed time was greater than the CPU time because the Oracle process had to wait on several wait events. The listing at the bottom of the report shows the process waited on network roundtrips to the client and reads and writes to disk.
All of these waits seem to make sense: There was one network roundtrip to the client for each fetch call, and it took SQL*Plus a long time (over 22 seconds) to display the 21,893 rows of data on my display. The db file sequential reads and db file scattered reads resulted from the table access and index fast full scan. The direct path writes and reads resulted from the hash join and sort operations. If you add the CPU time of 21.47 seconds and the wait times (5.06 seconds not including the SQL*Net message from client waits) you come up with an elapsed time of 26.53 seconds. However, the report shows the total elapsed time as 25.93 seconds. This is a good example of the round-off error that is not unusual when thousands of very short time intervals are added together.
In some situations you may not be able to identify exactly which session to trace. This happens frequently when your database is supporting a web application that has frequent short connections, or an application server tier that maintains a pool of database connections all logged on as the same Oracle user. You may have a specific query you want to collect wait event information for, but you may not know which session will be executing the query. One way to deal with this situation is to trace all of the sessions being started by your application for a brief period. This can be done easily with the following SQL*Plus script:

SPOOL traceall.sql
SET HEADING OFF FEEDBACK OFF
SELECT 'EXECUTE SYS.dbms_system.set_ev (' || TO_CHAR (sid) ||
', ' || TO_CHAR (serial#) || ', 10046, 8, '''')'
FROM v$session
WHERE username = 'WEB_USER';
SPOOL OFF
SET FEEDBACK ON
@traceall.sql

This script will start wait event tracing on all sessions connected to the database as the WEB_USER user. To stop tracing simply change the 8 after the 10046 to a 0 and run the script again. You should think about how many sessions this script will trace and how that will affect server load before running on a production system.
After you've traced a number of sessions, you can scan the trace files for occurrences of a specific query with a command like the following:

        grep -n 1234567890 *.trc

Replace 1234567890 with the hash value or address of the statement you are interested in. (You can get these values by querying v$sql.) The output of the above grep command will tell you which trace files the query appears in and on which lines. You can then go to that point in the trace files and get detailed wait event information.
Trace files are easiest to work with when you use a dedicated server connection to the database. If you connect to the database using Oracle's shared server architecture then different SQL statements may be executed by different server processes. Each server process writes to its own trace file. Thus the trace information for the session can be spread over several trace files.


Wait Event Enhancements in Oracle 10g

Oracle 10g brings more significant change to the wait event interface than we have seen in years. Now there are over 800 wait events and names are more descriptive, wait events are categorized into classes, several v$ views have been added, helpful columns have been added to existing v$ views, built-in statistics collection by Active Session History and the Automatic Workload Repository has been introduced, a new time model concept for looking at how sessions spend their time has appeared, and improvements have been made to the session tracing facility.
In this section, we will introduce what we see as the top dozen areas in which wait events and the wait event interface have been enhanced in Oracle 10g. We’ve listed these enhancements in no particular order.

More Descriptive Wait Event Names

Prior to Oracle 10g, some wait event names were quite vague and not very useful without looking at the parameter values for a specific occurrence of the event. For example, an enqueue wait could indicate various situations ranging from contention for a row in a table to waiting on a user-defined lock. Wait event names in Oracle 10g are more descriptive in the areas of latches, enqueues, and buffer busy waits.

There is still a latch free wait event in Oracle 10g, but there are also 26 more specific latch-related events. These cover the most common latches that experience contention. In the past, if we saw a session waiting on the latch free event, we would have output like the following:

SQL> SELECT event, state, p1, p2, p3
2 FROM v$session_wait
3 WHERE sid = 162;

EVENT STATE P1 P2 P3
------------- ------- ----------- ------ -----
latch free WAITING 15113593728 97 5

We would then have to query v$event_name to determine the meaning of the parameters p1, p2, and p3:

SQL> SELECT * FROM v$event_name WHERE name = 'latch free';

EVENT# NAME PARAMETER1 PARAMETER2 PARAMETER3
------ ---------- --------------- --------------- ---------------
3 latch free address number tries

And, seeing that p2 is the latch number, we would need to query v$latch to find out which latch was being waited upon:

SQL> SELECT name
2 FROM v$latch
3 WHERE latch# = 97;

NAME
--------------------
cache buffers chains

In Oracle 10g we simply see:

SQL> SELECT event, state
2 FROM v$session_wait
3 WHERE sid = 162;

EVENT STATE
------------------------------ -------
latch: cache buffers chains WAITING

The descriptive event name saves us two steps in determining which latch is causing the wait. The more detailed description enables one to more quickly drill down to the root cause of the wait.

The names of enqueue-related wait events have also been made more descriptive in Oracle 10g. There is no longer an enqueue wait event—it has been replaced by 184 events with more detailed names. In the past, if sessions were waiting on enqueues, we would have to decode the type of lock from the p1 parameter:

SQL> SELECT event, state, seconds_in_wait siw
2 FROM v$session_wait
3 WHERE sid = 96;

EVENT STATE SIW
----------------------------------- ------------------- ----------
enqueue WAITING 24

SQL> SELECT sid,
2 CHR (BITAND (p1,-16777216) / 16777215) ||
3 CHR (BITAND (p1, 16711680) / 65535) enq,
4 DECODE (CHR (BITAND (p1,-16777216) / 16777215) ||
5 CHR (BITAND (p1, 16711680) / 65535),
6 'TX', 'Transaction (RBS)',
7 'TM', 'DML Transaction',
8 'TS', 'Tablespace and Temp Seg',
9 'TT', 'Temporary Table',
10 'ST', 'Space Mgt (e.g., uet$, fet$)',
11 'UL', 'User Defined',
12 CHR (BITAND (p1,-16777216) / 16777215) ||
13 CHR (BITAND (p1, 16711680) / 65535)) enqueue_name,
14 DECODE (BITAND (p1, 65535), 1, 'Null', 2, 'Sub-Share',
15 3, 'Sub-Exclusive', 4, 'Share', 5, 'Share/Sub-Exclusive',
16 6, 'Exclusive', 'Other') lock_mode
17 FROM v$session_wait
18 WHERE sid = 96;

SID ENQ ENQUEUE_NAME LOCK_MODE
----- ---- ------------------------------ ----------
96 TX Transaction (RBS) Exclusive

In Oracle 10g we get more information directly from the enqueue name:

SQL> SELECT event, state, seconds_in_wait siw
2 FROM v$session_wait
3 WHERE sid = 143;

EVENT STATE SIW
----------------------------------- ------------------- ----------
enq: TX - row lock contention WAITING 495

Additionally, more information is available in some cases from the p1, p2, and p3 parameters, whose meanings vary with the different enqueue-related wait events. (See the Appendix for a listing of all wait events and their associated parameter meanings.)

Wait Event Classes

In Oracle 10g wait events are classified into categories which can help the DBA to more easily determine the likely root cause of the wait. The categories are:

 Administrative   Idle
 Application  Network
 Cluster  Scheduler
 Commit  System I/O    
 Concurrency  User I/O
 Configuration  Other

While nearly 70% of the wait events are in the “Other” category (557 out of 811 in release 10.1.0.3), the most frequently encountered ones are in wait classes with helpful names. Let’s look at the wait class designations of some enqueue events as an example:

SQL> SELECT   wait_class, name
2 FROM v$event_name
3 WHERE name LIKE 'enq%'
4 AND wait_class <> 'Other'
5 ORDER BY wait_class;

WAIT_CLASS NAME
------------------------------ ----------------------------------------
Administrative enq: TW - contention
Administrative enq: DB - contention
Application enq: PW - flush prewarm buffers
Application enq: RO - contention
Application enq: RO - fast object reuse
Application enq: TM - contention
Application enq: TX - row lock contention
Application enq: UL - contention
Concurrency enq: TX - index contention
Configuration enq: ST - contention
Configuration enq: TX - allocate ITL entry
Configuration enq: SQ - contention
Configuration enq: HW - contention

We see that TX enqueues (row locks) and TM enqueues (table locks) are in the Application class, which makes sense since these wait events generally occur because of application behavior. Meanwhile, ST (space management), HW (high-water mark extension), and SQ (sequence number) enqueues are in the Configuration class, as these can usually be alleviated by changes in object and database settings.

The User I/O class includes the db file scattered read, db file sequential read, direct path read, and direct path write events as one might expect, while the System I/O class includes many waits related to reading and writing of redo logs and archive logs. The Commit class has one member, log file sync, as that wait is caused by commits. And the Idle class is made up of various wait events which have traditionally been considered “idle events,” such as SQL*Net message from client. It should be noted that sometimes such “idle events” can actually be symptoms of the root cause of poor performance, so they should not be disregarded without consideration.

In general, the addition of wait classes helps direct the DBA more quickly toward the root cause of performance problems.

v$ View Enhancements

In Oracle 10g there are quite a few new v$ views that pertain to wait events, and helpful new columns have been added to existing v$ views. We will discuss several of the enhancements in this section, although some new v$ views are part of major new Oracle 10g functionality and will be discussed in separate sections later on.

v$event_name

Three columns have been added to the v$event_name view in Oracle 10g: wait_class_id, wait_class#, and wait_class. These columns show which wait class the wait event is part of. We saw in the previous section how this new information might be used. The columns in v$event_name now are:

SQL> DESCRIBE v$event_name
Name Null? Type
----------------------------------------- -------- ----------------------------
EVENT# NUMBER
EVENT_ID NUMBER
NAME VARCHAR2(64)
PARAMETER1 VARCHAR2(64)
PARAMETER2 VARCHAR2(64)
PARAMETER3 VARCHAR2(64)
WAIT_CLASS_ID NUMBER
WAIT_CLASS# NUMBER
WAIT_CLASS VARCHAR2(64)

v$sql and v$sqlarea

The v$sql and v$sqlarea views have six new columns in Oracle 10g that relate to wait events:

 application_wait_time 
 concurrency_wait_time 
 cluster_wait_time 
 user_io_wait_time 
 plsql_exec_time 
 java_exec_time 

These columns are designed to identify the amount of time a SQL statement spends in PL/SQL or Java code execution, or waiting in four specific wait classes. We will use an example to demonstrate the behavior and value of these new columns.

Suppose we create a table called testtab with about a million rows. We then run the following statement from one session without committing, and then run the same statement from another session:

SQL> UPDATE testtab SET numcol = numcol + 1 WHERE ROWNUM < 1000;

Obviously the second session will wait on an enqueue wait event until the first session either commits or rolls back. After a while we roll back the first session and then the second session. Next, in a third session, we run the following statement:

SQL> UPDATE testtab SET numcol = numcol + 1;

The instance we’re using has a small buffer cache, so quite a bit of physical I/O is caused by the statement. After the UPDATE completes we look at v$sqlarea:

SQL> SELECT sql_id, application_wait_time appl, concurrency_wait_time concurr,
2 user_io_wait_time user_io
3 FROM v$sqlarea
4 WHERE sql_text LIKE 'UPDATE testtab SET numcol%';

SQL_ID APPL CONCURR USER_IO
------------- --------- --------- -----------
038m56cp4am0c 178500000 0 20000
fd5mxhdbf09ny 0 10000 105040000

SQL> SELECT sql_id, sql_text
2 FROM v$sqlarea
3 WHERE sql_id IN ('fd5mxhdbf09ny','038m56cp4am0c');

SQL_ID SQL_TEXT
------------- -------------------------------------------------------------
038m56cp4am0c UPDATE testtab SET numcol = numcol + 1 WHERE ROWNUM < 1000
fd5mxhdbf09ny UPDATE testtab SET numcol = numcol + 1

So we see that the first statement (locking rows) spent 178.5 seconds (178,500,000 microseconds) waiting on events in the Application wait class and 0.02 seconds waiting on events in the User I/O wait class. If we recall from the discussion of wait classes, the TX enqueue for row contention is in the Application wait class, and data file reads are in the User I/O wait class. The second statement, which required more disk reads, shows 105 seconds of User I/O waits and a very small amount of concurrency waits.

While the currently available documentation from Oracle does not provide much information about these new columns in v$sql and v$sqlarea, they appear to hold much promise for diagnosing query performance problems.

v$session_wait_history

Up through Oracle 9i, the v$ views show us only the most recent wait event for each session. Even though wait times can accumulate to greatly slow down a process, many waits are very short (from a human perspective) individually. So, it’s often difficult to grab information on a wait event as it is happening. The v$session_wait_history view, new in Oracle 10g, helps by showing the last ten wait events each session has experienced As an example, the following query shows the ten most recent wait events for session 154:

SQL> SELECT   sid, seq#, event, wait_time, p1, p2, p3 
2 FROM v$session_wait_history
3 WHERE sid = 154
4 ORDER BY seq#;

SID SEQ# EVENT WAIT_TIME P1 P2 P3
--- ---- ------------------------ ---------- ------ ------ ------
154 1 db file sequential read 28 4 3547 1
154 2 log buffer space 18 0 0 0
154 3 log buffer space 36 0 0 0
154 4 db file sequential read 0 4 3559 1
154 5 db file sequential read 0 4 1272 1
154 6 db file sequential read 0 4 3555 1
154 7 log buffer space 9 0 0 0
154 8 db file sequential read 0 4 3551 1
154 9 db file sequential read 6 4 1268 1
154 10 log buffer space 8 0 0 0
The seq# column is supposed to show the chronological sequence of the wait events, with 1 being the most recent wait event in the session. Note that this seq# value differs from the seq# column in v$session, which is incremented for each wait experienced by the session throughout the life of the session.

In the above query, we see that the session’s most recent waits alternated between single-block disk reads and log buffer space. This makes sense, since the SQL that the session was performing looked like:

INSERT INTO table1 (column1, column2) 
SELECT column1, column2
FROM table2
WHERE ...

From this list of recent waits, we can also drill down to get more detail. The p1 and p2 values for db file sequential read indicate the file and block numbers being read, so we can quickly determine what segment was being read.

The columns in v$session_wait_history are:

SQL> DESCRIBE v$session_wait_history
Name Null? Type
----------------------------------------- -------- ----------------------------
SID NUMBER
SEQ# NUMBER
EVENT# NUMBER
EVENT VARCHAR2(64)
P1TEXT VARCHAR2(64)
P1 NUMBER
P2TEXT VARCHAR2(64)
P2 NUMBER
P3TEXT VARCHAR2(64)
P3 NUMBER
WAIT_TIME NUMBER
WAIT_COUNT NUMBER

v$session

The v$session view has been enhanced in Oracle 10g with several new columns which are quite useful. The wait event columns from v$session_wait have been added to v$session. In previous releases of Oracle, to get more detailed information about a session experiencing waits (such as what SQL the waiting session is executing), we had to join v$session_wait with v$session, as in:

SQL> SELECT s.sid, w.state, w.event, w.seconds_in_wait siw,
2 s.sql_address, s.sql_hash_value hash_value, w.p1, w.p2, w.p3
3 FROM v$session s, v$session_wait w
4 WHERE s.sid = w.sid
5 AND s.sid = 154;

In Oracle 10g we can get all of this information from v$session:

SQL> SELECT sid, state, event, seconds_in_wait siw,
2 sql_address, sql_hash_value hash_value, p1, p2, p3
3 FROM v$session
4 WHERE sid = 154;

SID STATE EVENT SIW SQL_ADDRESS HASH_VALUE P1 P2 P3
--- ------- ----------------------- --- ---------------- ---------- --- ---- ---
154 WAITING db file sequential read 1 000000038551E820 3625097388 4 9813 1

Two additional columns have been added to v$session that are helpful for wait event analysis: blocking_session and blocking_session_status. The blocking_session column contains the session id (SID) of the holder of the resource that the waiting session is waiting for. The blocking_session_status column indicates the validity of the contents of the blocking_session column. If blocking_session_status is VALID, a valid SID is present in the blocking_session column. In the past, if we saw that a session was waiting for a resource, we would have to do additional queries to determine who was holding the resource. If the resource was an enqueue lock, we would have to query v$lock (sometimes a very expensive query) to determine who was holding the lock. Now we can find out who is holding the lock much more quickly:

SQL> SELECT sid, blocking_session, username,
2 blocking_session_status status
3 FROM v$session
4 WHERE blocking_session_status = 'VALID';

SID BLOCKING_SESSION USERNAME STATUS
--- ---------------- -------- -----------
154 157 TSUTTON VALID

If we combine this with the wait event information now available in v$session, we see:

SQL> SELECT sid, blocking_session, username,
2 event, seconds_in_wait siw
3 FROM v$session
4 WHERE blocking_session_status = 'VALID';

SID BLOCKING_SESSION USERNAME EVENT SIW
--- ---------------- -------- ------------------------------ ---
154 157 TSUTTON enq: TX - row lock contention 318

v$event_histogram

The v$system_event view shows the number of waits, total time waited, and average wait time for a given wait event name (system-wide since instance startup). However, this aggregation can cloud the picture, because a small number of long waits can skew the data. For example, consider the following query from v$system_event:

SQL> SELECT event, total_waits, time_waited, average_wait
2 FROM v$system_event
3 WHERE event = 'enq: TX - row lock contention';

EVENT TOTAL_WAITS TIME_WAITED AVERAGE_WAIT
----------------------------- ----------- ----------- ------------
enq: TX - row lock contention 17218 2101966 122

We see that there have been 17,218 waits and that the average wait time was 1.22 seconds, but we have no idea how the wait times are distributed. Were all of these waits roughly the same length? Were most of them under one second long and a few really long waits threw off the average? We can’t tell from v$system_event. However, in Oracle 10g we can look at v$event_histogram for a more complete picture:

SQL> SELECT event, wait_time_milli, wait_count
2 FROM v$event_histogram
3 WHERE event = 'enq: TX - row lock contention';

EVENT WAIT_TIME_MILLI WAIT_COUNT
----------------------------- --------------- ----------
enq: TX - row lock contention 1 833
enq: TX - row lock contention 2 635
enq: TX - row lock contention 4 372
enq: TX - row lock contention 8 395
enq: TX - row lock contention 16 781
enq: TX - row lock contention 32 3729
enq: TX - row lock contention 64 3050
enq: TX - row lock contention 128 410
enq: TX - row lock contention 256 47
enq: TX - row lock contention 512 46
enq: TX - row lock contention 1024 37
enq: TX - row lock contention 2048 3
enq: TX - row lock contention 4096 6880

We see that very few of the waits were anywhere near 1.22 seconds. Nearly 60% of the waits were less than 0.128 seconds (with most of those between 16 milliseconds and 64 milliseconds), and most of the remaining waits were between 2.048 seconds and 4.096 seconds (at which point some timed out and started new waits). In this way, the v$event_histogram view gives us a more accurate picture of wait times summarized by event name.

The columns of v$event_histogram are:

SQL> DESCRIBE v$event_histogram
Name Null? Type
----------------------------------------- -------- ----------------------------
EVENT# NUMBER
EVENT VARCHAR2(64)
WAIT_TIME_MILLI NUMBER
WAIT_COUNT NUMBER

v$system_wait_class and v$session_wait_class

The two new views v$system_wait_class and v$session_wait_class enable us to get system and session wait information summarized by wait classes. This gives us a higher level view of what is happening in the system or session, rather than focusing on individual events. The views are roughly equivalent to the views v$system_event and v$session_event, except that they roll up the events by wait class. The wait times are expressed in centiseconds since instance startup for v$system_wait_class and centiseconds since session connection for v$session_wait_class.

The following queries show how much time (in centiseconds) has been spent on waits in each class across the system since instance start and for one specific session since that session began:

SQL> SELECT   wait_class, time_waited
2 FROM v$system_wait_class
3 ORDER BY time_waited DESC;

WAIT_CLASS TIME_WAITED
------------- -----------
Idle 777450022
System I/O 1261584
User I/O 116667
Configuration 116481
Application 72301
Other 12432
Commit 3496
Concurrency 319
Network 1

SQL> SELECT wait_class, time_waited
2 FROM v$session_wait_class
3 WHERE sid = 154
4 ORDER BY time_waited DESC;

WAIT_CLASS TIME_WAITED
------------- -----------
Idle 612453
User I/O 1500
Configuration 28
Commit 11
Other 0
Application 0
Network 0

Since the wait times shown in these views are aggregations since system or session startup, these views are best used by taking samples and comparing the results to determine waits over a period of time. For instance, you could get data for the entire instance at time T1:

DROP TABLE swc_snap;
CREATE TABLE swc_snap
AS
SELECT wait_class, total_waits, time_waited
FROM v$system_wait_class;

And then, at time T2 a while later, get a summary of waits between T1 and T2:

SELECT   a.wait_class, (a.time_waited - b.time_waited) tm_waited
FROM v$system_wait_class a, swc_snap b
WHERE a.wait_class = b.wait_class
AND a.total_waits > NVL (b.total_waits, 0)
ORDER BY tm_waited DESC;

WAIT_CLASS TM_WAITED
--------------- ----------
Idle 255767
Application 171
System I/O 156
User I/O 44
Other 21
Commit 13
Network 1

Active Session History

In previous releases of Oracle, the detailed information displayed in v$session_wait could prove extremely helpful in diagnosing performance problems—if you queried the view at the right time. The v$session_wait_history view in Oracle 10g makes it a little easier to catch detailed information by preserving the last ten waits for each session. But what if you want detailed information about a session’s waits for a period further back in time? This is where the Active Session History feature of Oracle 10g—ASH for short—comes in handy. ASH makes detailed information about a sampling of waits encountered by all sessions available to us for a very long time.

In Oracle 10g, a new background daemon process called MMNL queries v$session once each second and stores information about all active sessions in a circular buffer in memory accessible by a new view called v$active_session_history. How far back you can look at sessions in this view depends on session activity and how much memory Oracle allocated for ASH. Oracle’s goal is to keep at least a few hours of session data available in this view. The v$active_session_history view includes much of the detailed wait event information shown in v$session:

SQL> DESCRIBE v$active_session_history
Name Null? Type
----------------------------------------- -------- ----------------------------
SAMPLE_ID NUMBER
SAMPLE_TIME TIMESTAMP(3)
SESSION_ID NUMBER
SESSION_SERIAL# NUMBER
USER_ID NUMBER
SQL_ID VARCHAR2(13)
SQL_CHILD_NUMBER NUMBER
SQL_PLAN_HASH_VALUE NUMBER
SQL_OPCODE NUMBER
SERVICE_HASH NUMBER
SESSION_TYPE VARCHAR2(10)
SESSION_STATE VARCHAR2(7)
QC_SESSION_ID NUMBER
QC_INSTANCE_ID NUMBER
EVENT VARCHAR2(64)
EVENT_ID NUMBER
EVENT# NUMBER
SEQ# NUMBER
P1 NUMBER
P2 NUMBER
P3 NUMBER
WAIT_TIME NUMBER
TIME_WAITED NUMBER
CURRENT_OBJ# NUMBER
CURRENT_FILE# NUMBER
CURRENT_BLOCK# NUMBER
PROGRAM VARCHAR2(48)
MODULE VARCHAR2(48)
ACTION VARCHAR2(32)
CLIENT_ID VARCHAR2(64)

As you can see, v$active_session_history captures the essential wait-related data from v$session. It also captures useful information about the SQL statement currently being executed, as well as current object number, file, and block being accessed. When a wait that was sampled by ASH completes, Oracle fills in the time_waited column for the row in v$active_session_history with the actual duration of the wait.

The Automatic Workload Repository, which we will discuss in the next section, writes data from v$active_session_history to disk at regular intervals, preserving one sample every ten seconds from each active session. So, active session information remains accessible—although with less detail—even after the data has aged out of v$active_session_history.

Because ASH is always “on,” you always have access to detailed information about waits encountered in sessions within the last few hours. This means that if a user complains about a performance problem, you may be able to query v$active_session_history and gain insight into the problem without having to initiate an extended SQL trace or start a close watch of v$session while they reproduce the problem.

An important thing to keep in mind about v$active_session_history, however, is that it is populated by sampling v$session once each second. A session may encounter many different waits during a one second period, but only the one wait that was in progress when ASH collected its sample will be recorded in v$active_session_history. For this reason, ASH is valuable for general aggregate queries but not for precise counting of individual events or determining minimum or maximum wait times. Statistically speaking, the data collected by ASH is probably more accurate over a larger time interval and/or number of sessions.

For example, you might query v$active_session_history to see what percentage of time over the last two hours a particular group of sessions spent waiting on disk reads. However, using this view to determine how many disk read waits a session encountered in the last minute probably will not yield very accurate results.

Even though ASH data is only a sampling of active sessions, the information can prove to be quite useful. For example, the following query shows that sessions running the ARXENV application over the last two hours encountered a great deal of row-level lock contention:

SQL> SELECT   DECODE (session_state, 'WAITING', event, NULL) event,
2 session_state, COUNT(*), SUM (time_waited) time_waited
3 FROM v$active_session_history
4 WHERE module = 'ARXENV'
5 AND sample_time > SYSDATE - (2/24)
6 GROUP BY DECODE (session_state, 'WAITING', event, NULL),
7 session_state;

EVENT SESSION_STATE COUNT(*) TIME_WAITED
------------------------------ ------------- -------- -----------
ON CPU 124 0
log file sync WAITING 2 52686
db file scattered read WAITING 2 28254
db file sequential read WAITING 1 6059
control file sequential read WAITING 1 9206
SQL*Net break/reset to client WAITING 1 9140
enq: TX - row lock contention WAITING 922 930864016

In addition to running queries against the v$active_session_history view, you can use Enterprise Manager to run reports that will display ASH data.

Although ASH runs on all Oracle 10g databases by default, you are not allowed to query the v$active_session_history view (or run the corresponding reports in Enterprise Manager) unless you have purchased the Diagnostic Pack.

Automatic Workload Repository

Oracle 10g includes another significant facility relevant to the wait event interface. It’s known as the Automatic Workload Repository—or AWR for short. AWR is basically a next-generation Statspack. By default, AWR collects an hourly snapshot of database performance information, storing the data in tables in the SYS schema. AWR is configured automatically when you create an Oracle 10g database. You can call the dbms_workload_repository package to collect a snapshot on demand, purge a snapshot or range of snapshots, or change the snapshot interval or retention period. (By default snapshots are collected at the top of each hour and are purged after seven days.)

AWR collects the same type of data that Statspack does—including system-level statistics, resource-intensive SQL, and of course instance-wide wait event information. AWR also collects data that is new for Oracle 10g, such as time model statistics (which we will discuss in the next section). As an aside, the Oracle 10g version of Statspack also collects a lot of this new information, including time model statistics.

You can generate an AWR report of database activity between two snapshots by running the awrrpt.sql script in the rdbms/admin directory under $ORACLE_HOME. This script offers reports formatted as plain text or HTML. The reports will look familiar if you have used Statspack before. You can use Enterprise Manager to generate AWR reports as well.

AWR offers many benefits over Statspack. For one, it is more tightly integrated into the Oracle kernel, reducing resource requirements and overhead when collecting snapshots. AWR snapshots also include ASH data from v$active_session_history, providing session-level information to complement the system-level data collection familiar to Statspack users.

Data collected by AWR is made easily accessible via views with names that start DBA_HIST. This enables you to write your own reports that extract just the data you need to address a specific situation, if for some reason you don’t find what you need in the standard AWR report. For example, the following query displays the two most recent snapshot IDs:

SQL> SELECT snap_id, begin_interval_time, end_interval_time
2 FROM (
3 SELECT snap_id, begin_interval_time, end_interval_time
4 FROM dba_hist_snapshot
5 ORDER BY end_interval_time DESC
6 )
7 WHERE ROWNUM <= 2;

SNAP_ID BEGIN_INTERVAL_TIME END_INTERVAL_TIME
---------- ------------------------- -------------------------
362 10-MAR-05 04.00.02.018 PM 10-MAR-05 05.00.36.581 PM
361 10-MAR-05 03.00.25.885 PM 10-MAR-05 04.00.02.018 PM

Just like ASH, AWR runs on all Oracle 10g databases by default. Also like ASH, you are not allowed to query the AWR views (or run AWR reports) unless you have licensed the Diagnostic Pack. Because AWR consumes system resources when collecting snapshots and uses up storage in the SYSAUX tablespace, you may want to disable the collection of AWR snapshots if you are not licensed to use AWR. This may be done by using the dbms_workload_repository package. If AWR is not available to you, Statspack is still a good way to go in Oracle 10g.

Time Model Statistics

Oracle 10g introduces a new concept called Time Model Statistics. This information provides yet another way to see how time is spent, and with greater detail than was available previously. The v$sys_time_model view shows time model statistics for the entire system since instance startup, while the v$sess_time_model view shows time model statistics for each session since session start. The columns in these two views are as follows:

SQL> DESCRIBE v$sys_time_model
Name Null? Type
---------------------------------------- -------- ---------------------------
STAT_ID NUMBER
STAT_NAME VARCHAR2(64)
VALUE NUMBER

SQL> DESCRIBE v$sess_time_model
Name Null? Type
---------------------------------------- -------- ---------------------------
SID NUMBER
STAT_ID NUMBER
STAT_NAME VARCHAR2(64)
VALUE NUMBER

A sample query from v$sys_time_model shows the following:

SQL> SELECT   stat_name, value / 1000000 seconds
2 FROM v$sys_time_model
3 ORDER BY seconds DESC;

STAT_NAME SECONDS
------------------------------------------------ ----------
DB time 80970.190
sql execute elapsed time 75057.271
DB CPU 44448.628
background elapsed time 29333.160
PL/SQL execution elapsed time 8824.538
background cpu time 5170.311
parse time elapsed 1270.147
hard parse elapsed time 838.068
PL/SQL compilation elapsed time 176.731
sequence load elapsed time 112.334
connection management call elapsed time 44.644
failed parse elapsed time 11.946
hard parse (sharing criteria) elapsed time 5.579
hard parse (bind mismatch) elapsed time 4.610
failed parse (out of shared memory) elapsed time 0.000
Java execution elapsed time 0.000
inbound PL/SQL rpc elapsed time 0.000

This query shows us a lot more information about how Oracle sessions have spent their time (categorically) than v$sysstat and v$sesstat do. Of course, we have to know how to interpret this information before we can put it to work for us. Values in these views are shown in microseconds, and they do not include background processes unless “background” appears in the statistic name. The “DB time” statistic shows elapsed time spent on database calls (user processes only). This amounts to time spent on the CPU or waiting on non-idle wait events. For a description of the other time model statistics, see the v$sess_time_model view listing in the Oracle 10g Database Reference manual.

From this query, among many other useful facts, we can see that no time has been spent executing Java, very little time has been spent hard parsing or compiling PL/SQL, background processes have used about 10% of the CPU time, and about 11% of the elapsed time for user sessions was spent on PL/SQL execution.

Tracing Facility Improvements

The extended SQL trace facility, also known as event 10046, allows us to capture in a trace file detailed information about every wait event encountered by a database session. This feature has been available in Oracle for a long time. However, Oracle 10g offers some helpful improvements in this area.

Enabling extended SQL trace has always been a bit of a nuisance. In earlier releases of Oracle, you had to use a clumsy ALTER SESSION SET EVENTS statement or—worse yet—call the undocumented dbms_system.set_ev procedure to set the 10046 event in another user’s session. In Oracle 8i the dbms_support package was introduced to make this step easier, but the package was missing from many releases of Oracle and usually was not installed by default.

Oracle 10g introduces the new dbms_monitor package. This package, among many other things, makes it very easy to turn extended SQL trace on and off in any Oracle session. With one easy to remember call, you can turn extended SQL trace on or off, with wait events and/or bind variables captured in the trace file:

SQL> DESCRIBE dbms_monitor
...
PROCEDURE SESSION_TRACE_DISABLE
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
SESSION_ID BINARY_INTEGER IN DEFAULT
SERIAL_NUM BINARY_INTEGER IN DEFAULT
PROCEDURE SESSION_TRACE_ENABLE
Argument Name Type In/Out Default?
------------------------------ ----------------------- ------ --------
SESSION_ID BINARY_INTEGER IN DEFAULT
SERIAL_NUM BINARY_INTEGER IN DEFAULT
WAITS BOOLEAN IN DEFAULT
BINDS BOOLEAN IN DEFAULT

If the session_id parameter is not specified or set to NULL, your own session will be traced. Thus, the following two statements should be equivalent:

In Oracle 9i and earlier, extended SQL trace was easy to use if your application connected to the Oracle database via a dedicated server connection. If the shared server architecture was used, each shared server process that serviced a request from the session being traced would write its data to a separate trace file. Furthermore, tracing sessions in a connection pool environment became difficult because one Oracle session could actually process requests for many different end-user sessions.

The dbms_monitor package in Oracle 10g addresses this problem. Instead of enabling extended SQL trace for a specific Oracle session, you can enable it for a specific client identifier or combination of service, module, and action. Any time any Oracle session has the specified client identifier or combination of service, module, and action, the session will be traced. Sessions can set or clear their client identifier at will by calling the dbms_session package, and they can set their module and action by calling the dbms_application_info package.

Suppose a web-based application uses a pool of 30 database connections to serve user requests and maintains a current_sessions table to keep track of the state of each end-user session. When a user clicks a button in their browser window, the application server receives the HTTP request and hands it off to an application server process. The application server process grabs a free database connection from the pool and accesses the database as necessary to service the request. It is likely that subsequent requests from the same user will be processed using different database connections.

In Oracle 9i and earlier it would have been very difficult to trace the one user’s database activity in this environment. The user’s database accesses are spread among multiple Oracle server processes in the connection pool. Moreover, each of those Oracle processes is handling requests from many different end users.

With dbms_monitor in Oracle 10g, the application could be modified in a way to make extended SQL trace a whole lot easier. We mentioned that the application uses the current_sessions table to maintain state for each end user session. Each time the application server grabs a database connection from the pool, it could set the client identifier for the Oracle session to the session_id from the current_sessions table before doing any database access for that end user session. Then the application could clear the client identifier before returning the database connection to the pool. The Oracle calls could look like this:

EXECUTE dbms_session.set_identifier ('session_id174837492748');
...do the work for this end user session...
EXECUTE dbms_session.clear_identifier

To trace this end user session, we could now call the dbms_monitor package like this:

SQL> EXECUTE dbms_monitor.client_id_trace_enable -
> ('session_id174837492748', waits=>TRUE, binds=>TRUE);

This call to dbms_monitor will cause each Oracle process to write extended SQL trace data to a trace file for all calls that occur while the client identifier for the session is set to the specified value. However, each Oracle process will write to its own trace file. This will cause the trace data to be split over multiple files. To address this problem, Oracle 10g provides a new command-line utility called trcsess. The trcsess utility reads multiple trace files and consolidates entries from the various files that meet the specified criteria into one trace file that can be processed by TKPROF. To consolidate the trace data for our current example, we could use the following commands:

$ cd $ORACLE_BASE/admin/$ORACLE_SID/udump
$ trcsess output=/home/rschrag/case1403/case1403-trial1.trc \
clientid=session_id174837492748

In this way the dbms_monitor package and trcsess utility in Oracle 10g make it a lot easier to collect extended SQL trace data from an end user’s session when connection pooling or other session aggregation techniques are used by the application server tier.

Conclusion

Oracle 10g includes many enhancements to the wait event interface that should make performance management using wait event methodologies easier than ever. Some enhancements, such as descriptive wait event names, wait classes, and the session_trace_enable procedure in the dbms_monitor package, are simple conveniences that make our jobs easier. These enhancements don’t give us any information or power that we didn’t have before. Other enhancements, however, such as time model statistics, v$event_histogram, and new columns in v$sql and v$sqlarea, provide us with helpful timing and wait information that was previously unavailable.

Although as of this writing documentation on these new features is incomplete and discussion on Metalink is surprisingly sparse, many of these enhancements will likely prove quite valuable to the Oracle DBA responsible for performance management of a complex Oracle system.



Event Wait Analysis with STATSPACK

In STATSPACK, you can get a snapshot of Oracle wait events every hour and plot changes in wait behavior over time. You can also set thresholds, and report only on wait events that exceed your pre-defined threshold. Here is the script that is commonly used for exception reporting of wait events.

-- prompt
-- prompt
-- prompt ***********************************************************
-- prompt Excessive waits on background events
-- prompt ***********************************************************
-- prompt

ttitle 'High waits on background events|Rollup by hour'

column mydate heading 'Yr. Mo Dy Hr' format a13;
column event format a30;
column total_waits heading 'tot waits' format 999,999;
column time_waited heading 'time wait' format 999,999;
column total_timeouts heading 'timeouts' format 9,999;

break on to_char(snap_time,'yyyy-mm-dd') skip 1;

select
to_char(snap_time,'yyyy-mm-dd HH24') mydate,
e.event,
e.total_waits - nvl(b.total_waits,0) total_waits,
e.time_waited - nvl(b.time_waited,0) time_waited,
e.total_timeouts - nvl(b.total_timeouts,0) total_timeouts
from
stats$bg_event_summary b,
stats$bg_event_summary e,
stats$snapshot sn
where snap_time > sysdate-&1
and e.event not like '%timer'
and e.event not like '%message%'
and e.event not like '%slave wait%'
and e.snap_id = sn.snap_id
and b.snap_id = e.snap_id-1
and b.event = e.event
and e.total_timeouts > 100
and (e.total_waits - b.total_waits > 100
or
e.time_waited - b.time_waited > 100
);

Here is the output from this script. As we can see, we get a time-series result, showing us those days and hours when our thresholds are exceeded. If we look at this listing, we see that every evening between 10:00 p.m. and 11:00 p.m., we experience high waits on the redo logs.

Wed Aug 21                                                            page    1
High waits on background events
Rollup by hour

Yr. Mo Dy Hr EVENT tot waits time wait timeouts
------------- ------------------------------ --------- --------- --------
2002-08-18 22 LGWR wait for redo copy 9,326 1,109 286
2002-08-18 23 LGWR wait for redo copy 8,506 316 8
2002-08-18 23 buffer busy waits 214 21,388 206
2002-08-19 00 LGWR wait for redo copy 498 5 0
2002-08-19 01 LGWR wait for redo copy 497 15 0
2002-08-19 22 LGWR wait for redo copy 9,207 1,433 427
2002-08-19 22 buffer busy waits 529 53,412 515
2002-08-19 23 LGWR wait for redo copy 9,066 367 9
2002-08-19 23 buffer busy waits 250 24,479 236
2002-08-20 00 LGWR wait for redo copy 771 16 0
2002-08-20 22 LGWR wait for redo copy 8,030 2,013 634
2002-08-20 22 buffer busy waits 356 35,583 343
2002-08-20 23 LGWR wait for redo copy 8,021 579 86
2002-08-20 23 buffer busy waits 441 44,677 432
2002-08-21 00 LGWR wait for redo copy 1,013 26 1
2002-08-21 00 rdbms ipc reply 160 30,986 149
2002-08-21 01 LGWR wait for redo copy 541 17 0

Using Queries Against v$event_wait

Here is the most commonly used script for displaying system-wide events, called current_waits.sql.

set pages 999
set lines 90

column c1 heading 'Event|Name' format a30
column c2 heading 'Total|Waits' format 999,999,999
column c3 heading 'Seconds|Waiting' format 999,999
column c4 heading 'Total|Timeouts' format 999,999,999
column c5 heading 'Average|Wait|(in secs)' format 99.999

ttitle 'System-wide Wait Analysis|for current wait events'

select
event c1,
total_waits c2,
time_waited / 100 c3,
total_timeouts c4,
average_wait /100 c5
from
sys.v_$system_event
where
event not in (
'dispatcher timer',
'lock element cleanup',
'Null event',
'parallel query dequeue wait',
'parallel query idle wait - Slaves',
'pipe get',
'PL/SQL lock timer',
'pmon timer',
'rdbms ipc message',
'slave wait',
'smon timer',
'SQL*Net break/reset to client',
'SQL*Net message from client',
'SQL*Net message to client',
'SQL*Net more data to client',
'virtual circuit status',
'WMON goes to sleep'
)
AND event not like 'DFS%'
and event not like '%done%'
and event not like '%Idle%'
AND event not like 'KXFX%'
order by c2 desc;

Below is the output from this script. As we see, the report shows all cumulative waits since instance startup time.

Tue Aug 20                                                          page    1
System-wide Wait Analysis
for current wait events

Average
Event Total Seconds Total Wait
Name Waits Waiting Timeouts (in secs)
------------------------------ ------------ -------- ------------ ---------
db file sequential read 2,902,850 3,829 0 .001
latch free 2,248,864 496 1,524,235 .000
PX Deq: Table Q Normal 2,080,463 4,469 248 .002
PX Deq Credit: send blkd 1,321,019 52,251 23,032 .040
direct path read 986,951 6,931 0 .007
PX Deq Credit: need buffer 800,970 1,091 84 .001
log file parallel write 415,175 5,078 2 .012
direct path write 321,096 9,342 0 .029
PX Deq: Execution Msg 198,768 56,384 26,020 .284
log file sequential read 118,480 164 0 .001
PX Deq: Execute Reply 92,487 5,628 2,407 .061
log file sync 87,295 1,327 7 .015
db file scattered read 70,112 162 0 .002
enqueue 44,335 1,468 354 .033
PX Deq: Join ACK 42,503 77 0 .002
file open 28,545 8 0 .000
PX Deq: Signal ACK 26,753 253 6,705 .009
log file switch completion 54 18 5 .341
control file parallel write 23,785 340 0 .014
SQL*Net more data from client 19,847 878 0 .044
PX Deq: Parse Reply 17,885 26 0 .001
db file parallel write 17,745 1,293 4 .073
PX Deq: Msg Fragment 15,612 18 8 .001
rdbms ipc reply 11,013 167 66 .015
PX Deq: Table Q qref 7,778 2 0 .000
LGWR wait for redo copy 7,608 2 43 .000
control file sequential read 4,098 1 0 .000
buffer busy waits 2,970 9 0 .003
PX Deq: Table Q Sample 2,053 25 27 .012
library cache pin 1,353 6 0 .004
PX Deq: Table Q Get Keys 530 1 0 .001
local write wait 317 177 163 .560
file identify 315 0 0 .000
refresh controlfile command 214 0 0 .002
PX qref latch 135 105 102 .780
log file single write 80 2 0 .021
imm op 67 0 0 .000
process startup 61 2 0 .036
write complete waits 33 34 33 1.032
library cache load lock 25 6 2 .256
log buffer space 20 6 0 .286
db file single write 6 0 0 .007
row cache lock 5 0 0 .006
db file parallel read 2 0 0 .010
instance state change 2 0 0 .000
reliable message 1 0 0 .010
library cache lock 1 0 0 .020

This should give a list of "current" wait events, but some events are more important than others. These are the one's to watch-out for: