器→工具, 编程语言

Python堆栈跟踪信息traceback

钱魏Way · · 7 次浏览

Traceback是在Python中与经常遇到,特别是当你代码中有错误时,执行时会返回Traceback信息。在学习Traceback时,可以一起学习下trace, tracemalloc。

trace, tracemalloc, 和 traceback 都是 Python 标准库中用于调试和分析程序的不同工具,但它们各自有不同的用途和功能。

  • traceback:主要用于处理和打印异常的堆栈跟踪信息。
  • trace:用于跟踪程序的执行流程,生成详细的执行日志,支持覆盖率分析。
  • tracemalloc:用于跟踪内存分配,帮助分析内存使用情况和检测内存泄漏。

traceback:打印或格式化堆栈追踪

traceback 是 Python 标准库中的一个模块,用于提取、格式化和打印 Python 程序中的异常信息。这个模块提供了多种工具来处理和输出异常堆栈跟踪信息,有助于调试和诊断程序中的错误。

主要功能

  • 提取异常信息:提供函数来提取异常的详细信息,包括堆栈跟踪。
  • 格式化异常信息:将异常信息格式化为字符串,便于记录和输出。
  • 打印异常信息:直接打印异常信息到标准输出或自定义的输出流。
  • 处理异常信息:提供工具来操作和处理异常信息,以便进行进一步分析。

特性与优势

  • 详细的堆栈跟踪:traceback 模块提供了详细的堆栈跟踪信息,有助于识别程序中的错误位置和原因。
  • 灵活的格式化选项:可以将异常信息格式化为字符串,方便记录和输出,也可以提取和分析堆栈帧的信息。
  • 方便的调试工具:traceback 模块简化了异常处理和调试过程,提供了多种工具来获取和格式化异常信息。
  • 支持多种输出方式:允许将异常信息打印到不同的文件对象,支持自定义输出需求。

主要函数

traceback.format_exc()

traceback.format_exc() 函数用于获取最近的异常的堆栈跟踪信息,并将其格式化为字符串。这个函数通常在 except 块中使用。

示例:

import traceback

try:
    1 / 0
except ZeroDivisionError:
    error_message = traceback.format_exc()
    print("Exception occurred:")
    print(error_message)

在这个示例中,当 ZeroDivisionError 异常发生时,traceback.format_exc() 捕获并格式化堆栈跟踪信息,输出异常详细信息。

Exception occurred:
Traceback (most recent call last):
  File "D:\CodeHub\playground\my_script.py", line 4, in <module>
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero

traceback.format_exception(etype, value, tb)

traceback.format_exception() 函数用于将异常类型 (etype)、异常值 (value) 和异常回溯 (tb) 格式化为字符串列表。这个函数常用于获取异常的详细格式化信息。

示例:

import traceback
import sys

try:
    1 / 0
except Exception as e:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    formatted_exception = traceback.format_exception(exc_type, exc_value, exc_traceback)
    print("Formatted exception:")
    print("".join(formatted_exception))

sys.exc_info() 返回当前异常的类型、值和回溯信息,这些信息可以传递给 traceback.format_exception() 以获取格式化的异常信息。

Formatted exception:
Traceback (most recent call last):
  File "D:\CodeHub\playground\my_script.py", line 5, in <module>
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero

traceback.format_tb(tb)

traceback.format_tb() 函数用于将异常回溯 (tb) 格式化为字符串列表。这可以用于自定义的堆栈跟踪格式化需求。

示例:

import traceback
import sys

try:
    1 / 0
except Exception as e:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    formatted_tb = traceback.format_tb(exc_traceback)
    print("Formatted traceback:")
    print("".join(formatted_tb))

在这个示例中,traceback.format_tb() 用于格式化回溯信息,不包括异常类型和消息。

Formatted traceback:
  File "D:\CodeHub\playground\my_script.py", line 5, in <module>
    1 / 0
    ~~^~~

traceback.extract_tb(tb)

traceback.extract_tb() 函数用于将异常回溯 (tb) 提取为 FrameSummary 对象的列表。每个 FrameSummary 对象包含了堆栈帧的信息,如文件名、行号和代码行。

示例:

import traceback
import sys

try:
    1 / 0
except Exception as e:
    exc_type, exc_value, exc_traceback = sys.exc_info()
    extracted_tb = traceback.extract_tb(exc_traceback)
    print("Extracted traceback:")
    for frame in extracted_tb:
        print(f"File: {frame.filename}, Line: {frame.lineno}, Function: {frame.name}")
        print(f"Code: {frame.line}")

traceback.extract_tb() 允许提取回溯信息,并以更详细的形式输出堆栈帧的信息。

Extracted traceback:
File: D:\CodeHub\playground\my_script.py, Line: 5, Function: <module>
Code: 1 / 0

traceback.print_exc() 函数用于将最近的异常的堆栈跟踪信息直接打印到指定的文件对象(如 sys.stdout 或 sys.stderr)。如果未指定文件对象,则默认打印到标准错误流。

