2022-06-14

Extract information about deadlocks from traces

If you don’t have access to filesystem or love to do everything in SQL here is the quick and dirty solution how to extract information about ORA-00060 from Oracle trace files.
I’m to lazy to write a normal script with variables thus just add the comments in the code

-- Get trace files dir
SELECT VALUE FROM v$diag_info WHERE NAME = 'Diag Trace';
-- create directory object for trace files dir 
CREATE DIRECTORY test_trace_dir AS '&VALUE_FROM_PREVIOUS_STEP';

-- file to clob extractor procedure was found in stackoverflow
CREATE OR REPLACE FUNCTION test_trace_read(p_file_name IN VARCHAR2) RETURN CLOB AS
   l_clob     CLOB;
   l_bfile    BFILE;
   dst_offset NUMBER := 1;
   src_offset NUMBER := 1;
   lang_ctx   NUMBER := dbms_lob.default_lang_ctx;
   warning    NUMBER;
BEGIN
   l_bfile := bfilename('TEST_TRACE_DIR', p_file_name);
   dbms_lob.fileopen(l_bfile);
   dbms_lob.createtemporary(l_clob, TRUE);
   dbms_lob.loadclobfromfile(dest_lob     => l_clob,
                             src_bfile    => l_bfile,
                             amount       => dbms_lob.getlength(l_bfile),
                             dest_offset  => dst_offset,
                             src_offset   => src_offset,
                             bfile_csid   => dbms_lob.default_csid,
                             lang_context => lang_ctx,
                             warning      => warning);
   dbms_lob.fileclose(l_bfile);
   RETURN l_clob;
END;
/

-- query to return piece of tracefile
-- full tracefile content can be returned as well
-- number of rows is specified in variable
WITH traces AS (
   SELECT t.originating_timestamp, t.module_id, t.message_text,
     regexp_replace(t.message_text, '.*More info in file (.*)\.$', '\1') full_file_name,
     regexp_replace(t.message_text, '.*More info in file .*\/(.*)\.\s*$', '\1') file_name -- related from message format!
   FROM V$DIAG_ALERT_EXT t
   WHERE message_text LIKE '%ORA-00060%'
   ORDER BY 1 DESC
),
  trace_files AS (
   SELECT test_trace_read(file_name) CONTENT, t.* 
   FROM traces t
   WHERE ROWNUM <= &NUM_ROWS
  )
SELECT SUBSTR(CONTENT, INSTR(CONTENT, 'Deadlock graph:'), INSTR(CONTENT, '----- Call Stack Trace -----') - INSTR(CONTENT, 'Deadlock graph:')) substr_trace,
   t.originating_timestamp, t.module_id, t.full_file_name, t.message_text
FROM trace_files t
;