Instrumentos personalizados: quando a placa de sinalização não é suficiente

O Instruments for Xcode da Apple é uma ferramenta para analisar o desempenho de um aplicativo iOS. Eles são usados ​​para coletar e exibir dados necessários na depuração de código. No ano passado, a Apple lançou o Custom Instruments. Esta é uma oportunidade para expandir o conjunto padrão de ferramentas para aplicativos de criação de perfil. Quando as ferramentas existentes não são suficientes, você mesmo pode criar novas - elas coletarão, analisarão e exibirão os dados conforme necessário.

Um ano se passou e quase não há novas ferramentas e informações públicas sobre sua criação na rede. Por isso, decidimos retificar a situação e compartilhar como criamos nosso próprio Instrumento Personalizado, que determina o motivo do fraco isolamento dos testes de unidade. Ele é baseado na tecnologia de sinalização (escrevemos sobre isso no artigo anterior ) e permite determinar com rapidez e precisão onde o teste pisca.



Mínimo teórico


Para criar uma nova ferramenta para o Xcode, você precisa entender dois blocos teóricos. Para quem quiser descobrir por conta própria, forneceremos imediatamente os links necessários:


Para o resto - abaixo está um breve resumo dos tópicos necessários.

Primeiro, selecione Arquivo -> Novo -> Projeto -> Categoria macOS -> Pacote de instrumentos. O projeto criado inclui um arquivo com a extensão .instrpkg, no qual uma nova ferramenta é declarada declarativamente no formato xml. Vamos nos familiarizar com os elementos de marcação:

O queAtributosDescrição do produto
Esquemas de dados
esquema de intervalo, esquema de pontos etc.
Descreve a estrutura de dados como uma tabela como esquemas sql. Os esquemas são usados ​​em outros elementos de marcação para determinar o tipo de dados na entrada e na saída do modelo, por exemplo, ao descrever um mapeamento (UI).
Esquemas de importação de dados
esquema de importação
Importe esquemas prontos. Ele permite que você use estruturas de dados definidas pela Apple.
Modelo de ferramenta
modelador
Associa a ferramenta a um arquivo .clp, no qual a lógica da ferramenta é definida, e anuncia o esquema de dados esperado na entrada e saída do modelo.
Descrição da ferramenta
instrumento
Descreve o modelo de dados e determina como os eventos serão exibidos na interface do usuário. O modelo de dados é descrito usando os atributos create-table, create-parameter, etc. Os gráficos de ferramentas são definidos pelos atributos do gráfico e a tabela de peças é definida por lista, narrativa etc.

Se quisermos complementar a lógica da nova ferramenta, crie um arquivo .clp com o código CLIPS. Entidades de linguagem básica:

  • "Fato" é um determinado evento registrado no sistema usando o comando assert;
  • "Regra" é um bloco if com sintaxe específica que contém uma condição sob a qual um conjunto de ações é executado.

Quais regras e em que sequência serão ativadas são determinadas pelo próprio CLIPS com base nos fatos recebidos, nas prioridades das regras e no mecanismo de resolução de conflitos.

A linguagem suporta a criação de tipos de dados baseados em primitivos, o uso de operações e funções aritméticas e lógicas. Assim como a programação orientada a objetos (OOP) completa, com a definição de classes, envio de mensagens, herança múltipla.

Considere a sintaxe básica de uma linguagem que permite criar lógica para ferramentas personalizadas.

1. Para criar um fact , use a construção assert :

 CLIPS> (assert (duck)) 

Assim, obtemos a entrada do duck na tabela de fatos, que pode ser visualizada usando o comando facts :

 CLIPS> (facts) 

Para remover o fato, use o comando retract : (retract duck)

2. Para criar uma rule , use a construção defrule :

 CLIPS> (defrule duck) —     duck (animal-is duck)</i> —  animal-is duck     => (assert (sound-is quack))) —     sound-is quack 

