【轉】Oralce PL/SQL 堆棧資訊追蹤

來源:互聯網
上載者:User

標籤:資料庫   專註   編譯器   支援   val   使用者   obj   cat   other   

總結如下:

DBMS_UTILITY.FORMAT_CALL_STACK - 這是在Oracle7中引入的,DBMS_UTILITY.FORMAT_CALL_STACK這個內建函數返回一個格式化的字串,它顯示了執行呼叫堆疊:直至此函數的調用點處的所有過程或者函數的調用順序。換句話說,這個函數回答了這個問題:“我是怎麼來到這裡的?”

DBMS_UTILITY.FORMAT_ERROR_STACK - 這是在Oracle7中引入的,DBMS_UTILITY.FORMAT_ERROR_STACK 這個內建函數和SQLERRM一樣,返回的是和當前錯誤(SQLCODE返回的值)所關聯的錯誤資訊。

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE - 這是在Oracle 10g資料庫引入的,DBMS_UTILITY.FORMAT_ERROR_BACKTRACE內建函數返回一個格式化的字串堆棧,堆棧中的程式及其行號可以回溯到錯誤被最先拋出的那一行。

 

轉自:http://www.itpub.net/thread-1896005-1-1.html

複雜的呼叫堆疊分析

原文連結:http://www.oracle.com/technetwork/issue-archive/2014/14-jan/o14plsql-2045346.html
Steven Feuerstein (Oracle ACE Director)


Oracle 12c資料庫中的UTL_CALL_STACK包給了開發人員更好的答案。
這是關於 Oracle 12c資料庫 Release 1中PL/SQL新功能的第三篇也是最後一篇文章,它將專註於新的UTL_CALL_STACK包。

呼叫堆疊,出錯堆棧,和錯誤的回溯

在 Oracle 12c資料庫之前, Oracle 資料庫提供了幾種DBMS_UTILITY函數,以回答程式員在開發、排錯、維護他們的程式時所問的幾個關鍵問題,這些函數極其有用。然而,有待改善的空間依然存在,這就是為什麼Oracle 12c資料庫加入了UTL_CALL_STACK包。

在我深入UTL_CALL_STACK之前,讓我們複習一下三個DBMS_UTILITY函數,它們被UTL_CALL_STACK包重新構想了。

DBMS_UTILITY.FORMAT_CALL_STACK。這是在Oracle7中引入的,DBMS_UTILITY.FORMAT_CALL_STACK這個內建函數返回一個格式化的字串,它顯示了執行呼叫堆疊:直至此函數的調用點處的所有過程或者函數的調用順序。換句話說,這個函數回答了這個問題:“我是怎麼來到這裡的?”

代碼清單1展示了DBMS_UTILITY.FORMAT_CALL_STACK函數以及格式化子串的例子。

代碼清單 1: DBMS_UTILITY.FORMAT_CALL_STACK函數的展示
SQL> CREATE OR REPLACE PROCEDURE proc1
  2  IS
  3  BEGIN
  4     DBMS_OUTPUT.put_line (DBMS_UTILITY.format_call_stack);
  5  END;
  6  /

SQL> CREATE OR REPLACE PACKAGE pkg1
  2  IS
  3     PROCEDURE proc2;
  4  END pkg1;
  5  /

SQL> CREATE OR REPLACE PACKAGE BODY pkg1
  2  IS
  3     PROCEDURE proc2
  4     IS
  5     BEGIN
  6        proc1;
  7     END;
  8  END pkg1;
  9  /

SQL> CREATE OR REPLACE PROCEDURE proc3
  2  IS
  3  BEGIN
  4     FOR indx IN 1 .. 1000
  5     LOOP
  6        NULL;
  7     END LOOP;
  8
  9     pkg1.proc2;
10  END;
11  /

SQL> BEGIN
  2     proc3;
  3  END;
  4  /

——————— PL/SQL Call Stack ———————
   object handle    line number   object name
000007FF7EA83240              4   procedure HR.PROC1
000007FF7E9CC3B0              6   package body HR.PKG1
000007FF7EA0A3B0              9   procedure HR.PROC3
000007FF7EA07C00              2   anonymous block

