Análise de rastreamento em massa

Depois de coletar vários traces usando o ProfilingManager, analisá-los individualmente para encontrar problemas de desempenho se torna impraticável. A análise de traces em massa permite consultar um conjunto de dados de traces simultaneamente para:

  • Identificar regressões de desempenho comuns.
  • Calcule distribuições estatísticas (por exemplo, latência P50, P90, P99).
  • Encontrar padrões em vários traces.
  • Encontre traces atípicos para entender e depurar problemas de desempenho.

Nesta seção, mostramos como usar o processador de rastreamento em lote do Perfetto para Python para analisar métricas de inicialização em um conjunto de rastreamentos armazenados localmente e localizar rastreamentos atípicos para uma análise mais detalhada.

Criar a consulta

A primeira etapa para realizar uma análise em massa é criar uma consulta PerfettoSQL.

Nesta seção, apresentamos um exemplo de consulta que mede a latência de inicialização do app. Especificamente, é possível medir a duração de activityStart até o primeiro frame gerado (a primeira ocorrência da fração Choreographer#doFrame) para medir a latência de inicialização do app que está sob o controle dele. A Figura 1 mostra a seção para consulta.

Uma visualização da linha do tempo de um trace, destacando a duração do evento activityStart até o primeiro evento Choreographer#doFrame.
Figura 1. Seção de trace de "activityStart" até o primeiro frame gerado.
CREATE OR REPLACE PERFETTO FUNCTION find_slices(pattern STRING) RETURNS
TABLE (name STRING, ts LONG, dur LONG) AS
SELECT name,ts,dur FROM slice WHERE name GLOB $pattern;

CREATE OR REPLACE PERFETTO FUNCTION generate_start_to_end_slices(startSlicePattern STRING, endSlicePattern STRING, inclusive BOOL) RETURNS
TABLE(name STRING, ts LONG, dur LONG) AS
SELECT name, ts, MIN(startToEndDur) as dur
FROM
  (SELECT S.name as name, S.ts as ts, E.ts + IIF($inclusive, E.dur, 0) - S.ts as startToEndDur
  FROM find_slices($startSlicePattern) as S CROSS JOIN find_slices($endSlicePattern) as E
  WHERE startToEndDur > 0)
GROUP BY name, ts;

SELECT ts,name,dur from generate_start_to_end_slices('activityStart','*Choreographer#doFrame [0-9]*', true)

É possível executar a consulta na interface do Perfetto e usar os resultados para gerar uma faixa de depuração (Figura 2) e visualizá-la na linha do tempo (Figura 3).

Uma captura de tela da interface do Perfetto mostrando como criar uma faixa de depuração para uma consulta de inicialização.
Figura 2. Criar uma faixa de depuração para uma consulta de inicialização.
Uma visualização da linha do tempo na interface do Perfetto mostrando uma faixa de depuração gerada para uma consulta de inicialização.
Figura 3. Faixa de depuração gerada para uma consulta de inicialização.

Configure o ambiente Python

Instale o Python na máquina local e as bibliotecas necessárias:

pip install perfetto pandas plotly

Criar o script de análise de trace em massa

O script de amostra a seguir executa a consulta em vários traces usando o BatchTraceProcessor do Python do Perfetto.

from perfetto.batch_trace_processor import BatchTraceProcessor
import glob
import plotly.express as px

# Collect all trace files in the local directory
traces = glob.glob('*.perfetto-trace')

if not traces:
    print("No .perfetto-trace files found in the current directory.")
    exit(1)

if __name__ == '__main__':
    # Process all traces in parallel to aggregate metrics across runs
    with BatchTraceProcessor(traces) as btp:
        query = """
        CREATE OR REPLACE PERFETTO FUNCTION find_slices(pattern STRING) RETURNS
        TABLE (name STRING, ts LONG, dur LONG) AS
        SELECT name,ts,dur FROM slice WHERE name GLOB $pattern;

        CREATE OR REPLACE PERFETTO FUNCTION generate_start_to_end_slices(startSlicePattern STRING, endSlicePattern STRING, inclusive BOOL) RETURNS
        TABLE(name STRING, ts LONG, dur LONG) AS
        SELECT name, ts, MIN(startToEndDur) as dur
        FROM
          (SELECT S.name as name, S.ts as ts, E.ts + IIF($inclusive, E.dur, 0) - S.ts as startToEndDur
          FROM find_slices($startSlicePattern) as S CROSS JOIN find_slices($endSlicePattern) as E
          WHERE startToEndDur > 0)
        GROUP BY name, ts;

        SELECT ts,name,dur / 1000000 as dur_ms from generate_start_to_end_slices('activityStart','*Choreographer#doFrame [0-9]*', true)
        """
        df = btp.query_and_flatten(query)
        # Plot the distribution of startup times, tracking trace file paths on
        # hover
        violin = px.violin(df, x='dur_ms', hover_data='_path', title='startup time', points='all')
        violin.show()

Noções básicas sobre o script

Quando você executa o script Python, ele realiza as seguintes ações:

  1. O script pesquisa no diretório local todos os traces do Perfetto com o sufixo .perfetto-trace e os usa como traces de origem para análise.
  2. Ele executa uma consulta de trace em massa que calcula o subconjunto do tempo de inicialização correspondente ao tempo entre o fragmento de trace activityStart e o primeiro frame gerado pelo app.
  3. Ele mostra a latência em milissegundos usando um gráfico de violino para visualizar a distribuição dos tempos de inicialização.

Interprete os resultados

Um gráfico de violino que mostra a distribuição das latências de inicialização consultadas.
Figura 4. Gráfico de violino das latências de inicialização consultadas.

Depois de executar o script, ele gera um gráfico. Nesse caso, o gráfico mostra uma distribuição bimodal com dois picos distintos (Figura 4).

Em seguida, encontre a diferença entre as duas populações. Isso ajuda a examinar traces individuais em mais detalhes. Neste exemplo, o gráfico está configurado para que, ao passar o cursor sobre os pontos de dados (latências), seja possível identificar os nomes dos arquivos de trace. Em seguida, abra um dos traces que fazem parte do grupo de alta latência.

Ao abrir um trace do grupo de alta latência (Figura 5), você vai encontrar uma fração extra chamada MyFlaggedFeature em execução durante a inicialização (Figura 6). Por outro lado, selecionar um rastro da população de menor latência (o pico mais à esquerda) confirma a ausência dessa mesma fração (Figura 7). Essa comparação indica que uma flag de recurso específica, ativada para um subconjunto de usuários, aciona a regressão.

Um gráfico que destaca um trace de alta latência.
Figura 5. Ponto de dados de alta latência em um gráfico de violino.
Um trace que destaca a inicialização de alta latência devido à fração MyFlaggedFeature.
Figura 6. Início do trace de alta latência com uma fração extra de "MyFlaggedFeature".
Um rastro que destaca a inicialização de baixa latência sem a fração MyFlaggedFeature.
Figura 7. Início do trace de baixa latência.

Este exemplo demonstra uma das muitas maneiras de usar a análise de trace em massa. Outros casos de uso incluem extrair estatísticas do campo para avaliar o impacto, detectar regressões e muito mais.