C 语言中捕获段错误并打印出最后行数

因客户需要, 需要在我们的L1 程序发生段错误segmentation fault后, 要用消息的方式让高层知道。以前每次都要提供core dump 文件, 有些同学又不会看。

于是想到用信号函数来处理segmentation fault 信号, 并在这个时候发消息给L2。 另外为了方便定位, 我们需要自己知道是挂在哪一行了。

经过研究, 以下是可以直接用的代码:

1. C 代码中注册段错误的回调函数。

#include <iostream>
#include <execinfo.h>

 static void printStackTrace() {
    void* callstack[10];
    int frames = backtrace(callstack, 10);
    char** strs = backtrace_symbols(callstack, frames);

    FILE* file = fopen("segmentationFaultInfo.txt","w"); 
    if (file == NULL) { 
        return -1;
    }
    std::cout << "Stack trace:" << std::endl;
    for (int i = 0; i < frames; ++i) {
        std::cout << strs[i] << std::endl;
        fprintf(file, "%s\n", strs[i]); // 存到文件后续解析
    }

    fclose(file);
    free(strs);
    system("./parseSegmentationFault.sh"); // 解析行数
}

static void sig_handler(int signum) 
{
    // phy thread is running in background, this will be triggered by "kill -2 {pid}"
    if (SIGINT == signum )
        exit_task_g = 1;
    else if (SIGSEGV == signum) {
        printf("\nSegmentation Fault caught!\n");

        printStackTrace(); // 打印堆栈信息
    
        exit(1);
    }
}

int main(int argc, char **argv)
{
    signal(SIGSEGV, sig_handler); // 注册段错误信号

        uint8* p = NULL;

       *p = 1; // 构造的段错误代码。

}

2. 写一个脚本解析是挂在哪一行了。

parseSegmentationFault.sh

appname=phycApp  // 可执行文件名

logFile=./segmentationFaultInfo.txt

parseLog()
{
    echo "Start to pase segmeationFaultInfo:"
    cat $logFile
    addrLines=`cat $logFile|grep $appname`
    
    while IFS= read -r line; do
        tmpAddr=`echo $line|sed "s/.*\[\(0x.*\)\].*/\1/g"`
        echo "[addr $tmpAddr]:"
        addr2line -Cif -e $appname $tmpAddr

    done <<< "$addrLines"
    
}

if [ -e $logFile ];then
    parseLog
else
    echo "Error: $logFile is not exist !!!"
fi
 

3. 运行程序后可以打印出具体的行:

Segmentation Fault caught!
Stack trace:
./dpdk_bphy_phy() [0x440058]
linux-vdso.so.1(__kernel_rt_sigreturn+0) [0xffff819d05f4]
./dpdk_bphy_phy() [0x406c68]
/lib64/libc.so.6(__libc_start_main+0xe4) [0xffff81361024]
./dpdk_bphy_phy() [0x40a1b8]
Start to pase segmeationFaultInfo:
./dpdk_bphy_phy() [0x440058]
linux-vdso.so.1(__kernel_rt_sigreturn+0) [0xffff819d05f4]
./dpdk_bphy_phy() [0x406c68]
/lib64/libc.so.6(__libc_start_main+0xe4) [0xffff81361024]
./dpdk_bphy_phy() [0x40a1b8]
[addr 0x440058]:
printStackTrace
/data/jenkins/workspace/FM_5_DD_PHy@2/f95_nr_bphy/nr_phy_arm/build/../src/_AppMain.cpp:229
sig_handler
/data/jenkins/workspace/FM_5_DD_PHY_@2/f95_nr_bphy/nr_phy_arm/build/../src/_AppMain.cpp:255
sig_handler
/data/jenkins/workspace/FM_5_DD_PHY_@2/f95_nr_bphy/nr_phy_arm/build/../src/_AppMain.cpp:247
[addr 0x406c68]:
main
/data/jenkins/workspace/FM_5_DD_PHY_@2/_nr_bphy/nr_phy_arm/build/../src/_AppMain.cpp:3378  (这就是空指针那行)
[addr 0x40a1b8]:
_start
/home/jenkins/workspace/BuildToolchainAARCH64_GCC10/toolchain/gits/glibc/csu/../sysdeps/aarch64/start.S:95

