कोड की लाइन निर्धारित करें जो एक विभाजन गलती का कारण बनता है?


151

यह कैसे निर्धारित किया जाता है कि गलती उस कोड में कहां है जो एक विभाजन गलती का कारण बनता है ?

क्या मेरा कंपाइलर ( gcc) प्रोग्राम में गलती का स्थान दिखा सकता है ?


5
कोई gcc / gdb नहीं कर सकता। आपको पता चल सकता है कि सेगफॉल्ट कहां हुआ था, लेकिन वास्तविक त्रुटि बिल्कुल अलग स्थान पर हो सकती है।

जवाबों:


218

GCC ऐसा नहीं कर सकता है लेकिन GDB ( डिबगर ) निश्चित रूप से कर सकता है। आप -gइस तरह से स्विच का उपयोग कर प्रोग्राम संकलित करें:

gcc program.c -g

फिर gdb का उपयोग करें:

$ gdb ./a.out
(gdb) run
<segfault happens here>
(gdb) backtrace
<offending code is shown here>

GDB के साथ आरंभ करने के लिए यहां एक अच्छा ट्यूटोरियल है।

जहां सेगफॉल्ट होता है, आमतौर पर यह केवल एक सुराग होता है कि "गलती का कारण क्या होता है" यह कोड में है। दी गई जगह जरूरी नहीं है कि जहां समस्या रहती है।


28
ध्यान दें कि जहां सेगफॉल्ट होता है, वह आमतौर पर केवल एक सुराग होता है, जहां "गलती का कारण बनता है" यह कोड में है। एक महत्वपूर्ण सुराग, लेकिन यह जरूरी नहीं कि समस्या कहां रहती है।
mpez0

9
आप अधिक विवरण प्राप्त करने के लिए (bt full) का उपयोग भी कर सकते हैं।
ant2009

1
मुझे यह उपयोगी लगता है: gnu.org/software/gcc/bugs/segfault.html
संभावना

2
के btलिए एक आशुलिपि के रूप में उपयोग करें backtrace
rustyx

43

इसके अलावा, आप valgrindएक कोशिश दे सकते हैं : यदि आप इंस्टॉल करते हैं valgrindऔर चलाते हैं

valgrind --leak-check=full <program>

तब यह आपके प्रोग्राम को चलाएगा और किसी भी सेगफॉल्ट के लिए स्टैक के निशान को प्रदर्शित करेगा, साथ ही किसी भी अवैध मेमोरी को पढ़ता है या लिखता है और मेमोरी लीक करता है। यह वास्तव में काफी उपयोगी है।


2
+1, मेमोरी त्रुटियों को दूर करने के लिए Valgrind का उपयोग बहुत तेज़ / आसान है। गैर-अनुकूलित पर डिबगिंग प्रतीकों के साथ बनाता है, यह आपको बताता है कि वास्तव में कहां सेगफॉल्ट हुआ और क्यों।
टिम पोस्ट

1
दुःख की बात है कि -g0 के साथ संकलित करते समय और वैग्राइंड के साथ संयुक्त होने पर मेरा सेगफॉल्ट गायब हो जाता है।
जॉनमुड

2
--leak-check=fullsegfaults को डीबग करने में मदद नहीं करेगा। यह केवल मेमोरी लीक डिबगिंग के लिए उपयोगी है।
ks1322

@JohnMudd मेरे पास एक segfault है जो केवल 1% इनपुट फ़ाइलों का परीक्षण करता है, यदि आप विफल इनपुट को दोहराते हैं तो यह विफल नहीं होगा। मेरी समस्या मल्टीथ्रेडिंग के कारण हुई। अभी तक मुझे इस समस्या के कारण कोड की लाइन का पता नहीं चला है। मैं इस समस्या को अभी तक कवर करने के लिए पुनः प्रयास का उपयोग कर रहा हूं। यदि -g विकल्प का उपयोग करें, तो गलती दूर हो जाती है!
केमिन झोउ

18

आप कोर डंप का उपयोग भी कर सकते हैं और फिर इसे gdb के साथ जांच सकते हैं। उपयोगी जानकारी प्राप्त करने के लिए आपको -gध्वज के साथ संकलन करने की भी आवश्यकता है ।

