सी में स्टैक ट्रेस कैसे पकड़ सकता है?


83

मुझे पता है कि ऐसा करने के लिए कोई मानक C फ़ंक्शन नहीं है। मैं सोच रहा था कि विंडोज और * निक्स पर इसको लेकर क्या तकनीकें हैं? (विंडोज़ XP अभी ऐसा करने के लिए मेरा सबसे महत्वपूर्ण ओएस है।)


मैंने कई तरीकों का विस्तार से परीक्षण किया है: stackoverflow.com/questions/3899870/print-call-stack-in-c-or-c/…
Ciro Santilli 郝海东 in in 事件 法轮功

जवाबों:


81

glibc बैकट्रेस () फ़ंक्शन प्रदान करता है।

http://www.gnu.org/software/libc/manual/html_node/Backtraces.html


6
glibc FTW ... फिर से। (यह अभी तक एक और कारण है कि मैं glibc को पूर्ण सोने के मानक के रूप में मानता हूं, जब यह C प्रोग्रामिंग (और इसके साथ जाने वाले कंपाइलर) की बात आती है।)
ट्रेवर बॉयड स्मिथ

4
लेकिन रुको वहाँ अधिक है! बैकट्रेस () फ़ंक्शन केवल कॉलस्टैक फ़ंक्शन का प्रतिनिधित्व करने वाले शून्य * पॉइंटर्स की एक सरणी प्रदान करता है। "यह बहुत उपयोगी नहीं है। arg।" डर नहीं! glibc एक ऐसा फ़ंक्शन प्रदान करता है जो सभी शून्य * पतों (कॉलस्टैक फ़ंक्शन पतों) को मानव पठनीय स्ट्रिंग प्रतीकों में परिवर्तित करता है। char ** backtrace_symbols (void *const *buffer, int size)
ट्रेवर बोयड स्मिथ

1
कैविएट: केवल सी फ़ंक्शन के लिए काम करता है, मुझे लगता है। @ ट्रेवर: यह केवल ELF तालिका में पते द्वारा जिम देख रहा है।
कॉनरैड मेयर

2
ऐसा भी है void backtrace_symbols_fd(void *const *buffer, int size, int fd)जो उदाहरण के लिए आउटपुट को सीधे stdout / इरेट पर भेज सकता है।
wcp 24'12

2
backtrace_symbols()बेकार है। इसे सभी प्रतीकों को निर्यात करने की आवश्यकता है और यह DWARF (डीबगिंग) प्रतीकों का समर्थन नहीं करता है। कई (अधिकांश) मामलों में libbacktrace एक बेहतर विकल्प है।
एरवान लेग्रैंड

29

बैकट्रेस (), और बैकट्रेस_सिमबोल () हैं:

आदमी पृष्ठ से:

#include <execinfo.h>
#include <stdio.h>
...
void* callstack[128];
int i, frames = backtrace(callstack, 128);
char** strs = backtrace_symbols(callstack, frames);
for (i = 0; i < frames; ++i) {
    printf("%s\n", strs[i]);
}
free(strs);
...

एक और अधिक सुविधाजनक / ओओपी तरीके से इसका उपयोग करने का एक तरीका एक अपवाद वर्ग निर्माता में बैकट्रेस_सिमबोल () के परिणाम को बचाने के लिए है। इस प्रकार, जब भी आप उस प्रकार के अपवाद को फेंकते हैं तो आपके पास स्टैक ट्रेस होता है। फिर, इसे प्रिंट करने के लिए एक फ़ंक्शन प्रदान करें। उदाहरण के लिए:

class MyException : public std::exception {

    char ** strs;
    MyException( const std::string & message ) {
         int i, frames = backtrace(callstack, 128);
         strs = backtrace_symbols(callstack, frames);
    }

    void printStackTrace() {
        for (i = 0; i < frames; ++i) {
            printf("%s\n", strs[i]);
        }
        free(strs);
    }
};

...

try {
   throw MyException("Oops!");
} catch ( MyException e ) {
    e.printStackTrace();
}

ता दा!

नोट: अनुकूलन झंडे को सक्षम करने से परिणामी स्टैक ट्रेस गलत हो सकता है। आदर्श रूप से, कोई इस क्षमता का उपयोग डिबग फ्लैग ऑन और ऑप्टिमाइज़ेशन फ़्लैग के साथ करेगा।


