ব্যতিক্রম সি সি ++ প্রদর্শন স্ট্যাক ট্রেস


204

যদি কোনও ব্যতিক্রম ছুঁড়ে দেওয়া হয় তবে আমি স্ট্যাকের ট্রেসটি ব্যবহারকারীকে জানাতে একটি উপায় থাকতে চাই। এই কাজ করতে সবচেয়ে ভালো উপায় কি? এটি অতিরিক্ত পরিমাণে কোড নেয়?

প্রশ্নের উত্তর দিতে:

আমি যদি এটি সম্ভব হয় পোর্টেবল হতে চান। আমি পপ-আপ করার জন্য তথ্য চাই, যাতে ব্যবহারকারী স্ট্যাক ট্রেসটি অনুলিপি করতে পারে এবং কোনও ত্রুটি এলে তা আমাকে ইমেল করতে পারে।

উত্তর:


76

এটি নির্ভর করে কোন প্ল্যাটফর্মটি।

জিসিসিতে এটি বেশ তুচ্ছ, এই পোস্টটি দেখুন আরও তথ্যের জন্য দেখুন।

এমএসভিসিতে তারপরে আপনি স্ট্যাকওয়াকার লাইব্রেরিটি ব্যবহার করতে পারেন যা উইন্ডোজের জন্য প্রয়োজনীয় সমস্ত অন্তর্নিহিত API কলগুলি পরিচালনা করে।

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


71
আপনি যে পোস্টটির সাথে লিঙ্ক করেছেন সেটি বেশিরভাগ ক্ষেত্রে সেগফল্ট থেকে কোনও ট্রেস উত্পন্ন করার জন্য নির্দেশ করে, তবে প্রশ্নকর্তা বিশেষত ব্যতিক্রমগুলি উল্লেখ করেছেন, যা বেশ আলাদা জন্তু।
শেপ

8
আমি @ শেফের সাথে একমত - এই উত্তরটি জিসিসিতে নিক্ষেপকারী কোডটির স্ট্যাক ট্রেস পেতে সত্যিই সহায়তা করে না। সম্ভাব্য সমাধানের জন্য আমার উত্তরটি দেখুন।
টমাস টেম্পেলম্যান

1
এই উত্তরটি বিভ্রান্তিকর। লিংক উত্তর নির্দিষ্ট স্থানটিকে Linuxনা gcc
fjardon

এই উত্তরে বর্ণিত হিসাবে আপনি libstdc++(জিসিসি এবং সম্ভাব্য ঝনঝন দ্বারা ব্যবহৃত) এর নিক্ষেপ প্রক্রিয়াটিকে ওভাররাইড করতে পারেন ।
ingomueller.net

59

অ্যান্ড্রু গ্রান্টের উত্তর নিক্ষেপ ফাংশনটির স্ট্যাক ট্রেস পেতে সহায়তা করে না , কমপক্ষে জিসিসির সাথে নয়, কারণ একটি থ্রো স্টেটমেন্ট বর্তমান স্ট্যাকের ট্রেসটি নিজেই সংরক্ষণ করে না এবং ক্যাচ হ্যান্ডলারের স্ট্যাক ট্রেসটিতে অ্যাক্সেস থাকবে না won't যে বিন্দু আর।

এর সমাধানের জন্য জিসিসি - ব্যবহার করার একমাত্র উপায় হ'ল নিক্ষেপ নির্দেশের স্থানে একটি স্ট্যাক ট্রেস তৈরি করা এবং ব্যতিক্রম অবজেক্টের সাহায্যে এটি সংরক্ষণ করা।

অবশ্যই এই পদ্ধতির অবশ্যই প্রয়োজন যে প্রতিটি কোড যে ব্যতিক্রম ছুঁড়ে। সেই নির্দিষ্ট ব্যতিক্রম শ্রেণিটি ব্যবহার করে।

11 জুলাই 2017 আপডেট করুন : কিছু সহায়ক কোডের জন্য, ক্যাহিত বিয়াজের উত্তরটি দেখুন, যা http ://stacktrace.sourceforge.net- এ নির্দেশ করে - আমি এখনও এটি ব্যবহার করি নি তবে এটি আশাব্যঞ্জক মনে হচ্ছে।


1
দুর্ভাগ্যক্রমে লিঙ্কটি মারা গেছে। আপনি অন্য কিছু সরবরাহ করতে পারেন?
ওয়ারান

2
আর আর্কাইভ.অর্গও এটি জানে না। অভিশাপ। ঠিক আছে, পদ্ধতিটি পরিষ্কার হওয়া উচিত: একটি কাস্টম ক্লাসের 'বস্তু নিক্ষেপ করুন যা নিক্ষেপের সময় স্ট্যাক ট্রেস রেকর্ড করে।
টমাস টেম্পেলম্যান

