You are on page 1of 41

ANALYSING UNIX SYSTEM PERFORMANCE

WITH PRSTAT AND SQL

96232200.doc

Author:

Brendan Furey

Creation Date:

14 April 2012

Version:

1.2

Last Updated:

9 May 2012

Page 1 of 41

Table of Contents

Introduction.......................................................................................................4
Hardware/Software Summary.......................................................................4
Process Overview...............................................................................................5
Process Flow Diagram..................................................................................5
Entity-Relationship Diagram.........................................................................6
Unix/Perl Processes............................................................................................7
prstat...........................................................................................................7
File Format............................................................................................................7

Format Files (Perl)........................................................................................7


Process..................................................................................................................7
Output File Formats..............................................................................................8

Database Processes...........................................................................................9
Data Definition (External Tables)..................................................................9
Statistics Headers External Table - prstat_header_ext.........................................9
Statistics Lines Table External - prstat_lines........................................................9

Data Definition (Base Tables).......................................................................9


Statistics
Statistics
Statistics
Statistics
Statistics
Statistics

Runs Table - prstat_runs.......................................................................9


Headers Table - prstat_headers..........................................................10
Lines Table - prstat_lines.....................................................................10
Headers Index.....................................................................................10
Headers View - prstat_headers_v........................................................10
Headers Sequence - prstat_header_seq.............................................10

Populate Statistics Tables...........................................................................11


Parameters..........................................................................................................11
Process................................................................................................................11

Queries......................................................................................................11
Headers View......................................................................................................11
Long-Run Averages.............................................................................................12
Load Averages.....................................................................................................13
Process Summary................................................................................................14
Process Detail......................................................................................................16
Process Pivot.......................................................................................................18

Execution Plans..........................................................................................21
Excel Analysis..................................................................................................25
Long-Run Averages....................................................................................25
Sample Output....................................................................................................25
Columns..............................................................................................................25
Graphs.................................................................................................................27

Load Averages...........................................................................................29
Sample Output....................................................................................................29
Columns..............................................................................................................29
Graphs.................................................................................................................30

Process Summary.......................................................................................31
Sample Output....................................................................................................31
Columns..............................................................................................................31

Process Detail............................................................................................32
Sample Output....................................................................................................32
Columns..............................................................................................................32
Graphs for One Process in Detail........................................................................33

Process Pivot..............................................................................................34
Sample Output....................................................................................................34
Columns..............................................................................................................34
Graphs.................................................................................................................35

References.......................................................................................................36
96232200.doc

Page 2 of 41

APPENDIX: Code Listings..................................................................................37


prstat.pl: Perl Program...............................................................................37
C_Unix.sql: Database Creation Script..........................................................38
Pop_Unix.sql: Database Package Script......................................................40

Change Record
Date

Author

Version

Change Reference

14-Apr-2012
08-May-2012
09-May-2012

BPF
BPF
BPF

1.0
1.1
1.2

Initial
Added two new queries, and extended the others
Minor changes

96232200.doc

Page 3 of 41

Introduction
prstat is a Unix utility that prints timing and other statistics about running processes. It is useful for
checking system loading and performance, as well as specific processes, and provides averages over
given time intervals. This article describes how the raw data can be obtained, and loaded into an Oracle
database for analysing performance over extended periods via SQL, with production of reports and
Microsoft Excel graphs. The approach divides into three stages:

Gathering the statistics into a Unix file, and:


o

Formatting the file into separate headers and lines by a Perl script

Loading of the files into Oracle tables by means of external table definitions

Executing three main SQL queries on the tables to produce reports, and adding graphs where
relevant in Excel

The article starts with an overview of the process, with diagrams, and then provides details on the three
main areas involved. All relevant code, including the SQL statements, is included, with diagrams to
illustrate the SQL structures.

Hardware/Software Summary
Component
Database
Unix
Perl
Diagrammer
Grapher
Operating System
Computer

96232200.doc

Description
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Beta
Solaris 9
ActivePerl 5.12.4.1205, by ActiveState Software Inc.
Microsoft Visio 2003 (11.3216.5606)
Microsoft Excel 2007
Microsoft Windows 7 Home Premium (32 bit)
Samsung X120, 3GB memory, Intel U4100 @ 1.3GHz x 2

Page 4 of 41

Process Overview
Process Flow Diagram
The diagram below illustrates the stages involved in generating the excel reports on Unix process
statistics.

96232200.doc

Page 5 of 41

Entity-Relationship Diagram
.
Statistics Run

Statistics
Header

Statistics Line

96232200.doc

Page 6 of 41

Unix/Perl Processes
prstat
The prstat utility writes statistical information every minute about running processes for the top 15
processes by CPU time over the previous minute, along with summary information including the system
load averages over the previous 1, 5 and 15 minutes. We have executed it as a background process with
default settings, and output directed to a file, as follows:
prstat > prstat.txt &
File Format
Every minute the output file is updated by appending a text block in the following format.
PID
12785
8149
21338
175
3188
504
21992
21994
21982
21991
21986
6844
9660
3843
933
Total:

USERNAME SIZE
oracle
132M
weblogic 303M
oracle
102M
applprod
61M
oracle
578M
applprod 400M
brendan 4592K
brendan 1184K
oracle
396M
brendan 4560K
oracle
577M
rboxuser
90M
applprod
66M
root
602M
named
36M
494 processes,

RSS STATE PRI NICE


TIME
113M cpu2
0
0
0:43:13
180M sleep
29
10 25:42:26
84M sleep
59
0
0:00:13
37M sleep
59
0
0:01:31
449M sleep
59
0
0:01:30
383M sleep
59
0
0:01:17
4448K cpu18
29
0
0:00:00
1112K cpu0
19
0
0:00:00
377M sleep
59
0
0:00:00
4424K cpu16
39
0
0:00:00
440M sleep
59
0
0:00:00
69M sleep
59
0
0:00:06
43M sleep
59
0
0:01:28
13M sleep
59
0 26:45:46
18M sleep
59
0
4:29:29
4616 lwps, load averages: 1.27,

CPU PROCESS/NLWP
25% oracle/1
0.6% java/45
0.6% oracle/1
0.5% f60webmx/1
0.4% oracle/1
0.2% oracle/11
0.1% prstat/1
0.1% ps/1
0.1% oracle/1
0.1% prstat/1
0.1% oracle/1
0.1% oracle/1
0.1% f60webmx/1
0.0% snmpd/1
0.0% named/7
1.32, 1.45

Notice that there is no record of the time that the block was written, so this has to be worked out from the
timestamp of the file, and the position of the block in the file.

Format Files (Perl)


We want to load the data into an Oracle database, and the first step is to split the output file into separate
files for header and lines in CSV format, using a Perl script.
Process
Open the input file for reading, and the two output files for writing
Loop over the lines in the input file
If the line is the column headers line, increment the header number and initialise the line number
Else
Split the line into tokens based on space delimiters
If the line is the summary line print the header values to file in CSV format, including header
number
Else
Increment the line number
Remove the % and replace the / by a comma
Print the line values to file in CSV format, including both header and line numbers
End if
End if
End loop
Close files

96232200.doc

Page 7 of 41

Output File Formats


The headers file contains records like the following, which corresponds to the header in the input file block
above, with the first field being the header number, used to link to the lines:
1,494,4616,1.27,1.32,1.45

The lines file contains blocks of records like the following, which corresponds to the lines in the input file
block above, with the first field being the header number, used to link to the header, and the second being
the sequence of the line within the header:
1,1,12785,oracle,132M,113M,cpu2,0,0,0:43:13,25,oracle,1
1,2,8149,weblogic,303M,180M,sleep,29,10,25:42:26,0.6,java,45
1,3,21338,oracle,102M,84M,sleep,59,0,0:00:13,0.6,oracle,1
1,4,175,applprod,61M,37M,sleep,59,0,0:01:31,0.5,f60webmx,1
1,5,3188,oracle,578M,449M,sleep,59,0,0:01:30,0.4,oracle,1
1,6,504,applprod,400M,383M,sleep,59,0,0:01:17,0.2,oracle,11
1,7,21992,brendan,4592K,4448K,cpu18,29,0,0:00:00,0.1,prstat,1
1,8,21994,brendan,1184K,1112K,cpu0,19,0,0:00:00,0.1,ps,1
1,9,21982,oracle,396M,377M,sleep,59,0,0:00:00,0.1,oracle,1
1,10,21991,brendan,4560K,4424K,cpu16,39,0,0:00:00,0.1,prstat,1
1,11,21986,oracle,577M,440M,sleep,59,0,0:00:00,0.1,oracle,1
1,12,6844,rboxuser,90M,69M,sleep,59,0,0:00:06,0.1,oracle,1
1,13,9660,applprod,66M,43M,sleep,59,0,0:01:28,0.1,f60webmx,1
1,14,3843,root,602M,13M,sleep,59,0,26:45:46,0.0,snmpd,1
1,15,933,named,36M,18M,sleep,59,0,4:29:29,0.0,named,7

96232200.doc

Page 8 of 41

Database Processes
Data Definition (External Tables)
External tables are used to load the data into the base tables from the files formatted by the Perl process,
and the columns map directly to the fields in the files.
Statistics Headers External Table - prstat_header_ext

Column
header_no
n_procs
n_lwps
load_avg_1
load_avg_5
load_avg_15

Type
Number
Number
Number
Number
Number
Number

Notes
Sequence of header in file
Number of Unix processes
Number of Unix light weight processes
Unix load average over last minute
Unix load average over last 5 minutes
Unix load average over last 15 minutes

