Yesterday a colleague of mine asked the following question: “Is there a way to get information about free CPU resources within sqlplus?”

When I read it, I immediately thought to V$OSSTAT. In fact, as of 10g that dynamic performance view provides system utilization statistics from the operating system. For example, it provides the following statistics:

  • IDLE_TIME: Time (centi-secs) that CPUs have been in the idle state
  • USER_TIME: Time (centi-secs) spent in user code
  • SYS_TIME: Time (centi-secs) spent in the kernel
  • IOWAIT_TIME: Time (centi-secs) spent waiting for IO
  • NICE_TIME: Time (centi-secs) spend in low-priority user code

Note: In 10.1 the statistics are named IDLE_TICKS, USER_TICKS, SYS_TICKS and NICE_TICKS. In addition, IOWAIT_TIME is available as of 10.2.0.2 only.

Hence, the answer to the question is: “Yes, it’s possible”. But, how accurate it is? To answer this second question, I wrote a pipelined PL/SQL function to sample V$OSSTAT and compared the results with the values provided by sar(1). The necessary objects (two types and the function) are created with the following SQL statements:

CREATE OR REPLACE TYPE osstat_record IS OBJECT (
  date_time TIMESTAMP,
  idle_time NUMBER,
  user_time NUMBER,
  sys_time NUMBER,
  iowait_time NUMBER,
  nice_time NUMBER
);
/

CREATE OR REPLACE TYPE osstat_table AS TABLE OF osstat_record;
/
CREATE OR REPLACE FUNCTION osstat(p_interval IN NUMBER, p_count IN NUMBER)
   RETURN osstat_table
   PIPELINED
IS
  l_t1 osstat_record;
  l_t2 osstat_record;
  l_out osstat_record;
  l_num_cpus NUMBER;
  l_total NUMBER;
BEGIN
  l_t1 := osstat_record(NULL, NULL, NULL, NULL, NULL, NULL);
  l_t2 := osstat_record(NULL, NULL, NULL, NULL, NULL, NULL);

  SELECT value
  INTO l_num_cpus
  FROM v$osstat
  WHERE stat_name = 'NUM_CPUS';

  FOR i IN 1..p_count
  LOOP
    SELECT sum(decode(stat_name,'IDLE_TIME', value, NULL)) as idle_time,
           sum(decode(stat_name,'USER_TIME', value, NULL)) as user_time,
           sum(decode(stat_name,'SYS_TIME', value, NULL)) as sys_time,
           sum(decode(stat_name,'IOWAIT_TIME', value, NULL)) as iowait_time,
           sum(decode(stat_name,'NICE_TIME', value, NULL)) as nice_time
    INTO l_t2.idle_time, l_t2.user_time, l_t2.sys_time, l_t2.iowait_time, l_t2.nice_time
    FROM v$osstat
    WHERE stat_name in ('IDLE_TIME','USER_TIME','SYS_TIME','IOWAIT_TIME','NICE_TIME');

    l_out := osstat_record(systimestamp,
                           (l_t2.idle_time-l_t1.idle_time)/l_num_cpus/p_interval,
                           (l_t2.user_time-l_t1.user_time)/l_num_cpus/p_interval,
                           (l_t2.sys_time-l_t1.sys_time)/l_num_cpus/p_interval,
                           (l_t2.iowait_time-l_t1.iowait_time)/l_num_cpus/p_interval,
                           (l_t2.nice_time-l_t1.nice_time)/l_num_cpus/p_interval);
    l_total := l_out.idle_time+l_out.user_time+l_out.sys_time+l_out.iowait_time+nvl(l_out.nice_time,0);
    PIPE ROW(osstat_record(systimestamp,
                           l_out.idle_time/l_total*100,
                           l_out.user_time/l_total*100,
                           l_out.sys_time/l_total*100,
                           l_out.iowait_time/l_total*100,
                           l_out.nice_time/l_total*100));
    l_t1 := l_t2;

    dbms_lock.sleep(p_interval);

  END LOOP;
  RETURN;
END;
/

The statistics are displayed with a query like the following one (notice that I set ARRAYSIZE to keep as short as possible the delay between the generation and the display of the statistics):

SQL> SET ARRAYSIZE 1

SQL> COLUMN user_time FORMAT 990.00
SQL> COLUMN nice_time FORMAT 990.00
SQL> COLUMN sys_time FORMAT 990.00
SQL> COLUMN iowait_time FORMAT 990.00
SQL> COLUMN idle_time FORMAT 990.00

SQL> SELECT to_char(date_time,'HH:MI:SS') as date_time, user_time, nice_time, sys_time, iowait_time, idle_time
  2  FROM table(osstat(5,100));

