จะทำให้ backtrace () / backtrace_symbols () พิมพ์ชื่อฟังก์ชันได้อย่างไร?


90

ลินุกซ์เฉพาะbacktrace()และbacktrace_symbols()อนุญาตให้คุณสร้างการติดตามการโทรของโปรแกรม อย่างไรก็ตามมันพิมพ์เฉพาะที่อยู่ของฟังก์ชันไม่ใช่ชื่อของโปรแกรมของฉัน ฉันจะทำให้พวกเขาพิมพ์ชื่อฟังก์ชันด้วยได้อย่างไร ฉันได้ลองรวบรวมโปรแกรมด้วย-gเช่นเดียวกับ-ggdb. กรณีทดสอบด้านล่างเพียงแค่พิมพ์สิ่งนี้:

    พื้นหลัง ------------
    ./a.out () [0x8048616]
    ./a.out () [0x8048623]
    /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
    ./a.out () [0x8048421]
    ----------------------
    

ฉันต้องการให้ 2 รายการแรกแสดงชื่อฟังก์ชันด้วยfooและmain

รหัส:


เป็นไปได้ที่ซ้ำกันของวิธีรับรายละเอียด backtrace
Nemo

btw ฟังก์ชันจะbacktrace_symbols_fdดำเนินการเช่นเดียวกับbacktrace_symbols()แต่สตริงที่เป็นผลลัพธ์จะถูกเขียนลงใน file descriptor fdทันที
nhnghia

ฉันได้ทดสอบหลายวิธีโดยละเอียดแล้วที่stackoverflow.com/questions/3899870/print-call-stack-in-c-or-c/…
Ciro Santilli 郝海东冠状病六四事件法轮功

คำตอบ:


65

สัญลักษณ์นำมาจากตารางสัญลักษณ์แบบไดนามิก คุณต้องมี-rdynamicตัวเลือกgccซึ่งจะส่งต่อแฟล็กไปยังตัวเชื่อมโยงซึ่งทำให้แน่ใจว่าสัญลักษณ์ทั้งหมดถูกวางไว้ในตาราง

(ดูหน้าตัวเลือกลิงก์ของคู่มือ GCCและ / หรือหน้าBacktracesของคู่มือ glibc )


10
สิ่งนี้ใช้ไม่ได้กับสัญลักษณ์คงที่ libunwindที่ @Nemo กล่าวถึงใช้งานได้กับฟังก์ชันคงที่
Nathan Kidd

ในโครงการ CMake มีการตั้งค่าผิดพลาดเป็นADD_COMPILE_OPTIONS(-rdynamic). แต่จำเป็นต้องมีADD_LINK_OPTIONS(-rdynamic)หรือเทียบเท่ากับพฤติกรรมที่ต้องการ
Stéphane

30

ใช้คำสั่ง addr2lineเพื่อแมปแอดเดรสที่รันได้กับชื่อไฟล์ซอร์สโค้ด + หมายเลขบรรทัด ให้-fตัวเลือกในการรับชื่อฟังก์ชันด้วย

อีกทางเลือกหนึ่งให้ลองlibunwind


3
บรรทัด addr2 นั้นดีเพราะมีชื่อไฟล์และหมายเลขบรรทัดในเอาต์พุต แต่จะล้มเหลวทันทีที่ตัวโหลดดำเนินการย้ายตำแหน่ง
Erwan Legrand

... และด้วยการย้ายตำแหน่ง ASLR เป็นเรื่องปกติมากขึ้นกว่าเดิม
Erwan Legrand

@ErwanLegrand: ก่อนหน้า ASLR ฉันคิดว่าการย้ายตำแหน่งนั้นสามารถคาดเดาได้และaddr2lineทำงานได้อย่างน่าเชื่อถือแม้กระทั่งสำหรับที่อยู่ในวัตถุที่ใช้ร่วมกัน (?) แต่ใช่บนแพลตฟอร์มที่ทันสมัยคุณจะต้องรู้ที่อยู่โหลดที่แท้จริงของวัตถุที่ย้ายตำแหน่งได้แม้จะดำเนินการตามหลักการนี้ .
Nemo

ฉันไม่สามารถบอกได้ว่ามันทำงานได้ดีแค่ไหนก่อนหน้า ASLR ปัจจุบันจะใช้สำหรับโค้ดภายในไฟล์ปฏิบัติการ "ปกติ" เท่านั้นและล้มเหลวสำหรับโค้ดภายใน DSO และ PIEs (Position Independent Executables) โชคดีที่ libunwind ดูเหมือนจะใช้ได้กับสิ่งเหล่านี้ทั้งหมด
Erwan Legrand

ฉันลืมเกี่ยวกับการสนทนานี้ไปแล้ว Libbacktrace ซึ่งฉันไม่รู้จักในเวลานั้นเป็นตัวเลือกที่ดีกว่า (ดูคำตอบใหม่ของฉัน)
Erwan Legrand

12

Libbacktrace ที่ยอดเยี่ยมโดย Ian Lance Taylor ช่วยแก้ปัญหานี้ได้ จัดการสแต็กคลี่คลายและรองรับทั้งสัญลักษณ์ ELF ธรรมดาและสัญลักษณ์ดีบัก DWARF

Libbacktrace ไม่ต้องการการเอ็กซ์พอร์ตสัญลักษณ์ทั้งหมดซึ่งน่าเกลียดและ ASLR ไม่ทำลายมัน

เดิม Libbacktrace เป็นส่วนหนึ่งของการกระจาย GCC ตอนนี้สามารถพบเวอร์ชันสแตนด์อโลนได้ที่ Github:

https://github.com/ianlancetaylor/libbacktrace


2

คำตอบด้านบนมีข้อบกพร่องหาก ret == -1 และ errno เป็น EINTER คุณควรลองอีกครั้ง แต่จะไม่นับว่าถูกคัดลอก (จะไม่สร้างบัญชีสำหรับสิ่งนี้หากคุณไม่ชอบมันยาก)


0

เพิ่มการติดตามย้อนกลับ

สะดวกมากเพราะพิมพ์ทั้งสองอย่าง:

  • ชื่อฟังก์ชัน C ++ ที่ไม่มีการเชื่อม
  • หมายเลขบรรทัด

โดยอัตโนมัติสำหรับคุณ

สรุปการใช้งาน:

ฉันได้ให้ตัวอย่างที่รันได้น้อยที่สุดสำหรับมันและวิธีการอื่น ๆ อีกมากมายที่: print call stack ใน C หรือ C ++


คำถามถูกแท็ก [c] ไม่ใช่ [c ++]
Yakov Galka
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.