1
স্ট্যাকট্রেসের হোম পেজে, আমি দেখতে পাচ্ছি throw stack_runtime_error। আমি কি এই অনুদানের ক্ষেত্রে সঠিক হয়েছি যে এই লিবাটি কেবল class শ্রেণি থেকে প্রাপ্ত ব্যতিক্রমগুলির জন্য কাজ করে, এবং std::exceptionতৃতীয় পক্ষের লাইব্রেরি থেকে বা ব্যতিক্রম নয় ?
থমাস

3
সুতরাং দুঃখের সাথে উত্তরটি "না, আপনি একটি সি ++ ব্যতিক্রম থেকে স্ট্যাকের ট্রেস পেতে পারেন না", একমাত্র বিকল্পটি আপনার নিজের বর্গ নিক্ষেপ করা হবে যা এটি নির্মাণের পরে স্ট্যাক ট্রেস তৈরি করে। আপনি যদি C ++ std :: লাইব্রেরির যেকোন অংশের মতো ব্যবহার করে আটকে থাকেন তবে আপনার ভাগ্য খারাপ। দুঃখিত, আপনি হতে পারে সফলকাম।
কোড অ্যাবমোনিটার 21

43

আপনি যদি বুস্ট ১.6565 বা তার বেশি ব্যবহার করেন তবে আপনি বুস্ট :: স্ট্যাকট্রেস ব্যবহার করতে পারেন :

#include <boost/stacktrace.hpp>

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

5
বুস্ট ডক্স কেবল স্ট্যাক ট্রেস ক্যাপচারই করে না, তবে ব্যতিক্রম এবং দৃ ex়তার জন্য কীভাবে এটি করা যায় তা ব্যাখ্যা করে। দুর্দান্ত জিনিস।
মুডবুম

1
এই স্ট্যাকট্রেস () কী গেটিংস্টার্ড গাইড হিসাবে প্রদত্ত উত্স ফাইল এবং লাইন নম্বর মুদ্রণ করে?
গিমহানি

14

2
ম্যাক ব্যাকট্রাস লিঙ্কটি মারা গেছে।
কোডি কোডমনকি

ম্যাক ব্যাকট্রেসের জন্য সক্রিয় লিঙ্ক (আইওএসের জন্য) । ওএসএক্স 10.14.6 মোজাভেতে ঠিক আছে বলে মনে হচ্ছে।
জেটি বুলিট

11

আমি একটি স্ট্যান্ডার্ড গ্রন্থাগার বিকল্প যুক্ত করতে চাই (যেমন ক্রস-প্ল্যাটফর্ম) কীভাবে ব্যতিক্রম ব্যাকট্রেসগুলি উত্পন্ন করা যায়, যা সি ++ 11 এর সাথে উপলব্ধ হয়েছে :

ব্যবহার করুন std::nested_exceptionএবংstd::throw_with_nested

এটি আপনাকে কোনও স্ট্যাক আনওয়াইন্ড দেবে না, তবে আমার মতে পরবর্তী সেরা জিনিস। এটি স্ট্যাকওভারফ্লোতে এখানে এবং এখানে বর্ণিত হয়েছে , কীভাবে আপনি আপনার ব্যাতিক্রমগুলিতে ব্যাকট্রেস পেতে পারেন কোনও ডিবাগার বা কষ্টকর লগিংয়ের প্রয়োজন ছাড়াই আপনার কোডের অভ্যন্তরে , কেবলমাত্র একটি উপযুক্ত ব্যতিক্রম হ্যান্ডলার লিখে যা নেস্টেড ব্যতিক্রমগুলি পুনর্বিবেচনা করবে।

যেহেতু আপনি যে কোনও উদ্ভূত ব্যতিক্রম শ্রেণীর সাহায্যে এটি করতে পারেন, আপনি এই জাতীয় ব্যাকট্রাসে প্রচুর তথ্য যুক্ত করতে পারেন! আপনি গিটহাবের আমার এমডব্লিউইতে একবার দেখে নিতে পারেন , যেখানে ব্যাকট্রিসটি এরকম কিছু দেখায়:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

সাধারণ বোবা স্ট্যাক ট্রেসের চেয়ে আপনি অতিরিক্ত কাজ করতে ইচ্ছুক হলে এটি সম্ভবত অনেক ভাল।
ক্লিয়ারার

4

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


libunwind 1.1 OS x এ তৈরি করে না।
xaxxon

4

