PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Um dos cenários mais atuais de utilização do analisador PVS-Studio é a sua integração com sistemas de CI. E embora a análise de um projeto PVS-Studio de quase qualquer sistema de integração contínua possa ser integrada em apenas alguns comandos, continuamos a tornar esse processo ainda mais conveniente. O PVS-Studio agora tem suporte para converter a saída do analisador em um formato para TeamCity - TeamCity Inspections Type. Vamos ver como isso funciona.

Informações sobre o software utilizado

Estúdio PVS — um analisador estático de código C, C++, C# e Java, projetado para facilitar a tarefa de encontrar e corrigir vários tipos de erros. O analisador pode ser usado em Windows, Linux e macOS. Neste artigo usaremos ativamente não apenas o analisador em si, mas também alguns utilitários de sua distribuição.

CLMonitor — é um servidor de monitoramento que monitora as inicializações do compilador. Ele deve ser executado imediatamente antes de começar a construir seu projeto. No modo snooping, o servidor interceptará execuções de todos os compiladores suportados. Vale ressaltar que este utilitário só pode ser usado para analisar projetos C/C++.

PlogConverter – um utilitário para converter relatórios do analisador em diferentes formatos.

Informações sobre o projeto em estudo

Vamos testar esta funcionalidade em um exemplo prático - vamos analisar o projeto OpenRCT2.

OpenRCT2 - uma implementação aberta do jogo RollerCoaster Tycoon 2 (RCT2), expandindo-o com novas funções e corrigindo bugs. A jogabilidade gira em torno da construção e manutenção de um parque de diversões contendo atrações, lojas e instalações. O jogador deve tentar lucrar e manter a boa reputação do parque, ao mesmo tempo que mantém os visitantes satisfeitos. OpenRCT2 permite que você jogue tanto em cenário quanto em sandbox. Os cenários exigem que o jogador complete uma tarefa específica dentro de um tempo definido, enquanto o Sandbox permite ao jogador construir um parque mais flexível, sem quaisquer restrições ou finanças.

Fixação

Para economizar tempo, provavelmente pularei o processo de instalação e começarei a partir do momento em que tiver o servidor TeamCity em execução no meu computador. Precisamos ir para: localhost:{porta especificada durante o processo de instalação} (no meu caso, localhost:9090) e inserir os dados de autorização. Após entrar seremos recebidos por:

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Clique no botão Criar Projeto. Em seguida, selecione Manualmente e preencha os campos.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Depois de apertar o botão Crie, somos recebidos por uma janela com configurações.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Vamos clicar Criar configuração de compilação.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Preencha os campos e clique Crie. Vemos uma janela solicitando que você selecione um sistema de controle de versão. Como as fontes já estão localizadas localmente, clique pular.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Por fim, passamos para as configurações do projeto.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Vamos adicionar etapas de montagem, para isso clique em: Etapas de construção -> Adicionar etapa de construção.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Aqui escolhemos:

  • Tipo de corredor -> Linha de comando
  • Executar -> Script Personalizado

Como realizaremos a análise durante a compilação do projeto, a montagem e a análise deverão ser uma etapa, portanto preencha o campo Script Personalizado:

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Veremos as etapas individuais mais tarde. É importante que carregar o analisador, montar o projeto, analisá-lo, gerar o relatório e formatá-lo leve apenas onze linhas de código.

A última coisa que precisamos fazer é definir as variáveis ​​de ambiente, das quais descrevi algumas maneiras de melhorar sua legibilidade. Para fazer isso, vamos em frente: Parâmetros -> Adicionar novo parâmetro e adicione três variáveis:

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Tudo que você precisa fazer é apertar o botão Execute no canto superior direito. Enquanto o projeto está sendo montado e analisado, falarei sobre o roteiro.

Diretamente roteiro

Primeiro, precisamos baixar a distribuição mais recente do PVS-Studio. Para isso utilizamos o gerenciador de pacotes Chocolatey. Para quem quiser saber mais sobre isso, existe um correspondente artigo:

choco install pvs-studio -y

A seguir, vamos lançar o utilitário de rastreamento de construção do projeto CLMonitor.

%CLmon% monitor –-attach

Então construiremos o projeto como uma variável de ambiente MSB é o caminho para a versão do MSBuild que preciso construir

%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable

Vamos inserir o login e a chave de licença do PVS-Studio:

%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%

Após a conclusão da compilação, execute o CLMonitor novamente para gerar arquivos pré-processados ​​e análise estática:

%CLmon% analyze -l "c:ptest.plog"

Então usaremos outro utilitário da nossa distribuição. PlogConverter converte um relatório de um formato padrão para um formato específico do TeamCity. Graças a isso poderemos visualizá-lo diretamente na janela de construção.

%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"

A última etapa é exibir o relatório formatado em stdout, onde será obtido pelo analisador TeamCity.