對於跟蹤和錯誤記錄檔而言這是非常有用的資訊,但是使用DBMS_UTILITY.FORMAT_CALL_STACK及其返回的字串也有一些缺點:

如果你調用一個包中的子程式,格式化的呼叫堆疊只會顯示包的名字,而不顯示子程式的名字,當然也不會顯示在那個子程式中嵌套定義的子程式名。

如果你只需要最近執行的子程式名字,你不得不解析這個字串。這並不難,但你不得不書寫和維護更多的代碼。

這個“object handle”的值,對於所有實際的目的而言全是“噪音”。 PL/SQL程式員(至少,在ORACLE之外的程式員)從來不會使用這個值。

DBMS_UTILITY.FORMAT_ERROR_STACK。這是在Oracle7中引入的,DBMS_UTILITY.FORMAT_ERROR_STACK 這個內建函數和SQLERRM一樣,返回的是和當前錯誤(SQLCODE返回的值)所關聯的錯誤資訊。

DBMS_UTILITY.FORMAT_ERROR_STACK 函數和 SQLERRM 在兩個方面有所不同:

它可以返回長達1,899字元的錯誤資訊,從而在錯誤堆棧增長時避免了資訊截斷的問題(或者至少將可能性降到極低)。SQLERRM會截斷資訊只留下510個字元。

你不能將一個錯誤碼傳給這個函數,它也不能用來返回一個錯誤碼的所代表的錯誤資訊。

按照規則,你應該在你的異常處理器中調用這個函數,然後將錯誤堆棧儲存在你的錯誤記錄檔表中用以事後分析。

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE。這是在Oracle 10g資料庫引入的,DBMS_UTILITY.FORMAT_ERROR_BACKTRACE內建函數返回一個格式化的字串堆棧,堆棧中的程式及其行號可以回溯到錯誤被最先拋出的那一行。

這個函數把L/SQL中的一條大溝填平了。在Oracle9i資料庫以及更早的版本,一旦你在PL/SQL塊中處理了異常,你就無法確定錯誤是在哪一行發生的(這個對於開發人員來說可能是最重要的資訊)。


如果你確實想看到這個資訊,你不得不允許異常不被處理,這時你可以看到完整的錯誤回溯資訊被顯示在螢幕上,或者以其他方式展示給使用者。

DBMS_UTILITY.FORMAT_ERROR_BACKTRACE產生了及其有用的資訊。我建議,無論何時,當你處理一個錯誤的時候,你都調用DBMS_UTILITY.FORMAT_ERROR_BACKTRACE函數並且把跟蹤資訊寫入你的錯誤記錄檔表。它會在解決錯誤發生的原因時發揮很大的協助作用。

然而,就如DBMS_UTILITY.FORMAT_CALL_STACK函數一樣,關鍵的資訊(子程式的名稱以及出錯的行數)被藏在格式化的字串之內。並且,更糟糕的是,你看不到包內的子程式的名字。

所有這些缺陷,在Oracle 12c資料庫中的新包UTL_CALL_STACK中都得到瞭解決。

新的UTL_CALL_STACK包


UTL_CALL_STACK包提供了現在執行的子程式的相關資訊。雖然包的名稱看起來好像只提供了執行堆棧,其實它也提供了對出錯堆棧和錯誤回溯資訊的訪問。

每個堆棧包含了深度(位置),你可以要求這三種堆棧中的每一種的某一個特定深度的資訊,這在整個包都是可見的。這意味著你不再需要解析格式化字串來找到你所需要的特定資訊。

UTL_CALL_STACK 針對 DBMS_UTILITY.FORMAT_CALL_STACK的最重要的改善之一,是你可以獲得帶有單元限定的名字,它拼接了單元的名字,所有父程式的名字,以及子程式名。然而,在錯誤回溯堆棧中沒有這些額外資訊。表1包含了UTL_CALL_STACK包中的子程式的清單及其描述。


子程式名         描述
BACKTRACE_DEPTH  返回回溯堆棧中的元素數量
BACKTRACE_LINE   返回指定深度的那個程式單元的行號
BACKTRACE_UNIT   返回指定深度的那個程式單元的名稱
CONCATENATE_SUBPROGRAM   返回拼接形式的程式單元限定的名字
DYNAMIC_DEPTH    返回呼叫堆疊中的子程式的數量,包括一路上調用的 SQL, Java, 和其他的非PL/SQL的上下文調用——例如,假設A調用B調用C調用B, 這個堆棧如果寫成一行,看起來會是這樣子(下面是動態深度):

