aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Penz <ppenz@openismus.com>2011-01-24 08:53:48 +0100
committerHolger Schröder <holger.schroeder.ext@basyskom.de>2011-01-25 12:45:07 +0100
commit5d10bf42eb79bc438a615c0f450586933ccdfe76 (patch)
treec72ad03d157c8345f044abd902d6106ec059af2d
parent0804477bda6bfdc3eb1ee154999ed9d23f3cfc49 (diff)
Changes: Add instrumentation for memory-usage of the themedaemon server
RevBy: Kuisma Salonen, Armin Berres Details: By sending a SIGUSR1 the themedaemon server prints the memory-usage for all shared pixmaps to the file "mthemedaemonserver-status.txt" inside the temporary directory of the system.
-rw-r--r--mthemedaemon/main.cpp9
-rw-r--r--mthemedaemon/mthemedaemonserver.cpp229
-rw-r--r--mthemedaemon/mthemedaemonserver.h28
-rw-r--r--src/corelib/theme/mimagedirectory.cpp13
-rw-r--r--src/corelib/theme/mimagedirectory.h5
5 files changed, 280 insertions, 4 deletions
diff --git a/mthemedaemon/main.cpp b/mthemedaemon/main.cpp
index 692607fb..a574e92e 100644
--- a/mthemedaemon/main.cpp
+++ b/mthemedaemon/main.cpp
@@ -33,7 +33,7 @@ namespace
static int setup_unix_signal_handlers()
{
- struct sigaction sighup, sigterm, sigint;
+ struct sigaction sighup, sigterm, sigint, sigusr1;
sighup.sa_handler = MThemeDaemonServer::hupSignalHandler;
sigemptyset(&sighup.sa_mask);
@@ -56,6 +56,13 @@ static int setup_unix_signal_handlers()
if (sigaction(SIGINT, &sigint, 0) > 0)
return 3;
+ sigusr1.sa_handler = MThemeDaemonServer::usr1SignalHandler;
+ sigemptyset(&sigusr1.sa_mask);
+ sigusr1.sa_flags = SA_RESTART;
+
+ if (sigaction(SIGUSR1, &sigusr1, 0) > 0)
+ return 4;
+
return 0;
}
diff --git a/mthemedaemon/mthemedaemonserver.cpp b/mthemedaemon/mthemedaemonserver.cpp
index a58bf8e9..72daab77 100644
--- a/mthemedaemon/mthemedaemonserver.cpp
+++ b/mthemedaemon/mthemedaemonserver.cpp
@@ -27,6 +27,7 @@
#include <QLocalSocket>
#include <QDir>
+#include <QTime>
#include <QTimer>
#include <QSettings>
#include <QApplication>
@@ -38,6 +39,7 @@ const int THEME_CHANGE_TIMEOUT = 3000;
int MThemeDaemonServer::sighupFd[2];
int MThemeDaemonServer::sigtermFd[2];
int MThemeDaemonServer::sigintFd[2];
+int MThemeDaemonServer::sigusr1Fd[2];
MThemeDaemonServer::MThemeDaemonServer(const QString &serverAddress) :
daemon(MThemeDaemon::RemoteDaemon),
@@ -55,6 +57,8 @@ MThemeDaemonServer::MThemeDaemonServer(const QString &serverAddress) :
qFatal("Couldn't create TERM socketpair");
if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigintFd))
qFatal("Couldn't create INT socketpair");
+ if (::socketpair(AF_UNIX, SOCK_STREAM, 0, sigusr1Fd))
+ qFatal("Couldn't create USR1 socketpair");
snHup = new QSocketNotifier(sighupFd[1], QSocketNotifier::Read, this);
connect(snHup, SIGNAL(activated(int)), this, SLOT(handleSigHup()));
@@ -62,6 +66,8 @@ MThemeDaemonServer::MThemeDaemonServer(const QString &serverAddress) :
connect(snTerm, SIGNAL(activated(int)), this, SLOT(handleSigTerm()));
snInt = new QSocketNotifier(sigintFd[1], QSocketNotifier::Read, this);
connect(snInt, SIGNAL(activated(int)), this, SLOT(handleSigInt()));
+ snUsr1 = new QSocketNotifier(sigusr1Fd[1], QSocketNotifier::Read, this);
+ connect(snUsr1, SIGNAL(activated(int)), this, SLOT(handleSigUsr1()));
QString filename = M_INSTALL_SYSCONFDIR "/meegotouch/themedaemonpriorities.conf";
loadPriorities(filename);
@@ -708,6 +714,13 @@ void MThemeDaemonServer::intSignalHandler(int)
Q_UNUSED(writtenBytes);
}
+void MThemeDaemonServer::usr1SignalHandler(int)
+{
+ char a = 1;
+ ssize_t writtenBytes = ::write(sigusr1Fd[0], &a, sizeof(a));
+ Q_UNUSED(writtenBytes);
+}
+
void MThemeDaemonServer::handleSigTerm()
{
snTerm->setEnabled(false);
@@ -745,3 +758,219 @@ void MThemeDaemonServer::handleSigInt()
snInt->setEnabled(true);
}
+
+void MThemeDaemonServer::handleSigUsr1()
+{
+ snUsr1->setEnabled(false);
+ char tmp;
+ ssize_t readBytes = ::read(sigusr1Fd[1], &tmp, sizeof(tmp));
+ Q_UNUSED(readBytes);
+
+ MemoryUsagePrinter memoryUsagePrinter(registeredClients.values());
+ memoryUsagePrinter.print();
+
+ snUsr1->setEnabled(true);
+}
+
+MThemeDaemonServer::MemoryUsagePrinter::MemoryUsagePrinter(const QList<MThemeDaemonClient*> &clients) :
+ columnSeparator(QLatin1Char('|')),
+ lines(),
+ clientMemoryUsage(),
+ sharedMemoryUsage(0)
+{
+ QHash<QString, QString> sharedPixmaps;
+
+ // Iterate all themedaemon clients and create one line per shared pixmap
+ // for the output table (there all clients that use this pixmaps are listed).
+ foreach(const MThemeDaemonClient *client, clients) {
+ clientMemoryUsage[client->name()] = 0;
+
+ QHashIterator<M::MThemeDaemonProtocol::PixmapIdentifier, ImageResource*> it(client->pixmaps);
+ while (it.hasNext()) {
+ it.next();
+ if (it.value()) {
+ addPixmapCacheEntries(client->name(),
+ it.key().imageId,
+ it.value()->pixmapCacheEntries(),
+ sharedPixmaps);
+ }
+ }
+ }
+
+ // Layout the lines of the table to have an optimized column-width
+ lines = layoutedLines(sharedPixmaps.values());
+}
+
+void MThemeDaemonServer::MemoryUsagePrinter::print()
+{
+ QFile file(QDir::tempPath() + QDir::separator() + "mthemedaemonserver-status.txt");
+ if (file.open(QIODevice::Append | QIODevice::Text)) {
+ QTextStream out(&file);
+ out << "\n" << "Timestamp: " + QTime::currentTime().toString() << "\n";
+
+ foreach (const QString &line, lines) {
+ out << line << "\n";
+ }
+
+ int overallMemoryUsage = sharedMemoryUsage;
+ out << "\nClient exclusive memory usage:\n";
+ QHashIterator<QString, int> it(clientMemoryUsage);
+ while (it.hasNext()) {
+ it.next();
+ out << " " << it.key() << ": " << it.value() << " Bytes\n";
+ overallMemoryUsage += it.value();
+ }
+ out << "Shared memory usage: " << sharedMemoryUsage << " Bytes\n";
+ out << "Overall memory usage: " << overallMemoryUsage << " Bytes\n";
+ }
+}
+
+QString MThemeDaemonServer::MemoryUsagePrinter::keyForHandle(const MPixmapHandle &handle) const
+{
+ QString key;
+ if (handle.isValid()) {
+ if (handle.xHandle) {
+ key = QString::number(static_cast<qulonglong>(handle.xHandle));
+ } else {
+ key = QString::number(static_cast<qulonglong>(handle.eglHandle));
+ }
+ }
+ return key;
+}
+
+void MThemeDaemonServer::MemoryUsagePrinter::addPixmapCacheEntries(const QString &client,
+ const QString &imageId,
+ const QHash<QSize, const PixmapCacheEntry*> &pixmapCacheEntries,
+ QHash<QString, QString>& sharedPixmaps)
+{
+ QHashIterator<QSize, const PixmapCacheEntry*> it(pixmapCacheEntries);
+ while (it.hasNext()) {
+ it.next();
+
+ const PixmapCacheEntry *entry = it.value();
+ if (!entry) {
+ continue;
+ }
+
+ const QString key = keyForHandle(entry->handle);
+ if (key.isEmpty()) {
+ continue;
+ }
+
+ const int pixmapMemoryUsage = entry->pixmap->width() * entry->pixmap->height() * (QPixmap::defaultDepth() >> 3);
+
+ if (sharedPixmaps.contains(key)) {
+ // The entry is already available, just append the client-name to the end if the
+ // client-name has not already been added before.
+ const QChar clientSeparator(',');
+ const QString line = sharedPixmaps.value(key);
+ const int lastColumnIndex = line.lastIndexOf(columnSeparator);
+ const QString clientsLine = line.right(line.length() - lastColumnIndex - 1);
+ const QStringList clients = clientsLine.split(clientSeparator);
+ if (!clients.contains(client)) {
+ sharedPixmaps.insert(key, line + clientSeparator + client);
+
+ // If this is the second client that uses the same pixmap, the memory-usage
+ // of the first client must be adjusted by moving it to the shared memory-usage.
+ if (clients.count() == 1) {
+ sharedMemoryUsage += pixmapMemoryUsage;
+ clientMemoryUsage[clientsLine] -= pixmapMemoryUsage;
+ }
+ }
+ } else {
+ // The entry has not been added yet. Provide the size in bytes, image-ID, size in
+ // pixels and the current client-name as value content.
+ const QString width = QString::number(entry->pixmap->width());
+ const QString height = QString::number(entry->pixmap->height());
+ const QString size = QString::number(pixmapMemoryUsage);
+ QString value = size.rightJustified(8) + columnSeparator;
+ value += imageId + columnSeparator + "(";
+ value += width.rightJustified(3) + ",";
+ value += height.rightJustified(3) + ")" + columnSeparator;
+ value += client;
+
+ clientMemoryUsage[client] += pixmapMemoryUsage;
+ sharedPixmaps.insert(key, value);
+ }
+ }
+}
+
+QList<QString> MThemeDaemonServer::MemoryUsagePrinter::layoutedLines(const QList<QString> &lines) const
+{
+ int columnCount = countColumns(lines);
+
+ // maxColumnChars stores the maximum number of characters that fit
+ // into each column. Initialized with 0 per default.
+ QList<int> maxColumnChars;
+ for (int i = 0; i < columnCount; ++i) {
+ maxColumnChars.append(0);
+ }
+
+ // The layout of each line is done in two steps:
+ // 1. Calculate the maximum width for each column and remember the result
+ // in maxColumnChars
+ // 2. Align the columns for each line so that that the column-content is
+ // on the left and the rest filled with spaces.
+ QStringList layoutedLines;
+ bool calculatedMaxColumnChars = false;
+ bool layouted = false;
+ do {
+ foreach (const QString &line, lines) {
+ QString layoutedLine;
+
+ int separatorIndex = 0;
+ int oldSeparatorIndex = -1;
+ int columnIndex = 0;
+ do {
+ separatorIndex = line.indexOf(columnSeparator, oldSeparatorIndex + 1);
+ if (separatorIndex < 0) {
+ separatorIndex = line.length();
+ }
+
+ const int columnWidth = separatorIndex - oldSeparatorIndex - 1;
+ if (calculatedMaxColumnChars) {
+ // The maximum width of the column is known. Align the content of
+ // the column to the left and fill the rest with spaces.
+ Q_ASSERT(!layouted);
+ const int length = separatorIndex - oldSeparatorIndex - 1;
+ QString columnContent = line.mid(oldSeparatorIndex + 1, length);
+ const int columnGap = 1;
+ columnContent = columnContent.leftJustified(maxColumnChars[columnIndex] + columnGap);
+ layoutedLine += columnContent;
+ } else if (columnWidth > maxColumnChars[columnIndex]) {
+ maxColumnChars[columnIndex] = columnWidth;
+ }
+
+ ++columnIndex;
+ oldSeparatorIndex = separatorIndex;
+ } while (separatorIndex < line.length());
+
+ if (calculatedMaxColumnChars) {
+ layoutedLines.append(layoutedLine);
+ }
+ }
+
+ if (calculatedMaxColumnChars) {
+ layouted = true;
+ } else {
+ calculatedMaxColumnChars = true;
+ }
+ } while (!layouted);
+
+ layoutedLines.sort();
+ return layoutedLines;
+}
+
+int MThemeDaemonServer::MemoryUsagePrinter::countColumns(const QList<QString> &lines) const
+{
+ int columnCount = 0;
+
+ foreach (const QString &line, lines) {
+ const int valueColumnCount = line.count(columnSeparator) + 1;
+ if (valueColumnCount > columnCount) {
+ columnCount = valueColumnCount;
+ }
+ }
+
+ return columnCount;
+}
diff --git a/mthemedaemon/mthemedaemonserver.h b/mthemedaemon/mthemedaemonserver.h
index c0cf70ca..62e4177b 100644
--- a/mthemedaemon/mthemedaemonserver.h
+++ b/mthemedaemon/mthemedaemonserver.h
@@ -50,6 +50,7 @@ public:
static void hupSignalHandler(int unused);
static void termSignalHandler(int unused);
static void intSignalHandler(int unused);
+ static void usr1SignalHandler(int unused);
public slots:
void themeChanged(bool forceReload = false);
@@ -59,6 +60,7 @@ public slots:
void handleSigHup();
void handleSigTerm();
void handleSigInt();
+ void handleSigUsr1();
private slots:
void clientConnected();
@@ -116,6 +118,30 @@ private:
{ return (client != other.client || pixmapId != other.pixmapId); }
};
+ /**
+ * Prints the memory-usage required for all shared pixmaps to the default
+ * temporary directory (e.g. /var/tmp) as "mthemedaemonserver-stat.txt".
+ */
+ class MemoryUsagePrinter
+ {
+ public:
+ MemoryUsagePrinter(const QList<MThemeDaemonClient*> &clients);
+ void print();
+ private:
+ QString keyForHandle(const MPixmapHandle &handle) const;
+ void addPixmapCacheEntries(const QString &client,
+ const QString &imageId,
+ const QHash<QSize, const PixmapCacheEntry*> &pixmapCacheEntries,
+ QHash<QString, QString>& sharedPixmaps);
+ QList<QString> layoutedLines(const QList<QString> &lines) const;
+ int countColumns(const QList<QString> &lines) const;
+
+ const QChar columnSeparator;
+ QList<QString> lines;
+ QHash<QString, int> clientMemoryUsage;
+ int sharedMemoryUsage;
+ };
+
QLocalServer server;
QHash<QLocalSocket *, MThemeDaemonClient *> registeredClients;
MThemeDaemon daemon;
@@ -143,9 +169,11 @@ private:
static int sighupFd[2];
static int sigtermFd[2];
static int sigintFd[2];
+ static int sigusr1Fd[2];
QSocketNotifier *snHup;
QSocketNotifier *snTerm;
QSocketNotifier *snInt;
+ QSocketNotifier *snUsr1;
};
//! \internal_end
#endif
diff --git a/src/corelib/theme/mimagedirectory.cpp b/src/corelib/theme/mimagedirectory.cpp
index 595a6447..e3456d4d 100644
--- a/src/corelib/theme/mimagedirectory.cpp
+++ b/src/corelib/theme/mimagedirectory.cpp
@@ -164,6 +164,19 @@ MPixmapHandle ImageResource::pixmapHandle(const QSize &size)
}
}
+QHash<QSize, const PixmapCacheEntry*> ImageResource::pixmapCacheEntries() const
+{
+ QHash<QSize, const PixmapCacheEntry*> entries;
+
+ QHashIterator<QSize, PixmapCacheEntry*> it(cachedPixmaps);
+ while (it.hasNext()) {
+ it.next();
+ entries.insert(it.key(), it.value());
+ }
+
+ return entries;
+}
+
QImage ImageResource::loadFromFsCache(const QSize& size)
{
const QString cacheFileName = createCacheFilename(size);
diff --git a/src/corelib/theme/mimagedirectory.h b/src/corelib/theme/mimagedirectory.h
index 5ec19920..e18fd26f 100644
--- a/src/corelib/theme/mimagedirectory.h
+++ b/src/corelib/theme/mimagedirectory.h
@@ -85,13 +85,12 @@ public:
// returns a handle to pixmap, without increasing the refCount
MPixmapHandle pixmapHandle(const QSize &size);
-
- bool save(QIODevice* device, const QSize& size) const;
- bool load(QIODevice* device, const QSize& size);
QString absoluteFilePath()
{ return filePath; }
+ QHash<QSize, const PixmapCacheEntry*> pixmapCacheEntries() const;
+
protected:
virtual QImage createPixmap(const QSize &size) = 0;
// must not return an empty string