C या C ++ में प्रिंट कॉल स्टैक


120

क्या हर बार एक निश्चित फ़ंक्शन कहे जाने वाले C या C ++ में चल रही प्रक्रिया में कॉल स्टैक को डंप करने का कोई तरीका है? मेरे मन में कुछ इस तरह है:

void foo()
{
   print_stack_trace();

   // foo's body

   return
}

जहां पर्ल में print_stack_traceइसी तरह काम करता है caller

या इस तरह का कुछ:

int main (void)
{
    // will print out debug info every time foo() is called
    register_stack_trace_function(foo); 

    // etc...
}

जहां register_stack_trace_functionकिसी प्रकार का आंतरिक ब्रेकपॉइंट लगाया जाता है जिससे जब भी fooकहा जाता है एक स्टैक ट्रेस को प्रिंट किया जा सकेगा ।

क्या कुछ मानक सी लाइब्रेरी में ऐसा कुछ मौजूद है?

मैं जीसीसी का उपयोग करके लिनक्स पर काम कर रहा हूं।


पृष्ठभूमि

मेरे पास एक परीक्षण रन है जो कुछ कमांडलाइन स्विच के आधार पर भिन्न व्यवहार करता है जो इस व्यवहार को प्रभावित नहीं करना चाहिए। मेरे कोड में एक छद्म यादृच्छिक संख्या जनरेटर है जो मुझे लगता है कि इन स्विचों के आधार पर अलग-अलग कहा जा रहा है। मैं स्विच के प्रत्येक सेट के साथ परीक्षण चलाने में सक्षम होना चाहता हूं और देखें कि क्या यादृच्छिक संख्या जनरेटर को प्रत्येक के लिए अलग-अलग कहा जाता है।


1
@Armen, क्या आप इनमें से किसी से परिचित हैं?
नाथन फेलमैन

1
@ नथन: अगर आपका डिबगर gdb है तो यह उस केस को हैंडल कर सकता है । मैं आपको दूसरों के बारे में नहीं बता सकता, लेकिन मुझे लगता है कि जीडीबी इस कार्यक्षमता में अकेला नहीं है। एक तरफ: मैं सिर्फ देखा मेरी पहले टिप्पणी पर। :: गग :: s/easier/either/नर्क कैसे हुआ?
dmckee --- पूर्व-मध्यस्थ ने बिल्ली का बच्चा

2
@ मार्की: वास्तव में, यह होना चाहिए s/either/easier। मुझे gdb के साथ क्या करने की आवश्यकता होगी, एक स्क्रिप्ट लिखी जाती है जो उस फ़ंक्शन पर टूट जाती है और स्टैक ट्रेस को प्रिंट करती है, फिर जारी रहती है। अब जब मैं इसके बारे में सोचता हूं, तो शायद यह समय मेरे लिए जीडीबी स्क्रिप्टिंग के बारे में जानने का है।
नाथन फेलमैन

1
गाह! कुछ नींद लेने के लिए जा रहे हैं। अब जल्द ही असली ...
dmckee --- पूर्व-मध्यस्थ ने

जवाबों:


79

लाइनक्स-ओनली सॉल्यूशन के लिए आप बैकट्रेस (3) का उपयोग कर सकते हैं जो केवल एक सरणी देता है void *(वास्तव में इनमें से प्रत्येक स्टैक फ्रेम से रिटर्न एड्रेस को इंगित करता है)। उपयोग की किसी चीज़ में इनका अनुवाद करने के लिए, backtrace_symbols (3) है

बैकट्रेस में नोट्स अनुभाग पर ध्यान दें (3) :

विशेष लिंकर विकल्पों के उपयोग के बिना प्रतीक नाम अनुपलब्ध हो सकते हैं। जीएनयू लिंकर का उपयोग करने वाली प्रणालियों के लिए, -rdynamic लिंकर विकल्प का उपयोग करना आवश्यक है। ध्यान दें कि "स्थिर" कार्यों के नाम उजागर नहीं किए गए हैं, और बैकट्रेस में उपलब्ध नहीं होंगे।


10
FWIW, मैक ओएस एक्स पर यह कार्यक्षमता भी मौजूद है: developer.apple.com/library/mac/#documentation/Darwin/Reference/…
एमरीबर्गर

8
विंडोज है CaptureStackBackTrace
bobobobo


लिनक्स पर glibc, दुर्भाग्य से, backtrace_symbolsफ़ंक्शन फ़ंक्शन नाम, स्रोत फ़ाइल नाम और लाइन नंबर प्रदान नहीं करते हैं।
मैक्सिम इगोरुशिन

उपयोग करने के अलावा -rdynamic, यह भी जांच लें कि आपका निर्माण सिस्टम -fvisibility=hiddenविकल्प नहीं जोड़ता है! (जैसा कि यह पूरी तरह से प्रभाव को समाप्त कर देगा -rdynamic)
दीमा लितविनोव

37

बूस्ट स्टट्रेस

यहाँ पर प्रलेखित: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_started.html#stacktrace.getting_started.how_to_print_current_call_stack

यह सबसे सुविधाजनक विकल्प है जो मैंने अब तक देखा है, क्योंकि यह है:

  • वास्तव में लाइन नंबर का प्रिंट आउट ले सकते हैं।

    यह सिर्फ कॉल करता है addr2lineहालांकि , जो बदसूरत है और धीमा हो सकता है यदि आपके बहुत सारे निशान ले रहे हैं।

  • डिफ़ॉल्ट रूप से demangles

  • बूस्ट केवल हेडर है, इसलिए आपके बिल्ड सिस्टम को संशोधित करने की कोई आवश्यकता नहीं है

boost_stacktrace.cpp

#include <iostream>

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

