2010-12-03 10 views
5

मैं छोटे में पूरे प्रश्न बांट दिया है:कैसे gdb सी ++ के लिए stacktrace reconstructs?

  1. अलग एल्गोरिदम किस तरह GDB stacktraces फिर से संगठित करने का उपयोग करने के लिए सक्षम है?
  2. प्रत्येक स्टैकट्रैक पुनर्निर्माण एल्गोरिदम उच्च स्तर पर कैसे काम करता है? फायदे और नुकसान?
  3. प्रत्येक स्टैकट्रैक पुनर्निर्माण एल्गोरिदम के लिए काम करने के लिए किस तरह के मेटा-सूचना कंपाइलर को प्रोग्राम में प्रदान करने की आवश्यकता है?
  4. और इसी तरह के जी ++ कंपाइलर स्विच जो विशेष एल्गोरिदम को सक्षम/अक्षम करते हैं?
+1

http://en.wikipedia.org/wiki/Call_stack, कंपाइलर स्रोत लाइनों पर डीबगिंग जानकारी मैपिंग कोड ऑफ़सेट जोड़ता है। –

+1

वह विकिपीडिया लेख अन्य एल्गोरिदम के बारे में विवरण में नहीं जाता है, "एक अन्य फ़ंक्शन को कॉल करते समय स्टोर में आरबीपी स्टैक में स्टोर करें"। उदाहरण के लिए यदि एल्गोरिदम अब काम नहीं करेगा अगर -फॉमिट-फ्रेम-पॉइंटर संकलन समय पर सेट है। स्टैक को डिस्क्रिप्टर को खोलने के बारे में क्या? वह कैसे स्टैकट्रैक का पुनर्निर्माण करने के लिए उपयोग किया जाता है? क्या कोई अन्य एल्गोरिदम हैं? –

+1

'-फॉमिट-फ्रेम-पॉइंटर' के साथ जीडीबी पीओपी है। –

उत्तर

7

बात हो रही है स्यूडोकोड, आप ढेर, जहां हर स्टैक फ्रेम चर आकार के एक डेटा संरचना आप व्यक्त कर सकते हैं कि "पैक ढेर फ्रेम की एक सरणी" कह सकते हैं की तरह:

template struct stackframe<N> { 
    uintptr_t contents[N]; 
#ifndef OMIT_FRAME_POINTER 
    struct stackframe<> *nextfp; 
#endif 
    void *retaddr; 
}; 

समस्या यह है कि हर है फ़ंक्शन में एक अलग <N> है - फ्रेम आकार अलग-अलग होते हैं।

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

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

यदि आपके पास न तो फ्रेम आकार की जानकारी और न ही फ्रेमपॉइंटर्स हैं, लेकिन फिर भी एक प्रतीक तालिका है, तो आप वास्तविक बाइनरी से फ्रेममेज़ की गणना करने के लिए कुछ रिवर्स इंजीनियरिंग द्वारा बैकट्रैकिंग भी कर सकते हैं। प्रोग्राम काउंटर से शुरू करें, उस प्रतीक को देखें जो यह प्रतीक तालिका में है, और उसके बाद फ़ंक्शन को शुरुआत से अलग करें। फ़ंक्शन की शुरुआत और प्रोग्राम काउंटर के बीच सभी परिचालनों को अलग करें जो वास्तव में स्टैकपोइंटर को संशोधित करते हैं (स्टैक पर कुछ भी लिखें और/या स्टैक स्पेस आवंटित करें)। यह वर्तमान फ़ंक्शन के लिए फ्रेम आकार की गणना करता है, इसलिए स्टैकपोइंटर से घटाएं, और आपको (अधिकांश आर्किटेक्चर पर) फ़ंक्शन दर्ज करने से पहले स्टैक पर लिखे गए अंतिम शब्द को ढूंढना चाहिए - जो आम तौर पर कॉलर में वापसी का पता होता है। जरूरी के रूप में पुनः पुनरावृत्त करें।

अंत में, आप स्टैक की सामग्री का एक उदारवादी विश्लेषण कर सकते हैं - स्टैक में सभी शब्दों को अलग करें जो प्रक्रिया पता स्थान के निष्पादन-मैप किए गए सेगमेंट के भीतर हैं (और इस प्रकार ऑफसेट्स उर्फ ​​रिटर्न पतों का कार्य हो सकता है), और एक गेम-गेम खेलें जो स्मृति को देख रहा है, वहां निर्देश को अलग करता है और देखता है कि वास्तव में यह किस तरह का कॉल निर्देश है, अगर ऐसा है तो क्या इसे वास्तव में 'अगली' कहा जाता है और यदि आप उस से एक निर्बाध कॉल अनुक्रम बना सकते हैं। यह एक डिग्री तक काम करता है भले ही बाइनरी पूरी तरह से छीन ली गई हो (हालांकि आप उस मामले में जो भी प्राप्त कर सकते हैं वह वापसी पते की एक सूची है)। मुझे नहीं लगता कि जीडीबी इस तकनीक को नियुक्त करता है, लेकिन कुछ एम्बेडेड लोलेवल डिबगर्स करते हैं। X86 पर, अलग-अलग निर्देश की लंबाई के कारण, यह करना बहुत मुश्किल है क्योंकि आप आसानी से निर्देश धारा के माध्यम से "पीछे हटना" नहीं कर सकते हैं, लेकिन आरआईएससी पर, जहां निर्देश की लंबाई तय की जाती है, उदा। एआरएम पर, यह बहुत आसान है।

ऐसे कुछ छेद हैं जो इन एल्गोरिदम के सरल या जटिल/पूर्ण कार्यान्वयन को कभी-कभी गिरते हैं, जैसे कि पूंछ-पुनरावर्ती कार्य, इनलाइन कोड, और इसी तरह।gdb sourcecode आप कुछ और अधिक विचार दे सकता है:

http://sourceware.org/cgi-bin/cvsweb.cgi/src/gdb/frame.c?rev=1.287&content-type=text/x-cvsweb-markup&cvsroot=src

GDB इस तरह के विभिन्न तकनीकों का काम करते हैं।

+0

मुझे यह कहना चाहिए कि इसका कुछ भी विशेष रूप से C++ के लिए विशिष्ट नहीं है। अपवाद स्टैक्स को अनदेखा करते समय सी ++ विनिर्देश खेलते हैं, लेकिन यह कुछ ऐसा नहीं है जो डीबगर करता है (यदि आप 'थ्रो()' में दुर्घटनाग्रस्त हो जाते हैं, तो आपने अभी तक अपवाद स्टैक को अवांछित नहीं किया है, और यदि आप 'कैच {}' में क्रैश करते हैं तो आपने ऐसा किया है; यदि आप अनचाहे की प्रक्रिया में दुर्घटनाग्रस्त हो जाते हैं तो आपके पास एक कंपाइलर बग है ... और उनको डिबग करना वास्तविक देवताओं को छोड़ दिया जाता है ...)। –

+0

मुझे अभी एक और स्रोत मिला है जो बताता है कि .eh_frame (सी ++ के लिए विशिष्ट) का उपयोग जीडीबी में स्टैक को रिवाइंड करने के लिए किया जा सकता है: http://sourceware.org/gdb/papers/unwind.html –