วิธีการวิเคราะห์แบบไดนามิก
ที่นี่ฉันจะอธิบายวิธีการวิเคราะห์แบบไดนามิกบางส่วน
วิธีการแบบไดนามิกเรียกใช้โปรแกรมเพื่อกำหนดกราฟการโทร
วิธีที่ตรงกันข้ามกับวิธีไดนามิกคือวิธีการแบบคงที่ซึ่งพยายามกำหนดจากแหล่งที่มาเพียงอย่างเดียวโดยไม่ต้องเรียกใช้โปรแกรม
ข้อดีของวิธีการแบบไดนามิก:
- จับตัวชี้ฟังก์ชันและการเรียกใช้ 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
น่าจะเป็นวิธีที่มีประสิทธิภาพมากที่สุดนอกเหนือจากการรองรับการติดตามฮาร์ดแวร์เฉพาะ แต่มีข้อเสียที่คุณต้องคอมไพล์โค้ดใหม่