बैकट्रेस () / backtrace_symbols () फ़ंक्शन नाम कैसे प्रिंट करें?


90

लिनक्स विशिष्ट backtrace()और backtrace_symbols()आपको प्रोग्राम के कॉल ट्रेस का उत्पादन करने की अनुमति देता है। हालाँकि, यह केवल फ़ंक्शन पते को प्रिंट करता है, न कि मेरे प्रोग्राम के लिए उनके नाम। मैं उन्हें फ़ंक्शन नाम कैसे प्रिंट कर सकता हूं? मैंने कार्यक्रम को और -gसाथ ही संकलित करने की कोशिश की है -ggdb। नीचे दिए गए परीक्षण का मामला यह बताता है:

    BACKTRACE ------------
    ./a.out () [0x8048616]
    ./a.out () [0x8048623]
    /lib/libc.so.6(__libc_start_main+0xf3) [0x4a937413]
    ./a.out () [0x8048421]
    ----------------------
    

मैं पहले 2 आइटम भी फ़ंक्शन नाम दिखाना चाहते हैं, fooऔरmain

कोड:

#include <execinfo.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>

static void full_write(int fd, const char *buf, size_t len)
{
        while (len > 0) {
                ssize_t ret = write(fd, buf, len);

                if ((ret == -1) && (errno != EINTR))
                        break;

                buf += (size_t) ret;
                len -= (size_t) ret;
        }
}

void print_backtrace(void)
{
        static const char start[] = "BACKTRACE ------------\n";
        static const char end[] = "----------------------\n";

        void *bt[1024];
        int bt_size;
        char **bt_syms;
        int i;

        bt_size = backtrace(bt, 1024);
        bt_syms = backtrace_symbols(bt, bt_size);
        full_write(STDERR_FILENO, start, strlen(start));
        for (i = 1; i < bt_size; i++) {
                size_t len = strlen(bt_syms[i]);
                full_write(STDERR_FILENO, bt_syms[i], len);
                full_write(STDERR_FILENO, "\n", 1);
        }
        full_write(STDERR_FILENO, end, strlen(end));
    free(bt_syms);
}
void foo()
{
    print_backtrace();
}

int main()
{
    foo();
    return 0;
}


btw, फ़ंक्शन backtrace_symbols_fdउसी ऑपरेशन को करता है backtrace_symbols(), लेकिन परिणामी तार तुरंत फ़ाइल डिस्क्रिप्टर को लिखा जाता है fd
नन्हिया

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

जवाबों:


63

प्रतीकों को गतिशील प्रतीक तालिका से लिया जाता है; आपको -rdynamicविकल्प की आवश्यकता है gcc, जो इसे एक झंडे को लिंकर को पास करता है जो यह सुनिश्चित करता है कि सभी प्रतीकों को तालिका में रखा गया है।

( GCC मैनुअल और / या glibc मैनुअल के बैकट्रैक्स पृष्ठ के लिंक विकल्प पृष्ठ देखें ।)


10
यह स्थिर प्रतीकों के लिए काम नहीं करता है, हालांकि। libunwindवह @ नीमो उल्लेख करता है, स्थिर कार्यों के लिए काम करता है।
नाथन किड

एक सीएमके प्रोजेक्ट में, यह गलती से सेट हो गया था ADD_COMPILE_OPTIONS(-rdynamic)। इसके बजाय, ADD_LINK_OPTIONS(-rdynamic)वांछित व्यवहार के लिए उसका होना या समकक्ष होना आवश्यक है।
स्टीफन

30

स्रोत कोड फ़ाइल नाम + लाइन नंबर के लिए निष्पादन योग्य पते को मैप करने के लिए addr2line कमांड का उपयोग करें । -fफ़ंक्शन नाम के साथ ही विकल्प प्राप्त करने का विकल्प दें ।

वैकल्पिक रूप से, लिबुनविंड की कोशिश करें


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

... और एएसएलआर के साथ स्थानांतरण पहले से कहीं अधिक सामान्य हैं।
एरवान लीग्रैंड

@ErwanLegrand: ASLR से पहले, मैंने सोचा था कि रीलोकेशन पूर्वानुमानित थे और addr2lineसाझा वस्तुओं () में पते के लिए भी मज़बूती से काम किया था। लेकिन हाँ, आधुनिक प्लेटफार्मों पर आपको इस ऑपरेशन को करने के लिए भी रिलेबलटेबल ऑब्जेक्ट का वास्तविक लोड पता जानना होगा? ।
निमो

मैं यह सुनिश्चित करने के लिए नहीं कह सकता कि एएसएलआर से पहले कितना अच्छा काम किया। आजकल, यह केवल "नियमित" निष्पादनयोग्य के अंदर कोड के लिए है और DSOs और PIEs (स्थिति स्वतंत्र निष्पादनकर्ता) के अंदर कोड के लिए विफल है। सौभाग्य से, libunwind इन सभी के लिए काम करता दिखाई देता है।
एरवन लीग्रैंड

मैं इस चर्चा को भूल गया था। Libbacktrace, जिसे मैं उस समय नहीं जानता था, एक बेहतर विकल्प है। (मेरा नया उत्तर देखें।)
एरवान लेग्रैंड

11

इयान लांस टेलर द्वारा उत्कृष्ट लिबबैकट्रेस इस मुद्दे को हल करता है। यह स्टैक अनइंडिंग को हैंडल करता है और साधारण ELF प्रतीकों और DWARF डिबगिंग प्रतीकों दोनों का समर्थन करता है।

Libbacktrace को सभी प्रतीकों के निर्यात की आवश्यकता नहीं है, जो बदसूरत होगा, और ASLR इसे नहीं तोड़ता है।

लिबबैकट्रेस मूल रूप से जीसीसी वितरण का हिस्सा था। अब, एक स्टैंडअलोन संस्करण Github पर पाया जा सकता है:

https://github.com/ianlancetaylor/libbacktrace


2

शीर्ष पर दिए गए उत्तर में एक बग है, यदि ret == -1 और अर्नोइन है, तो आपको फिर से प्रयास करना चाहिए, लेकिन प्रतिलिपि को प्रतिलिपि के रूप में नहीं गिना जाना चाहिए (यदि आप इसे कठिन नहीं पसंद करते हैं, तो इसके लिए केवल एक खाता नहीं बनाना चाहिए)

static void full_write(int fd, const char *buf, size_t len)
{
        while (len > 0) {
                ssize_t ret = write(fd, buf, len);

                if ((ret == -1) {
                        if (errno != EINTR))
                                break;
                        //else
                        continue;
                }
                buf += (size_t) ret;
                len -= (size_t) ret;
        }
}

0

बैकस्ट्रेस को बूस्ट करें

बहुत सुविधाजनक है क्योंकि यह दोनों को प्रिंट करता है:

  • अनमैन्डल C ++ फ़ंक्शन नाम
  • पंक्ति संख्याएँ

आपके लिए स्वचालित रूप से।

उपयोग सारांश:

#define BOOST_STACKTRACE_USE_ADDR2LINE
#include <boost/stacktrace.hpp>

std::cout << boost::stacktrace::stacktrace() << std::endl;

मैंने इसके लिए और कई अन्य तरीकों पर एक न्यूनतम रननेबल उदाहरण प्रदान किया है: C या C ++ में प्रिंट कॉल स्टैक


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