mirror of
https://bitbucket.org/smil3y/kde-workspace.git
synced 2025-02-23 18:32:50 +00:00
235 lines
7.7 KiB
C++
235 lines
7.7 KiB
C++
/*
|
|
Copyright (C) 2009 George Kiagiadakis <gkiagia@users.sourceforge.net>
|
|
|
|
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 2 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#include "drkonqibackends.h"
|
|
|
|
#include <cstdlib>
|
|
#include <cerrno>
|
|
#include <sys/types.h>
|
|
#include <signal.h>
|
|
|
|
#include <QtCore/QTimer>
|
|
#include <QtCore/QDir>
|
|
|
|
#include <KCmdLineArgs>
|
|
#include <KStandardDirs>
|
|
#include <KDebug>
|
|
#include <KConfig>
|
|
#include <KConfigGroup>
|
|
#include <KGlobal>
|
|
#include <KStartupInfo>
|
|
#include <KCrash>
|
|
|
|
#include "crashedapplication.h"
|
|
#include "debugger.h"
|
|
#include "debuggermanager.h"
|
|
#include "backtracegenerator.h"
|
|
|
|
AbstractDrKonqiBackend::~AbstractDrKonqiBackend()
|
|
{
|
|
}
|
|
|
|
bool AbstractDrKonqiBackend::init()
|
|
{
|
|
m_crashedApplication = constructCrashedApplication();
|
|
m_debuggerManager = constructDebuggerManager();
|
|
return true;
|
|
}
|
|
|
|
|
|
KCrashBackend::KCrashBackend()
|
|
: QObject(), AbstractDrKonqiBackend(), m_state(ProcessRunning)
|
|
{
|
|
}
|
|
|
|
KCrashBackend::~KCrashBackend()
|
|
{
|
|
continueAttachedProcess();
|
|
}
|
|
|
|
bool KCrashBackend::init()
|
|
{
|
|
AbstractDrKonqiBackend::init();
|
|
|
|
QString startupId(KCmdLineArgs::parsedArgs()->getOption("startupid"));
|
|
if (!startupId.isEmpty()) { // stop startup notification
|
|
KStartupInfoId id;
|
|
id.initId(startupId.toLocal8Bit());
|
|
KStartupInfo::sendFinish(id);
|
|
}
|
|
|
|
//check whether the attached process exists and whether we have permissions to inspect it
|
|
if (crashedApplication()->pid() <= 0) {
|
|
kError() << "Invalid pid specified";
|
|
return false;
|
|
}
|
|
|
|
if (::kill(crashedApplication()->pid(), 0) < 0) {
|
|
switch (errno) {
|
|
case EPERM:
|
|
kError() << "DrKonqi doesn't have permissions to inspect the specified process";
|
|
break;
|
|
case ESRCH:
|
|
kError() << "The specified process does not exist.";
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--keeprunning means: generate backtrace instantly and let the process continue execution
|
|
if(KCmdLineArgs::parsedArgs()->isSet("keeprunning")) {
|
|
stopAttachedProcess();
|
|
debuggerManager()->backtraceGenerator()->start();
|
|
connect(debuggerManager(), SIGNAL(debuggerFinished()), SLOT(continueAttachedProcess()));
|
|
} else {
|
|
connect(debuggerManager(), SIGNAL(debuggerStarting()), SLOT(onDebuggerStarting()));
|
|
connect(debuggerManager(), SIGNAL(debuggerFinished()), SLOT(onDebuggerFinished()));
|
|
|
|
//stop the process to avoid high cpu usage by other threads (bug 175362).
|
|
//if the process was started by kdeinit, we need to wait a bit for KCrash
|
|
//to reach the alarm(0); call in kdeui/util/kcrash.cpp line 406 or else
|
|
//if we stop it before this call, pending alarm signals will kill the
|
|
//process when we try to continue it.
|
|
QTimer::singleShot(2000, this, SLOT(stopAttachedProcess()));
|
|
}
|
|
|
|
//Handle drkonqi crashes
|
|
s_pid = crashedApplication()->pid(); //copy pid for use by the crash handler, so that it is safer
|
|
KCrash::setEmergencySaveFunction(emergencySaveFunction);
|
|
|
|
return true;
|
|
}
|
|
|
|
CrashedApplication *KCrashBackend::constructCrashedApplication()
|
|
{
|
|
CrashedApplication *a = new CrashedApplication(this);
|
|
a->m_datetime = QDateTime::currentDateTime();
|
|
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
|
|
a->m_name = args->getOption("programname");
|
|
a->m_version = args->getOption("appversion").toUtf8();
|
|
a->m_reportAddress = args->getOption("bugaddress").toUtf8();
|
|
a->m_pid = args->getOption("pid").toInt();
|
|
a->m_signalNumber = args->getOption("signal").toInt();
|
|
a->m_restarted = args->isSet("restarted");
|
|
a->m_thread = args->getOption("thread").toInt();
|
|
|
|
//try to determine the executable that crashed
|
|
if ( QFileInfo(QString("/proc/%1/exe").arg(a->m_pid)).exists() ) {
|
|
//on linux, the fastest and most reliable way is to get the path from /proc
|
|
kDebug() << "Using /proc to determine executable path";
|
|
a->m_executable.setFile(QFile::readLink(QString("/proc/%1/exe").arg(a->m_pid)));
|
|
|
|
if (args->isSet("kdeinit") || a->m_executable.fileName().startsWith("python") ) {
|
|
a->m_fakeBaseName = args->getOption("appname");
|
|
}
|
|
} else {
|
|
if ( args->isSet("kdeinit") ) {
|
|
a->m_executable = QFileInfo(KStandardDirs::findExe("kdeinit4"));
|
|
a->m_fakeBaseName = args->getOption("appname");
|
|
} else {
|
|
QFileInfo execPath(args->getOption("appname"));
|
|
if ( execPath.isAbsolute() ) {
|
|
a->m_executable = execPath;
|
|
} else if ( !args->getOption("apppath").isEmpty() ) {
|
|
QDir execDir(args->getOption("apppath"));
|
|
a->m_executable = execDir.absoluteFilePath(execPath.fileName());
|
|
} else {
|
|
a->m_executable = QFileInfo(KStandardDirs::findExe(execPath.fileName()));
|
|
}
|
|
}
|
|
}
|
|
|
|
kDebug() << "Executable is:" << a->m_executable.absoluteFilePath();
|
|
kDebug() << "Executable exists:" << a->m_executable.exists();
|
|
|
|
return a;
|
|
}
|
|
|
|
DebuggerManager *KCrashBackend::constructDebuggerManager()
|
|
{
|
|
QList<Debugger> internalDebuggers = Debugger::availableInternalDebuggers("KCrash");
|
|
KConfigGroup config(KGlobal::config(), "DrKonqi");
|
|
QString defaultDebuggerName = config.readEntry("Debugger", QString("gdb"));
|
|
|
|
Debugger firstKnownGoodDebugger, preferredDebugger;
|
|
foreach (const Debugger & debugger, internalDebuggers) {
|
|
if (!firstKnownGoodDebugger.isValid() && debugger.isInstalled()) {
|
|
firstKnownGoodDebugger = debugger;
|
|
}
|
|
if (debugger.codeName() == defaultDebuggerName) {
|
|
preferredDebugger = debugger;
|
|
}
|
|
if (firstKnownGoodDebugger.isValid() && preferredDebugger.isValid()) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!preferredDebugger.isInstalled()) {
|
|
if (firstKnownGoodDebugger.isValid()) {
|
|
preferredDebugger = firstKnownGoodDebugger;
|
|
} else {
|
|
kError() << "Unable to find an internal debugger that can work with the KCrash backend";
|
|
}
|
|
}
|
|
|
|
return new DebuggerManager(preferredDebugger, Debugger::availableExternalDebuggers("KCrash"), this);
|
|
}
|
|
|
|
void KCrashBackend::stopAttachedProcess()
|
|
{
|
|
if (m_state == ProcessRunning) {
|
|
kDebug() << "Sending SIGSTOP to process";
|
|
::kill(crashedApplication()->pid(), SIGSTOP);
|
|
m_state = ProcessStopped;
|
|
}
|
|
}
|
|
|
|
void KCrashBackend::continueAttachedProcess()
|
|
{
|
|
if (m_state == ProcessStopped) {
|
|
kDebug() << "Sending SIGCONT to process";
|
|
::kill(crashedApplication()->pid(), SIGCONT);
|
|
m_state = ProcessRunning;
|
|
}
|
|
}
|
|
|
|
void KCrashBackend::onDebuggerStarting()
|
|
{
|
|
continueAttachedProcess();
|
|
m_state = DebuggerRunning;
|
|
}
|
|
|
|
void KCrashBackend::onDebuggerFinished()
|
|
{
|
|
m_state = ProcessRunning;
|
|
stopAttachedProcess();
|
|
}
|
|
|
|
//static
|
|
qint64 KCrashBackend::s_pid = 0;
|
|
|
|
//static
|
|
void KCrashBackend::emergencySaveFunction(int signal)
|
|
{
|
|
// In case drkonqi itself crashes, we need to get rid of the process being debugged,
|
|
// so we kill it, no matter what its state was.
|
|
Q_UNUSED(signal);
|
|
::kill(s_pid, SIGKILL);
|
|
}
|
|
|
|
#include "moc_drkonqibackends.cpp"
|