Skip to main content
raphi's corner
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

ashtop

ashtop - the Swiss Army Knife for ASH

Tanel Poder’s script “ashtop.sql” greatly simplifies querying ASH and is essentially a fully parameterizable “grep” for ASH. The script comes in two variants:

  • ashtop.sql → for querying ASH: gv$active_session_history
  • dashtop.sql → for querying DASH: dba_hist_active_sess_history

Both scripts are in the “ash” directory of Tanel’s github: https://github.com/tanelpoder/tpt-oracle

Pro tip: do yourself a favour and download the whole repository. There are many, many more very useful scripts for all kinds of DBA tasks.

There’s also a very good video by Tanel on YouTube that explains how ashtop works:
https://www.youtube.com/watch?v=D7bg1Am7Y9s

To use ashtop efficiently, it is necessary to know the structure of ASH and how it works. If you haven’t read it yet, check out this post: Active Session History. Note: whenever I mention “ASH” or “ashtop”, the same is true for “DASH” and “dashtop”.

Syntax

The basic syntax is very simple:

@ashtop <GROUP BY> <FILTER> <BEGIN_TIME> <END_TIME>;
  • GROUP BY: What to group by, e.g., session_id, sql_id, event
  • FILTER: What to filter by, e.g., session_type='FOREGROUND' or event like 'gc%'
  • BEGIN_TIME: From which point in time, e.g., sysdate-1/24
  • END_TIME: Until which point in time, e.g., sysdate

Structure of ashtop

This is the basic structure of the script, quite similar to the basic script covered in the post about ASH. However, the complete script has over 130 lines ;)

SELECT
&1
, COUNT(*) "TotalSeconds"
FROM
gv$active_session_history a
, dba_users u
, dba_objects o
WHERE
 a.user_id = u.user_id (+)
AND a.current_obj# = o.object_id(+)
AND &2
AND sample_time BETWEEN &3 AND &4
GROUP BY
&1
ORDER BY
"TotalSeconds" DESC
, &1

Time specifications

BEGIN and END_TIME can be passed to the script in various ways, mixed forms are also possible:

  • Relative to sysdate:
@ashtop username,sql_id session_type='FOREGROUND' sysdate-1/24/12 sysdate;
  • ANSI syntax 1:
@ashtop username,sql_id session_type='FOREGROUND' DATE'2021-08-24' DATE'2021-08-25';
  • ANSI syntax 2:
@ashtop username,sql_id session_type='FOREGROUND' "TIMESTAMP'2024-04-17 18:00:00'" "TIMESTAMP'2024-04-17 20:00:00'";
  • to_date:
@ashtop username,sql_id session_type='FOREGROUND' "to_date('24-AUG-21 18:00', 'DD-MON-YY HH24:MI')" sysdate;

More info on using ANSI syntax in Tanel’s scripts:
https://tanelpoder.com/2012/12/29/a-tip-for-lazy-oracle-users-type-less-with-ansi-date-and-timestamp-sql-syntax/

Derived columns

The script includes various derived columns that do not exist like this in ASH.

username and objt

Users and objects are only listed as IDs in ASH; if you want the names, you have to join dba_user and dba_objects. Ashtop simplifies this with the columns username and objt. These columns can be used as GROUP BY and as filters.

For example, to show the top 15 users accessing an object named “TABLE_NAME”:

@ashtop username "objt='TABLE_NAME'" sysdate-1/24 sysdate;

Or the top objects accessed by the user “USER”:

@ashtop objt "username='USER'" sysdate-1/24 sysdate;

The script always returns the top 15 rows that match the specified arguments. Usually, that’s enough, but it also means you don’t see the rest of the waits. If you can’t find the cause of a problem with ashtop, you should analyze the ASH with the basic script described in the post about ASH.

event2

When a session is on the CPU, the EVENT column is NULL. To see that it was really on the CPU, you would have to output SESSION_STATE as well:

@ashtop sql_id,session_state,event 1=1 sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_IDSESSIONEVENTFIRST_SEENLAST_SEENExecs Seen
2343.75%c0syzmwpwp3mwON CPU2021-09-10 12:44:192021-09-10 13:44:161449
1524.43%fj40waf596jvaON CPU2021-09-10 12:44:212021-09-10 13:44:167

