当前位置: 首页 > article >正文

【Unity3D杂谈】使用NDK命令行工具翻译Android Vitals上的内存堆栈

在Android游戏开发中,崩溃日志的分析至关重要,尤其是在Unity3D项目中,libunity.so 等动态库中的堆栈信息往往难以直接解读。本文介绍如何使用NDK提供的 addr2line 工具解析 Android Vitals 上的崩溃堆栈,以便快速定位问题。

1. addr2line 工具介绍

addr2line 是NDK提供的一个命令行工具,可将崩溃日志中的地址转换为具体的代码位置。

1.1 addr2line 命令示例

addr2line -f -e libunity.so 0x00000000001ea00c
  • -f:显示函数名称。

  • -e:指定符号文件(libunity.so)。

  • 0x00000000001ea00c:崩溃日志中的内存地址。

1.2 使用 addr2line 工具

如果 addr2line 没有在环境变量中配置,可以直接使用完整路径:

D:\Program Files\Unity\Editor\Unity 2021.3.44f1\Editor\Data\PlaybackEngines\AndroidPlayer\NDK\toolchains\aarch64-linux-android-4.9\prebuilt\windows-x86_64\bin\aarch64-linux-android-addr2line.exe

其中 aarch64 代表 ARM64 架构。

2. Unity符号库 so 文件的获取

要正确解析崩溃堆栈,需要符号化的 .so 文件。这要求在打包的时候Build Setting的Create symbol.zip开关打开。或者打包代码实现:

EditorUserBuildSettings.androidCreateSymbolsZip = true

将zip解压后会获得一些so符号库,比如:

  • Crash/arm64-v8a/libunity.so

  • Crash/armeabi-v7a/libunity.so

3. 堆栈解析示例

3.1 崩溃堆栈

  #00  pc 0x000000000038858c  /data/app/com.demo.examples/lib/arm64/libunity.so
  #01  pc 0x00000000003885c4  /data/app/com.demo.examples/lib/arm64/libunity.so
  #02  pc 0x000000000078c044  /data/app/com.demo.examples/lib/arm64/libunity.so
  #03  pc 0x0000000000786dc4  /data/app/com.demo.examples/lib/arm64/libunity.so
  at com.unity3d.player.UnityPlayer.nativeDone (Native method)
  at com.unity3d.player.UnityPlayer.shutdown (SourceFile)

3.2 addr2line 解析结果

_ZN14ConstantString7cleanupEv
_ZN14ConstantStringaSERKS_
_ZN13sorted_vectorINSt6__ndk14pairI14ConstantStringP11AssetBundleEE9erase_oneIS2_EEmRKT_
_ZN18AssetBundleManager23UnloadAssetBundleAtPathEP11AssetBundle

4. Python脚本实现自动解析

为了简化 addr2line 解析过程,可以编写一个 Python 脚本逐行解析崩溃堆栈。

4.1 解析崩溃堆栈的Python脚本

import os
import re

original_stack = """
  000000038858c  /data/app/com.demo.examples/lib/arm64/libunity.so
  #01  pc 0x00000000003885c4  /data/app/com.demo.examples/lib/arm64/libunity.so
  #02  pc 0x000000000078c044  /data/app/com.demo.examples/lib/arm64/libunity.so
  #03  pc 0x0000000000786dc4  /data/app/com.demo.examples/lib/arm64/libunity.so
  at com.unity3d.player.UnityPlayer.nativeDone (Native method)
  at com.unity3d.player.UnityPlayer.shutdown (SourceFile)
"""

valid_so_list = ["libil2cpp.so", "libunity.so", "libmain.so"]

class StackInfo:
    def __init__(self, address, arch, lib_name):
        self.address = address
        self.arch = arch
        self.libName = lib_name

def parse_stack_info():
    stackInfoList = []
    for line in original_stack.splitlines():
        if "pc" in line:
            address = re.findall(r"0x[0-9a-fA-F]+", line)[0]
            cpu_arch = "arm64" if "arm64" in line else "arm"
            lib_name = re.findall(r"lib[\w]+\.so", line)[0]
            if lib_name in valid_so_list:
                stackInfoList.append(StackInfo(address, cpu_arch, lib_name))
    return stackInfoList

def translate_stack(current_dir, address, cpu_arch, lib_name):
    addr2line_path = "D:/Program Files/Unity/Editor/Unity 2021.3.44f1/Editor/Data/PlaybackEngines/AndroidPlayer/NDK/toolchains/aarch64-linux-android-4.9/prebuilt/windows-x86_64/bin"
    addr2line_name = "./aarch64-linux-android-addr2line.exe"
    current_dir = current_dir + ("/Crash/armeabi-v7a/" if cpu_arch == "arm" else "/Crash/arm64-v8a/")
    lib_name = f'"{current_dir}{lib_name}"'
    cmd = f'{addr2line_name} -f -e {lib_name} {address}'
    os.chdir(addr2line_path)
    result = os.popen(cmd).read()
    return result.splitlines()[0]

if __name__ == '__main__':
    current_dir = os.getcwd()
    stack_info_list = parse_stack_info()
    translated_stack = "".join(translate_stack(current_dir, s.address, s.arch, s.libName) for s in stack_info_list)
    print(f"原始堆栈:\n{original_stack}\n\n翻译后:\n{translated_stack}")

4.2 脚本解析结果示例

原始堆栈:
  #00  pc 0x0000000000373714  /data/app/com.demo.examples/lib/arm64/libunity.so
  #01  pc 0x0000000000370848  /data/app/com.demo.examples/lib/arm64/libunity.so

翻译后:
_ZN14ConstantString7cleanupEv
_ZN14ConstantStringaSERKS_

5. 结论

通过 addr2line 工具结合符号库 .so,可以有效解析 Android Vitals 上的崩溃日志,帮助开发者快速定位问题。此外,使用 Python 自动化脚本可以批量解析堆栈,提高调试效率。

只要具备完整的符号表 (so 文件),这种方法适用于大多数崩溃日志的解析,极大提升了问题排查速度。


http://www.kler.cn/a/544581.html

相关文章:

  • 如何部署DeepDeepSeek-V3 大模型部署全流程解析:从云平台到本地化实践Seek—V3
  • OpenAI推出全新AI助手“Operator”:让人工智能帮你做事的新时代!
  • ConcurrentHashMap扩容
  • Spring Boot 中的事务管理:默认配置、失效场景及集中配置
  • Android原生的HighCPU使用率查杀机制
  • 基于Python的医院运营数据可视化平台:设计、实现与应用(下)
  • 使用CherryStudio、Ollama、腾讯云搭建本地个人知识库、智能体
  • LabVIEW外腔二极管激光器稳频实验
  • 【leetcode】关于循环数组的深入分析
  • Opensearch/ElasticSearch-ctx查询内容不全的问题
  • Python从0到100(八十八):LSTM网络详细介绍及实战指南
  • 基于千兆5G网关的5G急救车方案
  • git用法(简易版)
  • SSH 代理与私钥持久化:让你的开发环境不再因重启而中断
  • windows系统 从 Hugging Face网站上使用 huggingface-cli 命令下载AI大模型到本地
  • 模糊聚类分析方法:从模糊等价矩阵到动态分类
  • 【Java常用】注解与反射_2.反射
  • Windows 图形显示驱动开发-概述
  • .NET 9.0 的 Blazor Web App 项目,进度条 <progress> 组件使用注意事项
  • 相机闪光灯拍照流程分析