วิธีการวิเคราะห์แบบไดนามิก
ที่นี่ฉันจะอธิบายวิธีการวิเคราะห์แบบไดนามิกบางส่วน
วิธีการแบบไดนามิกเรียกใช้โปรแกรมเพื่อกำหนดกราฟการโทร
วิธีที่ตรงกันข้ามกับวิธีไดนามิกคือวิธีการแบบคงที่ซึ่งพยายามกำหนดจากแหล่งที่มาเพียงอย่างเดียวโดยไม่ต้องเรียกใช้โปรแกรม
ข้อดีของวิธีการแบบไดนามิก:
- จับตัวชี้ฟังก์ชันและการเรียกใช้ C ++ เสมือน สิ่งเหล่านี้มีอยู่เป็นจำนวนมากในซอฟต์แวร์ที่ไม่สำคัญ
ข้อเสียของวิธีการแบบไดนามิก:
- คุณต้องรันโปรแกรมซึ่งอาจจะช้าหรือต้องใช้การตั้งค่าที่คุณไม่มีเช่นการคอมไพล์ข้าม
- เฉพาะฟังก์ชันที่ถูกเรียกใช้จริงเท่านั้นที่จะแสดง เช่นบางฟังก์ชันอาจถูกเรียกหรือไม่ขึ้นอยู่กับอาร์กิวเมนต์บรรทัดคำสั่ง
KcacheGrind
https://kcachegrind.github.io/html/Home.html
โปรแกรมทดสอบ:
int f2(int i) { return i + 2; }
int f1(int i) { return f2(2) + i + 1; }
int f0(int i) { return f1(1) + f2(2); }
int pointed(int i) { return i; }
int not_called(int i) { return 0; }
int main(int argc, char **argv) {
int (*f)(int);
f0(1);
f1(1);
f = pointed;
if (argc == 1)
f(1);
if (argc == 2)
not_called(1);
return 0;
}
การใช้งาน:
sudo apt-get install -y kcachegrind valgrind
# Compile the program as usual, no special flags.
gcc -ggdb3 -O0 -o main -std=c99 main.c
# Generate a callgrind.out.<PID> file.
valgrind --tool=callgrind ./main
# Open a GUI tool to visualize callgrind data.
kcachegrind callgrind.out.1234
ตอนนี้คุณเหลืออยู่ในโปรแกรม GUI ที่ยอดเยี่ยมซึ่งมีข้อมูลประสิทธิภาพที่น่าสนใจมากมาย
ที่ด้านขวาล่างเลือกแท็บ "กราฟการโทร" แสดงกราฟการโทรแบบโต้ตอบที่สัมพันธ์กับเมตริกประสิทธิภาพในหน้าต่างอื่น ๆ เมื่อคุณคลิกฟังก์ชัน
ในการส่งออกกราฟให้คลิกขวาแล้วเลือก "ส่งออกกราฟ" PNG ที่ส่งออกมีลักษณะดังนี้:
จากนั้นเราจะเห็นว่า:
- โหนดรูทคือ
_start
ซึ่งเป็นจุดเริ่มต้นของ ELF ที่แท้จริงและมีบอยเลอร์การเริ่มต้นของ glibc
f0
, f1
และf2
จะเรียกว่าเป็นที่คาดหวังจากคนอื่น
pointed
จะแสดงด้วยแม้ว่าเราจะเรียกมันด้วยตัวชี้ฟังก์ชันก็ตาม อาจไม่มีการเรียกใช้หากเราผ่านอาร์กิวเมนต์บรรทัดคำสั่ง
not_called
ไม่แสดงเนื่องจากไม่ได้รับการเรียกใช้ในการรันเนื่องจากเราไม่ได้ส่งผ่านอาร์กิวเมนต์บรรทัดคำสั่งเพิ่มเติม
สิ่งที่น่าสนใจvalgrind
คือไม่ต้องการตัวเลือกการรวบรวมพิเศษใด ๆ
ดังนั้นคุณสามารถใช้งานได้แม้ว่าคุณจะไม่มีซอร์สโค้ด แต่มีเพียงไฟล์ปฏิบัติการเท่านั้น
valgrind
จัดการได้โดยการรันโค้ดของคุณผ่าน "เครื่องเสมือน" ที่มีน้ำหนักเบา นอกจากนี้ยังทำให้การดำเนินการช้ามากเมื่อเทียบกับการดำเนินการแบบเนทีฟ
ดังที่เห็นได้จากกราฟข้อมูลเวลาเกี่ยวกับการเรียกใช้ฟังก์ชันแต่ละครั้งและสามารถใช้ในการกำหนดโปรไฟล์โปรแกรมซึ่งน่าจะเป็นกรณีการใช้งานดั้งเดิมของการตั้งค่านี้ไม่ใช่เพื่อดูกราฟการโทร: ฉันจะทำโปรไฟล์ได้อย่างไร โค้ด C ++ ทำงานบน Linux?
ทดสอบบน Ubuntu 18.04
gcc -finstrument-functions
+ etrace
https://github.com/elcritch/etrace
-finstrument-functions
เพิ่มการเรียกกลับ etrace แยกวิเคราะห์ไฟล์ ELF และดำเนินการเรียกกลับทั้งหมด
ฉันไม่สามารถใช้งานได้ แต่น่าเสียดาย: เหตุใด "-finstrument-functions" จึงไม่ทำงานสำหรับฉัน
ผลลัพธ์ที่อ้างสิทธิ์เป็นรูปแบบ:
\-- main
| \-- Crumble_make_apple_crumble
| | \-- Crumble_buy_stuff
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | | \-- Crumble_buy
| | \-- Crumble_prepare_apples
| | | \-- Crumble_skin_and_dice
| | \-- Crumble_mix
| | \-- Crumble_finalize
| | | \-- Crumble_put
| | | \-- Crumble_put
| | \-- Crumble_cook
| | | \-- Crumble_put
| | | \-- Crumble_bake
น่าจะเป็นวิธีที่มีประสิทธิภาพมากที่สุดนอกเหนือจากการรองรับการติดตามฮาร์ดแวร์เฉพาะ แต่มีข้อเสียที่คุณต้องคอมไพล์โค้ดใหม่