Un dos escenarios máis actuais para o uso do analizador PVS-Studio é a súa integración con sistemas CI. E aínda que a análise dun proxecto de PVS-Studio desde case calquera sistema de integración continua pode integrarse en só algúns comandos, seguimos facendo este proceso aínda máis cómodo. PVS-Studio agora ten soporte para converter a saída do analizador nun formato para TeamCity - TeamCity Inspections Type. A ver como funciona.
Información sobre o software utilizado
Información sobre o proxecto en estudo
Probemos esta funcionalidade nun exemplo práctico: analicemos o proxecto OpenRCT2.
axuste
Para aforrar tempo, probablemente saltarei o proceso de instalación e comezarei desde o momento en que teña o servidor TeamCity en execución no meu ordenador. Necesitamos ir a: localhost:{porto especificado durante o proceso de instalación} (no meu caso, localhost:9090) e introducir os datos de autorización. Despois de entrar nos recibirán:
Fai clic no botón Crear proxecto. A continuación, seleccione Manualmente e enche os campos.
Despois de premer o botón crear, recíbenos unha fiestra con configuración.
Prememos Crear configuración de compilación.
Completa os campos e fai clic crear. Vemos unha fiestra na que se lle pide que seleccione un sistema de control de versións. Dado que as fontes xa están localizadas localmente, fai clic Saltar.
Finalmente, pasamos á configuración do proxecto.
Engademos pasos de montaxe, para facelo fai clic: Pasos de compilación -> Engadir paso de compilación.
Aquí escollemos:
- Tipo de corredor -> Liña de comandos
- Executar -> Script personalizado
Xa que realizaremos análises durante a compilación do proxecto, a montaxe e a análise deberían ser un paso, así que enche o campo Script personalizado:
Veremos os pasos individuais máis adiante. É importante que cargar o analizador, montar o proxecto, analizalo, emitir o informe e formatear só leva once liñas de código.
O último que temos que facer é establecer as variables de ambiente, que delineei algunhas formas de mellorar a súa lexibilidade. Para facelo, imos adiante: Parámetros -> Engadir un novo parámetro e engade tres variables:
Todo o que tes que facer é premer o botón Correr na esquina superior dereita. Mentres se está a montar e analizar o proxecto, falareivos do guión.
Directamente guión
En primeiro lugar, necesitamos descargar a última distribución de PVS-Studio. Para iso usamos o xestor de paquetes Chocolatey. Para os que queiran saber máis sobre isto, hai un correspondente
choco install pvs-studio -y
A continuación, imos lanzar a utilidade de seguimento de construción do proxecto CLMonitor.
%CLmon% monitor –-attach
Despois construímos o proxecto como unha variable de ambiente MSB é o camiño para a versión de MSBuild que necesito construír
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
Introduzamos o inicio de sesión e a clave de licenza para PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
Despois de completar a compilación, executa CLMonitor de novo para xerar ficheiros preprocesados e análise estática:
%CLmon% analyze -l "c:ptest.plog"
Despois usaremos outra utilidade da nosa distribución. PlogConverter converte un informe dun formato estándar a un formato específico de TeamCity. Grazas a isto, poderemos velo directamente na xanela de compilación.
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
O último paso é mostrar o informe con formato stdout, onde será recollido polo analizador de TeamCity.
type "C:tempptest.plog_TeamCity.txt"
Código de guión 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"
Mentres tanto, a montaxe e análise do proxecto completouse con éxito, podemos ir á pestana proxectos e asegúrate diso.
Agora imos premer Total de inspecciónspara ir a ver o informe do analizador:
Os avisos agrúpanse por números de regras de diagnóstico. Para navegar polo código, cómpre facer clic no número de liña coa advertencia. Facendo clic no signo de interrogación da esquina superior dereita abrirase unha nova pestana coa documentación. Tamén pode navegar polo código facendo clic no número de liña coa advertencia do analizador. A navegación desde un ordenador remoto é posible cando se usa SourceTreeRoot marcador. Calquera persoa que estea interesada neste modo de funcionamento do analizador pode familiarizarse coa sección correspondente
Visualización dos resultados do analizador
Agora que rematamos de implementar e configurar a compilación, vexamos algunhas advertencias interesantes que se atopan no proxecto que estamos a ver.
Aviso N1
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 analizador detectou un erro que despois de asignar memoria de forma dinámica CrearObxecto, cando se produce unha excepción, a memoria non se borra e prodúcese unha fuga de memoria.
Aviso N2
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 persoas ademais dun analizador estático poderían pasar esta proba de atención. Este exemplo de copiar e pegar é bo precisamente por este motivo.
Avisos N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
Por suposto, usar unha variable co mesmo nome na clase base e na descendente non sempre é un erro. Non obstante, a propia tecnoloxía de herdanza supón que todos os campos da clase pai están presentes na clase filla. Ao declarar campos co mesmo nome no herdeiro, introducimos confusión.
Aviso N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
Vexamos máis de cerca. Expresión Dirección da imaxe/8 será falso se Dirección da imaxe está no rango de -7 a 7. Segunda parte: (dirección da imaxe / 8) != 3 cheques Dirección da imaxe por estar fóra do rango: de -31 a -24 e de 24 a 31, respectivamente. Paréceme bastante estraño comprobar que se inclúen números nun determinado intervalo deste xeito e, aínda que non haxa ningún erro neste anaco de código, recomendaría reescribir estas condicións para ser máis explícito. Isto facilitaríalle moito a vida ás persoas que lerían e manterían este código.
Aviso N5
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;
....
}
....
}
Probablemente este fragmento de código obtivo por descompilación. Despois, a xulgar polo comentario deixado, eliminouse parte do código que non funcionaba. Non obstante, aínda quedan un par de operacións cursorId, que tampouco teñen moito sentido.
Aviso N6
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 sinxelo de corrixir; só tes que revisalo unha terceira vez xogador a un punteiro nulo ou engádeo ao corpo da instrución condicional. Suxeriría a segunda opción:
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
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));
....
}
....
}
Podes desfacerte dunha liña de código difícil de ler dun só golpe e resolver o problema coa comprobación de nullptr. Suxiro cambiar o código do seguinte xeito:
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
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
O código parece bastante estraño. Paréceme que houbo unha errata ben na condición ou ao reasignar a variable ColumnHeaderPressedCurrentState valores teito.
Saída
Como podemos ver, integrar o analizador estático PVS-Studio no teu proxecto TeamCity é bastante sinxelo. Para iso, abonda con escribir só un pequeno ficheiro de configuración. A verificación do código permitirache identificar os problemas inmediatamente despois da montaxe, o que axudará a eliminalos cando a complexidade e o custo dos cambios aínda sexan baixos.
Se queres compartir este artigo cun público de fala inglesa, utiliza a ligazón de tradución: Vladislav Stolyarov.
Fonte: www.habr.com