StackTrace栈跟踪

栈追踪(Stack Trace)

在本主题中,你将了解一个有助于调试应用程序的重要功能:栈追踪(Stack Trace)。它显示了应用程序中直到产生栈追踪消息为止的调用栈(Call Stack),当应用程序抛出错误时,这条信息就会在你的 IDE 中显示出来。我们将分析一个示例,并学习栈追踪消息能告诉我们什么、如何解读它。你还将学习如何在程序运行期间的任意时刻获取栈追踪。

如你所知,调用栈是一种后进先出(LIFO)的数据结构,提供了方法的执行顺序信息。它由多个 栈帧(Stack Frame) 组成,每个栈帧代表一个方法。


栈追踪详解

在学习不同类型的异常时,我们也讨论了不同的抛出异常方式。现在,是时候探索这些异常背后的消息了。请看以下示例:

import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    val input: String = scanner.nextLine()
    val number = input.toInt() // 此处可能发生异常!
    println(number + 1)
}

如果我们输入一个单词而不是数字(例如 "Kotlin"),应用程序将抛出错误,并显示如下的栈追踪信息:

Exception in thread "main" java.lang.NumberFormatException: For input string: "Kotlin"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:668)
	at java.base/java.lang.Integer.parseInt(Integer.java:784)
	at MainKt.main(Main.kt:6)
	at MainKt.main(Main.kt)
栈追踪解读

我们先来看看最上面的一行,其中包含三个重要信息:

  1. 异常发生的线程:比如 "main" 线程是应用程序启动时创建的主线程。

  2. 异常类型的类:这里是 NumberFormatException,属于 java.lang 包。

  3. 异常消息:指出发生异常的原因(这里是输入了字符串 "Kotlin")。

接下来,我们看下面的几行。倒数第二行指向程序中导致异常发生的那一行代码:input.toInt()。它调用了如下方法:

public actual inline fun String.toInt(): Int = java.lang.Integer.parseInt(this)

这个方法进一步调用了 Integer 类中的重载方法:

public static int parseInt(String s) throws NumberFormatException {
   return parseInt(s, 10);
}

而在底层的 parseInt(String s, int radix) 中,我们看到抛出异常的语句如下:

if (digit < 0 || result < multmin) {
    throw NumberFormatException.forInputString(s, radix);
}

异常消息的具体构建方式如下:

static NumberFormatException forInputString(String s, int radix) {
    return new NumberFormatException("For input string: \"" + s + "\"" +
                                     (radix == 10 ?
                                      "" :
                                      " under radix " + radix));
}

修改代码:调用另一个方法

我们稍作修改,把部分代码放到一个单独的方法中:

import java.util.*

fun main() {
    val scanner = Scanner(System.`in`)
    val input = scanner.nextLine()
    demo(input)
}

fun demo(input: String) {
    val number = input.toInt() // 此处可能发生异常!
    println(number + 1)
}

这时,如果输入 "Kotlin",栈追踪信息将多出一行,表示调用了 demo(input) 方法:

Exception in thread "main" java.lang.NumberFormatException: For input string: "Kotlin"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:668)
	at java.base/java.lang.Integer.parseInt(Integer.java:784)
	at MainKt.demo(Main.kt:10)
	at MainKt.main(Main.kt:6)
	at MainKt.main(Main.kt)
调用栈结构

栈追踪反映了调用栈的结构。由于调用栈是后进先出(LIFO),程序最开始执行的 main() 方法在最底部,是最后打印出来的。


主动获取栈追踪

除了抛出异常时自动生成栈追踪,你还可以主动获取栈追踪,例如通过:

for (element in Thread.currentThread().stackTrace) {
    println(element)
}

这段代码获取了当前线程的栈追踪,返回的是一个 StackTraceElement 数组。你也可以使用 Throwable().stackTraceThrowable().printStackTrace()


StackTraceElement 类简介

每个栈帧在 Kotlin 中由 StackTraceElement 表示。如果你在循环中写:

println(element.className)

输出如下:

java.lang.Thread
MainKt
MainKt
MainKt

StackTraceElement 类还提供了其他方法,如:

  • getMethodName():获取方法名

  • getLineNumber():获取代码行号

建议你查阅官方文档深入了解这个类的所有方法。


最后一个例子:主动打印栈追踪

import java.util.Scanner

fun main() {
    val scanner = Scanner(System.`in`)
    val input = scanner.nextLine()
    demo(input)
}

fun demo(input: String) {
    for (element in Thread.currentThread().stackTrace) {
        println(element)
    }
    val number = input.toInt() // 此处可能发生异常!
    println(number + 1)
}

如果输入一个数字且没有异常发生,输出类似:

java.base/java.lang.Thread.getStackTrace(Thread.java:1610)
MainKt.demo(Main.kt:10)
MainKt.main(Main.kt:6)

总结

本节你学习了栈追踪:Kotlin 提供的一个强大调试工具,帮助你了解程序的执行步骤,并快速定位错误。栈追踪初看可能令人困惑,但掌握之后,它是分析异常最有力的帮手之一。

### 使用 `std::stacktrace` 在 CMake 项目中的方法 为了在 CMake 项目中使用 `std::stacktrace`,需要确保编译器支持 C++17 或更高版本。现代标准库实现通常会提供实验性的跟踪功能。 #### 启用 C++17 支持 设置项目的最低 CMake 版本并启用 C++17 标准: ```cmake cmake_minimum_required(VERSION 3.8) project(MyProject) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) ``` #### 编写代码以捕获堆跟踪信息 下面是一个简单的例子来展示如何获取当前调用的信息[^1]: ```cpp #include <iostream> #include <string_view> int main() { try { throw std::runtime_error("An error occurred"); } catch (const std::exception& e) { #if defined(__has_include) && __has_include(<source_location>) auto loc = std::source_location::current(); std::cout << "Exception caught at function: " << loc.function_name() << ", line: " << loc.line() << '\n'; #endif #ifdef _GLIBCXX_STACKTRACE const char* stack_trace_str = std::__cxx11::get_stacktrace().c_str(); std::cout << "\nStack Trace:\n" << stack_trace_str; #else std::cout << "\nStack trace functionality is not available on this platform."; #endif return EXIT_FAILURE; } return EXIT_SUCCESS; } ``` 需要注意的是,并不是所有的平台都实现了 `std::stacktrace` 功能;因此,在实际应用之前应该先检测其可用性。上述代码片段展示了当异常被捕获时打印出错误位置以及尝试显示堆追踪的方法[^2]。 对于某些特定的操作系统或编译环境可能还需要额外配置才能使此特性正常工作。例如 GCC 和 Clang 提供了 `-fstack-trace` 参数用于生成更详细的调试信息[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值