void my_func_2(void) {
    std::cout << boost::stacktrace::stacktrace() << std::endl;
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main(int argc, char **argv) {
    long long unsigned int n;
    if (argc > 1) {
        n = strtoul(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    for (long long unsigned int i = 0; i < n; ++i) {
        my_func_1(1);   // line 28
        my_func_1(2.0); // line 29
    }
}

दुर्भाग्य से, यह एक और हाल ही में जोड़ प्रतीत होता है, और पैकेज libboost-stacktrace-devउबंटू 16.04 में मौजूद नहीं है, केवल 18.04:

sudo apt-get install libboost-stacktrace-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o boost_stacktrace.out -std=c++11 \
  -Wall -Wextra -pedantic-errors boost_stacktrace.cpp -ldl
./boost_stacktrace.out

हमें -ldlअंत में जोड़ना होगा अन्यथा संकलन विफल हो जाता है।

आउटपुट:

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
 1# my_func_1(int) at /home/ciro/test/boost_stacktrace.cpp:18
 2# main at /home/ciro/test/boost_stacktrace.cpp:29 (discriminator 2)
 3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 4# _start in ./boost_stacktrace.out

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::basic_stacktrace() at /usr/include/boost/stacktrace/stacktrace.hpp:129
 1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:13
 2# main at /home/ciro/test/boost_stacktrace.cpp:27 (discriminator 2)
 3# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 4# _start in ./boost_stacktrace.out

आउटपुट और नीचे "glibc backtrace" अनुभाग पर आगे समझाया गया है, जो कि अनुरूप है।

ध्यान दें कि कैसे my_func_1(int)और my_func_1(float), जो फ़ंक्शन अधिभार के कारण खराब हो गए हैं, हमारे लिए अच्छी तरह से ध्वस्त हो गए थे।

ध्यान दें कि पहली intकॉल एक लाइन से बंद है (27 के बजाय 28) और दूसरी एक दो लाइनों (29 के बजाय 27) से बंद है। टिप्पणियों में यह सुझाव दिया गया था कि ऐसा इसलिए है क्योंकि निम्नलिखित निर्देश पते पर विचार किया जा रहा है, जो 27 28 बन जाते हैं, और 29 लूप बंद हो जाते हैं और 27 बन जाते हैं।

हम तब देखते हैं कि -O3उत्पादन पूरी तरह से विकृत है:

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
 1# my_func_1(double) at /home/ciro/test/boost_stacktrace.cpp:12
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in ./boost_stacktrace.out

 0# boost::stacktrace::basic_stacktrace<std::allocator<boost::stacktrace::frame> >::size() const at /usr/include/boost/stacktrace/stacktrace.hpp:215
 1# main at /home/ciro/test/boost_stacktrace.cpp:31
 2# __libc_start_main in /lib/x86_64-linux-gnu/libc.so.6
 3# _start in ./boost_stacktrace.out

Backtraces सामान्य रूप से अनुकूलन द्वारा उत्परिवर्तित सामान्य रूप से होते हैं। टेल कॉल ऑप्टिमाइज़ेशन इसका एक उल्लेखनीय उदाहरण है: टेल कॉल ऑप्टिमाइज़ेशन क्या है?

बेंचमार्क चालू -O3:

time  ./boost_stacktrace.out 1000 >/dev/null

आउटपुट:

real    0m43.573s
user    0m30.799s
sys     0m13.665s

इसलिए जैसा कि अपेक्षित था, हम देखते हैं कि यह विधि बाहरी कॉल के लिए बेहद धीमी संभावना है addr2line, और केवल तभी संभव होगा जब सीमित संख्या में कॉल किए जा रहे हों।

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

उबंटू 19.10, जीसीसी 9.2.1 पर परीक्षण किया गया, 1.67.0 को बढ़ावा दिया।

glibc backtrace

यहाँ पर प्रलेखित: https://www.gnu.org/software/libc/manual/html_node/Backtracesces.html

main.c

#include <stdio.h>
#include <stdlib.h>

/* Paste this on the file you want to debug. */
#include <stdio.h>
#include <execinfo.h>
void print_trace(void) {
    char **strings;
    size_t i, size;
    enum Constexpr { MAX_SIZE = 1024 };
    void *array[MAX_SIZE];
    size = backtrace(array, MAX_SIZE);
    strings = backtrace_symbols(array, size);
    for (i = 0; i < size; i++)
        printf("%s\n", strings[i]);
    puts("");
    free(strings);
}

void my_func_3(void) {
    print_trace();
}

void my_func_2(void) {
    my_func_3();
}

void my_func_1(void) {
    my_func_3();
}

int main(void) {
    my_func_1(); /* line 33 */
    my_func_2(); /* line 34 */
    return 0;
}

संकलित करें:

gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -rdynamic -std=c99 \
  -Wall -Wextra -pedantic-errors main.c

-rdynamic प्रमुख आवश्यक विकल्प है।

Daud:

./main.out

आउटपुट:

./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0x9) [0x4008f9]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]

./main.out(print_trace+0x2d) [0x400a3d]
./main.out(main+0xe) [0x4008fe]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f35a5aad830]
./main.out(_start+0x29) [0x400939]

इसलिए हम तुरंत देखते हैं कि एक इनलाइनिंग ऑप्टिमाइज़ेशन हुआ, और कुछ फ़ंक्शन ट्रेस से खो गए।

यदि हम पते प्राप्त करने का प्रयास करते हैं:

addr2line -e main.out 0x4008f9 0x4008fe

हमने प्राप्त किया:

/home/ciro/main.c:21
/home/ciro/main.c:36

जो पूरी तरह से बंद है।

यदि हम -O0इसके बजाय ऐसा करते हैं , ./main.outतो सही पूर्ण ट्रेस देता है:

./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_1+0x9) [0x400a68]
./main.out(main+0x9) [0x400a74]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]

./main.out(print_trace+0x2e) [0x4009a4]
./main.out(my_func_3+0x9) [0x400a50]
./main.out(my_func_2+0x9) [0x400a5c]
./main.out(main+0xe) [0x400a79]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xf0) [0x7f4711677830]
./main.out(_start+0x29) [0x4008a9]

और फिर:

addr2line -e main.out 0x400a74 0x400a79

देता है:

/home/cirsan01/test/main.c:34
/home/cirsan01/test/main.c:35

इसलिए लाइनें सिर्फ एक ही हैं, TODO क्यों? लेकिन यह अभी भी प्रयोग करने योग्य हो सकता है।

निष्कर्ष: backtraces केवल संभवतः पूरी तरह से दिखा सकते हैं -O0। अनुकूलन के साथ, संकलित कोड में मूल बैकट्रेस को मौलिक रूप से संशोधित किया जाता है।

मुझे इस के साथ C ++ प्रतीकों को स्वचालित रूप से हटाने का एक सरल तरीका नहीं मिला, यहाँ कुछ हैक हैं:

Ubuntu 16.04, GCC 6.4.0, libc 2.23 पर परीक्षण किया गया।

glibc backtrace_symbols_fd

यह सहायक की तुलना में थोड़ा अधिक सुविधाजनक है backtrace_symbols, और मूल रूप से समान आउटपुट का उत्पादन करता है:

/* Paste this on the file you want to debug. */
#include <execinfo.h>
#include <stdio.h>
#include <unistd.h>
void print_trace(void) {
    size_t i, size;
    enum Constexpr { MAX_SIZE = 1024 };
    void *array[MAX_SIZE];
    size = backtrace(array, MAX_SIZE);
    backtrace_symbols_fd(array, size, STDOUT_FILENO);
    puts("");
}

Ubuntu 16.04, GCC 6.4.0, libc 2.23 पर परीक्षण किया गया।

backtraceC ++ के साथ glibc हैक 1: -export-dynamic+ हैकिंगdladdr

से अनुकूलित: https://gist.github.com/fmela/591333/c64f4eb86037bb237862a8283df70cdfc25f01d3

यह एक "हैक" है क्योंकि इसमें ईएलएफ को बदलने की आवश्यकता होती है -export-dynamic

glibc_ldl.cpp

#include <dlfcn.h>     // for dladdr
#include <cxxabi.h>    // for __cxa_demangle

#include <cstdio>
#include <string>
#include <sstream>
#include <iostream>

// This function produces a stack backtrace with demangled function & method names.
std::string backtrace(int skip = 1)
{
    void *callstack[128];
    const int nMaxFrames = sizeof(callstack) / sizeof(callstack[0]);
    char buf[1024];
    int nFrames = backtrace(callstack, nMaxFrames);
    char **symbols = backtrace_symbols(callstack, nFrames);

    std::ostringstream trace_buf;
    for (int i = skip; i < nFrames; i++) {
        Dl_info info;
        if (dladdr(callstack[i], &info)) {
            char *demangled = NULL;
            int status;
            demangled = abi::__cxa_demangle(info.dli_sname, NULL, 0, &status);
            std::snprintf(
                buf,
                sizeof(buf),
                "%-3d %*p %s + %zd\n",
                i,
                (int)(2 + sizeof(void*) * 2),
                callstack[i],
                status == 0 ? demangled : info.dli_sname,
                (char *)callstack[i] - (char *)info.dli_saddr
            );
            free(demangled);
        } else {
            std::snprintf(buf, sizeof(buf), "%-3d %*p\n",
                i, (int)(2 + sizeof(void*) * 2), callstack[i]);
        }
        trace_buf << buf;
        std::snprintf(buf, sizeof(buf), "%s\n", symbols[i]);
        trace_buf << buf;
    }
    free(symbols);
    if (nFrames == nMaxFrames)
        trace_buf << "[truncated]\n";
    return trace_buf.str();
}

void my_func_2(void) {
    std::cout << backtrace() << std::endl;
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

संकलित करें और चलाएं:

g++ -fno-pie -ggdb3 -O0 -no-pie -o glibc_ldl.out -std=c++11 -Wall -Wextra \
  -pedantic-errors -fpic glibc_ldl.cpp -export-dynamic -ldl
./glibc_ldl.out 

उत्पादन:

1             0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2             0x40139e my_func_1(int) + 16
./glibc_ldl.out(_Z9my_func_1i+0x10) [0x40139e]
3             0x4013b3 main + 18
./glibc_ldl.out(main+0x12) [0x4013b3]
4       0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5             0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]

1             0x40130a my_func_2() + 41
./glibc_ldl.out(_Z9my_func_2v+0x29) [0x40130a]
2             0x40138b my_func_1(double) + 18
./glibc_ldl.out(_Z9my_func_1d+0x12) [0x40138b]
3             0x4013c8 main + 39
./glibc_ldl.out(main+0x27) [0x4013c8]
4       0x7f7594552b97 __libc_start_main + 231
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe7) [0x7f7594552b97]
5             0x400f3a _start + 42
./glibc_ldl.out(_start+0x2a) [0x400f3a]

उबंटू 18.04 पर परीक्षण किया गया।

backtraceC ++ के साथ glibc हैक हैक 2: पार्स बैकट्रेस आउटपुट

पर दिखाया गया: https://panthema.net/2008/0901-stacktrace-demangled/

यह एक हैक है क्योंकि इसमें पार्सिंग की आवश्यकता होती है।

TODO इसे संकलन और यहां दिखाने के लिए इसे प्राप्त करता है।

libunwind

TODO को ग्लिबक बैकट्रेस पर कोई लाभ नहीं है? बहुत समान आउटपुट के लिए, बिल्ड कमांड को संशोधित करने की भी आवश्यकता होती है, लेकिन ग्लिबक का हिस्सा नहीं है, इसलिए अतिरिक्त पैकेज की स्थापना की आवश्यकता होती है।

कोड से अनुकूलित: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

main.c

/* This must be on top. */
#define _XOPEN_SOURCE 700

#include <stdio.h>
#include <stdlib.h>

/* Paste this on the file you want to debug. */
#define UNW_LOCAL_ONLY
#include <libunwind.h>
#include <stdio.h>
void print_trace() {
    char sym[256];
    unw_context_t context;
    unw_cursor_t cursor;
    unw_getcontext(&context);
    unw_init_local(&cursor, &context);
    while (unw_step(&cursor) > 0) {
        unw_word_t offset, pc;
        unw_get_reg(&cursor, UNW_REG_IP, &pc);
        if (pc == 0) {
            break;
        }
        printf("0x%lx:", pc);
        if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
            printf(" (%s+0x%lx)\n", sym, offset);
        } else {
            printf(" -- error: unable to obtain symbol name for this frame\n");
        }
    }
    puts("");
}

void my_func_3(void) {
    print_trace();
}

void my_func_2(void) {
    my_func_3();
}

void my_func_1(void) {
    my_func_3();
}

int main(void) {
    my_func_1(); /* line 46 */
    my_func_2(); /* line 47 */
    return 0;
}

संकलित करें और चलाएं:

sudo apt-get install libunwind-dev
gcc -fno-pie -ggdb3 -O3 -no-pie -o main.out -std=c99 \
  -Wall -Wextra -pedantic-errors main.c -lunwind

या तो #define _XOPEN_SOURCE 700शीर्ष पर होना चाहिए, या हमें उपयोग करना चाहिए -std=gnu99:

Daud:

./main.out

आउटपुट:

0x4007db: (main+0xb)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)

0x4007e2: (main+0x12)
0x7f4ff50aa830: (__libc_start_main+0xf0)
0x400819: (_start+0x29)

तथा:

