码迷,mamicode.com
首页 > 数据库 > 详细

【转】Oralce PL/SQL 堆栈信息追踪

时间:2017-08-17 17:18:12      阅读:1132      评论:0      收藏:0      [点我收藏+]

标签:数据库   专注   编译器   支持   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是堆栈的顶部(当前执行的子程序)。对错误回溯堆栈去而言,我的代码出错之处是用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 堆栈信息追踪

标签:数据库   专注   编译器   支持   val   用户   obj   cat   other   

原文地址:http://www.cnblogs.com/pompeii2008/p/7382640.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!