美文网首页
鸿蒙JS崩溃分析

鸿蒙JS崩溃分析

作者: 裴云飞 | 来源:发表于2024-07-30 00:07 被阅读0次

      当未处理的JS异常导致应用意外退出时,应用会生成对应的JS崩溃日志文件,开发者可通过错误日志查看引起崩溃的代码位置及分析应用崩溃的原因。本文将分别介绍JS崩溃分析思路以及典型分析案例。

    一、日志信息

      以下是崩溃日志信息中对应字段解释。

    Device info:emulator  // 设备信息
    Build info:emulator 5.0.0.31(SP37DEVC00E31R4P11log)  // 版本信息
    Fingerprint:983250ec758a62f9a6c4049e5d22210791fa49c6c91c321e619425045de615b1
    Module name:com.shijing.zijin  // 模块名
    Version:1.0.0 // 版本名称
    VersionCode:1000000 // 版本号
    PreInstalled:No
    Foreground:Yes // 前后台
    Pid:9827 // 进程id
    Uid:20020036
    Reason:SyntaxError // 异常原因
    Error name:SyntaxError // 异常名称
    Error message:Unexpected Text in JSON // 异常信息
    Cannot get SourceMap info, dump raw stack: // 应用安装包为release包安装时不包含sourcemap文件,JS栈通过sourcemap行列号解析会失败
    SourceCode:
                    JSON.parse(''); // 异常代码位置
                    ^
    Stacktrace:
        at anonymous (entry/src/main/ets/pages/Index.ets:18:13) // 异常代码调用栈
    

    二、异常类型

      JS异常根据不同的异常场景,在 Reason 字段进行了分类,分为Error、TypeError、SyntaxError、RangeError等错误类型。

    • 自定义 Error 类:Error 是最基本的错误类型,其他的错误类型都继承自该类型。Error 对象主要有两个重要属性 message 和 name 分别表示错误信息和错误名称。程序运行过程中抛出的异常一般都有具体的类型,Error 类型一般都是开发人员自己抛出的异常。
    • TypeError(类型错误)类:运行时最常见的异常,表示变量或参数不是预期类型。
    • SyntaxError(语法错误)类:语法错误也称为解析错误。语法错误在任何编程语言中都是最常见的错误类型,表示不符合编程语言的语法规范。
    • RangeError(边界错误)类:表示超出有效范围时发生的异常,主要的有以下几种情况:
      数组长度为负数或超长
      数字类型的方法参数超出预定义范围
      函数堆栈调用超过最大值
    • ReferenceError —— 引用错误:引用一个不存在的变量时发生的错误,每当我们创建一个变量时,变量名称都会写入一个变量存储中心中,这个变量存储中心就像键值存储一样,每当我们引用变量时,它都去存储中找到 key并提取并返回 value,如果我们要找的变量不在存储中,就会抛出 ReferenceError。
    • URI Error —— URL错误:在调用 URI 相关的方法中 URL 无效时抛出的异常,主要包括 encodeURI()、decodeURI()、encodeURIComponent()、decodeURIComponent()、escape() 和 unescape() 。

    三、堆栈

      StackTrace字段存放的是JS异常的调用栈信息,StackTrace的显示分为以下几种场景。
      1、JS调用栈可直接通过超链接跳转到对应错误代码行,栈顶即为问题第一现场,如下样例所示

    Device info:emulator
    Build info:emulator 5.0.0.31(SP37DEVC00E31R4P11log)
    Fingerprint:983250ec758a62f9a6c4049e5d22210791fa49c6c91c321e619425045de615b1
    Module name:com.shijing.zijin
    Version:1.0.0
    VersionCode:1000000
    PreInstalled:No
    Foreground:Yes
    Pid:9827
    Uid:20020036
    Reason:SyntaxError
    Error name:SyntaxError
    Error message:Unexpected Text in JSON
    SourceCode:
                    JSON.parse('');
                    ^
    Stacktrace:
        at anonymous (entry/src/main/ets/pages/Index.ets:18:13)
    

      2、异常代码调用栈包含 SourceMap is not initialized yet ,表示因SourceMap转换非常耗时,改为通过异步线程去进行初始化,导致会出现SourceMap没初始化完成就有异常产生的情况。针对这种情况增加这行日志来提示开发者。eTS栈对应编译后产物中代码行号,可通过超链接跳转到对应错误代码行。如下样例所示。

    Device info:xxx
    Build info:xxx-xxx x.x.x.xxx(xxxx)
    Fingerprint:377ef8529301363f373ce837d0bf83aacfc46112502143237e2f4026e86a0510
    Module name:com.xxx.xxx
    Version:1.0.0
    Versioncode:1000000
    PreInstalled:No
    Foreground:Yes
    Pid:6042
    Uid:20020145
    Reason:Error
    Error name:Error
    Error message:JSERROR
    Sourcecode:
                throw new Error("JSERROR");
                      ^
    Stacktrace:
    SourceMap is not initialized yet
    at anonymous (entry/src/main/ets/pages/Index.ets:49:49)
    

      3、异常代码调用栈中打印native栈,栈顶一般为libark_jsruntime.so动态库,这是因为JS异常最后都会经过虚拟机抛出。从崩溃栈从上往下找,libace_napi.z.so的上一帧一般是抛出异常的现场。如下样例所示。

    Device info:xxx
    Build info:xxx-xxx x.x.x.xxx(xxxx)
    Fingerprint:89f2b64b24d642b0fc64e3a7cf68ca39fecaa580ff5736bb9d6706ea4cdf2c93
    Module name:com.xxx.xxx
    Version:1.0.0
    VersionCode:1000000
    PreInstalled:No
    Foreground:No
    Pid:14325
    Uid:20020145
    Reason:ReferenceError
    Error name:ReferenceError
    Error message:Cannot find module 'com.xxx.xxx/entry/EntryAbility' , which is application Entry Point
    Stacktrace:
    SourceMap is not initialized yet
    #01 pc 000000000028ba3b /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #02 pc 00000000001452ff /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #03 pC 0000000000144c9f /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #04 pc 00000000001c617b /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #05 pc 00000000004c3cb7 /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #06 pc 00000000004c045f /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #07 pc 000000000038034f /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #08 pc 00000000004b2d9b /system/libó4/platformsdk/libark_jsruntime.so(bf6ea8e474ac3e417991f101e062fa90)
    #09 pc 0000000000037e7f /system/libó4/platformsdk/libace_napi.z.so(10ceafd39b5354314d2fe3059b8f9e4f)
    #10 pc 00000000000484cf /system/lib64/platformsdk/libruntime.z.so(3f6305a3843fae1de148a06eec4bd014) <- 异常抛出位置
    #11 pc 000000000004fce7 /system/libó4/platformsdk/libruntime.z.so(3f6305a3843fae1de148a06eec4bd014)
    #12 pc 000000000004e9fb /system/libó4/platformsdk/libruntime.z.so(3f6305a3843fae1de148a06eec4bd014)
    #13 pc 000000000004eb7b /system/libó4/platformsdk/libruntime.z.so(3f6305a3843fae1de148a06eec4bd014)
    #14 pc 000000000004f5c7 /system/libó4/platformsdk/libruntime.z.so(3f6305a3843fae1de148a06eec4bd014)
    #15 pc 00000000000303cf /system/lib64/platformsdk/libuiabilitykit_native.z.so(3203F4CCe84a43b519d0a731dfOdb1a3)
    

      4、如出现Cannot get Source Map info, dump raw stack信息,表示该应用为release包安装,JS栈转换eTS行列号失败,在DevEco Studio中点击链接会跳转到不正确的代码位置或不存在的代码行位置。如下样例所示。

    Device info:xxx
    Build info:xxx-xxx x.x.x.xxx(xxxx)
    Fingerprint:a370fceb59011d96e41e97bda139b1851c911012ab8c386d1a2d63986d6d226d
    Module name:com.xxx.xxx
    Version:1.0.0
    Versioncode:1000000
    PreInstalled:No
    Foreground:Yes
    Pid:39185
    Uid:20020145
    Reason:Error
    Error name:Error
    Error message:JSERROR
    Stacktrace:
    Cannot get SourceMap info, dump raw stack:
        at anonymous (entry/src/main/ets/paqes/Index.ts:49:49)
    

      Release应用都会混淆代码,去除其中的debug信息。因此无法直接通过Release应用的堆栈信息定位到源码的具体文件和行位置。对于这种情况,可以使用DevEco Studio还原堆栈,也可以通过hstack命令还原堆栈

    四、release模式编译产物

      在进行堆栈还原之前,我们需要拿到一些文件,这些文件只在构建release包才会有,构建debug包不会有这些文件。

    sourcemap文件

    release模式编译产物,文件位置:ProjectName/ModuleName/build/default/cache/default/default@CompileArkTS/esmodule/release/sourceMaps.map


    image.png
    so

    带调试信息的so数据,产物位置:ProjectName/ModuleName/build/default/intermediates/libs
    libs:带debug信息的so。
    stripped_native_libs:移除调试信息等冗余数据后的so。


    image.png
    nameCache

    反混淆映射表,release模式编译产物,产物位置:ProjectName/ModuleName/build/default/cache/default/default@CompileArkTS/esmodule/release/obfuscation


    image.png

    四、DevEco Studio还原堆栈

      DevEco Studio提供了Release应用堆栈解析功能,开发者可以利用构建产物中包含Debug信息的文件(so文件、sourcemap文件、nameCache文件等),对Release应用中C++堆栈、ArkTS堆栈以及ArkTS堆栈中混淆的方法名和文件名进行还原。
      1、单击菜单栏Code > Analyze Stack Trace,或在FaultLog页面异常堆栈信息处右键选择Analyze Stack Trace。


    image.png

      2、在弹出的Analyze Stack Trace对话框中,粘贴Release应用的异常堆栈信息。


    image.png
      3、如果当前工程为堆栈所在应用对应的工程,且存在Release构建产物,点击Start Analyze即可进行解析。如果当前工程不是堆栈所在应用对应的工程,则需要配置应用对应构建产物:勾选Unscramble stack trace, 在下方的文件选择框中,分别添加应用对应的sourcemap文件、so文件以及nameCache文件,点击Start Analyze进行转换。DevEco Studio将解析后的堆栈信息显示在右侧的输出框中。
    image.png
      在构建Release应用时,so文件默认不包含符号表信息,还原so的时候,需要使用包含符号表信息的so。在构建Release应用时生成包含符号表的so文件,需要在工程的模块级build-profile.json5文件的buildOption属性中,配置如下信息:
    "buildOption": {
      "externalNativeOptions": {
        "arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
      }
    }
    

    五、hstack还原堆栈

      hstack依赖Node环境, 需要将Node.js配置到环境变量中,还需要将SDK中的native\llvm\bin目录配置到环境变量中。
      使用如下命令,-i指定崩溃文件所在目录,-o指定输出目录,-s指定sourcemap文件所在目录,--so指定so文件所在目录,-n指定nameCache文件所在目录。解析完成后,输出目录下会生成对应的解析文件,文件以原始crash文件名加“_”前缀进行命名。

    hstack -i crashDir -o outputDir -s sourceMapDir --so soDir -n nameCacheDir
    

      需要注意的是,对于so文件,一定要使用包含符号表的so。在构建Release应用时,so文件是默认不包含符号表信息的,在构建Release应用时生成包含符号表的so文件,需要在工程的模块级build-profile.json5文件的buildOption属性中,配置如下信息:

    "buildOption": {
      "externalNativeOptions": {
        "arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo"
      }
    }
    

    六、JS崩溃案例解析

      Java有个折磨人的空指针异常,JS同样有个类似空指针的异常,那就是undefined,undefined在错误日志中多表现为如下:

    Error name:TypeError
    Error message:Cannot read property xxx of undefined
    

      解决方式也简单,增加保护性判断,判断对象是否为undefined,或者在访问前增加 '?' 操作符。

    相关文章

      网友评论

          本文标题:鸿蒙JS崩溃分析

          本文链接:https://www.haomeiwen.com/subject/kvzmhjtx.html