जब भी आपको संदेश मिले:

 Segmentation fault (core dumped)

एक कोर फ़ाइल को आपकी वर्तमान निर्देशिका में लिखा जाता है। और आप इसे कमांड से जांच सकते हैं

 gdb your_program core_file

जब प्रोग्राम क्रैश हो जाता है तो फ़ाइल में मेमोरी की स्थिति होती है। एक कोर डंप आपके सॉफ्टवेयर की तैनाती के दौरान उपयोगी हो सकता है।

सुनिश्चित करें कि आपका सिस्टम कोर डंप फ़ाइल आकार को शून्य पर सेट नहीं करता है। आप इसे असीमित के साथ सेट कर सकते हैं:

ulimit -c unlimited

हालांकि सावधान! कि कोर डंप विशाल हो सकते हैं।


मैंने हाल ही में आर्च-लाइनक्स पर स्विच किया। मेरी वर्तमान निर्देशिका में कोर डंप फ़ाइल नहीं है। मैं इसे कैसे उत्पन्न कर सकता हूं?
अभिनव

आप इसे उत्पन्न नहीं करते हैं; लिनक्स करता है। Core dumps को अलग-अलग स्थान पर अलग-अलग लिंज़ - Google के आसपास संग्रहीत किया जाता है। आर्क लिनक्स के लिए, इस wiki.archlinux.org/index.php/Core_dump को
Mawg का कहना है कि मोनिका

7

ऐसे कई उपकरण उपलब्ध हैं जो विभाजन दोषों को डीबग करने में मदद करते हैं और मैं अपने पसंदीदा टूल को सूची में जोड़ना चाहूंगा: एड्रेस सैनिटाइज़र (अक्सर संक्षिप्त एएसएएन)

आधुनिक कंपाइलर काम के -fsanitize=addressझंडे के साथ आते हैं , कुछ संकलन समय जोड़ते हैं और ओवरहेड रन करते हैं जो अधिक त्रुटि जाँच करता है।

प्रलेखन के अनुसार इन चेक में डिफ़ॉल्ट रूप से विभाजन दोषों को पकड़ना शामिल है। यहां लाभ यह है कि आपको जीडीबी के आउटपुट के समान स्टैक ट्रेस मिलता है, लेकिन डिबगर के अंदर प्रोग्राम को चलाने के बिना। एक उदाहरण:

int main() {
  volatile int *ptr = (int*)0;
  *ptr = 0;
}
$ gcc -g -fsanitize=address main.c
$ ./a.out
AddressSanitizer:DEADLYSIGNAL
=================================================================
==4848==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x5654348db1a0 bp 0x7ffc05e39240 sp 0x7ffc05e39230 T0)
==4848==The signal is caused by a WRITE memory access.
==4848==Hint: address points to the zero page.
    #0 0x5654348db19f in main /tmp/tmp.s3gwjqb8zT/main.c:3
    #1 0x7f0e5a052b6a in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26b6a)
    #2 0x5654348db099 in _start (/tmp/tmp.s3gwjqb8zT/a.out+0x1099)

AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /tmp/tmp.s3gwjqb8zT/main.c:3 in main
==4848==ABORTING

आउटपुट gdb के उत्पादन की तुलना में थोड़ा अधिक जटिल है, लेकिन अपसाइड होते हैं:

  • स्टैक ट्रेस प्राप्त करने के लिए समस्या को पुन: उत्पन्न करने की कोई आवश्यकता नहीं है। बस विकास के दौरान ध्वज को सक्षम करना पर्याप्त है।

  • ASANs केवल विभाजन दोषों की तुलना में बहुत अधिक पकड़ते हैं। भले ही वह स्मृति क्षेत्र इस प्रक्रिया के लिए सुलभ था, भले ही सीमा से बाहर कई को पकड़ा जाएगा।


Ang यही क्लैंग 3.1+ और GCC 4.8+ है


