সি বা সি ++ এ কল স্ট্যাক প্রিন্ট করুন


120

চলমান প্রক্রিয়াটিতে কল স্ট্যাকটি ডাম্প করার কোনও উপায় কি সি বা সি ++ এ প্রতিবার নির্দিষ্ট কোনও ফাংশন বলা হয়? আমার মনে যা কিছু আছে তা হ'ল:

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
@ আর্মেন, আপনি কি এর সাথে পরিচিত?
নাথান ফেলম্যান

1
@ নাথান: আপনার ডিবাগারটি যদি জিডিবি হয় তবে এটি সেই ক্ষেত্রে পরিচালনা করতে পারে । আমি আপনাকে অন্যের সম্পর্কে বলতে পারি না, তবে আমি ধরে নিই যে এই কার্যকারিতাটি জিডিবি একা নয়। পাশে: আমি কেবল আমার আগের মন্তব্যে তাকিয়েছি । :: ঠাট্টা :: s/easier/either/কীভাবে ঘটে গেল?
dmckee --- প্রাক্তন মডারেটর বিড়ালছানা

2
@ ডিএমকে: আসলে, তা হওয়া উচিত s/either/easier। Gdb এর সাথে আমার যা করা দরকার তা হ'ল একটি স্ক্রিপ্ট লিখুন যা সেই ফাংশনে ব্রেক হয়ে যায় এবং স্ট্যাকের চিহ্নটি ছাপিয়ে দেয়, তারপরে অবিরত থাকে। এখন যেহেতু আমি এটি সম্পর্কে চিন্তা করি, সম্ভবত এখন সময় এসেছে জিডিবি স্ক্রিপ্টিং সম্পর্কে শিখার।
নাথান ফেলম্যান

1
গাহ! কিছুটা ঘুম পেতে চলেছি। বাস্তব শীঘ্রই এখন ...
ডিএমকেকে --- প্রাক্তন মডারেটর বিড়ালছানা

1

উত্তর:


79

একটি লিনাক্স-কেবলমাত্র সমাধানের জন্য আপনি ব্যাকট্রেস (3) ব্যবহার করতে পারেন যা কেবলমাত্র একটি অ্যারে প্রদান করে void *(আসলে এই পয়েন্টগুলির প্রত্যেকটি সংশ্লিষ্ট স্ট্যাক ফ্রেম থেকে রিটার্ন ঠিকানায়)। এগুলিকে ব্যবহারের কিছুতে অনুবাদ করতে, ব্যাকট্র্যাস_সাইকোমোলস রয়েছে (3)

ব্যাকট্রেস নোট বিভাগে মনোযোগ দিন (3) :

বিশেষ লিঙ্কার বিকল্পগুলি ব্যবহার না করে প্রতীকের নামগুলি অনুপলব্ধ হতে পারে। জিএনইউ লিংকার ব্যবহারকারী সিস্টেমগুলির জন্য, -ডাইনামিক লিংকার বিকল্পটি ব্যবহার করা প্রয়োজন। নোট করুন যে "স্ট্যাটিক" ফাংশনগুলির নাম প্রকাশিত হয় না, এবং ব্যাকট্রেসে উপলব্ধ হবে না।


10
এফডাব্লুআইডাব্লু, এই কার্যকারিতাটি ম্যাক ওএস
এক্সেও রয়েছে


2
অ্যাপল লিঙ্কটি পরিবর্তিত developer.apple.com/legacy/library/documentation/Darwin/...
বেন Ylvisaker

লিনাক্স সহ glibc, দুর্ভাগ্যক্রমে, backtrace_symbolsফাংশনগুলি ফাংশন নাম, উত্স ফাইলের নাম এবং লাইন নম্বর সরবরাহ করে না।
ম্যাক্সিম এগারুশকিন

ব্যবহারের -rdynamicপাশাপাশি, আপনার বিল্ড সিস্টেমটি -fvisibility=hiddenবিকল্প যুক্ত করে না তাও পরীক্ষা করে দেখুন ! (এটি সম্পূর্ণরূপে এর প্রভাবটি বাতিল করে দেবে -rdynamic)
দিমা লিটভিনভ

36

স্ট্যাকট্রেস বুস্ট করুন