Statistics Lines Table External - prstat_lines

Column
header_no
line_no
pid
username
size_M
rss
state
pri
nice
time_h_m_s
cpu_percent
process
nlwp

Type
Number
Number
Number
Char(30)
Char(10)
Char(10)
Char(10)
Char(100)
Number
Char(10)
Number
Char(30)
Number

Notes
Sequence of header in file
Sequence of line in header
Process identifier
User name of process owner
Size in MB
Unix rss
Process state
Process priority
Process nice value
CPU time used by process in hh :mm :ss
format
Percentage of total CPU time used
Process name
Number of associated light weight
processes

Data Definition (Base Tables)


There are two tables corresponding to the headers and lines files, with a foreign key link between them
based on a surrogate primary key in the headers table. An additional table is used to store start and end
times and other information about a particular run of prstat, and headers are linked to it.
Statistics Runs Table - prstat_runs

Column
id
start_date
end_date
machine
notes

96232200.doc

Type
Number
Date
Date
Char(30)
Char(4000
)

Notes
Primary key
Start date (including time) of the run
End date (including time) of the run
Unix box identifier
Notes about the run

Page 9 of 41

Statistics Headers Table - prstat_headers

Column
id
run_id
header_no
n_procs
n_lwps
load_avg_1
load_avg_5
load_avg_15

Type
Number
Number
Number
Number
Number
Number
Number
Number

Notes
Primary key (surrogate)
Foreign key link to Statistics Runs
Sequence of header in file
Number of Unix processes
Number of Unix light weight processes
Unix load average over last minute
Unix load average over last 5 minutes
Unix load average over last 15 minutes

Statistics Lines Table - prstat_lines

Column
hdr_id
line_no
pid
username
size_M
rss
state
pri
nice
time_h_m_s
cpu_percent
process
nlwp

Type
Number
Number
Number
Char(30)
Char(10)
Char(10)
Char(10)
Char(100)
Number
Char(10)
Number
Char(30)
Number

Notes
Foreign key link to Statistics Headers
Sequence of line in header
Process identifier
User name of process owner
Size in MB
Unix rss
Process state
Process priority
Process nice value
CPU time used by process in hh :mm :ss
format
Percentage of total CPU time used
Process name
Number of associated light weight
processes

Statistics Headers Index

Index

Column

prstat_headers_U1

run_id
header_no

Statistics Headers View - prstat_headers_v


This view joins prstat_runs (run) to prstat_headers (hdr) in order calculate the datetime for each header by
counting the minutes back from the datetime of the last record.

Column

Alias
dt

run_id
id

Calculation
run.end_date - (Max (hdr.header_no) OVER () hdr.header_no) /24 / 60

hdr_i
d

header_no
n_procs
n_lwps
load_avg_1
load_avg_5
load_avg_15
Statistics Headers Sequence - prstat_header_seq
This sequence is used to populate the surrogate primary key for prstat_headers.

96232200.doc

Page 10 of 41

Populate Statistics Tables


This process consists of a call to a PL/SQL packaged procedure to populate the base tables from the files
by means of Oracle external tables.
Parameters

Column
p_run_id
p_machine
p_start
p_end
p_notes

Type
Intege
r
Char
Char
Char
Char

Notes

Primary key
Start date (including time) of the run
End date (including time) of the run
Unix box identifier
Notes about the run

Process
Delete lines for any headers for the input run_id
Delete headers for the input run_id
Delete run record for the input run_id
Insert run record using the input values
Insert headers by selecting from the external headers table, setting primary key from the sequence, and
using the input run id
Insert lines by selecting from the external lines table, joining the headers table on the run id and
header_no to get the foreign key hdr_id

Queries
Headers View
Description
This view joins the runs table to the headers table in order to calculate the datetime for each header by
counting the minutes back from the datetime of the last record.
Query Structure Diagram

96232200.doc

Page 11 of 41

Query Text
SELECT /* Brendans Header Viewt query, as described in 'Analysing Unix System Performance with prstat
and SQL', scribd.com/BrendanP */
run.end_date - (Max (hdr.header_no) OVER (PARTITION BY hdr.run_id) - hdr.header_no) /24 / 60 dt,
hdr.run_id,
hdr.id hdr_id,
header_no,
n_procs,
n_lwps,
load_avg_1,
load_avg_5,
load_avg_15
FROM prstat_headers hdr
JOIN prstat_runs run
ON run.id = hdr.run_id

Long-Run Averages
Description
This query gives performance averages over the entire date range available, by the hour, and with moving
averages over various time intervals, and cumulative sums. Note the following points of interest:

Aggregate functions are used to group by hour, and then analytic functions are applied to obtain
cumulative values by hour, and various moving averages

The window clauses in the analytic functions use interval constants, and note that days and
weeks are obtained using 23 hours and 167 hours respectively, rather than the more obvious day
and week constants that would include an extra hour

The calendar month window does include an extra hour, because subtracting an hour interval
from the month interval gave rise to an Oracle error - ORA-30081: invalid data type for
datetime/interval arithmetic. The clause tried was:

RANGE BETWEEN INTERVAL '1' MONTH - INTERVAL '1' HOUR PRECEDING AND CURRENT
ROW

The interval difference works in a select list, thus:

SELECT SYSDATE - INTERVAL '1' MONTH + INTERVAL '1' HOUR FROM DUAL

It might be interesting to separate out working from non-working hours, using a CASE
expression

Query Structure Diagram

Query Text
SELECT /* Brendans Long-Run Averages query, as described in 'Analysing Unix System Performance with
96232200.doc

Page 12 of 41

prstat and SQL', scribd.com/BrendanP */


Trunc (hdr.dt, 'HH24') "Datetime",
Round (Avg (hdr.n_procs)) "# processes",
Round (Avg (hdr.n_lwps)) "# LW processes",
Round (Avg (hdr.load_avg_1), 2) "Load average",
Round (Avg (Avg (hdr.load_avg_1)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE BETWEEN INTERVAL '23'
HOUR PRECEDING AND CURRENT ROW), 2) "Load average (day)",
Round (Avg (Avg (hdr.load_avg_1)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE BETWEEN INTERVAL '167'
HOUR PRECEDING AND CURRENT ROW), 2) "Load average (week)",
Round (Avg (Avg (hdr.load_avg_1)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE BETWEEN INTERVAL '1'
MONTH PRECEDING AND CURRENT ROW), 2) "Load average (month)",
Round (2.4 * Sum (lin.cpu_percent) / 3600, 1) "Hours (all processes)",
Round (240 * Sum (lin.cpu_percent) / (4 * 3600)) "% Utilization",
Round (Avg (240 * Sum (lin.cpu_percent) / (4 * 3600)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE
BETWEEN INTERVAL '23' HOUR PRECEDING AND CURRENT ROW), 1) "% Utilization (day)",
Round (Avg (240 * Sum (lin.cpu_percent) / (4 * 3600)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE
BETWEEN INTERVAL '167' HOUR PRECEDING AND CURRENT ROW), 1) "% Utilization (week)",
Round (Avg (240 * Sum (lin.cpu_percent) / (4 * 3600)) OVER (ORDER BY Trunc (hdr.dt, 'HH24') RANGE
BETWEEN INTERVAL '1' MONTH PRECEDING AND CURRENT ROW), 1) "% Utilization (month)",
Round (Sum (2.4 * Sum (lin.cpu_percent)) OVER (ORDER BY Trunc (hdr.dt, 'HH24')) / 3600, 1) "Hours
(all procs, cumulative)",
Round (Sum (240 * Sum (lin.cpu_percent)) OVER (ORDER BY Trunc (hdr.dt, 'HH24')) / Sum (240*60) OVER
(ORDER BY Trunc (hdr.dt, 'HH24'))) "% Utilization (cumulative)",
Round (Sum (240*60) OVER (ORDER BY Trunc (hdr.dt, 'HH24')) / 3600, 1) "Hours (available, cumulative)"
FROM prstat_headers_v hdr
JOIN prstat_lines lin
ON lin.hdr_id = hdr.hdr_id
WHERE hdr.run_id
= 1
GROUP BY Trunc (hdr.dt, 'HH24')
ORDER BY Trunc (hdr.dt, 'HH24')

Load Averages
Description
This query lists the load averages from prstat with the date and time for all headers between given times.
It also joins the lines to get percentages of available CPU time actually used, with 15-minute rolling
averages thereof, and cumulative used and available seconds.
Query Structure Diagram

Query Text
SELECT /* Brendans Load Averages query, as described in 'Analysing Unix System Performance with prstat
and SQL', scribd.com/BrendanP */
hdr.dt "Datetime",
hdr.n_procs "# processes",
hdr.n_lwps "# LW processes",
hdr.load_avg_1 "Load average",
hdr.load_avg_5 "Load average (5m)",
hdr.load_avg_15 "Load average (15m)",
Round (Sum (lin.cpu_percent)) "% Utilization (all processes)",
Round (Avg (Sum (lin.cpu_percent)) OVER (ORDER BY hdr.dt RANGE BETWEEN INTERVAL '14' MINUTE PRECEDING
AND CURRENT ROW)) "% Utilization (all processes, 15m)",
Round (Sum (2.4 * Sum (lin.cpu_percent)) OVER (ORDER BY hdr.dt)) "Seconds (all processes,
cumulative)",
96232200.doc

Page 13 of 41

Round (Sum (240) OVER (ORDER BY hdr.dt)) "Seconds (available, cumulative)"