আমি http://stacktrace.sourceforge.net/ প্রকল্পের প্রস্তাব দিই । এটি উইন্ডোজ, ম্যাক ওএস এবং লিনাক্সকে সমর্থন করে


4
এর হোম পেজে, আমি দেখতে পাচ্ছি throw stack_runtime_error। আমি কি এই অনুদানের ক্ষেত্রে সঠিক হয়েছি যে এই লিবাটি কেবল class শ্রেণি থেকে প্রাপ্ত ব্যতিক্রমগুলির জন্য কাজ করে, এবং std::exceptionতৃতীয় পক্ষের লাইব্রেরি থেকে বা ব্যতিক্রম নয় ?
থমাস

4

আপনি যদি সি ++ ব্যবহার করেন এবং বুস্টটি ব্যবহার করতে / না চান, তবে আপনি নিম্নলিখিত কোডটি [মূল সাইটের লিঙ্ক] ব্যবহার করে ডিমেলযুক্ত নামগুলি সহ ব্যাকট্রেস মুদ্রণ করতে পারেন ।

দ্রষ্টব্য, এই সমাধানটি লিনাক্সের জন্য নির্দিষ্ট। ব্যাকট্রেসগুলি পেতে এটি GNU এর libc ফাংশন ব্যাকট্রেস () / backtrace_symbols () (এক্সেকইনফো। থেকে) ব্যবহার করে এবং তারপরে ব্যাকট্রেস চিহ্নের নামগুলি ডিমেংল করার জন্য __cxa_demangle () (cxxabi.h থেকে) ব্যবহার করে।

// stacktrace.h (c) 2008, Timo Bingmann from http://idlebox.net/
// published under the WTFPL v2.0

#ifndef _STACKTRACE_H_
#define _STACKTRACE_H_

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

/** Print a demangled stack backtrace of the caller function to FILE* out. */
static inline void print_stacktrace(FILE *out = stderr, unsigned int max_frames = 63)
{
    fprintf(out, "stack trace:\n");

    // storage array for stack trace address data
    void* addrlist[max_frames+1];

    // retrieve current stack addresses
    int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));

    if (addrlen == 0) {
    fprintf(out, "  <empty, possibly corrupt>\n");
    return;
    }

    // resolve addresses into strings containing "filename(function+address)",
    // this array must be free()-ed
    char** symbollist = backtrace_symbols(addrlist, addrlen);

    // allocate string which will be filled with the demangled function name
    size_t funcnamesize = 256;
    char* funcname = (char*)malloc(funcnamesize);

    // iterate over the returned symbol lines. skip the first, it is the
    // address of this function.
    for (int i = 1; i < addrlen; i++)
    {
    char *begin_name = 0, *begin_offset = 0, *end_offset = 0;

    // find parentheses and +address offset surrounding the mangled name:
    // ./module(function+0x15c) [0x8048a6d]
    for (char *p = symbollist[i]; *p; ++p)
    {
        if (*p == '(')
        begin_name = p;
        else if (*p == '+')
        begin_offset = p;
        else if (*p == ')' && begin_offset) {
        end_offset = p;
        break;
        }
    }

    if (begin_name && begin_offset && end_offset
        && begin_name < begin_offset)
    {
        *begin_name++ = '\0';
        *begin_offset++ = '\0';
        *end_offset = '\0';

        // mangled name is now in [begin_name, begin_offset) and caller
        // offset in [begin_offset, end_offset). now apply
        // __cxa_demangle():

        int status;
        char* ret = abi::__cxa_demangle(begin_name,
                        funcname, &funcnamesize, &status);
        if (status == 0) {
        funcname = ret; // use possibly realloc()-ed string
        fprintf(out, "  %s : %s+%s\n",
            symbollist[i], funcname, begin_offset);
        }
        else {
        // demangling failed. Output function name as a C function with
        // no arguments.
        fprintf(out, "  %s : %s()+%s\n",
            symbollist[i], begin_name, begin_offset);
        }
    }
    else
    {
        // couldn't parse the line? print the whole line.
        fprintf(out, "  %s\n", symbollist[i]);
    }
    }

    free(funcname);
    free(symbollist);
}

#endif // _STACKTRACE_H_

আছে HTH!




3