এখানে নথিবদ্ধ: https://www.boost.org/doc/libs/1_66_0/doc/html/stacktrace/getting_st সূত্র

এটি এখন পর্যন্ত আমি সবচেয়ে সুবিধাজনক বিকল্পটি দেখেছি কারণ এটি:

  • আসলে লাইন নম্বর মুদ্রণ করতে পারেন।

    এটি কেবল কল করে 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

আউটপুটটি এবং নীচে "গ্লিবসি ব্যাকট্রেস" বিভাগে আরও ব্যাখ্যা করা হয়েছে, যা সাদৃশ্যপূর্ণ।

নোট কিভাবে my_func_1(int)এবং my_func_1(float), যা ফাংশন জমিদার কারণে mangled হয় , সুন্দরভাবে আমাদের জন্য demangled হয়।

মনে রাখবেন যে প্রথম intকলগুলি একটি লাইনের মাধ্যমে বন্ধ থাকে (২ 27 এর পরিবর্তে ২৮ এবং দ্বিতীয়টি দুটি লাইন দিয়ে বন্ধ থাকে (২৯ এর পরিবর্তে ২ is)। মন্তব্যগুলিতে পরামর্শ দেওয়া হয়েছিল যে এটি নীচের নির্দেশিকার ঠিকানা বিবেচনা করা হচ্ছে, যা 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

ব্যাকট্রেসগুলি অপ্টিমাইজেশন দ্বারা সাধারণ অপূরণীয়ভাবে বিকৃত হয়। টেইল কল অপ্টিমাইজেশন এর একটি উল্লেখযোগ্য উদাহরণ: টেল কল অপ্টিমাইজেশন কী?

বেঞ্চমার্ক চালু আছে -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/Backtraces.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 কী প্রয়োজনীয় বিকল্প।

চালান:

./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

সুতরাং লাইন কেবল এক দ্বারা বন্ধ করা হয়, কেন? তবে এটি এখনও ব্যবহারযোগ্য হতে পারে।

উপসংহার: ব্যাকট্রেসগুলি কেবলমাত্র পুরোপুরি সাথে পুরোপুরি প্রদর্শিত হতে পারে -O0। অপ্টিমাইজেশন সহ, মূল ব্যাকট্র্যাসটি সংকলিত কোডে মৌলিকভাবে সংশোধন করা হয়েছে।

আমি এর সাথে সি ++ চিহ্নগুলি স্বয়ংক্রিয়ভাবে ডিমেংল করার একটি সহজ উপায় খুঁজে পাইনি তবে এখানে কিছু হ্যাক রয়েছে:

উবুন্টু 16.04, জিসিসি 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("");
}

উবুন্টু 16.04, জিসিসি 6.4.0, libc 2.23 এ পরীক্ষিত।

glibc backtraceসি সঙ্গে ++, হ্যাক demangling 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 এ পরীক্ষিত।

glibc backtraceসি সঙ্গে ++, 2 হ্যাক demangling: পার্স ব্যাক-ট্রেস আউটপুট

এতে দেখানো হয়েছে: https://panthema.net/2008/0901-stacktrace-demangled/

এটি একটি হ্যাক কারণ এটি পার্সিংয়ের প্রয়োজন।

এটি সংকলন করতে এবং এটি এখানে দেখানোর জন্য TODO পান।

libunwind

টোডো কি গ্লাবসি ব্যাকট্রেসের চেয়ে কোনও সুবিধা রাখে? খুব অনুরূপ আউটপুট, এছাড়াও বিল্ড কমান্ড পরিবর্তন করতে হবে, কিন্তু গ্লিবসি অংশ নয় তাই অতিরিক্ত প্যাকেজ ইনস্টলেশন প্রয়োজন।

কোডটি রূপান্তরিত থেকে: 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:

চালান:

./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

উবুন্টু 16.04, জিসিসি 6.4.0, লাইবুনউইন্ড 1.1 এ পরীক্ষিত।

সি ++ নাম ডিমেংলিংয়ের সাথে লাইবুনউইন্ড

কোডটি রূপান্তরিত থেকে: 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

টোডো: কেন একের পর এক লাইন বন্ধ?