To avoid having to always specify session_state, you can simply use the column “event2”:

@ashtop sql_id,event2 1=1 sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_IDEVENT2FIRST_SEENLAST_SEENExecs Seen
2343.75%c0syzmwpwp3mwON CPU2021-09-10 12:44:322021-09-10 13:44:291449
1528.43%fj40waf596jvaON CPU2021-09-10 12:44:302021-09-10 13:44:297

A value of 1 for AAS and “ON CPU” EVENT2 means that approximately one CPU core was busy with this execution. The value scales; AAS of 5 means 5 cores were busy.

time_model_name

ASH offers many columns that show which step of execution the query was currently in, e.g. Hard Parse or SQL Execution:

ColumnDescription
IN_CONNECTION_MGMTWas the session doing connection management at sampling? (Y/N)
IN_PARSEWas the session parsing at sampling? (Y/N)
IN_HARD_PARSEWas the session hard parsing at sampling? (Y/N)
IN_SQL_EXECUTIONWas the session executing SQL at sampling? (Y/N)
IN_PLSQL_EXECUTIONWas the session executing PL/SQL at sampling? (Y/N)
IN_PLSQL_RPCWas the session executing inbound PL/SQL RPC at sampling? (Y/N)
IN_PLSQL_COMPILATIONWas the session compiling PL/SQL at sampling? (Y/N)
IN_JAVA_EXECUTIONWas the session executing Java at sampling? (Y/N)
IN_BINDWas the session doing bind operations at sampling? (Y/N)
IN_CURSOR_CLOSEWas the session closing a cursor at sampling? (Y/N)
IN_SEQUENCE_LOADWas the session loading in sequence at sampling? (Y/N)
IN_INMEMORY_QUERYWas the session querying In-Memory Column Store? (Y/N)
IN_INMEMORY_POPULATEWas the session populating IM column store? (Y/N)
IN_INMEMORY_PREPOPULATEWas the session prepopulating IM column store? (Y/N)
IN_INMEMORY_REPOPULATEWas the session repopulating IM column store? (Y/N)
IN_INMEMORY_TREPOPULATEWas the session trickle repopulating IM column store? (Y/N)
IN_TABLESPACE_ENCRYPTIONWas encryption/decryption of a tablespace occurring? (Y/N)

To avoid passing all these columns as arguments to ashtop, you can use the derived column “time_model_name”. It is based on the ASH “Time_Model” bitfield and maps the value to a descriptive text:

@ashtop sql_id,time_model_name 1=1 sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_IDTIME_MODEL_NAMEFIRST_SEENLAST_SEENExecs Seen
737.24%g9u23ytkurdwzSQL_EXECUTION2021-09-13 12:18:582021-09-13 13:16:23737
549.23%CONNECTION_MGMT2021-09-13 12:19:032021-09-13 13:18:271
480.13%1pyct56psc65zSQL_EXECUTION2021-09-13 12:19:032021-09-13 13:18:51480
478.13%6u8vh1hymdfh3SQL_EXECUTION2021-09-13 12:18:542021-09-13 13:18:52478
Meaning of some of the time_model_name
Time_Model_NameMeaning
SQL_EXECUTIONStatement is being executed
PARSESoft parse
HARD_PARSEHard parse
PARSE HARD_PARSESoft parse attempted, ended in hard parse
CONNECTION_MGMTLogin/Logouts
BINDBinds are being filled
(Empty line)Step not represented in ASH

Empty SQL_ID

Sometimes ashtop shows no SQL_ID for a session, either because the session belongs to a background process (in that case, the user is SYS and SESSION_TYPE is BACKGROUND) or because the session is mainly waiting for commits.

@ashtop sql_id,event2,username 1=1 sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_IDEVENT2USERNAMESESSION_TYPEFIRST_SEENLAST_SEENExecs Seen
3068.957%log file syncRAPHI_APPLFOREGROUND2021-09-13 11:50:282021-09-13 12:50:231
809.215%ON CPURAPHI_APPLFOREGROUND2021-09-13 11:50:262021-09-13 12:50:211
100.02%ON CPUSYSBACKGROUND2021-09-13 11:51:192021-09-13 12:50:031

