智能客服
你问我答,随时在线为你解决问题
hstack是为开发人员提供的用于将release应用混淆后的crash堆栈解析为源码对应堆栈的工具,支持Windows、Mac、Linux三个平台,关于堆栈解析的原理,请查看异常堆栈解析原理。
hstack命令行格式为:
hstack [options]
options: 可选配置,请参考表hstack命令行配置。
指令 | 说明 |
|---|---|
-i/--input | 可选,指定工程crash文件归档目录。 从26.0.0版本开始,支持指定crash文件。 |
-c/--crash | 可选,指定一条crash堆栈。 |
-o/--output | 可选,指定解析结果输出目录或输出文件。
|
-s/--sourcemapDir | 可选,指定工程sourceMap文件归档目录。 从26.0.0版本开始,支持指定sourceMap文件。 |
--so/--soDir | 可选,指定工程shared object文件归档目录。 从26.0.0版本开始,支持指定shared object文件。 |
-n/--nameObfuscation | 可选 ,指定工程nameCache文件归档目录。 从26.0.0版本开始,支持指定nameCache文件。 |
-v/--version | 查看hstack版本。 |
-h/--help | 查询hstack命令行帮助。 |





# 通过-i指定crash文件归档目录,并将解析结果输出至outputDir目录 hstack -i D:\crashDir -o D:\outputDir -s D:\sourcemapDir --so D:\soDir -n D:\nameCacheDir # 通过-c指定一条堆栈,并将解析结果输出至out.txt文件 hstack -c "at anonymous (entry|entry|1.0.0|src/main/ets/pages/Index.ts:401:1)" -o D:\outputDir\out.txt -s D:\sourcemapDir --so D:\soDir -n D:\nameCacheDir


如果是指定crash文件归档目录,解析完成后,outputDir目录下会生成对应的解析结果,文件以原始crash文件名加“_”前缀进行命名。crash堆栈中的C++日志以及ArkTS日志均已解析为源码对应的文件路径以及行列号,结果如下图所示:

