@gracker/smartperfetto 1.0.20 → 1.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/dist/agent/config/domainManifest.d.ts +5 -3
  2. package/dist/agent/config/domainManifest.d.ts.map +1 -1
  3. package/dist/agent/config/domainManifest.js +120 -5
  4. package/dist/agent/config/domainManifest.js.map +1 -1
  5. package/dist/agent/core/orchestratorTypes.d.ts +5 -0
  6. package/dist/agent/core/orchestratorTypes.d.ts.map +1 -1
  7. package/dist/agent/core/orchestratorTypes.js.map +1 -1
  8. package/dist/agent/scene/buildSmartChatReport.d.ts +13 -0
  9. package/dist/agent/scene/buildSmartChatReport.d.ts.map +1 -0
  10. package/dist/agent/scene/buildSmartChatReport.js +333 -0
  11. package/dist/agent/scene/buildSmartChatReport.js.map +1 -0
  12. package/dist/agent/scene/sceneAnalysisJobRunner.d.ts +8 -4
  13. package/dist/agent/scene/sceneAnalysisJobRunner.d.ts.map +1 -1
  14. package/dist/agent/scene/sceneAnalysisJobRunner.js +78 -3
  15. package/dist/agent/scene/sceneAnalysisJobRunner.js.map +1 -1
  16. package/dist/agent/scene/sceneIntervalBuilder.d.ts +14 -5
  17. package/dist/agent/scene/sceneIntervalBuilder.d.ts.map +1 -1
  18. package/dist/agent/scene/sceneIntervalBuilder.js +578 -1
  19. package/dist/agent/scene/sceneIntervalBuilder.js.map +1 -1
  20. package/dist/agent/scene/sceneStage1Verifier.d.ts +8 -0
  21. package/dist/agent/scene/sceneStage1Verifier.d.ts.map +1 -0
  22. package/dist/agent/scene/sceneStage1Verifier.js +245 -0
  23. package/dist/agent/scene/sceneStage1Verifier.js.map +1 -0
  24. package/dist/agent/scene/sceneStoryService.d.ts +20 -2
  25. package/dist/agent/scene/sceneStoryService.d.ts.map +1 -1
  26. package/dist/agent/scene/sceneStoryService.js +238 -25
  27. package/dist/agent/scene/sceneStoryService.js.map +1 -1
  28. package/dist/agent/scene/smartCancelBridge.d.ts +13 -0
  29. package/dist/agent/scene/smartCancelBridge.d.ts.map +1 -0
  30. package/dist/agent/scene/smartCancelBridge.js +44 -0
  31. package/dist/agent/scene/smartCancelBridge.js.map +1 -0
  32. package/dist/agent/scene/smartDeepDiveDispatch.d.ts +15 -0
  33. package/dist/agent/scene/smartDeepDiveDispatch.d.ts.map +1 -0
  34. package/dist/agent/scene/smartDeepDiveDispatch.js +170 -0
  35. package/dist/agent/scene/smartDeepDiveDispatch.js.map +1 -0
  36. package/dist/agent/scene/types.d.ts +97 -6
  37. package/dist/agent/scene/types.d.ts.map +1 -1
  38. package/dist/agent/types.d.ts +1 -1
  39. package/dist/agent/types.d.ts.map +1 -1
  40. package/dist/agent/types.js.map +1 -1
  41. package/dist/agentv3/claudeMcpServer.d.ts.map +1 -1
  42. package/dist/agentv3/claudeMcpServer.js +3 -2
  43. package/dist/agentv3/claudeMcpServer.js.map +1 -1
  44. package/dist/agentv3/claudeSystemPrompt.d.ts.map +1 -1
  45. package/dist/agentv3/claudeSystemPrompt.js +8 -0
  46. package/dist/agentv3/claudeSystemPrompt.js.map +1 -1
  47. package/dist/agentv3/queryComplexityClassifier.d.ts +1 -1
  48. package/dist/agentv3/queryComplexityClassifier.d.ts.map +1 -1
  49. package/dist/agentv3/queryComplexityClassifier.js +20 -7
  50. package/dist/agentv3/queryComplexityClassifier.js.map +1 -1
  51. package/dist/agentv3/strategyLoader.d.ts +3 -0
  52. package/dist/agentv3/strategyLoader.d.ts.map +1 -1
  53. package/dist/agentv3/strategyLoader.js +17 -4
  54. package/dist/agentv3/strategyLoader.js.map +1 -1
  55. package/dist/agentv3/types.d.ts +5 -1
  56. package/dist/agentv3/types.d.ts.map +1 -1
  57. package/dist/agentv3/types.js.map +1 -1
  58. package/dist/config/index.d.ts +12 -0
  59. package/dist/config/index.d.ts.map +1 -1
  60. package/dist/config/index.js +6 -0
  61. package/dist/config/index.js.map +1 -1
  62. package/dist/routes/agent/finalizeAgentDrivenSession.d.ts +67 -0
  63. package/dist/routes/agent/finalizeAgentDrivenSession.d.ts.map +1 -0
  64. package/dist/routes/agent/finalizeAgentDrivenSession.js +103 -0
  65. package/dist/routes/agent/finalizeAgentDrivenSession.js.map +1 -0
  66. package/dist/routes/agent/normalizeAnalyzeOptions.d.ts +42 -0
  67. package/dist/routes/agent/normalizeAnalyzeOptions.d.ts.map +1 -0
  68. package/dist/routes/agent/normalizeAnalyzeOptions.js +185 -0
  69. package/dist/routes/agent/normalizeAnalyzeOptions.js.map +1 -0
  70. package/dist/routes/agentRoutes.d.ts.map +1 -1
  71. package/dist/routes/agentRoutes.js +285 -125
  72. package/dist/routes/agentRoutes.js.map +1 -1
  73. package/dist/scripts/verifyAgentSseScrolling.js +131 -8
  74. package/dist/scripts/verifyAgentSseScrolling.js.map +1 -1
  75. package/dist/services/sceneReport/sceneJobArtifactStore.d.ts +22 -0
  76. package/dist/services/sceneReport/sceneJobArtifactStore.d.ts.map +1 -0
  77. package/dist/services/sceneReport/sceneJobArtifactStore.js +65 -0
  78. package/dist/services/sceneReport/sceneJobArtifactStore.js.map +1 -0
  79. package/dist/services/sceneReport/sceneReportMemoryCache.d.ts +5 -3
  80. package/dist/services/sceneReport/sceneReportMemoryCache.d.ts.map +1 -1
  81. package/dist/services/sceneReport/sceneReportMemoryCache.js +15 -10
  82. package/dist/services/sceneReport/sceneReportMemoryCache.js.map +1 -1
  83. package/dist/services/sceneReport/sceneReportStore.d.ts +9 -4
  84. package/dist/services/sceneReport/sceneReportStore.d.ts.map +1 -1
  85. package/dist/services/sceneReport/sceneReportStore.js +27 -7
  86. package/dist/services/sceneReport/sceneReportStore.js.map +1 -1
  87. package/dist/services/traceProcessor/sqlSemaphore.d.ts +16 -0
  88. package/dist/services/traceProcessor/sqlSemaphore.d.ts.map +1 -0
  89. package/dist/services/traceProcessor/sqlSemaphore.js +63 -0
  90. package/dist/services/traceProcessor/sqlSemaphore.js.map +1 -0
  91. package/dist/types/dataContract.d.ts +1 -1
  92. package/dist/types/dataContract.d.ts.map +1 -1
  93. package/dist/types/dataContract.js +2 -0
  94. package/dist/types/dataContract.js.map +1 -1
  95. package/package.json +1 -1
  96. package/skills/README.md +13 -5
  97. package/skills/composite/device_state_snapshot.skill.yaml +204 -0
  98. package/skills/composite/scene_reconstruction.skill.yaml +5 -1
  99. package/skills/composite/selection_range_cpu_sched_summary.skill.yaml +428 -0
  100. package/strategies/scene-reconstruction-verifier.template.md +18 -0
  101. package/strategies/scrolling.strategy.md +5 -1
  102. package/strategies/selection-area.template.md +24 -9
  103. package/strategies/smart.strategy.md +53 -0
  104. package/skills/atomic/device_state_snapshot.skill.yaml +0 -171