addr2line -e main.out 0x4007db 0x4007e2

देता है:

/home/ciro/main.c:34
/home/ciro/main.c:49

के साथ -O0:

0x4009cf: (my_func_3+0xe)
0x4009e7: (my_func_1+0x9)
0x4009f3: (main+0x9)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)

0x4009cf: (my_func_3+0xe)
0x4009db: (my_func_2+0x9)
0x4009f8: (main+0xe)
0x7f7b84ad7830: (__libc_start_main+0xf0)
0x4007d9: (_start+0x29)

तथा:

addr2line -e main.out 0x4009f3 0x4009f8

देता है:

/home/ciro/main.c:47
/home/ciro/main.c:48

Ubuntu 16.04, GCC 6.4.0, libunwind 1.1 पर परीक्षण किया गया।

C ++ नाम के साथ लिंबिंगविंड डिमांग्लिंग

कोड से अनुकूलित: https://eli.thegreenplace.net/2015/programmatic-access-to-the-call-stack-in-c/

unwind.cpp

#define UNW_LOCAL_ONLY
#include <cxxabi.h>
#include <libunwind.h>
#include <cstdio>
#include <cstdlib>
#include <iostream>

void backtrace() {
  unw_cursor_t cursor;
  unw_context_t context;

  // Initialize cursor to current frame for local unwinding.
  unw_getcontext(&context);
  unw_init_local(&cursor, &context);

  // Unwind frames one by one, going up the frame stack.
  while (unw_step(&cursor) > 0) {
    unw_word_t offset, pc;
    unw_get_reg(&cursor, UNW_REG_IP, &pc);
    if (pc == 0) {
      break;
    }
    std::printf("0x%lx:", pc);

    char sym[256];
    if (unw_get_proc_name(&cursor, sym, sizeof(sym), &offset) == 0) {
      char* nameptr = sym;
      int status;
      char* demangled = abi::__cxa_demangle(sym, nullptr, nullptr, &status);
      if (status == 0) {
        nameptr = demangled;
      }
      std::printf(" (%s+0x%lx)\n", nameptr, offset);
      std::free(demangled);
    } else {
      std::printf(" -- error: unable to obtain symbol name for this frame\n");
    }
  }
}

void my_func_2(void) {
    backtrace();
    std::cout << std::endl; // line 43
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}  // line 54

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

संकलित करें और चलाएं:

sudo apt-get install libunwind-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o unwind.out -std=c++11 \
  -Wall -Wextra -pedantic-errors unwind.cpp -lunwind -pthread
./unwind.out

आउटपुट:

0x400c80: (my_func_2()+0x9)
0x400cb7: (my_func_1(int)+0x10)
0x400ccc: (main+0x12)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)

0x400c80: (my_func_2()+0x9)
0x400ca4: (my_func_1(double)+0x12)
0x400ce1: (main+0x27)
0x7f4c68926b97: (__libc_start_main+0xe7)
0x400a3a: (_start+0x2a)

और फिर हम my_func_2और my_func_1(int)साथ की पंक्तियाँ पा सकते हैं :

addr2line -e unwind.out 0x400c80 0x400cb7

जो देता है:

/home/ciro/test/unwind.cpp:43
/home/ciro/test/unwind.cpp:54

TODO: लाइनें एक-एक करके बंद क्यों होती हैं?

उबंटू 18.04, जीसीसी 7.4.0, लिबुनविंड 1.2.1 पर परीक्षण किया गया।

GDB स्वचालन

हम GDB के साथ भी ऐसा कर सकते हैं बिना उपयोग किए बिना पुन: उपयोग करें : एक विशेष कार्रवाई कैसे करें जब एक निश्चित ब्रेकप्वाइंट GDB में मारा जाता है?

यद्यपि यदि आप बैकट्रेस को बहुत प्रिंट करने जा रहे हैं, तो यह संभवतः अन्य विकल्पों की तुलना में कम तेज़ होगा, लेकिन शायद हम मूल गति के साथ पहुंच सकते हैं compile code, लेकिन मैं अभी इसका परीक्षण करने के लिए आलसी हूं: gdb में असेंबली कैसे कॉल करें?

main.cpp

void my_func_2(void) {}

void my_func_1(double f) {
    my_func_2();
}

void my_func_1(int i) {
    my_func_2();
}

int main() {
    my_func_1(1);
    my_func_1(2.0);
}

main.gdb

start
break my_func_2
commands
  silent
  backtrace
  printf "\n"
  continue
end
continue

संकलित करें और चलाएं:

g++ -ggdb3 -o main.out main.cpp
gdb -nh -batch -x main.gdb main.out

आउटपुट:

Temporary breakpoint 1 at 0x1158: file main.cpp, line 12.

Temporary breakpoint 1, main () at main.cpp:12
12          my_func_1(1);
Breakpoint 2 at 0x555555555129: file main.cpp, line 1.
#0  my_func_2 () at main.cpp:1
#1  0x0000555555555151 in my_func_1 (i=1) at main.cpp:8
#2  0x0000555555555162 in main () at main.cpp:12

#0  my_func_2 () at main.cpp:1
#1  0x000055555555513e in my_func_1 (f=2) at main.cpp:4
#2  0x000055555555516f in main () at main.cpp:13

[Inferior 1 (process 14193) exited normally]

TODO मैं केवल -exकमांड लाइन से ऐसा नहीं करना चाहता था, main.gdbलेकिन मुझे commandsवहां काम करने के लिए नहीं मिला ।

उबंटू 19.04, GDB 8.2 में परीक्षण किया गया।

लिनक्स कर्नेल

लिनक्स कर्नेल के अंदर वर्तमान थ्रेड स्टैक ट्रेस कैसे प्रिंट करें?

libdwfl

इसका मूल रूप से उल्लेख किया गया था: https://stackoverflow.com/a/60713161/895245 और यह सबसे अच्छी विधि हो सकती है, लेकिन मुझे थोड़ा और बेंचमार्क करना होगा, लेकिन कृपया उस उत्तर को बढ़ाएं।

TODO: मैंने उस उत्तर में कोड को कम करने की कोशिश की, जो काम कर रहा था, एक एकल फ़ंक्शन के लिए, लेकिन यह सेगफॉल्टिंग है, मुझे बताएं कि क्या कोई भी ढूंढ सकता है।

dwfl.cpp

#include <cassert>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>

#include <cxxabi.h> // __cxa_demangle
#include <elfutils/libdwfl.h> // Dwfl*
#include <execinfo.h> // backtrace
#include <unistd.h> // getpid

