Docs Operation reference profile operations
profile operations#
14 operations. Generated from assets/operations.json; all areas.
On this page (14)
profile.capture#
Capture Android profile
Capture scheduler, memory, CPU and FrameTimeline data as a native Perfetto trace.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | ||
bundle_id | string | yes | ||
path | string | yes | ||
seconds | integer | 10 | 1 to 600 | |
buffer_mb | integer | 32 | 8 to 128 |
tomaccoctl call profile.capture '{"device":"<device>","bundle_id":"<bundle_id>","path":"<path>"}'
profile.analyze#
Analyze performance profile
Calculate application CPU, memory and frame metrics using the official native Perfetto trace processor. Missing data remains unavailable.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | yes | ||
bundle_id | string | yes | ||
report | string |
tomaccoctl call profile.analyze '{"path":"<path>","bundle_id":"<bundle_id>"}'
profile.compare#
Compare performance reports
Compare a current performance report with a saved baseline report of the same app and kind (from profile.analyze, profile.startup, profile.frames, profile.memory or profile.usage). changes lists every metric of either report with its baseline and current value, delta and delta_percent (null when a value is missing or the baseline is zero). budgets optionally gate metrics by maximum and increase_percent; an unavailable measurement fails its budget, and without budgets passed stays true. report saves the result as JSON.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
baseline | string | yes | Absolute path of the saved baseline report | |
current | string | yes | Absolute path of the current report | |
budgets | object | Optional gates per metric: maximum (absolute) and increase_percent (over the baseline) | ||
report | string | Absolute path to save the comparison as JSON |
tomaccoctl call profile.compare '{"baseline":"<baseline>","current":"<current>"}'
profile.startup#
Measure app startup
Launch an app several times and report each launch's time in milliseconds with count, min, max, mean, median and p90. On Android it reads am start -W's TotalTime and WaitTime: cold stops the app before each launch; warm keeps the process and recreates the activity; hot returns to the running activity from the home screen. Each run lists the launch state Android reported, and mismatched_runs counts launches whose state differs from mode. On an iPhone (platform ios with its UDID, through go-ios) or the virtual iPhone (device virtual-iphone) there is no launch timer, so the screen is timed: from the open request to the first picture that differs from the one before and stays different (first_frame_ms, the startup_<mode>* metrics), to the request returning with the app's process (launched_ms) and to the screen keeping still for settle_ms (settled_ms); modes are cold (the app's process ended first: go-ios kill, or restart_ui on the virtual iPhone) and hot (sent to the background and opened again), and launch_state compares the app's process with the one before. iPhone times are coarse (resolution_ms); virtual-iPhone times are emulated and far slower than an iPhone's. metrics (startup<mode>_median_ms and others) feed profile.compare and profile.budget; report saves the result as JSON.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | non-empty; Device serial / UDID from device.list, or virtual-iphone | |
platform | string | one of android, ios; ios for a USB-connected iPhone; Android when left out, and the virtual iPhone needs none | ||
bundle_id | string | yes | non-empty; Application ID or bundle ID of the installed app | |
activity | string | Android only: activity to start, as package/class, .Class or a class name; the launcher activity by default | ||
mode | string | "cold" | one of cold, warm, hot; cold, warm or hot on Android; cold or hot on an iPhone | |
runs | integer | 5 | 1 to 30; Measured launches; warm and hot on Android, and every iPhone measurement, add one unmeasured launch first | |
settle_ms | integer | 200 to 10000; iPhone only: how long the screen must keep still to count as settled, 2000 by default | ||
report | string | Absolute path to save the result as JSON, for profile.compare |
tomaccoctl call profile.startup '{"device":"<device>","bundle_id":"<bundle_id>"}'
profile.frames#
Measure frame rendering
Reset a running Android app's frame statistics (dumpsys gfxinfo reset), wait seconds or run a testing.run scenario on the device, then read them: frames rendered, janky frames and jank_percent, slow frames (17 ms or more) and frozen frames (700 ms or more) from the frame time histogram, the 50th, 90th, 95th and 99th percentile frame times, and the per-process counters such as missed vsync and slow UI thread. A process that rendered nothing leaves the metrics null. A failed scenario answers scenario_failed with its steps. report saves the result as JSON.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | non-empty; Device serial / UDID from device.list | |
bundle_id | string | yes | non-empty; Application ID of the running app | |
seconds | integer | 5 | 0 to 300; How long to wait between reset and read when no scenario is given; use the app meanwhile | |
scenario | string | Absolute path of a testing.run scenario JSON to run between reset and read, instead of waiting | ||
report | string | Absolute path to save the result as JSON, for profile.compare |
tomaccoctl call profile.frames '{"device":"<device>","bundle_id":"<bundle_id>"}'
profile.memory#
Measure app memory
Read a running Android app's memory from dumpsys meminfo: per process the App Summary in KB (PSS and RSS for Java heap, native heap, code, stack, graphics and the rest), total PSS, RSS and swap, the size, allocated and free parts of the native and Dalvik heaps, and object counts such as activities and views. metrics sum the processes in MB (memory_pss_mb, memory_rss_mb, java_heap_pss_mb and others). heap_dump also writes an HPROF heap dump of the main process to that path (am dumpheap), which needs a debuggable app or a debuggable system image; convert it with hprof-conv for tools other than Android Studio. report saves the result as JSON.
Annotations: writes, idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | non-empty; Device serial / UDID from device.list | |
bundle_id | string | yes | non-empty; Application ID of the running app | |
heap_dump | string | Absolute path to write an HPROF heap dump of the app's main process | ||
report | string | Absolute path to save the result as JSON, for profile.compare |
tomaccoctl call profile.memory '{"device":"<device>","bundle_id":"<bundle_id>"}'
profile.usage#
Measure CPU and battery use
Measure a running Android app's CPU and battery use over seconds. CPU comes from the user and system time of the app's processes in /proc before and after: cpu_ms, cpu_percent (of one core; above 100 means more than one core) and cpu_device_percent (of all cores), and per process. Battery is the change of the app's UID in batterystats' estimated power use (mAh since the last charge, from the device's power profile), in total and per part such as cpu and sensors; battery names the UID, whether a charger is connected and the level. Nothing on the device is reset. report saves the result as JSON.
Annotations: writes, idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | non-empty; Device serial / UDID from device.list | |
bundle_id | string | yes | non-empty; Application ID of the running app | |
seconds | integer | 10 | 1 to 600; Length of the interval; use the app meanwhile | |
report | string | Absolute path to save the result as JSON, for profile.compare |
tomaccoctl call profile.usage '{"device":"<device>","bundle_id":"<bundle_id>"}'
profile.budget#
Check performance budget
Check saved performance reports of one app (from profile.startup, profile.frames, profile.memory, profile.usage or profile.analyze) against a budget: per metric a maximum, a minimum or both, such as {"startup_cold_median_ms":{"maximum":800},"jank_percent":{"maximum":5},"memory_pss_mb":{"maximum":200}}. Give the budget as a JSON file (budget) or inline (budgets). Each result names the metric, its value, its limits, passed and the report it came from; a metric no report measured fails as measurement_unavailable. testing.run takes the same budget for measure steps. report saves the result as JSON.
Annotations: writes, idempotent, closed world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
reports | array of string | yes | 1 to 20 items; Absolute paths of saved reports; a later report's value of a metric replaces an earlier one | |
budget | string | Absolute path of a budget JSON file | ||
budgets | object | The budget inline: per metric an object with maximum, minimum or both | ||
report | string | Absolute path to save the result as JSON |
tomaccoctl call profile.budget '{"reports":["<report>"]}'
profile.macrobenchmark#
Run Macrobenchmark
Run a Gradle build's Macrobenchmark module on a device as a job: connected<Variant>AndroidTest of the com.android.test module that depends on androidx.benchmark:benchmark-macro (project.inspect lists it under performance.benchmark_modules, with the task it would run), with ANDROID_SERIAL set to device. class limits the run to one test class or Class#method. The job's last step is profile.benchmark_results, so the job ends with a performance report (kind macrobenchmark, saved to report or build/tomacco/macrobenchmark.json) whose flat metrics profile.compare and profile.budget read. The benchmark JSON, the Perfetto traces and the JUnit XML are the job's artifacts; testing.results reads the XML. A build without such a module answers benchmark_module_missing.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | yes | non-empty; Absolute local filesystem path | |
device | string | yes | non-empty; Device serial from device.list | |
module | string | Gradle path of the benchmark module, such as :baselineprofile; needed when the build has several | ||
variant | string | Variant to run, such as benchmarkRelease, instead of the one the build files imply | ||
class | string | Only this test class (package.Class) or method (package.Class#method) | ||
allow_emulator | boolean | Let the benchmarks run on an emulator (androidx.benchmark.suppressErrors=EMULATOR); true by default for an emulator-NNNN serial. An emulator's numbers are not a phone's | ||
report | string | Absolute path to save the performance report as JSON | ||
timeout | integer | 3600 | 1 to 86400; Seconds the Gradle job may run |
tomaccoctl call profile.macrobenchmark '{"path":"<path>","device":"<device>"}'
profile.benchmark_results#
Read Macrobenchmark results
Read the JSON a Macrobenchmark run wrote (build/outputs/connected_android_test_additional_output/**/*benchmarkData.json) into a performance report of kind macrobenchmark. Every benchmark's metrics become flat entries named <test><metric><statistic>: minimum, median and maximum for a metric such as timeToInitialDisplayMs (time_to_initial_display_ms), and p50, p90, p95 and p99 for a sampled one such as frameDurationCpuMs; the same names profile.compare and profile.budget read. benchmarks keeps every run's value and the Perfetto traces each benchmark wrote. With analyze, the first trace of each benchmark is summarised by profile.analyze for bundle_id. The newest file wins; others are listed in other_files. job_id or since leaves out older files; file reads one file.
Annotations: read-only, idempotent, closed world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | Absolute path of the Gradle project; give it or file | ||
file | string | Absolute path of one benchmarkData.json | ||
module | string | Gradle path of the benchmark module, to look only there | ||
job_id | string | Leave out files older than this job, such as the profile.macrobenchmark job | ||
since | number | at least 0; Leave out files modified before this time, in seconds since 1970 | ||
bundle_id | string | Application ID the benchmarks measure, for compare and analyze; read from the build files when it has one app | ||
device | string | Name for the device in the report; the model in the file by default | ||
analyze | boolean | false | Also summarise each benchmark's first trace with profile.analyze | |
report | string | Absolute path to save the report as JSON |
tomaccoctl call profile.benchmark_results
profile.baseline_profile#
Generate Baseline Profile
Generate a Baseline Profile on a device as a job: the generate<Variant>BaselineProfile task of the app module that applies the androidx.baselineprofile Gradle plugin (project.inspect lists it under performance.baseline_profile_modules), with ANDROID_SERIAL set to device. The plugin writes the profile to src/<variant>/generated/baselineProfiles/ of the app module and AGP packages it into the release build. The job's last step is profile.baseline_rules, so its report (saved to report or build/tomacco/baseline-profile.json) lists the profile files with their rule counts. A build without the plugin answers baseline_plugin_missing.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | yes | non-empty; Absolute local filesystem path | |
device | string | yes | non-empty; Device serial from device.list | |
module | string | Gradle path of the app module, such as :app; needed when several apply the plugin | ||
variant | string | Variant, such as release, for generate<Variant>BaselineProfile; all variants by default | ||
allow_emulator | boolean | Let the benchmarks run on an emulator (androidx.benchmark.suppressErrors=EMULATOR); true by default for an emulator-NNNN serial. An emulator's numbers are not a phone's | ||
report | string | Absolute path to save the rule report as JSON | ||
timeout | integer | 3600 | 1 to 86400; Seconds the Gradle job may run |
tomaccoctl call profile.baseline_profile '{"path":"<path>","device":"<device>"}'
profile.baseline_rules#
Read Baseline Profile rules
List a project's Baseline Profile files (src/*/baseline-prof.txt and src/*/generated/baselineProfiles/*-prof.txt, or one file) with their rules counted: classes, methods, hot (H), startup (S) and post-startup (P) flags, wildcard rules, size and modification time. fresh marks files modified since since. Nothing is changed. A project without a profile file answers baseline_profile_missing.
Annotations: read-only, idempotent, closed world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | Absolute path of the Gradle project; give it or file | ||
file | string | Absolute path of one profile file | ||
module | string | Gradle path of one module, such as :app | ||
since | number | at least 0; Mark files modified since this time, in seconds since 1970 as fresh | ||
report | string | Absolute path to save the list as JSON |
tomaccoctl call profile.baseline_rules
profile.baseline_install#
Install Baseline Profile
Copy an app module's newest generated Baseline Profile (or the file source names) to src/main/baseline-prof.txt of the module, or to destination inside it, where AGP packages it without the plugin. Copying a file that is already there changes nothing. A different file there answers baseline_profile_exists unless overwrite is true, which keeps the old one beside it as .bak.
Annotations: destructive, idempotent, closed world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
path | string | yes | non-empty; Absolute local filesystem path | |
module | string | Gradle path of the app module; the only app module by default | ||
source | string | Absolute path of the profile file; the module's newest generated baseline-prof.txt by default | ||
destination | string | "src/main/baseline-prof.txt" | Relative .txt path inside the module | |
overwrite | boolean | false | Replace a different file, keeping it as .bak |
tomaccoctl call profile.baseline_install '{"path":"<path>"}'
profile.baseline_compare#
Compare startup with and without Baseline Profile
Time cold starts of an installed release build twice and compare them. First the app is compiled with the verify filter (cmd package compile -f -m verify), which ignores the profile, and started runs times; then the app's androidx.profileinstaller installs its profile (the INSTALL_PROFILE broadcast to its ProfileInstallReceiver), the app is compiled with speed-profile and started runs times again. The report has both profile.startup reports, their compiler filters, the comparison, and the flat metrics startup_cold_<statistic>_ms_without_profile and _with_profile, improvement_ms and improvement_percent. An app that does not install a profile answers profile_install_failed. The device's compilation state of the app is left at speed-profile.
Annotations: writes, not idempotent, open world.
| Argument | Type | Required | Default | Details |
|---|---|---|---|---|
device | string | yes | non-empty; Device serial from device.list | |
bundle_id | string | yes | non-empty; Application ID of the installed release build | |
runs | integer | 5 | 1 to 30; Cold starts per state | |
report | string | Absolute path to save the result as JSON |
tomaccoctl call profile.baseline_compare '{"device":"<device>","bundle_id":"<bundle_id>"}'