feat: 添加 PowerShell 性能追踪脚本

一键抓取 Perfetto trace、帧统计、内存信息并生成 Markdown 报告

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-26 08:12:48 +08:00
parent bb0a72006d
commit 47876a233b

332
scripts/profile.ps1 Normal file
View File

@ -0,0 +1,332 @@
#requires -Version 5.1
[CmdletBinding()]
param(
[Parameter(Position = 0)]
[int]$Duration = 8,
[switch]$NoLaunch,
[switch]$Trace,
[switch]$Help
)
if ($Help) {
$helpLines = @(
"用法: .\scripts\profile.ps1 [秒数] [-NoLaunch] [-Trace]"
""
"选项:"
" 秒数 抓取时长(默认 8 秒)"
" -NoLaunch 不自动启动应用"
" -Trace 使用 trace 构建release 优化 + 保留 trace 标记)"
" -Help 显示此帮助"
)
$helpLines | ForEach-Object { Write-Host $_ }
exit 0
}
$PACKAGE = "plus.rua.project"
$ACTIVITY = "plus.rua.project.MainActivity"
$PROJECT_ROOT = Split-Path -Parent $PSScriptRoot
$LOGS_DIR = Join-Path $PROJECT_ROOT "logs"
$DURATION_MS = $Duration * 1000
$TIMESTAMP = Get-Date -Format "yyyyMMdd_HHmmss"
Write-Host "========================================"
Write-Host " YaYa 性能追踪"
Write-Host " 包名: $PACKAGE"
Write-Host " 时长: ${Duration}s"
if ($Trace) {
Write-Host " 构建: trace (release + trace)"
} else {
Write-Host " 构建: debug"
}
Write-Host " 输出: $LOGS_DIR/"
Write-Host "========================================"
New-Item -ItemType Directory -Force -Path $LOGS_DIR | Out-Null
$adb = Get-Command adb -ErrorAction SilentlyContinue
if (-not $adb) {
Write-Error "adb 未找到。请确保 Android SDK 的 platform-tools 在 PATH 中。"
exit 1
}
$deviceLines = & adb devices | Select-String "device$"
$deviceCount = ($deviceLines | Measure-Object).Count
if ($deviceCount -eq 0) {
Write-Error "没有已连接的 Android 设备。"
exit 1
}
if ($deviceCount -gt 1) {
Write-Warning "检测到多个设备,将使用默认设备。"
}
$installed = & adb shell pm list packages | Select-String $PACKAGE
if (-not $installed) {
Write-Error "应用 $PACKAGE 未安装。请先运行 .\gradlew :app:installDebug"
if ($Trace) {
Write-Host " 或使用 .\gradlew :app:installTrace 安装 trace 构建"
}
exit 1
}
if (-not $NoLaunch) {
Write-Host ""
Write-Host "[1/5] 启动应用..."
& adb shell am start -n "$PACKAGE/$ACTIVITY" 2>$null
Start-Sleep -Seconds 2
} else {
Write-Host ""
Write-Host "[1/5] 跳过启动 (-NoLaunch)"
}
Write-Host ""
Write-Host "[2/5] 抓取 Perfetto trace (${Duration}s)..."
Write-Host " 请在设备上操作应用trace 正在记录..."
$TRACE_FILE = "/data/misc/perfetto-traces/yaya_${TIMESTAMP}.perfetto-trace"
$LOCAL_TRACE = Join-Path $LOGS_DIR "trace_${TIMESTAMP}.perfetto-trace"
$LOCAL_CONFIG = Join-Path $LOGS_DIR ".perfetto_config_${TIMESTAMP}.txt"
$DEVICE_CONFIG = "/data/misc/perfetto-configs/yaya_config_${TIMESTAMP}.txt"
$perfettoLines = @(
"buffers {"
" size_kb: 131072"
" fill_policy: RING_BUFFER"
"}"
"buffers {"
" size_kb: 4096"
" fill_policy: RING_BUFFER"
"}"
"data_sources {"
" config {"
" name: `"linux.ftrace`""
" ftrace_config {"
" ftrace_events: `"sched/sched_switch`""
" ftrace_events: `"sched/sched_wakeup`""
" ftrace_events: `"power/cpu_frequency`""
" ftrace_events: `"power/cpu_idle`""
" atrace_categories: `"gfx`""
" atrace_categories: `"view`""
" atrace_categories: `"wm`""
" atrace_categories: `"am`""
" atrace_categories: `"input`""
" atrace_categories: `"sched`""
" atrace_categories: `"freq`""
" atrace_categories: `"idle`""
" atrace_apps: `"$PACKAGE`""
" }"
" }"
"}"
"data_sources {"
" config {"
" name: `"android.gpu.memory`""
" }"
"}"
"data_sources {"
" config {"
" name: `"android.surfaceflinger.frametimeline`""
" }"
"}"
"data_sources {"
" config {"
" name: `"linux.process_stats`""
" target_buffer: 1"
" process_stats_config {"
" scan_all_processes_on_start: true"
" }"
" }"
"}"
"duration_ms: $DURATION_MS"
)
$perfettoConfig = $perfettoLines -join "`r`n"
$perfettoConfig | Set-Content -Encoding UTF8 -Path $LOCAL_CONFIG
& adb push "$LOCAL_CONFIG" "$DEVICE_CONFIG" | Out-Null
Remove-Item -Force -Path $LOCAL_CONFIG -ErrorAction SilentlyContinue
& adb shell "perfetto --txt -c $DEVICE_CONFIG -o $TRACE_FILE"
& adb shell "rm -f $DEVICE_CONFIG"
Write-Host " 拉取 trace 文件..."
& adb pull "$TRACE_FILE" "$LOCAL_TRACE"
& adb shell "rm -f $TRACE_FILE" 2>$null
Write-Host ""
Write-Host "[3/5] 抓取帧统计..."
$FRAMESTATS_FILE = Join-Path $LOGS_DIR "framestats_${TIMESTAMP}.txt"
& adb shell dumpsys gfxinfo "$PACKAGE" framestats | Set-Content -Encoding UTF8 -Path $FRAMESTATS_FILE
Write-Host ""
Write-Host "[4/5] 抓取内存信息..."
$MEMINFO_FILE = Join-Path $LOGS_DIR "meminfo_${TIMESTAMP}.txt"
& adb shell dumpsys meminfo "$PACKAGE" | Set-Content -Encoding UTF8 -Path $MEMINFO_FILE
Write-Host ""
Write-Host "[5/5] 生成摘要..."
$REPORT_FILE = Join-Path $LOGS_DIR "report_${TIMESTAMP}.md"
$frameStatsContent = Get-Content -Path $FRAMESTATS_FILE -Raw
$FRAME_COUNT = ([regex]::Matches($frameStatsContent, "FrameTimeline")).Count
function Get-FirstMatchValue {
param([string]$Pattern, [string]$Content, [int]$CaptureGroup = 1)
$m = [regex]::Match($Content, $Pattern)
if ($m.Success) { $m.Groups[$CaptureGroup].Value.Trim() } else { "" }
}
$TOTAL_FRAMES = Get-FirstMatchValue "Total frames rendered:\s*(\d+)" $frameStatsContent
$JANKY_FRAMES = Get-FirstMatchValue "Janky frames:\s*(\d+)" $frameStatsContent
$JANKY_PERCENT = Get-FirstMatchValue "Janky frames:.*?\(([^)]+)\)" $frameStatsContent
$P50 = Get-FirstMatchValue "50th percentile:\s*([\d.]+)" $frameStatsContent
$P90 = Get-FirstMatchValue "90th percentile:\s*([\d.]+)" $frameStatsContent
$P99 = Get-FirstMatchValue "99th percentile:\s*([\d.]+)" $frameStatsContent
$SLOW_UI = Get-FirstMatchValue "Number Slow UI thread:\s*(\d+)" $frameStatsContent
$SLOW_DRAW = Get-FirstMatchValue "Number Slow issue draw commands:\s*(\d+)" $frameStatsContent
$HIGH_INPUT = Get-FirstMatchValue "Number High input latency:\s*(\d+)" $frameStatsContent
$APP_VERSION = (& adb shell dumpsys package "$PACKAGE" | Select-String "versionName" | Select-Object -First 1) -replace ".*versionName=", "" -replace "\s.*", "" -replace "`r", ""
if (-not $APP_VERSION) { $APP_VERSION = "unknown" }
$DEVICE_MODEL = (& adb shell getprop ro.product.model 2>$null).Trim()
$ANDROID_VERSION = (& adb shell getprop ro.build.version.release 2>$null).Trim()
$memInfoContent = Get-Content -Path $MEMINFO_FILE -Raw
$TOTAL_PSS = Get-FirstMatchValue "TOTAL PSS:\s*(\d+)" $memInfoContent
$JAVA_HEAP = Get-FirstMatchValue "^\s*Java Heap:\s*(\d+)" $memInfoContent
$NATIVE_HEAP = Get-FirstMatchValue "^\s*Native Heap:\s*(\d+)" $memInfoContent
$GRAPHICS = Get-FirstMatchValue "^\s*Graphics:\s*(\d+)" $memInfoContent
$buildType = if ($Trace) { "trace" } else { "debug" }
$now = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$reportLines = @(
"# YaYa 性能追踪报告"
""
"**时间:** $now"
"**设备:** $DEVICE_MODEL (Android $ANDROID_VERSION)"
"**应用版本:** $APP_VERSION"
"**构建类型:** $buildType"
"**追踪时长:** ${Duration}s"
""
"## 文件清单"
""
"| 文件 | 说明 |"
"|------|-------------|"
"| ``trace_${TIMESTAMP}.perfetto-trace`` | Perfetto trace在 https://ui.perfetto.dev 中打开) |"
"| ``framestats_${TIMESTAMP}.txt`` | GPU 帧统计 |"
"| ``meminfo_${TIMESTAMP}.txt`` | 内存快照 |"
"| ``report_${TIMESTAMP}.md`` | 本报告 |"
""
"## 帧率摘要"
""
"| 指标 | 数值 |"
"|------|------|"
"| 总渲染帧数 | $TOTAL_FRAMES |"
"| 掉帧数 | $JANKY_FRAMES |"
"| 掉帧比例 | $JANKY_PERCENT |"
"| 50th percentile | $P50 |"
"| 90th percentile | $P90 |"
"| 99th percentile | $P99 |"
"| Slow UI thread | $SLOW_UI |"
"| Slow draw commands | $SLOW_DRAW |"
"| High input latency | $HIGH_INPUT |"
""
"## 内存摘要"
""
"| 指标 | 数值 (KB) |"
"|------|----------|"
"| Total PSS | $TOTAL_PSS |"
"| Java Heap | $JAVA_HEAP |"
"| Native Heap | $NATIVE_HEAP |"
"| Graphics | $GRAPHICS |"
""
"## Perfetto 分析指南"
""
"打开 [Perfetto UI](https://ui.perfetto.dev),上传 trace 文件:"
""
"### 1. 查看 Compose 自定义标记"
"搜索以下 trace section"
""
"**月视图相关:**"
"- ``MonthView:Compose`` — 月视图整体重组"
"- ``CalendarPagerArea`` — 日历分页器区域重组"
"- ``CalendarPager:Page`` — 月视图单页重组"
"- ``WeekPager:Page`` — 周视图单页重组"
"- ``getMonthDays:*`` — 月份网格计算"
""
"**年视图相关:**"
"- ``YearView:Compose`` — 年视图整体重组"
"- ``YearGridView:*`` — 年视图网格重组"
"- ``generateMiniMonthDays:*`` — 迷你月日期计算"
"- ``YearView:SelectMonth`` — 年视图选择月份"
""
"**转场动画:**"
"- ``MonthView->YearView`` — 月->年视图切换"
"- ``YearView->MonthView`` — 年->月视图切换"
"- ``VM:collapseProgress`` — 折叠动画状态更新"
""
"**单日单元格:**"
"- ``DayCell`` — 单个日期单元格(通过 transition label"
""
"### 2. 分析帧率"
"``framestats_${TIMESTAMP}.txt`` 中查看:"
"- ``FrameTimeline`` 行 — 每帧的 CPU/GPU 耗时"
"- ``jank`` 标记 — 掉帧情况"
""
"### 3. 内存分析"
"``meminfo_${TIMESTAMP}.txt`` 中关注:"
"- ``TOTAL`` 行 — 应用总内存占用"
"- ``Graphics`` 行 — GPU 内存使用"
"- ``Native Heap`` 行 — 原生堆内存"
""
"### 4. 年月视图切换专项分析"
""
"在 Perfetto 中按以下步骤分析转场性能:"
""
"1. 找到 ``MonthView->YearView````YearView->MonthView`` 标记"
"2. 查看标记前后 500ms 的帧数据:"
" - 查找超过 16.67ms 的帧Choreographer#doFrame"
" - 检查是否有连续多帧超过预算"
"3. 同时搜索 ``MonthView:Compose````YearView:Compose``,观察重组重叠情况"
"4. 查看 ``YearGridView:*`` 的耗时,年视图 12 个月网格的计算和绘制成本"
""
"## 基线对比方法"
""
"要对比优化前后的性能:"
""
"``````powershell"
"# 1. 记录当前数据作为基线"
".\scripts\profile.ps1 -Trace 15"
""
"# 2. 修改代码后重新编译"
".\gradlew :app:installTrace"
""
"# 3. 再次记录"
".\scripts\profile.ps1 -Trace 15"
""
"# 4. 对比两个 report 中的帧率摘要表格"
"``````"
)
$report = $reportLines -join "`r`n"
$report | Set-Content -Encoding UTF8 -Path $REPORT_FILE
Write-Host ""
Write-Host "========================================"
Write-Host " 完成!"
Write-Host "========================================"
Write-Host ""
Write-Host "输出文件:"
Write-Host " trace: $LOCAL_TRACE"
Write-Host " framestats: $FRAMESTATS_FILE"
Write-Host " meminfo: $MEMINFO_FILE"
Write-Host " report: $REPORT_FILE"
Write-Host ""
Write-Host "下一步:"
Write-Host " 1. 打开 https://ui.perfetto.dev"
Write-Host " 2. 上传 trace_${TIMESTAMP}.perfetto-trace"
Write-Host " 3. 搜索 'MonthView->YearView' 查看转场 trace"
Write-Host ""