示例:

import traceback

try:
    1 / 0
except Exception:
    print("Exception details:")
    traceback.print_exc()

在这个示例中,traceback.print_exc() 将异常堆栈跟踪信息直接打印到标准错误流。

Exception details:
Traceback (most recent call last):
  File "D:\CodeHub\playground\my_script.py", line 4, in <module>
    1 / 0
    ~~^~~
ZeroDivisionError: division by zero

使用示例

以下是一个完整的示例,展示如何使用 traceback 模块处理和输出异常信息:

import traceback
import sys

def divide_numbers(a, b):
    return a / b

try:
    divide_numbers(1, 0)
except Exception:
    print("An error occurred:")
    # 打印完整的堆栈跟踪信息到标准错误流
    traceback.print_exc()
    # 获取并格式化异常信息
    formatted_exception = traceback.format_exc()
    print("Formatted exception:")
    print(formatted_exception)
    # 提取回溯信息并输出
    exc_type, exc_value, exc_traceback = sys.exc_info()
    extracted_tb = traceback.extract_tb(exc_traceback)
    print("Extracted traceback:")
    for frame in extracted_tb:
        print(f"File: {frame.filename}, Line: {frame.lineno}, Function: {frame.name}")
        print(f"Code: {frame.line}")

输出内容:

An error occurred:
Formatted exception:
Traceback (most recent call last):
  File "D:\CodeHub\playground\my_script.py", line 8, in <module>
    divide_numbers(1, 0)
  File "D:\CodeHub\playground\my_script.py", line 5, in divide_numbers
    return a / b
           ~~^~~
ZeroDivisionError: division by zero

Extracted traceback:
File: D:\CodeHub\playground\my_script.py, Line: 8, Function: <module>
Code: divide_numbers(1, 0)
File: D:\CodeHub\playground\my_script.py, Line: 5, Function: divide_numbers
Code: return a / b
Traceback (most recent call last):
  File "D:\CodeHub\playground\my_script.py", line 8, in <module>
    divide_numbers(1, 0)
  File "D:\CodeHub\playground\my_script.py", line 5, in divide_numbers
    return a / b
           ~~^~~
ZeroDivisionError: division by zero

trace:跟踪代码执行

trace 是 Python 标准库中的一个模块,用于跟踪程序的执行,包括行级跟踪和函数调用跟踪。它可以帮助开发者了解程序的执行流程、调试代码、以及分析代码的覆盖率。trace 模块适用于详细的代码执行分析和测试覆盖率的生成。

主要功能

  • 代码行级跟踪:trace 模块可以记录程序中每一行的执行情况,帮助开发者理解程序的执行流程。
  • 函数调用跟踪:记录函数调用的发生情况,包括函数的进入和退出,帮助调试复杂的函数调用关系。
  • 代码覆盖率分析:trace 可以生成代码覆盖率报告,显示哪些代码行被执行了,哪些没有被执行,用于测试覆盖率的评估。

特性与优势

  • 详细的代码执行跟踪:trace 模块可以详细跟踪每一行代码的执行情况和函数调用,帮助开发者调试复杂的程序。
  • 覆盖率分析:生成代码覆盖率报告,帮助评估测试用例的有效性和代码的测试覆盖范围。
  • 灵活的配置:支持多种跟踪和分析选项,可以根据需求灵活配置。

行级跟踪

行级跟踪可以用来了解程序执行时哪些行被执行,哪些没有被执行。通过 trace 模块,可以在程序运行时追踪每一行代码的执行情况。

import trace

def example_function():
    print("This is a test function.")
    for i in range(3):
        print(i)

# 创建 Trace 对象
tracer = trace.Trace(trace=True)
tracer.run('example_function()')

在这个示例中,trace.Trace(trace=True) 创建了一个跟踪对象,它会跟踪每一行代码的执行,并打印出执行的行。

输出内容:

--- modulename: my_script, funcname: <module>
<string>(1)
 --- modulename: my_script, funcname: example_function
my_script.py(4):      print("This is a test function.")
This is a test function.
my_script.py(5):      for i in range(3):
my_script.py(6):          print(i)
0
my_script.py(5):      for i in range(3):
my_script.py(6):          print(i)
1
my_script.py(5):      for i in range(3):
my_script.py(6):          print(i)
2
my_script.py(5):      for i in range(3):

函数调用跟踪

trace 还支持函数调用的跟踪,可以记录函数的调用情况以及调用的堆栈信息:

import trace

def foo():
    print("In foo")
    bar()

def bar():
    print("In bar")

# 创建 Trace 对象,启用函数调用跟踪
tracer = trace.Trace(count=True, trace=True)
tracer.run('foo()')

这个示例中,trace.Trace(count=True, trace=True) 创建了一个跟踪对象,它会记录函数调用的堆栈信息和每个函数的调用情况。

输出内容:

--- modulename: my_script, funcname: <module>
<string>(1)
 --- modulename: my_script, funcname: foo
