鸿蒙HarmonyOS实战开发知识:命令行工具篇—hstack

简介

hstack是DevEco Studio为开发人员提供的用于将release应用混淆后的crash堆栈还原为源码对应堆栈的工具,支持Windows、Mac、Linux三个平台。

hstack命令行格式为:

hstack -i <crash-dir> -o <output-dir> [options]

options: 可选配置,请参考表hstack命令行配置。

表1 hstack命令行配置

指令

说明

-s

可选,指定工程sourcemap文件归档目录。

--so

可选,指定工程shared object文件归档目录。

-n

可选 ,指定工程nameCache文件归档目录。

--version/-v

查看hstack版本。

--help/-h

查询hstack命令行帮助。

说明

  • sourcemap与shared object文件归档目录至少提供一项。
  • 如果需要对方法名进行解析还原,则需要同时提供sourcemap与nameCache文件。
  • 路径参数不支持以下特殊字符:`~!@#$^&*=|{};,\s\[\]<>?~!@#¥……&*()——|{}【】‘;:。,、?

环境配置

  1. 本工具依赖Node环境, 需要将Node.js配置到环境变量中。
  2. 如果需要对C++文件产生的异常进行解析,则需要将SDK中的native\llvm\bin目录配置到环境变量中,变量名设置为“ADDR2LINE_PATH”。

使用示例

  1. 将应用产生的crash文件归档到crashDir目录下。

  2. 使用-o指定输出目录。
  3. 使用-s指定工程对应sourcemap文件归档目录(可选, 与shared object文件归档目录至少提供一项)。

  4. 使用--so指定shared object文件归档目录(可选,与sourcemap归档目录至少提供一项)。

  5. 使用-n指定nameCache文件归档目录(可选)。

  6. 执行以下命令,可将release应用crash堆栈还原为源码对应堆栈,并将解析结果输出至outputDir目录:
    hstack -i crashDir -o outputDir -s sourceMapDir --so soDir -n nameCacheDir

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

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

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

堆栈解析方案说明

以如下代码为例。

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中,包含混淆后的方法名(或属性名)、路径信息以及混淆后的行列号信息,其中:

  • 方法名在配置相应混淆规则后,会进行混淆处理(例如上述例子中EntryClass的callHarFunction被混淆为i)。方法名混淆前后的映射关系保存在对应模块对应模块编译产物中的nameCache文件中。
  • 路径信息格式为:引用方entry-packageName|被引用方packageName|version|源码相对路径,其中packageName以及version保存在对应模块编译产物中的sourcemap文件中。
  • 行列号混淆前后的映射关系保存在对应模块编译产物中的sourcemap文件中,可利用文件对应的mappings字段进行解析还原。

在对堆栈进行还原时,可分为以下三步:

  1. 根据路径信息,找到对应模块sourcemap。例如第一条堆栈:
    at har (entry|har|1.0.0|src/main/ets/components/mainpage/MainPage.js:58:58)

    由路径信息,可在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"
      }
  2. 利用对应sourcemap信息进行堆栈路径以及行列号还原:

    基于步骤1找到的sourcemap信息,可以将路径以及行列号还原如下:

    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对该堆栈进行再次解析,方案如下:

    • 由路径中最后一个oh_modules起,向下两级,截断上述第一次解析结果路径,结果如下:
      src/main/ets/components/mainpage/MainPage.js
    • 上述路径拼接packageinfo, 拼接方式为:packageName|packageName|version|截断路径,得到路径如下:
      har|har|1.0.0|src/main/ets/components/mainpage/MainPage.js
    • 利用拼接后的路径,在har模块sourcemap文件中找到如下字段:
      "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"
      }

      利用该sourcemap进行再次解析,可得到该堆栈对应的源码信息为:

      at har (har/src/main/ets/components/mainpage/MainPage.ets:20:1)
  3. 利用nameCache文件,对方法名进行解析还原。

    以第二条堆栈为例:

    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.ts: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",利用上述字段对该方法名进行还原:

    • 在上述字段中找出所有混淆后方法名为"i"的条目,该字段中为:
      "callHarFunction:24:26": "i"
    • 找到行号范围包含步骤2中还原后行号的条目,步骤2中得到的行号为25,包含在24-26之内,因此可以得到源码对应方法名为"callHarFunction"。

    通过上述方式,可以得到源码的方法名。

  4. 步骤2与步骤3所得结果进行整合,得到最终堆栈结果如下:
    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信息进行解析还原。

最后

小编在之前的鸿蒙系统扫盲中,有很多朋友给我留言,不同的角度的问了一些问题,我明显感觉到一点,那就是许多人参与鸿蒙开发,但是又不知道从哪里下手,因为资料太多,太杂,教授的人也多,无从选择。有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(HarmonyOS NEXT)资料用来跟着学习是非常有必要的。 

为了确保高效学习,建议规划清晰的学习路线,涵盖以下关键阶段:


 鸿蒙(HarmonyOS NEXT)最新学习路线

该路线图包含基础技能、就业必备技能、多媒体技术、六大电商APP、进阶高级技能、实战就业级设备开发,不仅补充了华为官网未涉及的解决方案

路线图适合人群:

IT开发人员:想要拓展职业边界
零基础小白:鸿蒙爱好者,希望从0到1学习,增加一项技能。
技术提升/进阶跳槽:发展瓶颈期,提升职场竞争力,快速掌握鸿蒙技术

2.视频学习资料+学习PDF文档

HarmonyOS Next 最新全套视频教程

  纯血版鸿蒙全套学习资料(面试、文档、全套视频等)              

​​

总结

参与鸿蒙开发,你要先认清适合你的方向,如果是想从事鸿蒙应用开发方向的话,可以参考本文的学习路径,简单来说就是:为了确保高效学习,建议规划清晰的学习路线

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值