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


    总结如下:

    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开发者访问更多的深层的有用的信息的途径。

  • 相关阅读:
    嵌入式学习路线
    redhat历史以及对应的内核版本
    " Provisioning profile XXXX can't be found"
    IOS 单例模式
    【IOS】开源项目汇总(更新时间2012611)
    'release' is unavailable: not available in automatic reference counting mode.
    不要使用SBJSON(jsonframework)
    viewDidUnload 和 dealloc 的区别
    unrecognized selector sent to instance 问题的解决方法
    通过xib同样可以让UIView背景透明
  • 原文地址:https://www.cnblogs.com/pompeii2008/p/7382640.html
Copyright © 2020-2023  润新知