// /programming/281818/unmangling-the-result-of-stdtype-infoname
std::string demangle(const char* name) {
    int status = -4;
    std::unique_ptr<char, void(*)(void*)> res {
        abi::__cxa_demangle(name, NULL, NULL, &status),
        std::free
    };
    return (status==0) ? res.get() : name ;
}

std::string debug_info(Dwfl* dwfl, void* ip) {
    std::string function;
    int line = -1;
    char const* file;
    uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
    Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
    char const* name = dwfl_module_addrname(module, ip2);
    function = name ? demangle(name) : "<unknown>";
    if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
        Dwarf_Addr addr;
        file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
    }
    std::stringstream ss;
    ss << ip << ' ' << function;
    if (file)
        ss << " at " << file << ':' << line;
    ss << std::endl;
    return ss.str();
}

std::string stacktrace() {
    // Initialize Dwfl.
    Dwfl* dwfl = nullptr;
    {
        Dwfl_Callbacks callbacks = {};
        char* debuginfo_path = nullptr;
        callbacks.find_elf = dwfl_linux_proc_find_elf;
        callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
        callbacks.debuginfo_path = &debuginfo_path;
        dwfl = dwfl_begin(&callbacks);
        assert(dwfl);
        int r;
        r = dwfl_linux_proc_report(dwfl, getpid());
        assert(!r);
        r = dwfl_report_end(dwfl, nullptr, nullptr);
        assert(!r);
        static_cast<void>(r);
    }

    // Loop over stack frames.
    std::stringstream ss;
    {
        void* stack[512];
        int stack_size = ::backtrace(stack, sizeof stack / sizeof *stack);
        for (int i = 0; i < stack_size; ++i) {
            ss << i << ": ";

            // Works.
            ss << debug_info(dwfl, stack[i]);

#if 0
            // TODO intended to do the same as above, but segfaults,
            // so possibly UB In above function that does not blow up by chance?
            void *ip = stack[i];
            std::string function;
            int line = -1;
            char const* file;
            uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
            Dwfl_Module* module = dwfl_addrmodule(dwfl, ip2);
            char const* name = dwfl_module_addrname(module, ip2);
            function = name ? demangle(name) : "<unknown>";
            // TODO if I comment out this line it does not blow up anymore.
            if (Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
              Dwarf_Addr addr;
              file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
            }
            ss << ip << ' ' << function;
            if (file)
                ss << " at " << file << ':' << line;
            ss << std::endl;
#endif
        }
    }
    dwfl_end(dwfl);
    return ss.str();
}

void my_func_2() {
    std::cout << stacktrace() << std::endl;
    std::cout.flush();
}

void my_func_1(double f) {
    (void)f;
    my_func_2();
}

void my_func_1(int i) {
    (void)i;
    my_func_2();
}

int main(int argc, char **argv) {
    long long unsigned int n;
    if (argc > 1) {
        n = strtoul(argv[1], NULL, 0);
    } else {
        n = 1;
    }
    for (long long unsigned int i = 0; i < n; ++i) {
        my_func_1(1);
        my_func_1(2.0);
    }
}

संकलित करें और चलाएं:

sudo apt install libdw-dev
g++ -fno-pie -ggdb3 -O0 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw
./dwfl.out

आउटपुट:

0: 0x402b74 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402ce0 my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d7d my_func_1(int) at /home/ciro/test/dwfl.cpp:112
3: 0x402de0 main at /home/ciro/test/dwfl.cpp:123
4: 0x7f7efabbe1e3 __libc_start_main at ../csu/libc-start.c:342
5: 0x40253e _start at ../csu/libc-start.c:-1

0: 0x402b74 stacktrace[abi:cxx11]() at /home/ciro/test/dwfl.cpp:65
1: 0x402ce0 my_func_2() at /home/ciro/test/dwfl.cpp:100
2: 0x402d66 my_func_1(double) at /home/ciro/test/dwfl.cpp:107
3: 0x402df1 main at /home/ciro/test/dwfl.cpp:121
4: 0x7f7efabbe1e3 __libc_start_main at ../csu/libc-start.c:342
5: 0x40253e _start at ../csu/libc-start.c:-1

बेंचमार्क रन:

g++ -fno-pie -ggdb3 -O3 -no-pie -o dwfl.out -std=c++11 -Wall -Wextra -pedantic-errors dwfl.cpp -ldw
time ./dwfl.out 1000 >/dev/null

आउटपुट:

real    0m3.751s
user    0m2.822s
sys     0m0.928s

इसलिए हम देखते हैं कि यह विधि बूस्ट के स्टैकट्रेस की तुलना में 10 गुना तेज है, और इसलिए अधिक उपयोग के मामलों पर लागू हो सकती है।

Ubuntu 19.10 amd64 में परीक्षण किया गया, libdw-dev 0.176-1.1।

यह सभी देखें


1
सभी "TODO: लाइनें एक के बाद एक" हैं क्योंकि लाइन नंबर अगली अभिव्यक्ति की शुरुआत से लिया गया है।
एसएस ऐनी

6

ऐसा करने के लिए कोई मानकीकृत तरीका नहीं है। खिड़कियों के लिए DbgHelp लाइब्रेरी में कार्यक्षमता प्रदान की गई है


6

क्या हर बार एक निश्चित फ़ंक्शन कहे जाने वाले C या C ++ में चल रही प्रक्रिया में कॉल स्टैक को डंप करने का कोई तरीका है?

आप विशिष्ट फ़ंक्शन में रिटर्न स्टेटमेंट के बजाय मैक्रो फ़ंक्शन का उपयोग कर सकते हैं।

उदाहरण के लिए, रिटर्न का उपयोग करने के बजाय,

int foo(...)
{
    if (error happened)
        return -1;

    ... do something ...

    return 0
}

आप एक मैक्रो फ़ंक्शन का उपयोग कर सकते हैं।

#include "c-callstack.h"

int foo(...)
{
    if (error happened)
        NL_RETURN(-1);

    ... do something ...

    NL_RETURN(0);
}

जब भी किसी फ़ंक्शन में कोई त्रुटि होती है, तो आपको नीचे दिखाए गए अनुसार जावा-शैली कॉल स्टैक दिखाई देगा।

Error(code:-1) at : so_topless_ranking_server (sample.c:23)
Error(code:-1) at : nanolat_database (sample.c:31)
Error(code:-1) at : nanolat_message_queue (sample.c:39)
Error(code:-1) at : main (sample.c:47)