@@ -0,0 +1,204 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2024-2026 Gracker (Chris)
3
+ # This file is part of SmartPerfetto. See LICENSE for details.
4
+
5
+ # =============================================================================
6
+ # 设备环境状态快照(组合 Skill)
7
+ # =============================================================================
8
+ # 采集指定窗口内的屏幕、电量、温度、频率、内存压力与 idle 状态。
9
+
10
+ name: device_state_snapshot
11
+ version: "2.0"
12
+ type: composite
13
+ category: system_context
14
+ tier: B
15
+
16
+ meta:
17
+ display_name: "设备环境状态快照"
18
+ description: "采集 trace 期间的设备环境信息(屏幕、电量、温度、CPU、内存、idle 窗口等)"
19
+ icon: "devices"
20
+ tags: [device, state, environment, context, composite]
21
+
22
+ prerequisites:
23
+ required_tables:
24
+ - slice
25
+ - counter
26
+ - counter_track
27
+
28
+ inputs:
29
+ - name: start_ts
30
+ type: timestamp
31
+ required: false
32
+ description: "分析起始时间戳(ns)"
33
+ - name: end_ts
34
+ type: timestamp
35
+ required: false
36
+ description: "分析结束时间戳(ns)"
37
+
38
+ steps:
39
+ - id: environment_snapshot
40
+ type: atomic
41
+ name: "设备环境快照"
42
+ display:
43
+ level: summary
44
+ layer: overview
45
+ title: "设备环境状态"
46
+ columns:
47
+ - name: metric
48
+ label: "指标"
49
+ type: string
50
+ - name: value
51
+ label: "值"
52
+ type: string
53
+ - name: ts
54
+ label: "时间戳"
55
+ type: timestamp
56
+ unit: ns
57
+ clickAction: navigate_timeline
58
+ sql: |
59
+ WITH trace_bounds AS (
60
+ SELECT
61
+ COALESCE(${start_ts}, (SELECT MIN(ts) FROM slice)) as t_start,
62
+ COALESCE(${end_ts}, (SELECT MAX(ts + dur) FROM slice)) as t_end
63
+ ),
64
+ screen_state AS (
65
+ SELECT
66
+ 'screen_state' as metric,
67
+ CASE WHEN c.value > 0 THEN 'ON' ELSE 'OFF' END as value,
68
+ c.ts as ts
69
+ FROM counter c
70
+ JOIN counter_track ct ON c.track_id = ct.id
71
+ CROSS JOIN trace_bounds tb
72
+ WHERE (ct.name GLOB '*ScreenState*' OR ct.name GLOB '*screen_state*')
73
+ AND c.ts >= tb.t_start AND c.ts <= tb.t_end
74
+ ORDER BY c.ts DESC
75
+ LIMIT 1
76
+ ),
77
+ battery_info AS (
78
+ SELECT
79
+ 'battery_level' as metric,
80
+ CAST(c.value AS TEXT) as value,
81
+ c.ts as ts
82
+ FROM counter c
83
+ JOIN counter_track ct ON c.track_id = ct.id
84
+ CROSS JOIN trace_bounds tb
85
+ WHERE (ct.name GLOB '*battery_level*' OR ct.name GLOB '*BatteryLevel*')
86
+ AND c.ts >= tb.t_start AND c.ts <= tb.t_end
87
+ ORDER BY c.ts DESC
88
+ LIMIT 1
89
+ ),
90
+ charging_info AS (
91
+ SELECT
92
+ 'charging_state' as metric,
93
+ CASE WHEN c.value > 0 THEN 'CHARGING' ELSE 'NOT_CHARGING' END as value,
94
+ c.ts as ts
95
+ FROM counter c
96
+ JOIN counter_track ct ON c.track_id = ct.id
97
+ CROSS JOIN trace_bounds tb
98
+ WHERE (ct.name GLOB '*battery_status*' OR ct.name GLOB '*charging*' OR ct.name GLOB '*BatteryStatus*')
99
+ AND c.ts >= tb.t_start AND c.ts <= tb.t_end
100
+ ORDER BY c.ts DESC
101
+ LIMIT 1
102
+ ),
103
+ thermal_info AS (
104
+ SELECT
105
+ 'thermal_zone' as metric,
106
+ ct.name || ': ' || CAST(ROUND(MAX(CASE WHEN c.value > 1000 THEN c.value / 1000.0 ELSE c.value END), 1) AS TEXT) || ' C' as value,
107
+ MAX(c.ts) as ts
108
+ FROM counter c
109
+ JOIN counter_track ct ON c.track_id = ct.id
110
+ CROSS JOIN trace_bounds tb
111
+ WHERE (ct.name GLOB '*thermal_zone*' OR ct.name GLOB '*Temperature*' OR ct.name GLOB '*temp*')
112
+ AND c.ts >= tb.t_start AND c.ts <= tb.t_end
113
+ GROUP BY ct.name
114
+ ORDER BY MAX(c.value) DESC
115
+ LIMIT 5
116
+ ),
117
+ memory_info AS (
118
+ SELECT
119
+ 'memory_pressure' as metric,
120
+ ct.name || ': ' || CAST(ROUND(MAX(c.value), 0) AS TEXT) as value,
121
+ MAX(c.ts) as ts
122
+ FROM counter c
123
+ JOIN counter_track ct ON c.track_id = ct.id
124
+ CROSS JOIN trace_bounds tb
125
+ WHERE (ct.name GLOB '*mem.*pressure*' OR ct.name GLOB '*MemFree*' OR ct.name GLOB '*MemAvailable*' OR ct.name GLOB '*psi*mem*')
126
+ AND c.ts >= tb.t_start AND c.ts <= tb.t_end
127
+ GROUP BY ct.name
128
+ ORDER BY MAX(c.value) DESC
129
+ LIMIT 5
130
+ )
131
+ SELECT metric, value, printf('%d', ts) as ts FROM screen_state
132
+ UNION ALL SELECT metric, value, printf('%d', ts) as ts FROM battery_info
133
+ UNION ALL SELECT metric, value, printf('%d', ts) as ts FROM charging_info
134
+ UNION ALL SELECT metric, value, printf('%d', ts) as ts FROM thermal_info
135
+ UNION ALL SELECT metric, value, printf('%d', ts) as ts FROM memory_info
136
+ save_as: environment_snapshot
137
+ synthesize:
138
+ role: overview
139
+ fields:
140
+ - key: metric
141
+ label: 指标
142
+ - key: value
143
+ label: 值
144
+
145
+ - id: idle_window_summary
146
+ type: atomic
147
+ name: "Idle 窗口摘要"
148
+ optional: true
149
+ display:
150
+ level: detail
151
+ layer: list
152
+ title: "Idle / 空闲窗口"
153
+ columns:
154
+ - name: start_ts
155
+ label: "开始"
156
+ type: timestamp
157
+ unit: ns
158
+ clickAction: navigate_timeline
159
+ - name: end_ts
160
+ label: "结束"
161
+ type: timestamp
162
+ unit: ns
163
+ clickAction: navigate_timeline
164
+ - name: dur_ms
165
+ label: "时长"
166
+ type: duration
167
+ unit: ms
168
+ - name: source
169
+ label: "来源"
170
+ type: string
171
+ sql: |
172
+ WITH trace_bounds AS (
173
+ SELECT
174
+ COALESCE(${start_ts}, (SELECT MIN(ts) FROM slice)) as t_start,
175
+ COALESCE(${end_ts}, (SELECT MAX(ts + dur) FROM slice)) as t_end
176
+ )
177
+ SELECT
178
+ printf('%d', s.ts) as start_ts,
179
+ printf('%d', s.ts + s.dur) as end_ts,
180
+ ROUND(s.dur / 1000000.0, 2) as dur_ms,
181
+ s.name as source
182
+ FROM slice s
183
+ CROSS JOIN trace_bounds tb
184
+ WHERE s.dur > 0
185
+ AND s.ts + s.dur > tb.t_start
186
+ AND s.ts < tb.t_end
187
+ AND (
188
+ s.name GLOB '*idle*'
189
+ OR s.name GLOB '*Idle*'
190
+ OR s.name GLOB '*suspend*'
191
+ OR s.name GLOB '*doze*'
192
+ )
193
+ ORDER BY s.dur DESC
194
+ LIMIT 20
195
+ save_as: idle_window_summary
196
+ synthesize:
197
+ role: list
198
+ fields:
199
+ - key: dur_ms
200
+ label: Idle 时长
201
+ format: "{{value}} ms"
202
+
203
+ output:
204
+ format: structured
@@ -573,6 +573,9 @@ steps:
573
573
  - name: gesture_id