উবুন্টু 18.04, জিসিসি 7.4.0, লাইবুনউইন্ড 1.2.2।

জিডিবি অটোমেশন

আমরা এটি ব্যবহার করে পুনরায় সংশোধন না করে জিডিবি দিয়ে এটিও করতে পারি: জিডিবিতে কোনও নির্দিষ্ট ব্রেকপয়েন্ট পাকানো হলে একটি নির্দিষ্ট ক্রিয়া কীভাবে করবেন?

যদিও আপনি ব্যাকট্র্যাসটি প্রচুর মুদ্রণ করতে চলেছেন তবে অন্যান্য বিকল্পগুলির তুলনায় এটি সম্ভবত কম দ্রুত হবে তবে আমরা সম্ভবত দেশীয় গতিতে পৌঁছাতে compile codeপারি তবে এখনই এটি পরীক্ষা করতে আমি অলস: জিডিবিতে অ্যাসেম্বলি কীভাবে ডাকব?

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]

টুডো আমি -exকমান্ড লাইন থেকে তৈরি main.gdbকরতে হবে না তা দিয়ে এটি commandsকরতে চেয়েছিলাম কিন্তু আমি সেখানে কাজ করতে পারি না ।

উবুন্টু 19.04, জিডিবি 8.2 তে পরীক্ষিত।

লিনাক্স কার্নেল

লিনাক্স কার্নেলের ভিতরে কীভাবে বর্তমান থ্রেড স্ট্যাক ট্রেস প্রিন্ট করবেন?

libdwfl

এটি মূলত এখানে উল্লেখ করা হয়েছিল: https://stackoverflow.com/a/60713161/895245 এবং এটি সর্বোত্তম পদ্ধতি হতে পারে তবে আমাকে আরও কিছুটা বেঞ্চমার্ক করতে হবে, তবে দয়া করে উত্তরের উত্তরটি যান।

টোডো: আমি উত্তরটিতে কোডটি ছোট করার চেষ্টা করছিলাম, যা কাজ করছিল, একটি ফাংশনে, তবে এটি সেগফোল্টিং, যদি কেউ কেন এটি খুঁজে পেতে পারে তবে আমাকে জানান।

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

সুতরাং আমরা দেখতে পাই যে এই পদ্ধতিটি বুস্টের স্ট্যাকট্রেসের চেয়ে 10x দ্রুত এবং এটি ব্যবহারের ক্ষেত্রে আরও কার্যকর হতে পারে।

উবুন্টু 19.10 amd64 এ পরীক্ষিত, libdw-dev 0.176-1.1।

আরো দেখুন


1
সমস্ত "টোডো: লাইন এক দ্বারা বন্ধ" কারণ লাইন নম্বরটি পরবর্তী এক্সপ্রেশনটির শুরু থেকে নেওয়া হয়েছে।
এসএস আন


6

চলমান প্রক্রিয়াটিতে কল স্ট্যাকটি ডাম্প করার কোনও উপায় কি সি বা সি ++ এ প্রতিবার নির্দিষ্ট কোনও ফাংশন বলা হয়?

আপনি নির্দিষ্ট ফাংশনে রিটার্ন স্টেটমেন্টের পরিবর্তে ম্যাক্রো ফাংশন ব্যবহার করতে পারেন।

উদাহরণস্বরূপ, রিটার্ন ব্যবহারের পরিবর্তে,

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)

সম্পূর্ণ উত্স কোড এখানে উপলব্ধ।

সি-কলস্ট্যাক 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 ()

এটি লিনাক্স, ফ্রিবিএসডি এবং সোলারিসে কাজ করা উচিত। আমি মনে করি না যে ম্যাকোএসের পিস্ট্যাক বা একটি সাধারণ সমতুল্য রয়েছে তবে এই থ্রেডটির বিকল্প রয়েছে বলে মনে হয়

আপনি যদি ব্যবহার করছেন 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);
}

আমি এই পোস্টের উপর ভিত্তি করে পিআইডিতে সর্বাধিক সংখ্যার জন্য 7 ব্যবহার করেছি ।


