外观
工具箱、设备信息与深灰主题:产品设计
Owner 2026-11-02(内部包 244 试用后):① 会话列表按住 Control 单击弹出菜单不对;② 整体纯黑改深灰;③ 设备信息过于简单,参考 codesign4qc 的设备信息获取与展示,保留最重要的;④ 设备信息底部加“工具箱”按钮,打开独立窗口,仿 PerfDog 工具箱的样式与功能(崩溃日志获取、应用管理),实现参考 codesign4qc 的应用管理与日志获取。
一、Control+单击
Owner 期望 Control+单击用于跳选。macOS 默认 Control+单击等同右键;在会话列表内把 Control+左键单击转换为 ⌘+单击(跳选),右键与触控板双指点按仍弹出菜单。只作用于会话列表区域,不影响其他界面。
二、深灰主题
页面背景由深海军蓝(#0B1020 系)改为中性深灰(参考 PerfDog 工具箱):页面 #22252E、侧栏 #282B35、卡片 #2F333E、凸起 #383C48、边框 #484D5C、分隔 #3C404D;文字、强调蓝与状态色不变。只改 App;官网品牌 Token 不变。
三、设备信息
数据来源与 codesign4qc 一致(设备内置 pymobiledevice3,三条只读查询,约 1–1.5 秒/条,切换设备或手动刷新时读取,60 秒内不重复读取):
lockdown info:设备名称、型号、型号号码与地区、序列号、CPU 架构、UDID;lockdown get --domain com.apple.disk_usage:总容量、可用空间;diagnostics battery single:电量、是否充电、最大容量(健康度)、循环次数。
展示(只保留最重要的):
| 分组 | 项 |
|---|---|
| 设备 | 设备名称、设备型号(商品名 + 型号代码)、系统版本(iOS 与 Build)、序列号、UDID(可复制) |
| 状态 | 存储(可用 / 总量与占用条)、电池(电量、充电中、健康度、循环次数) |
| 连接 | 连接方式与状态、目标应用 |
内部诊断信息仍只在开发者模式显示。底部固定“工具箱”按钮。
四、工具箱(独立窗口,仿 PerfDog)
窗口标题“工具箱”,左侧两项:崩溃日志获取、应用管理;右侧顶部为设备选择(已连接设备)。
崩溃日志获取
crash ls --depth 9读取全部日志路径(约 1 秒),解析文件名得到应用/类型与时间;按时间倒序。- 表格:勾选、文件名、日期(可排序);顶部“共 N 个日志文件”、搜索框、“刷新列表”。
- 底部:全选、已选 N 项、“删除文件”(二次确认,
crash clear <路径>)、“导出到本地”(选择文件夹,crash pull,完成后在 Finder 中显示)。 - 与 PerfDog 的差异:设备不提供文件大小,不显示“大小”列。
应用管理
apps list --type User --calculate-sizes(约 3 秒):应用名、Bundle ID、版本、大小(静态 + 动态)。- 图标:
springboard icon,后台两路并行加载并按 Bundle ID + 版本缓存到本机,先显示占位图。 - 顶部:“导入 IPA”(
apps install,进度提示)、刷新、搜索(名称 / Bundle ID);“已安装应用:N”。 - 表格:勾选、图标与名称、Bundle ID、版本、大小(可排序);行尾卸载按钮。
- 底部:全选、已选 N 项与总大小、“批量卸载”(二次确认,
apps uninstall,逐个执行并汇报成功数)。 - 不包含 PerfDog 的“浏览”(应用沙盒文件浏览,仅对开启文件共享的应用有效),后续单独评估。
安全
- 删除、卸载均二次确认;正在测试的目标应用不允许卸载(提示先结束测试)。
- 所有命令带超时(列表 30 秒、安装 10 分钟、卸载 60 秒),失败如实显示错误。
验收
- E2:解析(崩溃日志文件名、应用列表含大小、设备信息三类 JSON)单元测试。
- E3:本机已连接设备上读取设备信息、应用列表与崩溃日志。
- E4:Owner 操作导入、卸载、导出与删除日志。
Implementation Update · 2026-11-02
| 文件 | 修改 |
|---|---|
PerfowlApp/ControlClickSelection.swift(新)、SessionReviewViews.swift | 会话列表区域内的 Control+左键单击转换为 ⌘+单击(跳选),右键与双指点按仍弹出菜单;只在该区域生效。 |
PerfowlApp/PerfowlPalette.swift | 页面、侧栏、卡片、凸起、边框、分隔改为中性深灰(#22252E 系);文字与强调色不变。 |
PerfowlCore/DeviceToolsClient.swift(新) | DeviceDetail(lockdown/磁盘/电池三类 JSON 合并,电池健康 = 最大容量 ÷ 设计容量)、ToolboxApp(含静态+动态大小)、CrashLogEntry(文件名解析进程与时间、只保留文件、按时间倒序);客户端封装 lockdown info、lockdown get --domain com.apple.disk_usage、diagnostics battery single、apps list --calculate-sizes、springboard icon、apps install/uninstall、crash ls --depth 9、crash pull --remote-file、crash clear <路径>,均带超时。 |
PerfowlApp/DeviceDetailStore.swift(新)、PerfowlWorkbenchView.swift | 设备信息按“设备 / 状态 / 连接”分组:设备名称、型号(商品名+代码+型号号码与地区)、系统版本、序列号、UDID;存储(可用/总量)、电池(电量、充电、健康、循环);连接状态与目标应用。切换设备时读取,60 秒内不重复,手动刷新可强制。底部固定“工具箱”与刷新按钮。 |
PerfowlApp/ToolboxView.swift(新) | 独立“工具箱”窗口(仿 PerfDog):左侧崩溃日志获取/应用管理;顶部设备选择。应用管理:导入 IPA、刷新、搜索、可排序表格(图标、应用名、Bundle ID、版本、大小、行内卸载)、全选、已选数量与总大小、批量卸载(二次确认,测试进行中不能卸载被测应用),图标两路并行加载并按 Bundle ID+版本缓存。崩溃日志:共 N 个、刷新、搜索、可排序表格(文件名、日期)、全选、删除文件(二次确认)、导出到本地(完成后在 Finder 中显示)。 |
偏差
- 崩溃日志不显示“大小”:
crash ls只返回路径,逐个查询大小代价高;如需要可后续用 AFC 批量读取。 - 未实现 PerfDog 的“浏览”(应用沙盒文件),见设计说明。
- 删除日志(
crash clear <路径>)与安装/卸载未在本机真实执行,以免改动 Owner 的测试手机;导出(crash pull)已在真机验证。
验证
- E2:新增
DeviceToolsClientTests3 项(设备详情合并与缺失不补 0、应用大小、日志列表解析排序);Swift 全量 451 项 0 失败(13 项跳过,含手动开启的真机/渲染测试)。 - E3:本机已连接手机(设备 A)上实测:设备详情读到存储与电池(健康 93%、循环 1159 次);应用 44 个、44 个均有大小;崩溃日志 71 个;图标读取与日志导出成功;工具箱两页用真实数据渲染核对。
- E4 待测:Owner 在 245 上操作 Control 跳选、深灰主题观感、设备信息、工具箱导入/卸载/导出/删除。
Validation Update · 2026-11-02:内部包 245 构建与同址安装(E3)
- 来源:main
cf8de2d1,Developer ID 签名构建 177.6 秒,来源门禁passed。 - 校验:冻结核心 107 个模块与 CLI 和源码一致,严格签名、DMG 与安装后文件树一致,安装后
probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-245-macos-x86_64-internal.dmg,144,342,158 B,SHA-25647bc7a7e6234acbf5cb2b3ac736e032d263a5e8604a167aebe9667dac05e57bd;244 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);工具箱安装/卸载/删除与界面观感的真机 E4 待 Owner 确认。
Plan Update · 2026-11-02:查看文件与设备信息精简(245 试用后)
Owner 反馈:① 工具箱导入与删除测试通过(E4);② 应用管理缺少“查看文件”(codesign4qc 有);③ 去掉工具箱按钮旁的刷新按钮;④ 设备信息中“状态”“连接”下为空,删掉;增加产品类型、WiFi 地址、蓝牙地址、可用容量;内容可选中复制,但不放复制按钮。
- 查看文件:设备内置 CLI 的
apps afc交互壳在冻结包内不可用,改由采集组件新增只读子命令container ls|pull(pymobiledevice3HouseArrestService,VendContainer打开整个应用容器,非仅 Documents)。列目录一次连接内listdir+ 逐项stat(单项失败容忍,不阻断),超时 60 秒;导出用pull,超时 15 分钟。界面为应用管理行内“查看文件”按钮与右键菜单,弹出浏览窗口:返回上级、路径、刷新、名称/大小/修改时间表格(文件夹在前),双击进入,选中后“导出到本地”(完成后在 Finder 中显示)。不提供写入、删除。 - 设备信息:改为单列平铺:设备名称、设备型号、产品类型(型号代码 + 型号号码与地区)、系统版本、序列号、UDID、WiFi 地址、蓝牙地址、可用容量(可用 / 共总量);文本可选中复制,无复制按钮;去掉“状态”“连接”分组与刷新按钮。异常时仍显示“重新检查设备 / 重新连接”。
Implementation Update · 2026-11-02:查看文件与设备信息精简
| 文件 | 修改 |
|---|---|
Sidecar/perfowl_collector_core/app_container.py(新)、perfowl_collector_core_cli.py | container ls/pull --udid --bundle-id --path [--output],输出 JSON(entries / pulled / error,出错退出码 2)。 |
PerfowlCore/DeviceToolsClient.swift | DeviceDetail 增加 WiFi / 蓝牙地址;ContainerEntry(文件夹在前、自然排序、错误透出);AppContainerClient(调用采集组件 container)。 |
PerfowlApp/ContainerBrowserView.swift(新)、ToolboxView.swift | 文件浏览窗口;应用管理行尾“查看文件”按钮与右键“查看文件”。 |
PerfowlApp/PerfowlWorkbenchView.swift | 设备信息平铺 9 项、可选中文本;删除“状态”“连接”分组与刷新按钮。原分组为空的原因:同一分组内行使用重复 id,SwiftUI 只渲染了首行之外的空白。 |
偏差
- 未复用设备内置 CLI 的
apps afc(冻结包内交互壳不可用),改走采集组件,与性能采集共用同一 pymobiledevice3 内核。 - 只读:不提供上传、删除容器内文件。
验证
- E2:
DeviceToolsClientTests4 项(新增 WiFi/蓝牙解析、容器目录排序与错误透出);Swift 全量 453 项 0 失败(14 项跳过,含手动开启的真机/渲染测试);Python 3145 项,失败均为既有环境基线(native_direct_archive、native_raw_graph、sw1)及一项与本批无关的墙钟基准(双回归并行时机器繁忙)。 - E3:本机已连接手机(设备 A)上,目标 App A 容器
/Documents列出 30 项、单文件导出成功;浏览窗口用真实数据渲染核对。 - E4 待测:Owner 在 246 上操作查看文件(进入、返回、导出)、设备信息新字段与选中复制。
Validation Update · 2026-11-02:内部包 246 构建与同址安装(E3)
- 来源:main
e6ce454c,Developer ID 签名构建 157.6 秒,来源门禁passed。 - 校验:冻结核心 108 个模块与 CLI 和源码一致(新增
app_container),包内采集组件在设备 A 上container ls列出 30 项;严格签名、DMG 与安装后文件树一致,安装后probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-246-macos-x86_64-internal.dmg,149,616,923 B,SHA-256f8e3a7b7086707777bfbf4edbd0c2d806ca465a69cff4e6629b43804052d2321;245 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);查看文件与设备信息新字段的真机 E4 待 Owner 确认。
Fix Plan · 2026-11-02:查看文件 InstallationLookupFailed(246 试用后)
- 现象(E4,Owner):部分应用“查看文件”报
PyMobileDevice3Exception: InstallationLookupFailed。 - 复现(E3,设备 A 全部 43 个用户应用逐一测试):完整容器(VendContainer)35 个成功、8 个被系统拒绝;被拒绝的 8 个中,2 个开启了文件共享,只读 Documents(VendDocuments)可读;其余 6 个两种方式都被拒绝(含 TestFlight)。被拒绝的确切条件未证实,不写成定论。
- 修复:完整容器被拒绝时自动退回 Documents 模式;该模式下根目录只列出
Documents,其他路径不可访问;窗口状态栏注明“仅文件共享目录”。两种方式都被拒绝时显示“系统不允许读取此应用的文件”,不再透出原始异常。仍只读,每次操作一次连接,额外开销仅在被拒绝时多一次握手(约 0.3 秒)。
Implementation Update · 2026-11-02
| 文件 | 修改 |
|---|---|
Sidecar/perfowl_collector_core/app_container.py | 完整容器被拒绝(InstallationLookupFailed)时退回 Documents 模式:根目录只列 Documents,其他路径返回“此应用只开放文件共享目录(Documents)”;导出根目录即导出 Documents;两种方式都被拒绝时返回“系统不允许读取此应用的文件”;ls 输出增加 documentsOnly。 |
PerfowlCore/DeviceToolsClient.swift、PerfowlApp/ContainerBrowserView.swift | AppContainerClient.list 同时返回是否仅 Documents;浏览窗口状态栏注明“此应用仅开放文件共享目录”。 |
验证:E2 DeviceToolsClientTests 4 项(新增 documentsOnly 解析),Swift 全量 453 项 0 失败;E3 设备 A 上:开启文件共享的应用根目录列出 Documents、进入后可读,/Library 返回中文提示;两种方式都被拒绝的应用返回“系统不允许读取此应用的文件”;普通应用完整容器不受影响。E4 待 Owner 在 247 上确认。
Validation Update · 2026-11-02:内部包 247 构建与同址安装(E3)
- 来源:main
8ed6ab1f,Developer ID 签名构建 393.7 秒,来源门禁passed。 - 校验:冻结核心 108 个模块与 CLI 和源码一致,包内采集组件对开启文件共享的应用返回
Documents;严格签名、DMG 与安装后文件树一致,安装后probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-247-macos-x86_64-internal.dmg,149,451,078 B,SHA-25683528a2909ffa937dd56c64d2c2ec45a1a263fd12beeb818e1b87e568432285e;246 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);查看文件回退与提示的真机 E4 待 Owner 确认。
Perf Plan · 2026-11-02:查看文件加载提速(247 试用后)
- 现象(E4,Owner):查看文件每次进入/返回目录都要 3 秒以上,体验不如 PerfDog。
- 剖析(E3,设备 A,目标 App A,30 项目录):每次操作新启动冻结采集组件约 0.5–1.0 秒,向设备申请容器(VendContainer)约 1.1 秒,列目录 0.003 秒,30 项逐个 stat 0.06 秒;单次命令总计约 2.3–2.5 秒,加上界面调度体感超过 3 秒。耗时几乎全在“每次都重新启动进程和重新申请容器”。
- 方案:
- 采集组件新增
container serve --udid --bundle-id:启动一次、申请一次容器,之后按行读取请求(ls/pull),按行返回 JSON;连接异常时下一请求自动重开一次;标准输入关闭即退出。 - App 端浏览窗口打开时启动会话、关闭时结束;请求串行,
ls超时 60 秒、pull15 分钟,超时或进程退出则丢弃会话,下次请求重建。 - 已浏览目录缓存:返回或再次进入时先显示缓存,再在后台刷新。
- 采集组件新增
- 目标:首次打开约 1.5–2 秒(启动+申请容器各一次,与 PerfDog 首次同量级);之后进入/返回目录 < 0.2 秒。常驻进程只在窗口打开期间存在,空闲无轮询。
Implementation Update · 2026-11-02:查看文件加载提速
| 文件 | 修改 |
|---|---|
Sidecar/perfowl_collector_core/app_container.py、perfowl_collector_core_cli.py | 新增 container serve:一次申请容器,按行处理 ls/pull;文件级错误(AFC 错误、不在 Documents 内)只回报不重连,连接类错误重开一次;标准输入关闭即退出。一次性 ls/pull 保留。 |
PerfowlCore/DeviceToolsClient.swift | AppContainerSession(actor):窗口内常驻一个采集组件进程,请求串行,单请求看门狗超时(ls 60 秒、pull 15 分钟),超时或进程退出即丢弃并在下一请求重建;进程登记到 OwnedProcessShutdownRegistry,App 退出时一并结束。LineQueue 按行拆分标准输出。 |
PerfowlApp/ContainerBrowserView.swift | 浏览窗口使用会话;已浏览目录缓存后先显示再后台刷新;窗口关闭结束会话;导出固定在发起时的目录。 |
验证:
- E2:Swift 全量 455 项 0 失败(新增
LineQueue分块与 EOF 测试;手动开启的会话计时测试);Python 无新增失败。 - E3(设备 A、目标 App A、源码解释器运行采集组件):命令层
serve首次 2.0 秒,此后/Documents30 项 54 ms、/Library31 ms、返回/11 ms、路径不存在 2 ms 且不重连;App 端会话首次 2.8 秒(源码解释器启动较冻结包慢),此后 6–53 ms。对照提速前每次 2.3–2.5 秒(命令层)。 - 偏差:首次打开仍需启动进程与申请容器(冻结包约 2 秒),未做“选中行即预热”,以免为未打开的应用常驻进程。
- E4 待 Owner 在 248 上确认体感。
Validation Update · 2026-11-02:内部包 248 构建与同址安装(E3)
- 来源:main
153a03be,Developer ID 签名构建 263.9 秒,来源门禁passed。 - 提速实测(冻结采集组件,设备 A,目标 App A):
serve首次打开 2.28–2.42 秒(新二进制第一次运行 3.8 秒),此后/Documents30 项 67 ms、/Library50 ms、返回/14 ms;提速前每次操作 2.3–2.5 秒。 - 校验:冻结核心 108 个模块与 CLI 和源码一致,严格签名、DMG 与安装后文件树一致,安装后
probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-248-macos-x86_64-internal.dmg,147,068,292 B,SHA-256a72c23d52fd9f33cc66e7411dc8d31de39bc8df46d25491ecd3e5b9450e4dc8e;247 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);体感 E4 待 Owner 确认。
Fix Plan · 2026-11-02:248 试用反馈(读取失败提示、重新连接按钮、Dock 退出、应用列表刷新)
AfcException [Errno 10] READ_DIR:设备 A 上可读完整容器的 36 个应用逐层扫描(3 层),唯一失败目录是每个应用都有的/SystemData,AFC 状态 10 = 无权限(系统保护目录,E3)。修复:错误按状态码转为中文(10“没有权限读取此文件夹(系统保护目录)”、8“文件或文件夹不存在”);进入失败时停留在原目录并保留原列表,只在状态栏提示。- 设备信息“重新连接”按钮:连接过程中短暂出现,连接成功后消失,Owner 要求去掉;该按钮整体移除(无设备时的“重新检查设备”保留)。
- 打开查看文件后无法从 Dock 退出:服务进程收到 SIGTERM 0.01 秒内退出(已排除);查看文件以工具箱窗口上的弹出层(sheet)呈现,弹出层未关闭时退出请求被搁置。修复:改为独立窗口(与工具箱相同方式,按应用复用,关闭即结束会话),退出 App 时窗口一并关闭。
- 应用列表刷新按钮:应用管理页去掉刷新按钮;安装、卸载完成后已自动刷新(保持);切换设备、打开窗口时自动读取。崩溃日志页保留刷新(日志会在设备上新增)。
Implementation Update · 2026-11-02:248 试用反馈
| 文件 | 修改 |
|---|---|
Sidecar/perfowl_collector_core/app_container.py | AFC 状态码转中文:10“没有权限读取此文件夹(系统保护目录)”、8“文件或文件夹不存在”;会话与一次性命令共用。 |
PerfowlApp/ContainerBrowserView.swift | 查看文件改为独立窗口(ContainerBrowserWindows,按设备+应用复用,关闭窗口即结束会话);进入失败时退回原目录并保留列表,状态栏提示“无法打开‘X’:原因”。 |
PerfowlApp/ToolboxView.swift | 去掉弹出层;应用管理页去掉刷新按钮(崩溃日志页保留),安装/卸载后自动刷新不变。 |
PerfowlApp/PerfowlWorkbenchView.swift | 设备信息去掉“重新连接”按钮;无设备时“重新检查设备”保留,且无按钮时不再留空白。 |
验证:E2 Swift 全量 455 项 0 失败,Python 无新增失败;E3 设备 A:/SystemData 返回“没有权限读取此文件夹(系统保护目录)”,不存在路径返回“文件或文件夹不存在”,随后 /Documents 30 项正常(会话不断开)。Dock 退出问题的原因(弹出层阻止退出请求)为推断,未能在本机自动化复现,待 Owner 在 249 上确认(E4)。
Validation Update · 2026-11-02:内部包 249 构建与同址安装(E3)
- 来源:main
d92306fb,Developer ID 签名构建 165.1 秒,来源门禁passed;包内采集组件读取/SystemData返回“没有权限读取此文件夹(系统保护目录)”。 - 校验:冻结核心 108 个模块与 CLI 和源码一致,严格签名、DMG 与安装后文件树一致,安装后
probeready;启动器只剩/Applications/Perfowl.app。 - 产物:
MacApp/dist/Perfowl-249-macos-x86_64-internal.dmg,147,975,099 B,SHA-256d8dd5517cdec54918dce75008d9ba449622b50aac101bcd4556c0c07d97ea9de;248 安装包与回滚包在私有归档,回滚已核对。 - 未完成:公证暂缓(Gatekeeper exit 3);Dock 退出、按钮移除与读取提示的 E4 待 Owner 确认。
Validation Update · 2026-11-03:Owner 真机确认
Owner 在内部包 251 上确认 2026-11-02 各批工具箱修改真机通过(E4):导入 IPA、卸载、查看文件(含文件共享目录回退、加载提速、/SystemData 中文原因)、打开查看文件窗口后可从 Dock 退出、去掉“重新连接”与刷新按钮、设备信息九项。单设备(设备 A,iOS 26)。