my_script.py(4):      print("In foo")
In foo
my_script.py(5):      bar()
 --- modulename: my_script, funcname: bar
my_script.py(8):      print("In bar")
In bar

代码覆盖率分析

trace 模块可以生成代码覆盖率报告,显示哪些行被执行了。这个功能对测试覆盖率的评估特别有用。

import trace

def example_function():
    print("This is a test function.")
    for i in range(3):
        print(i)

# 创建 Trace 对象,启用代码覆盖率分析
tracer = trace.Trace(count=True, trace=False)
tracer.run('example_function()')

# 获取覆盖率数据
results = tracer.results()
results.write_results(show_missing=True, summary=True)

在这个示例中,trace.Trace(count=True, trace=False) 创建了一个跟踪对象,启用代码覆盖率分析但不跟踪每一行代码。results.write_results() 方法生成覆盖率报告,显示哪些代码行被执行了,哪些没有被执行。

输出内容:

This is a test function.
0
1
2
lines   cov%   module   (path)
    9    33%   my_script   (D:\CodeHub\playground\my_script.py)

主要方法和功能

  • Trace(count=False, trace=False):创建 Trace 对象。count 和 trace 参数用于控制是否启用行级跟踪和函数调用跟踪。
    • count:启用代码覆盖率分析。
    • trace:启用行级跟踪。
  • run(statement):执行指定的 Python 语句,并根据创建 Trace 对象时的设置进行跟踪。
  • statement:要执行的 Python 语句。
  • results():返回一个TraceResults 对象,包含跟踪结果和覆盖率数据。
  • write_results(show_missing=False, summary=False):打印代码覆盖率报告。show_missing 参数控制是否显示未执行的代码行,summary 参数控制是否显示汇总信息。

tracemalloc:跟踪内存分配

tracemalloc 是 Python 标准库中的一个模块,用于跟踪内存分配情况。它可以帮助开发者监测程序的内存使用情况、查找内存泄漏和性能瓶颈,以及优化内存管理。tracemalloc 模块提供了详细的内存分配信息,包括内存分配的调用栈,从而帮助开发者更深入地了解程序的内存使用。

主要功能

  • 内存分配跟踪:跟踪程序运行过程中内存的分配情况,包括每种类型的对象和总内存使用量。
  • 调用栈信息:提供内存分配的调用栈信息,可以帮助开发者了解内存分配发生的具体位置。
  • 内存泄漏检测:通过分析内存分配情况,帮助识别可能的内存泄漏。
  • 内存使用优化:通过跟踪内存使用情况,优化代码以减少不必要的内存分配。

特性与优势

  • 详细的内存跟踪:提供内存分配的详细信息,包括调用栈和分配大小,帮助深入了解内存使用情况。
  • 内存泄漏检测:通过比较内存快照,帮助识别和分析内存泄漏问题。
  • 性能分析:了解内存使用情况,优化程序性能和内存管理。
  • 灵活的分析方法:支持多种内存分析方法,包括按代码行、文件名等进行统计和比较。

启动和停止内存跟踪

使用 tracemalloc 模块的基本步骤包括启动跟踪、执行代码、停止跟踪和分析内存分配数据。

启动内存跟踪

import tracemalloc

tracemalloc.start()

tracemalloc.start() 启动内存跟踪,默认情况下会记录所有的内存分配信息。

停止内存跟踪

内存跟踪会在代码执行完成后停止,或者可以手动停止:

tracemalloc.stop()

获取内存快照

在程序运行过程中,你可以获取当前的内存快照,用于分析内存分配情况:

snapshot = tracemalloc.take_snapshot()

tracemalloc.take_snapshot() 返回一个 Snapshot 对象,包含当前内存分配的信息。

分析内存分配数据

tracemalloc 提供了分析内存分配数据的方法,包括显示最占用内存的代码行、比较不同快照等。

显示最占用内存的代码行

top_stats = snapshot.statistics('lineno')
for stat in top_stats[:10]:
    print(stat)

这段代码显示了最占用内存的前 10 行代码。

比较不同的内存快照

snapshot1 = tracemalloc.take_snapshot()
# 执行代码...
snapshot2 = tracemalloc.take_snapshot()

top_stats = snapshot2.compare_to(snapshot1, 'lineno')
for stat in top_stats[:10]:
    print(stat)

这段代码比较两个快照之间的内存分配差异,显示最明显的变化。

主要类和方法

  • start(nframe=1):启动内存跟踪。nframe 参数指定要记录的调用栈的深度,默认值为 1。
  • stop():停止内存跟踪。
  • take_snapshot():返回当前内存分配的快照,生成 Snapshot 对象。
  • statistics(aggregate_type):返回内存分配的统计信息。aggregate_type 参数指定统计信息的聚合方式,例如 ‘lineno’(按代码行聚合)或 ‘filename’(按文件名聚合)。
  • compare_to(other_snapshot, aggregate_type):比较当前快照与另一个快照之间的内存分配差异。aggregate_type 参数指定比较的聚合方式。

参考链接:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注