type "C:tempptest.plog_TeamCity.txt"

Código de script completo:

choco install pvs-studio -y
%CLmon% monitor --attach
set platform=x64
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
%CLmon% analyze -l "c:ptest.plog"
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
type "C:tempptest.plog_TeamCity.txt"

Enquanto isso, a montagem e análise do projeto foram concluídas com sucesso, podemos ir para a aba Projectos e certifique-se disso.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Agora vamos clicar Total de inspeçõespara visualizar o relatório do analisador:

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Os avisos são agrupados por números de regras de diagnóstico. Para navegar pelo código, você precisa clicar no número da linha com o aviso. Clicar no ponto de interrogação no canto superior direito abrirá uma nova aba com documentação. Você também pode navegar pelo código clicando no número da linha com o aviso do analisador. A navegação a partir de um computador remoto é possível ao usar FonteTreeRoot marcador. Qualquer pessoa interessada neste modo de operação do analisador pode se familiarizar com a seção correspondente documentação.

Visualizando os resultados do analisador

Agora que terminamos de implantar e configurar a compilação, vamos dar uma olhada em alguns avisos interessantes encontrados no projeto que estamos analisando.

Aviso N1

V773 [CWE-401] A exceção foi lançada sem liberar o ponteiro 'resultado'. Um vazamento de memória é possível. libopenrct2 ObjectFactory.cpp 443

Object* CreateObjectFromJson(....)
{
  Object* result = nullptr;
  ....
  result = CreateObject(entry);
  ....
  if (readContext.WasError())
  {
    throw std::runtime_error("Object has errors");
  }
  ....
}

Object* CreateObject(const rct_object_entry& entry)
{
  Object* result;
  switch (entry.GetType())
  {
    case OBJECT_TYPE_RIDE:
      result = new RideObject(entry);
      break;
    case OBJECT_TYPE_SMALL_SCENERY:
      result = new SmallSceneryObject(entry);
      break;
    case OBJECT_TYPE_LARGE_SCENERY:
      result = new LargeSceneryObject(entry);
      break;
    ....
    default:
      throw std::runtime_error("Invalid object type");
  }
  return result;
}

O analisador notou um erro que após alocar memória dinamicamente em Createobject, quando ocorre uma exceção, a memória não é limpa e ocorre um vazamento de memória.

Aviso N2

V501 Existem subexpressões idênticas '(1ULL << WIDX_MONTH_BOX)' à esquerda e à direita de '|' operador. libopenrct2ui Cheats.cpp 487

static uint64_t window_cheats_page_enabled_widgets[] = 
{
  MAIN_CHEAT_ENABLED_WIDGETS |
  (1ULL << WIDX_NO_MONEY) |
  (1ULL << WIDX_ADD_SET_MONEY_GROUP) |
  (1ULL << WIDX_MONEY_SPINNER) |
  (1ULL << WIDX_MONEY_SPINNER_INCREMENT) |
  (1ULL << WIDX_MONEY_SPINNER_DECREMENT) |
  (1ULL << WIDX_ADD_MONEY) |
  (1ULL << WIDX_SET_MONEY) |
  (1ULL << WIDX_CLEAR_LOAN) |
  (1ULL << WIDX_DATE_SET) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_MONTH_UP) |
  (1ULL << WIDX_MONTH_DOWN) |
  (1ULL << WIDX_YEAR_BOX) |
  (1ULL << WIDX_YEAR_UP) |
  (1ULL << WIDX_YEAR_DOWN) |
  (1ULL << WIDX_DAY_BOX) |
  (1ULL << WIDX_DAY_UP) |
  (1ULL << WIDX_DAY_DOWN) |
  (1ULL << WIDX_MONTH_BOX) |  // <=
  (1ULL << WIDX_DATE_GROUP) |
  (1ULL << WIDX_DATE_RESET),
  ....
};

Poucas pessoas além de um analisador estático poderiam passar neste teste de atenção. Este exemplo de copiar e colar é bom exatamente por esse motivo.

Avisos N3

V703 É estranho que o campo 'flags' na classe derivada 'RCT12BannerElement' substitua o campo na classe base 'RCT12TileElementBase'. Verifique as linhas: RCT12.h:570, RCT12.h:259. libopenrct2 RCT12.h 570

struct RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};
struct rct1_peep : RCT12SpriteBase
{
  ....
  uint8_t flags;
  ....
};

É claro que usar uma variável com o mesmo nome na classe base e na descendente nem sempre é um erro. No entanto, a própria tecnologia de herança assume que todos os campos da classe pai estão presentes na classe filha. Ao declarar campos com o mesmo nome no herdeiro, criamos confusão.

Aviso N4

V793 É estranho que o resultado da instrução 'imageDirection/8' faça parte da condição. Talvez esta afirmação devesse ter sido comparada com outra coisa. libopenrct2 ObservationTower.cpp 38

