[Libreoffice-commits] online.git: 3 commits - loleaflet/build loleaflet/debug loleaflet/.eslintignore loleaflet/src loolwsd/Admin.cpp loolwsd/Admin.hpp loolwsd/AdminModel.hpp
Pranav Kant
pranavk at collabora.com
Sun Mar 13 14:15:57 UTC 2016
loleaflet/.eslintignore | 1
loleaflet/build/adminDeps.js | 12 +
loleaflet/debug/document/admin.html | 9
loleaflet/debug/document/adminAnalytics.html | 107 +++++++++++
loleaflet/debug/document/adminSettings.html | 115 ++++++++++++
loleaflet/src/admin/AdminSocketAnalytics.js | 245 +++++++++++++++++++++++++++
loleaflet/src/admin/AdminSocketBase.js | 3
loleaflet/src/admin/AdminSocketOverview.js | 86 +++++----
loleaflet/src/admin/AdminSocketSettings.js | 59 ++++++
loleaflet/src/admin/Util.js | 13 +
loleaflet/src/control/Control.Styles.js | 10 -
loolwsd/Admin.cpp | 170 +++++++++++++++++-
loolwsd/Admin.hpp | 64 +++++++
loolwsd/AdminModel.hpp | 137 +++++++++++++--
14 files changed, 965 insertions(+), 66 deletions(-)
New commits:
commit 7197dab02993ecd791148d38e984b014bdf65881
Author: Pranav Kant <pranavk at collabora.com>
Date: Sun Mar 13 19:23:08 2016 +0530
loleaflet: Fix lint errors
Change-Id: I4023c81cad92b6f617076b39d3cdbe34e231ede3
diff --git a/loleaflet/src/control/Control.Styles.js b/loleaflet/src/control/Control.Styles.js
index 13671cb..3ab5914 100644
--- a/loleaflet/src/control/Control.Styles.js
+++ b/loleaflet/src/control/Control.Styles.js
@@ -135,11 +135,11 @@ L.Control.Styles = L.Control.extend({
// For impress documents, LOK STATE_CHANGED callback return these internal names
// which are different from what is returned by initial .uno:StyleApply.
// Convert these names to our stored internal names before processing
- var impressMapping = { "Titel":"title","Untertitel":"subtitle",
- "Gliederung 1":"outline1","Gliederung 2":"outline2","Gliederung 3":"outline3",
- "Gliederung 4":"outline4","Gliederung 5":"outline5","Gliederung 6":"outline6",
- "Gliederung 7":"outline7","Gliederung 8":"outline8","Gliederung 9":"outline9",
- "Hintergrund":"background","Hintergrundobjekte":"backgroundobjects","Notizen":"notes"};
+ var impressMapping = {'Titel':'title', 'Untertitel':'subtitle',
+ 'Gliederung 1':'outline1', 'Gliederung 2':'outline2', 'Gliederung 3':'outline3',
+ 'Gliederung 4':'outline4', 'Gliederung 5':'outline5', 'Gliederung 6':'outline6',
+ 'Gliederung 7':'outline7', 'Gliederung 8':'outline8', 'Gliederung 9':'outline9',
+ 'Hintergrund':'background', 'Hintergrundobjekte':'backgroundobjects', 'Notizen':'notes'};
// For impress documents, template name is prefixed with style name.
// Strip the template name until we support it
commit 8519a6a4620cafe98a31d4c85b21ad31109865b1
Author: Pranav Kant <pranavk at collabora.com>
Date: Mon Mar 7 01:36:51 2016 +0530
loleaflet: Support for memory subscription
Library for creating graphs: D3.js
License: BSD
Currently only being served by a CDN, not copied into the source
tree.
Change-Id: Ib99ec9011da489fc42799c3610612a8c3c8e5c2b
diff --git a/loleaflet/.eslintignore b/loleaflet/.eslintignore
index dfbe7ac..45d125b 100644
--- a/loleaflet/.eslintignore
+++ b/loleaflet/.eslintignore
@@ -1,2 +1,3 @@
dist/scrollbar
dist/contextMenu
+src/admin/Base.js
diff --git a/loleaflet/build/adminDeps.js b/loleaflet/build/adminDeps.js
index 5286a6e..25757c4 100644
--- a/loleaflet/build/adminDeps.js
+++ b/loleaflet/build/adminDeps.js
@@ -20,6 +20,18 @@ var adminDeps = {
src: ['admin/AdminSocketOverview.js'],
desc: 'Socket to handle messages in overview page.',
deps: ['AdminSocketBase']
+ },
+
+ AdminSocketAnalytics: {
+ src: ['admin/AdminSocketAnalytics.js'],
+ desc: 'Socket to handle messages in analytics page.',
+ deps: ['AdminSocketBase']
+ },
+
+ AdminSocketSettings: {
+ src: ['admin/AdminSocketSettings.js'],
+ desc: 'Socket to handle settings from server',
+ deps: ['AdminSocketBase']
}
};
diff --git a/loleaflet/debug/document/admin.html b/loleaflet/debug/document/admin.html
index 4da6645..2e66709 100644
--- a/loleaflet/debug/document/admin.html
+++ b/loleaflet/debug/document/admin.html
@@ -65,9 +65,8 @@
</div>
<div id="navbar" class="navbar-collapse collapse">
<ul class="nav navbar-nav navbar-right">
- <li><a href="#">Dashboard</a></li>
- <li><a href="#">Settings</a></li>
- <li><a href="#">Profile</a></li>
+ <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+ <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
<li><a href="#">Help</a></li>
</ul>
<form class="navbar-form navbar-right">
@@ -82,9 +81,7 @@
<div class="col-sm-3 col-md-2 sidebar">
<ul class="nav nav-sidebar">
<li class="active"><a href="#">Overview <span class="sr-only">(current)</span></a></li>
- <li><a href="#">Reports</a></li>
- <li><a href="#">Analytics</a></li>
- <li><a href="#">Export</a></li>
+ <li><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
</ul>
</div>
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
diff --git a/loleaflet/debug/document/adminAnalytics.html b/loleaflet/debug/document/adminAnalytics.html
new file mode 100644
index 0000000..903be34
--- /dev/null
+++ b/loleaflet/debug/document/adminAnalytics.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <title>LibreOffice Online - Admin console</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="../../dist/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+ <link href="../../dist/bootstrap/assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
+
+ <!-- Custom styles for this template -->
+ <link href="../../dist/bootstrap/dashboard.css" rel="stylesheet">
+
+ <link rel="stylesheet" href="../../dist/dialog/vex.css" />
+ <link rel="stylesheet" href="../../dist/dialog/vex-theme-plain.css" />
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script>window.jQuery || document.write('<script src="../../dist/bootstrap/assets/js/vendor/jquery.min.js"><\/script>')</script>
+ <script src="../../dist/dialog/vex.combined.min.js"></script>
+ <script>vex.defaultOptions.className = 'vex-theme-plain';</script>
+ <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+ <script src="../../dist/admin-src.js"></script>
+ <script>
+
+ function getParameterByName(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : results[1].replace(/\+/g, " ");
+ }
+
+ var host = getParameterByName('host');
+ new AdminSocketAnalytics(host);
+
+ </script>
+
+ <nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">LibreOffice Online - Admin console</a>
+ </div>
+ <div id="navbar" class="navbar-collapse collapse">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+ <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
+ <li><a href="#">Help</a></li>
+ </ul>
+ <form class="navbar-form navbar-right">
+ <input type="text" class="form-control" placeholder="Search...">
+ </form>
+ </div>
+ </div>
+ </nav>
+
+ <div class="container-fluid">
+ <div class="row">
+ <div class="col-sm-3 col-md-2 sidebar">
+ <ul class="nav nav-sidebar">
+ <li><a href="admin.html?host=ws://localhost:9989">Overview <span class="sr-only">(current)</span></a></li>
+ <li class="active"><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
+ </ul>
+ </div>
+ <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+ <h1 class="page-header">Graphs</h1>
+ <div class="graph-container">
+ <div class="jumbotron">
+ <svg id="visualisation" width="1000" height="500"></svg>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
+ <script src="../../dist/bootstrap/js/bootstrap.min.js"></script>
+ <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+ <script src="../../dist/bootstrap/assets/js/vendor/holder.min.js"></script>
+ <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+ <script src="../../dist/bootstrap/assets/js/ie10-viewport-bug-workaround.js"></script>
+ </body>
+</html>
diff --git a/loleaflet/debug/document/adminSettings.html b/loleaflet/debug/document/adminSettings.html
new file mode 100644
index 0000000..0ad46c1
--- /dev/null
+++ b/loleaflet/debug/document/adminSettings.html
@@ -0,0 +1,115 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
+ <meta name="description" content="">
+ <meta name="author" content="">
+
+ <title>LibreOffice Online - Admin console</title>
+
+ <!-- Bootstrap core CSS -->
+ <link href="../../dist/bootstrap/css/bootstrap.min.css" rel="stylesheet">
+
+ <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+ <link href="../../dist/bootstrap/assets/css/ie10-viewport-bug-workaround.css" rel="stylesheet">
+
+ <!-- Custom styles for this template -->
+ <link href="../../dist/bootstrap/dashboard.css" rel="stylesheet">
+
+ <link rel="stylesheet" href="../../dist/dialog/vex.css" />
+ <link rel="stylesheet" href="../../dist/dialog/vex-theme-plain.css" />
+
+ <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
+ <!--[if lt IE 9]>
+ <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
+ <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
+ <![endif]-->
+ </head>
+
+ <body>
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script>window.jQuery || document.write('<script src="../../dist/bootstrap/assets/js/vendor/jquery.min.js"><\/script>')</script>
+ <script src="../../dist/dialog/vex.combined.min.js"></script>
+ <script>vex.defaultOptions.className = 'vex-theme-plain';</script>
+ <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
+ <script src="../../dist/admin-src.js"></script>
+ <script>
+
+ function getParameterByName(name) {
+ name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
+ var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+ results = regex.exec(location.search);
+ return results === null ? "" : results[1].replace(/\+/g, " ");
+ }
+
+ var host = getParameterByName('host');
+ var settingsSocket = new AdminSocketSettings(host);
+
+ </script>
+
+ <nav class="navbar navbar-inverse navbar-fixed-top">
+ <div class="container-fluid">
+ <div class="navbar-header">
+ <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
+ <span class="sr-only">Toggle navigation</span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ <span class="icon-bar"></span>
+ </button>
+ <a class="navbar-brand" href="#">LibreOffice Online - Admin console</a>
+ </div>
+ <div id="navbar" class="navbar-collapse collapse">
+ <ul class="nav navbar-nav navbar-right">
+ <li><a href="admin.html?host=ws://localhost:9989">Dashboard</a></li>
+ <li><a href="adminSettings.html?host=ws://localhost:9989">Settings</a></li>
+ <li><a href="#">Help</a></li>
+ </ul>
+ <form class="navbar-form navbar-right">
+ <input type="text" class="form-control" placeholder="Search...">
+ </form>
+ </div>
+ </div>
+ </nav>
+
+ <div class="container-fluid">
+ <div class="row">
+ <div class="col-sm-3 col-md-2 sidebar">
+ <ul class="nav nav-sidebar">
+ <li><a href="admin.html?host=ws://localhost:9989">Overview <span class="sr-only">(current)</span></a></li>
+ <li><a href="adminAnalytics.html?host=ws://localhost:9989">Analytics</a></li>
+ </ul>
+ </div>
+ <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
+ <h1 class="page-header">Settings</h1>
+ <form id="admin_settings">
+ <label for="mem_stats_size">Memory Stats Cache size</label>
+ <input type="text" id="mem_stats_size" name="Memory Stats Size"><br/>
+ <label for="mem_stats_interval">Memory Stats Interval (in ms)</label>
+ <input type="text" id="mem_stats_interval" name="Memory Stats Interval"><br/>
+
+ <label for="cpu_stats_size">Cpu Stats Cache size</label>
+ <input type="text" id="cpu_stats_size" name="Cpu Stats Size"><br/>
+ <label for="cpu_stats_interval">Cpu Stats Interval (in ms)</label>
+ <input type="text" id="cpu_stats_interval" name="Cpu Stats Interval"><br/>
+ <input type="submit" value="Save"/><br/>
+ </form>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- Bootstrap core JavaScript
+ ================================================== -->
+ <!-- Placed at the end of the document so the pages load faster -->
+ <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
+ <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
+ <script src="../../dist/bootstrap/js/bootstrap.min.js"></script>
+ <!-- Just to make our placeholder images work. Don't actually copy the next line! -->
+ <script src="../../dist/bootstrap/assets/js/vendor/holder.min.js"></script>
+ <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
+ <script src="../../dist/bootstrap/assets/js/ie10-viewport-bug-workaround.js"></script>
+ </body>
+</html>
diff --git a/loleaflet/src/admin/AdminSocketAnalytics.js b/loleaflet/src/admin/AdminSocketAnalytics.js
new file mode 100644
index 0000000..2f34b76
--- /dev/null
+++ b/loleaflet/src/admin/AdminSocketAnalytics.js
@@ -0,0 +1,245 @@
+/*
+ Socket to be intialized on opening the analytics page in Admin console
+ containing various graphs to show to the user on specified interval
+*/
+
+/* global d3 Util AdminSocketBase */
+var AdminSocketAnalytics = AdminSocketBase.extend({
+ constructor: function(host) {
+ this.base(host);
+ },
+
+ _memStatsData: [],
+ _cpuStatsData: [],
+
+ _memStatsSize: 0,
+ _memStatsInterval: 0,
+
+ _cpuStatsSize: 0,
+ _cpuStatsInterval: 0,
+
+ _initMemStatsData: function(memStatsSize, memStatsInterval, reset) {
+ if (reset) {
+ this._memStatsData = [];
+ }
+
+ var offset = this._memStatsData.length * memStatsInterval;
+ for (var i = 0; i < memStatsSize; i++) {
+ this._memStatsData.unshift({time: -(offset), value: 0});
+ offset += memStatsInterval;
+ }
+ },
+
+ _initCpuStatsData: function() {
+ for (var i = 0; i < this._cpuStatsSize; i++) {
+ this._cpuStatsData.push({time: -((this._cpuStatsSize - i - 1) * this._cpuStatsInterval), value: 0});
+ }
+ },
+
+ onSocketOpen: function() {
+ this.socket.send('subscribe mem_stats cpu_stats settings');
+ this.socket.send('settings');
+ this.socket.send('mem_stats');
+ },
+
+ _createMemData: function() {
+ for (var i = this._memStatsRawData.length - 1, j = this._memStatsData.length - 1; i >= 0 && j >= 0; i--, j--) {
+ this._memStatsData[j].value = parseInt(this._memStatsRawData[i]);
+ }
+ },
+
+ _d3xAxis: null,
+ _d3yAxis: null,
+ _d3line: null,
+ _xScale: null,
+ _yScale: null,
+
+ _graphWidth: 1000,
+ _graphHeight: 500,
+ _graphMargins: {
+ top: 20,
+ right: 20,
+ bottom: 20,
+ left: 100
+ },
+
+ _setUpAxis: function() {
+ this._xScale = d3.scale.linear().range([this._graphMargins.left, this._graphWidth - this._graphMargins.right]).domain([d3.min(this._memStatsData, function(d) {
+ return d.time;
+ }), d3.max(this._memStatsData, function(d) {
+ return d.time;
+ })]);
+
+
+ this._yScale = d3.scale.linear().range([this._graphHeight - this._graphMargins.bottom, this._graphMargins.top]).domain([d3.min(this._memStatsData, function(d) {
+ return d.value;
+ }), d3.max(this._memStatsData, function(d) {
+ return d.value;
+ })]);
+
+ this._d3xAxis = d3.svg.axis()
+ .scale(this._xScale)
+ .tickFormat(function(d) {
+ d = Math.abs(d / 1000);
+ var units = ['s', 'min', 'hr'];
+ for (var i = 0; i < units.length && Math.abs(d) >= 60; i++) {
+ d = parseInt(d / 60);
+ }
+ return parseInt(d) + units[i] + ' ago';
+ });
+
+ this._d3yAxis = d3.svg.axis()
+ .scale(this._yScale)
+ .tickFormat(function (d) {
+ return Util.humanize(d);
+ })
+ .orient('left');
+
+ var xScale = this._xScale;
+ var yScale = this._yScale;
+
+ this._d3line = d3.svg.line()
+ .x(function(d) {
+ return xScale(d.time);
+ })
+ .y(function(d) {
+ return yScale(d.value);
+ });
+ },
+
+ _createMemGraph: function() {
+ var vis = d3.select('#visualisation');
+
+ this._setUpAxis();
+
+ vis.append('svg:g')
+ .attr('class', 'x-axis')
+ .attr('transform', 'translate(0,' + (this._graphHeight - this._graphMargins.bottom) + ')')
+ .call(this._d3xAxis);
+
+ vis.append('svg:g')
+ .attr('class', 'y-axis')
+ .attr('transform', 'translate(' + this._graphMargins.left + ',0)')
+ .call(this._d3yAxis);
+
+ vis.append('svg:path')
+ .attr('d', this._d3line(this._memStatsData))
+ .attr('class', 'line')
+ .attr('stroke', 'blue')
+ .attr('stroke-width', 2)
+ .attr('fill', 'none');
+ },
+
+ _addNewMemData: function(data) {
+ // make a space for new data
+ for (var i = this._memStatsData.length - 1; i > 0; i--) {
+ this._memStatsData[i].time = this._memStatsData[i - 1].time;
+ }
+
+ // push new data at time '0'
+ this._memStatsData.push({time: 0, value: parseInt(data)});
+
+ // remove extra items
+ if (this._memStatsData.length > this._memStatsSize) {
+ this._memStatsData.shift();
+ }
+ },
+
+ _updateMemGraph: function() {
+ var svg = d3.select('#visualisation');
+
+ this._setUpAxis();
+
+ svg.select('.line')
+ .attr('d', this._d3line(this._memStatsData));
+
+ svg.select('.x-axis')
+ .call(this._d3xAxis);
+
+ svg.select('.y-axis')
+ .call(this._d3yAxis);
+ },
+
+ onSocketMessage: function(e) {
+ var textMsg;
+ if (typeof e.data === 'string') {
+ textMsg = e.data;
+ }
+ else {
+ textMsg = '';
+ }
+
+
+ if (textMsg.startsWith('settings')) {
+ textMsg = textMsg.substring('settings '.length);
+ textMsg = textMsg.split(' ');
+
+ //TODO: Add CPU statistics
+ var memStatsSize, memStatsInterval, cpuStatsSize, cpuStatsInterval;
+ var i, j, data;
+ memStatsSize = this._memStatsSize;
+ memStatsInterval = this._memStatsInterval;
+ cpuStatsSize = this._cpuStatsSize;
+ cpuStatsInterval = this._cpuStatsInterval;
+ for (i = 0; i < textMsg.length; i++) {
+ var setting = textMsg[i].split('=');
+ if (setting[0] === 'mem_stats_size') {
+ memStatsSize = parseInt(setting[1]);
+ }
+ else if (setting[0] === 'mem_stats_interval') {
+ memStatsInterval = parseInt(setting[1]);
+ }
+ else if (setting[0] === 'cpu_stats_size') {
+ cpuStatsSize = parseInt(setting[1]);
+ }
+ else if (setting[0] === 'cpu_stats_interval') {
+ cpuStatsInterval = parseInt(setting[1]);
+ }
+ }
+
+ // Fix the axes according to changed data
+ if (memStatsInterval !== this._memStatsInterval) {
+ // We can possibly reuse the data with a bit of work
+ this._initMemStatsData(memStatsSize, memStatsInterval, true);
+ }
+ else if (memStatsSize > this._memStatsSize) {
+ this._initMemStatsData(memStatsSize - this._memStatsSize, memStatsInterval, false);
+ }
+ else {
+ // just strip the extra items
+ for (i = 0; i < this._memStatsSize - memStatsSize; i++) {
+ this._memStatsData.shift();
+ }
+ }
+
+ this._memStatsSize = memStatsSize;
+ this._memStatsInterval = memStatsInterval;
+ this._cpuStatsSize = cpuStatsSize;
+ this._cpuStatsInterval = cpuStatsInterval;
+ }
+ else if (textMsg.startsWith('mem_stats') ||
+ textMsg.startsWith('cpu_stats')) {
+ textMsg = textMsg.split(' ')[1];
+ if (textMsg.endsWith(',')) {
+ // This is the result of query, not notification
+ data = textMsg.substring(0, textMsg.length - 1).split(',');
+ for (i = this._memStatsData.length - 1, j = data.length - 1; i >= 0 && j >= 0; i--, j--) {
+ this._memStatsData[i].value = parseInt(data[j]);
+ }
+
+ //this._createMemData(data);
+ this._createMemGraph();
+ }
+ else {
+ // this is a notification data; append to _memStatsData
+ data = textMsg.trim();
+ this._addNewMemData(data);
+ this._updateMemGraph();
+ }
+ }
+ },
+
+ onSocketClose: function() {
+ clearInterval(this._basicStatsIntervalId);
+ }
+});
diff --git a/loleaflet/src/admin/AdminSocketBase.js b/loleaflet/src/admin/AdminSocketBase.js
index f82bcfc..c0216d0 100644
--- a/loleaflet/src/admin/AdminSocketBase.js
+++ b/loleaflet/src/admin/AdminSocketBase.js
@@ -1,6 +1,9 @@
/*
Abstract class
*/
+
+/* global vex Base */
+/* exported AdminSocketBase */
var AdminSocketBase = Base.extend({
socket: null,
diff --git a/loleaflet/src/admin/AdminSocketOverview.js b/loleaflet/src/admin/AdminSocketOverview.js
index 6371d8e..ea62e55 100644
--- a/loleaflet/src/admin/AdminSocketOverview.js
+++ b/loleaflet/src/admin/AdminSocketOverview.js
@@ -1,6 +1,7 @@
/*
Socket to be intialized on opening the overview page in Admin console
*/
+/* global vex $ Util AdminSocketBase */
var AdminSocketOverview = AdminSocketBase.extend({
constructor: function(host) {
this.base(host);
@@ -40,7 +41,7 @@ var AdminSocketOverview = AdminSocketBase.extend({
$('#rowContextMenu').hide();
});
- $('#rowContextMenu').on('click', 'a', function(e) {
+ $('#rowContextMenu').on('click', 'a', function() {
vex.dialog.confirm({
message: 'Are you sure you want to kill this child ?',
callback: function(value) {
@@ -59,86 +60,100 @@ var AdminSocketOverview = AdminSocketBase.extend({
if (typeof e.data === 'string') {
textMsg = e.data;
}
- else
+ else {
textMsg = '';
+ }
var tableContainer = document.getElementById('doclist');
+ var rowContainer;
+ var pidEle, urlEle, viewsEle, memEle, docEle;
+ var nViews, nTotalViews;
+ var docProps, sPid, sUrl, sViews, sMem;
if (textMsg.startsWith('documents')) {
var documents = textMsg.substring('documents'.length);
documents = documents.trim().split('\n');
for (var i = 0; i < documents.length; i++) {
- if (documents[i] == '')
+ if (documents[i] === '') {
continue;
- var docProps = documents[i].trim().split(' ');
- var sPid = docProps[0];
- var sUrl = docProps[1];
- var sViews = docProps[2];
- var sMem = docProps[3];
+ }
+ docProps = documents[i].trim().split(' ');
+ sPid = docProps[0];
+ sUrl = docProps[1];
+ sViews = docProps[2];
+ sMem = docProps[3];
if (sUrl === '0') {
continue;
}
- var rowContainer = document.createElement('tr');
+ rowContainer = document.createElement('tr');
rowContainer.id = 'doc' + sPid;
tableContainer.appendChild(rowContainer);
- var pidEle = document.createElement('td');
+ pidEle = document.createElement('td');
pidEle.innerHTML = sPid;
rowContainer.appendChild(pidEle);
- var urlEle = document.createElement('td');
+
+ urlEle = document.createElement('td');
urlEle.innerHTML = sUrl;
rowContainer.appendChild(urlEle);
- var viewsEle = document.createElement('td');
+
+ viewsEle = document.createElement('td');
viewsEle.id = 'docview' + sPid;
viewsEle.innerHTML = sViews;
rowContainer.appendChild(viewsEle);
- var memEle = document.createElement('td');
- memEle.innerHTML = sMem;
+
+ memEle = document.createElement('td');
+ memEle.innerHTML = Util.humanize(parseInt(sMem));
rowContainer.appendChild(memEle);
}
}
else if (textMsg.startsWith('addview')) {
- var sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
- var nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
+ sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
+ nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
document.getElementById('docview' + sPid).innerHTML = nViews + 1;
- var nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
+ nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
document.getElementById('active_users_count').innerHTML = nTotalViews + 1;
}
else if (textMsg.startsWith('rmview')) {
- var sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
- var nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
+ sPid = textMsg.substring('addview'.length).trim().split(' ')[0];
+ nViews = parseInt(document.getElementById('docview' + sPid).innerHTML);
document.getElementById('docview' + sPid).innerHTML = nViews - 1;
- var nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
+ nTotalViews = parseInt(document.getElementById('active_users_count').innerHTML);
document.getElementById('active_users_count').innerHTML = nTotalViews - 1;
}
else if (textMsg.startsWith('document')) {
textMsg = textMsg.substring('document'.length);
- var docProps = textMsg.trim().split(' ');
- var sPid = docProps[0];
- var sUrl = docProps[1];
- var sMem = docProps[2];
- var docEle = document.getElementById('doc' + sPid);
+ docProps = textMsg.trim().split(' ');
+ sPid = docProps[0];
+ sUrl = docProps[1];
+ sMem = docProps[2];
+
+ docEle = document.getElementById('doc' + sPid);
if (docEle) {
tableContainer.removeChild(docEle);
}
if (sUrl === '0') {
return;
}
- var rowContainer = document.createElement('tr');
+
+ rowContainer = document.createElement('tr');
rowContainer.id = 'doc' + docProps[0];
tableContainer.appendChild(rowContainer);
- var pidEle = document.createElement('td');
+ pidEle = document.createElement('td');
pidEle.innerHTML = docProps[0];
rowContainer.appendChild(pidEle);
- var urlEle = document.createElement('td');
+
+ urlEle = document.createElement('td');
urlEle.innerHTML = docProps[1];
rowContainer.appendChild(urlEle);
- var viewsEle = document.createElement('td');
+
+ viewsEle = document.createElement('td');
viewsEle.innerHTML = 0;
viewsEle.id = 'docview' + docProps[0];
rowContainer.appendChild(viewsEle);
- var memEle = document.createElement('td');
- memEle.innerHTML = sMem;
+
+ memEle = document.createElement('td');
+ memEle.innerHTML = Util.humanize(parseInt(sMem));
rowContainer.appendChild(memEle);
var totalUsersEle = document.getElementById('active_docs_count');
@@ -152,13 +167,16 @@ var AdminSocketOverview = AdminSocketBase.extend({
var sCommand = textMsg[0];
var nData = parseInt(textMsg[1]);
+ if (sCommand === 'total_mem') {
+ nData = Util.humanize(nData);
+ }
document.getElementById(sCommand).innerHTML = nData;
}
else if (textMsg.startsWith('rmdoc')) {
textMsg = textMsg.substring('rmdoc'.length);
- var docProps = textMsg.trim().split(' ');
- var sPid = docProps[0];
- var docEle = document.getElementById('doc' + sPid);
+ docProps = textMsg.trim().split(' ');
+ sPid = docProps[0];
+ docEle = document.getElementById('doc' + sPid);
if (docEle) {
tableContainer.removeChild(docEle);
}
diff --git a/loleaflet/src/admin/AdminSocketSettings.js b/loleaflet/src/admin/AdminSocketSettings.js
new file mode 100644
index 0000000..070cd69
--- /dev/null
+++ b/loleaflet/src/admin/AdminSocketSettings.js
@@ -0,0 +1,59 @@
+/*
+ Socket to be intialized on opening the settings page in Admin console
+*/
+/* global $ AdminSocketBase */
+var AdminSocketSettings = AdminSocketBase.extend({
+ constructor: function(host) {
+ this.base(host);
+ this._init();
+ },
+
+ _init: function() {
+ var socketSettings = this.socket;
+ $(document).ready(function() {
+ $('#admin_settings').on('submit', function(e) {
+ e.preventDefault();
+ var memStatsSize = $('#mem_stats_size').val();
+ var memStatsInterval = $('#mem_stats_interval').val();
+ var cpuStatsSize = $('#cpu_stats_size').val();
+ var cpuStatsInterval = $('#cpu_stats_interval').val();
+ var command = 'set';
+ command += ' mem_stats_size=' + memStatsSize;
+ command += ' mem_stats_interval=' + memStatsInterval;
+ command += ' cpu_stats_size=' + cpuStatsSize;
+ command += ' cpu_stats_interval=' + cpuStatsInterval;
+ socketSettings.send(command);
+ });
+ });
+ },
+
+ onSocketOpen: function() {
+ this.socket.send('subscribe settings');
+ this.socket.send('settings');
+ },
+
+ onSocketMessage: function(e) {
+ var textMsg;
+ if (typeof e.data === 'string') {
+ textMsg = e.data;
+ }
+ else {
+ textMsg = '';
+ }
+
+ if (textMsg.startsWith('settings')) {
+ textMsg = textMsg.substring('settings '.length);
+ var settings = textMsg.split(' ');
+ for (var i = 0; i < settings.length; i++) {
+ var setting = settings[i].split('=');
+ var settingKey = setting[0];
+ var settingVal = setting[1];
+ document.getElementById(settingKey).value = settingVal;
+ }
+ }
+ },
+
+ onSocketClose: function() {
+ clearInterval(this._basicStatsIntervalId);
+ }
+});
diff --git a/loleaflet/src/admin/Util.js b/loleaflet/src/admin/Util.js
index 5ed7da2..a409328 100644
--- a/loleaflet/src/admin/Util.js
+++ b/loleaflet/src/admin/Util.js
@@ -1,6 +1,19 @@
/*
Utility class
*/
+/* global Base */
var Util = Base.extend({
constructor: null
+
+}, { // class itnerface
+
+ humanize: function humanFileSize(kbytes) {
+ var unit = 1000;
+ var units = ['kB', 'MB', 'GB', 'TB'];
+ for (var i = 0; Math.abs(kbytes) >= unit && i < units.length; i++) {
+ kbytes /= unit;
+ }
+
+ return kbytes.toFixed(1) + ' ' + units[i];
+ }
});
commit ea29196fed2b4ffded88a414b7252594209722e1
Author: Pranav Kant <pranavk at collabora.com>
Date: Sun Mar 6 02:09:35 2016 +0530
loolwsd: Periodic memory usage subscription
By default, queries and stores the total memory usage in
AdminModel every 5 seconds, and caches the last 100 such values.
Both cache size and interval can be changed by simple commands
from the clients.
Change-Id: I86cf8228d0129dc8aab0a03856c12dfeb240b169
diff --git a/loolwsd/Admin.cpp b/loolwsd/Admin.cpp
index b3fb390..5d77ebb 100644
--- a/loolwsd/Admin.cpp
+++ b/loolwsd/Admin.cpp
@@ -8,6 +8,7 @@
*/
#include <cassert>
+#include <mutex>
#include <sys/poll.h>
#include <sys/prctl.h>
@@ -18,6 +19,7 @@
#include <Poco/Net/NetException.h>
#include <Poco/Net/WebSocket.h>
#include <Poco/StringTokenizer.h>
+#include <Poco/Util/Timer.h>
#include "Admin.hpp"
#include "AdminModel.hpp"
@@ -65,9 +67,12 @@ public:
auto ws = std::make_shared<WebSocket>(request, response);
- // Subscribe the websocket of any AdminModel updates
- AdminModel& model = _admin->getModel();
- model.subscribe(nSessionId, ws);
+ {
+ std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+ // Subscribe the websocket of any AdminModel updates
+ AdminModel& model = _admin->getModel();
+ model.subscribe(nSessionId, ws);
+ }
const Poco::Timespan waitTime(POLL_TIMEOUT_MS * 1000);
int flags = 0;
@@ -118,6 +123,10 @@ public:
if (tokens.count() < 1)
continue;
+ // Lock the model mutex before interacting with it
+ std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+ AdminModel& model = _admin->getModel();
+
if (tokens[0] == "stats")
{
//TODO: Collect stats and reply back to admin.
@@ -168,13 +177,9 @@ public:
}
else if (tokens[0] == "total_mem")
{
- Poco::Process::PID nBrokerPid = _admin->getBrokerPid();
- unsigned totalMem = Util::getMemoryUsage(nBrokerPid);
- totalMem += model.getTotalMemoryUsage();
- totalMem += Util::getMemoryUsage(Poco::Process::id());
-
- std::string responseFrame = "total_mem " + std::to_string(totalMem);
- ws->sendFrame(responseFrame.data(), responseFrame.size());
+ unsigned totalMem = _admin->getTotalMemoryUsage(model);
+ std::string response = "total_mem " + std::to_string(totalMem);
+ ws->sendFrame(response.data(), response.size());
}
else if (tokens[0] == "active_users_count")
{
@@ -199,6 +204,89 @@ public:
Log::warn() << "Could not kill given PID" << Log::end;
}
}
+ else if (tokens[0] == "mem_stats")
+ {
+ std::ostringstream oss;
+ oss << tokens[0] << " "
+ << model.query(tokens[0]);
+
+ std::string response = oss.str();
+ ws->sendFrame(response.data(), response.size());
+ }
+ else if (tokens[0] == "cpu_stats")
+ {
+ std::ostringstream oss;
+ oss << tokens[0] << " "
+ << model.query(tokens[0]);
+
+ std::string response = oss.str();
+ ws->sendFrame(response.data(), response.size());
+ }
+ else if (tokens[0] == "settings")
+ {
+ // for now, we have only these settings
+ std::ostringstream oss;
+ oss << tokens[0] << " "
+ << "mem_stats_size=" << model.query("mem_stats_size") << " "
+ << "mem_stats_interval=" << std::to_string(_admin->getMemStatsInterval()) << " "
+ << "cpu_stats_size=" << model.query("cpu_stats_size") << " "
+ << "cpu_stats_interval=" << std::to_string(_admin->getCpuStatsInterval());
+
+ std::string response = oss.str();
+ ws->sendFrame(response.data(), response.size());
+ }
+ else if (tokens[0] == "set" && tokens.count() > 1)
+ {
+ for (unsigned i = 1; i < tokens.count(); i++)
+ {
+ StringTokenizer setting(tokens[i], "=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+ unsigned settingVal = 0;
+ try
+ {
+ settingVal = std::stoi(setting[1]);
+ }
+ catch (const std::exception& exc)
+ {
+ Log::warn() << "Invalid setting value: "
+ << setting[1] << " for "
+ << setting[0] << Log::end;
+ continue;
+ }
+
+ if (setting[0] == "mem_stats_size")
+ {
+ if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
+ {
+ model.setMemStatsSize(settingVal);
+ }
+ }
+ else if (setting[0] == "mem_stats_interval")
+ {
+ if (settingVal != _admin->getMemStatsInterval())
+ {
+ _admin->rescheduleMemTimer(settingVal);
+ model.clearMemStats();
+ model.notify("settings mem_stats_interval=" + std::to_string(settingVal));
+ }
+ }
+ else if (setting[0] == "cpu_stats_size")
+ {
+ if (settingVal != static_cast<unsigned>(std::stoi(model.query(setting[0]))))
+ {
+ model.setCpuStatsSize(settingVal);
+ }
+ }
+ else if (setting[0] == "cpu_stats_interval")
+ {
+ if (settingVal != _admin->getCpuStatsInterval())
+ {
+ _admin->rescheduleCpuTimer(settingVal);
+ model.clearCpuStats();
+ model.notify("settings cpu_stats_interval=" + std::to_string(settingVal));
+ }
+ }
+ }
+ }
}
}
}
@@ -282,9 +370,65 @@ Admin::~Admin()
void Admin::handleInput(std::string& message)
{
+ std::lock_guard<std::mutex> modelLock(modelMutex);
_model.update(message);
}
+void MemoryStats::run()
+{
+ std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+ AdminModel& model = _admin->getModel();
+ unsigned totalMem = _admin->getTotalMemoryUsage(model);
+
+ Log::trace() << "Total memory used: " << std::to_string(totalMem);
+ model.addMemStats(totalMem);
+}
+
+void CpuStats::run()
+{
+ //TODO: Implement me
+ //std::lock_guard<std::mutex> modelLock(_admin->modelMutex);
+ //model.addCpuStats(totalMem);
+}
+
+void Admin::rescheduleMemTimer(unsigned interval)
+{
+ _memStatsTask->cancel();
+ _memStatsTaskInterval = interval;
+ _memStatsTask = new MemoryStats(this);
+ _memStatsTimer.schedule(_memStatsTask, _memStatsTaskInterval, _memStatsTaskInterval);
+ Log::info("Memory stats interval changed - New interval: " + std::to_string(interval));
+}
+
+void Admin::rescheduleCpuTimer(unsigned interval)
+{
+ _cpuStatsTask->cancel();
+ _cpuStatsTaskInterval = interval;
+ _cpuStatsTask = new CpuStats(this);
+ _cpuStatsTimer.schedule(_cpuStatsTask, _cpuStatsTaskInterval, _cpuStatsTaskInterval);
+ Log::info("CPU stats interval changed - New interval: " + std::to_string(interval));
+}
+
+unsigned Admin::getTotalMemoryUsage(AdminModel& model)
+{
+ Poco::Process::PID nBrokerPid = getBrokerPid();
+ unsigned totalMem = Util::getMemoryUsage(nBrokerPid);
+ totalMem += model.getTotalMemoryUsage();
+ totalMem += Util::getMemoryUsage(Poco::Process::id());
+
+ return totalMem;
+}
+
+unsigned Admin::getMemStatsInterval()
+{
+ return _memStatsTaskInterval;
+}
+
+unsigned Admin::getCpuStatsInterval()
+{
+ return _cpuStatsTaskInterval;
+}
+
void Admin::run()
{
Log::info("Listening on Admin port " + std::to_string(ADMIN_PORT_NUMBER));
@@ -292,6 +436,12 @@ void Admin::run()
// Start a server listening on the admin port.
_srv.start();
+ _memStatsTask = new MemoryStats(this);
+ _memStatsTimer.schedule(_memStatsTask, _memStatsTaskInterval, _memStatsTaskInterval);
+
+ _cpuStatsTask = new CpuStats(this);
+ _cpuStatsTimer.schedule(_cpuStatsTask, _cpuStatsTaskInterval, _cpuStatsTaskInterval);
+
// Start listening for data changes
struct pollfd pollPipeNotify;
pollPipeNotify.fd = NotifyPipe;
diff --git a/loolwsd/Admin.hpp b/loolwsd/Admin.hpp
index c02f824..3b98b9b 100644
--- a/loolwsd/Admin.hpp
+++ b/loolwsd/Admin.hpp
@@ -13,6 +13,8 @@
#include <Poco/Net/HTTPServer.h>
#include <Poco/Runnable.h>
#include <Poco/Types.h>
+#include <Poco/Util/Timer.h>
+#include <Poco/Util/TimerTask.h>
#include "AdminModel.hpp"
@@ -30,10 +32,24 @@ public:
static int getBrokerPipe() { return Admin::BrokerPipe; }
+ unsigned getTotalMemoryUsage(AdminModel&);
+
void run() override;
+ /// Callers must ensure that modelMutex is acquired
AdminModel& getModel();
+ unsigned getMemStatsInterval();
+
+ unsigned getCpuStatsInterval();
+
+ void rescheduleMemTimer(unsigned interval);
+
+ void rescheduleCpuTimer(unsigned interval);
+
+public:
+ std::mutex modelMutex;
+
private:
void handleInput(std::string& message);
@@ -41,11 +57,59 @@ private:
Poco::Net::HTTPServer _srv;
AdminModel _model;
+ Poco::Util::Timer _memStatsTimer;
+ Poco::Util::TimerTask::Ptr _memStatsTask;
+ unsigned _memStatsTaskInterval = 5000;
+
+ Poco::Util::Timer _cpuStatsTimer;
+ Poco::Util::TimerTask::Ptr _cpuStatsTask;
+ unsigned _cpuStatsTaskInterval = 5000;
+
static Poco::Process::PID BrokerPid;
static int BrokerPipe;
static int NotifyPipe;
};
+class MemoryStats : public Poco::Util::TimerTask
+{
+public:
+ MemoryStats(Admin* admin)
+ : _admin(admin)
+ {
+ Log::info("Memory stat ctor");
+ }
+
+ ~MemoryStats()
+ {
+ Log::info("Memory stat dtor");
+ }
+
+ void run() override;
+
+private:
+ Admin* _admin;
+};
+
+class CpuStats : public Poco::Util::TimerTask
+{
+public:
+ CpuStats(Admin* admin)
+ : _admin(admin)
+ {
+ Log::info("Cpu stat ctor");
+ }
+
+ ~CpuStats()
+ {
+ Log::info("Cpu stat dtor");
+ }
+
+ void run() override;
+
+private:
+ Admin* _admin;
+};
+
#endif
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/loolwsd/AdminModel.hpp b/loolwsd/AdminModel.hpp
index 0d9f21c..1ab242d 100644
--- a/loolwsd/AdminModel.hpp
+++ b/loolwsd/AdminModel.hpp
@@ -14,6 +14,7 @@
#include <set>
#include <sstream>
#include <string>
+#include <queue>
#include <Poco/Net/WebSocket.h>
#include <Poco/Process.h>
@@ -265,6 +266,22 @@ public:
{
return std::to_string(_nActiveDocuments);
}
+ else if (tokens[0] == "mem_stats")
+ {
+ return getMemStats();
+ }
+ else if (tokens[0] == "mem_stats_size")
+ {
+ return std::to_string(_memStatsSize);
+ }
+ else if (tokens[0] == "cpu_stats")
+ {
+ return getCpuStats();
+ }
+ else if (tokens[0] == "cpu_stats_size")
+ {
+ return std::to_string(_cpuStatsSize);
+ }
return std::string("");
}
@@ -311,6 +328,92 @@ public:
subscriber->second.unsubscribe(command);
}
+ void clearMemStats()
+ {
+ _memStats.clear();
+ }
+
+ void clearCpuStats()
+ {
+ _cpuStats.clear();
+ }
+
+ void addMemStats(unsigned memUsage)
+ {
+ _memStats.push_back(memUsage);
+ if (_memStats.size() > _memStatsSize)
+ {
+ _memStats.pop_front();
+ }
+
+ std::ostringstream oss;
+ oss << "mem_stats "
+ << std::to_string(memUsage);
+ notify(oss.str());
+ }
+
+ void addCpuStats(unsigned cpuUsage)
+ {
+ _cpuStats.push_back(cpuUsage);
+ if (_cpuStats.size() > _cpuStatsSize)
+ {
+ _cpuStats.pop_front();
+ }
+
+ std::ostringstream oss;
+ oss << "cpu_stats "
+ << std::to_string(cpuUsage);
+ notify(oss.str());
+ }
+
+ void setCpuStatsSize(unsigned size)
+ {
+ int wasteValuesLen = _cpuStats.size() - size;
+ while (wasteValuesLen-- > 0)
+ {
+ _cpuStats.pop_front();
+ }
+ _cpuStatsSize = size;
+
+ std::ostringstream oss;
+ oss << "settings "
+ << "cpu_stats_size="
+ << std::to_string(_cpuStatsSize);
+ notify(oss.str());
+ }
+
+ void setMemStatsSize(unsigned size)
+ {
+ int wasteValuesLen = _memStats.size() - size;
+ while (wasteValuesLen-- > 0)
+ {
+ _memStats.pop_front();
+ }
+ _memStatsSize = size;
+
+ std::ostringstream oss;
+ oss << "settings "
+ << "mem_stats_size="
+ << std::to_string(_memStatsSize);
+ notify(oss.str());
+ }
+
+ void notify(const std::string& message)
+ {
+ auto it = std::begin(_subscribers);
+ while (it != std::end(_subscribers))
+ {
+ if (!it->second.notify(message))
+ {
+ it = _subscribers.erase(it);
+ }
+ else
+ {
+ it++;
+ }
+ }
+ }
+
private:
// FIXME: we have a problem if new document to be added has PID = expired document in the map
// Prolly, *move* expired documents to another container (?)
@@ -337,20 +440,26 @@ private:
}
}
- void notify(const std::string& message)
+ std::string getMemStats()
{
- auto it = std::begin(_subscribers);
- while (it != std::end(_subscribers))
+ std::string response;
+ for (auto& i: _memStats)
{
- if (!it->second.notify(message))
- {
- it = _subscribers.erase(it);
- }
- else
- {
- it++;
- }
+ response += std::to_string(i) + ",";
}
+
+ return response;
+ }
+
+ std::string getCpuStats()
+ {
+ std::string response;
+ for (auto& i: _cpuStats)
+ {
+ response += std::to_string(i) + ",";
+ }
+
+ return response;
}
unsigned getTotalActiveViews()
@@ -393,6 +502,12 @@ private:
std::map<int, Subscriber> _subscribers;
std::map<Poco::Process::PID, Document> _documents;
+ std::list<unsigned> _memStats;
+ unsigned _memStatsSize = 100;
+
+ std::list<unsigned> _cpuStats;
+ unsigned _cpuStatsSize = 100;
+
/// Number of active documents
unsigned _nActiveDocuments = 0;
};
More information about the Libreoffice-commits
mailing list