หนึ่งในสถานการณ์ปัจจุบันที่สุดสำหรับการใช้เครื่องวิเคราะห์ PVS-Studio คือการผสานรวมกับระบบ CI และถึงแม้ว่าการวิเคราะห์โปรเจ็กต์ PVS-Studio จากระบบบูรณาการต่อเนื่องเกือบทุกระบบจะสามารถสร้างได้โดยใช้คำสั่งเพียงไม่กี่คำสั่ง แต่เรายังคงทำให้กระบวนการนี้สะดวกยิ่งขึ้นต่อไป ขณะนี้ PVS-Studio รองรับการแปลงเอาต์พุตของตัววิเคราะห์เป็นรูปแบบสำหรับ TeamCity - TeamCity Inspections Type มาดูกันว่ามันทำงานอย่างไร
ข้อมูลเกี่ยวกับซอฟต์แวร์ที่ใช้
ข้อมูลเกี่ยวกับโครงการที่อยู่ระหว่างการศึกษา
ลองใช้ฟังก์ชันนี้กับตัวอย่างเชิงปฏิบัติ - มาวิเคราะห์โครงการ OpenRCT2 กัน
การตั้งค่า
เพื่อประหยัดเวลา ฉันอาจจะข้ามขั้นตอนการติดตั้งและเริ่มจากช่วงเวลาที่ฉันมีเซิร์ฟเวอร์ TeamCity ทำงานบนคอมพิวเตอร์ของฉัน เราต้องไปที่: localhost:{port specified ในระหว่างกระบวนการติดตั้ง} (ในกรณีของฉัน localhost:9090) และป้อนข้อมูลการอนุญาต หลังจากเข้ามาแล้วเราจะพบกับ:
คลิกที่ปุ่มสร้างโครงการ จากนั้นเลือกด้วยตนเอง และกรอกข้อมูลในฟิลด์
หลังจากกดปุ่ม สร้างบัญชีตัวแทนเราได้รับการต้อนรับจากหน้าต่างพร้อมการตั้งค่า
คลิกกันได้เลย สร้างการกำหนดค่าบิลด์.
กรอกข้อมูลลงในช่องแล้วคลิก สร้างบัญชีตัวแทน. เราเห็นหน้าต่างขอให้คุณเลือกระบบควบคุมเวอร์ชัน เนื่องจากแหล่งที่มามีอยู่ในเครื่องแล้ว คลิก ข้ามไป.
ในที่สุด เราก็ไปยังการตั้งค่าโปรเจ็กต์
มาเพิ่มขั้นตอนการประกอบกัน โดยคลิก: ขั้นตอนการสร้าง -> เพิ่มขั้นตอนการสร้าง.
ที่นี่เราเลือก:
- ประเภทนักวิ่ง -> บรรทัดคำสั่ง
- เรียกใช้ -> สคริปต์ที่กำหนดเอง
เนื่องจากเราจะทำการวิเคราะห์ในระหว่างการรวบรวมโครงการ การประกอบและการวิเคราะห์ควรเป็นขั้นตอนเดียว ดังนั้นให้กรอกข้อมูลในฟิลด์ สคริปต์ที่กำหนดเอง:
เราจะดูแต่ละขั้นตอนในภายหลัง สิ่งสำคัญคือการโหลดตัววิเคราะห์ การประกอบโปรเจ็กต์ การวิเคราะห์ การส่งออกรายงาน และการจัดรูปแบบนั้นใช้โค้ดเพียงสิบเอ็ดบรรทัดเท่านั้น
สิ่งสุดท้ายที่เราต้องทำคือตั้งค่าตัวแปรสภาพแวดล้อม ซึ่งฉันได้สรุปวิธีการบางอย่างในการปรับปรุงให้อ่านง่ายขึ้น หากต้องการทำสิ่งนี้ เรามาเริ่มกันเลย: พารามิเตอร์ -> เพิ่มพารามิเตอร์ใหม่ และเพิ่มตัวแปรสามตัว:
สิ่งที่คุณต้องทำคือกดปุ่ม วิ่ง ที่มุมขวาบน ในขณะที่โครงการกำลังรวบรวมและวิเคราะห์ ฉันจะบอกคุณเกี่ยวกับสคริปต์
สคริปต์โดยตรง
ก่อนอื่น เราต้องดาวน์โหลดการแจกจ่าย PVS-Studio ล่าสุด สำหรับสิ่งนี้เราใช้ตัวจัดการแพ็คเกจ Chocolatey สำหรับผู้ที่ต้องการทราบข้อมูลเพิ่มเติมก็มีข้อมูลที่เกี่ยวข้องกัน
choco install pvs-studio -y
ต่อไป มาเปิดตัวยูทิลิตีการติดตามการสร้างโปรเจ็กต์ CLMonitor
%CLmon% monitor –-attach
จากนั้นเราจะสร้างโครงการเป็นตัวแปรสภาพแวดล้อม MSB เป็นเส้นทางไปยังเวอร์ชันของ MSBuild ที่ฉันต้องสร้าง
%MSB% %ProjPath% /t:clean
%MSB% %ProjPath% /t:rebuild /p:configuration=release
%MSB% %ProjPath% /t:g2
%MSB% %ProjPath% /t:PublishPortable
ป้อนข้อมูลเข้าสู่ระบบและรหัสลิขสิทธิ์สำหรับ PVS-Studio:
%PVS-Studio_cmd% credentials --username %PVS_Name% --serialNumber %PVS_Key%
หลังจากบิลด์เสร็จสมบูรณ์ ให้รัน CLMonitor อีกครั้งเพื่อสร้างไฟล์ที่ประมวลผลล่วงหน้าและการวิเคราะห์แบบสแตติก:
%CLmon% analyze -l "c:ptest.plog"
จากนั้นเราจะใช้ยูทิลิตี้อื่นจากการแจกจ่ายของเรา PlogConverter แปลงรายงานจากรูปแบบมาตรฐานเป็นรูปแบบเฉพาะของ TeamCity ด้วยเหตุนี้ เราจึงสามารถดูได้โดยตรงในหน้าต่างบิลด์
%PlogConverter% "c:ptest.plog" --renderTypes=TeamCity -o "C:temp"
ขั้นตอนสุดท้ายคือการแสดงรายงานที่จัดรูปแบบแล้ว แย่โดยที่ตัวแยกวิเคราะห์ TeamCity จะถูกเลือกขึ้นมา
type "C:tempptest.plog_TeamCity.txt"
รหัสสคริปต์เต็ม:
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"
ในระหว่างนี้ การประกอบและการวิเคราะห์โครงการเสร็จสมบูรณ์แล้ว เราสามารถไปที่แท็บได้ โครงการ และให้แน่ใจว่ามัน
ตอนนี้เรามาคลิกที่ การตรวจสอบรวมเพื่อเข้าไปดูรายงานตัววิเคราะห์:
คำเตือนจะถูกจัดกลุ่มตามหมายเลขกฎการวินิจฉัย หากต้องการดูโค้ด คุณต้องคลิกหมายเลขบรรทัดที่มีคำเตือน การคลิกที่เครื่องหมายคำถามที่มุมขวาบนจะเป็นการเปิดแท็บใหม่พร้อมเอกสารประกอบ คุณยังสามารถนำทางผ่านโค้ดได้โดยคลิกที่หมายเลขบรรทัดที่มีคำเตือนเกี่ยวกับตัววิเคราะห์ สามารถนำทางจากคอมพิวเตอร์ระยะไกลได้เมื่อใช้งาน SourceTreeRoot เครื่องหมาย ใครก็ตามที่สนใจโหมดการทำงานของเครื่องวิเคราะห์นี้สามารถทำความคุ้นเคยกับส่วนที่เกี่ยวข้องได้
การดูผลลัพธ์ของเครื่องวิเคราะห์
ตอนนี้เราได้ปรับใช้และกำหนดค่าบิลด์เสร็จแล้ว มาดูคำเตือนที่น่าสนใจที่พบในโปรเจ็กต์ที่เรากำลังพิจารณากัน
คำเตือน 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;
}
เครื่องวิเคราะห์สังเกตเห็นข้อผิดพลาดหลังจากจัดสรรหน่วยความจำแบบไดนามิกแล้ว สร้างวัตถุเมื่อมีข้อยกเว้นเกิดขึ้น หน่วยความจำจะไม่ถูกล้าง และหน่วยความจำรั่วเกิดขึ้น
คำเตือน 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),
....
};
มีเพียงไม่กี่คนที่ไม่ใช่เครื่องวิเคราะห์แบบคงที่ที่สามารถผ่านการทดสอบความใส่ใจนี้ได้ ตัวอย่างการคัดลอกและวางนี้ดีด้วยเหตุผลนี้
คำเตือน N3
struct RCT12SpriteBase
{
....
uint8_t flags;
....
};
struct rct1_peep : RCT12SpriteBase
{
....
uint8_t flags;
....
};
แน่นอนว่าการใช้ตัวแปรที่มีชื่อเดียวกันในคลาสพื้นฐานและในคลาสสืบทอดก็ไม่ใช่ข้อผิดพลาดเสมอไป อย่างไรก็ตาม เทคโนโลยีการสืบทอดนั้นถือว่าฟิลด์ทั้งหมดของคลาสพาเรนต์มีอยู่ในคลาสย่อย โดยการประกาศเขตข้อมูลที่มีชื่อเดียวกันในทายาทเราสร้างความสับสน
คำเตือน N4
void vehicle_visual_observation_tower(...., int32_t imageDirection, ....)
{
if ((imageDirection / 8) && (imageDirection / 8) != 3)
{
....
}
....
}
มาดูกันดีกว่า การแสดงออก ภาพทิศทาง/8 จะเป็นเท็จถ้า ภาพทิศทาง อยู่ในช่วงตั้งแต่ -7 ถึง 7 ส่วนที่สอง: (imageDirection / 8) != 3 เช็ค ภาพทิศทาง สำหรับการอยู่นอกช่วง: จาก -31 ถึง -24 และจาก 24 ถึง 31 ตามลำดับ ดูเหมือนว่าค่อนข้างแปลกสำหรับฉันที่จะตรวจสอบตัวเลขเพื่อรวมในช่วงใดช่วงหนึ่งในลักษณะนี้ และแม้ว่าจะไม่มีข้อผิดพลาดในโค้ดส่วนนี้ ฉันขอแนะนำให้เขียนเงื่อนไขเหล่านี้ใหม่เพื่อให้ชัดเจนยิ่งขึ้น สิ่งนี้จะทำให้ชีวิตง่ายขึ้นมากสำหรับผู้ที่อ่านและดูแลรักษาโค้ดนี้
คำเตือน 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;
....
}
....
}
ส่วนของโค้ดนี้น่าจะได้มาจากการคอมไพล์ จากนั้นเมื่อพิจารณาจากความคิดเห็นที่เหลือ โค้ดที่ไม่ทำงานบางส่วนก็ถูกลบออก อย่างไรก็ตาม ยังมีการดำเนินการอีกสองสามรายการที่เหลืออยู่ รหัสเคอร์เซอร์ซึ่งก็ไม่สมเหตุสมผลเช่นกัน
คำเตือน 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); // <=
}
....
}
รหัสนี้แก้ไขได้ง่ายมาก คุณเพียงแค่ต้องตรวจสอบเป็นครั้งที่สาม ผู้เล่น ไปยังตัวชี้ว่างหรือเพิ่มลงในเนื้อความของคำสั่งแบบมีเงื่อนไข ฉันอยากจะแนะนำตัวเลือกที่สอง:
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);
}
}
....
}
คำเตือน 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));
....
}
....
}
คุณสามารถกำจัดบรรทัดโค้ดที่อ่านยากได้ในคราวเดียว และแก้ไขปัญหาด้วยการตรวจสอบ nullptr. ฉันแนะนำให้เปลี่ยนรหัสดังนี้:
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);
....
}
....
}
คำเตือน N8
void CustomListView::MouseUp(....)
{
....
if (!ColumnHeaderPressedCurrentState)
{
ColumnHeaderPressed = std::nullopt;
ColumnHeaderPressedCurrentState = false;
Invalidate();
}
}
รหัสดูค่อนข้างแปลก สำหรับฉันดูเหมือนว่ามีการพิมพ์ผิดไม่ว่าจะในเงื่อนไขหรือเมื่อกำหนดตัวแปรใหม่ คอลัมน์ HeaderPressedCurrentState ความหมาย เท็จ.
เอาท์พุต
ดังที่เราเห็นแล้วว่าการรวมเครื่องวิเคราะห์แบบคงที่ PVS-Studio เข้ากับโปรเจ็กต์ TeamCity ของคุณนั้นค่อนข้างง่าย ในการดำเนินการนี้ การเขียนไฟล์การกำหนดค่าขนาดเล็กเพียงไฟล์เดียวก็เพียงพอแล้ว การตรวจสอบโค้ดจะช่วยให้คุณสามารถระบุปัญหาได้ทันทีหลังการประกอบ ซึ่งจะช่วยขจัดปัญหาเหล่านี้เมื่อความซับซ้อนและต้นทุนของการเปลี่ยนแปลงยังต่ำ
หากคุณต้องการแบ่งปันบทความนี้กับผู้ชมที่พูดภาษาอังกฤษ โปรดใช้ลิงก์การแปล: Vladislav Stolyarov
ที่มา: will.com