पूर्ण स्रोत कोड यहां उपलब्ध है।

c-callstack https://github.com/Nanolat पर


6

एक पुराने धागे का एक और जवाब।

जब मुझे ऐसा करने की आवश्यकता होती है, तो मैं आमतौर पर उपयोग करता हूं system()औरpstack

तो कुछ इस तरह:

#include <sys/types.h>
#include <unistd.h>
#include <string>
#include <sstream>
#include <cstdlib>

void f()
{
    pid_t myPid = getpid();
    std::string pstackCommand = "pstack ";
    std::stringstream ss;
    ss << myPid;
    pstackCommand += ss.str();
    system(pstackCommand.c_str());
}

void g()
{
   f();
}


void h()
{
   g();
}

int main()
{
   h();
}

यह आउटपुट

#0  0x00002aaaab62d61e in waitpid () from /lib64/libc.so.6
#1  0x00002aaaab5bf609 in do_system () from /lib64/libc.so.6
#2  0x0000000000400c3c in f() ()
#3  0x0000000000400cc5 in g() ()
#4  0x0000000000400cd1 in h() ()
#5  0x0000000000400cdd in main ()

यह लिनक्स, FreeBSD और Solaris पर काम करना चाहिए। मुझे नहीं लगता है कि macOS में pstack या एक साधारण समतुल्य है, लेकिन इस धागे का एक विकल्प है

यदि आप उपयोग कर रहे हैं C, तो आपको Cस्ट्रिंग फ़ंक्शन का उपयोग करने की आवश्यकता होगी ।

#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void f()
{
    pid_t myPid = getpid();
    /*
      length of command 7 for 'pstack ', 7 for the PID, 1 for nul
    */
    char pstackCommand[7+7+1];
    sprintf(pstackCommand, "pstack %d", (int)myPid);
    system(pstackCommand);
}

मैंने इस पोस्ट के आधार पर PID में अंकों की अधिकतम संख्या के लिए 7 का उपयोग किया है ।


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

6

लिनक्स विशिष्ट, TLDR:

  1. backtraceमें glibcसही stacktraces केवल जब पैदा करता है -lunwindजुड़ा हुआ है (गैर-दस्तावेजी प्लेटफ़ॉर्म-विशिष्ट सुविधा)।
  2. करने के लिए उत्पादन समारोह नाम , स्रोत फ़ाइल और लाइन नंबर उपयोग #include <elfutils/libdwfl.h>(इस लाइब्रेरी केवल अपने हेडर फाइल में दर्ज है)। backtrace_symbolsऔर backtrace_symbolsd_fdकम से कम जानकारीपूर्ण हैं।

आधुनिक लिनक्स पर आप फ़ंक्शन का उपयोग करके स्टैकट्रेस पते प्राप्त कर सकते हैं backtracebacktraceलोकप्रिय प्लेटफॉर्म्स पर अधिक सटीक पते बनाने के लिए अनजाना तरीका -lunwind( libunwind-devUbuntu 18.04 पर) के साथ लिंक करना है ( नीचे उदाहरण उदाहरण देखें)। backtraceफ़ंक्शन का उपयोग करता है _Unwind_Backtraceऔर डिफ़ॉल्ट रूप से बाद से आता है libgcc_s.so.1और यह कार्यान्वयन सबसे पोर्टेबल है। जब -lunwindजोड़ा जाता है तो यह अधिक सटीक संस्करण प्रदान करता है _Unwind_Backtraceलेकिन यह पुस्तकालय कम पोर्टेबल है (समर्थित आर्किटेक्चर देखें libunwind/src)।

दुर्भाग्य से, साथी backtrace_symbolsdऔरbacktrace_symbols_fd फ़ंक्शन शायद एक दशक से अब स्रोत फ़ाइल नाम और लाइन नंबर के साथ फ़ंक्शन नाम के लिए स्टैकट्रेस पतों को हल करने में सक्षम नहीं हुए हैं (नीचे उदाहरण उदाहरण देखें)।

हालांकि, प्रतीकों के पते को हल करने के लिए एक और तरीका है और यह फ़ंक्शन नाम , स्रोत फ़ाइल और लाइन नंबर के साथ सबसे उपयोगी निशान पैदा करता है । विधि ( उबंटू 18.04 पर) के #include <elfutils/libdwfl.h>साथ है और लिंक करना है ।-ldwlibdw-dev

कार्य C ++ उदाहरण ( test.cc):

#include <stdexcept>
#include <iostream>
#include <cassert>
#include <cstdlib>
#include <string>

#include <boost/core/demangle.hpp>

#include <execinfo.h>
#include <elfutils/libdwfl.h>

struct DebugInfoSession {
    Dwfl_Callbacks callbacks = {};
    char* debuginfo_path = nullptr;
    Dwfl* dwfl = nullptr;

    DebugInfoSession() {
        callbacks.find_elf = dwfl_linux_proc_find_elf;
        callbacks.find_debuginfo = dwfl_standard_find_debuginfo;
        callbacks.debuginfo_path = &debuginfo_path;

        dwfl = dwfl_begin(&callbacks);
        assert(dwfl);

        int r;
        r = dwfl_linux_proc_report(dwfl, getpid());
        assert(!r);
        r = dwfl_report_end(dwfl, nullptr, nullptr);
        assert(!r);
        static_cast<void>(r);
    }

    ~DebugInfoSession() {
        dwfl_end(dwfl);
    }

    DebugInfoSession(DebugInfoSession const&) = delete;
    DebugInfoSession& operator=(DebugInfoSession const&) = delete;
};

struct DebugInfo {
    void* ip;
    std::string function;
    char const* file;
    int line;

    DebugInfo(DebugInfoSession const& dis, void* ip)
        : ip(ip)
        , file()
        , line(-1)
    {
        // Get function name.
        uintptr_t ip2 = reinterpret_cast<uintptr_t>(ip);
        Dwfl_Module* module = dwfl_addrmodule(dis.dwfl, ip2);
        char const* name = dwfl_module_addrname(module, ip2);
        function = name ? boost::core::demangle(name) : "<unknown>";

        // Get source filename and line number.
        if(Dwfl_Line* dwfl_line = dwfl_module_getsrc(module, ip2)) {
            Dwarf_Addr addr;
            file = dwfl_lineinfo(dwfl_line, &addr, &line, nullptr, nullptr, nullptr);
        }
    }
};

