จะทำให้ 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.