วิธีการทำให้เกิดการถ่ายโอนข้อมูลหลักใน C / C ++ โดยทางโปรแกรม


93

ฉันต้องการบังคับให้ดัมพ์หลักที่ตำแหน่งเฉพาะในแอปพลิเคชัน C ++ ของฉัน

ฉันรู้ว่าฉันทำได้โดยทำสิ่งที่ชอบ:

int * crash = NULL;
*crash = 1;

แต่อยากทราบว่ามีวิธีที่สะอาดกว่านี้ไหม?

ฉันกำลังใช้ Linux อยู่


18
วิธี "สะอาดกว่า" ในการถ่ายโอนข้อมูลหลัก? .... ดีครับ;)
OJ.

5
อันนี้น่ารัก. ยังดีกว่าใช้บูลีน (enum ใน c?) ... if ( crash = TRUE) {/ OH SHI ... * /}
Ape-inago

3
BTW วิธีนั้นใช้ไม่ได้ใน UNIX ทั้งหมด HPUX หนึ่งตัวช่วยให้คุณอ่านและเขียน NULL โดยไม่ต้องรับโทษ (ขอบคุณที่สามารถกำหนดค่าได้)
paxdiablo

1
ฉันเพิ่งเรียนรู้สิ่งใหม่ ๆ ที่ยอดเยี่ยม 3 หรือ 4 อย่าง ขอบคุณ.
Trevor Boyd Smith

@pax นั่นเป็นเหตุผลมากกว่าที่จะหาวิธีทั่วไป;) ขอบคุณ
hhafez

คำตอบ:


78

การเพิ่มสัญญาณหมายเลข 6 ( SIGABRTใน Linux) เป็นวิธีหนึ่งที่ทำได้ (แต่โปรดทราบว่า SIGABRT ไม่จำเป็นต้องเป็น 6 ในการใช้งาน POSIX ทั้งหมดดังนั้นคุณอาจต้องการใช้SIGABRTค่านี้เองหากเป็นอย่างอื่นที่ไม่ใช่ quick'n 'รหัสดีบักสกปรก)

#include <signal.h>
: : :
raise (SIGABRT);

การโทรabort()จะทำให้เกิดการถ่ายโอนข้อมูลหลักและคุณสามารถทำได้โดยไม่ต้องยุติกระบวนการของคุณด้วยการโทรfork()ตามด้วยabort()ในลูกเท่านั้น - ดูคำตอบนี้สำหรับรายละเอียด


7
SIGABRT ไม่จำเป็นต้องเป็นสัญญาณหมายเลข 6 (แม้ว่าจะเป็น - และโดยเฉพาะบน Linux)
Jonathan Leffler

4
ไม่คุณพูดถูกไม่ใช่ แต่ฉันมักจะไม่กังวลมากเกินไปเกี่ยวกับความถูกต้องของโค้ดดีบัก หากสิ่งนั้นหลุดรอดเข้าไปในป่าความสะอาดของรหัสของฉันก็เป็นสิ่งที่ฉันกังวลน้อยที่สุด :-)
paxdiablo

2
การเรียก abort () อาจไม่มีประโยชน์ในบางสถาปัตยกรรมที่มีคอมไพเลอร์บางตัวและไลบรารี C บางตัว (เช่น gcc และ glibc หรือ uClibc บน ARM) เนื่องจากฟังก์ชัน abort () ถูกประกาศด้วยแอตทริบิวต์noreturnและคอมไพเลอร์จะปรับข้อมูลที่ส่งคืนทั้งหมดให้เหมาะสมโดยสิ้นเชิง ซึ่งทำให้ไฟล์หลักไม่สามารถใช้งานได้ คุณไม่สามารถติดตามมันผ่านการเรียกเพื่อเพิ่ม () หรือยกเลิก () เอง ดังนั้นจึงเป็นการดีกว่ามากที่จะเรียก Raise (SIGABRT) โดยตรงหรือ kill (getpid (), SIGABRT) ซึ่งแทบจะเหมือนกัน
Alexander Amelkin

3
ขออภัยบน ARM สิ่งเดียวกันนี้เกิดขึ้นแม้จะมีการเพิ่ม (SIGABRT) ดังนั้นวิธีเดียวที่จะสร้างไฟล์หลักที่ตรวจสอบย้อนกลับได้คือkill (getpid (), SIGABRT)
Alexander Amelkin

คำแนะนำulimit -c unlimitedจากคำตอบของ Suvesh Pratapa ช่วยฉันได้มากสำหรับคำตอบนี้
Boris Däppen

75

ไม่กี่ปีที่ผ่านมา Google ได้เปิดตัวห้องสมุดcoredumper

ภาพรวม