std::ostream& operator<<(std::ostream& s, DebugInfo const& di) {
    s << di.ip << ' ' << di.function;
    if(di.file)
        s << " at " << di.file << ':' << di.line;
    return s;
}

void terminate_with_stacktrace() {
    void* stack[512];
    int stack_size = ::backtrace(stack, sizeof stack / sizeof *stack);

    // Print the exception info, if any.
    if(auto ex = std::current_exception()) {
        try {
            std::rethrow_exception(ex);
        }
        catch(std::exception& e) {
            std::cerr << "Fatal exception " << boost::core::demangle(typeid(e).name()) << ": " << e.what() << ".\n";
        }
        catch(...) {
            std::cerr << "Fatal unknown exception.\n";
        }
    }

    DebugInfoSession dis;
    std::cerr << "Stacktrace of " << stack_size << " frames:\n";
    for(int i = 0; i < stack_size; ++i) {
        std::cerr << i << ": " << DebugInfo(dis, stack[i]) << '\n';
    }
    std::cerr.flush();

    std::_Exit(EXIT_FAILURE);
}

int main() {
    std::set_terminate(terminate_with_stacktrace);
    throw std::runtime_error("test exception");
}

Gcc-8.3 के साथ Ubuntu 18.04.4 LTS पर संकलित:

g++ -o test.o -c -m{arch,tune}=native -std=gnu++17 -W{all,extra,error} -g -Og -fstack-protector-all test.cc
g++ -o test -g test.o -ldw -lunwind

आउटपुट:

Fatal exception std::runtime_error: test exception.
Stacktrace of 7 frames:
0: 0x55f3837c1a8c terminate_with_stacktrace() at /home/max/src/test/test.cc:76
1: 0x7fbc1c845ae5 <unknown>
2: 0x7fbc1c845b20 std::terminate()
3: 0x7fbc1c845d53 __cxa_throw
4: 0x55f3837c1a43 main at /home/max/src/test/test.cc:103
5: 0x7fbc1c3e3b96 __libc_start_main at ../csu/libc-start.c:310
6: 0x55f3837c17e9 _start

जब कोई -lunwindलिंक नहीं किया जाता है, तो यह कम सटीक स्टैकट्रेस पैदा करता है:

0: 0x5591dd9d1a4d terminate_with_stacktrace() at /home/max/src/test/test.cc:76
1: 0x7f3c18ad6ae6 <unknown>
2: 0x7f3c18ad6b21 <unknown>
3: 0x7f3c18ad6d54 <unknown>
4: 0x5591dd9d1a04 main at /home/max/src/test/test.cc:103
5: 0x7f3c1845cb97 __libc_start_main at ../csu/libc-start.c:344
6: 0x5591dd9d17aa _start

तुलना के backtrace_symbols_fdलिए, समान स्टैकट्रेस के लिए आउटपुट कम से कम जानकारीपूर्ण है:

/home/max/src/test/debug/gcc/test(+0x192f)[0x5601c5a2092f]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(+0x92ae5)[0x7f95184f5ae5]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(_ZSt9terminatev+0x10)[0x7f95184f5b20]
/usr/lib/x86_64-linux-gnu/libstdc++.so.6(__cxa_throw+0x43)[0x7f95184f5d53]
/home/max/src/test/debug/gcc/test(+0x1ae7)[0x5601c5a20ae7]
/lib/x86_64-linux-gnu/libc.so.6(__libc_start_main+0xe6)[0x7f9518093b96]
/home/max/src/test/debug/gcc/test(+0x1849)[0x5601c5a20849]

एक उत्पादन संस्करण (और साथ ही सी भाषा संस्करण) में आप बदल कर भी इस कोड अतिरिक्त मजबूत बनाने के लिए पसंद कर सकते हैं boost::core::demangle, std::stringऔरstd::cout उनके अंतर्निहित कॉल्स के साथ ।

आप __cxa_throwस्टैकट्रेस को पकड़ने के लिए ओवरराइड भी कर सकते हैं जब एक अपवाद फेंक दिया जाता है और अपवाद पकड़े जाने पर इसे प्रिंट करें। जब तक यह प्रवेश करता है तब तक catchब्लॉक निष्कासित नहीं होता है, इसलिए इसे कॉल करने में बहुत देर हो चुकी है backtrace, और यही कारण है कि स्टैक को पकड़ना चाहिए, throwजिस पर फ़ंक्शन द्वारा इसे लागू किया जाता है __cxa_throw। ध्यान दें कि एक बहु-थ्रेडेड प्रोग्राम में एक __cxa_throwसाथ कई थ्रेड्स को कॉल किया जा सकता है, ताकि अगर यह स्टैकट्रेस को एक वैश्विक सरणी में कैप्चर करता है जो कि होना चाहिए thread_local


1
अच्छा उत्तर! अच्छी तरह से शोध, भी।
एसएस ऐनी

@SSAnne बहुत दयालु, धन्यवाद। -lunwindइस पोस्ट को बनाते समय उस मुद्दे का पता चला, मैंने पहले libunwindसीधे स्टैकट्रेस प्राप्त करने के लिए उपयोग किया था और इसे पोस्ट करने जा रहा था, लेकिन लिंक backtraceहोने पर यह मेरे लिए करता -lunwindहै।
मैक्सिम इगोरुस्किन

1
@SSAnne हो सकता है क्योंकि लाइब्रेरी के मूल लेखक डेविड मोस्बर्गर शुरू में IA-64 पर केंद्रित थे, लेकिन तब लाइब्रेरी को अधिक ट्रैक्शन nongnu.org/libunwind/people.html मिल गया । gccएपीआई को उजागर नहीं करता है, क्या यह सही है?
मैक्सिम एगोरुस्किन

3

आप स्वयं कार्यक्षमता को कार्यान्वित कर सकते हैं:

एक वैश्विक (स्ट्रिंग) स्टैक का उपयोग करें और प्रत्येक फ़ंक्शन के प्रारंभ में इस स्टैक पर फ़ंक्शन नाम और ऐसे अन्य मान (जैसे पैरामीटर) पुश करें; फ़ंक्शन के बाहर निकलने पर इसे फिर से पॉप करें।

एक फ़ंक्शन लिखें, जिसे कॉल करने पर स्टैक सामग्री प्रिंट हो जाएगी, और इस फ़ंक्शन में उसका उपयोग करें जहां आप कॉलस्टैक देखना चाहते हैं।

