File indexing completed on 2024-05-12 05:26:20
0001 /* 0002 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org> 0003 * 0004 * This program is free software; you can redistribute it and/or modify 0005 * it under the terms of the GNU General Public License as published by 0006 * the Free Software Foundation; either version 2 of the License, or 0007 * (at your option) any later version. 0008 * 0009 * This program is distributed in the hope that it will be useful, 0010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0012 * GNU General Public License for more details. 0013 * 0014 * You should have received a copy of the GNU General Public License 0015 * along with this program; if not, write to the 0016 * Free Software Foundation, Inc., 0017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 0018 */ 0019 0020 #include <QGuiApplication> 0021 #include <QLockFile> 0022 #include <QDir> 0023 #include <QElapsedTimer> 0024 0025 #include <signal.h> 0026 #ifndef Q_OS_WIN 0027 #include <unistd.h> 0028 #else 0029 #include <io.h> 0030 #include <process.h> 0031 #endif 0032 0033 #include "listener.h" 0034 #include "log.h" 0035 #include "test.h" 0036 #include "definitions.h" 0037 #include "backtrace.h" 0038 #ifdef Q_OS_OSX 0039 #include <CoreFoundation/CoreFoundation.h> 0040 #endif 0041 0042 0043 0044 0045 /* 0046 * We capture all qt debug messages in the same process and feed it into the sink debug system. 0047 * This way we get e.g. kimap debug messages as well together with the rest. 0048 */ 0049 void qtMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) 0050 { 0051 QByteArray localMsg = msg.toLocal8Bit(); 0052 switch (type) { 0053 case QtDebugMsg: 0054 Sink::Log::debugStream(Sink::Log::DebugLevel::Trace, context.line, context.file, context.function, context.category) << msg; 0055 break; 0056 case QtInfoMsg: 0057 Sink::Log::debugStream(Sink::Log::DebugLevel::Log, context.line, context.file, context.function, context.category) << msg; 0058 break; 0059 case QtWarningMsg: 0060 Sink::Log::debugStream(Sink::Log::DebugLevel::Warning, context.line, context.file, context.function, context.category) << msg; 0061 break; 0062 case QtCriticalMsg: 0063 Sink::Log::debugStream(Sink::Log::DebugLevel::Error, context.line, context.file, context.function, context.category) << msg; 0064 break; 0065 case QtFatalMsg: 0066 Sink::Log::debugStream(Sink::Log::DebugLevel::Error, context.line, context.file, context.function, context.category) << msg; 0067 abort(); 0068 } 0069 } 0070 0071 QString read(const QString &filename) 0072 { 0073 QFile file{filename}; 0074 file.open(QIODevice::ReadOnly); 0075 return file.readAll(); 0076 } 0077 0078 void printStats() 0079 { 0080 0081 #if defined(Q_OS_LINUX) 0082 /* 0083 * See 'man proc' for details 0084 */ 0085 { 0086 auto statm = read("/proc/self/statm").split(' '); 0087 SinkTrace() << "Program size:" << statm.value(0).toInt() << "pages"; 0088 SinkTrace() << "RSS:"<< statm.value(1).toInt() << "pages"; 0089 SinkTrace() << "Resident Shared:" << statm.value(2).toInt() << "pages"; 0090 SinkTrace() << "Text (code):" << statm.value(3).toInt() << "pages"; 0091 SinkTrace() << "Data (data + stack):" << statm.value(5).toInt() << "pages"; 0092 } 0093 0094 { 0095 auto stat = read("/proc/self/stat").split(' '); 0096 SinkTrace() << "Minor page faults: " << stat.value(10).toInt(); 0097 SinkTrace() << "Children minor page faults: " << stat.value(11).toInt(); 0098 SinkTrace() << "Major page faults: " << stat.value(12).toInt(); 0099 SinkTrace() << "Children major page faults: " << stat.value(13).toInt(); 0100 } 0101 0102 //Dump the complete memory map for the process 0103 // std::cout << "smaps: " << read("/proc/self/smaps").toStdString(); 0104 //Dump all sorts of stats for the process 0105 // std::cout << read("/proc/self/status").toStdString(); 0106 0107 { 0108 auto io = read("/proc/self/io").split('\n'); 0109 QHash<QString, QString> hash; 0110 for (const auto &s : io) { 0111 const auto parts = s.split(": "); 0112 hash.insert(parts.value(0), parts.value(1)); 0113 } 0114 SinkTrace() << "Read syscalls: " << hash.value("syscr").toInt(); 0115 SinkTrace() << "Write syscalls: " << hash.value("syscw").toInt(); 0116 SinkTrace() << "Read from disk: " << hash.value("read_bytes").toInt() / 1024 << "kb"; 0117 SinkTrace() << "Written to disk: " << hash.value("write_bytes").toInt() / 1024 << "kb"; 0118 SinkTrace() << "Cancelled write bytes: " << hash.value("cancelled_write_bytes").toInt(); 0119 } 0120 0121 #endif 0122 } 0123 0124 class SynchronizerApplication : public QGuiApplication 0125 { 0126 Q_OBJECT 0127 protected: 0128 using QGuiApplication::QGuiApplication; 0129 0130 QElapsedTimer time; 0131 0132 /* 0133 * If we block the event loop for too long the system becomes unresponsive to user inputs, 0134 * so we monitor it and attempt to avoid blocking behaviour 0135 */ 0136 bool notify(QObject *receiver, QEvent *event) override 0137 { 0138 if (time.isValid()) { 0139 time.restart(); 0140 } else { 0141 time.start(); 0142 } 0143 const auto ret = QGuiApplication::notify(receiver, event); 0144 const auto elapsed = time.elapsed(); 0145 if (elapsed > 1000) { 0146 SinkWarning() << "Blocked the eventloop for " << Sink::Log::TraceTime(elapsed) << " with event " << event->type(); 0147 } 0148 return ret; 0149 } 0150 }; 0151 0152 int main(int argc, char *argv[]) 0153 { 0154 if (qEnvironmentVariableIsSet("SINK_GDB_DEBUG")) { 0155 #ifndef Q_OS_WIN 0156 SinkWarning() << "Running resource in debug mode and waiting for gdb to attach: gdb attach " << getpid(); 0157 raise(SIGSTOP); 0158 #endif 0159 } else { 0160 Sink::installCrashHandler(); 0161 } 0162 0163 qInstallMessageHandler(qtMessageHandler); 0164 0165 #ifdef Q_OS_OSX 0166 //Necessary to hide this QGuiApplication from the dock and application switcher on mac os. 0167 if (CFBundleRef mainBundle = CFBundleGetMainBundle()) { 0168 // get the application's Info Dictionary. For app bundles this would live in the bundle's Info.plist, 0169 if (CFMutableDictionaryRef infoDict = (CFMutableDictionaryRef) CFBundleGetInfoDictionary(mainBundle)) { 0170 // Add or set the "LSUIElement" key with/to value "1". This can simply be a CFString. 0171 CFDictionarySetValue(infoDict, CFSTR("LSUIElement"), CFSTR("1")); 0172 // That's it. We're now considered as an "agent" by the window server, and thus will have 0173 // neither menubar nor presence in the Dock or App Switcher. 0174 } 0175 } 0176 #endif 0177 0178 SynchronizerApplication app(argc, argv); 0179 app.setQuitLockEnabled(false); 0180 0181 QByteArrayList arguments; 0182 for (int i = 0; i < argc; i++) { 0183 arguments << argv[i]; 0184 } 0185 if (arguments.contains("--test")) { 0186 SinkLog() << "Running in test-mode"; 0187 arguments.removeAll("--test"); 0188 Sink::Test::setTestModeEnabled(true); 0189 } 0190 0191 if (arguments.count() < 3) { 0192 SinkWarning() << "Not enough args passed, no resource loaded."; 0193 return app.exec(); 0194 } 0195 0196 const QByteArray instanceIdentifier = arguments.at(1); 0197 const QByteArray resourceType = arguments.at(2); 0198 app.setApplicationName(instanceIdentifier); 0199 Sink::Log::setPrimaryComponent(instanceIdentifier); 0200 SinkLog() << "Starting: " << instanceIdentifier << resourceType; 0201 0202 QDir{}.mkpath(Sink::resourceStorageLocation(instanceIdentifier)); 0203 QLockFile lockfile(Sink::storageLocation() + QString("/%1.lock").arg(QString(instanceIdentifier))); 0204 lockfile.setStaleLockTime(500); 0205 if (!lockfile.tryLock(0)) { 0206 const auto error = lockfile.error(); 0207 if (error == QLockFile::LockFailedError) { 0208 qint64 pid; 0209 QString hostname, appname; 0210 lockfile.getLockInfo(&pid, &hostname, &appname); 0211 SinkWarning() << "Failed to acquire exclusive resource lock."; 0212 SinkLog() << "Pid:" << pid << "Host:" << hostname << "App:" << appname; 0213 } else { 0214 SinkError() << "Error while trying to acquire exclusive resource lock: " << error; 0215 } 0216 return -1; 0217 } 0218 0219 auto listener = new Listener(instanceIdentifier, resourceType, &app); 0220 Sink::setListener(listener); 0221 listener->checkForUpgrade(); 0222 0223 QObject::connect(&app, &QCoreApplication::aboutToQuit, listener, &Listener::closeAllConnections); 0224 QObject::connect(listener, &Listener::noClients, &app, &QCoreApplication::quit); 0225 0226 auto ret = app.exec(); 0227 SinkLog() << "Exiting: " << instanceIdentifier; 0228 printStats(); 0229 return ret; 0230 } 0231 0232 #include "main.moc"