# UI Automation

This page covers how to automate common Perfetto UI tasks to speed up your trace
analysis workflow using commands, startup commands, and macros.

## Commands System Overview

### Commands

**Commands** are individual UI actions that can be triggered manually or
automatically. Examples include pinning tracks, running queries, or creating
debug tracks. Access commands through:

- Command palette: `Ctrl-Shift-P` (Windows/Linux) or `Cmd-Shift-P` (Mac)
- Omnibox: Type `>` to transform it into a command palette
- Commands support fuzzy matching and autocomplete

### Startup Commands

**Startup commands** run automatically every time you open any trace. Configure
them in **Settings > Startup Commands** to set up your preferred view
immediately. These affect only the UI display - the trace file itself is
unchanged.

#### JSON Schema

Startup commands must be a JSON array of command objects:

```typescript
[
  {
    "id": string,      // Command identifier
    "args": unknown[]  // Array of arguments (types depend on the commands)
  },
  ...
]
```

#### Notes

- Commands execute in the order specified
- Invalid JSON or unknown command IDs will cause errors
- These commands affect only the UI display - the trace file is unchanged

### Macros

**Macros** are named sequences of commands you trigger manually when needed.
Configure them in **Settings > Macros** and run them via the command palette
(`Ctrl-Shift-P` and then type `>macro name`). Use macros for analysis workflows
you run occasionally rather than always.

#### JSON Schema

Macros must be a JSON array of macro objects:

```typescript
[
  {
    "id": string,        // Unique identifier for the macro
    "name": string,      // Display name shown in command palette
    "run": [             // Commands to run when this macro is executed
      {
        "id": string,      // Command identifier
        "args": unknown[]  // Array of arguments (types depend on the command)
      },
      ...
    ]
  },
  ...
]
```

#### Notes

- **id**: A unique identifier for your macro. Use reverse-domain style naming
  (e.g., `user.myteam.MemoryAnalysis`, `com.mycompany.LatencyCheck`). Avoid
  spaces - use PascalCase or camelCase. Keep IDs stable as they are used when
  referencing macros from startup commands.
- **name**: The display name shown in the command palette. Can contain spaces.
- Run macros by typing `>name` in the command palette (e.g., `>Memory Analysis`)
- Commands in a macro execute sequentially

> **Note (Migration):** The macros format was changed from a dictionary to an
> array structure. If you had existing macros, they were automatically migrated
> to the new format. The migrated macros use IDs in the format
> `dev.perfetto.UserMacro.<old_name>`.

### Common Issues

- **JSON syntax errors**: Missing commas, trailing commas, or unescaped quotes
- **Invalid command IDs**: Use autocomplete in the command palette to find valid
  IDs
- **Wrong argument types**: All arguments must be strings, even numbers
- **Wrong argument count**: Each command expects a specific number of arguments
- **Module dependency errors**: If your debug track query uses Perfetto modules
  (e.g., `android.screen_state`), you must include a `RunQuery` command with the
  module include statement before the debug track command. The module include
  must come first in the command sequence.

## Startup Command Examples

### Pin important tracks automatically

Add this to **Settings > Startup Commands** to always pin CPU tracks when
opening traces:

```json
[
  {
    "id": "dev.perfetto.PinTracksByRegex",
    "args": [".*CPU [0-3].*"]
  }
]
```

This runs every time you open a trace, ensuring your most important tracks are
always visible.

### Create debug tracks for custom metrics

This startup command creates a debug track showing thread scheduling latency:

```json
[
  {
    "id": "dev.perfetto.AddDebugSliceTrack",
    "args": [
      "SELECT ts, thread.name, dur FROM thread_state JOIN thread USING(utid) WHERE state = 'R' AND dur > 1000000",
      "Long Scheduling Delays (>1ms)"
    ]
  }
]
```

### Debug tracks using Perfetto modules

When your query uses Perfetto modules (like `android.screen_state` or
`android.memory.lmk`), you must include the module first as a separate command.
**Important: The module include command must come before the query that uses
it.**

```json
[
  {
    "id": "dev.perfetto.RunQuery",
    "args": ["include perfetto module android.screen_state"]
  },
  {
    "id": "dev.perfetto.AddDebugSliceTrack",
    "args": [
      "SELECT ts, dur FROM android_screen_state WHERE simple_screen_state = 'on'",
      "Screen On Events"
    ]
  }
]
```

Another example with memory LMK events:

```json
[
  {
    "id": "dev.perfetto.RunQuery",
    "args": ["include perfetto module android.memory.lmk"]
  },
  {
    "id": "dev.perfetto.AddDebugSliceTrackWithPivot",
    "args": [
      "SELECT ts, process_name as name, 0 as dur FROM android_lmk_events",
      "name",
      "LMK Events by Process"
    ]
  }
]
```

Debug tracks visualize SQL query results on the timeline. The query must return:

- `ts` (timestamp)
- For slice tracks: `dur` (duration)
- For counter tracks: `value` (the metric value)
- Optional pivot column. The query results will be grouped by the unique values
  in this column, with each group appearing in its own track.

**Command argument patterns:**