If you want to see what the user session is actually doing even though no SQL_ID is listed, you can search for “Top_Level_Call_Name”:

@ashtop sql_id,top_level_call_name,event2 "sql_id is null and username='RAPHI_APPL'" sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_IDTOP_LEVEL_CALL_NAMEEVENT2FIRST_SEENLAST_SEENExecs Seen
2879.865%COMMITlog file sync2021-09-13 11:54:402021-09-13 12:54:381
195.14%COMMITON CPU2021-09-13 11:55:092021-09-13 12:54:131
183.14%V8 Bundled ExecON CPU2021-09-13 11:55:562021-09-13 12:54:051

Formatting

Tanel assumes you use his scripts in a terminal where you can scroll horizontally. If not, you should at least increase the linesize and maybe shorten a few columns:

set lines 400
col objt format a30
col event format a30

Customizing ashtop.sql

Sometimes it is necessary to customize ashtop.sql, for example to show more than the top 15 events or to sort by another criteria. It’s best to create a copy of the original ashtop.sql before you customize it.

cp /nas/smdb/scripts/sql/ash/ashtop.sql ${HOME}
cd ${HOME}
vi ashtop.sql

Change the order by and the number of rows returned:

ORDER BY
       -- TotalSeconds DESC
       time_waited DESC
       , &1
...
-- ROWNUM <= 15
    ROWNUM <= 30

Working with a copy of the ASH

If a copy of the ASH has been created with CTAS for backup, the following line needs to be changed to the saved ASH:

cp /nas/smdb/scripts/sql/ash/ashtop.sql ${HOME}
cd ${HOME}
vi ashtop.sql
-- FROM gv$active_session_history a) a
   FROM ash_save220901 a) a

Using ashtop

A simple query looks like this, the structure is always the same:

sys@CIS_1/T1> @ashtop username,sql_id session_type='FOREGROUND' sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisUSERNAMESQL_IDFIRST_SEENLAST_SEENExecs Seen
1184.29%RAPHI_USER37hs5hm167vhy2019-11-26 12:35:382019-11-26 12:40:242
583.14%RAPHI_APPLbxj6rfn7du7c82019-11-26 11:57:302019-11-26 12:54:252
378.13%RAPHI_APPL9jwmpa0zs1czv2019-11-26 12:02:152019-11-26 12:03:231
  • First, these three columns are listed:
    • Total Seconds: How many seconds all executions of the query are listed in ASH for the specified period
    • AAS: Average Active Sessions spent per second with the wait event in the specified period
    • %This: Ratio of the specified period to seconds all sessions spent with this event
  • Then, the columns passed as the first argument (GROUP BY) are shown:
    • USERNAME
    • SQL_ID
  • Lastly, some timing information
    • FIRST_SEEN: First execution in the specified period
    • LAST_SEEN: Last execution in the specified period
    • Distinct Execs Seen: Number of different executions in the specified period

Example queries

There are countless ways to query ASH with ashtop, here is a selection:

Top events of the last hour

@ashtop event2 1=1 sysdate-1/24 sysdate;

Top waits of the last 5 minutes

@ashtop event2 "event2 not like 'ON CPU'" sysdate-1/24/12 sysdate;

Sessions with highest CPU usage

@ashtop session_id,sql_id "event2='ON CPU'" sysdate-1/24 sysdate;

Sessions with the highest I/O waits

@ashtop session_id,sql_id,event2 "wait_class = 'User I/O'" sysdate-1/24 sysdate;

What was the application doing

Assuminge all application users begin with RAPHI_

@ashtop sql_id,sql_opname,event2 "username like 'RAPHI%'" sysdate-1/24 sysdate;

What was a specific session doing

@ashtop sql_id,sql_opname,event2 "session_id=1667 and session_serial#=45570" sysdate-1/24 sysdate;

Which waits on which objects

@ashtop objt,event2,sql_id "event2 NOT LIKE 'ON CPU'" sysdate-1/24 sysdate;

Which SQL statement read the longest from what

@ashtop objt,sql_id "event like '%read%'" sysdate-1/24 sysdate;

What caused the most log file syncs

@ashtop sql_opname,top_level_call_name "event='log file sync'" sysdate-1/24 sysdate;