3. Para criar e usar variáveis, é usada a seguinte sintaxe (antes do nome da variável existe um sinal obrigatório "?"):

 ?<variable-name> 

4. Você pode criar novos tipos de dados usando:

 CLIPS> (deftemplate prospect (slot name (type STRING) (default ?DERIVE)) (slot assets (type SYMBOL) (default rich)) (slot age (type NUMBER) (default 80))) 

Assim, definimos uma estrutura com o nome prospect e três atributos nome, ativos e idade do tipo correspondente e um valor padrão.

5. Operações aritméticas e lógicas possuem sintaxe de prefixo. Ou seja, para adicionar 2 e 3, você deve usar a seguinte construção:

 CLIPS> (+ 2 3) 

Ou para comparar duas variáveis ​​x e y:

 CLIPS> (> ?x ?y) 

Exemplo prático


Em nosso projeto, usamos a biblioteca OCMock para criar objetos stub. No entanto, existem situações em que um mok vive mais do que o teste para o qual foi criado e afeta o isolamento de outros testes. Como resultado, isso leva ao "piscar" (instabilidade) dos testes de unidade. Para acompanhar a vida útil dos testes e simulações, criaremos nossa própria ferramenta. A seguir, é apresentado um algoritmo de ações.

Etapa 1. Fazendo marcação para eventos de orientação


Para detectar moxes problemáticos, são necessárias duas categorias de eventos de intervalo - o tempo de criação e destruição do moxa, o horário de início e término do teste. Para obter esses eventos, acesse a biblioteca do OCMock e OCMock -os com stopMocking métodos init e stopMocking da classe stopMocking .





Em seguida, vá para o projeto em estudo, faça a marcação nos testes de unidade, tearDown setUp e tearDown :



Etapa 2. Crie uma nova ferramenta a partir do modelo do Pacote de Instrumentos




Primeiro, determinamos o tipo de dados da entrada. Para fazer isso, importamos o esquema de signpost . Agora, os eventos criados pelo signpost cairão na ferramenta:



Em seguida, determinamos o tipo de dados na saída. Neste exemplo, produziremos eventos simultâneos. Cada evento terá um horário e uma descrição. Para fazer isso, declare o esquema:



Etapa 3. Descrevemos a lógica da ferramenta


Criamos um arquivo separado com a extensão .clp , no qual definimos as regras usando a linguagem CLIPS. Para que a nova ferramenta saiba em qual arquivo a lógica está definida, adicione o bloco modeler :



Nesse bloco, usando o atributo do production-system , especifique o caminho relativo para o arquivo com a lógica. Nos atributos output e required-input definimos os esquemas de dados para input e output, respectivamente.



Etapa 4. Descrevemos as especificidades da apresentação da ferramenta (UI)


No arquivo .instrpkg , resta descrever a própria ferramenta, ou seja, exibir os resultados. Crie uma tabela para os dados no atributo create-table usando o schema-ref anteriormente declarado detected-mocks-narrative no atributo schema-ref . E configure o tipo de saída de informação - narrativa (descritiva):



Etapa 5. Escrevemos o código lógico


Vamos para o arquivo .clp , no qual a lógica do sistema especialista está definida. A lógica será a seguinte: se o horário de início do teste se sobrepuser ao intervalo da vida do moka, acreditamos que esse mok "veio" de outro teste - o que viola o isolamento do atual teste de unidade. Para, eventualmente, criar um evento com informações de interesse, você precisa executar as seguintes etapas:

1. Defina as estruturas mock e unitTest com campos - a hora do evento, o identificador do evento, o nome do teste e a classe do mok.



2. Definimos as regras que criarão fatos com os tipos mock e unitTest base nos eventos de entrada da signpost de signpost :