574
574
  label: "手势ID"
575
575
  type: number
576
+ - name: app_package
577
+ label: "前台应用"
578
+ type: string
576
579
  - name: category
577
580
  label: "类别"
578
581
  type: string
@@ -633,6 +636,7 @@ steps:
633
636
  ELSE ''
634
637
  END AS event,
635
638
  gesture_id,
639
+ app AS app_package,
636
640
  'scroll_start' AS category,
637
641
  '💡 此时刻手指已移动足够距离,系统开始响应滑动' AS explanation
638
642
  FROM scroll_starts
@@ -2785,4 +2789,4 @@ steps:
2785
2789
 
2786
2790
  output:
2787
2791
  format: layered
2788
- default_expanded: [L1, L2]
2792
+ default_expanded: [L1, L2]
@@ -0,0 +1,428 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ # Copyright (C) 2024-2026 Gracker (Chris)
3
+ # This file is part of SmartPerfetto. See LICENSE for details.
4
+
5
+ name: selection_range_cpu_sched_summary
6
+ version: "1.0"
7
+ type: composite
8
+ category: kernel
9
+ tier: B
10
+
11
+ meta:
12
+ display_name: "选区 CPU 调度与频率摘要"
13
+ description: "面向用户选区/可见窗口的快速 CPU 摆核、Running 排名、四象限和频率分布分析"
14
+ icon: "speed"
15
+ tags: [selection, range, cpu, sched, frequency, quadrant, migration, quick]
16
+
17
+ triggers:
18
+ keywords:
19
+ zh: [选区, 这一段, 这段, 摆核, 核心摆放, 平均频率, 频率分布, Running 排名, 四象限]
20
+ en: [selection range, current window, cpu placement, core placement, average frequency, frequency distribution, running ranking, quadrant]
21
+ patterns:
22
+ - ".*(选区|这段|这一段).*(CPU|摆核|核心|频率|Running|四象限).*"
23
+ - ".*(selected|current).*(range|window).*(cpu|core|frequency|running|quadrant).*"
24
+
25
+ prerequisites:
26
+ required_tables:
27
+ - thread_state
28
+ - thread
29
+ - process
30
+ modules:
31
+ - sched
32
+ - linux.cpu.frequency
33
+
34
+
35
+ inputs:
36
+ - name: start_ts
37
+ type: timestamp
38
+ required: true
39
+ description: "选区起始时间戳(ns)"
40
+ - name: end_ts
41
+ type: timestamp
42
+ required: true
43
+ description: "选区结束时间戳(ns)"
44
+ - name: package
45
+ type: string
46
+ required: false
47
+ description: "可选进程名过滤,支持 GLOB 前缀匹配"
48
+ - name: thread_name
49
+ type: string
50
+ required: false
51
+ description: "可选线程名包含匹配"
52
+ - name: top_k
53
+ type: number
54
+ required: false
55
+ description: "线程/进程排名返回数量"
56
+ - name: freq_bucket_mhz
57
+ type: number
58
+ required: false
59
+ description: "频率分布桶大小,单位 MHz"
60
+
61
+ steps:
62
+ - id: init_cpu_topology
63
+ type: skill
64
+ name: "初始化 CPU 拓扑"
65
+ skill: cpu_topology_view
66
+ params:
67
+ start_ts: "${start_ts}"
68
+ end_ts: "${end_ts}"
69
+ display:
70
+ level: hidden
71
+ optional: true
72
+
73
+ - id: running_thread_quadrants
74
+ type: atomic
75
+ name: "Running 线程四象限与摆核"
76
+ optional: true
77
+ display:
78
+ level: key
79
+ layer: overview
80
+ title: "选区 Running 线程四象限"
81
+ columns:
82
+ - name: thread_name
83
+ label: "线程"
84
+ type: string
85
+ - name: process_name
86
+ label: "进程"
87
+ type: string
88
+ - name: tid
89
+ label: "TID"
90
+ type: number
91
+ - name: total_cpu_ms
92
+ label: "CPU 时间"
93
+ type: duration
94
+ format: duration_ms
95
+ unit: ms
96
+ - name: q1_perf_running_ms
97
+ label: "Q1 性能核运行"
98
+ type: duration
99
+ format: duration_ms
100
+ unit: ms
101
+ - name: q2_little_running_ms
102
+ label: "Q2 小核运行"
103
+ type: duration
104
+ format: duration_ms
105
+ unit: ms
106
+ - name: q3_runnable_ms
107
+ label: "Q3 等待调度"
108
+ type: duration
109
+ format: duration_ms
110
+ unit: ms
111
+ - name: q4a_io_blocked_ms
112
+ label: "Q4a IO 阻塞"
113
+ type: duration
114
+ format: duration_ms
115
+ unit: ms
116
+ - name: q4b_sleeping_ms
117
+ label: "Q4b 睡眠等待"
118
+ type: duration
119
+ format: duration_ms
120
+ unit: ms
121
+ - name: perf_core_pct
122
+ label: "性能核占比"
123
+ type: percentage
124
+ format: percentage
125
+ - name: running_cpus
126
+ label: "运行 CPU"
127
+ type: string
128
+ - name: running_core_types
129
+ label: "核心类型"
130
+ type: string
131
+ - name: migrations
132
+ label: "核迁移"
133
+ type: number
134
+ - name: cross_cluster_migrations
135
+ label: "跨簇迁移"
136
+ type: number
137
+ sql: |
138
+ WITH
139
+ target_threads AS (
140
+ SELECT
141
+ t.utid,
142
+ t.tid,
143
+ COALESCE(t.name, '<unknown>') AS thread_name,
144
+ p.upid,
145
+ p.pid,
146
+ COALESCE(p.name, '<unknown>') AS process_name
147
+ FROM thread t
148
+ LEFT JOIN process p ON t.upid = p.upid
149
+ WHERE ('${package|}' = '' OR COALESCE(p.name, '') GLOB '${package|}*')
150
+ AND ('${thread_name|}' = '' OR COALESCE(t.name, '') GLOB '*${thread_name|}*')
151
+ ),
152
+ states AS (
153
+ SELECT
154
+ tt.utid,
155
+ tt.tid,
156
+ tt.thread_name,
157
+ tt.process_name,
158
+ ts.ts,
159
+ ts.state,
160
+ ts.cpu,
161
+ COALESCE(ct.core_type, 'unknown') AS core_type,
162
+ MIN(ts.ts + ts.dur, ${end_ts}) - MAX(ts.ts, ${start_ts}) AS clipped_dur
163
+ FROM thread_state ts
164
+ JOIN target_threads tt ON ts.utid = tt.utid
165
+ LEFT JOIN _cpu_topology ct ON ts.cpu = ct.cpu_id
166
+ WHERE ts.ts < ${end_ts}
167
+ AND ts.ts + ts.dur > ${start_ts}
168
+ AND ts.dur > 0
169
+ ),
170
+ running_events AS (
171
+ SELECT
172
+ utid,
173
+ ts,
174
+ cpu,
175
+ core_type,
176
+ LAG(cpu) OVER (PARTITION BY utid ORDER BY ts) AS prev_cpu,
177
+ LAG(core_type) OVER (PARTITION BY utid ORDER BY ts) AS prev_core_type
178
+ FROM states
179
+ WHERE state = 'Running' AND clipped_dur > 0
180
+ ),
181
+ migrations AS (
182
+ SELECT
183
+ utid,
184
+ SUM(CASE WHEN prev_cpu IS NOT NULL AND cpu != prev_cpu THEN 1 ELSE 0 END) AS migrations,
185
+ SUM(CASE WHEN prev_cpu IS NOT NULL AND cpu != prev_cpu AND core_type != prev_core_type THEN 1 ELSE 0 END) AS cross_cluster_migrations
186
+ FROM running_events
187
+ GROUP BY utid
188
+ )
189
+ SELECT
190
+ s.thread_name,
191
+ s.process_name,
192
+ s.tid,
193
+ ROUND(SUM(CASE WHEN s.state = 'Running' THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS total_cpu_ms,
194
+ ROUND(SUM(CASE WHEN s.state = 'Running' AND s.core_type IN ('prime', 'big', 'medium') THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS q1_perf_running_ms,
195
+ ROUND(SUM(CASE WHEN s.state = 'Running' AND s.core_type = 'little' THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS q2_little_running_ms,
196
+ ROUND(SUM(CASE WHEN s.state IN ('R', 'R+') THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS q3_runnable_ms,
197
+ ROUND(SUM(CASE WHEN s.state IN ('D', 'DK') THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS q4a_io_blocked_ms,
198
+ ROUND(SUM(CASE WHEN s.state IN ('S', 'I') THEN s.clipped_dur ELSE 0 END) / 1e6, 2) AS q4b_sleeping_ms,
199
+ ROUND(100.0 * SUM(CASE WHEN s.state = 'Running' AND s.core_type IN ('prime', 'big', 'medium') THEN s.clipped_dur ELSE 0 END)
200
+ / NULLIF(SUM(CASE WHEN s.state = 'Running' THEN s.clipped_dur ELSE 0 END), 0), 1) AS perf_core_pct,
201
+ GROUP_CONCAT(DISTINCT CASE WHEN s.state = 'Running' THEN s.cpu END) AS running_cpus,
202
+ GROUP_CONCAT(DISTINCT CASE WHEN s.state = 'Running' THEN s.core_type END) AS running_core_types,
203
+ COALESCE(m.migrations, 0) AS migrations,
204
+ COALESCE(m.cross_cluster_migrations, 0) AS cross_cluster_migrations
205
+ FROM states s
206
+ LEFT JOIN migrations m ON s.utid = m.utid
207
+ WHERE s.clipped_dur > 0
208
+ GROUP BY s.utid
209
+ HAVING total_cpu_ms > 0
210
+ ORDER BY total_cpu_ms DESC
211
+ LIMIT ${top_k|20}
212
+ save_as: running_thread_quadrants
213
+
214
+ - id: running_process_ranking
215
+ type: atomic
216
+ name: "Running 进程排名"
217
+ optional: true
218
+ display:
219
+ level: summary
220
+ layer: list
221
+ title: "选区 Running 进程排名"
222
+ columns:
223
+ - name: process_name
224
+ label: "进程"
225
+ type: string
226
+ - name: pid
227
+ label: "PID"
228
+ type: number
229
+ - name: running_ms
230
+ label: "Running 时间"
231
+ type: duration
232
+ format: duration_ms
233
+ unit: ms
234
+ - name: thread_count
235
+ label: "线程数"
236
+ type: number
237
+ sql: |
238
+ SELECT
239
+ COALESCE(p.name, '<unknown>') AS process_name,
240
+ p.pid,
241
+ ROUND(SUM(MIN(ts.ts + ts.dur, ${end_ts}) - MAX(ts.ts, ${start_ts})) / 1e6, 2) AS running_ms,
242
+ COUNT(DISTINCT ts.utid) AS thread_count
243
+ FROM thread_state ts
244
+ JOIN thread t ON ts.utid = t.utid
245
+ LEFT JOIN process p ON t.upid = p.upid
246
+ WHERE ts.ts < ${end_ts}
247
+ AND ts.ts + ts.dur > ${start_ts}
248
+ AND ts.dur > 0
249
+ AND ts.state = 'Running'
250
+ AND ('${package|}' = '' OR COALESCE(p.name, '') GLOB '${package|}*')
251
+ AND ('${thread_name|}' = '' OR COALESCE(t.name, '') GLOB '*${thread_name|}*')
252
+ GROUP BY p.upid
253
+ ORDER BY running_ms DESC
254
+ LIMIT ${top_k|20}
255
+ save_as: running_process_ranking
256
+
257
+ - id: cpu_freq_by_core
258
+ type: atomic
259
+ name: "各核 duration-weighted 频率"
260
+ optional: true
261
+ display:
262
+ level: summary
263
+ layer: list
264
+ title: "选区各核平均频率"
265
+ columns:
266
+ - name: cpu
267
+ label: "CPU"
268
+ type: number
269
+ - name: core_type
270
+ label: "核心类型"
271
+ type: string
272
+ - name: avg_freq_mhz
273
+ label: "平均频率"
274
+ type: number
275
+ - name: min_freq_mhz
276
+ label: "最低频率"
277
+ type: number
278
+ - name: max_freq_mhz
279
+ label: "最高频率"
280
+ type: number
281
+ - name: covered_ms
282
+ label: "覆盖时长"
283
+ type: duration
284
+ format: duration_ms
285
+ unit: ms
286
+ sql: |
287
+ WITH
288
+ cpu_tracks AS (
289
+ SELECT id, cpu
290
+ FROM cpu_counter_track
291
+ WHERE name = 'cpufreq' AND cpu IS NOT NULL
292
+ ),
293
+ freq_points AS (
294
+ SELECT
295
+ t.cpu,
296
+ ${start_ts} AS ts,
297
+ (
298
+ SELECT c2.value
299
+ FROM counter c2
300
+ WHERE c2.track_id = t.id AND c2.ts <= ${start_ts}
301
+ ORDER BY c2.ts DESC
302
+ LIMIT 1
303
+ ) AS freq_khz,
304
+ 0 AS source_order
305
+ FROM cpu_tracks t
306
+ UNION ALL
307
+ SELECT t.cpu, c.ts, c.value AS freq_khz, 1 AS source_order
308
+ FROM counter c
309
+ JOIN cpu_tracks t ON c.track_id = t.id
310
+ WHERE c.ts >= ${start_ts} AND c.ts < ${end_ts}
311
+ ),
312
+ freq_spans AS (
313
+ SELECT
314
+ cpu,
315
+ freq_khz,
316
+ ts,
317
+ LEAD(ts, 1, ${end_ts}) OVER (PARTITION BY cpu ORDER BY ts, source_order) AS next_ts
318
+ FROM freq_points
319
+ WHERE freq_khz IS NOT NULL AND freq_khz > 0
320
+ ),
321
+ clipped AS (
322
+ SELECT
323
+ cpu,
324
+ freq_khz,
325
+ MIN(next_ts, ${end_ts}) - MAX(ts, ${start_ts}) AS dur_ns
326
+ FROM freq_spans
327
+ WHERE ts < ${end_ts} AND next_ts > ${start_ts}
328
+ )
329
+ SELECT
330
+ c.cpu,
331
+ COALESCE(ct.core_type, 'unknown') AS core_type,
332
+ ROUND(SUM(c.freq_khz * c.dur_ns) / NULLIF(SUM(c.dur_ns), 0) / 1000, 0) AS avg_freq_mhz,
333
+ ROUND(MIN(c.freq_khz) / 1000, 0) AS min_freq_mhz,
334
+ ROUND(MAX(c.freq_khz) / 1000, 0) AS max_freq_mhz,
335
+ ROUND(SUM(c.dur_ns) / 1e6, 2) AS covered_ms
336
+ FROM clipped c
337
+ LEFT JOIN _cpu_topology ct ON c.cpu = ct.cpu_id
338
+ WHERE c.dur_ns > 0
339
+ GROUP BY c.cpu
340
+ ORDER BY c.cpu
341
+ save_as: cpu_freq_by_core
342
+
343
+ - id: cpu_freq_distribution
344
+ type: atomic
345
+ name: "各核频率分布"
346
+ optional: true
347
+ display:
348
+ level: detail
349
+ layer: deep
350
+ title: "选区各核频率分布"
351
+ columns:
352
+ - name: cpu
353
+ label: "CPU"
354
+ type: number
355
+ - name: core_type
356
+ label: "核心类型"
357
+ type: string
358
+ - name: freq_mhz_bucket
359
+ label: "频率桶"
360
+ type: number
361
+ - name: duration_ms
362
+ label: "时长"
363
+ type: duration
364
+ format: duration_ms
365
+ unit: ms
366
+ - name: pct_of_range
367
+ label: "区间占比"
368
+ type: percentage
369
+ format: percentage
370
+ sql: |
371
+ WITH
372
+ cpu_tracks AS (
373
+ SELECT id, cpu
374
+ FROM cpu_counter_track
375
+ WHERE name = 'cpufreq' AND cpu IS NOT NULL
376
+ ),
377
+ freq_points AS (
378
+ SELECT
379
+ t.cpu,
380
+ ${start_ts} AS ts,
381
+ (
382
+ SELECT c2.value
383
+ FROM counter c2
384
+ WHERE c2.track_id = t.id AND c2.ts <= ${start_ts}
385
+ ORDER BY c2.ts DESC
386
+ LIMIT 1
387
+ ) AS freq_khz,
388
+ 0 AS source_order
389
+ FROM cpu_tracks t
390
+ UNION ALL
391
+ SELECT t.cpu, c.ts, c.value AS freq_khz, 1 AS source_order
392
+ FROM counter c
393
+ JOIN cpu_tracks t ON c.track_id = t.id
394
+ WHERE c.ts >= ${start_ts} AND c.ts < ${end_ts}
395
+ ),
396
+ freq_spans AS (
397
+ SELECT
398
+ cpu,
399
+ freq_khz,
400
+ ts,
401
+ LEAD(ts, 1, ${end_ts}) OVER (PARTITION BY cpu ORDER BY ts, source_order) AS next_ts
402
+ FROM freq_points
403
+ WHERE freq_khz IS NOT NULL AND freq_khz > 0
404
+ ),
405
+ clipped AS (
406
+ SELECT
407
+ cpu,
408
+ CAST(ROUND(freq_khz / (${freq_bucket_mhz|100} * 1000.0)) * ${freq_bucket_mhz|100} AS INTEGER) AS freq_mhz_bucket,
409
+ MIN(next_ts, ${end_ts}) - MAX(ts, ${start_ts}) AS dur_ns
410
+ FROM freq_spans
411
+ WHERE ts < ${end_ts} AND next_ts > ${start_ts}
412
+ )
413
+ SELECT
414
+ c.cpu,
415
+ COALESCE(ct.core_type, 'unknown') AS core_type,
416
+ c.freq_mhz_bucket,
417
+ ROUND(SUM(c.dur_ns) / 1e6, 2) AS duration_ms,
418
+ ROUND(100.0 * SUM(c.dur_ns) / NULLIF(${end_ts} - ${start_ts}, 0), 1) AS pct_of_range
419
+ FROM clipped c
420
+ LEFT JOIN _cpu_topology ct ON c.cpu = ct.cpu_id
421
+ WHERE c.dur_ns > 0
422
+ GROUP BY c.cpu, c.freq_mhz_bucket
423
+ ORDER BY c.cpu, duration_ms DESC
424
+ LIMIT 100
425
+ save_as: cpu_freq_distribution
426
+
427
+ output:
428
+ format: structured
@@ -0,0 +1,18 @@
1
+ <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
2
+ <!-- Copyright (C) 2024-2026 Gracker (Chris) -->
3
+ <!-- This file is part of SmartPerfetto. See LICENSE for details. -->
4
+
5
+ 你是 Android Perfetto trace 的场景还原复核器。请检查下面的 Smart 场景时间线是否存在明显的拆分、合并、类型或归因问题。
6
+
7
+ 只能基于输入证据判断,不要创造没有证据的场景。不要输出长报告。
8
+
9
+ 请只输出 JSON,格式:{"status":"passed|needs_review","summary":"一句中文复核意见"}。
10
+
11
+ deterministic_summary:
12
+ {{deterministicSummary}}
13
+
14
+ deterministic_issues:
15
+ {{deterministicIssues}}
16
+
17
+ scenes:
18
+ {{scenes}}
@@ -107,7 +107,7 @@ phase_hints:
107
107
  critical: false
108
108
  - id: conclusion
109
109
  keywords: ['结论', 'conclusion', '输出', 'output', '报告', 'report', '总结']
110
- constraints: '输出必须包含:全帧根因分布表(按 reason_code 聚合)+ 代表帧分析(含四象限+频率+根因推理链)+ 按优先级排序的优化建议。每个 CRITICAL/HIGH 必须有量化证据+因果链。'
110
+ constraints: '输出必须包含:全帧根因分布表(按 reason_code 聚合)+ 代表帧分析(含四象限+频率+根因推理链)+ 按优先级排序的优化建议。每个 CRITICAL/HIGH 必须有量化证据+因果链。若深钻证据纠正了 batch reason_code(例如 lock_binder_wait 但 binder_overlap_ms=0,render_slices_json 指向 cache_miss/makePipeline/shader 编译),最终结论必须使用纠正后的根因命名,并明确标注原 reason_code 为误分类。'
111
111
  critical_tools: []
112
112
  critical: false
113
113
 
@@ -347,6 +347,10 @@ Phase 1 的 `batch_frame_root_cause` 已包含每帧的**完整统计数据**(
347
347
  - **reason_code 为 unknown 且帧数 >5%**:必须对至少 1 帧调用 jank_frame_detail 获取更多线索,不能在分布表中仅标记"未分类"就跳过
348
348
  - reason_code 与实际数据矛盾时(如 `lock_binder_wait` 但 Binder 耗时 0ms):应在结论中标注可能的误分类原因
349
349
 
350
+ 如果深钻结果已给出更具体的根因,不要在最终报告继续把原始 `reason_code` 当作根因名称。典型例子:
351
+ - `lock_binder_wait` 但 `binder_overlap_ms=0`,且 `render_slices_json` 出现 `cache_miss: makePipeline` / shader 编译 / Vulkan finish frame / `postAndWait`,最终根因应写成 **shader_compile + sync_wait** 或等价机制,并说明 `lock_binder_wait` 是批量分类误判。
352
+ - `workload_heavy` 但 `main_slices_json` 明确指向应用自定义方法,最终根因应写具体方法名和所处阶段,例如 `CustomScroll_longFrameLoad` 在 ANIMATION 回调同步执行,而不是只写 "workload_heavy"。
353
+
350
354
  `frame_blocking_calls` 是 Phase 1.9 的帧内阻塞证据补充,不占 `jank_frame_detail` 的 2 帧上限。遇到 Binder/IO/futex/锁相关根因时,优先用它确认阻塞调用是否真的与掉帧帧重叠。
351
355
 
352
356
  ```