diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -305,6 +305,7 @@ option(KWIN_BUILD_DECORATIONS "Enable building of KWin decorations." ON) option(KWIN_BUILD_KCMS "Enable building of KWin configuration modules." ON) option(KWIN_BUILD_TABBOX "Enable building of KWin Tabbox functionality" ON) +option(KWIN_BUILD_PERF "Build internal tools for performance analysis at runtime." ON) option(KWIN_BUILD_XRENDER_COMPOSITING "Enable building of KWin with XRender Compositing support" ON) cmake_dependent_option(KWIN_BUILD_ACTIVITIES "Enable building of KWin with kactivities support" ON "KF5Activities_FOUND" OFF) @@ -500,6 +501,7 @@ xdgshellclient.cpp xkb.cpp xwl/xwayland_interface.cpp + perf.cpp ) include(ECMQtDeclareLoggingCategory) @@ -535,6 +537,13 @@ ) endif() +if(KWIN_BUILD_PERF) + set( + kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} + helpers/perf/ftrace_marker.cpp + ) +endif() + if (HAVE_LINUX_VT_H) set(kwin_KDEINIT_SRCS ${kwin_KDEINIT_SRCS} virtual_terminal.cpp diff --git a/composite.cpp b/composite.cpp --- a/composite.cpp +++ b/composite.cpp @@ -26,6 +26,7 @@ #include "effects.h" #include "internal_client.h" #include "overlaywindow.h" +#include "perf.h" #include "platform.h" #include "scene.h" #include "screens.h" @@ -596,6 +597,8 @@ } } +static ulong s_msc = 0; + void Compositor::performCompositing() { // If a buffer swap is still pending, we return to the event loop and @@ -664,6 +667,8 @@ return; } + Perf::ftraceBegin(QStringLiteral("Paint"), s_msc); + // Skip windows that are not yet ready for being painted and if screen is locked skip windows // that are neither lockscreen nor inputmethod windows. // @@ -713,6 +718,9 @@ // through m_scene->paint(). compositeTimer.stop(); + Perf::ftraceEnd(QStringLiteral("Paint"), s_msc); + s_msc++; + // Trigger at least one more pass even if there would be nothing to paint, so that scene->idle() // is called the next time. If there would be nothing pending, it will not restart the timer and // scheduleRepaint() would restart it again somewhen later, called from functions that diff --git a/config-kwin.h.cmake b/config-kwin.h.cmake --- a/config-kwin.h.cmake +++ b/config-kwin.h.cmake @@ -1,6 +1,7 @@ #cmakedefine KWIN_BUILD_DECORATIONS 1 #cmakedefine KWIN_BUILD_TABBOX 1 #cmakedefine KWIN_BUILD_ACTIVITIES 1 +#cmakedefine KWIN_BUILD_PERF 1 #define KWIN_NAME "${KWIN_NAME}" #define KWIN_INTERNAL_NAME_X11 "${KWIN_INTERNAL_NAME_X11}" #define KWIN_CONFIG "${KWIN_NAME}rc" diff --git a/data/org_kde_kwin.categories b/data/org_kde_kwin.categories --- a/data/org_kde_kwin.categories +++ b/data/org_kde_kwin.categories @@ -20,3 +20,4 @@ kwin_scene_xrender KWin XRender based compositor scene plugin DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_XRENDER] kwin_scene_qpainter KWin QPainter based compositor scene plugin DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_QPAINTER] kwin_scene_opengl KWin OpenGL based compositor scene plugins DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_OPENGL] +kwin_perf KWin Performance measurement and debugging tools DEFAULT_SEVERITY [CRITICAL] IDENTIFIER [KWIN_PERF] diff --git a/dbusinterface.h b/dbusinterface.h --- a/dbusinterface.h +++ b/dbusinterface.h @@ -68,6 +68,7 @@ QString supportInformation(); Q_NOREPLY void unclutterDesktop(); Q_NOREPLY void showDebugConsole(); + void enableFtrace(bool enable); QVariantMap queryWindowInfo(); QVariantMap getWindowInfo(const QString &uuid); diff --git a/dbusinterface.cpp b/dbusinterface.cpp --- a/dbusinterface.cpp +++ b/dbusinterface.cpp @@ -28,6 +28,9 @@ #include "atoms.h" #include "composite.h" #include "debug_console.h" +#ifdef KWIN_BUILD_PERF +#include "helpers/perf/ftrace_marker.h" +#endif #include "main.h" #include "placement.h" #include "platform.h" @@ -189,6 +192,27 @@ console->show(); } +void DBusInterface::enableFtrace(bool enable) +{ + const QString name = QStringLiteral("org.kde.kwin.enableFtrace"); +#ifdef KWIN_BUILD_PERF + if (!Perf::FtraceMarker::self()) { + const QString msg = QStringLiteral("Ftrace marker not available"); + QDBusConnection::sessionBus().send(message().createErrorReply(name, msg)); + return; + } + if (!Perf::FtraceMarker::self()->setEnabled(enable)) { + const QString msg = QStringLiteral("Ftrace marker is available but could not be ").append( + enable ? "enabled" : "disabled"); + QDBusConnection::sessionBus().send(message().createErrorReply(name, msg)); + } +#else + Q_UNUSED(enable) + const QString msg = QStringLiteral("KWin built without ftrace marking capability"); + QDBusConnection::sessionBus().send(message().createErrorReply(name, msg)); +#endif +} + namespace { QVariantMap clientToVariantMap(const AbstractClient *c) { diff --git a/helpers/perf/ftrace_marker.h b/helpers/perf/ftrace_marker.h new file mode 100644 --- /dev/null +++ b/helpers/perf/ftrace_marker.h @@ -0,0 +1,60 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#pragma once + +#include + +#include + +namespace KWin +{ +namespace Perf +{ + +/** + * Provides an interface to mark the Ftrace output for debugging. + */ +class FtraceMarker : public QObject +{ + Q_OBJECT +public: + virtual ~FtraceMarker() = default; + + /** + * @brief Enables or disables the marker + * + * @param enable The enablement state to set + * @return True if setting enablement succeeded, else false + */ + bool setEnabled(bool enable); + void print(const QString &message); + void printBegin(const QString &message, ulong ctx); + void printEnd(const QString &message, ulong ctx); + +private: + bool findFile(); + + QFile *m_file = nullptr; + + KWIN_SINGLETON(FtraceMarker) +}; + +} +} diff --git a/helpers/perf/ftrace_marker.cpp b/helpers/perf/ftrace_marker.cpp new file mode 100644 --- /dev/null +++ b/helpers/perf/ftrace_marker.cpp @@ -0,0 +1,146 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#include "ftrace_marker.h" + +#include "utils.h" + +#include +#include + +namespace KWin +{ +namespace Perf +{ + +void writeFunctionEnabled(QFile *file, const QString &message) +{ + file->write(message.toLatin1()); + file->flush(); +} + +void writeFunctionDisabled(QFile *file, const QString &message) +{ + Q_UNUSED(file) + Q_UNUSED(message) +} + +void (*s_writeFunction)(QFile *file, const QString &message) = writeFunctionDisabled; + + +KWIN_SINGLETON_FACTORY(FtraceMarker) + +FtraceMarker::FtraceMarker(QObject *parent) + : QObject(parent) +{ + if (qEnvironmentVariableIsSet("KWIN_PERF_FTRACE")) { + qCDebug(KWIN_PERF) << "Ftrace marking initially enabled via environment variable"; + setEnabled(true); + } +} + +bool FtraceMarker::setEnabled(bool enable) +{ + if ((bool)m_file == enable) { + // no change + return true; + } + if (enable) { + if (!findFile()) { + qCWarning(KWIN_PERF) << "Ftrace marking not available. Try reenabling after issue is solved."; + return false; + } + s_writeFunction = writeFunctionEnabled; + } else { + s_writeFunction = writeFunctionDisabled; + delete m_file; + m_file = nullptr; + } + return true; +} + +void FtraceMarker::print(const QString &message) +{ + (*s_writeFunction)(m_file, message); +} + +void FtraceMarker::printBegin(const QString &message, ulong ctx) +{ + (*s_writeFunction)(m_file, message + QStringLiteral(" (begin_ctx=%1)").arg(ctx)); +} + +void FtraceMarker::printEnd(const QString &message, ulong ctx) +{ + (*s_writeFunction)(m_file, message + QStringLiteral(" (end_ctx=%1)").arg(ctx)); +} + +bool FtraceMarker::findFile() +{ + QFile mountsFile("/proc/mounts"); + if (!mountsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { + qCWarning(KWIN_PERF) + << "No access to mounts file. Can not determine trace marker file location."; + return false; + } + + auto lineInfo = [](const QString &line) { + const int start = line.indexOf(' ') + 1; + const int end = line.indexOf(' ', start); + const QString dirPath(line.mid(start, end - start)); + if (dirPath.isEmpty() || !QFileInfo(dirPath).exists()) { + return QFileInfo(); + } + return QFileInfo(QDir(dirPath), QStringLiteral("trace_marker")); + }; + QFileInfo markerFileInfo; + QTextStream mountsIn(&mountsFile); + QString mountsLine = mountsIn.readLine(); + + while (!mountsLine.isNull()) { + if (mountsLine.startsWith("tracefs")) { + const auto info = lineInfo(mountsLine); + if (info.exists()) { + markerFileInfo = info; + break; + } + } + if (mountsLine.startsWith("debugfs")) { + markerFileInfo = lineInfo(mountsLine); + } + mountsLine = mountsIn.readLine(); + } + mountsFile.close(); + if (!markerFileInfo.exists()) { + qCWarning(KWIN_PERF) << "Could not determine trace marker file location from mounts."; + return false; + } + + const QString path = markerFileInfo.absoluteFilePath(); + m_file = new QFile(path, this); + if (!m_file->open(QIODevice::WriteOnly)) { + qCWarning(KWIN_PERF) << "No access to trace marker file at:" << path; + delete m_file; + m_file = nullptr; + return false; + } + return true; +} + +} +} diff --git a/main.cpp b/main.cpp --- a/main.cpp +++ b/main.cpp @@ -37,6 +37,8 @@ #include +#include "helpers/perf/ftrace_marker.h" + // KDE #include #include @@ -109,6 +111,10 @@ , m_inputConfig() , m_operationMode(mode) { +#ifdef KWIN_BUILD_PERF + Perf::FtraceMarker::create(this); +#endif + qRegisterMetaType("Options::WindowOperation"); qRegisterMetaType(); qRegisterMetaType("KWayland::Server::SurfaceInterface *"); diff --git a/org.kde.KWin.xml b/org.kde.KWin.xml --- a/org.kde.KWin.xml +++ b/org.kde.KWin.xml @@ -45,5 +45,8 @@ + + + diff --git a/perf.h b/perf.h new file mode 100644 --- /dev/null +++ b/perf.h @@ -0,0 +1,63 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#pragma once + +#include + +#include +#include + +namespace KWin +{ +namespace Perf +{ + +/** + * Internal perf API for consumers + */ +class KWIN_EXPORT PerfInterface +{ + +public: + static void ftrace(const QString &message); + static void ftraceBegin(const QString &message, ulong ctx); + static void ftraceEnd(const QString &message, ulong ctx); +}; + +inline +void ftrace(const QString &message) +{ + PerfInterface::ftrace(message); +} + +inline +void ftraceBegin(const QString &message, ulong ctx) +{ + PerfInterface::ftraceBegin(message, ctx); +} + +inline +void ftraceEnd(const QString &message, ulong ctx) +{ + PerfInterface::ftraceEnd(message, ctx); +} + +} +} diff --git a/perf.cpp b/perf.cpp new file mode 100644 --- /dev/null +++ b/perf.cpp @@ -0,0 +1,66 @@ +/******************************************************************** + KWin - the KDE window manager + This file is part of the KDE project. + +Copyright 2019 Roman Gilg + +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 . +*********************************************************************/ +#include "perf.h" + +#include + +#ifdef KWIN_BUILD_PERF +#include "helpers/perf/ftrace_marker.h" +#endif + +namespace KWin +{ +namespace Perf +{ + +#ifdef KWIN_BUILD_PERF +void PerfInterface::ftrace(const QString &message) +{ + FtraceMarker::self()->print(message); +} +void PerfInterface::ftraceBegin(const QString &message, ulong ctx) +{ + FtraceMarker::self()->printBegin(message, ctx); +} +void PerfInterface::ftraceEnd(const QString &message, ulong ctx) +{ + FtraceMarker::self()->printEnd(message, ctx); +} + +#else + +void PerfInterface::ftrace(const QString &message) +{ + Q_UNUSED(message) +} +void PerfInterface::ftrace(ulong ctx, const QString &message) +{ + Q_UNUSED(message) + Q_UNUSED(ctx) +} +void PerfInterface::ftrace(const QString &message, ulong ctx) +{ + Q_UNUSED(message) + Q_UNUSED(ctx) +} +#endif + +} +} diff --git a/utils.h b/utils.h --- a/utils.h +++ b/utils.h @@ -38,6 +38,7 @@ #include Q_DECLARE_LOGGING_CATEGORY(KWIN_CORE) Q_DECLARE_LOGGING_CATEGORY(KWIN_VIRTUALKEYBOARD) +Q_DECLARE_LOGGING_CATEGORY(KWIN_PERF) namespace KWin { diff --git a/utils.cpp b/utils.cpp --- a/utils.cpp +++ b/utils.cpp @@ -45,6 +45,7 @@ Q_LOGGING_CATEGORY(KWIN_CORE, "kwin_core", QtCriticalMsg) Q_LOGGING_CATEGORY(KWIN_VIRTUALKEYBOARD, "kwin_virtualkeyboard", QtCriticalMsg) +Q_LOGGING_CATEGORY(KWIN_PERF, "kwin_perf", QtCriticalMsg) namespace KWin { diff --git a/workspace.cpp b/workspace.cpp --- a/workspace.cpp +++ b/workspace.cpp @@ -1399,6 +1399,12 @@ support.append(yes); #else support.append(no); +#endif + support.append(QStringLiteral("KWIN_BUILD_PERF: ")); +#ifdef KWIN_BUILD_PERF + support.append(yes); +#else + support.append(no); #endif support.append(QStringLiteral("HAVE_DRM: ")); #if HAVE_DRM