在构建Release应用时,so文件是默认不包含符号表信息的,如果需要在构建Release应用时生成包含符号表的so文件,需要在工程的模块级build-profile.json5文件的buildOption属性中,配置如下信息:
"buildOption": { "externalNativeOptions": { "arguments": "-DCMAKE_BUILD_TYPE=RelWithDebInfo" } }
以如下代码为例。
Entry模块通过独立har包形式引用har模块中的har方法:
import {har} from 'Har'
@Entry
@Component
struct Index {
@State har: string = 'Har';
build() {
Row() {
Column() {
Text(this.har)
.fontSize(50)
.fontWeight(FontWeight.Bold)
.onClick(() => {
let entryClass = new EntryClass();
entryClass.callHarFunction();
})
}
.width('100%')
}
.height('100%')
}
}
class EntryClass {
callHarFunction() {
har()
}
} @Component
export struct MainPage {
@State message: string = 'Hello World';
build() {
Row() {
Column() {
Text(this.message)
.fontSize(50)
.fontWeight(FontWeight.Bold)
}
.width('100%')
}
.height('100%')
}
}
export function har() {
BigInt(1.1)
} 生成的crash如下:
at har (entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js:58:58) at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71) at anonymous (entry|entry|1.0.0|src/main/ets/pages/Index.ts:55:55)
crash中,包含混淆后的方法名(或属性名)、路径信息以及混淆后的行列号信息,其中:
在对堆栈进行还原时,可分为以下三步:
at har (entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js:58:58)
根据路径信息entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js,可在entry模块sourceMap文件中找到如下字段:
"entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js": {
"version": 3,
"file": "MainPage.js",
"sources": [
"oh_modules/.ohpm/Har@ue9rwlwgmslvadnmypsedjcin6a=/oh_modules/Har/src/main/ets/components/mainpage/MainPage.js"
],
"names": [],
"mappings": "AAAA,IAAA,CAAA,CAAA,sBAAA,IAAA,MAAA,CAAA,SAAA,CAAA,EAAA;IACA,OAAA,CAAA,GAAA,CAAA,MAAA,CAAA,SAAA,EAAA,sBAAA,EAAA,GAAA,EAAA,GAAA,CAAA,CAAA,CAAA;CACA;AACA,MAAA,OAAA,QAAA,SAAA,MAAA;IACA,YAAA,CAAA,EAAA,EAAA,EAAA,CAAA,EAAA,CAAA,GAAA,CAAA,CAAA,EAAA,CAAA,GAAA,SAAA,EAAA,CAAA;QACA,KAAA,CAAA,CAAA,EAAA,CAAA,EAAA,CAAA,EAAA,CAAA,CAAA,CAAA;QACA,IAAA,OAAA,CAAA,KAAA,UAAA,EAAA;YACA,IAAA,CAAA,gBAAA,GAAA,CAAA,CAAA;SACA;QACA,IAAA,EAAA,GAAA,IAAA,wBAAA,CAAA,aAAA,EAAA,IAAA,EAAA,SAAA,CAAA,CAAA;QACA,IAAA,CAAA,yBAAA,IAAA,CAAA;QACA,IAAA,CAAA,oBAAA,EAAA,CAAA;IACA,CAAA;IACA,yBAAA,CAAA,EAAA;QACA,IAAA,GAAA,OAAA,KAAA,SAAA,EAAA;YACA,IAAA,CAAA,OAAA,GAAA,GAAA,OAAA,CAAA;SACA;IACA,CAAA;IACA,eAAA,CAAA,CAAA;IACA,CAAA;IACA,iCAAA,CAAA,CAAA;QACA,IAAA,EAAA,CAAA,uBAAA,CAAA,CAAA,CAAA,CAAA;IACA,CAAA;IACA,gBAAA;QACA,IAAA,EAAA,CAAA,gBAAA,EAAA,CAAA;QACA,iBAAA,CAAA,GAAA,EAAA,CAAA,MAAA,CAAA,IAAA,CAAA,IAAA,EAAA,CAAA,CAAA;QACA,IAAA,CAAA,wBAAA,EAAA,CAAA;IACA,CAAA;IACA,IAAA,OAAA;QACA,OAAA,IAAA,EAAA,CAAA,GAAA,EAAA,CAAA;IACA,CAAA;IACA,IAAA,OAAA,CAAA,EAAA;QACA,IAAA,EAAA,CAAA,GAAA,IAAA,CAAA;IACA,CAAA;IACA,aAAA;QACA,IAAA,CAAA,yBAAA,CAAA,CAAA,CAAA,EAAA,EAAA,EAAA,EAAA;YACA,GAAA,CAAA,MAAA,EAAA,CAAA;YACA,GAAA,CAAA,MAAA,CAAA,MAAA,CAAA,CAAA;QACA,CAAA,EAAA,GAAA,CAAA,CAAA;QACA,IAAA,CAAA,yBAAA,CAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA;YACA,MAAA,CAAA,MAAA,EAAA,CAAA;YACA,MAAA,CAAA,KAAA,CAAA,MAAA,CAAA,CAAA;QACA,CAAA,EAAA,MAAA,CAAA,CAAA;QACA,IAAA,CAAA,yBAAA,CAAA,CAAA,CAAA,EAAA,CAAA,EAAA,EAAA;YACA,IAAA,CAAA,MAAA,CAAA,IAAA,CAAA,OAAA,CAAA,CAAA;YACA,IAAA,CAAA,QAAA,CAAA,EAAA,CAAA,CAAA;YACA,IAAA,CAAA,UAAA,CAAA,UAAA,CAAA,IAAA,CAAA,CAAA;QACA,CAAA,EAAA,IAAA,CAAA,CAAA;QACA,IAAA,CAAA,GAAA,EAAA,CAAA;QACA,MAAA,CAAA,GAAA,EAAA,CAAA;QACA,GAAA,CAAA,GAAA,EAAA,CAAA;IACA,CAAA;IACA,QAAA;QACA,IAAA,CAAA,mBAAA,EAAA,CAAA;IACA,CAAA;CACA;AACA,MAAA,UAAA,GAAA;IACA,MAAA,CAAA,GAAA,CAAA,CAAA;AACA,CAAA",
"entry-package-info": "entry|1.0.0",
"package-info": "har|1.0.0"
} 基于步骤1找到的sourceMap信息,根据sources及mappings字段进行解析,可以将路径以及行列号还原如下:
at har (oh_modules/.ohpm/Har@ue9rwlwgmslvadnmypsedjcin6a=/oh_modules/Har/src/main/ets/components/mainpage/MainPage.js:58:58)
该文件位于entry模块oh_modules路径下。
如果对应sourceMap中包含package-info字段,则可以利用package-info中对应模块的sourceMap,对该条堆栈进行二次解析。例如该堆栈中包package-info为har|1.0.0,可利用har中的sourceMap对该堆栈进行再次解析,方案如下:
src/main/ets/components/mainpage/MainPage.js
har|har|1.0.0|src/main/ets/components/mainpage/MainPage.js
"har|har|1.0.0|src/main/ets/components/mainpage/MainPage.js": {
"version": 3,
"file": "MainPage.ets",
"sources": [
"har/src/main/ets/components/mainpage/MainPage.ets"
],
"names": [],
"mappings": ";;;AAEA,MAAA,OAAA,QAAA,SAAA,MAAA;IADA,YAAA,CAAA,EAAA,CAAA,EAAA,CAAA,EAAA,IAAA,CAAA,CAAA,EAAA,IAAA,SAAA,EAAA,CAAA;;;;;;;;IADyB,CAAA;;;;;;;;;;;;;;;;;;;;;;IAKvB,aAAA;;;;;;;;;;;;YAGM,IAAA,CAAA,UAAA,CAAA,UAAA,CAAA,IAAA,CAAA,CAAA;;;;;IAOL,CAAA;;;;;AAGH,MAAA,UAAA,GAAA;;AAEA,CAAA",
"entry-package-info": "har|1.0.0"
} at har (har/src/main/ets/components/mainpage/MainPage.ets:20:1)
以第二条堆栈为例:
at i (entry|entry|1.0.0|src/main/ets/pages/Index.ts:71:71)
通过步骤1与步骤2,将该堆栈路径以及行列号信息进行解析,结果如下:
at i (entry/src/main/ets/pages/Index.ets:25:3)
在对应模块编译产物中的nameCache文件中,通过解析后的文件路径找到如下字段:
"entry/src/main/ets/pages/Index.ets": {
"IdentifierCache": {
"Index#initialRender#__function": "o",
"Index#initialRender#$2#__function": "t",
"Index#initialRender#$2#$0#entryClass": "u",
"$0#__function": "a1"
},
"MemberMethodCache": {
"initialRender:6:20": "initialRender",
"callHarFunction:24:26": "i"
},
"obfName": "entry/src/main/ets/pages/Index.ets"
} 该字段的IdentifierCache与MemberMethodCache中保存了方法名混淆前后的映射关系,对应格式为:
"源码方法名:该方法起始行号:该方法结束行号":"混淆后方法名"。
第二条堆栈混淆后的方法名为"i",利用上述字段对该方法名进行还原:
"callHarFunction:24:26": "i"
通过上述方式,可以得到源码的方法名。
at har (har/src/main/ets/components/mainpage/MainPage.ets:20:1) at callHarFunction (entry/src/main/ets/pages/Index.ets:25:3) at anonymous (entry/src/main/ets/pages/Index.ets:14:47)
通过上述方式,即可利用编译产物对release应用的crash信息进行解析还原。