// ---------------------------------------------------------------------- // File: StackTrace.hh // Author: Andreas-Joachim Peters - CERN // ---------------------------------------------------------------------- /************************************************************************ * EOS - the CERN Disk Storage System * * Copyright (C) 2011 CERN/ASwitzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ //----------------------------------------------------------------------------- //! @brief Class providing readable stack traces using GDB //----------------------------------------------------------------------------- #ifndef __EOSCOMMON__STACKTRACE__HH #define __EOSCOMMON__STACKTRACE__HH #include "common/ShellCmd.hh" #include "common/Timing.hh" #include "common/StringConversion.hh" #include EOSCOMMONNAMESPACE_BEGIN static const std::string EOS_DEFAULT_STACKTRACE_PATH = "/var/eos/md/stacktrace"; //------------------------------------------------------------------------------ //! Static Class implementing comfortable readable stack traces //! To use this static functions include this header file //------------------------------------------------------------------------------ class StackTrace { public: //---------------------------------------------------------------------------- //! Construct gdb command //---------------------------------------------------------------------------- static std::string constructGdbCommand() { struct stat statbuf; if (stat("/opt/rh/devtoolset-8/root/usr/bin/gdb", &statbuf) == 0) { return "/opt/rh/devtoolset-8/root/usr/bin/gdb"; } if (stat("/opt/rh/devtoolset-7/root/usr/bin/gdb", &statbuf) == 0) { return "/opt/rh/devtoolset-7/root/usr/bin/gdb"; } if (stat("/opt/rh/devtoolset-6/root/usr/bin/gdb", &statbuf) == 0) { return "/opt/rh/devtoolset-6/root/usr/bin/gdb"; } return "gdb"; } //---------------------------------------------------------------------------- //! Create a readable back trace using gdb //---------------------------------------------------------------------------- static void GdbTrace(const char* executable, pid_t pid, const char* what, std::string file = EOS_DEFAULT_STACKTRACE_PATH, std::string* ret_dump = 0) { std::string exe; // Append timestamp to easily distinguish multiple failures if (file == EOS_DEFAULT_STACKTRACE_PATH) { auto now = std::time(nullptr); file += "-"; file += eos::common::Timing::UnixTimestamp_to_ISO8601(now); } if (!executable) { std::string procentry = "/proc/"; procentry += std::to_string(pid); procentry += "/exe"; char buf[4096]; ssize_t size_link = ::readlink(procentry.c_str(), buf, sizeof(buf)); if (size_link > 0) { exe.assign(buf, size_link); } } else { exe = executable; } fprintf(stderr, "#########################################################" "################\n"); fprintf(stderr, "# stack trace exec=%s pid=%u what='%s'\n", exe.c_str(), (unsigned int) pid, what); fprintf(stderr, "#########################################################" "################\n"); XrdOucString gdbline = "ulimit -v 10000000000; "; gdbline += constructGdbCommand().c_str(); gdbline += " --quiet "; gdbline += exe.c_str(); gdbline += " -p "; gdbline += (int) pid; gdbline += " <<< "; gdbline += "\""; gdbline += what; gdbline += "\" >&" ; gdbline += file.c_str(); eos::common::ShellCmd shelltrace(gdbline.c_str()); shelltrace.wait(120); std::string cat = "cat "; cat += file.c_str(); std::string gdbdump = StringConversion::StringFromShellCmd (cat.c_str()); if (ret_dump) { *ret_dump = gdbdump; } fprintf(stderr, "%s\n", gdbdump.c_str()); if (!strcmp("thread apply all bt", what)) { if (!ret_dump) { // We can extract the signal thread from all thread back traces GdbSignaledTrace(gdbdump); } } } //---------------------------------------------------------------------------- //! Extract the thread stack trace creating responsible signal //---------------------------------------------------------------------------- static void GdbSignaledTrace(std::string& trace) { // Analyze the trace until we find '' and extract // this trace std::vector lines; eos::common::StringConversion::Tokenize(trace, lines, "\n"); size_t thread_start = 0; size_t thread_stop = 0; size_t trace_start = 0; for (size_t i = 0; i < lines.size(); i++) { if (lines[i].substr(0, 6) == "Thread") { if (thread_start && trace_start) { thread_stop = i - 1; break; } thread_start = i; } if (lines[i].length() < 2) { thread_stop = i; if (trace_start) { break; } } if (lines[i].find("") != std::string::npos) { trace_start = i; } } if (!thread_stop) { thread_stop = lines.size() - 1; } if ((thread_start < trace_start) && (trace_start < thread_stop)) { fprintf(stderr, "#######################################################" "##################\n"); fprintf(stderr, "# -----------------------------------------------------" "------------------\n"); fprintf(stderr, "# Responsible thread =>\n"); fprintf(stderr, "# -----------------------------------------------------" "------------------\n"); fprintf(stderr, "# %s\n", lines[thread_start].c_str()); fprintf(stderr, "#######################################################" "##################\n"); for (size_t l = trace_start; l <= thread_stop; ++l) { fprintf(stderr, "%s\n", lines[l].c_str()); } } else { fprintf(stderr, "#######################################################" "##################\n"); fprintf(stderr, "# warning: failed to parse the thread responsible for signal [%u %u %u]\n", (unsigned int)thread_start, (unsigned int)trace_start, (unsigned int)thread_stop); fprintf(stderr, "#######################################################" "##################\n"); } } }; EOSCOMMONNAMESPACE_END #endif