A B C B 
4 3 2 1

ERROR_DEPTH      返回呼叫堆疊中的錯誤數量
ERROR_MSG        返回指定深度的錯誤資訊
ERROR_NUMBER     返回指定深度的錯誤代號
LEXICAL_DEPTH    返回指定動態深度的子程式的詞彙嵌套層級
UNIT_LINE        返回指定深度的那個程式單元的行號
SUBPROGRAM       返回指定深度的程式單元限定的名字


表1: UTL_CALL_STACK包中的子程式

首先,讓我們來看看如何用UTL_CALL_STACK來類比DBMS_UTILITY.FORMAT_CALL_STACK函數並且顯示完整的呼叫堆疊。為了做到這一點,你必須以深度來遍曆堆棧中的條目。代碼清單2中的format_call_stack_12c過程精確地完成了這個任務。

代碼清單2: format_call_stack_12c程序呼叫了UTL_CALL_STACK子程式

SQL> CREATE OR REPLACE PROCEDURE format_call_stack_12c
  2  IS
  3  BEGIN
  4     DBMS_OUTPUT.put_line (
  5        ‘LexDepth Depth LineNo Name‘);
  6     DBMS_OUTPUT.put_line (
  7        ‘-------- ----- ------ ----‘);
  8
  9     FOR the_depth IN REVERSE 1 ..
10                          utl_call_stack.dynamic_depth ()
11     LOOP
12        DBMS_OUTPUT.put_line (
13              RPAD (
14                 utl_call_stack.lexical_depth (
15                    the_depth),
16                 9)
17           || RPAD (the_depth, 5)
18           || RPAD (
19                 TO_CHAR (
20                    utl_call_stack.unit_line (
21                       the_depth),
22                    ‘99‘),
23                 8)
24           || utl_call_stack.concatenate_subprogram (
25                 utl_call_stack.subprogram (
26                    the_depth)));
27     END LOOP;
28  END;
29  /

這是代碼清單2中對UTL_CALL_STACK包的幾處關鍵調用:

第9和第10行設定了FOR迴圈,利用DYNAMIC_DEPTH函數,從堆棧中的最後一個元素開始,以反序遍曆到堆棧中的第一個元素。

第14行調用LEXICAL_DEPTH函數來顯示堆棧中每個元素的深度。

第20行和21調用UNIT_LINE來獲得程式單元的行號。

第24和第25行先調用SUBPROGRAM來獲得堆棧中當前深度的元素。然後用CONCATENATE_SUBPROGRAM獲得子程式的完整的帶限定的名字。

然後我在pkg.do_stuff過程使用了代碼清單2中的format_call_stack_12c,並且執行了這個過程,如代碼清單3所示。

代碼清單 3:  pkg.do_stuff 程序呼叫了 format_call_stack_12c 過程 
SQL> CREATE OR REPLACE PACKAGE pkg
  2  IS
  3     PROCEDURE do_stuff;
  4  END;
  5  /

SQL> CREATE OR REPLACE PACKAGE BODY pkg
  2  IS
  3     PROCEDURE do_stuff
  4     IS
  5        PROCEDURE np1
  6        IS
  7           PROCEDURE np2
  8           IS
  9              PROCEDURE np3
10              IS
11              BEGIN
12                 format_call_stack_12c;
13              END;
14           BEGIN
15              np3;
16           END;
17        BEGIN
18           np2;
19        END;
20     BEGIN
21        np1;
22     END;
23  END;
24  /

SQL> BEGIN
  2     pkg.do_stuff;
  3  END;
  4  /

LexDepth  Depth   LineNo     Name
———————   ——————— ————————   ——————————————————————————
0         6       2          __anonymous_block
1         5      21          PKG.DO_STUFF
2         4      18          PKG.DO_STUFF.NP1
3         3      15          PKG.DO_STUFF.NP1.NP2
4         2      12          PKG.DO_STUFF.NP1.NP2.NP3
0         1      12          FORMAT_CALL_STACK_12C