@shcc केवल एक प्रतीक स्ट्रिंग में पते को परिवर्तित करने के लिए, जिसे बाहरी उपकरणों का उपयोग करके किया जा सकता है, यदि आवश्यक हो।
वुड्रो बारलो

22

विंडोज के लिए StackWalk64 () एपीआई (32 बिट विंडोज पर भी) की जांच करें। UNIX के लिए आपको OS 'के मूल तरीके का उपयोग करना चाहिए, या ग्लिबेक के बैकट्रेस () में, यदि उपलब्ध हो, तो कमबैक करना चाहिए।

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

ज्यादातर समय लोग एक असाधारण परिस्थिति में, एक असाधारण परिस्थिति, जैसे कि एक अपवाद पकड़े जाने पर, एक जोरदार असफलता या - उन सभी में सबसे खराब और सबसे गलत - जब आप एक घातक "अपवाद" या संकेत प्राप्त करते हैं, जैसे एक की कोशिश करते हैं विभाजन का उल्लंघन।

अंतिम मुद्दे पर विचार करते हुए, अधिकांश एपीआई को आपको स्पष्ट रूप से मेमोरी आवंटित करने या आंतरिक रूप से करने की आवश्यकता होगी। नाजुक स्थिति में ऐसा करना जिसमें आपका कार्यक्रम वर्तमान में हो सकता है, अकस्मात चीजों को और भी बदतर बना सकता है। उदाहरण के लिए, क्रैश रिपोर्ट (या coredump) समस्या के वास्तविक कारण को नहीं दर्शाएगी, लेकिन इसे संभालने का आपका असफल प्रयास)।

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


2
आप सिग्नल या अपवाद हैंडलर में मेमोरी आवंटन का प्रयास करने के लिए इसके बारे में नाजुक हैं। एक संभावित तरीका यह है कि कार्यक्रम की शुरुआत में एक निश्चित मात्रा में "आपातकालीन" स्थान आवंटित किया जाए, या एक स्थिर बफर का उपयोग किया जाए।
j_random_hacker 12

एक और तरीका है एक coredump सेवा बना रहा है, जो स्वतंत्र रूप से चलता है
Kobor42

5

आपको खोलना पुस्तकालय का उपयोग करना चाहिए ।

unw_cursor_t cursor; unw_context_t uc;
unw_word_t ip, sp;
unw_getcontext(&uc);
unw_init_local(&cursor, &uc);
unsigned long a[100];
int ctr = 0;

while (unw_step(&cursor) > 0) {
  unw_get_reg(&cursor, UNW_REG_IP, &ip);
  unw_get_reg(&cursor, UNW_REG_SP, &sp);
  if (ctr >= 10) break;
  a[ctr++] = ip;
}

जब तक आप साझा लाइब्रेरी से कॉल नहीं करते हैं, तब तक आपका दृष्टिकोण भी ठीक रहेगा।

आप addr2lineसंबंधित पीसी के स्रोत फ़ंक्शन / लाइन नंबर प्राप्त करने के लिए लिनक्स पर कमांड का उपयोग कर सकते हैं ।


"स्रोत फ़ंक्शन / लाइन नंबर"? क्या होगा यदि लिंकिंग कम कोड आकार के लिए अनुकूलित है? मैं कहूंगा, हालांकि, यह एक उपयोगी परियोजना की तरह लग रहा है। अफ़सोस कि वहाँ रजिस्टर पाने के लिए कोई रास्ता नहीं है। मैं निश्चित रूप से इस पर गौर करूंगा। क्या आप जानते हैं कि यह बिल्कुल स्वतंत्र प्रोसेसर है? बस कुछ भी है कि एक सी संकलक है पर काम करता है?
मावग का कहना है कि मोनिका

1
ठीक है, यह टिप्पणी इसके लायक थी, यदि केवल सहायक एड्रलाइन कमांड के उल्लेख के कारण!
Ogre Psalm33

addr2line ASLR (यानी पिछले एक दशक के दौरान लोगों द्वारा उपयोग किए जाने वाले अधिकांश) के साथ सिस्टम पर रिलोकेबल कोड के लिए विफल रहता है।
एरवान लेग्रैंड

4

इसे करने के लिए कोई स्वतंत्र मंच नहीं है।

निकटतम चीज़ जो आप कर सकते हैं वह है बिना अनुकूलन के कोड को चलाना। इस तरह आप प्रक्रिया से जुड़ सकते हैं (दृश्य c ++ डिबगर या GDB का उपयोग करके) और एक प्रयोग करने योग्य स्टैक ट्रेस प्राप्त करें।