<think>我们正在讨论的是Android构建系统(Soong)中的Android.bp文件。用户询问如何在bp文件中打印log。根据之前的对话,我们知道bp文件是BluePrint文件,用于描述Android模块的构建规则。在Soong构建系统中,打印日志通常不是通过bp文件直接完成的,而是通过修改构建系统的Go语言代码(如Soong)或使用构建规则中的命令来实现。 但是,用户可能指的是在构建过程中输一些信息,例如在构建模块时打印自定义消息。在Android.bp文件中,通常不会直接包含打印日志的语句,因为bp文件是声明式的,不包含命令式操作。不过,我们可以通过一些间接方式实现类似效果。 参考之前的引用,用户提到了在kali中使用Zmap的日志选项,以及Android.bp中prebuilt_etc模块的声明。这些与当前问题关系不大,但我们可以从构建系统的角度来思考。 在Soong构建系统中,如果想在构建时输信息,主要有以下几种方式: 1. **使用`println`在Go模块中打印**:如果用户正在编写自定义的Soong模块(在.go文件中),可以使用Go语言的`println`或`fmt.Println`函数。但这需要修改Go代码,并重新编译构建系统。 2. **通过shell命令输**:在`android_binary`或`genrule`等模块中,可以在命令中使用shell命令来输日志。例如,在`genrule`的`cmd`中使用`echo`。 3. **利用构建系统的日志机制**:Soong本身有日志系统,可以通过环境变量控制日志级别。但对于在bp文件中直接打印,不支持。 由于用户明确提到“在bp文件中打印log”,我们重点考虑第二种方法,因为bp文件本身不支持命令式的日志打印,但某些模块允许执行命令。 具体实现: 使用`genrule`模块来执行命令,并在命令中使用`echo`输日志。 示例Android.bp文件: ```python genrule { name: "print_log_example", out: ["dummy_output"], cmd: "echo 'This is a log message from bp file' > /dev/null && touch $(out)", // 这里我们只是打印消息,然后创建一个空文件作为输,以避免真正产生文件 // 注意:构建系统要求每个genrule必须有输 } ``` 然而,上述示例中的`echo`命令的输默认不会显示在终端,除非构建系统在详细模式下运行。为了让日志输可见,可以: - 使用`bazel build`(如果使用Bazel)或`m`/`mm`命令时增加日志级别,例如`m -j print_log_example`可能不会显示,但使用`m -j PRINT_LOGS=1`(需要构建系统支持)或修改构建系统的日志级别。 另一种方法是让构建系统始终显示命令输: 在Soong中,默认情况下,构建命令的输是被捕获的,只有在命令失败或指定详细日志时才会显示。我们可以通过设置环境变量来控制: ```python genrule { name: "print_log_example", out: ["dummy_output"], cmd: "echo 'This is a log message from bp file'; touch $(out)", } ``` 然后构建时使用: ```bash m -j print_log_example 2>&1 | grep -i 'log message' ``` 但是,这依赖于在命令行中过滤输。 更直接的方法是,在构建时使用`TARGET_BUILD_VARIANT=eng`或`userdebug`,这样默认会显示更多信息。 另外,Soong提供了`console`模块,专门用于在构建时输信息到控制台。 在Android.bp中,可以这样写: ```python console { name: "my_console_message", message: "Hello, this is a log from bp file", } ``` 但是,请注意:`console`模块并不是标准模块,可能只在某些Android版本中存在(如AOSP中的development/build目录下可能有相关定义)。如果没有,则需要自定义Soong模块。 自定义Soong模块来实现日志打印: 如果上述方法都不满足,我们可以创建一个自定义的Soong模块,在Go代码中实现打印。 步骤: 1. 在合适的目录(如`build/soong`下的某个目录)创建新的Go文件,例如`custom_logger.go`。 2. 定义模块类型和属性。 3. 在生成规则的方法中打印日志。 示例代码(`custom_logger.go`): ```go package custom import ( "android/soong/android" "android/soong/bazel" "fmt" ) func init() { android.RegisterModuleType("custom_logger", customLoggerFactory) } type customLogger struct { android.ModuleBase properties struct { Message string } } func customLoggerFactory() android.Module { module := &customLogger{} module.AddProperties(&module.properties) android.InitAndroidModule(module) return module } func (c *customLogger) GenerateAndroidBuildActions(ctx android.ModuleContext) { fmt.Println(c.properties.Message) // 或者使用ctx.ModuleErrorf()等构建上下文的方法 } ``` 然后在Android.bp中使用: ```python custom_logger { name: "my_logger", message: "This is a custom log message", } ``` 这样,在构建包含`my_logger`的目标时,这条消息就会打印。 但是,这种方法需要修改构建系统代码,并且需要重新编译构建系统(soong_ui等),适合系统开发者。 **总结**: - 对于简单的日志输,可以使用`genrule`模块的`cmd`中的`echo`命令,并确保构建时日志级别足够(如使用`m -j`时加上`showcommands`)。 - 对于更复杂的需求,可以自定义Soong模块。 相关问题: §1. 如何在Soong构建系统的Go模块中打印日志? §2. 如何控制Soong构建系统的日志详细级别? §3. 在Android.bp中,`genrule`模块的常见用途有哪些? §4. 如何自定义Soong模块? §5. 如何让`genrule`模块的命令输在构建时可见?[^1][^2] </think>在 Android.bp 文件中实现日志打印,需要通过 Soong 构建系统的特殊机制实现。以下是具体方法和注意事项: ### 🔧 日志打印方法 #### 1. **使用 `bazel` 日志指令** ```python android_app { name: "my_app", manifest: "AndroidManifest.xml", // 添加日志输指令 bazel: { cmd: "echo '[LOG] Manifest path validation passed' >&2", // 输到标准错误流 output_tags: ["verbose"], // 标记为详细日志 }, } ``` - `>&2` 将输重定向到 **标准错误流**(构建系统默认捕获) - `output_tags` 控制日志显示级别(`verbose`/`info`/`warning`) #### 2. **通过 `genrule` 模块打印** ```python genrule { name: "log_generator", out: ["dummy.log"], // 虚拟输文件 cmd: "echo 'Build started at $(date)' > $(out); cat $(out) >&2", // 同时写入文件和标准错误流 } ``` #### 3. **启用详细构建日志** 在构建命令中添加参数: ```bash m -j my_app BP_DEBUG=1 # 显示bp文件处理日志 m -j my_app SOONG_VERBOSE=1 # 显示Soong详细日志 ``` ### ⚠️ 关键注意事项 1. **输流选择** - `>&2`(标准错误)优先于 `>`(文件),构建系统默认捕获 stderr - 避免输到 stdout,否则可能被构建系统过滤 2. **日志级别控制** ```python product_variables: { eng: { bazel: { cmd: "echo '[DEBUG] Advanced logs' >&2" }, }, userdebug: { // 减少日志量 } } ``` 3. **性能影响** $$ \text{日志过量} \implies \text{构建时间} \propto \text{日志行数} $$ 建议: - 仅在 `debuggable: true` 模块启用 - 使用 `select()` 按条件输: ```python cmd: select({ "debug_build": "echo '[LOG] Debug mode active' >&2", "//conditions:default": ":" // 空命令 }), ``` ### 📊 日志查看技巧 1. **定位日志文件** ```bash # Soong主日志 less out/soong.log # 模块级日志 grep -r '\[LOG\]' out/.module* ``` 2. **实时监控** ```bash m -j my_app 2>&1 | grep '\[LOG\]' ``` ### 💡 最佳实践 ```python android_app { name: "logger_demo", manifest: "AndroidManifest.xml", bazel: { cmd: "echo '[LOG] Using manifest: " + manifest + "' >&2", output_tags: ["info"], }, // 仅调试版本打印 target: { android: { debuggable: true, }, }, } ``` --- ### 🔍 相关问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值