- Without pivot: `AddDebugSliceTrack` - [query, title]
- With pivot: `AddDebugSliceTrackWithPivot` - [query, pivot_column, title]

### Standard analysis setup

This comprehensive startup configuration prepares the UI for system analysis:

```json
[
  {
    "id": "dev.perfetto.CollapseTracksByRegex",
    "args": [".*"]
  },
  {
    "id": "dev.perfetto.PinTracksByRegex",
    "args": [".*CPU \\d+$"]
  },
  {
    "id": "dev.perfetto.ExpandTracksByRegex",
    "args": [".*freq.*"]
  },
  {
    "id": "dev.perfetto.AddDebugSliceTrackWithPivot",
    "args": [
      "SELECT ts, blocked_function as name, dur FROM thread_state WHERE state = 'D' AND blocked_function IS NOT NULL",
      "name",
      "Blocking Functions"
    ]
  }
]
```

## Macro Examples

### Focus on specific subsystem

Add this to **Settings > Macros** for analyzing memory when needed. This macro
creates a new workspace to isolate memory-related tracks from the main view:

```json
[
  {
    "id": "user.example.MemoryAnalysis",
    "name": "Memory Analysis",
    "run": [
      {
        "id": "dev.perfetto.CreateWorkspace",
        "args": ["Memory Analysis"]
      },
      {
        "id": "dev.perfetto.CopyTracksToWorkspaceByRegexWithAncestors",
        "args": [".*mem.*|.*rss.*", "Memory Analysis"]
      },
      {
        "id": "dev.perfetto.SwitchWorkspace",
        "args": ["Memory Analysis"]
      },
      {
        "id": "dev.perfetto.AddDebugCounterTrackWithPivot",
        "args": [
          "SELECT ts, process.name as process, value FROM counter JOIN process_counter_track ON counter.track_id = process_counter_track.id JOIN process USING (upid) WHERE counter.name = 'mem.rss' AND value > 50000000",
          "process",
          "High Memory Processes (>50MB)"
        ]
      }
    ]
  }
]
```

Run this macro by typing `>Memory Analysis` in the command palette when you need
to investigate memory issues.

### Latency investigation

This macro helps identify performance bottlenecks:

```json
[
  {
    "id": "user.example.FindLatency",
    "name": "Find Latency",
    "run": [
      {
        "id": "dev.perfetto.PinTracksByRegex",
        "args": [".*CPU.*"]
      },
      {
        "id": "dev.perfetto.RunQueryAndShowTab",
        "args": [
          "SELECT thread.name, COUNT(*) as blocks, SUM(dur)/1000000 as total_ms FROM thread_state JOIN thread USING(utid) WHERE state = 'D' GROUP BY thread.name ORDER BY total_ms DESC LIMIT 10"
        ]
      },
      {
        "id": "dev.perfetto.AddDebugSliceTrackWithPivot",
        "args": [
          "SELECT ts, 'blocked' as name, thread.name as thread_name, dur FROM thread_state JOIN thread USING (utid) WHERE state IN ('R', 'D+') AND dur > 5000000",
          "thread_name",
          "Long Waits (>5ms)"
        ]
      }
    ]
  }
]
```

## Combining with trace recording

When recording traces, you can specify startup commands that will execute when
the trace opens in the UI:

```bash
# These commands affect only the UI view when the trace opens from the script.
./record_android_trace \
  --app com.example.app \
  --ui-startup-commands '[
    {"id":"dev.perfetto.PinTracksByRegex","args":[".*CPU.*"]},
    {"id":"dev.perfetto.AddDebugSliceTrackWithPivot","args":["SELECT ts, thread.name, dur FROM thread_state JOIN thread USING(utid) WHERE state = \"R\"","thread","Runnable Time"]}
  ]'
```

## Tips for effective automation

1. **Use startup commands for always-needed views**: If you always want certain
   tracks pinned, use startup commands.

2. **Use macros for specific investigations**: Create macros for workflows you
   run occasionally (memory analysis, latency hunting, etc.).

3. **Test interactively first**: Use the command palette (`Ctrl/Cmd+Shift+P`) to
   test commands before adding to settings. Type commands to see available
   options with autocomplete.

4. **Start clean**: Begin command sequences with `CollapseTracksByRegex` using
   `".*"` to collapse all tracks first.

5. **Common regex patterns**:

   - Escape dots in package names: `"com\\.example\\.app"`
   - Match any digit: `\\d+`
   - Match beginning/end: `^` and `$`

6. **Debug tracks need good queries**: Ensure your SQL returns `ts` and either
   `dur` (for slices) or `value` (for counters) columns. Use the pivot commands
   to split into multiple tracks. For Android use cases, see
   [Android Trace Analysis Cookbook](/docs/getting-started/android-trace-analysis.md)
   for examples of common queries used by Android engineers.

## See Also

- [Commands Automation Reference](/docs/visualization/commands-automation-reference.md) -
  Complete reference for stable automation commands with backwards compatibility
  guarantees
- [Perfetto UI Guide](/docs/visualization/perfetto-ui.md) - General UI
  documentation including commands configuration
- [Deep Linking](/docs/visualization/deep-linking-to-perfetto-ui.md) - Opening
  traces with pre-configured commands via URLs
