Array stackTraceToBackTrace(const StackTrace& st) { std::vector<void*> bt_pointers; st.get(bt_pointers); Array ret; if (RuntimeOption::FullBacktrace) { for (unsigned int i = 0; i < bt_pointers.size(); i++) { StackTrace::FramePtr f = StackTrace::Translate(bt_pointers[i]); if (RuntimeOption::TranslateSource) { SourceInfo::TheSourceInfo.translate(f); } Array frame; frame.set("file", String(f->filename)); frame.set("line", f->lineno); frame.set("function", String(f->funcname)); frame.set("args", ""); frame.set("bt", (int64)bt_pointers[i]); ret.append(frame); } } else { for (unsigned int i = 0; i < bt_pointers.size(); i++) { Array frame; frame.set("file", ""); frame.set("line", 0LL); frame.set("function", ""); frame.set("args", ""); frame.set("bt", (int64)bt_pointers[i]); ret.append(frame); } ret.set("bts", String(st.hexEncode())); } return ret; }
Logger::LogStream Logger::createStream(const string &_domain, int level, const std::string &_prefix) { string domain = simplifyDomain(_domain); if (enabled(domain, level)) { // Log date periodically uint64_t now = Time::now(); if (logDatePeriodically && lastDate + logDatePeriodically <= now) { write(String::bar(Time(now, "Date: %Y-%m-%d").toString()) + (logCRLF ? "\r\n" : "\n")); lastDate = now; } string prefix = startColor(level) + getHeader(domain, level) + _prefix; string suffix = endColor(level); string trailer; #ifdef HAVE_DEBUGGER if (domainTraces.find(domain) != domainTraces.end()) { StackTrace trace; Debugger::instance().getStackTrace(trace); for (int i = 0; i < 3; i++) trace.pop_front(); trailer = SSTR(trace); } #endif return new cb::LogStream(prefix, suffix, trailer); } else return new NullStream<>; }
static void LogStacktrace(const int logLevel, StackTrace& stacktrace) { int colFileline = 0; const std::string& exe_path = Platform::GetProcessExecutablePath(); const std::string& cwd_path = Platform::GetOrigCWD(); for (auto fit = stacktrace.begin(); fit != stacktrace.end(); fit++) { for (auto eit = fit->entries.begin(); eit != fit->entries.end(); eit++) { eit->abbrev_funcname = eit->funcname; std::string fileline = eit->fileline; if (fileline[1] == '?') { // case "??:?", ":?" fileline = fit->symbol; // print raw backtrace_symbol } eit->abbrev_fileline = fileline; int abbrev_start = 0; if (fileline[0] == '/') { // See if we can shorten the file/line bit by removing the common path if (CommonStringLength(fileline, exe_path, &abbrev_start) > 1) { // i.e. one char for first '/' eit->abbrev_fileline = std::string(".../") + fileline.substr(abbrev_start, std::string::npos); } else if (CommonStringLength(fileline, cwd_path, &abbrev_start) > 1) { eit->abbrev_fileline = std::string("./") + fileline.substr(abbrev_start, std::string::npos); } } if (eit->abbrev_funcname.size() > 100) { eit->abbrev_funcname.resize(94); eit->abbrev_funcname.append(" [...]"); } colFileline = std::max(colFileline, (int)eit->abbrev_fileline.length()); } } bool hideSignalHandler = true; // Print out the translated StackTrace unsigned numLine = 0; unsigned hiddenLines = 0; while (numLine == 0) { // outer loop at most twice -- tries to find the signal handler once and if that doesn't work, then just print every frame for (auto fit = stacktrace.cbegin(); fit != stacktrace.cend(); fit++) { for (auto eit = fit->entries.begin(); eit != fit->entries.end(); eit++) { if (hideSignalHandler) { hiddenLines++; if (eit->fileline.find("sigaction.c:?") == 0) { hideSignalHandler = false; LOG_I(logLevel, "(Signal handler calls suppressed [%d]. Inlined calls denoted by < > brackets.)", hiddenLines); } continue; } if (eit->inLine) { LOG_I(logLevel, " <%02u> %*s %s", fit->level, colFileline, eit->abbrev_fileline.c_str(), eit->abbrev_funcname.c_str()); } else { LOG_I(logLevel, "[%02u] %*s %s", fit->level, colFileline, eit->abbrev_fileline.c_str(), eit->abbrev_funcname.c_str()); } numLine++; } } hideSignalHandler = false; } }
String debug_string_backtrace(bool skip) { if (RuntimeOption::InjectedStackTrace) { Array bt; StringBuffer buf; bt = g_vmContext->debugBacktrace(skip); int i = 0; for (ArrayIter it = bt.begin(); !it.end(); it.next(), i++) { Array frame = it.second().toArray(); buf.append('#'); buf.append(i); if (i < 10) buf.append(' '); buf.append(' '); if (frame.exists(s_class)) { buf.append(frame->get(s_class).toString()); buf.append(frame->get(s_type).toString()); } buf.append(frame->get(s_function).toString()); buf.append("()"); if (frame.exists(s_file)) { buf.append(" called at ["); buf.append(frame->get(s_file).toString()); buf.append(':'); buf.append(frame->get(s_line).toString()); buf.append(']'); } buf.append('\n'); } return buf.detach(); } else { StackTrace st; return String(st.toString()); } }
void f_debug_print_backtrace() { if (RuntimeOption::InjectedStackTrace) { Array bt = FrameInjection::GetBacktrace(true); int i = 0; for (ArrayIter it = bt.begin(); !it.end(); it.next(), i++) { Array frame = it.second().toArray(); StringBuffer buf; buf.append('#'); buf.append(i); if (i < 10) buf.append(' '); buf.append(' '); if (frame.exists("class")) { buf.append(frame->get("class").toString()); buf.append(frame->get("type").toString()); } buf.append(frame->get("function").toString()); buf.append("()"); if (frame.exists("file")) { buf.append(" called at ["); buf.append(frame->get("file").toString()); buf.append(':'); buf.append(frame->get("line").toString()); buf.append(']'); } buf.append('\n'); echo(buf.detach()); } } else { StackTrace st; echo(String(st.toString())); } }
LONG WINAPI WindowsExceptionHandler(struct _EXCEPTION_POINTERS *exceptionInfo) { LD_UNUSED_PARAMETER(exceptionInfo); LD_LOG(Platform, Error, "~~~~~~~~~~~~ UNHANDLED EXCEPTION OCCURRED ~~~~~~~~~~~"); LD_LOG(Platform, Error, "Stack Trace:"); StackTrace Trace; if (StackTrace::Generate(Trace, (void*)exceptionInfo).Failed()) { LD_LOG(Platform, Error, "Failed to generate stack trace."); return 0; } else { for (int i = 0; i < Trace.FrameCount(); i++) { StackFrame Frame = Trace.GetFrame(i); if (Frame.Resolve().Failed()) { LD_LOGF(Platform, Error, "[%i] 0x%p (Symbols Unresolved)", i, Frame.Address); } else { LD_LOGF(Platform, Error, "[%i] (%s:%i) %s", i, Frame.File.Data(), Frame.Line, Frame.Function.Data()); } } } return EXCEPTION_EXECUTE_HANDLER; }
bool ThreadHandler::_HandleSingleStepStep(CpuState* cpuState) { TRACE_CONTROL("ThreadHandler::_HandleSingleStepStep(): ip: %llx\n", cpuState->InstructionPointer()); switch (fStepMode) { case STEP_INTO: { // We continue stepping as long as we're in the statement. if (fStepStatement->ContainsAddress(cpuState->InstructionPointer())) { _SingleStepThread(cpuState->InstructionPointer()); return true; } StackTrace* stackTrace = fThread->GetStackTrace(); BReference<StackTrace> stackTraceReference(stackTrace); if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace != NULL) { StackFrame* frame = stackTrace->FrameAt(0); Image* image = frame->GetImage(); ImageDebugInfo* info = NULL; if (GetImageDebugInfo(image, info) != B_OK) return false; BReference<ImageDebugInfo>(info, true); if (info->GetAddressSectionType( cpuState->InstructionPointer()) == ADDRESS_SECTION_TYPE_PLT) { _SingleStepThread(cpuState->InstructionPointer()); return true; } } return false; } case STEP_OVER: { // If we have stepped out of the statement, we're done. if (!fStepStatement->ContainsAddress(cpuState->InstructionPointer())) return false; return _DoStepOver(cpuState); } case STEP_OUT: // We never single-step in this case. default: return false; } }
//! @brief is called when the app crashes void SpringLobbyApp::OnFatalException() { wxLogError("Fatal exception!"); StackTrace stackTracer; stackTracer.WalkFromException(); auto trace = stackTracer.GetStackTrace(); wxLogError("Stack trace: " + trace); }
void print_backtrace(std::size_t frames = 32) { using namespace backward; StackTrace stackTrace; Printer printer; stackTrace.load_here(frames); printer.object = true; printer.color = true; printer.print(stackTrace, stdout); }
LockProfiler::~LockProfiler() { if (m_profiling) { timespec unlockTime; Timer::GetMonotonicTime(unlockTime); time_t dsec = unlockTime.tv_sec - m_lockTime.tv_sec; long dnsec = unlockTime.tv_nsec - m_lockTime.tv_nsec; int64_t dusec = dsec * 1000000 + dnsec / 1000; StackTrace st; s_pfunc_profile(st.hexEncode(3, 9), dusec); } }
status_t DebugReportGenerator::_DumpDebuggedThreadInfo(BString& _output, ::Thread* thread) { AutoLocker< ::Team> locker; if (thread->State() != THREAD_STATE_STOPPED) return B_OK; StackTrace* trace = NULL; for (;;) { trace = thread->GetStackTrace(); if (trace != NULL) break; locker.Unlock(); status_t result = acquire_sem(fTeamDataSem); if (result != B_OK) return result; locker.Lock(); } _output << "\t\tFrame\t\tIP\t\t\tFunction Name\n"; _output << "\t\t-----------------------------------------------\n"; BString data; for (int32 i = 0; StackFrame* frame = trace->FrameAt(i); i++) { char functionName[512]; data.SetToFormat("\t\t%#08" B_PRIx64 "\t%#08" B_PRIx64 "\t%s\n", frame->FrameAddress(), frame->InstructionPointer(), UiUtils::FunctionNameForFrame(frame, functionName, sizeof(functionName))); _output << data; } _output << "\n\t\tRegisters:\n"; CpuState* state = thread->GetCpuState(); BVariant value; const Register* reg = NULL; for (int32 i = 0; i < fArchitecture->CountRegisters(); i++) { reg = fArchitecture->Registers() + i; state->GetRegisterValue(reg, value); char buffer[64]; data.SetToFormat("\t\t\t%5s:\t%s\n", reg->Name(), UiUtils::VariantToString(value, buffer, sizeof(buffer))); _output << data; } return B_OK; }
/** * * This entry point is tailored for the Watchdog module. * Since the thread to be traced may be running, it requires a ThreadControls object in order to suspend/resume the thread. * @brief RemoteStacktrace */ void SuspendedStacktrace(Threading::ThreadControls* ctls, const std::string& threadName) { #if !(DEDICATED || UNIT_TEST) Watchdog::ClearTimer(); #endif assert(ctls != nullptr); assert(ctls->handle != 0); assert(threadName.size() > 0); LOG_L(L_WARNING, "Suspended-thread Stacktrace (%s) for Spring %s:", threadName.c_str(), (SpringVersion::GetFull()).c_str()); LOG_L(L_DEBUG, "SuspendedStacktrace[1]"); StackTrace stacktrace; // Get untranslated stacktrace symbols { // process and analyse the raw stack trace void* iparray[MAX_STACKTRACE_DEPTH]; ctls->Suspend(); const int numLines = thread_unwind(&ctls->ucontext, iparray, stacktrace); ctls->Resume(); LOG_L(L_DEBUG, "SuspendedStacktrace[2]"); if(numLines > MAX_STACKTRACE_DEPTH) { LOG_L(L_ERROR, "thread_unwind returned more lines than we allotted space for!"); } char** lines = backtrace_symbols(iparray, numLines); // give them meaningfull names ExtractSymbols(lines, stacktrace); } if (stacktrace.empty()) { LOG_L(L_WARNING, " Unable to create suspended stacktrace"); return; } LOG_L(L_DEBUG, "SuspendedStacktrace[3]"); // Translate symbols into code line numbers TranslateStackTrace(NULL, stacktrace, LOG_LEVEL_WARNING); LOG_L(L_DEBUG, "SuspendedStacktrace[4]"); // Print out the translated StackTrace LogStacktrace(LOG_LEVEL_WARNING, stacktrace); }
String debug_string_backtrace(bool skip, bool ignore_args /* = false */, int limit /* = 0 */) { if (RuntimeOption::InjectedStackTrace) { Array bt; StringBuffer buf; bt = g_context->debugBacktrace(skip, false, false, nullptr, ignore_args, limit); int i = 0; for (ArrayIter it = bt.begin(); !it.end(); it.next(), i++) { Array frame = it.second().toArray(); buf.append('#'); buf.append(i); if (i < 10) buf.append(' '); buf.append(' '); if (frame.exists(s_class)) { buf.append(frame->get(s_class).toString()); buf.append(frame->get(s_type).toString()); } buf.append(frame->get(s_function).toString()); buf.append("("); if (!ignore_args) { bool first = true; for (ArrayIter it = frame->get(s_args).begin(); !it.end(); it.next()) { if (!first) { buf.append(", "); } else { first = false; } try { buf.append(it.second().toString()); } catch (FatalErrorException& fe) { buf.append(fe.getMessage()); } } } buf.append(")"); if (frame.exists(s_file)) { buf.append(" called at ["); buf.append(frame->get(s_file).toString()); buf.append(':'); buf.append(frame->get(s_line).toString()); buf.append(']'); } buf.append('\n'); } return buf.detach(); } else { StackTrace st; return String(st.toString()); } }
void HeapProfiler::malloc(void *ptr, size_t size, const StackTrace &trace){ std::lock_guard<std::mutex> lk(mutex); fprintf(data.output, "+ %lx %lx %lx\n", size, trace.index(), reinterpret_cast<uintptr_t>(ptr)); data.tick(); // Store the stracktrace hash of this allocation in the pointers map. ptrs[ptr] = trace.hash; }
/** * Consumes (and frees) the lines produced by backtrace_symbols and puts these into the StackTrace fields */ static void ExtractSymbols(char** lines, StackTrace& stacktrace) { int l=0; auto fit = stacktrace.begin(); while (fit != stacktrace.end()) { LOG_L(L_DEBUG, "backtrace_symbols: %s", lines[l]); if (strncmp(lines[l], "[(nil)]", 20) != 0) { fit->symbol = lines[l]; fit++; } else { fit = stacktrace.erase(fit); } l++; } free(lines); }
void RequestError::segfault(Request* request, Exceptions::Segfault& e) { std::cerr << std::endl << " \033[31;1m******** SEGMENTATION FAULT ********\033[0m" << std::endl << std::endl << "WebCpp received a SIGSEGV signal: segmentation fault." << std::endl << std::endl << "This should never happen. Please file a bug to the WebCpp's " << std::endl << "developers on <http://bugs.webcpp.org/>." << std::endl << std::endl << "\033[33;1m*** STACK TRACE ***\033[0m" << std::endl << std::endl; StackTrace* trace = new StackTrace(2); List<StackTrace::Frame> frames = trace->stackFrames(); int i = 0, iLen = std::ceil(std::log10(frames.count())); for (const StackTrace::Frame& frame : frames) { std::cerr << " " << std::setw(iLen) << i++ << ". " << "\033[34;1m" << frame.function << "\033[0m" << " + 0x" << String::fromNumber(frame.functionOffset, 16) << " @ 0x" << String::fromNumber(frame.address, 16) << std::endl << std::setw(iLen + 6) << " " << "Sources/System.cpp line 112" << " (from " << frame.object.absolutePath() << ")" << std::endl; } delete trace; std::cerr << std::endl; try { View tpl = new Template("Sys::Segfault.html", nullptr, request); tpl["webcppVersion"] = System::get()->version(); tpl["serverName"] = request->env("SERVER_NAME", ""); loadStackTrace(e, dynamic_cast<Template*>(*tpl)); if (!request->headersSent()) request->setStatus(500); tpl->render(); } catch (const Exception& e2) { fallbackErrorPage(request, e, e2.type()); } catch (const std::exception& e2) { fallbackErrorPage(request, e2, typeid(e2).name()); } }
/** * This stack trace is tailored for the SIGSEGV / SIGILL / SIGFPE etc signal handler. * The thread to be traced is usually in a halted state, but the signal handler can provide siginfo_t and ucontext_t structures to help produce the trace using libunwind. * @brief PrepareStacktrace */ void HaltedStacktrace(const std::string& errstr, siginfo_t* siginfo, ucontext_t* ucontext) { LOG_L(L_ERROR, "Halted Stacktrace for Spring %s using libunwind:", (SpringVersion::GetFull()).c_str()); assert(siginfo != nullptr); assert(ucontext != nullptr); StackTrace stacktrace; LOG_L(L_DEBUG, "HaltedStacktrace[1]"); // Get untranslated stacktrace symbols { // process and analyse the raw stack trace void* iparray[MAX_STACKTRACE_DEPTH]; const int numLines = thread_unwind(nullptr, iparray, stacktrace); LOG_L(L_DEBUG, "HaltedStacktrace[2]"); if(numLines > MAX_STACKTRACE_DEPTH) { LOG_L(L_ERROR, "thread_unwind returned more lines than we allotted space for!"); } char** lines = backtrace_symbols(iparray, numLines); // give them meaningfull names ExtractSymbols(lines, stacktrace); } LOG_L(L_DEBUG, "HaltedStacktrace[3]"); if (stacktrace.empty()) { LOG_I(LOG_LEVEL_ERROR, " Unable to create stacktrace"); return; } // Translate it TranslateStackTrace(NULL, stacktrace, LOG_LEVEL_ERROR); LOG_L(L_DEBUG, "HaltedStacktrace[4]"); // Print out the translated StackTrace. Ignore the frames that occur inside the signal handler (before its line in the trace) -- they are likely some kind of padding or just garbage. LogStacktrace(LOG_LEVEL_ERROR, stacktrace); }
void CliStackTraceCommand::Execute(int argc, const char* const* argv, CliContext& context) { // get the current thread Team* team = context.GetTeam(); AutoLocker<Team> teamLocker(team); Thread* thread = context.CurrentThread(); if (thread == NULL) { printf("no current thread\n"); return; } if (thread->State() != THREAD_STATE_STOPPED) { printf("Current thread is not stopped. Can't get stack trace.\n"); return; } // get its stack trace StackTrace* stackTrace = thread->GetStackTrace(); while (stackTrace == NULL) { context.WaitForEvents(CliContext::EVENT_THREAD_STACK_TRACE_CHANGED); if (context.IsTerminating()) return; stackTrace = thread->GetStackTrace(); } BReference<StackTrace> stackTraceReference(stackTrace); // hold a reference until we're done teamLocker.Unlock(); // print the stack trace int32 frameCount = stackTrace->CountFrames(); for (int32 i = 0; i < frameCount; i++) { StackFrame* frame = stackTrace->FrameAt(i); printf("%3" B_PRId32 " %#" B_PRIx64 " %#" B_PRIx64, i, (uint64)frame->FrameAddress(), (uint64)frame->InstructionPointer()); char functionName[512]; UiUtils::FunctionNameForFrame(frame, functionName, sizeof(functionName)); printf(" %s\n", functionName); } }
static inline bool get_frame_reg(StackTrace& trace, size_t regIndex, reg_t& r) { switch (regIndex) { case PT_R1: r = CHKPTR(trace.selection())->stack_pointer(); return true; case PT_R31: r = CHKPTR(trace.selection())->frame_pointer(); return true; case PT_NIP: r = CHKPTR(trace.selection())->program_count(); return true; } return false; }
void StructuredLogEntry::setStackTrace(folly::StringPiece key, const StackTrace& st) { std::vector<folly::StringPiece> stackFrames; folly::split("\n", st.toString(), stackFrames); for (auto& frame : stackFrames) { const char* p = frame.begin(); while (*p == '#' || *p == ' ' || (*p >= '0' && *p <= '9')) ++p; frame = folly::StringPiece(p, frame.end()); } setVec(key, stackFrames); }
void SimpleCounter::Count(const string &name) { if (Enabled) { int count = ++s_counter->m_counters[name]; if (SampleStackCount > 0) { assert(StackTrace::Enabled); vector<string> &stackVec = s_counter->m_stacks[name]; if ((int)stackVec.size() < SampleStackCount || f_rand(0, count - 1) < SampleStackCount) { StackTrace st; if ((int)stackVec.size() < SampleStackCount) { // skip StackTrace methods and the Count() call. stackVec.push_back(st.hexEncode(3, 3 + SampleStackDepth)); } else { // skip StackTrace methods and the Count() call. stackVec[f_rand(0, SampleStackCount - 1)] = st.hexEncode(3, 3 + SampleStackDepth); } } } } }
void StackTraceImpl::copy_symbols(const StackTrace& other) { // todo: work it from the bottom, and break out of // the loop as soon as the frames don't match const size_t n = std::min(frames_.size(), other.size()); for (size_t i = 0; i != n; ++i) { Frame* f = other.frame(i); Frame* g = frames_[i].get(); if (f->program_count() != g->program_count()) { continue; } if (Symbol* sym = f->function(no_symbol)) { g->function(sym); } } }
static void Stacktrace(bool* aiCrash, pthread_t* hThread = NULL, const char* threadName = NULL, const int logLevel = LOG_LEVEL_ERROR) { #if !(DEDICATED || UNIT_TEST) Watchdog::ClearTimer(); #endif if (threadName != NULL) { LOG_I(logLevel, "Stacktrace (%s) for Spring %s:", threadName, (SpringVersion::GetFull()).c_str()); } else { LOG_I(logLevel, "Stacktrace for Spring %s:", (SpringVersion::GetFull()).c_str()); } StackTrace stacktrace; // Get untranslated stacktrace symbols { // process and analyse the raw stack trace void* iparray[MAX_STACKTRACE_DEPTH]; int numLines = thread_unwind(nullptr, iparray, stacktrace); if (numLines > MAX_STACKTRACE_DEPTH) { LOG_L(L_ERROR, "thread_unwind returned more lines than we allotted space for!"); } char** lines = backtrace_symbols(iparray, numLines); // give them meaningfull names ExtractSymbols(lines, stacktrace); } if (stacktrace.empty()) { LOG_I(logLevel, " Unable to create stacktrace"); return; } // Translate it TranslateStackTrace(aiCrash, stacktrace, logLevel); LogStacktrace(logLevel, stacktrace); }
LockProfiler::~LockProfiler() { if (m_profiling) { #if defined(__APPLE__) timeval unlockTime; unlockTime.tv_sec = 0; unlockTime.tv_usec = 0; gettimeofday(&unlockTime, NULL); time_t dsec = unlockTime.tv_sec - m_lockTime.tv_sec; long dnsec = unlockTime.tv_usec - m_lockTime.tv_usec; int64 dusec = dsec * 1000000 + dnsec; #else timespec unlockTime; unlockTime.tv_sec = 0; unlockTime.tv_nsec = 0; clock_gettime(CLOCK_MONOTONIC, &unlockTime); time_t dsec = unlockTime.tv_sec - m_lockTime.tv_sec; long dnsec = unlockTime.tv_nsec - m_lockTime.tv_nsec; int64 dusec = dsec * 1000000 + dnsec / 1000; #endif StackTrace st; s_pfunc_profile(st.hexEncode(3, 9), dusec); } }
void CrashReport::GenerateReport(EXCEPTION_POINTERS* p) #endif { wxLogMessage( _T( "Report generated in " ) ); CwdGuard cwgGuard( wxFileName::GetTempDir() ); NetDebugReport* report = new NetDebugReport( "http://debug.springlobby.info/upload" ) ; // NetDebugReport* report = new NetDebugReport( "http://localhost/upload" ) ; // add all standard files: currently this means just a minidump and an // XML file with system info and stack trace report->AddAll( ); wxString dir = report->GetDirectory(); wxString SystemInfos; SystemInfos += _T( "SpringLobby version " ) + TowxString(GetSpringLobbyVersion()) + _T( "\n" ) ; SystemInfos += _T( "Built from " ) + wxString( wxVERSION_STRING ) + _T(" on ") + wxPlatformInfo::Get().GetOperatingSystemIdName() + _T( "\n" ) ; report->AddText( _T( "SystemInfos.txt" ), SystemInfos, _( "System informations" ) ); #if wxUSE_STD_IOSTREAM report->AddText( _T( "AppLog.txt" ), TowxString( crashlog.str() ), _( "Application verbose log" ) ); #endif wxString script_file = TowxString(SlPaths::GetDataDir()) + wxFileName::GetPathSeparator() + _T("script.txt"); if ( wxFile::Exists( script_file ) ) report->AddFile( script_file, _( "Last generated spring launching script" ) ); #if wxUSE_STACKWALKER && !__WXMSW__ StackTrace stacktrace; stacktrace.Walk( 3, 20 ); report->AddText( _T( "stacktrace.txt" ), _T("Call stack:\n") + stacktrace.GetStackTrace(), _( "StackTrace" ) ); #else wxString report_fn = ( wxGetCwd() + wxFileName::GetPathSeparator() + _T("stacktrace.txt") ); if ( wxFile::Exists( report_fn ) ) wxRemoveFile( report_fn ); wxCharBuffer report_fn_char = report_fn.mb_str(); DrMingwGenerateStacktrace( p, (const char*)report_fn_char ); report->AddFile( report_fn, _( "StackTrace" ) ); #endif wxString SlBuildFlags; #ifdef DISABLE_SOUND SlBuildFlags += _T("sound=0"); #else SlBuildFlags += _T("sound=1"); #endif report->AddText( _T("buildflags.txt"), SlBuildFlags, _T("BuildFlags") ); report->AddText( _T("nick.txt"), sett().GetServerAccountNick( sett().GetDefaultServer() ), _T("Nick") ); wxDebugReportPreviewStd* bkl = new wxDebugReportPreviewStd(); // calling Show() is not mandatory, but is more polite if ( bkl->Show( *report ) ) { if ( report->Process() ) { wxLogMessage( _T( "Report successfully uploaded." ) ); } else { wxLogMessage( _T( "Report generated in \"%s\", but failed to upload" ), report->GetCompressedFileName().c_str() ); report->Reset(); wxLogMessage( _T("report reset") ); } } //else: user cancelled the report delete report; wxLogMessage( _T("deleted report") ); }
/** * This is used to obtain the list of symbols using a ucontext_t structure. * So, it is used by both the HaltedStacktrace and the SuspendedStacktrace. * Since this method is pure implementation, it's */ int thread_unwind(ucontext_t* uc, void** iparray, StackTrace& stacktrace) { assert(iparray != nullptr); assert(&stacktrace != nullptr); unw_cursor_t cursor; // Effective ucontext_t. If uc not supplied, use unw_getcontext locally. This is appropriate inside signal handlers. #if defined(__APPLE__) unw_context_t thisctx; unw_getcontext(&thisctx); #else ucontext_t thisctx; if (uc == nullptr) { unw_getcontext(&thisctx); uc = &thisctx; } #endif const int BUFR_SZ = 1000; char procbuffer[BUFR_SZ]; stacktrace.clear(); stacktrace.reserve(120); /* * Note: documentation seems to indicate that uc_link contains a pointer to a "successor" context * that is to be resumed after the current one (as you might expect in a signal handler). * In practice however, the Linux OS seems to re-use the existing context of a thread, and so this approach * does not work. The uc_link is sometimes non-NULL however, but I do not know what the relation is to the * target context (uc). * while (uc->uc_link) { uc = uc->uc_link; LOG_L(L_DEBUG, "Dereferencing uc_link"); } */ #if defined(__APPLE__) int err = unw_init_local(&cursor, &thisctx); #else int err = unw_init_local(&cursor, uc); #endif if (err) { LOG_L(L_ERROR, "unw_init_local returned %d", err); return 0; } int i=0; while (i < MAX_STACKTRACE_DEPTH && unw_step(&cursor)) { StackFrame frame; unw_word_t ip; unw_word_t offp; unw_get_reg(&cursor, UNW_REG_IP, &ip); frame.ip = reinterpret_cast<void*>(ip); frame.level = i; iparray[i] = frame.ip; if (!unw_get_proc_name(&cursor, procbuffer, BUFR_SZ-1, &offp)) { frame.mangled = std::string(procbuffer); } else { frame.mangled = std::string("UNW_ENOINFO"); } stacktrace.push_back(frame); i++; } stacktrace.resize(i); LOG_L(L_DEBUG, "thread_unwind returned %d frames", i); return i; }
status_t Architecture::CreateStackTrace(Team* team, ImageDebugInfoProvider* imageInfoProvider, CpuState* cpuState, StackTrace*& _stackTrace, int32 maxStackDepth, bool useExistingTrace) { BReference<CpuState> cpuStateReference(cpuState); StackTrace* stackTrace = NULL; ObjectDeleter<StackTrace> stackTraceDeleter; StackFrame* frame = NULL; if (useExistingTrace) stackTrace = _stackTrace; else { // create the object stackTrace = new(std::nothrow) StackTrace; if (stackTrace == NULL) return B_NO_MEMORY; stackTraceDeleter.SetTo(stackTrace); } // if we're passed an already existing partial stack trace, // attempt to continue building it from where it left off. if (stackTrace->CountFrames() > 0) { frame = stackTrace->FrameAt(stackTrace->CountFrames() - 1); cpuState = frame->GetCpuState(); } while (cpuState != NULL) { // get the instruction pointer target_addr_t instructionPointer = cpuState->InstructionPointer(); if (instructionPointer == 0) break; // get the image for the instruction pointer AutoLocker<Team> teamLocker(team); Image* image = team->ImageByAddress(instructionPointer); BReference<Image> imageReference(image); teamLocker.Unlock(); // get the image debug info ImageDebugInfo* imageDebugInfo = NULL; if (image != NULL) imageInfoProvider->GetImageDebugInfo(image, imageDebugInfo); BReference<ImageDebugInfo> imageDebugInfoReference(imageDebugInfo, true); // get the function teamLocker.Lock(); FunctionInstance* function = NULL; FunctionDebugInfo* functionDebugInfo = NULL; if (imageDebugInfo != NULL) { function = imageDebugInfo->FunctionAtAddress(instructionPointer); if (function != NULL) functionDebugInfo = function->GetFunctionDebugInfo(); } BReference<FunctionInstance> functionReference(function); teamLocker.Unlock(); // If the CPU state's instruction pointer is actually the return address // of the next frame, we let the architecture fix that. if (frame != NULL && frame->ReturnAddress() == cpuState->InstructionPointer()) { UpdateStackFrameCpuState(frame, image, functionDebugInfo, cpuState); } // create the frame using the debug info StackFrame* previousFrame = NULL; CpuState* previousCpuState = NULL; if (function != NULL) { status_t error = functionDebugInfo->GetSpecificImageDebugInfo() ->CreateFrame(image, function, cpuState, previousFrame, previousCpuState); if (error != B_OK && error != B_UNSUPPORTED) break; } // If we have no frame yet, let the architecture create it. if (previousFrame == NULL) { status_t error = CreateStackFrame(image, functionDebugInfo, cpuState, frame == NULL, previousFrame, previousCpuState); if (error != B_OK) break; } cpuStateReference.SetTo(previousCpuState, true); previousFrame->SetImage(image); previousFrame->SetFunction(function); if (!stackTrace->AddFrame(previousFrame)) { delete previousFrame; return B_NO_MEMORY; } frame = previousFrame; cpuState = previousCpuState; if (--maxStackDepth == 0) break; } stackTraceDeleter.Detach(); _stackTrace = stackTrace; return B_OK; }
void ThreadHandler::HandleThreadAction(uint32 action, target_addr_t address) { AutoLocker<Team> locker(fThread->GetTeam()); if (fThread->State() == THREAD_STATE_UNKNOWN) return; // When stop is requested, thread must be running, otherwise stopped. if (action == MSG_THREAD_STOP ? fThread->State() != THREAD_STATE_RUNNING : fThread->State() != THREAD_STATE_STOPPED) { return; } // When stepping we need a stack trace. Save it before unsetting the state. CpuState* cpuState = fThread->GetCpuState(); StackTrace* stackTrace = fThread->GetStackTrace(); BReference<CpuState> cpuStateReference(cpuState); BReference<StackTrace> stackTraceReference(stackTrace); if (action == MSG_THREAD_SET_ADDRESS) { _HandleSetAddress(cpuState, address); return; } // When continuing the thread update thread state before actually issuing // the command, since we need to unlock. if (action != MSG_THREAD_STOP) { _SetThreadState(THREAD_STATE_RUNNING, NULL, THREAD_STOPPED_UNKNOWN, BString()); } locker.Unlock(); switch (action) { case MSG_THREAD_RUN: fStepMode = address != 0 ? STEP_UNTIL : STEP_NONE; if (address != 0) _InstallTemporaryBreakpoint(address); _RunThread(0); return; case MSG_THREAD_STOP: fStepMode = STEP_NONE; if (fDebuggerInterface->StopThread(ThreadID()) == B_OK) fThread->SetStopRequestPending(); return; case MSG_THREAD_STEP_OVER: case MSG_THREAD_STEP_INTO: case MSG_THREAD_STEP_OUT: break; } TRACE_CONTROL("ThreadHandler::HandleThreadAction(MSG_THREAD_STEP_*)\n"); // We want to step. We need a stack trace for that purpose. If we don't // have one yet, get it. Start with the CPU state. if (stackTrace == NULL && cpuState == NULL) { if (fDebuggerInterface->GetCpuState(fThread->ID(), cpuState) == B_OK) cpuStateReference.SetTo(cpuState, true); } if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1, false, false) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace == NULL || stackTrace->CountFrames() == 0) { _StepFallback(); return; } StackFrame* frame = stackTrace->FrameAt(0); TRACE_CONTROL(" ip: %#" B_PRIx64 "\n", frame->InstructionPointer()); target_addr_t frameIP = frame->GetCpuState()->InstructionPointer(); // When the thread is in a syscall, do the same for all step kinds: Stop it // when it returns by means of a breakpoint. if (frame->Type() == STACK_FRAME_TYPE_SYSCALL) { // set a breakpoint at the CPU state's instruction pointer (points to // the return address, unlike the stack frame's instruction pointer) // TODO: This is doesn't work correctly anymore. When stepping over a "syscall" // instruction the thread is stopped twice. The after the first step the PC is // incorrectly shown at the "syscall" instruction. Then we step again and are // stopped at the temporary breakpoint after the "syscall" instruction. There // are two problems. The first one is that we don't (cannot?) discriminate // between the thread being in a syscall (like in a blocking syscall) and the // thread having been stopped (or singled-stepped) at the end of the syscall. // The second issue is that the temporary breakpoint is probably not necessary // anymore, since single-stepping over "syscall" instructions should just work // as expected. status_t error = _InstallTemporaryBreakpoint(frameIP); if (error != B_OK) { _StepFallback(); return; } fStepMode = STEP_OUT; _RunThread(frameIP); return; } // For "step out" just set a temporary breakpoint on the return address. if (action == MSG_THREAD_STEP_OUT) { status_t error = _InstallTemporaryBreakpoint(frame->ReturnAddress()); if (error != B_OK) { _StepFallback(); return; } fPreviousInstructionPointer = frameIP; fPreviousFrameAddress = frame->FrameAddress(); fStepMode = STEP_OUT; _RunThread(frameIP); return; } // For "step in" and "step over" we also need the source code statement at // the current instruction pointer. fStepStatement = _GetStatementAtInstructionPointer(frame); if (fStepStatement == NULL) { _StepFallback(); return; } TRACE_CONTROL(" statement: %#" B_PRIx64 " - %#" B_PRIx64 "\n", fStepStatement->CoveringAddressRange().Start(), fStepStatement->CoveringAddressRange().End()); if (action == MSG_THREAD_STEP_INTO) { // step into fStepMode = STEP_INTO; _SingleStepThread(frameIP); } else { fPreviousFrameAddress = frame->FrameAddress(); // step over fStepMode = STEP_OVER; if (!_DoStepOver(frame->GetCpuState())) _StepFallback(); } }
bool BacktraceDebugger::getStackTrace(StackTrace &trace) { init(); // Might set enabled false if (!enabled) return false; void *stack[maxStack]; int n = backtrace(stack, maxStack); SmartPointer<char *>::Malloc symbols = backtrace_symbols(stack, n); #ifdef VALGRIND_MAKE_MEM_DEFINED (void)VALGRIND_MAKE_MEM_DEFINED(stack, n * sizeof(void *)); #endif // VALGRIND_MAKE_MEM_DEFINED SmartLock lock(this); for (int i = 0; i < n; i++) { bfd_vma pc = (bfd_vma)stack[i]; const char *filename = 0; const char *function = 0; unsigned line = 0; // Find section asection *section = 0; for (asection *s = p->abfd->sections; s; s = s->next) { if ((bfd_get_section_flags(p->abfd, s) & SEC_CODE) == 0) continue; bfd_vma vma = bfd_get_section_vma(p->abfd, s); if (pc < vma) { section = s->prev; break; } } if (section) { bfd_vma vma = bfd_get_section_vma(p->abfd, section); bfd_find_nearest_line(p->abfd, section, p->syms, pc - vma, &filename, &function, &line); #ifdef VALGRIND_MAKE_MEM_DEFINED if (filename) (void)VALGRIND_MAKE_MEM_DEFINED(filename, strlen(filename)); if (function) (void)VALGRIND_MAKE_MEM_DEFINED(function, strlen(function)); (void)VALGRIND_MAKE_MEM_DEFINED(&line, sizeof(line)); #endif // VALGRIND_MAKE_MEM_DEFINED } // Fallback to backtrace symbols if ((!function || !filename) && symbols.get()) { char *sym = symbols[i]; // Parse symbol string // Expected format: <module>(<function>+0x<offset>) char *ptr = sym; while (*ptr && *ptr != '(') ptr++; if (*ptr == '(') { *ptr++ = 0; if (!filename) filename = sym; // Not really the source file if (!function) { function = ptr; while (*ptr && *ptr != '+') ptr++; if (*ptr) { *ptr++ = 0; char *offset = ptr; while (*ptr && *ptr != ')') ptr++; if (*ptr == ')') *ptr = 0; int save_errno = errno; errno = 0; line = strtol(offset, 0, 0); // Byte offset not line number if (errno) line = 0; errno = save_errno; } } } } // Filename if (!filename) filename = ""; else if (0 <= parents) { const char *ptr = filename + strlen(filename); for (int j = 0; j <= parents; j++) while (filename < ptr && *--ptr != '/') continue; if (*ptr == '/') filename = ptr + 1; } // Function name char *demangled = 0; if (!function) function = ""; else { int status = 0; demangled = abi::__cxa_demangle(function, 0, 0, &status); if (!status && demangled) function = demangled; } trace.push_back (StackFrame(stack[i], FileLocation(filename, function, line))); if (demangled) free(demangled); } return true; }
bool ThreadHandler::_HandleBreakpointHitStep(CpuState* cpuState) { // in any case uninstall the temporary breakpoint _UninstallTemporaryBreakpoint(); switch (fStepMode) { case STEP_OVER: { StackTrace* stackTrace = fThread->GetStackTrace(); BReference<StackTrace> stackTraceReference(stackTrace); if (stackTrace == NULL && cpuState != NULL) { if (fDebuggerInterface->GetArchitecture()->CreateStackTrace( fThread->GetTeam(), this, cpuState, stackTrace, NULL, 1, false, false) == B_OK) { stackTraceReference.SetTo(stackTrace, true); } } if (stackTrace != NULL) { StackFrame* frame = stackTrace->FrameAt(0); // If we're not in the same frame we started in, // keep executing. if (frame != NULL && fPreviousFrameAddress != frame->FrameAddress()) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } } if (fPreviousFrameAddress != 0 && fSteppedOverFunctionAddress != cpuState->InstructionPointer()) { TRACE_CONTROL("STEP_OVER: called function address %#" B_PRIx64 ", previous frame address: %#" B_PRIx64 ", frame address: %#" B_PRIx64 ", adding return info\n", fSteppedOverFunctionAddress, fPreviousFrameAddress, stackTrace->FrameAt(0)->FrameAddress()); ReturnValueInfo* returnInfo = new(std::nothrow) ReturnValueInfo( fSteppedOverFunctionAddress, cpuState); if (returnInfo == NULL) return false; BReference<ReturnValueInfo> returnInfoReference(returnInfo, true); if (fThread->AddReturnValueInfo(returnInfo) != B_OK) return false; returnInfoReference.Detach(); fSteppedOverFunctionAddress = 0; } // If we're still in the statement, we continue single-stepping, // otherwise we're done. if (fStepStatement->ContainsAddress( cpuState->InstructionPointer())) { if (!_DoStepOver(cpuState)) _StepFallback(); return true; } fPreviousFrameAddress = 0; return false; } case STEP_INTO: // Should never happen -- we don't set a breakpoint in this case. return false; case STEP_OUT: { // That's the return address, so we're done in theory, // unless we're a recursive function. Check if we've actually // exited the previous stack frame or not if (!_HasExitedFrame(cpuState->StackFramePointer())) { status_t error = _InstallTemporaryBreakpoint( cpuState->InstructionPointer()); if (error != B_OK) _StepFallback(); else _RunThread(cpuState->InstructionPointer()); return true; } if (fPreviousFrameAddress == 0) return false; TRACE_CONTROL("ThreadHandler::_HandleBreakpointHitStep() - " "frame pointer 0x%#" B_PRIx64 ", previous: 0x%#" B_PRIx64 " - step out adding return value\n", cpuState ->StackFramePointer(), fPreviousFrameAddress); ReturnValueInfo* info = new(std::nothrow) ReturnValueInfo( fPreviousInstructionPointer, cpuState); if (info == NULL) return false; BReference<ReturnValueInfo> infoReference(info, true); if (fThread->AddReturnValueInfo(info) != B_OK) return false; infoReference.Detach(); fPreviousFrameAddress = 0; } default: return false; } }