DATE_TIM USER_TIME NICE_TIME SYS_TIME IOWAIT_TIME IDLE_TIME
-------- --------- --------- -------- ----------- ---------
12:26:11
12:26:16      0.05      0.00     0.05        0.10     99.80
12:26:21      0.76      0.00     0.05        0.66     98.52
12:26:26      0.05      0.00     0.10        0.10     99.74
12:26:31      0.15      0.00     8.03        0.31     91.50
12:26:36      0.27      0.00    21.06       15.75     62.92
12:26:41      0.10      0.00     2.57        8.13     89.21
12:26:46      0.05      0.00     0.10        0.71     99.14
12:26:51      0.10      0.00     0.05        0.41     99.44
12:26:56     24.37      0.00     0.65        3.28     71.71
12:27:01     24.50      0.00     0.97        1.27     73.26
12:27:06     24.31      0.00     1.17        1.32     73.20
12:27:11     25.05      0.00     0.66        0.82     73.47
12:27:16     25.06      0.00     0.61        0.76     73.56
12:27:21     25.13      0.00     0.56        0.46     73.85
12:27:26     24.91      0.00     0.45        1.77     72.87
12:27:31     23.97      0.00     1.41        2.17     72.46
12:27:36     24.90      0.00     0.97        0.91     73.22
12:27:41     25.18      0.00     0.51        0.36     73.95
12:27:46     25.64      0.00     0.41        0.36     73.59
12:27:51     46.37      0.05     3.48        0.45     49.65
12:27:56     46.81      0.00     3.14        0.35     49.70
12:28:01     46.63      0.00     3.34        0.20     49.83
12:28:06     45.76      0.00     4.19        0.25     49.80
12:28:11     46.58      0.00     3.40        0.15     49.88
12:28:16     46.76      0.00     3.54        0.25     49.45
12:28:21     46.06      0.00     6.74        0.25     46.96
12:28:26     43.73      0.00     6.24        0.10     49.93
12:28:31     34.87      0.00     6.98        0.30     57.84
12:28:36     29.60      0.00     5.50        0.71     64.20
12:28:41     38.40      0.00     9.60        7.69     44.32
12:28:46     39.20      0.00     9.39        6.32     45.09
12:28:51     34.73      0.00     8.37       13.81     43.09
...

And here is the sar(1) output for the very same period of time:

oracle@helicon:~/ [rdbms11107] sar 5 100
Linux 2.6.9-42.ELsmp (helicon.antognini.ch)     05/01/2009

12:26:11 AM       CPU     %user     %nice   %system   %iowait     %idle
12:26:16 AM       all      0.05      0.00      0.05      0.10     99.80
12:26:21 AM       all      0.77      0.00      0.10      0.67     98.46
12:26:26 AM       all      0.05      0.00      0.10      0.10     99.74
12:26:31 AM       all      0.15      0.00      8.31      0.31     91.23
12:26:36 AM       all      0.31      0.00     26.54     18.63     54.52
12:26:41 AM       all      0.10      0.00      3.74      8.41     87.75
12:26:46 AM       all      0.05      0.00      0.20      0.72     99.03
12:26:51 AM       all      0.26      0.00      0.05      0.41     99.28
12:26:56 AM       all     25.15      0.00      0.72      3.39     70.74
12:27:01 AM       all     24.87      0.00      0.98      1.29     72.86
12:27:06 AM       all     24.64      0.00      1.23      1.34     72.79
12:27:11 AM       all     25.23      0.00      0.67      0.82     73.27
12:27:16 AM       all     25.26      0.00      0.72      0.77     73.25
12:27:21 AM       all     25.19      0.00      0.62      0.46     73.73
12:27:26 AM       all     25.40      0.00      0.51      1.80     72.29
12:27:31 AM       all     24.46      0.00      1.44      2.21     71.88
12:27:36 AM       all     25.13      0.00      1.03      0.92     72.92
12:27:41 AM       all     25.26      0.00      0.56      0.36     73.82
12:27:46 AM       all     25.73      0.00      0.46      0.36     73.44
12:27:51 AM       all     46.58      0.05      3.50      0.45     49.43
12:27:56 AM       all     46.95      0.00      3.15      0.35     49.55
12:28:01 AM       all     46.70      0.00      3.45      0.20     49.65
12:28:06 AM       all     45.85      0.00      4.25      0.25     49.65
12:28:11 AM       all     46.65      0.00      3.45      0.15     49.75
12:28:16 AM       all     46.80      0.00      3.55      0.25     49.40
12:28:21 AM       all     46.20      0.00      6.80      0.25     46.75
12:28:26 AM       all     43.80      0.00      6.25      0.10     49.85
12:28:31 AM       all     35.05      0.00      7.01      0.30     57.64
12:28:36 AM       all     29.70      0.00      5.58      0.71     64.01
12:28:41 AM       all     41.18      0.00     11.29      8.20     39.33
12:28:46 AM       all     41.57      0.00     10.66      6.75     41.02
12:28:51 AM       all     39.96      0.00     10.62     15.87     33.55
...

As you can verify, the values provided by the pipelined function are quite good! Also note that small difference are normal because the sampling interval is quite short (5 seconds) and the two gathering methods are not synchronized.