ভাল কথা, যেহেতু বিষয়টি সি সম্পর্কে জিজ্ঞাসা করে না এটির জন্য কোনও অভিযোজন প্রয়োজন হবে না, যেহেতু স্ট্যান্ড :: স্ট্রিংটি কেবল সি ++। আমি আমার উত্তরটি একটি সি সংস্করণ দিয়ে আপডেট করব।
পল ফ্লয়েড

5

লিনাক্স নির্দিষ্ট, টিএলডিআর:

  1. backtraceমধ্যে সংযুক্ত থাকে glibcকেবল তখনই সঠিক স্ট্যাকট্রেস উত্পাদন করে -lunwind(অনথিভুক্ত প্ল্যাটফর্ম-নির্দিষ্ট বৈশিষ্ট্য)।
  2. ফাংশন নাম , উত্স ফাইল এবং লাইন নম্বর ব্যবহার আউটপুট করতে #include <elfutils/libdwfl.h>(এই গ্রন্থাগারটি কেবলমাত্র তার শিরোনামের ফাইলে ডকুমেন্টেড রয়েছে)। backtrace_symbolsএবং backtrace_symbolsd_fdঅন্তত তথ্যবহুল হয়।

আধুনিক লিনাক্সে আপনি ফাংশন ব্যবহার করে স্ট্যাকট্রেস ঠিকানাগুলি পেতে পারেন backtracebacktraceজনপ্রিয় প্ল্যাটফর্মগুলিতে আরও নির্ভুল ঠিকানা তৈরি করার অননুমোদিত উপায় হ'ল -lunwind( libunwind-devউবুন্টু 18.04 এ) সাথে লিঙ্ক করা ( নীচের উদাহরণ আউটপুট দেখুন)। backtraceফাংশন ব্যবহার করে _Unwind_Backtraceএবং ডিফল্টরূপে পরবর্তীটি আসে libgcc_s.so.1এবং সেই প্রয়োগটি সবচেয়ে বহনযোগ্য। যখন -lunwindসংযুক্ত থাকে এটির আরও সঠিক সংস্করণ সরবরাহ করা হয় _Unwind_Backtraceতবে এই গ্রন্থাগারটি কম পোর্টেবল (এতে সমর্থিত আর্কিটেকচার দেখুন libunwind/src)।

দুর্ভাগ্যক্রমে, সঙ্গী backtrace_symbolsdএবং backtrace_symbols_fdফাংশনগুলি সম্ভবত এক দশক ধরে উত্স ফাইলের নাম এবং লাইন নম্বর সহ ফাংশন নামগুলির জন্য স্ট্যাকট্রেস ঠিকানাগুলি সমাধান করতে সক্ষম হয়নি (নীচের উদাহরণ আউটপুট দেখুন)।

তবে, চিহ্নগুলির ঠিকানাগুলি সমাধান করার জন্য আরও একটি পদ্ধতি রয়েছে এবং এটি ফাংশনের নাম , উত্স ফাইল এবং লাইন নম্বর সহ সর্বাধিক দরকারী ট্রেস তৈরি করে । পদ্ধতিটি হল #include <elfutils/libdwfl.h>এবং এর সাথে লিঙ্ক করুন -ldw(libdw-dev উবুন্টু 18.04 এ) এর করুন।

