@swmansion/argent 0.10.0 → 0.11.0
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.
- package/README.md +4 -1
- package/assets/argent.tracecfg.pbtxt +133 -0
- package/assets/queries/README.md +93 -0
- package/assets/queries/cpu-hotspots.sql +106 -0
- package/assets/queries/function-callers.sql +62 -0
- package/assets/queries/hang-folds-batched.sql +88 -0
- package/assets/queries/hang-main-thread-samples.sql +32 -0
- package/assets/queries/hang-state-breakdown.sql +44 -0
- package/assets/queries/memory-rss.sql +25 -0
- package/assets/queries/thread-breakdown.sql +34 -0
- package/assets/queries/trace-bounds.sql +6 -0
- package/assets/queries/ui-hangs.sql +59 -0
- package/assets/trace-processor/LICENSE +235 -0
- package/assets/trace-processor/SHA256SUMS +4 -0
- package/assets/trace-processor/engine.mjs +42423 -0
- package/assets/trace-processor/engine_bundle.node.js +11130 -0
- package/assets/trace-processor/trace_processor.wasm +0 -0
- package/{dist → bin}/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/darwin/ax-service +0 -0
- package/dist/cli-cmds.mjs +868 -701
- package/dist/installer.mjs +9 -0
- package/dist/mcp-server.mjs +297 -79
- package/dist/tool-server.cjs +3270 -1413
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +2 -2
- package/skills/argent-native-profiler/SKILL.md +5 -5
- /package/{dist → assets}/Argent.tracetemplate +0 -0
- /package/{manifest.json → assets/manifest.json} +0 -0
package/README.md
CHANGED
|
@@ -4,7 +4,10 @@
|
|
|
4
4
|
<img width="1100" height="382" alt="argent-header" src="https://github.com/user-attachments/assets/6cec01d5-da3c-4b6c-97c3-0374a63c213c" />
|
|
5
5
|
</a>
|
|
6
6
|
</p>
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-1&n=1)
|
|
9
|
+
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-2&n=1)
|
|
10
|
+
[](https://swm-delivery.com/www/delivery/ck-slug.php?zoneid=zone-gh-argent-3&n=1)
|
|
8
11
|
|
|
9
12
|
**[Argent](https://argent.swmansion.com)** is an **agentic toolkit** that gives your AI assistant direct access to iOS Simulators and Android Emulators. Ask it to tap a button, run a profiler or reproduce an issue manually - all from within your CLI, without switching context.
|
|
10
13
|
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Argent — Perfetto TraceConfig (text protobuf)
|
|
2
|
+
#
|
|
3
|
+
# The Android twin of packages/tool-server/src/utils/ios-profiler/Argent.tracetemplate.
|
|
4
|
+
# Pushed to the device and consumed by `perfetto --txt -c <this-file>`.
|
|
5
|
+
#
|
|
6
|
+
# In production this is template-substituted: TARGET_CMDLINE_PLACEHOLDER and
|
|
7
|
+
# TARGET_PACKAGE_PLACEHOLDER are replaced with the auto-detected foreground app
|
|
8
|
+
# (mirrors detectRunningApp in native-profiler-start.ts:46-117).
|
|
9
|
+
#
|
|
10
|
+
# Buffers sized for ~5 minutes of capture on a typical RN app. Adjust if you
|
|
11
|
+
# extend RECORDING_CAP_MS beyond the iOS 10-min default.
|
|
12
|
+
|
|
13
|
+
# duration_ms is intentionally omitted — the recording is stopped explicitly via
|
|
14
|
+
# `kill -TERM <perfetto-pid>` from native-profiler-stop, mirroring xctrace SIGINT.
|
|
15
|
+
# If you want a hard cap, set duration_ms here AND keep the stop signal as a
|
|
16
|
+
# safety net (the perfetto daemon will exit either way).
|
|
17
|
+
|
|
18
|
+
buffers: {
|
|
19
|
+
size_kb: 65536 # 64 MB — CPU samples + atrace slices
|
|
20
|
+
fill_policy: DISCARD # ring-buffer once full; we want recency over completeness
|
|
21
|
+
}
|
|
22
|
+
buffers: {
|
|
23
|
+
size_kb: 16384 # 16 MB — frame timeline + ANRs (sparser, smaller buffer)
|
|
24
|
+
fill_policy: DISCARD
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# ----------------------------------------------------------------------------
|
|
28
|
+
# Data source 1: CPU callstack sampling (the Time Profiler equivalent)
|
|
29
|
+
# ----------------------------------------------------------------------------
|
|
30
|
+
# Outputs perf_sample rows we GROUP BY callsite + thread in queries/cpu-hotspots.sql.
|
|
31
|
+
# target_cmdline restricts sampling to the user's app — system-wide noise is
|
|
32
|
+
# excluded at capture time, not analysis time.
|
|
33
|
+
data_sources: {
|
|
34
|
+
config: {
|
|
35
|
+
name: "linux.perf"
|
|
36
|
+
target_buffer: 0
|
|
37
|
+
perf_event_config: {
|
|
38
|
+
timebase: {
|
|
39
|
+
# 100 Hz software CPU clock — matches the iOS coresampler2 default.
|
|
40
|
+
# Adjust higher (e.g. 1000) for tight CPU-bound investigations.
|
|
41
|
+
counter: SW_CPU_CLOCK
|
|
42
|
+
frequency: 100
|
|
43
|
+
timestamp_clock: PERF_CLOCK_MONOTONIC
|
|
44
|
+
}
|
|
45
|
+
callstack_sampling: {
|
|
46
|
+
scope: {
|
|
47
|
+
target_cmdline: "TARGET_CMDLINE_PLACEHOLDER"
|
|
48
|
+
# Drop the next two if Perfetto warns about missing /proc access:
|
|
49
|
+
# target_installed_by: "PLAY_STORE" # only for production-builds w/ trusted source
|
|
50
|
+
}
|
|
51
|
+
kernel_frames: true # include kernel callsites; great for "stuck in syscall" diagnosis
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# ----------------------------------------------------------------------------
|
|
58
|
+
# Data source 2: kernel scheduler events + atrace app slices
|
|
59
|
+
# ----------------------------------------------------------------------------
|
|
60
|
+
# Feeds thread_state table; used in queries/ui-hangs.sql to attribute main-thread
|
|
61
|
+
# stalls to "blocked on I/O", "blocked on lock", etc.
|
|
62
|
+
#
|
|
63
|
+
# atrace categories / atrace_apps are nested in ftrace_config (NOT as a separate
|
|
64
|
+
# `android.atrace` data source with `atrace_config` — that field was removed
|
|
65
|
+
# from DataSourceConfig and is rejected by perfetto >= v45). See
|
|
66
|
+
# https://perfetto.dev/docs/data-sources/atrace.
|
|
67
|
+
data_sources: {
|
|
68
|
+
config: {
|
|
69
|
+
name: "linux.ftrace"
|
|
70
|
+
target_buffer: 0
|
|
71
|
+
ftrace_config: {
|
|
72
|
+
ftrace_events: "sched/sched_switch"
|
|
73
|
+
ftrace_events: "sched/sched_wakeup"
|
|
74
|
+
ftrace_events: "sched/sched_wakeup_new"
|
|
75
|
+
ftrace_events: "sched/sched_process_exit"
|
|
76
|
+
# sched_blocked_reason gives us the "main thread blocked on what?" answer.
|
|
77
|
+
ftrace_events: "sched/sched_blocked_reason"
|
|
78
|
+
# binder events — surfaces IPC contention on the main thread.
|
|
79
|
+
ftrace_events: "binder/binder_transaction"
|
|
80
|
+
ftrace_events: "binder/binder_transaction_received"
|
|
81
|
+
# System atrace categories — see `adb shell atrace --list_categories` for the full set.
|
|
82
|
+
atrace_categories: "view" # View system: measure/layout/draw
|
|
83
|
+
atrace_categories: "gfx" # SurfaceFlinger/HWUI
|
|
84
|
+
atrace_categories: "wm" # WindowManager
|
|
85
|
+
atrace_categories: "am" # ActivityManager
|
|
86
|
+
atrace_categories: "input" # input dispatch (key/touch)
|
|
87
|
+
atrace_categories: "binder_driver"
|
|
88
|
+
atrace_categories: "dalvik" # GC, JIT, class loading
|
|
89
|
+
atrace_categories: "ss" # SystemServer
|
|
90
|
+
atrace_categories: "aidl" # AIDL call slices
|
|
91
|
+
# Per-app categories — capture slices that the app itself emits via Trace.beginSection.
|
|
92
|
+
atrace_apps: "TARGET_PACKAGE_PLACEHOLDER"
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
# ----------------------------------------------------------------------------
|
|
98
|
+
# Data source 4: SurfaceFlinger frame timeline (the Hangs equivalent + reason codes)
|
|
99
|
+
# ----------------------------------------------------------------------------
|
|
100
|
+
# Produces expected_frame_timeline_slice and actual_frame_timeline_slice rows.
|
|
101
|
+
# A frame whose actual end > expected end is a jank; the jank_type enum tells us
|
|
102
|
+
# whose fault (AppDeadlineMissed / BufferStuffing / SfCpu / SfGpu / ...).
|
|
103
|
+
data_sources: {
|
|
104
|
+
config: {
|
|
105
|
+
name: "android.surfaceflinger.frametimeline"
|
|
106
|
+
target_buffer: 1
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
# ----------------------------------------------------------------------------
|
|
111
|
+
# Data source 5: process stats — for the weak RSS-growth signal until phase-2 leak detection lands
|
|
112
|
+
# ----------------------------------------------------------------------------
|
|
113
|
+
data_sources: {
|
|
114
|
+
config: {
|
|
115
|
+
name: "linux.process_stats"
|
|
116
|
+
target_buffer: 1
|
|
117
|
+
process_stats_config: {
|
|
118
|
+
proc_stats_poll_ms: 1000 # 1 s cadence is enough for RSS-trend detection
|
|
119
|
+
scan_all_processes_on_start: true
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Drop incremental_state every 5 s so we recover symbol tables even if perfetto crashes
|
|
125
|
+
# mid-recording — partial trace is still useful (mirrors iOS handleXctraceExit recovery).
|
|
126
|
+
incremental_state_config: {
|
|
127
|
+
clear_period_ms: 5000
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# When the on-device file is large, write it directly instead of going through the
|
|
131
|
+
# 32 MB ring buffer before flushing. Avoids data loss on long recordings.
|
|
132
|
+
write_into_file: true
|
|
133
|
+
file_write_period_ms: 2500
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Argent Android profiler — PerfettoSQL queries
|
|
2
|
+
|
|
3
|
+
These `*.sql` files are the source of truth for every query the Argent Android
|
|
4
|
+
native profiler runs against a captured `.pftrace`. They run through the
|
|
5
|
+
in-process Perfetto WASM trace-processor from the tool-server pipeline
|
|
6
|
+
(`runTpQuery` / `runTpInline` in
|
|
7
|
+
`tool-server/src/utils/android-profiler/pipeline/run-tp.ts`).
|
|
8
|
+
|
|
9
|
+
The directory lives in `native-devtools-android`; the bundler copies it next to
|
|
10
|
+
the bundled tool-server at publish time, so `traceProcessorQueriesDir()` resolves
|
|
11
|
+
the same path in dev and packaged builds.
|
|
12
|
+
|
|
13
|
+
Each `.sql` header documents only what's specific to that query. The shared
|
|
14
|
+
conventions live here so they aren't repeated nine times.
|
|
15
|
+
|
|
16
|
+
## What each file is for
|
|
17
|
+
|
|
18
|
+
| File | Consumed by | Purpose |
|
|
19
|
+
| ------------------------------ | -------------------------------------------- | ---------------------------------------------------------------- |
|
|
20
|
+
| `trace-bounds.sql` | every analyze run | Trace start timestamp anchor (see _Timestamps_). |
|
|
21
|
+
| `ui-hangs.sql` | analyze | ANRs + app-jank frames → one hang per frame. |
|
|
22
|
+
| `cpu-hotspots.sql` | analyze | Per-thread hottest leaf functions + burst windows. |
|
|
23
|
+
| `thread-breakdown.sql` | profiler-stack-query `mode=thread_breakdown` | Per-thread sample share. |
|
|
24
|
+
| `hang-folds-batched.sql` | batched analyze | State breakdown + GC overlap for ALL hangs in one batched query. |
|
|
25
|
+
| `hang-state-breakdown.sql` | drill-down (single hang) | Main-thread state breakdown for one hang window. |
|
|
26
|
+
| `hang-main-thread-samples.sql` | profiler-stack-query `mode=hang_stacks` | Main-thread CPU samples inside one hang window. |
|
|
27
|
+
| `function-callers.sql` | profiler-stack-query `mode=function_callers` | Callsites that hit one hot function. |
|
|
28
|
+
| `memory-rss.sql` | analyze | RSS-growth weak signal (not leak detection). |
|
|
29
|
+
|
|
30
|
+
## Conventions
|
|
31
|
+
|
|
32
|
+
### Parameters via the `_argent_args` view
|
|
33
|
+
|
|
34
|
+
Each query declares its runtime parameters once in a small `_argent_args`
|
|
35
|
+
PERFETTO VIEW at the top, then references them by name:
|
|
36
|
+
|
|
37
|
+
```sql
|
|
38
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
39
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
40
|
+
SELECT '{{TARGET_PROCESS}}' AS target_process;
|
|
41
|
+
...
|
|
42
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This keeps each value at one self-documenting site, and the body reads like
|
|
46
|
+
normal SQL instead of scattering bare tokens through it.
|
|
47
|
+
|
|
48
|
+
### `{{NAME}}` template tokens
|
|
49
|
+
|
|
50
|
+
`{{NAME}}` placeholders are resolved by `renderSqlTemplate` (`run-tp.ts`) before
|
|
51
|
+
the query runs. It throws on a mismatch either way: a `{{NAME}}` with no
|
|
52
|
+
substitution, or a substitution the template never uses — catching forgotten or
|
|
53
|
+
stale tokens early.
|
|
54
|
+
|
|
55
|
+
Values are **not** escaped for SQL injection — they're interpolated into the
|
|
56
|
+
query string passed to the in-process engine, so callers must validate them
|
|
57
|
+
(numeric for `*_ns`; identifier-shaped for process/thread/function names) — see
|
|
58
|
+
`hang-folds-batched.ts` for the strictest example.
|
|
59
|
+
|
|
60
|
+
Most queries render through `runTpQuery`. `hang-folds-batched.sql` is the
|
|
61
|
+
exception: `pipeline/hang-folds-batched.ts` loads it directly, builds the
|
|
62
|
+
`{{HANG_WINDOWS_VALUES}}` tuple list, and resolves it through the same renderer.
|
|
63
|
+
|
|
64
|
+
### Timestamps are CLOCK_MONOTONIC nanoseconds
|
|
65
|
+
|
|
66
|
+
Perfetto's `ts` columns are CLOCK_MONOTONIC nanoseconds since device boot — not
|
|
67
|
+
trace-relative. `trace-bounds.sql` returns the earliest `ts`; the JS side
|
|
68
|
+
subtracts it (`traceStartMs`) to normalise every emitted timestamp to
|
|
69
|
+
trace-relative ns. Any native ms/ns a query emits (burst windows, hang bounds)
|
|
70
|
+
stays native until JS does that subtraction.
|
|
71
|
+
|
|
72
|
+
### One trace parse per warm engine → batch
|
|
73
|
+
|
|
74
|
+
Re-parsing the whole trace on every query is expensive (~1.3 s for 76 MB), so
|
|
75
|
+
one query per item is quadratic — the per-hang loop this replaced took ~47 min
|
|
76
|
+
for 1013 hangs. Instead, fold many per-item queries into a single script with
|
|
77
|
+
`CREATE PERFETTO VIEW`/`TABLE` + a terminal `UNION ALL SELECT`, joining over a
|
|
78
|
+
runtime-built table. See `hang-folds-batched.sql`: one ~1.7 s run regardless of
|
|
79
|
+
hang count. Only the final SELECT reaches stdout.
|
|
80
|
+
|
|
81
|
+
### Two copies of the hang state breakdown — keep them in sync
|
|
82
|
+
|
|
83
|
+
The main-thread state-breakdown logic lives in two places:
|
|
84
|
+
|
|
85
|
+
- `hang-state-breakdown.sql` — single window, drill-down path;
|
|
86
|
+
- the `argent_hang_state` view in `hang-folds-batched.sql` — all windows.
|
|
87
|
+
|
|
88
|
+
Keep the window-clipping math in sync: each `thread_state` slice is clipped to
|
|
89
|
+
the hang window (`MIN(ts.ts + ts.dur, end) - MAX(ts.ts, start)`) so a slice
|
|
90
|
+
straddling a boundary only counts its overlap. A plain `SUM(dur)` over slices
|
|
91
|
+
that merely start inside the window can overshoot its length.
|
|
92
|
+
|
|
93
|
+
There's no standalone GC query — GC overlap lives only in the batched file.
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
-- Argent — CPU hotspots.
|
|
2
|
+
--
|
|
3
|
+
-- One output row per (thread_name, leaf_function) — the leaf frame name IS the
|
|
4
|
+
-- dominant function, so one SQL row maps 1:1 to one aggregateCpuHotspots group
|
|
5
|
+
-- (we drop leaf_mapping, which was unused and only fragmented the grouping).
|
|
6
|
+
-- The aggregator normalises the thread name and applies severity bands.
|
|
7
|
+
--
|
|
8
|
+
-- Burst windows are computed here in SQL rather than shipping every sample
|
|
9
|
+
-- timestamp (the old `GROUP_CONCAT(ts_ns)` shipped ~54 KB of timestamps that
|
|
10
|
+
-- JS re-parsed). A "burst" is a run of samples for the same (thread, function)
|
|
11
|
+
-- with no gap larger than the burst threshold. LAG() finds the gaps, a running
|
|
12
|
+
-- SUM() assigns burst ids, and we emit one `start_ms:end_ms:count` triple per
|
|
13
|
+
-- burst. start_ms/end_ms are native ms (README.md, "Timestamps"); the JS side
|
|
14
|
+
-- subtracts traceStartMs to make them trace-relative.
|
|
15
|
+
--
|
|
16
|
+
-- The total_samples column is repeated on every output row so the JS side can
|
|
17
|
+
-- compute weight % without a second round-trip.
|
|
18
|
+
--
|
|
19
|
+
-- Placeholders (declared in the _argent_args view below): target_process —
|
|
20
|
+
-- package / cmdline; burst_gap_ns — burst gap threshold in ns (BURST_GAP_MS ×
|
|
21
|
+
-- 1e6 from aggregate.ts, so the SQL and iOS-JS burst paths share one constant).
|
|
22
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
23
|
+
|
|
24
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
25
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
26
|
+
SELECT
|
|
27
|
+
'{{TARGET_PROCESS}}' AS target_process,
|
|
28
|
+
{{BURST_GAP_NS}} AS burst_gap_ns;
|
|
29
|
+
|
|
30
|
+
DROP VIEW IF EXISTS argent_app_total_samples;
|
|
31
|
+
CREATE PERFETTO VIEW argent_app_total_samples AS
|
|
32
|
+
SELECT COUNT(*) AS total_samples
|
|
33
|
+
FROM perf_sample ps
|
|
34
|
+
JOIN thread t USING (utid)
|
|
35
|
+
JOIN process p USING (upid)
|
|
36
|
+
WHERE p.name = (SELECT target_process FROM _argent_args);
|
|
37
|
+
|
|
38
|
+
WITH samples AS (
|
|
39
|
+
SELECT
|
|
40
|
+
ps.ts AS ts_ns,
|
|
41
|
+
t.name AS thread_name,
|
|
42
|
+
t.is_main_thread AS is_main_thread,
|
|
43
|
+
spf.name AS leaf_function
|
|
44
|
+
FROM perf_sample ps
|
|
45
|
+
JOIN thread t USING (utid)
|
|
46
|
+
JOIN process p USING (upid)
|
|
47
|
+
LEFT JOIN stack_profile_callsite spc ON ps.callsite_id = spc.id
|
|
48
|
+
LEFT JOIN stack_profile_frame spf ON spc.frame_id = spf.id
|
|
49
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
50
|
+
),
|
|
51
|
+
-- Flag each sample whose gap to the previous sample of the same
|
|
52
|
+
-- thread+function exceeds the burst threshold. LAG over the first sample is
|
|
53
|
+
-- NULL, so its CASE yields 0 — the opening sample never counts as a gap.
|
|
54
|
+
flagged AS (
|
|
55
|
+
SELECT
|
|
56
|
+
thread_name, is_main_thread, leaf_function, ts_ns,
|
|
57
|
+
CASE
|
|
58
|
+
WHEN ts_ns - LAG(ts_ns) OVER w > (SELECT burst_gap_ns FROM _argent_args) THEN 1
|
|
59
|
+
ELSE 0
|
|
60
|
+
END AS is_new_burst
|
|
61
|
+
FROM samples
|
|
62
|
+
WINDOW w AS (PARTITION BY thread_name, leaf_function ORDER BY ts_ns)
|
|
63
|
+
),
|
|
64
|
+
-- Running sum of the gap flags == a monotonically increasing burst id within
|
|
65
|
+
-- each thread+function partition.
|
|
66
|
+
ided AS (
|
|
67
|
+
SELECT
|
|
68
|
+
thread_name, is_main_thread, leaf_function, ts_ns,
|
|
69
|
+
SUM(is_new_burst) OVER (
|
|
70
|
+
PARTITION BY thread_name, leaf_function
|
|
71
|
+
ORDER BY ts_ns
|
|
72
|
+
ROWS UNBOUNDED PRECEDING
|
|
73
|
+
) AS burst_id
|
|
74
|
+
FROM flagged
|
|
75
|
+
),
|
|
76
|
+
-- Collapse each burst to [start_ns, end_ns, sample_count]. Every sample lands
|
|
77
|
+
-- in exactly one burst, so summing burst counts == total samples and
|
|
78
|
+
-- MIN/MAX of burst bounds == first/last sample of the (thread, function).
|
|
79
|
+
per_burst AS (
|
|
80
|
+
SELECT
|
|
81
|
+
thread_name, leaf_function,
|
|
82
|
+
MAX(is_main_thread) AS is_main_thread,
|
|
83
|
+
MIN(ts_ns) AS burst_start_ns,
|
|
84
|
+
MAX(ts_ns) AS burst_end_ns,
|
|
85
|
+
COUNT(*) AS burst_count
|
|
86
|
+
FROM ided
|
|
87
|
+
GROUP BY thread_name, leaf_function, burst_id
|
|
88
|
+
)
|
|
89
|
+
SELECT
|
|
90
|
+
thread_name,
|
|
91
|
+
MAX(is_main_thread) AS is_main_thread,
|
|
92
|
+
leaf_function,
|
|
93
|
+
SUM(burst_count) AS sample_count,
|
|
94
|
+
MIN(burst_start_ns) AS first_ts_ns,
|
|
95
|
+
MAX(burst_end_ns) AS last_ts_ns,
|
|
96
|
+
(SELECT total_samples FROM argent_app_total_samples) AS total_samples,
|
|
97
|
+
-- Compact `start_ms:end_ms:count` triples, comma-separated. JS sorts them
|
|
98
|
+
-- by start before display, so GROUP_CONCAT order is irrelevant.
|
|
99
|
+
GROUP_CONCAT(
|
|
100
|
+
(burst_start_ns / 1000000) || ':' || (burst_end_ns / 1000000) || ':' || burst_count,
|
|
101
|
+
','
|
|
102
|
+
) AS burst_windows
|
|
103
|
+
FROM per_burst
|
|
104
|
+
GROUP BY thread_name, leaf_function
|
|
105
|
+
ORDER BY sample_count DESC
|
|
106
|
+
LIMIT 200;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
-- Argent — callers/callees for a single hot function.
|
|
2
|
+
--
|
|
3
|
+
-- Drill-down for profiler-stack-query mode=function_callers. Returns one row
|
|
4
|
+
-- per unique callsite (+ owning thread) whose LEAF frame matches the requested
|
|
5
|
+
-- function, callstack text unwound via experimental_annotated_callstack.
|
|
6
|
+
--
|
|
7
|
+
-- Function matching is a literal, case-sensitive SUBSTRING test (INSTR), not
|
|
8
|
+
-- exact equality: perf frame names are stored MANGLED (e.g. the source symbol
|
|
9
|
+
-- "uncompressLZW" lives inside "_Z13uncompressLZWP7_JNIEnv..."), so an exact
|
|
10
|
+
-- match on a demangled name would miss. The Itanium length prefix means the
|
|
11
|
+
-- bare symbol still appears verbatim, so a substring catches it. matched_function
|
|
12
|
+
-- exposes the real leaf name and is_exact flags the rows that matched verbatim
|
|
13
|
+
-- (ordered first) so a precise query isn't drowned by incidental substrings.
|
|
14
|
+
--
|
|
15
|
+
-- Thread filter (the thread_name placeholder), resolved caller-side:
|
|
16
|
+
-- '__ALL__' → all threads; each row is labelled with its thread so the
|
|
17
|
+
-- caller can see where the function runs without knowing names.
|
|
18
|
+
-- '__MAIN__' → the UI/main thread, matched via thread.is_main_thread. The
|
|
19
|
+
-- main thread's raw perf `comm` is the truncated package
|
|
20
|
+
-- (e.g. ".blueskyweb.app"), never the literal "main", so a
|
|
21
|
+
-- name match would silently miss it.
|
|
22
|
+
-- <name> → exact thread name match (raw perf `comm`).
|
|
23
|
+
-- Sentinels are upper-snake so they can't collide with a real comm name.
|
|
24
|
+
--
|
|
25
|
+
-- Placeholders (declared in the _argent_args view below): target_process —
|
|
26
|
+
-- package / cmdline; thread_name — see above; function_name — leaf function.
|
|
27
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
28
|
+
|
|
29
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
30
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
31
|
+
SELECT
|
|
32
|
+
'{{TARGET_PROCESS}}' AS target_process,
|
|
33
|
+
'{{THREAD_NAME}}' AS thread_name,
|
|
34
|
+
'{{FUNCTION_NAME}}' AS function_name;
|
|
35
|
+
|
|
36
|
+
SELECT
|
|
37
|
+
t.name AS thread_name,
|
|
38
|
+
MAX(t.is_main_thread) AS is_main_thread,
|
|
39
|
+
spf.name AS matched_function,
|
|
40
|
+
(spf.name = (SELECT function_name FROM _argent_args)) AS is_exact,
|
|
41
|
+
(
|
|
42
|
+
SELECT GROUP_CONCAT(inner_spf.name, ' <- ' ORDER BY eac.depth DESC)
|
|
43
|
+
FROM experimental_annotated_callstack(ps.callsite_id) eac
|
|
44
|
+
LEFT JOIN stack_profile_frame inner_spf ON eac.frame_id = inner_spf.id
|
|
45
|
+
) AS callstack_text,
|
|
46
|
+
COUNT(*) AS occurrences
|
|
47
|
+
FROM perf_sample ps
|
|
48
|
+
JOIN thread t USING (utid)
|
|
49
|
+
JOIN process p USING (upid)
|
|
50
|
+
LEFT JOIN stack_profile_callsite spc ON ps.callsite_id = spc.id
|
|
51
|
+
LEFT JOIN stack_profile_frame spf ON spc.frame_id = spf.id
|
|
52
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
53
|
+
AND spf.name IS NOT NULL
|
|
54
|
+
AND INSTR(spf.name, (SELECT function_name FROM _argent_args)) > 0
|
|
55
|
+
AND (
|
|
56
|
+
(SELECT thread_name FROM _argent_args) = '__ALL__'
|
|
57
|
+
OR ((SELECT thread_name FROM _argent_args) = '__MAIN__' AND t.is_main_thread = 1)
|
|
58
|
+
OR t.name = (SELECT thread_name FROM _argent_args)
|
|
59
|
+
)
|
|
60
|
+
GROUP BY ps.callsite_id, t.name, spf.name
|
|
61
|
+
ORDER BY is_exact DESC, occurrences DESC
|
|
62
|
+
LIMIT 50;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
-- Argent — batched per-hang annotation (state breakdown + GC overlap).
|
|
2
|
+
--
|
|
3
|
+
-- Computes the main-thread state breakdown AND ART GC overlap for EVERY hang
|
|
4
|
+
-- window in ONE batched trace-processor query, via a JOIN over the
|
|
5
|
+
-- runtime-built `argent_hang_windows` table instead of looping one query
|
|
6
|
+
-- per hang. See README.md, "One trace parse per warm engine → batch".
|
|
7
|
+
--
|
|
8
|
+
-- Single source of truth for the batched analyze path. The `argent_hang_state`
|
|
9
|
+
-- view below mirrors the standalone, single-window `hang-state-breakdown.sql`
|
|
10
|
+
-- (drill-down) — keep the two consistent (README.md, "Two copies of the hang
|
|
11
|
+
-- state breakdown"). There is no standalone GC query; GC overlap lives here
|
|
12
|
+
-- only (drill-down never surfaced GC).
|
|
13
|
+
--
|
|
14
|
+
-- Loaded directly by pipeline/hang-folds-batched.ts, not the generic runTpQuery
|
|
15
|
+
-- path.
|
|
16
|
+
--
|
|
17
|
+
-- Placeholders: target_process (declared in the _argent_args view below); and a
|
|
18
|
+
-- hang-windows token in the `FROM (VALUES ...)` below, which the TS replaces
|
|
19
|
+
-- with one `(hang_index, start_ns, end_ns)` tuple per hang. That windows token
|
|
20
|
+
-- must NOT appear anywhere else (e.g. this header): its replacement spans
|
|
21
|
+
-- multiple lines and would break a comment, so it is referenced only obliquely.
|
|
22
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
23
|
+
|
|
24
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
25
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
26
|
+
SELECT '{{TARGET_PROCESS}}' AS target_process;
|
|
27
|
+
|
|
28
|
+
DROP TABLE IF EXISTS argent_hang_windows;
|
|
29
|
+
CREATE PERFETTO TABLE argent_hang_windows AS
|
|
30
|
+
SELECT
|
|
31
|
+
column1 AS hang_index,
|
|
32
|
+
column2 AS start_ns,
|
|
33
|
+
column3 AS end_ns
|
|
34
|
+
FROM (VALUES
|
|
35
|
+
{{HANG_WINDOWS_VALUES}}
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
DROP VIEW IF EXISTS argent_hang_state;
|
|
39
|
+
CREATE PERFETTO VIEW argent_hang_state AS
|
|
40
|
+
SELECT
|
|
41
|
+
hw.hang_index AS hang_index,
|
|
42
|
+
'state' AS row_kind,
|
|
43
|
+
ts.state AS state_v,
|
|
44
|
+
ts.blocked_function AS blocked_function_v,
|
|
45
|
+
-- Clip each thread_state slice to the hang window so a state that begins
|
|
46
|
+
-- before the window or extends past its end contributes only the overlapping
|
|
47
|
+
-- duration. Plain SUM(dur) over states whose START falls in the window can
|
|
48
|
+
-- otherwise exceed the window length (SmartPerfetto time-interval JOIN).
|
|
49
|
+
CAST(SUM(MIN(ts.ts + ts.dur, hw.end_ns) - MAX(ts.ts, hw.start_ns)) AS TEXT) AS total_dur_ns_v,
|
|
50
|
+
CAST(COUNT(*) AS TEXT) AS occurrences_v,
|
|
51
|
+
NULL AS gc_reason_v,
|
|
52
|
+
NULL AS gc_ts_ns_v,
|
|
53
|
+
NULL AS gc_dur_ns_v
|
|
54
|
+
FROM argent_hang_windows hw
|
|
55
|
+
JOIN thread_state ts
|
|
56
|
+
ON ts.ts < hw.end_ns AND ts.ts + ts.dur > hw.start_ns
|
|
57
|
+
JOIN thread t USING (utid)
|
|
58
|
+
JOIN process p USING (upid)
|
|
59
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
60
|
+
AND t.is_main_thread
|
|
61
|
+
GROUP BY hw.hang_index, ts.state, ts.blocked_function;
|
|
62
|
+
|
|
63
|
+
DROP VIEW IF EXISTS argent_hang_gc;
|
|
64
|
+
CREATE PERFETTO VIEW argent_hang_gc AS
|
|
65
|
+
SELECT
|
|
66
|
+
hw.hang_index AS hang_index,
|
|
67
|
+
'gc' AS row_kind,
|
|
68
|
+
NULL AS state_v,
|
|
69
|
+
NULL AS blocked_function_v,
|
|
70
|
+
NULL AS total_dur_ns_v,
|
|
71
|
+
NULL AS occurrences_v,
|
|
72
|
+
s.name AS gc_reason_v,
|
|
73
|
+
CAST(s.ts AS TEXT) AS gc_ts_ns_v,
|
|
74
|
+
CAST(s.dur AS TEXT) AS gc_dur_ns_v
|
|
75
|
+
FROM argent_hang_windows hw
|
|
76
|
+
JOIN slice s
|
|
77
|
+
ON s.ts < hw.end_ns AND s.ts + s.dur > hw.start_ns
|
|
78
|
+
JOIN thread_track tt ON s.track_id = tt.id
|
|
79
|
+
JOIN thread t USING (utid)
|
|
80
|
+
JOIN process p USING (upid)
|
|
81
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
82
|
+
AND t.is_main_thread
|
|
83
|
+
AND s.name GLOB 'GC*';
|
|
84
|
+
|
|
85
|
+
SELECT * FROM argent_hang_state
|
|
86
|
+
UNION ALL
|
|
87
|
+
SELECT * FROM argent_hang_gc
|
|
88
|
+
ORDER BY hang_index, row_kind;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
-- Argent — main-thread CPU samples during a hang window.
|
|
2
|
+
--
|
|
3
|
+
-- Drill-down for profiler-stack-query mode=hang_stacks. Returns one row per
|
|
4
|
+
-- perf_sample on the main thread inside the hang window, with the full
|
|
5
|
+
-- callstack text unwound via experimental_slice_callstack.
|
|
6
|
+
--
|
|
7
|
+
-- Placeholders (declared in the _argent_args view below): target_process —
|
|
8
|
+
-- package / cmdline; hang_start_ns / hang_end_ns — hang window bounds, native ns.
|
|
9
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
10
|
+
|
|
11
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
12
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
13
|
+
SELECT
|
|
14
|
+
'{{TARGET_PROCESS}}' AS target_process,
|
|
15
|
+
{{HANG_START_NS}} AS hang_start_ns,
|
|
16
|
+
{{HANG_END_NS}} AS hang_end_ns;
|
|
17
|
+
|
|
18
|
+
SELECT
|
|
19
|
+
ps.ts AS ts_ns,
|
|
20
|
+
(
|
|
21
|
+
SELECT GROUP_CONCAT(inner_spf.name, ' <- ' ORDER BY eac.depth DESC)
|
|
22
|
+
FROM experimental_annotated_callstack(ps.callsite_id) eac
|
|
23
|
+
LEFT JOIN stack_profile_frame inner_spf ON eac.frame_id = inner_spf.id
|
|
24
|
+
) AS callstack_text
|
|
25
|
+
FROM perf_sample ps
|
|
26
|
+
JOIN thread t USING (utid)
|
|
27
|
+
JOIN process p USING (upid)
|
|
28
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
29
|
+
AND t.is_main_thread
|
|
30
|
+
AND ps.ts BETWEEN (SELECT hang_start_ns FROM _argent_args)
|
|
31
|
+
AND (SELECT hang_end_ns FROM _argent_args)
|
|
32
|
+
ORDER BY ts_ns;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
-- Argent — main-thread state breakdown during a single hang window.
|
|
2
|
+
--
|
|
3
|
+
-- Called once per UiHang from the drill-down path (pipeline/index.ts
|
|
4
|
+
-- renderHangStacksAndroid). The aggregator folds the rows back into the hang
|
|
5
|
+
-- object as `stateBreakdown`.
|
|
6
|
+
--
|
|
7
|
+
-- iOS literally cannot produce this — Time Profiler only samples while a
|
|
8
|
+
-- thread is *running*, so a 500ms hang spent blocked on a futex shows up as
|
|
9
|
+
-- an empty sample window. ftrace gives us the partition for free.
|
|
10
|
+
--
|
|
11
|
+
-- Single-window twin of the `argent_hang_state` view in hang-folds-batched.sql;
|
|
12
|
+
-- keep the two consistent (see README.md, "Two copies of the hang state
|
|
13
|
+
-- breakdown").
|
|
14
|
+
--
|
|
15
|
+
-- Placeholders (declared in the _argent_args view below): target_process —
|
|
16
|
+
-- package / cmdline; hang_start_ns / hang_end_ns — hang window bounds, native ns.
|
|
17
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
18
|
+
|
|
19
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
20
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
21
|
+
SELECT
|
|
22
|
+
'{{TARGET_PROCESS}}' AS target_process,
|
|
23
|
+
{{HANG_START_NS}} AS hang_start_ns,
|
|
24
|
+
{{HANG_END_NS}} AS hang_end_ns;
|
|
25
|
+
|
|
26
|
+
SELECT
|
|
27
|
+
state,
|
|
28
|
+
blocked_function,
|
|
29
|
+
-- Clip each thread_state slice to the hang window so a state that begins
|
|
30
|
+
-- before the window or extends past its end contributes only the overlapping
|
|
31
|
+
-- duration. Plain SUM(dur) over states whose START falls in the window can
|
|
32
|
+
-- otherwise exceed the window length (SmartPerfetto time-interval JOIN).
|
|
33
|
+
SUM(MIN(ts.ts + ts.dur, (SELECT hang_end_ns FROM _argent_args))
|
|
34
|
+
- MAX(ts.ts, (SELECT hang_start_ns FROM _argent_args))) AS total_dur_ns,
|
|
35
|
+
COUNT(*) AS occurrences
|
|
36
|
+
FROM thread_state ts
|
|
37
|
+
JOIN thread t USING (utid)
|
|
38
|
+
JOIN process p USING (upid)
|
|
39
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
40
|
+
AND t.is_main_thread
|
|
41
|
+
AND ts.ts < (SELECT hang_end_ns FROM _argent_args)
|
|
42
|
+
AND ts.ts + ts.dur > (SELECT hang_start_ns FROM _argent_args)
|
|
43
|
+
GROUP BY state, blocked_function
|
|
44
|
+
ORDER BY total_dur_ns DESC;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
-- Argent — RSS growth weak signal.
|
|
2
|
+
--
|
|
3
|
+
-- NOT real leak detection: heap-dump-based leak detection lands in a later
|
|
4
|
+
-- phase. The analyze step tags this row as YELLOW and renders it under its
|
|
5
|
+
-- own "RSS Growth — Weak Signal" header with a "manual confirmation needed"
|
|
6
|
+
-- caveat (see render.ts).
|
|
7
|
+
--
|
|
8
|
+
-- Placeholder (declared in the _argent_args view below): target_process —
|
|
9
|
+
-- package / cmdline.
|
|
10
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
11
|
+
|
|
12
|
+
INCLUDE PERFETTO MODULE android.memory.process;
|
|
13
|
+
|
|
14
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
15
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
16
|
+
SELECT '{{TARGET_PROCESS}}' AS target_process;
|
|
17
|
+
|
|
18
|
+
SELECT
|
|
19
|
+
process_name,
|
|
20
|
+
MIN(anon_rss + file_rss) / 1048576.0 AS start_rss_mb,
|
|
21
|
+
MAX(anon_rss + file_rss) / 1048576.0 AS peak_rss_mb,
|
|
22
|
+
(MAX(anon_rss + file_rss) - MIN(anon_rss + file_rss)) / 1048576.0 AS growth_mb
|
|
23
|
+
FROM memory_oom_score_with_rss_and_swap_per_process
|
|
24
|
+
WHERE process_name = (SELECT target_process FROM _argent_args)
|
|
25
|
+
GROUP BY process_name;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
-- Argent — per-thread CPU breakdown.
|
|
2
|
+
--
|
|
3
|
+
-- Powers profiler-stack-query mode=thread_breakdown for Android sessions.
|
|
4
|
+
-- Returns sample_count + share of total per thread.
|
|
5
|
+
--
|
|
6
|
+
-- Placeholder (declared in the _argent_args view below): target_process —
|
|
7
|
+
-- package / cmdline.
|
|
8
|
+
-- See README.md for the shared _argent_args / template-token conventions.
|
|
9
|
+
|
|
10
|
+
DROP VIEW IF EXISTS _argent_args;
|
|
11
|
+
CREATE PERFETTO VIEW _argent_args AS
|
|
12
|
+
SELECT '{{TARGET_PROCESS}}' AS target_process;
|
|
13
|
+
|
|
14
|
+
WITH per_thread AS (
|
|
15
|
+
SELECT
|
|
16
|
+
t.name AS thread_name,
|
|
17
|
+
t.is_main_thread AS is_main_thread,
|
|
18
|
+
COUNT(ps.id) AS sample_count
|
|
19
|
+
FROM perf_sample ps
|
|
20
|
+
JOIN thread t USING (utid)
|
|
21
|
+
JOIN process p USING (upid)
|
|
22
|
+
WHERE p.name = (SELECT target_process FROM _argent_args)
|
|
23
|
+
GROUP BY t.name, t.is_main_thread
|
|
24
|
+
),
|
|
25
|
+
total AS (
|
|
26
|
+
SELECT SUM(sample_count) AS total_samples FROM per_thread
|
|
27
|
+
)
|
|
28
|
+
SELECT
|
|
29
|
+
thread_name,
|
|
30
|
+
is_main_thread,
|
|
31
|
+
sample_count,
|
|
32
|
+
ROUND(100.0 * sample_count / (SELECT total_samples FROM total), 2) AS pct_of_app
|
|
33
|
+
FROM per_thread
|
|
34
|
+
ORDER BY sample_count DESC;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
-- Argent — trace timestamp anchor.
|
|
2
|
+
--
|
|
3
|
+
-- Returns the trace's earliest ts so the JS side can normalise Perfetto's
|
|
4
|
+
-- CLOCK_MONOTONIC timestamps to trace-relative ns.
|
|
5
|
+
-- See README.md ("Timestamps are CLOCK_MONOTONIC nanoseconds").
|
|
6
|
+
SELECT start_ts FROM trace_bounds;
|