Under very rare circumstances, I need Oracle SQL trace files from a long period of time. Because trace files usually grow large—especially over several days—there is the need to rotate the trace file during that time so that they can be compressed and put away. The problem is that there is no “rotate tracefile“ button in Oracle. However, I have found a “undocumented feature“ that does exactly that—without disabling tracing.
My procedure uses the close_trace
call of oradebug
. This call closes the currently written trace file for a session. Alex Gorbachev has used this to delete big trace files that are still open. My procedure goes a little bit further and exposes one more side effect of oradebug close_trace
.
Warning: The technique described here uses the undocumented and unsupported oradebug
facility. Although I am not aware of any negative side effects of this procedure, the use of this method takes place at your own risk.
The oradebug
utility can be used from the SQL*Plus prompt. If you have an account with sufficient rights (sigh) you can attach to any session using the oradebug setorapid
command. To obtain the required Oracle PID, just query the v$process
view:
SQL> SELECT s.sid, s.serial#, p.pid FROM v$session s, v$process p WHERE s.paddr=p.addr; SID SERIAL# PID ---------- ---------- ---------- 1 1 2 [... skipped ...] 20 3126 21 26 rows selected. SQL> oradebug setorapid 21 Oracle pid: 21, Unix process pid: 145, image: oracle@test.fatalmind.com SQL>
Once attached to a specific process, every oradebug
operation is applied to that particular process. The trick to rotate the trace file is very simple and exploits a “feature” of UNIX file systems; that is, that the name of the file is used only at the moment the file is opened. Once the file is open, all access is managed with the so called inode
and the name is not relevant for the read and write operations anymore. The inode
system has some more implications, one of them is frequently used to rotate log files without data loss. Another implications makes it hard to delete open files, as Alex Gorbachev explained.
The log rotation trick is to rename the open file and then cause the writing process to re-open that file. All write operations to the renamed file will be written properly to new file name—more correctly expressed: to the file which can be accessed under the new name. If the writing process re-opens the file under its original name, it will create a new file because there is no file with that name anymore. Every subsequent write operation goes to the new file. The original file will not grow anymore and can be taken away safely.
Although the oradebug
command name close_trace
doesn’t suggest that the trace file is reopened, it is—if SQL tracing is enabled. So we have everything to rotate a trace file. Consider the following example:
$ ls -lrt TEST_ora_14457.trc* -rw-r----- 1 oracle oinstall 14758723 2010-02-01 TEST_ora_14457.trc $ mv TEST_ora_14457.trc TEST_ora_14457.trc.old
After renaming the file, the file still grows as the SQL trace is written. You can now use oradebug close_trace
to actually rotate the file:
SQL> oradebug close_trace Statement processed. SQL>
The verification in the file system shows that the ‘old’ file became bigger, and the new file is also filling up:
$ ls -lrt TEST_ora_14457.trc* -rw-r----- 1 oracle oinstall 18878704 2010-02-01 TEST_ora_14457.trc.old -rw-r----- 1 oracle oinstall 1555972 2010-02-01 TEST_ora_14457.trc $
Because I often need to trace many sessions, I wrote a very tiny script (switch_traces.sql
) that creates another script to re-open all trace files:
set echo off set feedback off set pages 0 set heading off set define off spool oradebug_close_traces.sql select cmd from ( select pid, 1 o, 'oradebug setorapid ' || pid cmd from v$process UNION ALL select pid, 2 o, 'oradebug flush' from v$process UNION ALL select pid, 3 o, 'oradebug close_trace' from v$process) order by pid,o; spool off prompt prompt c&p >>> @oradebug_close_traces.sql <<< to switch traces set feedback on set pages 1000 set heading on set echo on
This script will close all traces in all sessions and continue tracing to new files if SQL tracing is enabled. You might want to add additional where
clauses to limit your scope—e.g., to skip background processes. The script will also issue a flush
before the close_trace
. I don’t know if this is required or not, it doesn’t seem to make any harm. The use of the script is very simple:
SQL> @switch_traces.sql SQL> set echo off oradebug setorapid 1 oradebug flush oradebug close_trace [... skipped ...] oradebug setorapid 29 oradebug flush oradebug close_trace c&p >>> @oradebug_close_traces.sql <<< to switch traces SQL> @oradebug_close_traces.sql SQL> oradebug setorapid 1 ORA-00072: process "Unix process pid: 0, image: PSEUDO" is not active SQL> oradebug flush Statement processed. SQL> oradebug close_trace Statement processed. SQL> oradebug setorapid 2 Oracle pid: 2, Unix process pid: 137, image: oracle@test.fatalmind.com SQL> oradebug flush Statement processed. SQL> oradebug close_trace Statement processed. [... skipped ...] Oracle pid: 29, Unix process pid: 138, image: oracle@test.fatalmind.com SQL> oradebug flush Statement processed. SQL> oradebug close_trace Statement processed. SQL>
In case you have not forgotten to rename your trace files you will see the new files growing now.
Read also my previous article To Trace or Not to Trace for information about fine grained SQL tracing.