Are top sessions blocked by another session

@ashtop session_id,sql_id,event2,blocking_session session_type='FOREGROUND' sysdate-1/24 sysdate;

Top blocked sessions

@ashtop session_id,sql_id,event2,blocking_session "event2 NOT LIKE 'ON CPU' and blocking_session is not null" sysdate-1/24 sysdate;

On which objects blocked sessions each other

@ashtop username,sql_id,session_id,event,blocking_session,objt "event like 'enq: TX%'" sysdate-1/24 sysdate;

Highest PGA usage

Change the ORDER BY in a copy of ashtop.sql

vi ashtop.sql
ORDER BY
-- TotalSeconds DESC
 PGA_ALLOCATED DESC

Then:

@ashtop pga_allocated,sql_id "session_type='FOREGROUND'" sysdate-1/24 sysdate;

SQL plan statistics

With ASH it’s possible to find out in which step of the execution plan the query spent the most time. It can be done with ashtop or with another script from Tanel, asqlmon.

With ashtop

Unless specified with a hint, SQL Monitor only records statements that ran at least for 5 seconds. If you want to know which part of the execution plan consumed the most time for a query not captured by SQL Monitor, you have to use ASH. The following columns are available:

ColumnDescription
SQL_FULL_PLAN_HASH_VALUENumerical representation of the complete SQL plan for the cursor
SQL_PLAN_HASH_VALUENumeric representation of the SQL plan for the cursor
SQL_PLAN_LINE_IDSQL plan line ID
SQL_PLAN_OPERATIONPlan operation name
SQL_PLAN_OPTIONSPlan operation options

With the SQL_ID of the statement, you can see where most of the time was spent:

@ashtop sql_plan_line_id,sql_plan_operation,sql_plan_options "sql_id='28w88hbtt3766'" sysdate-1/24 sysdate;

Example output:

SecondsAAS%ThisSQL_PLAN_LINE_IDSQL_PLAN_OPERATIONSQL_PLAN_OPTIONSFIRST_SEENLAST_SEENExecs Seen
387.158%8TABLE ACCESSBY LOCAL INDEX ROWID BATCHED2021-09-13 10:38:462021-09-13 11:11:32159
136.020%9INDEXRANGE SCAN2021-09-13 10:38:462021-09-13 11:11:31114
108.016%15TABLE ACCESSBY LOCAL INDEX ROWID BATCHED2021-09-13 10:38:502021-09-13 11:11:33108
27.04%16INDEXSKIP SCAN2021-09-13 10:38:502021-09-13 11:11:3227
4.01%3SORTUNIQUE2021-09-13 10:38:492021-09-13 11:11:274
3.00%11TABLE ACCESSBY GLOBAL INDEX ROWID BATCHED2021-09-13 10:38:472021-09-13 10:38:533
2.00%12INDEXRANGE SCAN2021-09-13 10:38:482021-09-13 11:11:242
1.00%5FILTER2021-09-13 10:38:462021-09-13 10:38:461

This means that the query spent 58% of its time reading rows from a table, 20% searching the rows in the index, etc.

With asqlmon

Also in the “ash” directory of Tanel’s github repo is the script asqlmon.sql:
https://github.com/tanelpoder/tpt-oracle

The script simplifies querying plan statistics and provides even more information.

  • First argument: sql_id
  • Second argument: Plan Hash Value (or % for all plans)
  • Third argument: Start time
  • Fourth argument: End time

Call for the same query as in the ashtop example:

@asqlmon 28w88hbtt3766 % sysdate-1/24 sysdate;

Output: asqlmon

Conclusion

Ashtop is a true time saver when it comes to working with ASH. It is much more flexible than “Top Activity/ASH Analytics” in Oracle’s Enterprise Manager and provides some very handy shortcuts which have to be programmed when using self-made scripts, like the time_model_name column. Other useful scripts from Tanel that go hand-in-hand with ashtop:

  • awr_sqlstats_per_exec -> discover plan changes of a query
  • (d)ash_wait_chains -> follow blocking sessions
  • (d)aslqmon -> execution plan statistics, as described above
  • nonshared -> show reasons why cursors were not shared
  • help.sql -> description for many of Tanel’s scripts