जब क्षेत्र में एम्बेडेड कंप्यूटर पर कोई दुर्घटना होती है, तो इससे मुझे मदद नहीं मिलती है। :(
केविन

@ केविन: एम्बेडेड मशीनों पर भी, आमतौर पर एक दूरस्थ डिबगर स्टब या कम से कम एक कोर डंप प्राप्त करने का एक तरीका है। शायद एक बार यह क्षेत्र में तैनात नहीं है, हालांकि ...
भावुक

यदि आप अपनी पसंद की खिड़कियों / लाइनक्स / मैक के मंच पर gcc-glibc का उपयोग करते हैं ... तो backtrace () और backtrace_symbols () सभी तीन प्लेटफार्मों पर काम करेंगे। उस कथन को देखते हुए, मैं "यह करने के लिए कोई [पोर्टेबल] तरीका नहीं है" शब्दों का उपयोग करेगा।
ट्रेवर बॉयड स्मिथ

4

विंडोज के लिए, CaptureStackBackTrace()यह भी एक विकल्प है, जिसके लिए उपयोगकर्ता की समाप्ति पर कम तैयारी कोड की आवश्यकता होती StackWalk64()है। (इसके अलावा, इसी तरह के परिदृश्य के लिए, मैं CaptureStackBackTrace()बेहतर (अधिक मज़बूती से) काम कर रहा था StackWalk64()।)


2

सोलारिस में pstack कमांड है, जिसे लिनक्स में भी कॉपी किया गया था।


1
उपयोगी है, लेकिन वास्तव में सी नहीं है (यह एक बाहरी उपयोगिता है)।
भावार्थ

1
यह भी, विवरण (अनुभाग: प्रतिबंध) से ": pstack वर्तमान में केवल लिनक्स पर काम करता है, केवल एक x86 मशीन पर 32 बिट ELF बायनेरिज़ (64 बिट समर्थित नहीं) चल रहा है"
Ciro कोस्टा

0

आप स्टैक को पीछे की तरफ करके कर सकते हैं। वास्तविकता में, हालांकि, प्रत्येक फ़ंक्शन की शुरुआत में एक कॉल स्टैक पर पहचानकर्ता को जोड़ना और अंत में इसे पॉप करना आसान होता है, फिर बस उस प्रिंटिंग सामग्री को चलाएं। यह PITA का एक सा है, लेकिन यह अच्छी तरह से काम करता है और अंत में आपका समय बचाएगा।


1
क्या आप "आगे की ओर स्टैक घूमना" समझा सकते हैं?
स्पाइडी

@Spidey एम्बेडेड सिस्टम पर, कभी-कभी यह सब आपके पास होता है - मुझे लगता है कि यह नीचे वोट दिया गया था क्योंकि प्लेटफॉर्म WinXP है। लेकिन बिना किसी परिवाद के जो स्टैक वॉकिंग का समर्थन करता है, मूल रूप से आपको स्टैक को "चलना" पड़ता है। आप वर्तमान बेस पॉइंटर से शुरू करते हैं (x86 पर यह RBP reg की सामग्री है)। यह आपको 1 के साथ स्टैक पर इंगित करने के लिए लेता है। सहेजे गए पिछले RBP (कि स्टैक वॉक को कैसे रखा जाता है), और 2. कॉल / ब्रांच रिटर्न एड्रेस (कॉलिंग फंक्शन की सेव आरआईपी रेज), जो आपको बताता है कि फ़ंक्शन क्या था। फिर, यदि आपके पास प्रतीक तालिका तक पहुंच है, तो आप फ़ंक्शन का पता देख सकते हैं।
टेड मिडलटन

0

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

Libbacktrace शुरू में GCC वितरण का हिस्सा था, लेकिन अब इसे बीएसडी लाइसेंस के तहत लेखक द्वारा स्टैंडअलोन लाइब्रेरी के रूप में उपलब्ध कराया गया है:

https://github.com/ianlancetaylor/libbacktrace

लेखन के समय, मैं किसी और चीज का उपयोग नहीं करूंगा जब तक कि मुझे एक मंच पर बैकट्रैक उत्पन्न करने की आवश्यकता नहीं है जो कि libbacktrace द्वारा समर्थित नहीं है।

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