FROM prstat_headers_v hdr
JOIN prstat_lines lin
ON lin.hdr_id
= hdr.hdr_id
WHERE hdr.dt
> To_Date ('02-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.dt
<= To_Date ('03-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.run_id
= 1
GROUP BY hdr.dt,
hdr.n_procs,
hdr.n_lwps,
hdr.load_avg_1,
hdr.load_avg_5,
hdr.load_avg_15
ORDER BY hdr.dt

Process Summary
Description
This.query lists all processes appearing in the prstat output between given times, with total CPU time and
other data, in descending order of total CPU time in the range. The SQL KEEP grouping option is used to
obtain the first and last prstat overall process CPU times appearing within the listing. The total CPU times
in the input time range are calculated from the prstat percent values. Note the following points of interest:

96232200.doc

The Headers in Range subquery appears in two further subqueries using it as a subquery factor

The middle two subqueries select from the same subquery and table, but group differently,
namely by process and by time, respectively, which is why they are separate

The usage subquery first aggregates grouping by the time-dependent fields, and then applies
analytic sums to obtain cumulative values by time

The main query takes the middle two subqueries as inputs and aggregates over the time intervals
during which each process is active, grouping by the process fields,

Page 14 of 41

Query Structure Diagram

Query Text
WITH ids AS (
SELECT
hdr.hdr_id, hdr.dt,
hdr.load_avg_1
FROM prstat_headers_v hdr
WHERE hdr.dt
> To_Date ('02-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.dt
<= To_Date ('03-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.run_id
= 1
), sec AS (
SELECT
hdr.hdr_id,
hdr.dt,
hdr.load_avg_1,
Sum (2.4 * Sum (lin.cpu_percent)) OVER (ORDER BY hdr.dt) all_pid_sec_cum,
Sum (240) OVER (ORDER BY hdr.dt) all_sec_cum
FROM ids hdr
JOIN prstat_lines lin
ON lin.hdr_id = hdr.hdr_id
GROUP BY hdr.hdr_id, hdr.dt, hdr.load_avg_1
), pid AS (
96232200.doc

Page 15 of 41

SELECT
Sum (0.04 * lin.cpu_percent)*60 pid_secs,
lin.pid pid,
lin.username username,
lin.process process,
Min (lin.time_h_m_s) KEEP (DENSE_RANK FIRST ORDER BY ids.dt) first_time_sum,
Max (lin.time_h_m_s) KEEP (DENSE_RANK LAST ORDER BY ids.dt) last_time_sum,
Min (ids.dt) first_dt,
Max (ids.dt) last_dt,
Min (lin.size_M) size_M_min,
Max (lin.size_M) size_M_max,
Min (lin.rss) rss_min,
Max (lin.rss) rss_max
FROM ids
JOIN prstat_lines lin
ON lin.hdr_id = ids.hdr_id
GROUP BY lin.pid, lin.username, lin.process
)
SELECT /* Brendans Process Summary query, as described in 'Analysing Unix System Performance with prstat
and SQL', scribd.com/BrendanP */
Round (pid.pid_secs / 3600, 1) "Total hours per process",
pid.pid "Process Id",
pid.username "User",
pid.process "Process",
pid.first_time_sum "Initial CPU usage",
pid.last_time_sum "Final CPU usage",
pid.first_dt "First time in report",
pid.last_dt "Last time in report",
pid.size_M_min "Minimum Size",
pid.size_M_max "Maximum Size",
pid.rss_min "Minimum RSS",
pid.rss_max "Maximum RSS",
Round (400 * pid.pid_secs / (Max (sec.all_sec_cum) KEEP (DENSE_RANK LAST ORDER BY sec.dt) - Min
(sec.all_sec_cum) KEEP (DENSE_RANK FIRST ORDER BY sec.dt))) "% Utilisation (process)",
Round (100 * (Max (sec.all_pid_sec_cum) KEEP (DENSE_RANK LAST ORDER BY sec.dt) - Min
(sec.all_pid_sec_cum) KEEP (DENSE_RANK FIRST ORDER BY sec.dt)) / (Max (sec.all_sec_cum) KEEP (DENSE_RANK
LAST ORDER BY sec.dt) - Min (sec.all_sec_cum) KEEP (DENSE_RANK FIRST ORDER BY sec.dt))) "% Utilisation
(all processes)",
Round (Max (sec.all_pid_sec_cum) KEEP (DENSE_RANK LAST ORDER BY sec.dt) - Min (sec.all_pid_sec_cum)
KEEP (DENSE_RANK FIRST ORDER BY sec.dt)) "Seconds (all processes)",
To_Char (Max (sec.all_sec_cum) KEEP (DENSE_RANK LAST ORDER BY sec.dt) - Min (sec.all_sec_cum) KEEP
(DENSE_RANK FIRST ORDER BY sec.dt)) "Seconds (available)",
Round (Avg (sec.load_avg_1), 1) "Average load average",
Round (Min (sec.load_avg_1), 1) "Minimum load average",
Round (Max (sec.load_avg_1), 1) "Maximum load average"
FROM pid
JOIN sec
ON sec.dt BETWEEN pid.first_dt AND pid.last_dt
WHERE pid.first_dt < pid.last_dt
GROUP BY pid.pid_secs,
Round (pid.pid_secs / 3600, 1),
pid.pid,
pid.username,
pid.process,
pid.first_time_sum,
pid.last_time_sum,
pid.first_dt,
pid.last_dt,
pid.size_M_min,
pid.size_M_max,
pid.rss_min,
pid.rss_max
ORDER BY pid.pid_secs DESC, pid.pid

Process Detail
Description
This.query lists line details for processes exceeding an input CPU total (10 minutes) within the input range.
Note the following points of interest:

96232200.doc

Only the top 15 processes are in the prstat output file at each time point, but it is better to have a
record for each time point for processes that are included in the output. The SQL technique of
partitioned outer joining is used for this purpose

The partitioned outer join increases the size of the output file and the execution time, so it is
convenient to include only processes in the output that consumed significant CPU times

The subquery factor, High CPU pids, is used to improve performance of the query by allowing
early exclusion of the low CPU time processes
Page 16 of 41

Query Structure Diagram

Query Text
WITH ids AS (
SELECT hdr.hdr_id, hdr.dt, Round ((hdr.dt - Min (hdr.dt) OVER ()) * 86400 + 60) secs
FROM prstat_headers_v hdr
WHERE hdr.dt
> To_Date ('02-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.dt
<= To_Date ('03-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.run_id
= 1
), pids AS (
SELECT lin_g.pid,
Sum (2.4 * lin_g.cpu_percent) pid_sec_tot
FROM ids
JOIN prstat_lines
lin_g
ON lin_g.hdr_id
= ids.hdr_id
GROUP BY lin_g.pid
HAVING Sum (2.4 * lin_g.cpu_percent) > 600
)
SELECT /* Brendans Process Detail query, as described in 'Analysing Unix System Performance with prstat
and SQL', scribd.com/BrendanP */
lin.pid "Process Id",
ids.dt "Datetime",
lin.username "User",
lin.process "Process",
lin.size_M "Memory",
lin.rss "RSS",
lin.state "State",
lin.pri "Priority",
lin.nice "Nice",
lin.nlwp "# LW processes",
lin.time_h_m_s "prstat time",
Nvl (4*lin.cpu_percent, 0) "% Utilisation (process)",
Round (Sum (lin.cpu_percent) OVER (PARTITION BY ids.hdr_id)) "% Utilisation (all processes)",
Nvl (Round (Sum (240 * lin.cpu_percent) OVER (PARTITION BY lin.pid ORDER BY ids.hdr_id) /
(ids.secs)), 0) "% Utilisation (process, cum.)",
Round (Sum (60 * lin.cpu_percent) OVER (ORDER BY ids.hdr_id) / (ids.secs)) "% Utilisation (all,
cum.)",
Nvl (Round (Sum (2.4 * lin.cpu_percent) OVER (PARTITION BY lin.pid ORDER BY ids.hdr_id)), 0) "Seconds
(process, cumulative)",
Round (Sum (2.4 * lin.cpu_percent) OVER (PARTITION BY lin.pid)) "Seconds (process total)",
Round (Sum (2.4 * lin.cpu_percent) OVER (ORDER BY ids.hdr_id)) "Seconds (all procs, cumul.)",
ids.secs * 4 "Seconds (available total)",
Round (Sum (2.4 * lin.cpu_percent) OVER ()) "Seconds (all procs, total)"
FROM ids
LEFT JOIN prstat_lines
lin
PARTITION BY (lin.pid)
ON lin.hdr_id
= ids.hdr_id
JOIN pids
ON pids.pid
= lin.pid
96232200.doc

Page 17 of 41

ORDER BY pids.pid_sec_tot DESC, pids.pid, ids.dt

Process Pivot
Description
This query lists % utilisations for the ten processes using most CPU time in the input interval. The values
are listed for all ten in a single record at each minute, in decreasing order of CPU usage across the record.
This allows for easy comparisons. Note the following points of interest:

96232200.doc

The query is based on the Process Detail query, but with only one fact attribute, an extra filtering
to include only the top ten processes, and an extra pivoting step

The pivoting is performed using the PIVOT keyword that is new in Oracle Database version 11.1;
previously you could achieve the same result by a combination of Max and CASE

Pivoting is performed on the row number of the filtered processes, which is obtained by applying
the analytic function Row_Number on top of an aggregated sum

The % utilisation is averaged over 15 minutes using the analytic Avg function, with a range clause
based on an interval constant

Page 18 of 41

Query Structure Diagram

Query Text
WITH ids AS (
SELECT hdr.hdr_id, hdr.dt, Round ((hdr.dt - Min (hdr.dt) OVER ()) * 86400 + 60) secs
FROM prstat_headers_v hdr
WHERE hdr.dt
> To_Date ('02-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.dt
<= To_Date ('03-APR-2012 1200', 'DD-MON-YYYY HH24MI')
AND hdr.run_id
= 1
), pids AS (
SELECT lin_g.pid,
Sum (2.4 * lin_g.cpu_percent) pid_sec_tot,
Row_Number() OVER (ORDER BY Sum (2.4 * lin_g.cpu_percent) DESC) rn
FROM ids
JOIN prstat_lines lin_g
ON lin_g.hdr_id = ids.hdr_id
GROUP BY lin_g.pid
HAVING Sum (2.4 * lin_g.cpu_percent) > 600
), unp AS (
SELECT ids.dt,
pids.rn,
Round (Nvl (Avg (4*lin.cpu_percent) OVER (PARTITION BY lin.pid ORDER BY ids.dt RANGE BETWEEN
INTERVAL '14' MINUTE PRECEDING AND CURRENT ROW), 0)) pid_percent_15
96232200.doc

Page 19 of 41

FROM ids
LEFT JOIN prstat_lines lin
PARTITION BY (lin.pid)
ON lin.hdr_id = ids.hdr_id
JOIN pids
ON pids.pid = lin.pid
WHERE pids.rn <= 10
)
SELECT /* Brendans Process Pivot query, as described in 'Analysing Unix System Performance with prstat
and SQL', scribd.com/BrendanP */
dt dt,
p1 p1,
p2 p2,
p3 p3,
p4 p4,
p5 p5,
p6 p6,
p7 p7,
p8 p8,
p9 p9,
p10 p10
FROM unp
PIVOT (Max (pid_percent_15) FOR rn IN (1 AS p1, 2 AS p2, 3 AS p3, 4 AS p4, 5 AS p5, 6 AS p6, 7 AS p7, 8
AS p8, 9 AS p9, 10 AS p10))
ORDER BY dt

96232200.doc

Page 20 of 41

Execution Plans
The queries were run through a performance-testing system to obtain execution plans and timings, as well as the output CSV files. The system was first described
in SQL Pivot and Prune Queries Keeping an Eye on Performance in May 2011, but has been significantly developed since then.
Long-Run Averages
----------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Starts | E-Rows | A-Rows |
A-Time
| Buffers | Reads | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
1 |
|
690 |00:00:02.86 |
6053 |
5137 |
|
|
|
|
1 | WINDOW SORT
|
|
1 |
621K|
690 |00:00:02.86 |
6053 |
5137 | 64512 | 64512 |57344 (0)|
|
2 |
HASH GROUP BY
|
|
1 |
621K|
690 |00:00:02.84 |
6053 |
5137 |
708K|
708K| 2713K (0)|
|* 3 |
HASH JOIN
|
|
1 |
621K|
619K|00:00:05.11 |
6053 |
5137 | 2391K| 1063K| 3214K (0)|
|
4 |
VIEW
| PRSTAT_HEADERS_V |
1 | 41405 | 41324 |00:00:00.52 |
301 |
0 |
|
|
|
|
5 |
WINDOW BUFFER
|
|
1 | 41405 | 41324 |00:00:00.23 |
301 |
0 | 3667K|
828K| 3259K (0)|
|
6 |
NESTED LOOPS
|
|
1 | 41405 | 41324 |00:00:00.31 |
301 |
0 |
|
|
|
|
7 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_RUNS
|
1 |
1 |
1 |00:00:00.01 |
2 |
0 |
|
|
|
|* 8 |
INDEX UNIQUE SCAN
| PRSTAT_RUN_PK
|
1 |
1 |
1 |00:00:00.01 |
1 |
0 |
|
|
|
|
9 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_HEADERS
|
1 | 41405 | 41324 |00:00:00.20 |
299 |
0 |
|
|
|
|* 10 |
INDEX RANGE SCAN
| PRSTAT_HEADERS_U1 |
1 | 41324 | 41324 |00:00:00.07 |
93 |
0 |
|
|
|
| 11 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:00.99 |
5752 |
5137 |
|
|
|
-----------------------------------------------------------------------------------------------------------------------------------------------------

The query that produced this plan wrote 691 rows and 1.7s (2.9s) of CPU (elapsed) time.
Load Averages
----------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Starts | E-Rows | A-Rows |
A-Time
| Buffers | Reads | OMem | 1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
1 |
|
1440 |00:00:01.69 |
6053 |
5113 |
|
|
|
|
1 | WINDOW SORT
|
|
1 |
621K|
1440 |00:00:01.69 |
6053 |
5113 |
115K|
115K| 102K (0)|
|
2 |
HASH GROUP BY
|
|
1 |
621K|
1440 |00:00:01.67 |
6053 |
5113 |
743K|
743K| 9172K (0)|
|* 3 |
HASH JOIN
|
|
1 |
621K| 21600 |00:00:00.51 |
6053 |
5113 |
771K|
771K| 1503K (0)|
|* 4 |
VIEW
| PRSTAT_HEADERS_V |
1 | 41405 |
1440 |00:00:00.15 |
301 |
0 |
|
|
|
|
5 |
WINDOW BUFFER
|
|
1 | 41405 | 41324 |00:00:00.24 |
301 |
0 | 4092K|
862K| 3637K (0)|
|
6 |
NESTED LOOPS
|
|
1 | 41405 | 41324 |00:00:00.31 |
301 |
0 |
|
|
|
|
7 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_RUNS
|
1 |
1 |
1 |00:00:00.01 |
2 |
0 |
|
|
|
|* 8 |
INDEX UNIQUE SCAN
| PRSTAT_RUN_PK
|
1 |
1 |
1 |00:00:00.01 |
1 |
0 |
|
|
|
|
9 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_HEADERS
|
1 | 41405 | 41324 |00:00:00.19 |
299 |
0 |
|
|
|
|* 10 |
INDEX RANGE SCAN
| PRSTAT_HEADERS_U1 |
1 | 41324 | 41324 |00:00:00.06 |
93 |
0 |
|
|
|
| 11 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.03 |
5752 |
5113 |
|
|
|
----------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------3 - access("LIN"."HDR_ID"="HDR"."HDR_ID")
4 - filter((INTERNAL_FUNCTION("HDR"."DT")>TO_DATE(' 2012-04-02 12:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
INTERNAL_FUNCTION("HDR"."DT")<=TO_DATE(' 2012-04-03 12:00:00', 'syyyy-mm-dd hh24:mi:ss')))
8 - access("RUN"."ID"=1)
10 - access("HDR"."RUN_ID"=1)
96232200.doc

Page 21 of 41

The query that produced this plan wrote 1,440 rows and took 0.8s (1.7s) of CPU (elapsed) time.
Process Summary
---------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Starts | E-Rows | A-Rows |
A-Time
| Buffers | Reads | Writes | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
1 |
|
1249 |00:00:05.91 |
11826 | 11455 |
5 |
|
|
|
|
1 | TEMP TABLE TRANSFORMATION
|
|
1 |
|
1249 |00:00:05.91 |
11826 | 11455 |
5 |
|
|
|
|
2 |
LOAD AS SELECT
|
|
1 |
|
0 |00:00:00.30 |
309 |
0 |
5 |
264K|
264K| 264K (0)|
|* 3 |
VIEW
| PRSTAT_HEADERS_V
|
1 | 41405 |
1440 |00:00:00.13 |
301 |
0 |
0 |
|
|
|
|
4 |
WINDOW BUFFER
|
|
1 | 41405 | 41324 |00:00:00.22 |
301 |
0 |
0 | 3313K|
798K| 2944K (0)|
|
5 |
NESTED LOOPS
|
|
1 | 41405 | 41324 |00:00:00.31 |
301 |
0 |
0 |
|
|
|
|
6 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_RUNS
|
1 |
1 |
1 |00:00:00.01 |
2 |
0 |
0 |
|
|
|
|* 7 |
INDEX UNIQUE SCAN
| PRSTAT_RUN_PK
|
1 |
1 |
1 |00:00:00.01 |
1 |
0 |
0 |
|
|
|
|
8 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_HEADERS
|
1 | 41405 | 41324 |00:00:00.19 |
299 |
0 |
0 |
|
|
|
|* 9 |
INDEX RANGE SCAN
| PRSTAT_HEADERS_U1
|
1 | 41324 | 41324 |00:00:00.06 |
93 |
0 |
0 |
|
|
|
| 10 |
SORT GROUP BY
|
|
1 |
17M|
1249 |00:00:05.61 |
11514 | 11455 |
0 |
478K|
478K| 424K (0)|
| 11 |
MERGE JOIN
|
|
1 |
48M|
104K|00:00:05.26 |
11514 | 11455 |
0 |
|
|
|
| 12 |
SORT JOIN
|
|
1 |
621K|
1440 |00:00:00.98 |
5756 |
5745 |
0 | 90112 | 90112 |79872 (0)|
| 13 |
VIEW
|
|
1 |
621K|
1440 |00:00:00.98 |
5756 |
5745 |
0 |
|
|
|
| 14 |
WINDOW SORT
|
|
1 |
621K|
1440 |00:00:00.98 |
5756 |
5745 |
0 | 90112 | 90112 |79872 (0)|
| 15 |
HASH GROUP BY
|
|
1 |
621K|
1440 |00:00:00.97 |
5756 |
5745 |
0 |
761K|
761K| 8943K (0)|
|* 16 |
HASH JOIN
|
|
1 |
621K| 21600 |00:00:00.21 |
5756 |
5745 |
0 |
842K|
842K| 1224K (0)|
| 17 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 18 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6622_15|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 19 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.00 |
5747 |
5740 |
0 |
|
|
|
|* 20 |
FILTER
|
|
1440 |
|
104K|00:00:03.42 |
5758 |
5710 |
0 |
|
|
|
|* 21 |
SORT JOIN
|
|
1440 | 31066 |
1014K|00:00:04.98 |
5758 |
5710 |
0 |
178K|
178K| 158K (0)|
| 22 |
VIEW
|
|
1 | 31066 |
1249 |00:00:02.38 |
5758 |
5710 |
0 |
|
|
|
|* 23 |
FILTER
|
|
1 |
|
1249 |00:00:02.37 |
5758 |
5710 |
0 |
|
|
|
| 24 |
SORT GROUP BY
|
|
1 | 31066 |
3127 |00:00:02.38 |
5758 |
5710 |
0 |
619K|
619K| 550K (0)|
|* 25 |
HASH JOIN
|
|
1 |
621K| 21600 |00:00:00.22 |
5758 |
5710 |
0 |
878K|
878K| 1264K (0)|
| 26 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
| 27 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6622_15|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
| 28 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.21 |
5752 |
5710 |
0 |
|
|
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------3 - filter((INTERNAL_FUNCTION("HDR"."DT")>TO_DATE(' 2012-04-02 12:00:00', 'syyyy-mm-dd hh24:mi:ss') AND INTERNAL_FUNCTION("HDR"."DT")<=TO_DATE('
2012-04-03 12:00:00', 'syyyy-mm-dd hh24:mi:ss')))
7 - access("RUN"."ID"=1)
9 - access("HDR"."RUN_ID"=1)
16 - access("LIN"."HDR_ID"="HDR"."HDR_ID")
20 - filter("PID"."LAST_DT">=INTERNAL_FUNCTION("SEC"."DT"))
21 - access(INTERNAL_FUNCTION("PID"."FIRST_DT")<=INTERNAL_FUNCTION("SEC"."DT"))
filter(INTERNAL_FUNCTION("PID"."FIRST_DT")<=INTERNAL_FUNCTION("SEC"."DT"))
23 - filter(MIN(INTERNAL_FUNCTION("IDS"."DT"))<MAX(INTERNAL_FUNCTION("IDS"."DT")))
25 - access("LIN"."HDR_ID"="IDS"."HDR_ID")

The query that produced this plan wrote 1,249 rows and took 3.6s (5.9s) of CPU (elapsed) time.

96232200.doc

Page 22 of 41

Process Detail
---------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Starts | E-Rows | A-Rows |
A-Time
| Buffers | Reads | Writes | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------------|
0 | SELECT STATEMENT
|
|
1 |
| 21600 |00:05:33.79 |
11831 | 10477 |
5 |
|
|
|
|
1 | TEMP TABLE TRANSFORMATION
|
|
1 |
| 21600 |00:05:33.79 |
11831 | 10477 |
5 |
|
|
|
|
2 |
LOAD AS SELECT
|
|
1 |
|
0 |00:00:00.32 |
309 |
0 |
5 |
264K|
264K| 264K (0)|
|
3 |
WINDOW BUFFER
|
|
1 | 41405 |
1440 |00:00:00.30 |
301 |
0 |
0 | 52224 | 52224 |47104 (0)|
|* 4 |
VIEW
| PRSTAT_HEADERS_V
|
1 | 41405 |
1440 |00:00:00.13 |
301 |
0 |
0 |
|
|
|
|
5 |
WINDOW BUFFER
|
|
1 | 41405 | 41324 |00:00:00.21 |
301 |
0 |
0 | 3100K|
779K| 2755K (0)|
|
6 |
NESTED LOOPS
|
|
1 | 41405 | 41324 |00:00:00.36 |
301 |
0 |
0 |
|
|
|
|
7 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_RUNS
|
1 |
1 |
1 |00:00:00.01 |
2 |
0 |
0 |
|
|
|
|* 8 |
INDEX UNIQUE SCAN
| PRSTAT_RUN_PK
|
1 |
1 |
1 |00:00:00.01 |
1 |
0 |
0 |
|
|
|
|
9 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_HEADERS
|
1 | 41405 | 41324 |00:00:00.25 |
299 |
0 |
0 |
|
|
|
|* 10 |
INDEX RANGE SCAN
| PRSTAT_HEADERS_U1
|
1 | 41324 | 41324 |00:00:00.07 |
93 |
0 |
0 |
|
|
|
| 11 |
SORT ORDER BY
|
|
1 |
3713M| 21600 |00:05:33.40 |
11519 | 10477 |
0 | 3738K|
834K| 3322K (0)|
| 12 |
WINDOW SORT
|
|
1 |
3713M| 21600 |00:05:33.48 |
11519 | 10477 |
0 | 2675K|
740K| 2377K (0)|
| 13 |
WINDOW SORT
|
|
1 |
3713M| 21600 |00:05:32.67 |
11519 | 10477 |
0 | 2037K|
674K| 1810K (0)|
|* 14 |
HASH JOIN
|
|
1 |
3713M| 21600 |01:59:31.59 |
11519 | 10477 |
0 |
898K|
898K| 1070K (0)|
| 15 |
VIEW
|
|
1 |
705 |
15 |00:00:01.30 |
5761 |
5240 |
0 |
|
|
|
|* 16 |
FILTER
|
|
1 |
|
15 |00:00:01.30 |
5761 |
5240 |
0 |
|
|
|
| 17 |
HASH GROUP BY
|
|
1 |
705 |
3029 |00:00:01.30 |
5761 |
5240 |
0 |
795K|
795K| 2629K (0)|
|* 18 |
HASH JOIN
|
|
1 |
621K| 21600 |00:00:00.23 |
5761 |
5240 |
0 | 1036K| 1036K| 1222K (0)|
| 19 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 20 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6624_1|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 21 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.13 |
5752 |
5235 |
0 |
|
|
|
| 22 |
VIEW
|
|
1 |
583M|
40M|00:09:54.48 |
5758 |
5237 |
0 |
|
|
|
| 23 |
MERGE JOIN PARTITION OUTER |
|
1 |
583M|
40M|00:07:57.05 |
5758 |
5237 |
0 |
|
|
|
| 24 |
SORT JOIN
|
| 28269 | 41405 |
40M|00:01:15.21 |
6 |
0 |
0 | 57344 | 57344 |51200 (0)|
| 25 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
| 26 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6624_1|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
|* 27 |
SORT PARTITION JOIN
|
|
40M|
621K| 21600 |00:02:31.60 |
5752 |
5237 |
0 |
56M| 2621K|
50M (0)|
| 28 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.14 |
5752 |
5237 |
0 |
|
|
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------4 - filter((INTERNAL_FUNCTION("HDR"."DT")>TO_DATE(' 2012-04-02 12:00:00', 'syyyy-mm-dd hh24:mi:ss') AND INTERNAL_FUNCTION("HDR"."DT")<=TO_DATE(' 2012-04-03
12:00:00', 'syyyy-mm-dd hh24:mi:ss')))
8 - access("RUN"."ID"=1)
10 - access("HDR"."RUN_ID"=1)
14 - access("PIDS"."PID"="LIN"."PID")
16 - filter(SUM(2.4*"LIN_G"."CPU_PERCENT")>600)
18 - access("LIN_G"."HDR_ID"="IDS"."HDR_ID")
27 - access("LIN"."HDR_ID"="IDS"."HDR_ID")
filter("LIN"."HDR_ID"="IDS"."HDR_ID")

The query that produced this plan wrote 21,600 rows and took 327s (334s) of CPU (elapsed) time.
Process Pivot
---------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation
| Name
| Starts | E-Rows | A-Rows |
A-Time
| Buffers | Reads | Writes | OMem | 1Mem | Used-Mem |
---------------------------------------------------------------------------------------------------------------------------------------------------------------96232200.doc

Page 23 of 41

|
0 | SELECT STATEMENT
|
|
1 |
|
1440 |00:05:17.68 |
11831 | 10444 |
5 |
|
|
|
|
1 | TEMP TABLE TRANSFORMATION
|
|
1 |
|
1440 |00:05:17.68 |
11831 | 10444 |
5 |
|
|
|
|
2 |
LOAD AS SELECT
|
|
1 |
|
0 |00:00:00.30 |
309 |
0 |
5 |
264K|
264K| 264K (0)|
|
3 |
WINDOW BUFFER
|
|
1 | 41405 |
1440 |00:00:00.29 |
301 |
0 |
0 | 52224 | 52224 |47104 (0)|
|* 4 |
VIEW
| PRSTAT_HEADERS_V
|
1 | 41405 |
1440 |00:00:00.13 |
301 |
0 |
0 |
|
|
|
|
5 |
WINDOW BUFFER
|
|
1 | 41405 | 41324 |00:00:00.21 |
301 |
0 |
0 | 3100K|
779K| 2755K (0)|
|
6 |
NESTED LOOPS
|
|
1 | 41405 | 41324 |00:00:00.31 |
301 |
0 |
0 |
|
|
|
|
7 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_RUNS
|
1 |
1 |
1 |00:00:00.01 |
2 |
0 |
0 |
|
|
|
|* 8 |
INDEX UNIQUE SCAN
| PRSTAT_RUN_PK
|
1 |
1 |
1 |00:00:00.01 |
1 |
0 |
0 |
|
|
|
|
9 |
TABLE ACCESS BY INDEX ROWID| PRSTAT_HEADERS
|
1 | 41405 | 41324 |00:00:00.19 |
299 |
0 |
0 |
|
|
|
|* 10 |
INDEX RANGE SCAN
| PRSTAT_HEADERS_U1
|
1 | 41324 | 41324 |00:00:00.06 |
93 |
0 |
0 |
|
|
|
| 11 |
SORT ORDER BY
|
|
1 |
3713M|
1440 |00:05:17.38 |
11519 | 10444 |
0 |
142K|
142K| 126K (0)|
| 12 |
HASH GROUP BY PIVOT
|
|
1 |
3713M|
1440 |00:05:17.35 |
11519 | 10444 |
0 |
922K|
922K|
33M (0)|
| 13 |
VIEW
|
|
1 |
3713M| 14400 |00:05:17.31 |
11519 | 10444 |
0 |
|
|
|
| 14 |
WINDOW SORT
|
|
1 |
3713M| 14400 |00:05:17.26 |
11519 | 10444 |
0 |
832K|
511K| 739K (0)|
|* 15 |
HASH JOIN
|
|
1 |
3713M| 14400 |00:43:04.88 |
11519 | 10444 |
0 |
951K|
951K| 849K (0)|
|* 16 |
VIEW
|
|
1 |
705 |
10 |00:00:01.32 |
5761 |
5202 |
0 |
|
|
|
|* 17 |
WINDOW SORT PUSHED RANK
|
|
1 |
705 |
11 |00:00:01.32 |
5761 |
5202 |
0 | 2048 | 2048 | 2048 (0)|
|* 18 |
FILTER
|
|
1 |
|
15 |00:00:01.32 |
5761 |
5202 |
0 |
|
|
|
| 19 |
HASH GROUP BY
|
|
1 |
705 |
3029 |00:00:01.32 |
5761 |
5202 |
0 |
795K|
795K| 2629K (0)|
|* 20 |
HASH JOIN
|
|
1 |
621K| 21600 |00:00:00.21 |
5761 |
5202 |
0 | 1036K| 1036K| 1219K (0)|
| 21 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 22 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6623_1|
1 | 41405 |
1440 |00:00:00.01 |
9 |
5 |
0 |
|
|
|
| 23 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.07 |
5752 |
5197 |
0 |
|
|
|
| 24 |
VIEW
|
|
1 |
583M|
40M|00:09:39.78 |
5758 |
5242 |
0 |
|
|
|
| 25 |
MERGE JOIN PARTITION OUTER|
|
1 |
583M|
40M|00:07:41.62 |
5758 |
5242 |
0 |
|
|
|
| 26 |
SORT JOIN
|
| 28269 | 41405 |
40M|00:01:13.91 |
6 |
0 |
0 | 52224 | 52224 |47104 (0)|
| 27 |
VIEW
|
|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
| 28 |
TABLE ACCESS FULL
| SYS_TEMP_0FD9D6623_1|
1 | 41405 |
1440 |00:00:00.01 |
6 |
0 |
0 |
|
|
|
|* 29 |
SORT PARTITION JOIN
|
|
40M|
621K| 21600 |00:02:28.44 |
5752 |
5242 |
0 |
19M| 1641K|
17M (0)|
| 30 |
TABLE ACCESS FULL
| PRSTAT_LINES
|
1 |
621K|
619K|00:00:01.05 |
5752 |
5242 |
0 |
|
|
|
---------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):
--------------------------------------------------4 - filter((INTERNAL_FUNCTION("HDR"."DT")>TO_DATE(' 2012-04-02 12:00:00', 'syyyy-mm-dd hh24:mi:ss') AND INTERNAL_FUNCTION("HDR"."DT")<=TO_DATE(' 2012-04-03
12:00:00', 'syyyy-mm-dd hh24:mi:ss')))
8 - access("RUN"."ID"=1)
10 - access("HDR"."RUN_ID"=1)
15 - access("PIDS"."PID"="LIN"."PID")
16 - filter("PIDS"."RN"<=10)
17 - filter(ROW_NUMBER() OVER ( ORDER BY SUM(2.4*"LIN_G"."CPU_PERCENT") DESC )<=10)
18 - filter(SUM(2.4*"LIN_G"."CPU_PERCENT")>600)
20 - access("LIN_G"."HDR_ID"="IDS"."HDR_ID")
29 - access("LIN"."HDR_ID"="IDS"."HDR_ID")
filter("LIN"."HDR_ID"="IDS"."HDR_ID")

The query that produced this plan wrote 1,440 rows and took 321s (318s) of CPU (elapsed) time.

96232200.doc

Page 24 of 41

Excel Analysis
The benchmarking system mentioned earlier uses the SQL queries inserted as text into a table and
produces CSV output files for each query, which can then be loaded into a multi-tabbed Excel file, with
graphs added where appropriate. Note that all computations are performed in the SQL, not in the
spreadsheet.

Long-Run Averages
This report gives performance averages over the entire date range available, by the hour, and with moving
averages over various time intervals.
Load Average
This reference, CPU Loading, describes load averages thus:
Intuitively, the load average is an average over time of the number of processes in the run queue...
Typically, load averages are divided by the number of CPU cores to find the load per CPU. Load
averages above 1 per CPU indicate that the CPUs are fully utilized. Depending on the type of load and the
I/O requirements, user-visible performance may not be affected until levels of 2 per CPU are reached. A
general rule of thumb is that load averages that are persistently above 4 times the number of CPUs will
result in sluggish performance.
The prstat utility provides the base load average over the past minute, and SQL is used to obtain averages
over longer periods.
% Utilisation
This is defined as the amount of time consumed by running processes divided by the amount of time
available over any given interval, expressed as a percentage. For the system averages in this report,
available time is elapsed time multiplied by the number of CPUs (four in my examples). For individual
process averages in later reports, available time is simply the elapsed time, assuming that the process
only uses one CPU at a time.
Sample Output
The lines have been split into two for display here.
Hour
29 19
29 20
29 21
Hour

#
processe
s
468
442
435

# LW
processe
s
4541
4461
4444

%
Utilisatio
n

%
Utilisatio
n (day)

29 19
29 20
29 21

19
13
1

Columns
Column
Hour
# processes
# LW processes
Load average
Load average (day)
Load average (week)
Load average (month)
Hours (process)
% Utilisation
96232200.doc

19.3
16.3
11.1

Load
average
1.29
0.68
0.16
%
Utilisatio
n (week)
19.3
16.3
11.1

Load
average
(day)
1.29
0.98
0.71
%
Utilisatio
n
(month)
19.3
16.3
11.1

Load
average
(week)
1.29
0.98
0.71
Hours
(process,
cumulativ
e)
0.8
1.3
1.3

Load
average
(month)
1.29
0.98
0.71
%
Utilisation
(cumulativ
e)
19
16
11

Hours
(process)
0.8
0.5
0
Hours
(available,
cumulativ
e)
4
8
12

Description
Format DD HH24MI the month might be more useful than the minute
here (and I dropped it from the table above)
Number of processes
Number of lightweight processes
Load average over the hour past
Load average over the past 24 hours
Load average over the past 168 hours
Load average over the past calendar month, includes current hour of
last month
Hours counted against processes by prstat utility over the past hour
Hours used by all processes / hours available. Base value is over past
hour (where hours available = 4, the number of CPUs)
.

Page 25 of 41

% Utilisation (day)
% Utilisation (week)
% Utilisation (month)
Hours (process,
cumulative)
% Utilisation (cumulative)
Hours (available,
cumulative)

96232200.doc

% Utilisation over the past 24 hours


% Utilisation over the past 168 hours
% Utilisation over the past calendar month, includes current hour of
last month
Hours used by all processes summed to the current hour
% Utilisation calculated up to the current hour
Hours available to the current hour (= elapsed hours x 4, the number
of CPUs)

Page 26 of 41

Graphs
Below are displayed the graphs for the load averages and % utilisations, both hourly and for the weekly
averaged data.

96232200.doc

Page 27 of 41

There are two main spikes in the data that correspond to parallel running of four database jobs on the
nights of 2 April and 26 April. The weekly graphs show quite consistent performance outside the periods
affected by these spikes. The step change on 23 April was due to a runaway Oracle Applications process
that was later killed.

96232200.doc

Page 28 of 41

Load Averages
This report gives performance averages at each time within the input range, by the minute. The load
averages come directly from the prstat utility, while % utilisations are calculated.
Sample Output
The lines have been split into two for display here.
Minute
02 1201
02 1202
02 1203
Minute

# LW
processes

# processes
626
616
612

58
49
36

Columns
Column
Minute
# processes
# LW processes
Load average
Load average (5m)
Load average (15m)
% Utilisation
% Utilisation (15m)
Seconds (process,
cumulative)
% Utilisation (cumulative)
Seconds (available,
cumulative)

96232200.doc

4986
4987
4953
% Utilisation
(15m)

% Utilisation

02 1201
02 1202
02 1203

Load average

58
53
48

3.16
4.14
3
Seconds
(process,
cumulative)
139
256
342

Load average
(5m)
4.04
4.16
3.84
Seconds
(available,
cumulative)
240
480
720

Load average
(15m)
4.12
4.16
4.05

Description
Format DD HH24MI
Number of processes
Number of lightweight processes
Load average over the minute past
Load average over the past 5 minutes
Load average over the past 15 minutes
Seconds used by all processes / Seconds available. Base value is over
past minute (where seconds available = 60 x 4, the number of CPUs)
% Utilisation over the past 15 minutes
Seconds used by all processes, summed to the current minute
% Utilisation calculated up to the current minute
Seconds available up to the current minute (= elapsed seconds x 4,
the number of CPUs)

Page 29 of 41

Graphs

There were 4 database processes running in parallel during the time interval covered, to export data into
staging tables as part of a data migration. The processes started at 18:19 on 2 April, and were killed at
10:45 on 3 April.

96232200.doc

Page 30 of 41

Process Summary
This report gives the total CPU hours and other data by process in descending order of total CPU hours. It
gives a good overview of what is consuming processing capacity over an extended period. Note that the
system aggregates included are taken over the duration of each process. The lines have been split into
three for display here.
Sample Output
Process Id
1568
21590
21587
21586
21588
11844
Process Id
1568
21590
21587
21586
21588
11844
Process Id
1568
21590
21587
21586
21588
11844

Total hours
per process
10.1
3.1
3.1
3.1
3
0.5
Last time in
report
03 0123
03 1045
03 1045
03 1045
03 1045
03 0706
% Utilisation
(all
processes)
43
43
43
43
43
66

Columns
Column
Process Id
Total hours per process
User
Process
Initial CPU usage
Final CPU usage
First time in report
Last time in report
Minimum Size
Maximum Size
Minimum RSS
Maximum RSS
% Utilisation (process)
% Utilisation (all
processes)
Seconds (all processes)
Seconds (all available)
96232200.doc

oracle
oracle
oracle
oracle
oracle
oracle
Maximum
Size
582M
400M
402M
400M
402M
131M

Initial CPU
usage
2:48:58
0:00:01
0:00:01
0:00:01
0:00:01
0:00:14
Minimum
RSS
453M
381M
381M
381M
381M
111M

Final CPU
usage
12:55:18
3:08:29
3:08:00
3:05:32
3:03:03
0:32:11
Maximum
RSS
456M
384M
386M
384M
385M
112M

First time in
report
02 1201
02 1844
02 1844
02 1844
02 1844
03 0436
% Utilisation
(process)
75
19
19
19
19
21

Seconds (all
available)
192480
230640
230640
230640
230640
36000

Average load
average
2.5
3.4
3.4
3.4
3.4
7.5

Minimum
load average
1.1
0.1
0.1
0.1
0.1
0.7

Maximum
load average
11.1
13.6
13.6
13.6
13.6
13.6

User

Process

oracle
brendan
brendan
brendan
brendan
oracle
Minimum
Size
579M
399M
400M
400M
400M
131M
Seconds (all
processes)
81922
98569
98569
98569
98569
23625

Description
Process Id
Total hours CPU time used by the process during the input interval
User running the process
Process name
CPU usage from prstat utility on first appearance in report
CPU usage from prstat utility on last appearance in report
Time of first appearance in report
Time of last appearance in report
Minimum size of the process from prstat utility from first to last
appearance in report
Maximum size of the process from prstat utility from first to last
appearance in report
Minimum RSS of the process from prstat utility from first to last
appearance in report
Maximum RSS of the process from prstat utility from first to last
appearance in report
CPU time used by the process / time available from first to last
appearance in report (= elapsed time)
CPU time used by all processes / time available from first to last
appearance in report of the process (= elapsed time x 4, the number of
CPUs)
CPU time in seconds used by all processes from first to last appearance in
report of the process
CPU time available to all processes from first to last appearance in report
of the process (= elapsed time x 4, the number of CPUs)
Page 31 of 41

Average load average

Average load average from first to last appearance in report of the


process
Minimum load average from first to last appearance in report of the
process
Maximum load average from first to last appearance in report of the
process

Minimum load average


Maximum load average

Process Detail
This report gives prstat and other derived data by process for all processes using a total of more than an
input CPU time value (10 minutes). The lines have been split into three for display here, and the first
column, Process Id has been omitted.
Sample Output
Datetime
02 1201
02 1202
02 1203
Datetime

User
oracle
oracle
oracle

Process
oracle
oracle
oracle

Memory
581M
581M
581M

RSS
455M
455M
455M

Nice

# LW
processes

prstat time

% Utilisation
(process)

2:48:58
2:49:01
2:49:05
Seconds
(process
total)
36240
36240
36240

6
5.2
6
Seconds (all
procs,
cumul.)
6
11
16

02 1201
02 1202
02 1203
Datetime

0
0
0
% Utilisation
(all, cum.)

02 1201
02 1202
02 1203
Columns
Column
Process Id
Datetime
User
Process
Memory
RSS
State
Priority
Nice
# LW processes
prstat time
% Utilisation (process)
% Utilisation (all processes)
% Utilisation (process, cum.)
% Utilisation (all, cum.)
Seconds (process,
cumulative)
Seconds (process total)
Seconds (all procs, cumul.)
Seconds (available total)
Seconds (all procs, total)

96232200.doc

2
2
2

11
11
11
Seconds
(process,
cumulative)
4
7
10

State
sleep
sleep
sleep
% Utilisation
(all
processes)
2
2
2
Seconds
(available
total)
240
480
720

Priority
60
60
60
% Utilisation
(process,
cum.)
6
6
6
Seconds (all
procs, total)
90575
90575
90575

Description
Process Id
Datetime
User running the process
Process name
prstat size
prstat RSS
prstat state
prstat priority
prstat nice
Number of lightweight processes attached
prstat time
CPU time used by the process / time available. Base value is over last
minute (= 60 seconds)
CPU time used by all processes / time available. Base value is over last
minute (= 60 seconds x 4, the number of CPUs)
% Utilisation (process) calculated up to the current minute
% Utilisation (all processes) calculated up to the current minute
CPU time in seconds used by the process up to the current minute
CPU time in seconds used by the process during the input interval
CPU time in seconds used by all processes up to the current minute
CPU time in seconds available to all processes during the input interval
(= interval elapsed seconds x 4, the number of CPUs)
CPU time in seconds used by all processes during the input interval

Page 32 of 41

Graphs for One Process in Detail


There were 4 database processes running in parallel during the time interval covered, to export data into
staging tables as part of a data migration. The processes started at 18:19 on 2 April, and were killed at
10:20 on 3 April.

The graph above shows the cumulative CPU time in seconds for one of the processes mentioned.

The graph above shows the % of CPU time over the previous minute, for the process, multiplied by 4 to
account for 4 CPUs.

96232200.doc

Page 33 of 41

Process Pivot
This report lists % utilisations for the ten processes using most CPU time in the input interval. The values
are listed for all ten in a single record at each minute, in decreasing order of CPU usage across the record.
This allows for easy construction of comparative graphs.
Sample Output
The lines have been split into two for display here.
Minute
03 0131
03 0132
03 0133
Columns
Column
Minute
Pi

P1

P2
86
84
81

P3
14
13
12

P4
12
10
10

P5
12
11
10

P6
14
13
12

P7
0
0
0

P8
0
0
0

P9
0
0
0

P10
7
7
8

0
60
64

Description
Format DD HH24MI
% Utilisation of the ith process in decreasing order of CPU usage over the input interval,
over the past minute. The process can be identified from the Process Summary report

96232200.doc

Page 34 of 41

Graphs
It was convenient to place the four parallel jobs in their own graph separately from the remaining six in the
top ten.

96232200.doc

Page 35 of 41

References
REF

Document

REF-1

SQL Pivot and Prune Queries Keeping an Eye on


Performance, Brendan Furey, May 2011

REF-2

'Code Timing and Object Orientation and Zombies', Brendan


Furey, November 2010

REF-3

CPU Loading, Princeton University, 2011

REF-4

Oracle Database SQL Language Reference 11g Release 2


(11.2)

96232200.doc

Location
http://www.scribd.com/fullscreen/57696
875?access_key=key1exh47ppyi1yklevkz9p
http://www.scribd.com/fullscreen/43588
788?access_key=key23rlpqinxuz4npjcq9l3
http://www.princeton.edu/~unix/Solaris/t
roubleshoot/cpuload.html
http://www.oracle.com/pls/db112

Page 36 of 41

APPENDIX: Code Listings


prstat.pl: Perl Program
#
# Author:
Brendan Furey
# Date:
14 April 2012
# Description:
Takes prstat output file as input and produces headers and lines files,
#
as described in 'Analysing Unix System Performance with prstat and SQL'
#
at scribd.com/BrendanP. The timing package is needed to run this, and
#
can be obtained from an article, 'Code Timing and Object Orientation
#
and Zombies', at the same site; or timing references can be deleted.
#
use strict; use warnings;
use TimerSet;
my $timer = new TimerSet ('prstat');
my
my
my
my
my
my

$input_dir='C:/Users/Brendan/Documents/Home - X120/Perl/Input/';
$output_dir='C:/Users/Brendan/Documents/Home - X120/Perl/out/';
$fn_in = 'prstat_050412_1018.txt';
$fn_out_h = 'prstat_h.txt';
$fn_out_l = 'prstat_l.txt';
$stat_no = 0;

my
my
my
my

$infile = $input_dir.$fn_in;
$outfile_h = $output_dir.$fn_out_h;
$outfile_l = $output_dir.$fn_out_l;
$line_no;

print "Opening file $infile\n";


open (DAT, $infile) || die "Could not open file $infile: $!";
open (OUT_H, ">",
open (OUT_L, ">",

$outfile_h) or die "Can't open $outfile_h: $!";


$outfile_l) or die "Can't open $outfile_l: $!";

$timer->incrementTime ('Open files');


while ( my $item = <DAT> ) {
if ($item =~ /^
PID/) {
$stat_no++;
$line_no = 0;
next;
} else {
my @tokens = split (' ', $item);
if ($item =~ /^Total:/) {
print OUT_H $stat_no.','.$tokens[1].','.$tokens[3].','.$tokens[7].$tokens[8].
$tokens[9], "\n";
#
print OUT_H $stat_no.','.join (',', @tokens), "\n";
} else {
$line_no++;
my $line = $stat_no.','.$line_no.','.join (',', @tokens);
$line =~ s/\//,/;
$line =~ s/%//;
print OUT_L $line, "\n";
}
}
}
$timer->incrementTime ('Write files');
close (OUT_H);
close (OUT_L);
$timer->incrementTime ('Close files');
$timer->writeTimes;

The output log with timings looks like this:


Opening file C:/Users/Brendan/Documents/Home - X120/Perl/Input/prstat_050412_1018.txt
Timer Set: prstat, constructed at 06/04/12 12:42:21, written at 12:42:24
========================================================================
[Timer timed: Elapsed (per call): 0.18 (0.000018), CPU (per call): 0.17 (0.000017), calls:
10000, '***' denotes corrected line below]
Timer
Elapsed
CPU/Call
-------------------------------Open files
0.00
0.01500
Write files
3.27
96232200.doc

CPU

= User

+ System

Calls

Ela/Call

----------

----------

----------

----------

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

0.01

0.00

0.01

0.00151

3.28

3.25

0.03

3.26868
Page 37 of 41

3.27700
Close files
0.00
0.00000
(Other)
0.00
0.00000
-------------------------------Totals
3.27
0.82300
--------------------------------

0.00

0.00

0.00

0.00059

0.00

0.00

0.00

0.00004

----------

----------

----------

----------

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

3.29

3.25

0.05

0.81771

----------

----------

----------

----------

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

C_Unix.sql: Database Creation Script


SPOOL C_Unix
REM
REM Author:
Brendan Furey
REM Date:
14 April 2011
REM Description: Database tables etc. creation script for storing Unix prstat data,
REM
as described in 'Analysing Unix System Performance with prstat and SQL'
REM
at scribd.com/BrendanP.
REM
CREATE OR REPLACE DIRECTORY unix_dir as 'C:\Users\Brendan\Documents\Home X120\SQL\Brendan\Unix'
/
DROP TABLE prstat_header_ext
/
CREATE TABLE prstat_header_ext (
header_no
NUMBER,
n_procs
NUMBER,
n_lwps
NUMBER,
load_avg_1
NUMBER,
load_avg_5
NUMBER,
load_avg_15
NUMBER
)
ORGANIZATION EXTERNAL (
TYPE
oracle_loader
DEFAULT DIRECTORY
unix_dir
LOCATION
('prstat_h.txt')
)
/
DROP TABLE prstat_line_ext
/
CREATE TABLE prstat_line_ext (
header_no
NUMBER,
line_no
NUMBER,
pid
NUMBER,
username
VARCHAR2(30),
size_M
VARCHAR2(10),
rss
VARCHAR2(10),
state
VARCHAR2(10),
pri
NUMBER,
nice
NUMBER,
time_h_m_s
VARCHAR2(10),
cpu_percent
NUMBER,
process
VARCHAR2(30),
nlwp
NUMBER
)
ORGANIZATION EXTERNAL (
TYPE
oracle_loader
DEFAULT DIRECTORY
unix_dir
LOCATION
('prstat_l.txt')
)
/
DROP TABLE prstat_lines
/
DROP TABLE prstat_headers
/
DROP TABLE prstat_runs
/
CREATE TABLE prstat_runs (
id
NUMBER,
start_date
DATE,
end_date
DATE,
machine
VARCHAR2(30),
notes
VARCHAR2(4000),
CONSTRAINT prstat_run_pk PRIMARY KEY (id)
)
/
CREATE TABLE prstat_headers (
id
NUMBER,
96232200.doc

Page 38 of 41

run_id
NUMBER,
header_no
NUMBER,
n_procs
NUMBER,
n_lwps
NUMBER,
load_avg_1
NUMBER,
load_avg_5
NUMBER,
load_avg_15
NUMBER,
CONSTRAINT prstat_header_pk PRIMARY KEY (id),
CONSTRAINT hdr_run_fk FOREIGN KEY (run_id) REFERENCES prstat_runs (id)
)
/
CREATE UNIQUE INDEX prstat_headers_U1 ON prstat_headers (run_id, header_no)
/
CREATE TABLE prstat_lines (
hdr_id
NUMBER,
line_no
NUMBER,
pid
NUMBER,
username
VARCHAR2(30),
size_M
VARCHAR2(10),
rss
VARCHAR2(10),
state
VARCHAR2(10),
pri
NUMBER,
nice
NUMBER,
time_h_m_s
VARCHAR2(10),
cpu_percent
NUMBER,
process
VARCHAR2(30),
nlwp
NUMBER,
CONSTRAINT prstat_line_pk PRIMARY KEY (hdr_id, line_no),
CONSTRAINT lin_hdr_fk FOREIGN KEY (hdr_id) REFERENCES prstat_headers (id)
)
/
CREATE SEQUENCE prstat_header_seq
/
CREATE OR REPLACE VIEW prstat_headers_v AS
SELECT run.end_date - (Max (hdr.header_no) OVER (PARTITION BY hdr.run_id) - hdr.header_no) /
24 / 60 dt,
hdr.run_id,
hdr.id hdr_id,
header_no,
n_procs,
n_lwps,
load_avg_1,
load_avg_5,
load_avg_15
FROM prstat_headers hdr
JOIN prstat_runs run
ON run.id = hdr.run_id
/
GRANT SELECT ON prstat_headers_v TO brendan
/
GRANT ALL ON prstat_runs TO brendan
/
GRANT ALL ON prstat_headers TO brendan
/
GRANT ALL ON prstat_lines TO brendan
/
CREATE PUBLIC SYNONYM prstat_headers_v FOR prstat_headers_v
/
CREATE PUBLIC SYNONYM prstat_runs FOR prstat_runs
/
CREATE PUBLIC SYNONYM prstat_headers FOR prstat_headers
/
CREATE PUBLIC SYNONYM prstat_lines FOR prstat_lines
/
SPOOL OFF

96232200.doc

Page 39 of 41

Pop_Unix.sql: Database Package Script


CREATE OR REPLACE PACKAGE Pop_Unix AS
/********************************************************************************************
Author:
Date:
Description:

Brendan Furey
14 April 2011
Package used for loading Unix prstat data into Oracle tables, as described in
'Analysing Unix System Performance with prstat and SQL', scribd.com/BrendanP.
Package spec.

********************************************************************************************/
PROCEDURE Load_Data (

p_run_id
p_machine
p_start
p_end
p_notes

PLS_INTEGER,
VARCHAR2,
VARCHAR2,
VARCHAR2,
VARCHAR2);

END Pop_Unix;
/
SHO ERR
CREATE OR REPLACE PACKAGE BODY Pop_Unix AS
/********************************************************************************************
Author:
Date:
Description:

Brendan Furey
14 April 2011
Package used for loading Unix prstat data into Oracle tables, as described in
'Analysing Unix System Performance with prstat and SQL', scribd.com/BrendanP.
Package body.

********************************************************************************************/
PROCEDURE Load_Data (

p_run_id
p_machine
p_start
p_end
p_notes

PLS_INTEGER,
VARCHAR2,
VARCHAR2,
VARCHAR2,
VARCHAR2) IS

BEGIN
DELETE prstat_lines WHERE hdr_id IN (SELECT id FROM prstat_headers WHERE run_id =
p_run_id);
DELETE prstat_headers WHERE run_id = p_run_id;
DELETE prstat_runs WHERE id = p_run_id;
INSERT INTO prstat_runs (
id,
start_date,
end_date,
machine,
notes
) VALUES (
p_run_id,
To_Date (p_start, 'DD-MON-YYYY HH24:MI'),
To_Date (p_end, 'DD-MON-YYYY HH24:MI'),
p_machine,
p_notes
);
INSERT INTO prstat_headers (
id,
run_id,
header_no,
n_procs,
n_lwps,
load_avg_1,
load_avg_5,
load_avg_15)
SELECT
prstat_header_seq.NEXTVAL,
p_run_id,
header_no,
n_procs,
n_lwps,
load_avg_1,
load_avg_5,
load_avg_15
96232200.doc

Page 40 of 41

FROM prstat_header_ext;
INSERT INTO prstat_lines (
hdr_id,
line_no,
pid,
username,
size_M,
rss,
state,
pri,
nice,
time_h_m_s,
cpu_percent,
process,
nlwp
)
SELECT
hdr.id,
lin_e.line_no,
lin_e.pid,
lin_e.username,
lin_e.size_M,
lin_e.rss,
lin_e.state,
lin_e.pri,
lin_e.nice,
lin_e.time_h_m_s,
lin_e.cpu_percent,
lin_e.process,
lin_e.nlwp
FROM prstat_line_ext lin_e
JOIN prstat_headers
hdr
ON hdr.header_no
= lin_e.header_no
AND hdr.run_id
= p_run_id;
END Load_Data;
END Pop_Unix;
/
SHO ERR

96232200.doc

Page 41 of 41

You might also like