下一步我將用UTL_CALL_STACK包來顯示拋出當前異常的程式單元名字和所在行號。在代碼清單4中,我建立並且執行了一個名為BACKTRACE_TO的函數,它“隱藏”了對UTL_CALL_STACK子程式的調用。在每次對BACKTRACE_UNIT和BACKTRACE_LINE的調用當中,我都傳入了ERROR_DEPTH函數的傳回值。

代碼清單 4: backtrace_to 函數調用了 UTL_CALL_STACK 子程式 

SQL> CREATE OR REPLACE FUNCTION backtrace_to
  2     RETURN VARCHAR2
  3  IS
  4  BEGIN
  5     RETURN
  6        utl_call_stack.backtrace_unit (
  7           utl_call_stack.error_depth)
  8        || ‘ line ‘
  9        ||
10        utl_call_stack.backtrace_line (
11           utl_call_stack.error_depth);
12  END;
13  /

SQL> CREATE OR REPLACE PACKAGE pkg1
  2  IS
  3     PROCEDURE proc1;
  4     PROCEDURE proc2;
  5  END;
  6  /

SQL> CREATE OR REPLACE PACKAGE BODY pkg1
  2  IS
  3     PROCEDURE proc1
  4     IS
  5        PROCEDURE nested_in_proc1
  6        IS
  7        BEGIN
  8           RAISE VALUE_ERROR;
  9        END;
10     BEGIN
11        nested_in_proc1;
12     END;
13
14     PROCEDURE proc2
15     IS
16     BEGIN
17        proc1;
18     EXCEPTION
19        WHEN OTHERS THEN RAISE NO_DATA_FOUND;
20     END;
21  END pkg1;
22  /

SQL> CREATE OR REPLACE PROCEDURE proc3
  2  IS
  3  BEGIN
  4     pkg1.proc2;
  5  END;
  6  /

SQL> BEGIN
  2     proc3;
  3  EXCEPTION
  4     WHEN OTHERS
  5     THEN
  6        DBMS_OUTPUT.put_line (backtrace_to);
  7  END;
  8  /

HR.PKG1 line 19

注意,錯誤回溯堆棧中的深度值和呼叫堆疊的深度值不同。對呼叫堆疊而言,1是堆棧的頂部(當前執行的子程式)。對錯誤回溯堆棧去而言,My Code出錯之處是用ERROR_DEPTH找到的,而不是1。

有了UTL_CALL_STACK,我不再需要解析完整的回溯字串,而用DBMS_UTILITY.FORMAT_ERROR_BACKTRACE就不得不這麼做。相反,我可以精確地發現,顯示並且記錄我所需要的關鍵資訊。

關於UTL_CALL_STACK要記住的有幾點:

編譯器的最佳化可能會改變詞彙,動態和回溯的深度,因為最佳化過程可能意味著子程式調用被跳過。

如果越過了遠程調用的邊界,UTL_CALL_STACK就不被支援。例如,proc1 調用遠程過程remoteproc2,那麼remoteproc2利用UTL_CALL_STACK將得不到proc1的相關資訊。

詞彙單元的資訊不是通過UTL_CALL_STACK來得到的。你可以利用PL/SQL的條件編譯來得到該資訊。

UTL_CALL_STACK是非常方便的工具,但是在現實世界中,你可能需要在這個包的子程式之外再建立一些自己的工具代碼。我建立了一個協助包,裡面有些工具,我想你可能會覺得有用。你可以在12c_utl_call_stack_helper.sql 和 12c_utl_call_stack_helper_demo.sql檔案中找到代碼。

http://www.oracle.com/technetwork/issue-archive/2014/14-jan/o14plsql-2041787.zip

更好的診斷,更好的編程

三個DBMS_UTILITY函數(DBMS_UTILITY.FORMAT_CALL_STACK, DBMS_UTILITY.FORMAT_ERROR_STACK, 和 DBMS_UTILITY.FORMAT_ERROR_ BACKTRACE) 一直都是PL/SQL代碼中診斷和解決問題的好幫手。UTL_CALL_STACK包認識到這個資料的重要性,往前跨出了一大步,給了PL/SQL開發人員訪問更多的深層的有用的資訊的途徑。

 

【轉】Oralce PL/SQL 堆棧資訊追蹤

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.