yayacal/scripts/profile.ps1
xfy 47876a233b feat: 添加 PowerShell 性能追踪脚本
一键抓取 Perfetto trace、帧统计、内存信息并生成 Markdown 报告

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-26 08:12:48 +08:00

333 lines
11 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 ""