আমারও অনুরূপ সমস্যা রয়েছে এবং যদিও আমি বহনযোগ্যতা পছন্দ করি তবে আমার কেবল জিসিসি সমর্থন দরকার। জিসিসিতে এক্সিকিউনফো এবং ব্যাকট্রেস কল উপলব্ধ। ফাংশনটির নামগুলি স্থিত করতে, মিঃ বিংম্যানের একটি দুর্দান্ত কোড রয়েছে। ব্যতিক্রমকে ব্যাকট্রেস ডাম্প করতে, আমি একটি ব্যতিক্রম তৈরি করি যা কনস্ট্রাক্টরে ব্যাকট্রেস মুদ্রণ করে। যদি আমি এটি একটি লাইব্রেরিতে নিক্ষিপ্ত ব্যতিক্রম নিয়ে কাজ করার আশা করছিলাম তবে এটি পুনর্নির্মাণ / লিঙ্কিংয়ের প্রয়োজন হতে পারে যাতে ব্যাকট্র্যাসিং ব্যতিক্রমটি ব্যবহৃত হয়।

/******************************************
#Makefile with flags for printing backtrace with function names
# compile with symbols for backtrace
CXXFLAGS=-g
# add symbols to dynamic symbol table for backtrace
LDFLAGS=-rdynamic
turducken: turducken.cc
******************************************/

#include <cstdio>
#include <stdexcept>
#include <execinfo.h>
#include "stacktrace.h" /* https://panthema.net/2008/0901-stacktrace-demangled/ */

// simple exception that prints backtrace when constructed
class btoverflow_error: public std::overflow_error
{
    public:
    btoverflow_error( const std::string& arg ) :
        std::overflow_error( arg )
    {
        print_stacktrace();
    };
};


void chicken(void)
{
    throw btoverflow_error( "too big" );
}

void duck(void)
{
    chicken();
}

void turkey(void)
{
    duck();
}

int main( int argc, char *argv[])
{
    try
    {
        turkey();
    }
    catch( btoverflow_error e)
    {
        printf( "caught exception: %s\n", e.what() );
    }
}

এটি জিসিসি ৪.৮.৪ দিয়ে সংকলন এবং চালনা করে অবিচ্ছিন্ন সি ++ ফাংশন নামের সাথে একটি ব্যাকট্রেস দেয়:

stack trace:
 ./turducken : btoverflow_error::btoverflow_error(std::string const&)+0x43
 ./turducken : chicken()+0x48
 ./turducken : duck()+0x9
 ./turducken : turkey()+0x9
 ./turducken : main()+0x15
 /lib/x86_64-linux-gnu/libc.so.6 : __libc_start_main()+0xf5
 ./turducken() [0x401629]

3

ক্যাচ ব্লকে প্রবেশ করার সময় যেহেতু স্ট্যাকটি ইতিমধ্যে অযাচিত, তাই আমার ক্ষেত্রে সমাধানটি ছিল কিছু ব্যতিক্রম না ধরা যা তারপরে একটি স্যাব্যাবআরটি বাড়ে। সিগ্যাবআরটি আই-এর জন্য সিগন্যাল হ্যান্ডলারের পরে কাঁটা () এবং এক্সিল () হয় জিডিবি (ডিবাগ বিল্ডে) বা গুগল ব্রেকপ্যাড স্ট্যাকওয়াক (রিলিজ বিল্ডে)। এছাড়াও আমি কেবল সিগন্যাল হ্যান্ডলার নিরাপদ ফাংশন ব্যবহার করার চেষ্টা করি।

, GDB:

static const char BACKTRACE_START[] = "<2>--- backtrace of entire stack ---\n";
static const char BACKTRACE_STOP[] = "<2>--- backtrace finished ---\n";

static char *ltrim(char *s)
{
    while (' ' == *s) {
        s++;
    }
    return s;
}

void Backtracer::print()
{
    int child_pid = ::fork();
    if (child_pid == 0) {
        // redirect stdout to stderr
        ::dup2(2, 1);

        // create buffer for parent pid (2+16+1 spaces to allow up to a 64 bit hex parent pid)
        char pid_buf[32];
        const char* stem = "                   ";
        const char* s = stem;
        char* d = &pid_buf[0];
        while (static_cast<bool>(*s))
        {
            *d++ = *s++;
        }
        *d-- = '\0';
        char* hexppid = d;

        // write parent pid to buffer and prefix with 0x
        int ppid = getppid();
        while (ppid != 0) {
            *hexppid = ((ppid & 0xF) + '0');
            if(*hexppid > '9') {
                *hexppid += 'a' - '0' - 10;
            }
            --hexppid;
            ppid >>= 4;
        }
        *hexppid-- = 'x';
        *hexppid = '0';

        // invoke GDB
        char name_buf[512];
        name_buf[::readlink("/proc/self/exe", &name_buf[0], 511)] = 0;
        ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_START[0], sizeof(BACKTRACE_START));
        (void)r;
        ::execl("/usr/bin/gdb",
                "/usr/bin/gdb", "--batch", "-n", "-ex", "thread apply all bt full", "-ex", "quit",
                &name_buf[0], ltrim(&pid_buf[0]), nullptr);
        ::exit(1); // if GDB failed to start
    } else if (child_pid == -1) {
        ::exit(1); // if forking failed
    } else {
        // make it work for non root users
        if (0 != getuid()) {
            ::prctl(PR_SET_PTRACER, PR_SET_PTRACER_ANY, 0, 0, 0);
        }
        ::waitpid(child_pid, nullptr, 0);
        ssize_t r = ::write(STDERR_FILENO, &BACKTRACE_STOP[0], sizeof(BACKTRACE_STOP));
        (void)r;
    }
}

