[Libreoffice-commits] online.git: common/Util.cpp common/Util.hpp kit/Kit.cpp wsd/Admin.cpp wsd/Admin.hpp wsd/AdminModel.cpp wsd/AdminModel.hpp wsd/DocumentBroker.cpp
Ashod Nakashian
ashod.nakashian at collabora.co.uk
Fri Feb 3 07:12:50 UTC 2017
common/Util.cpp | 72 +++++++++++++++++++++++++++++++++++++++++++++++--
common/Util.hpp | 10 ++++++
kit/Kit.cpp | 29 +++++++++++++++++++
wsd/Admin.cpp | 41 ++++++++++++++++++++-------
wsd/Admin.hpp | 11 ++++---
wsd/AdminModel.cpp | 48 +++++++++++++++++++++++++-------
wsd/AdminModel.hpp | 7 ++++
wsd/DocumentBroker.cpp | 8 +++++
8 files changed, 195 insertions(+), 31 deletions(-)
New commits:
commit d7a9a76ddbd0590542b6c0f765c31d7af9fe09c4
Author: Ashod Nakashian <ashod.nakashian at collabora.co.uk>
Date: Fri Feb 3 01:29:53 2017 -0500
wsd: report PSS of kit processes
Each Kit process now reports its own PSS,
which is much more accurate as they share
a significant ratio of their pages with
one another.
Admin tracks the PSS values of the Kits
and reports to the console.
Change-Id: Ifa66d17749c224f0dc211db80c44f7c913f2d6c4
Reviewed-on: https://gerrit.libreoffice.org/33864
Reviewed-by: Ashod Nakashian <ashnakash at gmail.com>
Tested-by: Ashod Nakashian <ashnakash at gmail.com>
diff --git a/common/Util.cpp b/common/Util.cpp
index 48c0acf..bb64025 100644
--- a/common/Util.cpp
+++ b/common/Util.cpp
@@ -125,7 +125,76 @@ namespace Util
return std::getenv("DISPLAY") != nullptr;
}
- int getMemoryUsage(const Poco::Process::PID pid)
+ static const char *startsWith(const char *line, const char *tag)
+ {
+ int len = strlen(tag);
+ if (!strncmp(line, tag, len))
+ {
+ while (!isdigit(line[len]) && line[len] != '\0')
+ ++len;
+
+ const auto str = std::string(line + len, strlen(line + len) - 1);
+ return line + len;
+ }
+
+ return nullptr;
+ }
+
+ std::pair<size_t, size_t> getPssAndDirtyFromSMaps(FILE* file)
+ {
+ size_t numPSSKb = 0;
+ size_t numDirtyKb = 0;
+ if (file)
+ {
+ rewind(file);
+ char line[4096] = { 0 };
+ while (fgets(line, sizeof (line), file))
+ {
+ const char *value;
+ if ((value = startsWith(line, "Private_Dirty:")) ||
+ (value = startsWith(line, "Shared_Dirty:")))
+ {
+ numDirtyKb += atoi(value);
+ }
+ else if ((value = startsWith(line, "Pss:")))
+ {
+ numPSSKb += atoi(value);
+ }
+ }
+ }
+
+ return std::make_pair(numPSSKb, numDirtyKb);
+ }
+
+ std::string getMemoryStats(FILE* file)
+ {
+ const auto pssAndDirtyKb = getPssAndDirtyFromSMaps(file);
+ std::ostringstream oss;
+ oss << "procmemstats: pid=" << getpid()
+ << " pss=" << pssAndDirtyKb.first
+ << " dirty=" << pssAndDirtyKb.second;
+ LOG_TRC("Collected " << oss.str());
+ return oss.str();
+ }
+
+ size_t getMemoryUsagePSS(const Poco::Process::PID pid)
+ {
+ if (pid > 0)
+ {
+ const auto cmd = "/proc/" + std::to_string(pid) + "/smaps";
+ FILE* fp = fopen(cmd.c_str(), "r");
+ if (fp != nullptr)
+ {
+ const auto pss = getPssAndDirtyFromSMaps(fp).first;
+ fclose(fp);
+ return pss;
+ }
+ }
+
+ return 0;
+ }
+
+ size_t getMemoryUsageRSS(const Poco::Process::PID pid)
{
if (pid == -1)
{
@@ -134,7 +203,6 @@ namespace Util
try
{
- //TODO: Instead of RSS, return PSS
const auto cmd = "ps o rss= -p " + std::to_string(pid);
FILE* fp = popen(cmd.c_str(), "r");
if (fp == nullptr)
diff --git a/common/Util.hpp b/common/Util.hpp
index 3e3d706..ad0fea8 100644
--- a/common/Util.hpp
+++ b/common/Util.hpp
@@ -82,7 +82,15 @@ namespace Util
assert(!mtx.try_lock());
}
- int getMemoryUsage(const Poco::Process::PID pid);
+ /// Returns the process PSS in KB (works only when we have perms for /proc/pid/smaps).
+ size_t getMemoryUsagePSS(const Poco::Process::PID pid);
+
+ /// Returns the process RSS in KB.
+ size_t getMemoryUsageRSS(const Poco::Process::PID pid);
+
+ /// Returns the RSS and PSS of the current process in KB.
+ /// Example: "procmemstats: pid=123 rss=12400 pss=566"
+ std::string getMemoryStats(FILE* file);
std::string replace(const std::string& s, const std::string& a, const std::string& b);
diff --git a/kit/Kit.cpp b/kit/Kit.cpp
index cb9c778..b527fda 100644
--- a/kit/Kit.cpp
+++ b/kit/Kit.cpp
@@ -419,6 +419,8 @@ public:
}
};
+static FILE* ProcSMapsFile = nullptr;
+
/// A document container.
/// Owns LOKitDocument instance and connections.
/// Manages the lifetime of a document.
@@ -1352,11 +1354,29 @@ private:
LOG_DBG("Thread started.");
+ // Update memory stats every 5 seconds.
+ const auto memStatsPeriodMs = 5000;
+ auto lastMemStatsTime = std::chrono::steady_clock::now();
+ sendTextFrame(Util::getMemoryStats(ProcSMapsFile));
+
try
{
while (!_stop && !TerminationFlag)
{
- const TileQueue::Payload input = _tileQueue->get();
+ const TileQueue::Payload input = _tileQueue->get(POLL_TIMEOUT_MS * 2);
+ if (input.empty())
+ {
+ const auto duration = (std::chrono::steady_clock::now() - lastMemStatsTime);
+ const auto durationMs = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
+ if (durationMs > memStatsPeriodMs)
+ {
+ sendTextFrame(Util::getMemoryStats(ProcSMapsFile));
+ lastMemStatsTime = std::chrono::steady_clock::now();
+ }
+
+ continue;
+ }
+
LOG_TRC("Kit Recv " << LOOLProtocol::getAbbreviatedMessage(input));
if (_stop || TerminationFlag)
@@ -1649,6 +1669,7 @@ void lokit_main(const std::string& childRoot,
{
LOG_SYS("mknod(" << jailPath.toString() << "/dev/random) failed.");
}
+
if (mknod((jailPath.toString() + "/dev/urandom").c_str(),
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH,
makedev(1, 9)) != 0)
@@ -1656,6 +1677,12 @@ void lokit_main(const std::string& childRoot,
LOG_SYS("mknod(" << jailPath.toString() << "/dev/urandom) failed.");
}
+ ProcSMapsFile = fopen("/proc/self/smaps", "r");
+ if (ProcSMapsFile == nullptr)
+ {
+ LOG_SYS("Failed to symlink /proc/self/smaps. Memory stats will be missing.");
+ }
+
LOG_INF("chroot(\"" << jailPath.toString() << "\")");
if (chroot(jailPath.toString().c_str()) == -1)
{
diff --git a/wsd/Admin.cpp b/wsd/Admin.cpp
index 6d62878..9dc608b 100644
--- a/wsd/Admin.cpp
+++ b/wsd/Admin.cpp
@@ -289,7 +289,12 @@ Admin::Admin() :
{
LOG_INF("Admin ctor.");
- _memStatsTask.reset(new MemoryStats(this));
+ std::unique_lock<std::mutex> modelLock(getLock());
+ const auto totalMem = getTotalMemoryUsage();
+ LOG_TRC("Total memory used: " << totalMem);
+ _model.addMemStats(totalMem);
+
+ _memStatsTask.reset(new MemoryStatsTask(this));
_memStatsTimer.schedule(_memStatsTask.get(), _memStatsTaskInterval, _memStatsTaskInterval);
_cpuStatsTask = new CpuStats(this);
@@ -323,19 +328,18 @@ void Admin::rmDoc(const std::string& docKey)
_model.removeDocument(docKey);
}
-void MemoryStats::run()
+void MemoryStatsTask::run()
{
std::unique_lock<std::mutex> modelLock(_admin->getLock());
- AdminModel& model = _admin->getModel();
- const auto totalMem = model.getKitsMemoryUsage();
+ const auto totalMem = _admin->getTotalMemoryUsage();
if (totalMem != _lastTotalMemory)
{
LOG_TRC("Total memory used: " << totalMem);
+ _lastTotalMemory = totalMem;
}
- _lastTotalMemory = totalMem;
- model.addMemStats(totalMem);
+ _admin->getModel().addMemStats(totalMem);
}
void CpuStats::run()
@@ -349,7 +353,7 @@ void Admin::rescheduleMemTimer(unsigned interval)
{
_memStatsTask->cancel();
_memStatsTaskInterval = interval;
- _memStatsTask.reset(new MemoryStats(this));
+ _memStatsTask.reset(new MemoryStatsTask(this));
_memStatsTimer.schedule(_memStatsTask.get(), _memStatsTaskInterval, _memStatsTaskInterval);
LOG_INF("Memory stats interval changed - New interval: " << interval);
}
@@ -365,10 +369,19 @@ void Admin::rescheduleCpuTimer(unsigned interval)
unsigned Admin::getTotalMemoryUsage()
{
- unsigned totalMem = Util::getMemoryUsage(_forKitPid);
- totalMem += _memStatsTask->getLastTotalMemory();
- totalMem += Util::getMemoryUsage(Poco::Process::id());
-
+ Util::assertIsLocked(_modelMutex);
+
+ // PSS would be wrong for forkit since we will have one or
+ // more prespawned kits that will share their pages with forkit,
+ // but we don't count the kits unless and until a document is loaded.
+ // So RSS is a decent approximation (albeit slightly on the high side).
+ const size_t forkitRssKb = Util::getMemoryUsageRSS(_forKitPid);
+ const size_t wsdPssKb = Util::getMemoryUsagePSS(Poco::Process::id());
+ const size_t kitsPssKb = _model.getKitsMemoryUsage();
+ const size_t totalMem = wsdPssKb + forkitRssKb + kitsPssKb;
+
+ LOG_TRC("Total mem: " << totalMem << ", wsd pss: " << wsdPssKb <<
+ ", forkit rss: " << forkitRssKb << ", kits pss: " << kitsPssKb);
return totalMem;
}
@@ -393,4 +406,10 @@ void Admin::updateLastActivityTime(const std::string& docKey)
_model.updateLastActivityTime(docKey);
}
+void Admin::updateMemoryPss(const std::string& docKey, int pss)
+{
+ std::unique_lock<std::mutex> modelLock(_modelMutex);
+ _model.updateMemoryPss(docKey, pss);
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/Admin.hpp b/wsd/Admin.hpp
index d02a195..6e7ef58 100644
--- a/wsd/Admin.hpp
+++ b/wsd/Admin.hpp
@@ -48,7 +48,7 @@ private:
bool _isAuthenticated;
};
-class MemoryStats;
+class MemoryStatsTask;
/// An admin command processor.
class Admin
@@ -97,6 +97,7 @@ public:
std::unique_lock<std::mutex> getLock() { return std::unique_lock<std::mutex>(_modelMutex); }
void updateLastActivityTime(const std::string& docKey);
+ void updateMemoryPss(const std::string& docKey, int pss);
private:
Admin();
@@ -107,7 +108,7 @@ private:
int _forKitPid;
Poco::Util::Timer _memStatsTimer;
- std::unique_ptr<MemoryStats> _memStatsTask;
+ std::unique_ptr<MemoryStatsTask> _memStatsTask;
unsigned _memStatsTaskInterval = 5000;
Poco::Util::Timer _cpuStatsTimer;
@@ -116,17 +117,17 @@ private:
};
/// Memory statistics.
-class MemoryStats : public Poco::Util::TimerTask
+class MemoryStatsTask : public Poco::Util::TimerTask
{
public:
- MemoryStats(Admin* admin)
+ MemoryStatsTask(Admin* admin)
: _admin(admin),
_lastTotalMemory(0)
{
LOG_DBG("Memory stat ctor");
}
- ~MemoryStats()
+ ~MemoryStatsTask()
{
LOG_DBG("Memory stat dtor");
}
diff --git a/wsd/AdminModel.cpp b/wsd/AdminModel.cpp
index 43d7d46..87a8336 100644
--- a/wsd/AdminModel.cpp
+++ b/wsd/AdminModel.cpp
@@ -131,14 +131,13 @@ std::string AdminModel::query(const std::string& command)
/// Returns memory consumed by all active loolkit processes
unsigned AdminModel::getKitsMemoryUsage()
{
- Poco::Timestamp ts;
unsigned totalMem = 0;
unsigned docs = 0;
for (const auto& it : _documents)
{
if (!it.second.isExpired())
{
- const auto bytes = Util::getMemoryUsage(it.second.getPid());
+ const auto bytes = it.second.getMemoryPss();
if (bytes > 0)
{
totalMem += bytes;
@@ -149,9 +148,8 @@ unsigned AdminModel::getKitsMemoryUsage()
if (docs > 0)
{
- LOG_TRC("Got total Kits memory of " << totalMem << " bytes in " << ts.elapsed()/1001. <<
- " ms for " << docs << " docs, avg: " << static_cast<double>(totalMem) / docs <<
- " bytes / doc in " << ts.elapsed() / 1000. / docs << " ms per doc.");
+ LOG_TRC("Got total Kits memory of " << totalMem << " bytes for " << docs <<
+ " docs, avg: " << static_cast<double>(totalMem) / docs << " bytes / doc.");
}
return totalMem;
@@ -256,16 +254,35 @@ void AdminModel::addDocument(const std::string& docKey, Poco::Process::PID pid,
ret.first->second.addView(sessionId);
LOG_DBG("Added admin document [" << docKey << "].");
- // Notify the subscribers
- const unsigned memUsage = Util::getMemoryUsage(pid);
- std::ostringstream oss;
std::string encodedFilename;
Poco::URI::encode(filename, " ", encodedFilename);
+
+ // Notify the subscribers
+ std::ostringstream oss;
oss << "adddoc "
<< pid << ' '
<< encodedFilename << ' '
- << sessionId << ' '
- << memUsage;
+ << sessionId << ' ';
+
+ // We have to wait until the kit sends us its PSS.
+ // Here we guestimate until we get an update.
+ if (_documents.size() < 2) // If we aren't the only one.
+ {
+ if (_memStats.empty())
+ {
+ oss << 0;
+ }
+ else
+ {
+ // Estimate half as much as wsd+forkit.
+ oss << _memStats.front() / 2;
+ }
+ }
+ else
+ {
+ oss << _documents.begin()->second.getMemoryPss();
+ }
+
notify(oss.str());
}
@@ -360,7 +377,7 @@ std::string AdminModel::getDocuments() const
oss << it.second.getPid() << ' '
<< encodedFilename << ' '
<< it.second.getActiveViews() << ' '
- << Util::getMemoryUsage(it.second.getPid()) << ' '
+ << it.second.getMemoryPss() << ' '
<< it.second.getElapsedTime() << ' '
<< it.second.getIdleTime() << " \n ";
}
@@ -382,4 +399,13 @@ void AdminModel::updateLastActivityTime(const std::string& docKey)
}
}
+void AdminModel::updateMemoryPss(const std::string& docKey, int pss)
+{
+ auto docIt = _documents.find(docKey);
+ if (docIt != _documents.end())
+ {
+ docIt->second.updateMemoryPss(pss);
+ }
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/wsd/AdminModel.hpp b/wsd/AdminModel.hpp
index 1251470..0e46ca5 100644
--- a/wsd/AdminModel.hpp
+++ b/wsd/AdminModel.hpp
@@ -49,6 +49,7 @@ public:
: _docKey(docKey),
_pid(pid),
_filename(filename),
+ _memoryPss(0),
_start(std::time(nullptr)),
_lastActivity(_start)
{
@@ -73,6 +74,8 @@ public:
const std::map<std::string, View>& getViews() const { return _views; }
void updateLastActivityTime() { _lastActivity = std::time(nullptr); }
+ void updateMemoryPss(int pss) { _memoryPss = pss; }
+ int getMemoryPss() const { return _memoryPss; }
private:
const std::string _docKey;
@@ -83,6 +86,8 @@ private:
unsigned _activeViews = 0;
/// Hosted filename
std::string _filename;
+ /// The PSS of the document's Kit process.
+ int _memoryPss;
std::time_t _start;
std::time_t _lastActivity;
@@ -173,6 +178,7 @@ public:
void removeDocument(const std::string& docKey);
void updateLastActivityTime(const std::string& docKey);
+ void updateMemoryPss(const std::string& docKey, int pss);
private:
std::string getMemStats();
@@ -187,6 +193,7 @@ private:
std::map<int, Subscriber> _subscribers;
std::map<std::string, Document> _documents;
+ /// The last N total memory PSS.
std::list<unsigned> _memStats;
unsigned _memStatsSize = 100;
diff --git a/wsd/DocumentBroker.cpp b/wsd/DocumentBroker.cpp
index 91f3ce4..c454612 100644
--- a/wsd/DocumentBroker.cpp
+++ b/wsd/DocumentBroker.cpp
@@ -747,6 +747,14 @@ bool DocumentBroker::handleInput(const std::vector<char>& payload)
LOG_CHECK_RET(kind != "", false);
Util::alertAllUsers(cmd, kind);
}
+ else if (command == "procmemstats:")
+ {
+ int pss;
+ if (message->getTokenInteger("pss", pss))
+ {
+ Admin::instance().updateMemoryPss(_docKey, pss);
+ }
+ }
else
{
LOG_ERR("Unexpected message: [" << msg << "].");
More information about the Libreoffice-commits
mailing list