標籤:資料庫 專註 編譯器 支援 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 堆棧資訊追蹤