यह मेरे लिए सबसे ज्यादा मददगार है। मेरे पास एक बहुत ही सूक्ष्म बग है जो लगभग 1% की आवृत्ति के साथ यादृच्छिक रूप से होता है। मैं बड़ी संख्या में इनपुट फ़ाइलों के साथ प्रक्रिया करता हूं (16 प्रमुख चरण; प्रत्येक एक अलग सी या सी ++ बाइनरी द्वारा किया जाता है)। बहु-थ्रेडिंग के कारण एक बाद का चरण केवल अनियमित रूप से विभाजन दोष को ट्रिगर करेगा। डिबग करना कठिन है। इस विकल्प ने डिबग सूचना आउटपुट को ट्रिगर किया, कम से कम इसने मुझे बग के स्थान को खोजने के लिए कोड समीक्षा के लिए एक प्रारंभिक बिंदु दिया।
केमिन झोउ

2

कोर डंप के बारे में लुकास का जवाब अच्छा है। मेरे .cshrc में मेरे पास है:

alias core 'ls -lt core; echo where | gdb -core=core -silent; echo "\n"'

'कोर' दर्ज करके बैकट्रेस प्रदर्शित करने के लिए। और तारीख की मोहर, यह सुनिश्चित करने के लिए कि मैं सही फ़ाइल देख रहा हूँ :(।

जोड़ा गया : यदि कोई स्टैक करप्ट बग है, तो कोर डंप पर लागू बैकट्रेस अक्सर कचरा होता है। इस मामले में, जीडीबी के भीतर कार्यक्रम को चलाने से बेहतर परिणाम मिल सकते हैं, जैसा कि स्वीकृत उत्तर के अनुसार (दोष आसानी से प्रतिलिपि प्रस्तुत करने योग्य है)। और कई प्रक्रियाओं को एक साथ कोर डंपिंग से भी सावधान रहें; कुछ OS कोर फाइल के नाम में PID जोड़ते हैं।


4
और ulimit -c unlimitedपहली जगह में कोर डंप को सक्षम करने के लिए मत भूलना ।
जेम्स मोरिस

@ नाम: सही। लुकास ने पहले ही इसका उल्लेख किया है। और हम में से जो अभी भी csh में फंसे हुए हैं, 'सीमा' का उपयोग करते हैं। और मैं CYGWIN स्टैकडम्प्स को पढ़ने में सक्षम नहीं हूं (लेकिन मैंने 2 या 3 वर्षों से कोशिश नहीं की है)।
जोसेफ क्विंसे

2

उपरोक्त सभी उत्तर सही और अनुशंसित हैं; यह उत्तर केवल एक अंतिम उपाय के रूप में अभिप्रेत है यदि उपरोक्त किसी भी दृष्टिकोण का उपयोग नहीं किया जा सकता है।

यदि बाकी सभी विफल हो जाते हैं, तो आप हमेशा अपने अस्थायी डिबग-प्रिंट स्टेटमेंट (जैसे fprintf(stderr, "CHECKPOINT REACHED @ %s:%i\n", __FILE__, __LINE__);) के साथ अपने कार्यक्रम को फिर से जोड़ सकते हैं, जिसे आप अपने कोड के प्रासंगिक भागों के रूप में मानते हैं। फिर प्रोग्राम को चलाएं, और देखें कि क्रैश होने से ठीक पहले आखिरी डिबग-प्रिंट क्या छपा था - आपको पता है कि आपका प्रोग्राम अभी तक मिला है, इसलिए क्रैश उस बिंदु के बाद हुआ होगा। डिबग-प्रिंट जोड़ें, हटाएं, पुन: परीक्षण करें, और फिर से परीक्षण चलाएं, जब तक कि आपने इसे कोड की एकल पंक्ति तक सीमित न कर दिया हो। उस बिंदु पर आप बग को ठीक कर सकते हैं और सभी अस्थायी डिबग-प्रिंट निकाल सकते हैं।

यह काफी थकाऊ है, लेकिन इसके बारे में कहीं भी काम करने का फायदा है - केवल एक बार ऐसा नहीं हो सकता है यदि आपके पास किसी कारण से स्टडआउट या स्टेडर तक पहुंच नहीं है, या यदि आप जिस बग को ठीक करने की कोशिश कर रहे हैं वह एक दौड़ है -कंडिशन जिसका व्यवहार कार्यक्रम के समय में परिवर्तन होता है (चूंकि डिबग-प्रिंट प्रोग्राम को धीमा कर देगा और इसके समय को बदल देगा)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.