鸿蒙JS崩溃分析

  当未处理的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,或者在访问前增加 '?' 操作符。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,744评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,505评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,105评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,242评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,269评论 6 389
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,215评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,096评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,939评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,354评论 1 311
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,573评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,745评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,448评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,048评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,683评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,838评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,776评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,652评论 2 354

推荐阅读更多精彩内容