কার্যকারী সি ++ উদাহরণ ( 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 সহ উবুন্টু 18.04.4 এলটিএসে সংকলিত:

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
চমৎকার উত্তর! ভাল গবেষণাও হয়েছে।
এসএস আন

@ এসএসএন খুব দয়ালু, আপনাকে ধন্যবাদ -lunwindএই পোস্টটি তৈরি করার সময় এই সমস্যাটি আবিষ্কার হয়ে গেছে, আমি আগে libunwindসরাসরি স্ট্যাকট্রেস প্রাপ্ত করার জন্য ব্যবহার করতাম এবং এটি পোস্ট করতে যাচ্ছিলাম, তবে লিঙ্কযুক্ত backtraceহওয়ার পরে -lunwindএটি আমার জন্য করে ।
ম্যাক্সিম এগুরুশকিন

1
@ এসএসএন হতে পারে কারণ গ্রন্থাগারের মূল লেখক ডেভিড মোসবার্গার শুরুতে আইএ -৪৪ এর দিকে মনোনিবেশ করেছিলেন তবে লাইব্রেরিটি আরও ট্র্যাকশন পেয়েছে nongnu.org/libunwind/people.htmlgccএপিআই প্রকাশ করে না, এটা কি ঠিক?
ম্যাক্সিম এগারুশকিন

3

আপনি নিজে কার্যকারিতা বাস্তবায়ন করতে পারেন:

একটি গ্লোবাল (স্ট্রিং) স্ট্যাক ব্যবহার করুন এবং প্রতিটি ফাংশনের শুরুতে এই স্ট্যাকের উপরে ফাংশনটির নাম এবং এই জাতীয় অন্যান্য মানগুলি (যেমন পরামিতি) টিপুন; ফাংশন থেকে প্রস্থান করার সময় এটি আবার পপ করুন।

এমন একটি ফাংশন লিখুন যা কল করার সময় স্ট্যাকের সামগ্রী প্রিন্টআউট করবে এবং যেখানে আপনি কলস্ট্যাকটি দেখতে চান সেই ফাংশনে এটি ব্যবহার করুন।

এটি অনেক কাজের মতো শোনাতে পারে তবে এটি বেশ কার্যকর।


2
আমি সেটা করতাম না. বরং আমি এমন একটি মোড়ক তৈরি করব যা অন্তর্নিহিত প্ল্যাটফর্ম নির্দিষ্ট API গুলি ব্যবহার করে (নীচে দেখুন)। কাজের পরিমাণ সম্ভবত একই হবে তবে বিনিয়োগের দ্রুত অর্থ প্রদান করা উচিত।
পল মিশালিক

3
@ পল: আপনার উত্তরটি উইন্ডোজকে বোঝায় যখন ওপি স্পষ্টভাবে লিনাক্স নির্দিষ্ট করে ... তবে উইন্ডোজ-ছেলেরা যারা এখানে প্রদর্শিত হবে তাদের পক্ষে কার্যকর হতে পারে।
স্ল্যাশমেজ

ঠিক আছে, আমি এটিকে উপেক্ষা করেছি ... এইচএম, এটি প্রশ্নের শেষ বাক্য, তাই পোস্টারটির দ্বারা তার টার্গেট প্ল্যাটফর্মটি আরও বিশিষ্ট জায়গায় উল্লেখ করার জন্য তাঁর অনুরোধটি পরিবর্তন করা উচিত।
পল মিশালিক

1
এটি আমার পক্ষে একটি ভাল ধারণা হবে, আমার কোডবেজে কয়েক ডজন ফাইল রয়েছে যা কয়েকশ (যদি কয়েক হাজার নয়) থাকে তবে এটি অপরিবর্তনীয়।
নাথান ফেলম্যান

আপনি যদি প্রতিটি ফাংশন ঘোষণার পরে যোগ করার জন্য একটি সেড / পার্ল স্ক্রিপ্ট হ্যাক-আপ করেন না call_registror MY_SUPERSECRETNAME(__FUNCTION__);যা এটির নির্মাণকারীর মধ্যে যুক্তিটি ঠেলে দেয় এবং তার ডেস্ট্রাক্টরে পপ করে ফাংশন সর্বদা বর্তমান ফাংশনটির নাম উপস্থাপন করে।
ফুল দিন

2

অবশ্যই পরবর্তী প্রশ্নটি: এটি কি যথেষ্ট হবে?

স্ট্যাক-ট্রেসগুলির প্রধান অসুবিধা হ'ল আপনার যথাযথ ফাংশনটি কেন বলা হচ্ছে এটির তর্কগুলির মূল্য মত, যা ডিবাগিংয়ের জন্য খুব কার্যকর।

আপনার যদি জিসিসি এবং জিডিবিতে অ্যাক্সেস থাকে তবে আমি assertএকটি নির্দিষ্ট শর্ত যাচাই করার জন্য ব্যবহার করার পরামর্শ দিচ্ছি এবং যদি এটি পূরণ না হয় তবে একটি মেমরি ডাম্প উত্পাদন করতে পারি। অবশ্যই এর অর্থ প্রক্রিয়াটি বন্ধ হয়ে যাবে তবে আপনার কাছে কেবল স্ট্যাক-ট্রেসের পরিবর্তে একটি পূর্ণাঙ্গ প্রতিবেদন থাকবে।

আপনি যদি কম বাধা উপায়ের জন্য চান, আপনি সর্বদা লগিং ব্যবহার করতে পারেন। উদাহরণস্বরূপ প্যানথিয়াসের মতো খুব দক্ষ লগিং সুবিধা রয়েছে । যা আবার আপনাকে যা চলছে তার আরও সঠিক চিত্র দিতে পারে।


1
অবশ্যই এটি যথেষ্ট নাও হতে পারে, তবে যদি আমি দেখতে পারি যে ফাংশনটি একটি কনফিগারেশনের সাথে অন্যদিকে নয় বরং জায়গায় কল করা হয়েছে তবে এটি শুরু করার জন্য বেশ সুন্দর জায়গা।
নাথান ফেলম্যান

2

আপনি এর জন্য পপি ব্যবহার করতে পারেন । এটি সাধারণত ক্র্যাশের সময় স্ট্যাক ট্রেস সংগ্রহ করতে ব্যবহৃত হয় তবে এটি চলমান প্রোগ্রামের জন্য এটি আউটপুটও করতে পারে।

এখন এখানে ভাল অংশটি রয়েছে: এটি স্ট্যাকের প্রতিটি ফাংশনের জন্য প্রকৃত প্যারামিটার মানগুলি এবং এমনকি স্থানীয় ভেরিয়েবলগুলি, লুপ কাউন্টারগুলিও আউটপুট করতে পারে etc.


2

আমি জানি যে এই থ্রেডটি পুরানো, তবে আমি মনে করি এটি অন্যান্য ব্যক্তির পক্ষে কার্যকর হতে পারে। আপনি যদি জিসিসি ব্যবহার করে থাকেন তবে যেকোন ফাংশন কল (প্রবেশ এবং প্রস্থান) লগ করতে আপনি তার উপকরণের বৈশিষ্ট্যগুলি (-ফিনস্রামমেন্ট-ফাংশন বিকল্প) ব্যবহার করতে পারেন। আরো তথ্যের জন্য এই কটাক্ষপাত আছে: http://hacktalks.blogspot.fr/2013/08/gcc-instrument-functions.html

আপনি উদাহরণস্বরূপ উদাহরণস্বরূপ প্রতিটি কলকে স্ট্যাকের মধ্যে ধাক্কা দিতে এবং পপ করতে পারেন এবং আপনি যখন এটি মুদ্রণ করতে চান, আপনি কেবল আপনার স্ট্যাকের মধ্যে কী তা দেখুন।

আমি এটি পরীক্ষা করেছি, এটি পুরোপুরি কাজ করে এবং খুব কার্যকর

আপডেট: আপনি ইনস্ট্রুমেন্টেশন বিকল্পগুলির সাথে সম্পর্কিত জিসিসি ডক-এ-ইনফ্রান্সমেন্ট-ফাংশন সংকলন বিকল্প সম্পর্কেও তথ্য পেতে পারেন: https://gcc.gnu.org/onlinesocs/gcc/Instrumentation-Options.html


নিবন্ধটি নীচে নেমে গেলে আপনার জিসিসি ডক্সের সাথেও লিঙ্ক করা উচিত।
হলিব্ল্যাকগেট

ধন্যবাদ, আপনি ঠিক বলেছেন। আমি এইভাবে জিসিসি ডকের সাথে একটি লিঙ্ক সহ আমার পোস্টে একটি আপডেট যোগ করেছি
ফ্রান্সোইস

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

আপনি জিএনইউ প্রোফাইলার ব্যবহার করতে পারেন। এটি কল-গ্রাফটিও দেখায়! কমান্ডটি হ'ল gprofএবং আপনাকে কিছু বিকল্পের সাথে আপনার কোডটি সংকলন করতে হবে।


-6

চলমান প্রক্রিয়াটিতে কল স্ট্যাকটি ডাম্প করার কোনও উপায় কি সি বা সি ++ এ প্রতিবার নির্দিষ্ট কোনও ফাংশন বলা হয়?

নেই, যদিও প্ল্যাটফর্ম-নির্ভর সমাধান থাকতে পারে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.