Administrator
发布于 2026-09-02 / 1,630 阅读
0
0

鸿蒙调试技巧:断点调试、attach 进程与链路追踪

系列文章《鸿蒙开发从入门到专家》第 47 篇 · 中级

写代码一半时间在写,另一半时间在调。但很多人调试全靠 console.log,遇到偶发崩溃、分布式问题、多线程竞态就束手无策。

这篇把鸿蒙调试工具链过一遍。从最基本的断点调试,到 attach 到已运行进程,到用 HiLog 做链路追踪,覆盖实际开发中最需要的调试场景。

一、断点调试

DevEco Studio 基于 IntelliJ 平台,断点调试的操作和 Android Studio 几乎一样。

1.1 基本操作

在代码行号左侧点击设置断点(红点),然后点击 Debug 按钮(虫子图标或 Shift+F9)以调试模式运行。执行到断点时应用暂停,Debug 面板显示变量、调用栈、线程信息。

快捷键

功能

说明

F8

Step Over

执行当前行,不进入函数内部

F7

Step Into

进入函数内部

Shift+F8

Step Out

跳出当前函数

F9

Resume

继续执行到下一个断点

1.2 条件断点

在循环或高频回调中,每次都断下来不现实。右键断点,在 Condition 框中输入表达式,只有条件为 true 时才暂停:

index === 5

这样只在第 5 次循环时断下,其余循环直接跳过。

1.3 日志断点(Logpoint)

这个功能很多人不知道。有时候你想知道某行代码执行了没有,但又不想暂停程序。右键断点,取消勾选 “Suspend”,勾选 “Evaluate and log”,输入要打印的内容:

"Processing item " + item.id + ", value=" + item.value

程序运行到这行时会输出日志但不暂停。比改代码加 console.log 然后重新编译方便得多。

1.4 变量监视

在 Watches 面板中添加表达式,断点暂停时实时显示值。比如监视 this.list.length 或 response.data.items[0].name,方便观察复杂对象的状态变化。

二、Attach 到已运行进程

有时候 bug 只在特定操作序列下出现。用 Debug 模式从头启动应用,手动重现操作序列可能要花几分钟。更高效的方式是先正常启动应用,复现到出问题前一步,然后 attach 调试器。

2.1 操作步骤

  1. 先用 Run 模式正常启动应用。

  2. 执行操作序列到接近出现 bug 的位置。

  3. 在 DevEco Studio 中点击 “Attach to Process” 按钮(带虫子图标的下拉箭头)。

  4. 选择目标设备的进程。

  5. 断点生效,继续操作触发 bug。

2.2 限制

Attach 到进程后,之前已执行的代码不会回溯。但此时断点已生效,后续执行的代码会在断点处暂停。这个方式特别适合调试"用户操作了 N 步后才出现"的问题。

2.3 分布式调试

鸿蒙分布式场景涉及跨设备调用。调试时可以在多个设备的进程上同时 attach。比如手机发起跨端调用到平板,在平板的 Ability 上打断点,能看到跨端调用的参数和返回值。

三、HiLog:鸿蒙的日志系统

3.1 为什么不用 console.log

console.log 在鸿蒙中有几个问题:

  • 输出到标准输出,容易被系统海量日志淹没。

  • 无法按标签、级别过滤。

  • Release 模式下性能损耗大(不会自动移除)。

  • 不能跨设备关联日志。

HiLog 是鸿蒙原生的日志系统,支持分级、标签化、隐私过滤。

3.2 HiLog 基本用法

import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0xFF00;
const TAG = 'MyApp_Main';

// 四个级别
hilog.debug(DOMAIN, TAG, '%{public}s: debug message', 'functionA');
hilog.info(DOMAIN, TAG, '%{public}s: user logged in', username);
hilog.warn(DOMAIN, TAG, '%{public}s: slow response %{public}dms', url, elapsed);
hilog.error(DOMAIN, TAG, '%{public}s: request failed, code=%{public}d', url, errorCode);

3.3 隐私标记

%{public}s 表示公开内容,会原样输出。%{private}s 表示隐私内容,在日志中会被替换为 ***:

hilog.info(DOMAIN, TAG, 'token=%{private}s', userToken);
// 输出: token=***

这个设计在 Release 包中很有用。敏感数据不会被日志泄露,但开发时可以通过命令动态打开隐私日志:

hdc shell hilog -p off  # 关闭隐私过滤,显示完整内容
hdc shell hilog -p on   # 开启隐私过滤

3.4 封装 Logger

直接写 HiLog 太啰嗦。建议封装一个 Logger 工具:

// utils/Logger.ts
import { hilog } from '@kit.PerformanceAnalysisKit';

const DOMAIN = 0xFF00;

export class Logger {
  private tag: string;

  constructor(tag: string) {
    this.tag = tag;
  }

  debug(msg: string, ...args: any[]): void {
    hilog.debug(DOMAIN, this.tag, `%{public}s`, this.format(msg, args));
  }