minidump_stackwalk:

static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, void* context, bool succeeded)
{
    int child_pid = ::fork();
    if (child_pid == 0) {
        ::dup2(open("/dev/null", O_WRONLY), 2); // ignore verbose output on stderr
        ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_START[0], sizeof(MINIDUMP_STACKWALK_START));
        (void)r;
        ::execl("/usr/bin/minidump_stackwalk", "/usr/bin/minidump_stackwalk", descriptor.path(), "/usr/share/breakpad-syms", nullptr);
        ::exit(1); // if minidump_stackwalk failed to start
    } else if (child_pid == -1) {
        ::exit(1); // if forking failed
    } else {
        ::waitpid(child_pid, nullptr, 0);
        ssize_t r = ::write(STDOUT_FILENO, &MINIDUMP_STACKWALK_STOP[0], sizeof(MINIDUMP_STACKWALK_STOP));
        (void)r;
    }
    ::remove(descriptor.path()); // this is not signal safe anymore but should still work
    return succeeded;
}

সম্পাদনা: ব্রেকপ্যাডের জন্য এটি কাজ করতে আমাকে এটিও যুক্ত করতে হয়েছিল:

std::set_terminate([]()
{
    ssize_t r = ::write(STDERR_FILENO, EXCEPTION, sizeof(EXCEPTION));
    (void)r;
    google_breakpad::ExceptionHandler::WriteMinidump(std::string("/tmp"), dumpCallback, NULL);
    exit(1); // avoid creating a second dump by not calling std::abort
});

উত্স: লাইন নম্বর তথ্যের সাথে জিসিসি ব্যবহার করে সি ++ এর জন্য স্ট্যাক ট্রেস কীভাবে পাবেন? এবং কোনও ক্র্যাশ প্রক্রিয়াতে জিডিবি সংযুক্ত করা সম্ভব (ওরফে "ইন-টাইম-ইন-টাইম" ডিবাগিং)


2

পপি কেবল স্ট্যাক ট্রেসই নয়, প্যারামিটারের মানগুলি, স্থানীয় ভেরিয়েবলগুলিও সংগ্রহ করতে পারে - যা ক্রাশের দিকে নিয়ে যায় everything


2

নিম্নলিখিত কোডটি একটি ব্যতিক্রম ছোঁড়ার ঠিক পরে কার্যকর করা বন্ধ করে। আপনাকে একটি সমাপ্তি হ্যান্ডলারের সাথে একটি উইন্ডোজ_সেপশন_হ্যান্ডলার সেট করতে হবে। আমি এটি MinGW 32 বাইটে পরীক্ষা করেছি।

void beforeCrash(void);

static const bool SET_TERMINATE = std::set_terminate(beforeCrash);

void beforeCrash() {
    __asm("int3");
}

int main(int argc, char *argv[])
{
SetUnhandledExceptionFilter(windows_exception_handler);
...
}

উইন্ডোজ_সেপশন_হ্যান্ডলার ফাংশনের জন্য নিম্নলিখিত কোডটি পরীক্ষা করুন: http://www.codedisqus.com/0ziVPgVPUk/exception-handling-and-stacktrace-under-windows-mingwgcc.html


1

সিপিপি -সরঞ্জাম ex_diag - সহজ ওজন, একাধিক প্ল্যাটফর্ম, ন্যূনতম সংস্থান ব্যবহার করে, সহজ এবং ট্রেস এ নমনীয়।


আমি এই প্রকল্পটি 2017.12.24 এ দেখেছি, উত্স এবং অ্যাক্সেসযোগ্য উভয়ই ডাউনলোড করেছি।
zhaorufei

1
আমি সবেমাত্র কোড. google.com/archive/p/exception-diagnostic/source/default/… পরীক্ষা করেছি এবং উত্সটি ডাউনলোডযোগ্য। আপনি আরও একবার চেষ্টা করতে পারেন?
বরিস
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.