Tomacco

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.

ArgumentTypeRequiredDefaultDetails
devicestringyes
bundle_idstringyes
pathstringyes
secondsinteger101 to 600
buffer_mbinteger328 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.

ArgumentTypeRequiredDefaultDetails
pathstringyes
bundle_idstringyes
reportstring
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.

ArgumentTypeRequiredDefaultDetails
baselinestringyesAbsolute path of the saved baseline report
currentstringyesAbsolute path of the current report
budgetsobjectOptional gates per metric: maximum (absolute) and increase_percent (over the baseline)
reportstringAbsolute 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_&lt;mode&gt;* 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&lt;mode&gt;_median_ms and others) feed profile.compare and profile.budget; report saves the result as JSON.

Annotations: writes, not idempotent, open world.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Device serial / UDID from device.list, or virtual-iphone
platformstringone of android, ios; ios for a USB-connected iPhone; Android when left out, and the virtual iPhone needs none
bundle_idstringyesnon-empty; Application ID or bundle ID of the installed app
activitystringAndroid only: activity to start, as package/class, .Class or a class name; the launcher activity by default
modestring"cold"one of cold, warm, hot; cold, warm or hot on Android; cold or hot on an iPhone
runsinteger51 to 30; Measured launches; warm and hot on Android, and every iPhone measurement, add one unmeasured launch first
settle_msinteger200 to 10000; iPhone only: how long the screen must keep still to count as settled, 2000 by default
reportstringAbsolute 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.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Device serial / UDID from device.list
bundle_idstringyesnon-empty; Application ID of the running app
secondsinteger50 to 300; How long to wait between reset and read when no scenario is given; use the app meanwhile
scenariostringAbsolute path of a testing.run scenario JSON to run between reset and read, instead of waiting
reportstringAbsolute 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.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Device serial / UDID from device.list
bundle_idstringyesnon-empty; Application ID of the running app
heap_dumpstringAbsolute path to write an HPROF heap dump of the app's main process
reportstringAbsolute 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.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Device serial / UDID from device.list
bundle_idstringyesnon-empty; Application ID of the running app
secondsinteger101 to 600; Length of the interval; use the app meanwhile
reportstringAbsolute 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.

ArgumentTypeRequiredDefaultDetails
reportsarray of stringyes1 to 20 items; Absolute paths of saved reports; a later report's value of a metric replaces an earlier one
budgetstringAbsolute path of a budget JSON file
budgetsobjectThe budget inline: per metric an object with maximum, minimum or both
reportstringAbsolute 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&lt;Variant&gt;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.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
devicestringyesnon-empty; Device serial from device.list
modulestringGradle path of the benchmark module, such as :baselineprofile; needed when the build has several
variantstringVariant to run, such as benchmarkRelease, instead of the one the build files imply
classstringOnly this test class (package.Class) or method (package.Class#method)
allow_emulatorbooleanLet 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
reportstringAbsolute path to save the performance report as JSON
timeoutinteger36001 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 &lt;test&gt;&lt;metric&gt;&lt;statistic&gt;: 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.

ArgumentTypeRequiredDefaultDetails
pathstringAbsolute path of the Gradle project; give it or file
filestringAbsolute path of one benchmarkData.json
modulestringGradle path of the benchmark module, to look only there
job_idstringLeave out files older than this job, such as the profile.macrobenchmark job
sincenumberat least 0; Leave out files modified before this time, in seconds since 1970
bundle_idstringApplication ID the benchmarks measure, for compare and analyze; read from the build files when it has one app
devicestringName for the device in the report; the model in the file by default
analyzebooleanfalseAlso summarise each benchmark's first trace with profile.analyze
reportstringAbsolute 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&lt;Variant&gt;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/&lt;variant&gt;/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.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
devicestringyesnon-empty; Device serial from device.list
modulestringGradle path of the app module, such as :app; needed when several apply the plugin
variantstringVariant, such as release, for generate&lt;Variant&gt;BaselineProfile; all variants by default
allow_emulatorbooleanLet 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
reportstringAbsolute path to save the rule report as JSON
timeoutinteger36001 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.

ArgumentTypeRequiredDefaultDetails
pathstringAbsolute path of the Gradle project; give it or file
filestringAbsolute path of one profile file
modulestringGradle path of one module, such as :app
sincenumberat least 0; Mark files modified since this time, in seconds since 1970 as fresh
reportstringAbsolute 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.

ArgumentTypeRequiredDefaultDetails
pathstringyesnon-empty; Absolute local filesystem path
modulestringGradle path of the app module; the only app module by default
sourcestringAbsolute path of the profile file; the module's newest generated baseline-prof.txt by default
destinationstring"src/main/baseline-prof.txt"Relative .txt path inside the module
overwritebooleanfalseReplace 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_&lt;statistic&gt;_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.

ArgumentTypeRequiredDefaultDetails
devicestringyesnon-empty; Device serial from device.list
bundle_idstringyesnon-empty; Application ID of the installed release build
runsinteger51 to 30; Cold starts per state
reportstringAbsolute path to save the result as JSON
tomaccoctl call profile.baseline_compare '{"device":"<device>","bundle_id":"<bundle_id>"}'

View this page as Markdown on GitHub