Essas regras podem ser lidas da seguinte forma: se, na entrada, obtivermos um fato do tipo os-signpost com o subsystem , a category , o name e o event-type desejados, crie um novo fato com o tipo definido acima (unitTest ou mock) e preencha-o com valores. É importante lembrar aqui - o CLIPS é uma linguagem que diferencia maiúsculas de minúsculas e os valores do subsistema, categoria, nome e tipo de evento devem corresponder ao que foi usado no código do projeto em estudo.



Variáveis ​​de eventos de orientação são transmitidas da seguinte maneira:



3. Definimos as regras que liberam eventos concluídos (são redundantes, pois não afetam o resultado).



Etapa 6. Defina a regra que irá gerar os resultados.


Você pode ler a regra assim.

Se

1) existe unitTest e mock;

2) neste caso, o início do teste ocorre depois do moka existente;

3) existe uma tabela para armazenar resultados com o esquema de simulação-simulação-narrativa detectada;

então

4) criar um novo registro;

5) preencher com o tempo;

6) ... e uma descrição.



Como resultado, vemos a seguinte imagem ao usar a nova ferramenta:



O código fonte do instrumento personalizado e um projeto de amostra para o uso do instrumento podem ser visualizados no GitHub .

Depuração de ferramenta


O depurador é usado para depurar ferramentas personalizadas.



Ele permite

1. Veja o código compilado com base na descrição no instrpkg.
2. Veja informações detalhadas sobre o que acontece com a ferramenta em tempo de execução.



3. Exiba uma lista completa e uma descrição dos esquemas de dados do sistema que podem ser usados ​​como entrada em novas ferramentas.



4. Execute comandos arbitrários no console. Por exemplo, exiba uma lista de regras com o comando list-defrules ou fatos com o comando fatos



Configuração no servidor de IC


Você pode executar ferramentas na linha de comando - para criar um perfil do aplicativo durante a execução de testes de unidade ou de interface do usuário no servidor de CI. Isso permitirá, por exemplo, detectar um vazamento de memória o mais cedo possível. Para criar um perfil de testes no pipeline, use os seguintes comandos:

1. Ferramentas em execução com atributos:

 xcrun instruments -t <template_name> -l <average_duration_ms> -w <device_udid> 

  • onde template_name é o caminho para o modelo com ferramentas ou o nome do modelo. Você pode obter o comando xcrun instruments -s ;
  • average_duration_ms - o tempo de gravação em milissegundos deve ser maior ou igual ao tempo de execução do teste;
  • device_udid - identificador do simulador. Você pode obter o comando xcrun instruments -s. Deve corresponder ao identificador do simulador no qual os testes serão executados.

2. Execute testes no mesmo simulador com o comando:

 xcodebuild -workspace <path_to_workspace>-scheme <scheme_with_tests> -destination <device> test-without-building 

  • onde path_to_workspace é o caminho para o espaço de trabalho do Xcode;
  • scheme_with_tests - esquema com testes;
  • identificador do device - simulador.

Como resultado, um relatório com a extensão .trace será criado no diretório de trabalho, que pode ser aberto pelo aplicativo Instruments ou clicando com o botão direito do mouse no arquivo e selecionando Mostrar Conteúdo do Pacote.

Conclusões


Vimos um exemplo de atualização da placa de sinalização para uma ferramenta completa e ensinamos como aplicá-la automaticamente nas "execuções" do servidor de CI e usá-la na solução do problema de testes "instáveis".

Ao explorar as possibilidades de instrumentos personalizados, você entenderá melhor em que outros casos você pode usar os instrumentos. Por exemplo, eles também nos ajudam a entender os problemas do multithreading - onde e quando usar o acesso a dados seguros para threads.
Criar uma nova ferramenta foi bastante simples. Mas o principal é que, depois de passar vários dias estudando a mecânica e a documentação para criá-lo hoje, você será capaz de evitar várias noites sem dormir na tentativa de corrigir bugs.

Fontes



O artigo foi escrito com @regno , Anton Vlasov, desenvolvedor do iOS.

Source: https://habr.com/ru/post/pt447594/


All Articles