  info(msg: string, ...args: any[]): void {
    hilog.info(DOMAIN, this.tag, `%{public}s`, this.format(msg, args));
  }

  warn(msg: string, ...args: any[]): void {
    hilog.warn(DOMAIN, this.tag, `%{public}s`, this.format(msg, args));
  }

  error(msg: string, ...args: any[]): void {
    hilog.error(DOMAIN, this.tag, `%{public}s`, this.format(msg, args));
  }

  private format(msg: string, args: any[]): string {
    let result = msg;
    args.forEach((arg, i) => {
      result = result.replace(`{${i}}`, String(arg));
    });
    return result;
  }
}

// 使用
const logger = new Logger('UserService');
logger.info('User {0} logged in from {1}', userId, deviceName);

3.5 查看日志

在 DevEco Studio 底部 Log 面板查看,或用 hdc 命令行:

# 查看全部日志
hdc shell hilog

# 按 tag 过滤
hdc shell hilog | grep MyApp_Main

# 按级别过滤(D=Debug, I=Info, W=Warn, E=Error)
hdc shell hilog -L D

# 清空日志缓冲
hdc shell hilog -r

四、链路追踪

4.1 用 HiLog 做请求链路追踪

网络请求链路是调试高频场景。一个请求从前端发出到返回,经过网络层、业务逻辑、UI 更新。用统一的 traceId 串联整条链路:

export class NetworkTracer {
  private logger: Logger;
  private traceId: number = 0;

  constructor() {
    this.logger = new Logger('Network');
  }

  startTrace(url: string): string {
    const id = `trace_${Date.now()}_${++this.traceId}`;
    this.logger.info('[{0}] Start request: {1}', id, url);
    return id;
  }

  logRequest(id: string, method: string, headers: Record<string, string>): void {
    this.logger.info('[{0}] Sending {1} request', id, method);
  }

  logResponse(id: string, statusCode: number, duration: number): void {
    this.logger.info('[{0}] Response {1} in {2}ms', id, statusCode, duration);
    if (duration > 3000) {
      this.logger.warn('[{0}] Slow request: {1}ms', id, duration);
    }
  }

  logError(id: string, error: string): void {
    this.logger.error('[{0}] Error: {1}', id, error);
  }
}

使用:

const tracer = new NetworkTracer();

async function fetchData(url: string) {
  const traceId = tracer.startTrace(url);
  try {
    const response = await fetch(url);
    tracer.logResponse(traceId, response.status, 150);
    return response;
  } catch (e) {
    tracer.logError(traceId, e.message);
    throw e;
  }
}

在日志中搜索 trace_1234 就能看到完整链路。

4.2 ArkUI Inspector

ArkUI Inspector 是 DevEco Studio 里的 UI 组件树查看工具。在底部工具栏打开 ArkUI Inspector(连接设备后可用)。

功能:

  • 查看组件树层级结构。

  • 实时修改组件属性(宽高、颜色),立即在设备上预览。

  • 检查组件是否过度嵌套。

过度嵌套是性能问题的常见来源。如果一个简单列表项的组件树有 15 层深度,渲染开销就会偏高。Inspector 能直观看到层级深度。

4.3 Bytrace / HiTrace

系统级链路追踪,适合跨进程、跨线程的时序问题。通过 hdc 执行:

# 抓取一段 trace
hdc shell bytrace -t 5 -b 8192 --overwrite avs > trace.ftrace

# 或用 hitrace(新版)
hdc shell hitrace --time 5s -o /data/local/tmp/trace.ftrace

抓到的 trace 文件可以用 Chrome 的 chrome://tracing 打开,查看系统级的事件时序。这对分析"分布式调用延迟"“多线程时序问题”"渲染管线耗时分布"很有用。

五、调试常见问题速查

问题

排查方向

应用闪退

hdc shell hilog | grep -i crash,看崩溃堆栈

白屏

ArkUI Inspector 看组件树是否为空,检查 loadContent 的路径

滑动卡顿

DevEco Profiler → Frame 泳道看掉帧

内存泄漏

Profiler → Heap 泳道看内存曲线是否只涨不降

网络请求失败

HiLog 过滤 traceId,看请求是否发出、响应码

跨端调用失败

hdc shell dumpsys distbms 确认服务路由表

小结

调试工具链用好了,定位 bug 的效率能差出十倍。记住几个要点:

  • 条件断点和日志断点是利器,能减少无效暂停。

  • Attach 适合调试"操作 N 步后才出现"的问题。

  • HiLog 比 console.log 强在分级、标签、隐私过滤。

  • 链路追踪用统一 traceId 串联整条调用链。

  • ArkUI Inspector 看组件嵌套深度,Bytrace 看系统级时序。

下一篇讲 SDK 二次封装。当你有多个业务模块重复封装同一套系统能力时,该不该抽象一个内部 framework?


评论