| # Quickstart: SQL-based analysis and trace-based metrics |
| |
| _This quickstart explains how to use `trace_processor` as well as its Python API to |
| programmatically query the trace contents through SQL and compute trace-based metrics._ |
| |
| ## Trace Processor |
| |
| TraceProcessor is a multi-format trace importing and query engine based on |
| SQLite. It comes both as a C++ library and as a standalone executable: |
| `trace_processor_shell` (or just `trace_processor`). |
| |
| ### Setup |
| |
| ```bash |
| # Download prebuilts (Linux and Mac only) |
| curl -LO https://get.perfetto.dev/trace_processor |
| chmod +x ./trace_processor |
| |
| # Start the interactive shell |
| ./trace_processor trace.perfetto-trace |
| |
| # Start a local trace processor instance to replace wasm module in the UI |
| ./trace_processor trace.perfetto-trace --httpd |
| ``` |
| |
| NOTE: In HTTP mode the trace will be loaded into the `trace_processor` and |
| the UI will connect and issue queries over TCP. This can allow |
| arbitrary sized traces to be loaded since there are no memory |
| constraints, unlike the WASM module. In addition, this can improve |
| performance in the UI as it issues SQL queries. |
| |
| See [Trace Processor docs](/docs/analysis/trace-processor.md) for the full |
| TraceProcessor guide. |
| |
| ### Sample queries |
| |
| For more exhaustive examples see the _SQL_ section of the various _Data sources_ |
| docs. |
| |
| #### Slices |
| |
| Slices are stackable events which have name and span some duration of time. |
| |
| ![](/docs/images/slices.png "Example of slices in the UI") |
| |
| ``` |
| > SELECT ts, dur, name FROM slice |
| ts dur name |
| -------------------- -------------------- --------------------------- |
| 261187017446933 358594 eglSwapBuffersWithDamageKHR |
| 261187017518340 357 onMessageReceived |
| 261187020825163 9948 queueBuffer |
| 261187021345235 642 bufferLoad |
| 261187121345235 153 query |
| ... |
| ``` |
| |
| #### Counters |
| |
| Counters are events with a value which changes over time. |
| |
| ![](/docs/images/counters.png "Example of counters in the UI") |
| |
| ``` |
| > SELECT ts, value FROM counter |
| ts value |
| -------------------- -------------------- |
| 261187012149954 1454.000000 |
| 261187012399172 4232.000000 |
| 261187012447402 14304.000000 |
| 261187012535839 15490.000000 |
| 261187012590890 17490.000000 |
| 261187012590890 16590.000000 |
| ... |
| ``` |
| |
| #### Scheduler slices |
| |
| Scheduler slices indicate which thread was scheduled on which CPU at which time. |
| |
| ![](/docs/images/sched-slices.png "Example of scheduler slices in the UI") |
| |
| ``` |
| > SELECT ts, dur, cpu, utid FROM sched |
| ts dur cpu utid |
| -------------------- -------------------- -------------------- -------------------- |
| 261187012170489 267188 0 390 |
| 261187012170995 247153 1 767 |
| 261187012418183 12812 2 2790 |
| 261187012421099 220000 6 683 |
| 261187012430995 72396 7 2791 |
| ... |
| ``` |
| |
| ### Trace-based metrics |
| |
| Trace Processor offers also a higher-level query interface that allows to run |
| pre-baked queries, herein called "metrics". Metrics are generally curated by |
| domain experts, often the same people who add the instrumentation points in the |
| first place, and output structured JSON/Protobuf/text. |
| Metrics allow to get a summarized view of the trace without having to type any |
| SQL or having to load the trace in the UI. |
| |
| The metrics` schema files live in the |
| [/protos/perfetto/metrics](/protos/perfetto/metrics/) directory. |
| The corresponding SQL queries live in |
| [/src/trace_processor/metrics](/src/trace_processor/metrics/). |
| |
| #### Run a single metric |
| |
| Let's run the [`android_cpu`](/protos/perfetto/metrics/android/cpu_metric.proto) |
| metric. This metrics computes the total CPU time and the total cycles |
| (CPU frequency * time spent running at that frequency) for each process in the |
| trace, breaking it down by CPU (_core_) number. |
| |
| ```protobuf |
| ./trace_processor --run-metrics android_cpu trace.perfetto-trace |
| |
| android_cpu { |
| process_info { |
| name: "/system/bin/init" |
| threads { |
| name: "init" |
| core { |
| id: 1 |
| metrics { |
| mcycles: 1 |
| runtime_ns: 570365 |
| min_freq_khz: 1900800 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1902017 |
| } |
| } |
| core { |
| id: 3 |
| metrics { |
| mcycles: 0 |
| runtime_ns: 366406 |
| min_freq_khz: 1900800 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1902908 |
| } |
| } |
| ... |
| } |
| ... |
| } |
| process_info { |
| name: "/system/bin/logd" |
| threads { |
| name: "logd.writer" |
| core { |
| id: 0 |
| metrics { |
| mcycles: 8 |
| runtime_ns: 33842357 |
| min_freq_khz: 595200 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1891825 |
| } |
| } |
| core { |
| id: 1 |
| metrics { |
| mcycles: 9 |
| runtime_ns: 36019300 |
| min_freq_khz: 1171200 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1887969 |
| } |
| } |
| ... |
| } |
| ... |
| } |
| ... |
| } |
| ``` |
| |
| #### Running multiple metrics |
| |
| Multiple metrics can be flagged using comma separators to the `--run-metrics` |
| flag. This will output a text proto with the combined result of running both |
| metrics. |
| |
| ```protobuf |
| $ ./trace_processor --run-metrics android_mem,android_cpu trace.perfetto-trace |
| |
| android_mem { |
| process_metrics { |
| process_name: ".dataservices" |
| total_counters { |
| anon_rss { |
| min: 19451904 |
| max: 19890176 |
| avg: 19837548.157829277 |
| } |
| file_rss { |
| min: 25804800 |
| max: 25829376 |
| avg: 25827909.957489081 |
| } |
| swap { |
| min: 9289728 |
| max: 9728000 |
| avg: 9342355.8421707246 |
| } |
| anon_and_swap { |
| min: 29179904 |
| max: 29179904 |
| avg: 29179904 |
| } |
| } |
| ... |
| } |
| ... |
| } |
| android_cpu { |
| process_info { |
| name: "/system/bin/init" |
| threads { |
| name: "init" |
| core { |
| id: 1 |
| metrics { |
| mcycles: 1 |
| runtime_ns: 570365 |
| min_freq_khz: 1900800 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1902017 |
| } |
| } |
| ... |
| } |
| ... |
| } |
| ... |
| } |
| ``` |
| |
| #### JSON and binary output |
| |
| The trace processor also supports binary protobuf and JSON as alternative output |
| formats. This is useful when the intended reader is an offline tool. |
| |
| Both single and multiple metrics are supported as with proto text output. |
| |
| ``` |
| ./trace_processor --run-metrics android_mem --metrics-output=binary trace.perfetto-trace |
| <binary protobuf output> |
| |
| ./trace_processor --run-metrics android_mem,android_cpu --metrics-output=json trace.perfetto-trace |
| { |
| "android_mem": { |
| "process_metrics": [ |
| { |
| "process_name": ".dataservices", |
| "total_counters": { |
| "anon_rss": { |
| "min": 19451904.000000, |
| "max": 19890176.000000, |
| "avg": 19837548.157829 |
| }, |
| "file_rss": { |
| "min": 25804800.000000, |
| "max": 25829376.000000, |
| "avg": 25827909.957489 |
| }, |
| "swap": { |
| "min": 9289728.000000, |
| "max": 9728000.000000, |
| "avg": 9342355.842171 |
| }, |
| "anon_and_swap": { |
| "min": 29179904.000000, |
| "max": 29179904.000000, |
| "avg": 29179904.000000 |
| } |
| }, |
| ... |
| }, |
| ... |
| ] |
| } |
| "android_cpu": { |
| "process_info": [ |
| { |
| "name": "\/system\/bin\/init", |
| "threads": [ |
| { |
| "name": "init", |
| "core": [ |
| { |
| "id": 1, |
| "metrics": { |
| "mcycles": 1, |
| "runtime_ns": 570365, |
| "min_freq_khz": 1900800, |
| "max_freq_khz": 1900800, |
| "avg_freq_khz": 1902017 |
| } |
| }, |
| ... |
| ] |
| ... |
| } |
| ... |
| ] |
| ... |
| }, |
| ... |
| ] |
| ... |
| } |
| } |
| ``` |
| |
| ## Python API |
| |
| The API can be run without requiring the `trace_processor` binary to be |
| downloaded or installed. |
| |
| ### Setup |
| ``` |
| $ pip install perfetto |
| ``` |
| NOTE: The API is only compatible with Python3. |
| |
| ### Example functions |
| See the Python API section of |
| [Trace Processor (SQL)](/docs/analysis/trace-processor.md) to get |
| more details on all available functions. |
| |
| #### Query |
| ```python |
| from perfetto.trace_processor import TraceProcessor |
| tp = TraceProcessor(trace='trace.perfetto-trace') |
| |
| qr_it = tp.query('SELECT name FROM slice') |
| for row in qr_it: |
| print(row.name) |
| ``` |
| **Output** |
| ``` |
| eglSwapBuffersWithDamageKHR |
| onMessageReceived |
| queueBuffer |
| bufferLoad |
| query |
| ... |
| ``` |
| #### Query as Pandas DataFrame |
| ```python |
| from perfetto.trace_processor import TraceProcessor |
| tp = TraceProcessor(trace='trace.perfetto-trace') |
| |
| qr_it = tp.query('SELECT ts, name FROM slice') |
| qr_df = qr_it.as_pandas_dataframe() |
| print(qr_df.to_string()) |
| ``` |
| **Output** |
| ``` |
| ts name |
| -------------------- --------------------------- |
| 261187017446933 eglSwapBuffersWithDamageKHR |
| 261187017518340 onMessageReceived |
| 261187020825163 queueBuffer |
| 261187021345235 bufferLoad |
| 261187121345235 query |
| ... |
| ``` |
| #### Metric |
| ```python |
| from perfetto.trace_processor import TraceProcessor |
| tp = TraceProcessor(trace='trace.perfetto-trace') |
| |
| cpu_metrics = tp.metric(['android_cpu']) |
| print(cpu_metrics) |
| ``` |
| **Output** |
| ``` |
| metrics { |
| android_cpu { |
| process_info { |
| name: "/system/bin/init" |
| threads { |
| name: "init" |
| core { |
| id: 1 |
| metrics { |
| mcycles: 1 |
| runtime_ns: 570365 |
| min_freq_khz: 1900800 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1902017 |
| } |
| } |
| core { |
| id: 3 |
| metrics { |
| mcycles: 0 |
| runtime_ns: 366406 |
| min_freq_khz: 1900800 |
| max_freq_khz: 1900800 |
| avg_freq_khz: 1902908 |
| } |
| } |
| ... |
| } |
| ... |
| } |
| ... |
| } |
| } |
| ``` |
| |
| ## Next steps |
| |
| There are several options for exploring more of the trace analysis features |
| Perfetto provides: |
| |
| * The [trace conversion quickstart](/docs/quickstart/traceconv.md) gives an |
| overview on how to convert Perfetto traces to legacy formats to integrate with |
| existing tooling. |
| * The [Trace Processor documentation](/docs/analysis/trace-processor.md) gives |
| more information about how to work with trace processor including details on |
| how to write queries and how tables in trace processor are organized. |
| * The [metrics documentation](/docs/analysis/metrics.md) gives a more in-depth |
| look into metrics including a short walkthrough on how to build an |
| experimental metric from scratch. |
| * The [SQL table reference](/docs/analysis/sql-tables.autogen) gives a |
| comprehensive guide to the all the available tables in trace processor. |
| * The [common tasks](/docs/contributing/common-tasks.md) page gives a list of |
| steps on how new metrics can be added to the trace processor. |