ไลบรารี coredumper สามารถคอมไพล์ลงในแอปพลิเคชันเพื่อสร้างแกนกลางของโปรแกรมที่กำลังทำงานอยู่ - โดยไม่ต้องยุติ รองรับการถ่ายโอนข้อมูลหลักทั้งแบบเดี่ยวและแบบมัลติเธรดแม้ว่าเคอร์เนลจะไม่รองรับไฟล์คอร์แบบมัลติเธรด

Coredumper จัดจำหน่ายภายใต้เงื่อนไขของ BSD License

ตัวอย่าง

นี่ไม่ใช่ตัวอย่างที่สมบูรณ์ เพียงแค่ให้คุณรู้สึกว่า coredumper API มีลักษณะอย่างไร

#include <google/coredumper.h>
...
WriteCoreDump('core.myprogram');
/* Keep going, we generated a core file,
 * but we didn't crash.
 */

ไม่ใช่สิ่งที่คุณขอ แต่อาจจะดีกว่านี้ก็ได้ :)


3
ตอนแรกฉันค่อนข้างตื่นเต้นเมื่อเจอคำตอบนี้ แต่รถเทพื้นดูค่อนข้างเก่าและทรุดโทรมในทุกวันนี้ ยังมีข้อบ่งชี้ว่ามันใช้ไม่ได้อีกต่อไปบนเคอร์เนล Linux ร่วมสมัย: stackoverflow.com/questions/38314020/…
jefe2000

37

ตามที่ระบุไว้ในการจัดการสัญญาณสัญญาณใด ๆ ที่มีการดำเนินการที่ระบุว่าเป็น 'แกน' จะบังคับให้มีการถ่ายโอนข้อมูลหลัก ตัวอย่างบางส่วน ได้แก่ :

SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGSEGV      11       Core    Invalid memory reference

ตรวจสอบให้แน่ใจว่าคุณเปิดใช้งานการทิ้งหลัก:

ulimit -c unlimited

ขอบคุณความคิดเห็นของคุณเกี่ยวกับการเปิดใช้งานการทิ้งหลักด้วยความulimit -c unlimitedช่วยเหลือ

คุณจะตั้งค่า ulimit จากภายในรหัสได้อย่างไร? @ ks1322
Karan Joisher

@KaranJoisher นั่นอาจจะคุ้มค่ากับคำถามอื่นสำหรับตัวมันเอง แต่ในระยะสั้นคุณสามารถsetrlimit(RLIMIT_CORE, &core_limits);ใช้ได้ผ่านทาง#include <sys/resource.h>. คุณสร้างโครงสร้างของชนิดrlimitจากนั้นตั้งค่าrlim_curและrlim_maxสมาชิก
Brent Writes Code


11

วิงวอน

abort();

ที่เกี่ยวข้องบางครั้งคุณต้องการการติดตามย้อนกลับโดยไม่มีการถ่ายโอนข้อมูลหลักจริงและอนุญาตให้โปรแกรมทำงานต่อไป: ตรวจสอบฟังก์ชัน glibc backtrace () และ backtrace_symbols (): http://www.gnu.org/s/libc/ manual / html_node / Backtraces.html


6

อีกวิธีในการสร้าง core ดัมพ์:

$ bash
$ kill -s SIGSEGV $$

เพียงสร้างอินสแตนซ์ใหม่ของการทุบตีและฆ่าด้วยสัญญาณที่ระบุ $$เป็น PID ของเปลือก มิฉะนั้นคุณจะฆ่า bash ปัจจุบันของคุณและจะออกจากระบบเทอร์มินัลปิดหรือตัดการเชื่อมต่อ

$ bash 
$ kill -s SIGABRT $$
$ bash
$ kill -s SIGFPE $$

ง่ายมากและมีประโยชน์!
firo

1
ฉันชอบอันนั้นเหมือนกัน สามารถทำให้ง่ายbash -c 'kill -SIGSEGV $$'ขึ้นได้
Christian Krause


2

บางครั้งอาจเหมาะสมที่จะทำสิ่งนี้:

int st = 0;
pid_t p = fork();

if (!p) {
    signal(SIGABRT, SIG_DFL);
    abort(); // having the coredump of the exact copy of the calling thread
} else {
    waitpid(p, &st, 0); // rip the zombie
}

// here the original process continues to live

ปัญหาอย่างหนึ่งของแนวทางง่ายๆนี้คือเธรดเดียวเท่านั้นที่จะถูกตัดทอน


1
 #include <stdio.h>
 #include <stdlib.h>
 int main()
 {
   printf("\n");
   printf("Process is aborting\n");
   abort();
   printf("Control not reaching here\n");
   return 0;
 }

ใช้แนวทางนี้ทุกที่ที่คุณต้องการ :)


0
#include <assert.h>
.
.
.
     assert(!"this should not happen");

อาจจำเป็นต้องโคลนด้วย NDEBUG เพื่อให้การยืนยันโดยเฉพาะนี้ใช้งานได้แม้ว่าจะไม่มีการยืนยันอื่น ๆ ก็ตาม
Rhys Ulerich
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.