void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
  if ((imageDirection / 8) && (imageDirection / 8) != 3)
  {
    ....
  }
  ....
}

Vamos olhar mais de perto. Expressão direção da imagem/8 será falso se direção da imagem está na faixa de -7 a 7. Segunda parte: (direção da imagem / 8)! = 3 Verificações direção da imagem por estar fora da faixa: de -31 a -24 e de 24 a 31, respectivamente. Parece-me muito estranho verificar números para inclusão em um determinado intervalo desta forma e, mesmo que não haja erro neste trecho de código, eu recomendaria reescrever essas condições para serem mais explícitas. Isso tornaria a vida muito mais fácil para as pessoas que leriam e manteriam esse código.

Aviso N5

V587 Uma sequência estranha de atribuições deste tipo: A = B; B = UMA;. Verifique as linhas: 1115, 1118. libopenrct2ui MouseInput.cpp 1118

void process_mouse_over(....)
{
  ....
  switch (window->widgets[widgetId].type)
  {
    case WWT_VIEWPORT:
      ebx = 0;
      edi = cursorId;                                 // <=
      // Window event WE_UNKNOWN_0E was called here,
      // but no windows actually implemented a handler and
      // it's not known what it was for
      cursorId = edi;                                 // <=
      if ((ebx & 0xFF) != 0)
      {
        set_cursor(cursorId);
        return;
      }
      break;
      ....
  }
  ....
}

Este fragmento de código provavelmente foi obtido por descompilação. Então, a julgar pelo comentário deixado, parte do código que não funcionava foi removida. No entanto, ainda restam algumas operações cursorId, o que também não faz muito sentido.

Aviso N6

V1004 [CWE-476] O ponteiro 'player' foi usado de forma insegura após ser verificado em relação ao nullptr. Verifique as linhas: 2085, 2094. libopenrct2 Network.cpp 2094

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)                                          // <=
    {
      *player = pendingPlayer;
       if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
       {
         _serverConnection->Player = player;
       }
    }
    newPlayers.push_back(player->Id);                    // <=
  }
  ....
}

Este código é bastante fácil de corrigir; você só precisa verificá-lo uma terceira vez jogador a um ponteiro nulo ou adicione-o ao corpo da instrução condicional. Eu sugeriria a segunda opção:

void Network::ProcessPlayerList()
{
  ....
  auto* player = GetPlayerByID(pendingPlayer.Id);
  if (player == nullptr)
  {
    // Add new player.
    player = AddPlayer("", "");
    if (player)
    {
      *player = pendingPlayer;
      if (player->Flags & NETWORK_PLAYER_FLAG_ISSERVER)
      {
        _serverConnection->Player = player;
      }
      newPlayers.push_back(player->Id);
    }
  }
  ....
}

Aviso N7

V547 [CWE-570] A expressão 'name == nullptr' é sempre falsa. libopenrct2 ServerList.cpp 102

std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    ....
  }
  else
  {
    ....
    entry.name = (name == nullptr ? "" : json_string_value(name));
    ....
  }
  ....
}

Você pode se livrar de uma linha de código difícil de ler de uma só vez e resolver o problema verificando nullptr. Sugiro alterar o código da seguinte forma:

std::optional<ServerListEntry> ServerListEntry::FromJson(...)
{
  auto name = json_object_get(server, "name");
  .....
  if (name == nullptr || version == nullptr)
  {
    name = ""
    ....
  }
  else
  {
    ....
    entry.name = json_string_value(name);
    ....
  }
  ....
}

Aviso N8

V1048 [CWE-1164] A variável 'ColumnHeaderPressedCurrentState' recebeu o mesmo valor. libopenrct2ui CustomListView.cpp 510

void CustomListView::MouseUp(....)
{
  ....
  if (!ColumnHeaderPressedCurrentState)
  {
    ColumnHeaderPressed = std::nullopt;
    ColumnHeaderPressedCurrentState = false;
    Invalidate();
  }
}

O código parece bastante estranho. Parece-me que houve um erro de digitação na condição ou ao reatribuir a variável ColumnHeaderPressedCurrentState significados falso.

Jogar aviator online grátis: hack aviator funciona

Como podemos ver, integrar o analisador estático PVS-Studio ao seu projeto TeamCity é bastante simples. Para fazer isso, basta escrever apenas um pequeno arquivo de configuração. A verificação do código permitirá identificar problemas imediatamente após a montagem, o que ajudará a eliminá-los quando a complexidade e o custo das alterações ainda forem baixos.

PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2
Se você quiser compartilhar este artigo com um público que fala inglês, use o link de tradução: Vladislav Stolyarov. PVS-Studio e Integração Contínua: TeamCity. Análise do projeto Open RollerCoaster Tycoon 2.

Fonte: habr.com

Adicionar um comentário