यह बहुत काम की तरह लग सकता है लेकिन काफी उपयोगी है।


2
मैं वह न करता। बल्कि, मैं एक रैपर बनाऊंगा जो अंतर्निहित प्लेटफ़ॉर्म विशिष्ट एपीआई (नीचे देखें) का उपयोग करता है। काम की मात्रा संभवतः समान होगी, लेकिन निवेश को तेजी से भुगतान करना चाहिए।
पॉल मिशालिक

3
@ अंपुल: आपका जवाब खिड़कियों को संदर्भित करता है जब ओपी स्पष्ट रूप से लिनक्स को निर्दिष्ट करता है ... लेकिन यहां प्रदर्शित होने वाले विंडोज़-लड़कों के लिए उपयोगी हो सकता है।
स्लैशमाईस

ठीक है, मैंने इस बात को नजरअंदाज कर दिया कि..मैं, यह सवाल का आखिरी वाक्य है, इसलिए हो सकता है कि पोस्टर को अपने लक्ष्य मंच का उल्लेख करने के लिए अपने अनुरोध को संशोधित करना चाहिए।
पॉल माइकालिक

1
यह एक अच्छा विचार होगा, सिवाय मेरे कोडबेस में कुछ सौ (यदि कुछ हजार नहीं) फाइलों वाली कुछ दर्जन फाइलें शामिल हैं, तो यह अक्षम्य है।
नाथन फेलमैन

हो सकता है कि यदि आप प्रत्येक फ़ंक्शन घोषणा के बाद जोड़ने के लिए एक सेड / पर्ल स्क्रिप्ट को हैक करते हैं call_registror MY_SUPERSECRETNAME(__FUNCTION__);जो कि इसके निर्माता में तर्क को धक्का देता है और इसके विनाशकर्ता में पॉप करता है FUNCTION हमेशा वर्तमान फ़ंक्शन के नाम का प्रतिनिधित्व करता है।
flownt

2

बेशक अगला सवाल यह है: क्या यह पर्याप्त होगा?

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

यदि आपके पास gcc और gdb तक पहुंच है, तो मैं assertएक विशिष्ट स्थिति की जांच करने के लिए उपयोग करने का सुझाव दूंगा , और यह पूरा नहीं होने पर मेमोरी डंप का उत्पादन करूंगा । बेशक इसका मतलब यह है कि प्रक्रिया बंद हो जाएगी, लेकिन आपके पास केवल स्टैक-ट्रेस के बजाय एक पूर्ण रिपोर्ट होगी।

यदि आप कम मोटे रास्ते की इच्छा रखते हैं, तो आप हमेशा लॉगिंग का उपयोग कर सकते हैं। वहाँ बहुत कुशल लॉगिंग सुविधाएं हैं, उदाहरण के लिए Pantheios । जो एक बार फिर से आपको अधिक सटीक छवि दे सकता है कि क्या चल रहा है।


1
बेशक यह पर्याप्त नहीं हो सकता है, लेकिन अगर मैं देख सकता हूं कि फ़ंक्शन को एक कॉन्फ़िगरेशन के साथ जगह में बुलाया गया है और दूसरे के साथ नहीं है, तो यह शुरू करने के लिए एक बहुत अच्छी जगह है।
नाथन फेलमैन

2

इसके लिए आप पोपी का इस्तेमाल कर सकते हैं । यह आमतौर पर क्रैश के दौरान स्टैक ट्रेस को इकट्ठा करने के लिए उपयोग किया जाता है, लेकिन यह इसे एक रनिंग प्रोग्राम के लिए भी आउटपुट कर सकता है।

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


2

मुझे पता है कि यह धागा पुराना है, लेकिन मुझे लगता है कि यह अन्य लोगों के लिए उपयोगी हो सकता है। यदि आप gcc का उपयोग कर रहे हैं, तो आप किसी भी फ़ंक्शन कॉल (प्रवेश और निकास) को लॉग करने के लिए इसके इंस्ट्रूमेंट फीचर्स (-finstrument- फ़ंक्शंस ऑप्शन) का उपयोग कर सकते हैं। अधिक जानकारी के लिए इस पर एक नज़र डालें: http://hacktalks.blogspot.fr/2013/08/gcc-instrument-actions.html

आप उदाहरण के लिए पुश और हर कॉल को एक स्टैक में पॉप कर सकते हैं, और जब आप इसे प्रिंट करना चाहते हैं, तो आप सिर्फ यह देखते हैं कि आपके स्टैक में क्या है।

मैंने इसका परीक्षण किया है, यह पूरी तरह से काम करता है और बहुत उपयोगी है

अद्यतन: आप इंस्ट्रूमेंटेशन विकल्पों के विषय में GCC doc में -finstrument-functions संकलन विकल्प के बारे में भी जानकारी प्राप्त कर सकते हैं: https://gcc.gnu.org/oniltocs/gcc/Instrumentation-Options.html


लेख नीचे जाने की स्थिति में आपको GCC डॉक्स से भी लिंक होना चाहिए।
पवित्रब्लैकैट

धन्यवाद, आप सही हैं। मैंने इस प्रकार अपनी पोस्ट में एक अद्यतन के साथ जोड़ा है gcc doc
फ्रांस्वा

2

आप वर्तमान कॉलस्टैक को प्रिंट करने के लिए बूस्ट लाइब्रेरी का उपयोग कर सकते हैं।

#include <boost/stacktrace.hpp>

// ... somewhere inside the `bar(int)` function that is called recursively:
std::cout << boost::stacktrace::stacktrace();

यहाँ आदमी: https://www.boost.org/doc/libs/1_65_1/doc/html/stacktrace.html


मुझे cannot locate SymEnumSymbolsExW at C:\Windows\SYSTEM32\dbgeng.dllWin10 पर एक त्रुटि मिली ।
zwcloud

0

आप GNU प्रोफाइलर का उपयोग कर सकते हैं। यह कॉल-ग्राफ को भी दर्शाता है! कमांड है gprofऔर आपको कुछ विकल्प के साथ अपना कोड संकलित करना होगा।


-6

क्या हर बार एक निश्चित फ़ंक्शन कहे जाने वाले C या C ++ में चल रही प्रक्रिया में कॉल स्टैक को डंप करने का कोई तरीका है?

नहीं, वहाँ नहीं है, हालांकि मंच पर निर्भर समाधान मौजूद हो सकता है।

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