decompose, add new main group "Application"
PICLI code brush
This commit is contained in:
255
libs/main/application/picli.cpp
Normal file
255
libs/main/application/picli.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Command-Line Parser
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picli.h"
|
||||
|
||||
#include "pisysteminfo.h"
|
||||
|
||||
|
||||
//! \class PICLI picli.h
|
||||
//! \details
|
||||
//! \~english \section PICLI_sec0 Synopsis
|
||||
//! \~russian \section PICLI_sec0 Краткий обзор
|
||||
//! \~english
|
||||
//! This class provide handy parsing of command-line arguments. First you should add
|
||||
//! arguments to %PICLI with function \a addArgument(). Then you can check if there
|
||||
//! is some argument in application command-line with function \a hasArgument(),
|
||||
//! or obtain argument value by \a argumentValue().
|
||||
//!
|
||||
//! \~russian
|
||||
//! Этот класс предоставляет удобный механизм для разбора аргументов командной строки.
|
||||
//! Сперва необходимо добавить аргументы в %PICLI с помощью методов \a addArgument().
|
||||
//! Далее можно проверять аргументы на наличие в командной строке методом \a hasArgument(),
|
||||
//! а также получать их значения при помощи \a argumentValue().
|
||||
//!
|
||||
//! \~english \section PICLI_sec1 Example
|
||||
//! \~russian \section PICLI_sec1 Пример
|
||||
//! \~\code
|
||||
//! int main(int argc, char ** argv) {
|
||||
//! PICLI cli(argc, argv);
|
||||
//! cli.addArgument("console");
|
||||
//! cli.addArgument("debug");
|
||||
//! cli.addArgument("Value", "v", "value", true);
|
||||
//! if (cli.hasArgument("console"))
|
||||
//! piCout << "console active";
|
||||
//! if (cli.hasArgument("debug"))
|
||||
//! piCout << "debug active";
|
||||
//! piCout << "Value =" << cli.argumentValue("Value");
|
||||
//! return 0;
|
||||
//! }
|
||||
//! \endcode
|
||||
//!
|
||||
//! \~english These executions are similar:
|
||||
//! \~russian Эти вызовы будут идентичны:
|
||||
//!
|
||||
//! \~\code
|
||||
//! a.out -cd -v 10
|
||||
//! a.out --value 10 -dc
|
||||
//! a.out -c -v 10 -d
|
||||
//! a.out --console -d -v 10
|
||||
//! a.out --debug -c --value 10
|
||||
//! \endcode
|
||||
//!
|
||||
|
||||
|
||||
PICLI::PICLI(int argc, char * argv[]) {
|
||||
for (int i = 0; i < argc; ++i)
|
||||
_args_raw << argv[i];
|
||||
if (argc > 0) PISystemInfo::instance()->execCommand = argv[0];
|
||||
}
|
||||
|
||||
|
||||
void PICLI::parse() {
|
||||
if (!needParse) return;
|
||||
PIString cra, full;
|
||||
Argument * last = 0;
|
||||
for (int i = 1; i < _args_raw.size_s(); ++i) {
|
||||
cra = _args_raw[i];
|
||||
if (cra.left(2) == _prefix_full) {
|
||||
last = 0;
|
||||
full = cra.right(cra.length() - 2);
|
||||
for (auto & a: _args) {
|
||||
if (a.full_key == full) {
|
||||
a.found = true;
|
||||
last = &a;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (cra.left(1) == _prefix_short) {
|
||||
last = 0;
|
||||
for (int j = 1; j < cra.length(); ++j) {
|
||||
bool found = false;
|
||||
for (auto & a: _args) {
|
||||
if ((a.short_key != '\0') && (a.short_key == cra[j])) {
|
||||
a.found = true;
|
||||
last = &a;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) break;
|
||||
}
|
||||
} else {
|
||||
if (last == 0 ? true : !last->has_value) {
|
||||
if (_args_mand.size_s() < _count_mand) {
|
||||
_args_mand << cra;
|
||||
continue;
|
||||
}
|
||||
if (_args_opt.size_s() < _count_opt || _count_opt < 0) {
|
||||
_args_opt << cra;
|
||||
continue;
|
||||
}
|
||||
piCoutObj << "Arguments overflow, \"" << cra << "\" ignored";
|
||||
}
|
||||
if (last == 0 ? false : last->has_value) {
|
||||
last->value = cra;
|
||||
last = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
needParse = false;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::addArgument(const PIString & name, bool value) {
|
||||
_args << Argument(name, name[0], name, value);
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::addArgument(const PIString & name, const PIChar & shortKey, bool value) {
|
||||
_args << Argument(name, shortKey, name, value);
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::addArgument(const PIString & name, const char * shortKey, bool value) {
|
||||
_args << Argument(name, PIChar::fromUTF8(shortKey), name, value);
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value) {
|
||||
_args << Argument(name, shortKey, fullKey, value);
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value) {
|
||||
_args << Argument(name, PIChar::fromUTF8(shortKey), fullKey, value);
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::rawArgument(int index) {
|
||||
parse();
|
||||
return _args_raw[index];
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::mandatoryArgument(int index) {
|
||||
parse();
|
||||
return _args_mand[index];
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::optionalArgument(int index) {
|
||||
parse();
|
||||
return _args_opt[index];
|
||||
}
|
||||
|
||||
|
||||
const PIStringList & PICLI::rawArguments() {
|
||||
parse();
|
||||
return _args_raw;
|
||||
}
|
||||
|
||||
|
||||
const PIStringList & PICLI::mandatoryArguments() {
|
||||
parse();
|
||||
return _args_mand;
|
||||
}
|
||||
|
||||
|
||||
const PIStringList & PICLI::optionalArguments() {
|
||||
parse();
|
||||
return _args_opt;
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::programCommand() {
|
||||
parse();
|
||||
return _args_raw.isNotEmpty() ? _args_raw.front() : PIString();
|
||||
}
|
||||
|
||||
|
||||
bool PICLI::hasArgument(const PIString & name) {
|
||||
parse();
|
||||
for (const auto & i: _args)
|
||||
if (i.name == name && i.found) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::argumentValue(const PIString & name) {
|
||||
parse();
|
||||
for (const auto & i: _args)
|
||||
if (i.name == name && i.found) return i.value;
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::argumentShortKey(const PIString & name) {
|
||||
for (const auto & i: _args)
|
||||
if (i.name == name) return PIString(i.short_key);
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
PIString PICLI::argumentFullKey(const PIString & name) {
|
||||
for (const auto & i: _args)
|
||||
if (i.name == name) return i.full_key;
|
||||
return PIString();
|
||||
}
|
||||
|
||||
|
||||
void PICLI::setShortKeyPrefix(const PIString & prefix) {
|
||||
_prefix_short = prefix;
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::setFullKeyPrefix(const PIString & prefix) {
|
||||
_prefix_full = prefix;
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::setMandatoryArgumentsCount(const int count) {
|
||||
_count_mand = count;
|
||||
needParse = true;
|
||||
}
|
||||
|
||||
|
||||
void PICLI::setOptionalArgumentsCount(const int count) {
|
||||
_count_opt = count;
|
||||
needParse = true;
|
||||
}
|
||||
136
libs/main/application/picli.h
Normal file
136
libs/main/application/picli.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/*! \file picli.h
|
||||
* \ingroup Application
|
||||
* \~\brief
|
||||
* \~english Command-Line parser
|
||||
* \~russian Парсер командной строки
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Command-Line Parser
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PICLI_H
|
||||
#define PICLI_H
|
||||
|
||||
#include "piset.h"
|
||||
#include "pistringlist.h"
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Command-Line parser.
|
||||
//! \~russian Парсер командной строки.
|
||||
class PIP_EXPORT PICLI {
|
||||
public:
|
||||
//! \~english Constructs %PICLI from "argc" and "argv" from "int main()" method.
|
||||
//! \~russian Создает %PICLI из "argc" и "argv" из метода "int main()".
|
||||
PICLI(int argc, char * argv[]);
|
||||
|
||||
|
||||
//! \~english Add argument with name "name", short key = name first letter and full key = name.
|
||||
//! \~russian Добавляет аргумент с именем "name", коротким ключом = первой букве имени и полным ключом = имени.
|
||||
void addArgument(const PIString & name, bool value = false);
|
||||
|
||||
//! \~english Add argument with name "name", short key = "shortKey" and full key = name.
|
||||
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = имени.
|
||||
void addArgument(const PIString & name, const PIChar & shortKey, bool value = false);
|
||||
|
||||
//! \~english Add argument with name "name", short key = "shortKey" and full key = name.
|
||||
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = имени.
|
||||
void addArgument(const PIString & name, const char * shortKey, bool value = false);
|
||||
|
||||
//! \~english Add argument with name "name", short key = "shortKey" and full key = "fullKey".
|
||||
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = "fullKey".
|
||||
void addArgument(const PIString & name, const PIChar & shortKey, const PIString & fullKey, bool value = false);
|
||||
|
||||
//! \~english Add argument with name "name", short key = "shortKey" and full key = "fullKey".
|
||||
//! \~russian Добавляет аргумент с именем "name", коротким ключом = "shortKey" и полным ключом = "fullKey".
|
||||
void addArgument(const PIString & name, const char * shortKey, const PIString & fullKey, bool value = false);
|
||||
|
||||
|
||||
//! \~english Returns unparsed command-line argument by index "index". Index 0 is program execute command.
|
||||
//! \~russian Возвращает исходный аргумент командной строки по индексу "index". Индекс 0 это команда вызова программы.
|
||||
PIString rawArgument(int index);
|
||||
PIString mandatoryArgument(int index);
|
||||
PIString optionalArgument(int index);
|
||||
|
||||
//! \~english Returns unparsed command-line arguments.
|
||||
//! \~russian Возвращает исходные аргументы командной строки.
|
||||
const PIStringList & rawArguments();
|
||||
const PIStringList & mandatoryArguments();
|
||||
const PIStringList & optionalArguments();
|
||||
|
||||
//! \~english Returns program execute command without arguments.
|
||||
//! \~russian Возвращает команду вызова программы без аргументов.
|
||||
PIString programCommand();
|
||||
|
||||
//! \~english Returns if argument "name" found.
|
||||
//! \~russian Возвращает найден ли аргумент "name".
|
||||
bool hasArgument(const PIString & name);
|
||||
|
||||
//! \~english Returns argument "name" value, or empty string if this is no value.
|
||||
//! \~russian Возвращает значение аргумента "name" или пустую строку, если значения нет.
|
||||
PIString argumentValue(const PIString & name);
|
||||
|
||||
//! \~english Returns short key of argument "name", or empty string if this is no argument.
|
||||
//! \~russian Возвращает короткий ключ аргумента "name" или пустую строку, если аргумента нет.
|
||||
PIString argumentShortKey(const PIString & name);
|
||||
|
||||
//! \~english Returns full key of argument "name", or empty string if this is no argument.
|
||||
//! \~russian Возвращает полный ключ аргумента "name" или пустую строку, если аргумента нет.
|
||||
PIString argumentFullKey(const PIString & name);
|
||||
|
||||
const PIString & shortKeyPrefix() const { return _prefix_short; }
|
||||
const PIString & fullKeyPrefix() const { return _prefix_full; }
|
||||
int mandatoryArgumentsCount() const { return _count_mand; }
|
||||
int optionalArgumentsCount() const { return _count_opt; }
|
||||
void setShortKeyPrefix(const PIString & prefix);
|
||||
void setFullKeyPrefix(const PIString & prefix);
|
||||
void setMandatoryArgumentsCount(const int count);
|
||||
void setOptionalArgumentsCount(const int count);
|
||||
|
||||
bool debug() const { return debug_; }
|
||||
void setDebug(bool debug) { debug_ = debug; }
|
||||
PIConstChars className() const { return "PICLI"; }
|
||||
PIString name() const { return PIStringAscii("CLI"); }
|
||||
|
||||
private:
|
||||
struct Argument {
|
||||
Argument() {}
|
||||
Argument(const PIString & n, const PIChar & s, const PIString & f, bool v) {
|
||||
name = n;
|
||||
short_key = s;
|
||||
full_key = f;
|
||||
has_value = v;
|
||||
}
|
||||
PIString name;
|
||||
PIChar short_key;
|
||||
PIString full_key;
|
||||
PIString value;
|
||||
bool has_value = false, found = false;
|
||||
};
|
||||
|
||||
void parse();
|
||||
|
||||
PIString _prefix_short = "-", _prefix_full = "--";
|
||||
PIStringList _args_raw, _args_mand, _args_opt;
|
||||
PISet<PIString> keys_full, keys_short;
|
||||
PIVector<Argument> _args;
|
||||
int _count_mand = 0, _count_opt = 0;
|
||||
bool needParse = true, debug_ = true;
|
||||
};
|
||||
|
||||
#endif // PICLI_H
|
||||
152
libs/main/application/pisingleapplication.cpp
Normal file
152
libs/main/application/pisingleapplication.cpp
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Single application
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pisingleapplication.h"
|
||||
|
||||
#include "piliterals_bytes.h"
|
||||
#include "piliterals_time.h"
|
||||
#include "pisharedmemory.h"
|
||||
#include "pitime.h"
|
||||
|
||||
|
||||
//! \class PISingleApplication pisingleapplication.h
|
||||
//! \~\details
|
||||
//! \~english
|
||||
//!
|
||||
//!
|
||||
//! \~russian
|
||||
//! Этот класс позволяет отслеживать повторный запуск приложения
|
||||
//! и передавать сообщение первому запущеному.
|
||||
//!
|
||||
//! Видят друг друга %PISingleApplication с одинаковыми "app_name",
|
||||
//! задаваемым в конструкторе.
|
||||
//!
|
||||
//! Если проверка \a isFirst() успешна, значит этот экземпляр
|
||||
//! запущен первым, и можно соединиться к событию \a messageReceived().
|
||||
//!
|
||||
//! Если проверка провалена, значит этот экземпляр не первый,
|
||||
//! и можно послать ему сообщение с помощью \a sendMessage(),
|
||||
//! а затем выйти.
|
||||
//!
|
||||
//! \~\code
|
||||
//! int main(int argc, char * argv[]) {
|
||||
//! PISingleApplication sapp("myapp");
|
||||
//! if (sapp.isFirst()) {
|
||||
//! piCout << "I`m first, wait for another";
|
||||
//! CONNECTL(&sapp, messageReceived, [](PIByteArray msg){
|
||||
//! piCout << "Msg from another:" << PIString(msg);
|
||||
//! });
|
||||
//! } else {
|
||||
//! piCout << "I`m not first, send and exit";
|
||||
//! sapp.sendMessage(PIString("Hello!").toByteArray());
|
||||
//! return 0;
|
||||
//! }
|
||||
//! WAIT_FOREVER
|
||||
//! return 0;
|
||||
//! }
|
||||
//! \endcode
|
||||
//!
|
||||
|
||||
|
||||
#define SHM_SIZE 32_KiB
|
||||
|
||||
|
||||
PISingleApplication::PISingleApplication(const PIString & app_name): PIThread() {
|
||||
first = true;
|
||||
started = false;
|
||||
sacnt = 0;
|
||||
shm = new PISharedMemory("sa_" + app_name, SHM_SIZE);
|
||||
start(10_Hz);
|
||||
}
|
||||
|
||||
|
||||
PISingleApplication::~PISingleApplication() {
|
||||
stop();
|
||||
if (!waitForFinish(5_s)) terminate();
|
||||
delete shm;
|
||||
}
|
||||
|
||||
|
||||
bool PISingleApplication::isFirst() const {
|
||||
waitFirst();
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
void PISingleApplication::sendMessage(const PIByteArray & m) {
|
||||
waitFirst();
|
||||
PIByteArray ba;
|
||||
int lm[3] = {0, 0, 0};
|
||||
for (;;) {
|
||||
shm->read(lm, 12);
|
||||
if (lm[2] == 0) break;
|
||||
piMSleep(10);
|
||||
}
|
||||
ba << sacnt << sacnt << int(1) << m;
|
||||
shm->write(ba);
|
||||
}
|
||||
|
||||
|
||||
void PISingleApplication::begin() {
|
||||
int cnt[2] = {0, 0};
|
||||
int tcnt = 0;
|
||||
shm->read(cnt, 8);
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
tcnt = cnt[0];
|
||||
shm->read(cnt, 8);
|
||||
if (cnt[0] == cnt[1] && cnt[0] != tcnt) {
|
||||
first = false;
|
||||
break;
|
||||
}
|
||||
piMSleep(100);
|
||||
}
|
||||
// piCoutObj << "started" << first << shm->size();
|
||||
readed.reserve(shm->size());
|
||||
started = true;
|
||||
}
|
||||
|
||||
|
||||
void PISingleApplication::run() {
|
||||
if (!first) return;
|
||||
++sacnt;
|
||||
int st_[2] = {sacnt, sacnt};
|
||||
shm->write(st_, 8);
|
||||
// piCoutObj << "write" << sacnt;
|
||||
int ri[3] = {0, 0, 0};
|
||||
const int hdr_sz = sizeof(int) * 3;
|
||||
shm->read(ri, hdr_sz);
|
||||
if (ri[2] != 0 && ri[0] == ri[1]) {
|
||||
readed.resize(shm->size() - hdr_sz);
|
||||
shm->read(readed.data(), readed.size(), hdr_sz);
|
||||
PIByteArray msg;
|
||||
readed >> msg;
|
||||
if (msg.isNotEmpty()) {
|
||||
messageReceived(msg);
|
||||
// piCoutObj << "message" << msg;
|
||||
}
|
||||
int wi[3] = {sacnt, sacnt, 0};
|
||||
shm->write(wi, 12);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PISingleApplication::waitFirst() const {
|
||||
while (!started)
|
||||
piMSleep(50);
|
||||
}
|
||||
86
libs/main/application/pisingleapplication.h
Normal file
86
libs/main/application/pisingleapplication.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*! \file pisingleapplication.h
|
||||
* \ingroup Application
|
||||
* \~\brief
|
||||
* \~english Single-instance application control
|
||||
* \~russian Контроль одного экземпляра приложения
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Single application
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISINGLEAPPLICATION_H
|
||||
#define PISINGLEAPPLICATION_H
|
||||
|
||||
#include "pithread.h"
|
||||
|
||||
class PISharedMemory;
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Single-instance application control.
|
||||
//! \~russian Контроль одного экземпляра приложения.
|
||||
class PIP_EXPORT PISingleApplication: public PIThread {
|
||||
PIOBJECT_SUBCLASS(PISingleApplication, PIThread);
|
||||
|
||||
public:
|
||||
//! \~english Construct %PISingleApplication with name "app_name"
|
||||
//! \~russian Создает %PISingleApplication с именем "app_name"
|
||||
PISingleApplication(const PIString & app_name = PIString());
|
||||
|
||||
~PISingleApplication();
|
||||
|
||||
|
||||
//! \~english Returns if this application instance is launched first
|
||||
//! \~russian Возвращает первым ли был запущен этот экземпляр приложения
|
||||
bool isFirst() const;
|
||||
|
||||
EVENT_HANDLER1(void, sendMessage, const PIByteArray &, m);
|
||||
EVENT1(messageReceived, PIByteArray, m);
|
||||
|
||||
//! \handlers
|
||||
//! \{
|
||||
|
||||
//! \fn void sendMessage(const PIByteArray & m)
|
||||
//! \brief
|
||||
//! \~english Send message "m" to first launched application
|
||||
//! \~russian Посылает сообщение "m" первому запущеному приложению
|
||||
|
||||
//! \}
|
||||
//! \events
|
||||
//! \{
|
||||
|
||||
//! \fn void messageReceived(PIByteArray m)
|
||||
//! \brief
|
||||
//! \~english Raise on first launched application receive message from another
|
||||
//! \~russian Вызывается первым запущеным приложением по приему сообщения от других
|
||||
|
||||
//! \}
|
||||
|
||||
private:
|
||||
void begin() override;
|
||||
void run() override;
|
||||
void waitFirst() const;
|
||||
|
||||
PISharedMemory * shm;
|
||||
PITimeMeasurer ftm;
|
||||
PIByteArray readed;
|
||||
bool first, started;
|
||||
int sacnt;
|
||||
};
|
||||
|
||||
#endif // PISINGLEAPPLICATION_H
|
||||
456
libs/main/application/pisystemmonitor.cpp
Normal file
456
libs/main/application/pisystemmonitor.cpp
Normal file
@@ -0,0 +1,456 @@
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Process resource monitor
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "pisystemmonitor.h"
|
||||
|
||||
#include "pidir.h"
|
||||
#include "piliterals_string.h"
|
||||
#include "piprocess.h"
|
||||
#include "pisysteminfo.h"
|
||||
#include "pitime_win.h"
|
||||
#ifdef WINDOWS
|
||||
# include <psapi.h>
|
||||
# include <tlhelp32.h>
|
||||
#endif
|
||||
#ifdef MAC_OS
|
||||
struct kqueue_id_t;
|
||||
# include <libproc.h>
|
||||
# include <sys/proc_info.h>
|
||||
#endif
|
||||
#ifdef ESP_PLATFORM
|
||||
# include "esp_heap_caps.h"
|
||||
#endif
|
||||
|
||||
|
||||
void PISystemMonitor::ProcessStats::makeStrings() {
|
||||
physical_memsize_readable.setReadableSize(physical_memsize);
|
||||
resident_memsize_readable.setReadableSize(resident_memsize);
|
||||
share_memsize_readable.setReadableSize(share_memsize);
|
||||
virtual_memsize_readable.setReadableSize(virtual_memsize);
|
||||
data_memsize_readable.setReadableSize(data_memsize);
|
||||
}
|
||||
|
||||
|
||||
#ifndef MICRO_PIP
|
||||
PRIVATE_DEFINITION_START(PISystemMonitor)
|
||||
# ifndef WINDOWS
|
||||
# ifdef MAC_OS
|
||||
PISystemTime
|
||||
# else
|
||||
llong
|
||||
# endif
|
||||
cpu_u_cur,
|
||||
cpu_u_prev, cpu_s_cur, cpu_s_prev;
|
||||
PIString proc_dir;
|
||||
PIFile file, filem;
|
||||
# else
|
||||
HANDLE hProc;
|
||||
PROCESS_MEMORY_COUNTERS mem_cnt;
|
||||
PISystemTime tm_kernel, tm_user;
|
||||
PITimeMeasurer tm;
|
||||
# endif
|
||||
PRIVATE_DEFINITION_END(PISystemMonitor)
|
||||
#endif
|
||||
|
||||
|
||||
PISystemMonitor::PISystemMonitor(): PIThread() {
|
||||
pID_ = cycle = 0;
|
||||
cpu_count = PISystemInfo::instance()->processorsCount;
|
||||
#ifndef MICRO_PIP
|
||||
# ifndef WINDOWS
|
||||
# ifdef QNX
|
||||
page_size = 4096;
|
||||
# else
|
||||
page_size = getpagesize();
|
||||
# endif
|
||||
# else
|
||||
PRIVATE->hProc = 0;
|
||||
PRIVATE->mem_cnt.cb = sizeof(PRIVATE->mem_cnt);
|
||||
# endif
|
||||
#endif
|
||||
setName("system_monitor"_a);
|
||||
}
|
||||
|
||||
|
||||
PISystemMonitor::~PISystemMonitor() {
|
||||
stop();
|
||||
}
|
||||
|
||||
|
||||
#ifndef MICRO_PIP
|
||||
bool PISystemMonitor::startOnProcess(int pID, PISystemTime interval) {
|
||||
stop();
|
||||
pID_ = pID;
|
||||
Pool::instance()->add(this);
|
||||
cycle = -1;
|
||||
# ifndef WINDOWS
|
||||
# ifndef MAC_OS
|
||||
PRIVATE->proc_dir = PIStringAscii("/proc/") + PIString::fromNumber(pID_) + PIStringAscii("/");
|
||||
PRIVATE->file.open(PRIVATE->proc_dir + "stat", PIIODevice::ReadOnly);
|
||||
PRIVATE->filem.open(PRIVATE->proc_dir + "statm", PIIODevice::ReadOnly);
|
||||
if (!PRIVATE->file.isOpened()) {
|
||||
piCoutObj << "Can`t find process with ID = " << pID_ << "!";
|
||||
return false;
|
||||
}
|
||||
# endif
|
||||
# else
|
||||
PRIVATE->hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pID_);
|
||||
if (PRIVATE->hProc == 0) {
|
||||
piCoutObj << "Can`t open process with ID = " << pID_ << "," << errorString();
|
||||
return false;
|
||||
}
|
||||
PRIVATE->tm.reset();
|
||||
# endif
|
||||
return start(interval);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bool PISystemMonitor::startOnSelf(PISystemTime interval) {
|
||||
#ifndef MICRO_PIP
|
||||
bool ret = startOnProcess(PIProcess::currentPID(), interval);
|
||||
cycle = -1;
|
||||
#else
|
||||
bool ret = start(interval);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
PIVector<PISystemMonitor::ThreadStats> PISystemMonitor::threadsStatistic() const {
|
||||
lock();
|
||||
PIVector<PISystemMonitor::ThreadStats> ret = cur_ts;
|
||||
unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void PISystemMonitor::setStatistic(const PISystemMonitor::ProcessStats & s) {
|
||||
PIMutexLocker _ml(stat_mutex);
|
||||
stat = s;
|
||||
stat.makeStrings();
|
||||
}
|
||||
|
||||
|
||||
void PISystemMonitor::stop() {
|
||||
PIThread::stopAndWait();
|
||||
#ifdef WINDOWS
|
||||
if (PRIVATE->hProc != 0) {
|
||||
CloseHandle(PRIVATE->hProc);
|
||||
PRIVATE->hProc = 0;
|
||||
}
|
||||
#endif
|
||||
Pool::instance()->remove(this);
|
||||
}
|
||||
|
||||
|
||||
PISystemMonitor::ProcessStats PISystemMonitor::statistic() const {
|
||||
PIMutexLocker _ml(stat_mutex);
|
||||
return stat;
|
||||
}
|
||||
|
||||
|
||||
#ifdef MAC_OS
|
||||
PISystemTime uint64toST(uint64_t v) {
|
||||
return PISystemTime(((uint *)&(v))[1], ((uint *)&(v))[0]);
|
||||
}
|
||||
#endif
|
||||
|
||||
void PISystemMonitor::run() {
|
||||
cur_tm.clear();
|
||||
tbid.clear();
|
||||
__PIThreadCollection * pitc = __PIThreadCollection::instance();
|
||||
pitc->lock();
|
||||
PIVector<PIThread *> tv = pitc->threads();
|
||||
piForeach(PIThread * t, tv)
|
||||
if (t->isPIObject()) tbid[t->tid()] = t->name();
|
||||
pitc->unlock();
|
||||
// piCout << tbid.keys().toType<uint>();
|
||||
ProcessStats tstat;
|
||||
tstat.ID = pID_;
|
||||
#ifdef MICRO_PIP
|
||||
piForeach(PIThread * t, tv)
|
||||
if (t->isPIObject()) gatherThread(t->tid());
|
||||
#else
|
||||
# ifndef WINDOWS
|
||||
double delay_ms = delay_.toMilliseconds();
|
||||
tbid[pID_] = "main";
|
||||
# ifdef MAC_OS
|
||||
rusage_info_current ru;
|
||||
proc_pid_rusage(pID_, RUSAGE_INFO_CURRENT, (rusage_info_t *)&ru);
|
||||
// piCout << PISystemTime(((uint*)&(ru.ri_user_time))[1], ((uint*)&(ru.ri_user_time))[0]);
|
||||
if (cycle < 0) {
|
||||
PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur = uint64toST(ru.ri_user_time);
|
||||
PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur = uint64toST(ru.ri_system_time);
|
||||
}
|
||||
PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur;
|
||||
PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur;
|
||||
PRIVATE->cpu_u_cur = uint64toST(ru.ri_user_time);
|
||||
PRIVATE->cpu_s_cur = uint64toST(ru.ri_system_time);
|
||||
tstat.cpu_load_system = 100.f * (PRIVATE->cpu_s_cur - PRIVATE->cpu_s_prev).toMilliseconds() / delay_ms;
|
||||
tstat.cpu_load_user = 100.f * (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
||||
cycle = 0;
|
||||
// piCout << (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev).toMilliseconds() / delay_ms;
|
||||
# else
|
||||
PRIVATE->file.seekToBegin();
|
||||
PIString str = PIString::fromAscii(PRIVATE->file.readAll(true));
|
||||
int si = str.find('(') + 1, fi = 0, cc = 1;
|
||||
for (int i = si; i < str.size_s(); ++i) {
|
||||
if (str[i] == '(') cc++;
|
||||
if (str[i] == ')') cc--;
|
||||
if (cc <= 0) {
|
||||
fi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
tstat.exec_name = str.mid(si, fi - si);
|
||||
str.cutMid(si - 1, fi - si + 3);
|
||||
PIStringList sl = str.split(" ");
|
||||
if (sl.size_s() < 19) return;
|
||||
tstat.ID = sl[0].toInt();
|
||||
tstat.state = sl[1];
|
||||
tstat.parent_ID = sl[2].toInt();
|
||||
tstat.group_ID = sl[3].toInt();
|
||||
tstat.session_ID = sl[4].toInt();
|
||||
if (cycle < 0) {
|
||||
PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur = sl[12].toLLong();
|
||||
PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur = sl[13].toLLong();
|
||||
}
|
||||
PRIVATE->cpu_u_prev = PRIVATE->cpu_u_cur;
|
||||
PRIVATE->cpu_s_prev = PRIVATE->cpu_s_cur;
|
||||
PRIVATE->cpu_u_cur = sl[12].toLLong();
|
||||
PRIVATE->cpu_s_cur = sl[13].toLLong();
|
||||
tstat.cpu_load_system = (PRIVATE->cpu_s_cur - PRIVATE->cpu_s_prev) / (delay_ms / 1000.);
|
||||
tstat.cpu_load_user = (PRIVATE->cpu_u_cur - PRIVATE->cpu_u_prev) / (delay_ms / 1000.);
|
||||
tstat.cpu_load_system /= cpu_count;
|
||||
tstat.cpu_load_user /= cpu_count;
|
||||
cycle = 0;
|
||||
tstat.priority = sl[16].toInt();
|
||||
tstat.threads = sl[18].toInt();
|
||||
// piCout << "\n";
|
||||
// piCout << sl[0] << sl[12] << sl[13];
|
||||
|
||||
PRIVATE->filem.seekToBegin();
|
||||
str = PIString::fromAscii(PRIVATE->filem.readAll(true));
|
||||
sl = str.split(" ");
|
||||
if (sl.size_s() < 6) return;
|
||||
tstat.virtual_memsize = sl[0].toLong() * page_size;
|
||||
tstat.resident_memsize = sl[1].toLong() * page_size;
|
||||
tstat.share_memsize = sl[2].toLong() * page_size;
|
||||
tstat.data_memsize = sl[5].toLong() * page_size;
|
||||
tstat.physical_memsize = tstat.resident_memsize - tstat.share_memsize;
|
||||
|
||||
PIVector<PIFile::FileInfo> tld = PIDir(PRIVATE->proc_dir + "task").entries();
|
||||
piForeachC(PIFile::FileInfo & i, tld) {
|
||||
if (i.flags[PIFile::FileInfo::Dot] || i.flags[PIFile::FileInfo::DotDot]) continue;
|
||||
gatherThread(i.name().toInt());
|
||||
}
|
||||
# endif
|
||||
# else
|
||||
if (GetProcessMemoryInfo(PRIVATE->hProc, &PRIVATE->mem_cnt, sizeof(PRIVATE->mem_cnt)) != 0) {
|
||||
tstat.physical_memsize = PRIVATE->mem_cnt.WorkingSetSize;
|
||||
}
|
||||
tstat.priority = GetPriorityClass(PRIVATE->hProc);
|
||||
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pID_);
|
||||
int thcnt = 0;
|
||||
if (snap != 0) {
|
||||
THREADENTRY32 thread;
|
||||
thread.dwSize = sizeof(THREADENTRY32);
|
||||
if (Thread32First(snap, &thread) == TRUE) {
|
||||
if (thread.th32OwnerProcessID == DWORD(pID_)) {
|
||||
++thcnt;
|
||||
gatherThread(thread.th32ThreadID);
|
||||
}
|
||||
while (Thread32Next(snap, &thread) == TRUE) {
|
||||
if (thread.th32OwnerProcessID == DWORD(pID_)) {
|
||||
++thcnt;
|
||||
gatherThread(thread.th32ThreadID);
|
||||
}
|
||||
// piCout << thread.th32ThreadID;
|
||||
}
|
||||
}
|
||||
tstat.threads = thcnt;
|
||||
CloseHandle(snap);
|
||||
}
|
||||
FILETIME ft0, ft1, ft_kernel, ft_user;
|
||||
double el_s = PRIVATE->tm.elapsed_s() * cpu_count / 100.;
|
||||
if (GetProcessTimes(PRIVATE->hProc, &ft0, &ft1, &ft_kernel, &ft_user) != 0) {
|
||||
PISystemTime tm_kernel_c = FILETIME2PISystemTime(ft_kernel);
|
||||
PISystemTime tm_user_c = FILETIME2PISystemTime(ft_user);
|
||||
if (cycle < 0) {
|
||||
PRIVATE->tm_kernel = tm_kernel_c;
|
||||
PRIVATE->tm_user = tm_user_c;
|
||||
}
|
||||
cycle = 0;
|
||||
if (el_s <= 0.) {
|
||||
tstat.cpu_load_system = 0.f;
|
||||
tstat.cpu_load_user = 0.f;
|
||||
} else {
|
||||
tstat.cpu_load_system = (tm_kernel_c - PRIVATE->tm_kernel).toSeconds() / el_s;
|
||||
tstat.cpu_load_user = (tm_user_c - PRIVATE->tm_user).toSeconds() / el_s;
|
||||
}
|
||||
PRIVATE->tm_kernel = tm_kernel_c;
|
||||
PRIVATE->tm_user = tm_user_c;
|
||||
} else {
|
||||
tstat.cpu_load_system = 0.f;
|
||||
tstat.cpu_load_user = 0.f;
|
||||
}
|
||||
PRIVATE->tm.reset();
|
||||
# endif
|
||||
#endif
|
||||
|
||||
tstat.cpu_load_system = piClampf(tstat.cpu_load_system, 0.f, 100.f);
|
||||
tstat.cpu_load_user = piClampf(tstat.cpu_load_user, 0.f, 100.f);
|
||||
|
||||
auto i = cur_tm.makeIterator();
|
||||
while (i.next()) {
|
||||
if (!last_tm.contains(i.key())) continue;
|
||||
ThreadStats & ts_new(i.value());
|
||||
ThreadStats & ts_old(last_tm[i.key()]);
|
||||
ts_new.cpu_load_kernel = calcThreadUsage(ts_new.kernel_time, ts_old.kernel_time);
|
||||
ts_new.cpu_load_user = calcThreadUsage(ts_new.user_time, ts_old.user_time);
|
||||
// piCout << ts_new.cpu_load_user;
|
||||
}
|
||||
last_tm = cur_tm;
|
||||
lock();
|
||||
cur_ts = cur_tm.values();
|
||||
unlock();
|
||||
tstat.ram_total = totalRAM();
|
||||
tstat.ram_used = usedRAM();
|
||||
tstat.ram_free = freeRAM();
|
||||
stat_mutex.lock();
|
||||
stat = tstat;
|
||||
stat.makeStrings();
|
||||
stat_mutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
void PISystemMonitor::gatherThread(llong id) {
|
||||
PISystemMonitor::ThreadStats ts;
|
||||
if (id == 0) return;
|
||||
ts.id = id;
|
||||
#ifdef MICRO_PIP
|
||||
ts.name = tbid.value(id, "<PIThread>");
|
||||
#else
|
||||
ts.name = tbid.value(id, "<non-PIThread>");
|
||||
# ifndef WINDOWS
|
||||
PIFile f(PRIVATE->proc_dir + "task/" + PIString::fromNumber(id) + "/stat");
|
||||
// piCout << f.path();
|
||||
if (!f.open(PIIODevice::ReadOnly)) return;
|
||||
PIString str = PIString::fromAscii(f.readAll(true));
|
||||
int si = str.find('(') + 1, fi = 0, cc = 1;
|
||||
for (int i = si; i < str.size_s(); ++i) {
|
||||
if (str[i] == '(') cc++;
|
||||
if (str[i] == ')') cc--;
|
||||
if (cc <= 0) {
|
||||
fi = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
str.cutMid(si - 1, fi - si + 3);
|
||||
PIStringList sl = str.split(" ");
|
||||
if (sl.size_s() < 14) return;
|
||||
// piCout << sl[0] << sl[12] << sl[13];
|
||||
ts.user_time = PISystemTime::fromMilliseconds(sl[12].toInt() * 10.);
|
||||
ts.kernel_time = PISystemTime::fromMilliseconds(sl[13].toInt() * 10.);
|
||||
# else
|
||||
PISystemTime ct = PISystemTime::current();
|
||||
FILETIME times[4];
|
||||
HANDLE thdl = OpenThread(THREAD_QUERY_INFORMATION, FALSE, DWORD(id));
|
||||
if (!thdl) {
|
||||
piCout << "[PISystemMonitor] gatherThread(" << id << "):: OpenThread() error:" << errorString();
|
||||
return;
|
||||
}
|
||||
if (GetThreadTimes(thdl, &(times[0]), &(times[1]), &(times[2]), &(times[3])) == 0) {
|
||||
CloseHandle(thdl);
|
||||
piCout << "[PISystemMonitor] gatherThread(" << id << "):: GetThreadTimes() error:" << errorString();
|
||||
return;
|
||||
}
|
||||
CloseHandle(thdl);
|
||||
ts.created = FILETIME2PIDateTime(times[0]);
|
||||
ts.work_time = ct - ts.created.toSystemTime();
|
||||
ts.kernel_time = FILETIME2PISystemTime(times[2]);
|
||||
ts.user_time = FILETIME2PISystemTime(times[3]);
|
||||
# endif
|
||||
#endif
|
||||
cur_tm[id] = ts;
|
||||
}
|
||||
|
||||
|
||||
float PISystemMonitor::calcThreadUsage(PISystemTime & t_new, PISystemTime & t_old) {
|
||||
if (delay_.isNull()) return -1.;
|
||||
return piClampf(100. * ((t_new - t_old).toMilliseconds() / delay_.toMilliseconds()), 0.f, 100.f);
|
||||
}
|
||||
|
||||
|
||||
ullong PISystemMonitor::totalRAM() {
|
||||
#ifdef ESP_PLATFORM
|
||||
multi_heap_info_t heap_info;
|
||||
memset(&heap_info, 0, sizeof(multi_heap_info_t));
|
||||
heap_caps_get_info(&heap_info, MALLOC_CAP_8BIT);
|
||||
return heap_info.total_allocated_bytes + heap_info.total_free_bytes;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ullong PISystemMonitor::freeRAM() {
|
||||
#ifdef ESP_PLATFORM
|
||||
multi_heap_info_t heap_info;
|
||||
memset(&heap_info, 0, sizeof(multi_heap_info_t));
|
||||
heap_caps_get_info(&heap_info, MALLOC_CAP_8BIT);
|
||||
return heap_info.total_free_bytes;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
ullong PISystemMonitor::usedRAM() {
|
||||
#ifdef ESP_PLATFORM
|
||||
multi_heap_info_t heap_info;
|
||||
memset(&heap_info, 0, sizeof(multi_heap_info_t));
|
||||
heap_caps_get_info(&heap_info, MALLOC_CAP_8BIT);
|
||||
return heap_info.total_allocated_bytes;
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
PISystemMonitor::Pool * PISystemMonitor::Pool::instance() {
|
||||
static Pool ret;
|
||||
return &ret;
|
||||
}
|
||||
|
||||
|
||||
PISystemMonitor * PISystemMonitor::Pool::getByPID(int pID) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
return sysmons.value(pID, 0);
|
||||
}
|
||||
|
||||
|
||||
void PISystemMonitor::Pool::add(PISystemMonitor * sm) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
sysmons[sm->pID()] = sm;
|
||||
}
|
||||
|
||||
|
||||
void PISystemMonitor::Pool::remove(PISystemMonitor * sm) {
|
||||
PIMutexLocker _ml(mutex);
|
||||
sysmons.remove(sm->pID());
|
||||
}
|
||||
323
libs/main/application/pisystemmonitor.h
Normal file
323
libs/main/application/pisystemmonitor.h
Normal file
@@ -0,0 +1,323 @@
|
||||
/*! \file pisystemmonitor.h
|
||||
* \ingroup Application
|
||||
* \~\brief
|
||||
* \~english System resources monitoring
|
||||
* \~russian Мониторинг ресурсов системы
|
||||
*/
|
||||
/*
|
||||
PIP - Platform Independent Primitives
|
||||
Process resource monitor
|
||||
Ivan Pelipenko peri4ko@yandex.ru
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef PISYSTEMMONITOR_H
|
||||
#define PISYSTEMMONITOR_H
|
||||
|
||||
#include "pifile.h"
|
||||
#include "pithread.h"
|
||||
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Process monitoring.
|
||||
//! \~russian Мониторинг процесса.
|
||||
class PIP_EXPORT PISystemMonitor: public PIThread {
|
||||
PIOBJECT_SUBCLASS(PISystemMonitor, PIThread);
|
||||
friend class PIIntrospectionServer;
|
||||
|
||||
public:
|
||||
//! \~english Constructs unassigned %PISystemMonitor
|
||||
//! \~russian Создает непривязанный %PISystemMonitor
|
||||
PISystemMonitor();
|
||||
|
||||
~PISystemMonitor();
|
||||
|
||||
#pragma pack(push, 1)
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Process statistics (fixed-size fields).
|
||||
//! \~russian Статистика процесса (фиксированные поля).
|
||||
struct PIP_EXPORT ProcessStatsFixed {
|
||||
//! \~english PID
|
||||
//! \~russian PID
|
||||
int ID = 0;
|
||||
|
||||
//! \~english Parent PID
|
||||
//! \~russian PID родителя
|
||||
int parent_ID = 0;
|
||||
|
||||
//! \~english Group ID
|
||||
//! \~russian ID группы
|
||||
int group_ID = 0;
|
||||
|
||||
//! \~english Session ID
|
||||
//! \~russian ID сессии
|
||||
int session_ID = 0;
|
||||
|
||||
//! \~english Priority
|
||||
//! \~russian Приоритет
|
||||
int priority = 0;
|
||||
|
||||
//! \~english Threads count
|
||||
//! \~russian Количество потоков
|
||||
int threads = 0;
|
||||
|
||||
//! \~english Physical memory in bytes
|
||||
//! \~russian Физическая память в байтах
|
||||
ullong physical_memsize = 0;
|
||||
|
||||
//! \~english Resident memory in bytes
|
||||
//! \~russian Резидентная память в байтах
|
||||
ullong resident_memsize = 0;
|
||||
|
||||
//! \~english Share memory in bytes
|
||||
//! \~russian Разделяемая память в байтах
|
||||
ullong share_memsize = 0;
|
||||
|
||||
//! \~english Virtual memory in bytes
|
||||
//! \~russian Виртуальная память в байтах
|
||||
ullong virtual_memsize = 0;
|
||||
|
||||
//! \~english Data memory in bytes
|
||||
//! \~russian Память данных в байтах
|
||||
ullong data_memsize = 0;
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
ullong ram_total = 0;
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
ullong ram_free = 0;
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
ullong ram_used = 0;
|
||||
|
||||
//! \~english CPU load in kernel space
|
||||
//! \~russian Загрузка CPU в пространстве ядра
|
||||
float cpu_load_system = 0.f;
|
||||
|
||||
//! \~english CPU load in user space
|
||||
//! \~russian Загрузка CPU в пространстве пользователя
|
||||
float cpu_load_user = 0.f;
|
||||
};
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Thread statistics (fixed-size fields).
|
||||
//! \~russian Статистика потока (фиксированные поля).
|
||||
struct PIP_EXPORT ThreadStatsFixed {
|
||||
//! \~english TID
|
||||
//! \~russian TID
|
||||
llong id = 0;
|
||||
|
||||
//! \~english Overall live time
|
||||
//! \~russian Полное время жизни
|
||||
PISystemTime work_time;
|
||||
|
||||
//! \~english Busy time in kernel space
|
||||
//! \~russian Время работы в пространстве ядра
|
||||
PISystemTime kernel_time;
|
||||
|
||||
//! \~english Busy time in user space
|
||||
//! \~russian Время работы в пространстве пользователя
|
||||
PISystemTime user_time;
|
||||
|
||||
//! \~english CPU load in kernel space
|
||||
//! \~russian Загрузка CPU в пространстве ядра
|
||||
float cpu_load_kernel = -1.f;
|
||||
|
||||
//! \~english CPU load in user space
|
||||
//! \~russian Загрузка CPU в пространстве пользователя
|
||||
float cpu_load_user = -1.f;
|
||||
|
||||
//! \~english Date and time of creation
|
||||
//! \~russian Дата и время создания
|
||||
PIDateTime created;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Process statistics.
|
||||
//! \~russian Статистика процесса.
|
||||
struct PIP_EXPORT ProcessStats: ProcessStatsFixed {
|
||||
//! \~english Fill human-readable fields
|
||||
//! \~russian Заполнить читаемые поля
|
||||
void makeStrings();
|
||||
|
||||
//! \~english Execution command
|
||||
//! \~russian Команда запуска
|
||||
PIString exec_name;
|
||||
|
||||
//! \~english State
|
||||
//! \~russian Состояние
|
||||
PIString state;
|
||||
|
||||
//! \~english Human-readable physical memory
|
||||
//! \~russian Физическая память в читаемом виде
|
||||
PIString physical_memsize_readable;
|
||||
|
||||
//! \~english Human-readable resident memory
|
||||
//! \~russian Резидентная память в читаемом виде
|
||||
PIString resident_memsize_readable;
|
||||
|
||||
//! \~english Human-readable share memory
|
||||
//! \~russian Разделяемая память в читаемом виде
|
||||
PIString share_memsize_readable;
|
||||
|
||||
//! \~english Human-readable virtual memory
|
||||
//! \~russian Виртуальная память в читаемом виде
|
||||
PIString virtual_memsize_readable;
|
||||
|
||||
//! \~english Human-readable data memory
|
||||
//! \~russian Память данных в читаемом виде
|
||||
PIString data_memsize_readable;
|
||||
};
|
||||
|
||||
//! \ingroup Application
|
||||
//! \~\brief
|
||||
//! \~english Thread statistics.
|
||||
//! \~russian Статистика потока.
|
||||
struct PIP_EXPORT ThreadStats: ThreadStatsFixed {
|
||||
//! \~english Name
|
||||
//! \~russian Имя
|
||||
PIString name;
|
||||
};
|
||||
|
||||
#ifndef MICRO_PIP
|
||||
|
||||
//! \~english Starts monitoring of process with PID "pID" and update interval "interval_ms" milliseconds
|
||||
//! \~russian Начинает мониторинг процесса с PID "pID" и интервалом обновления "interval_ms" миллисекунд
|
||||
bool startOnProcess(int pID, PISystemTime interval = PISystemTime::fromSeconds(1.));
|
||||
#endif
|
||||
|
||||
//! \~english Starts monitoring of application process with update interval "interval_ms" milliseconds
|
||||
//! \~russian Начинает мониторинг процесса приложения с интервалом обновления "interval_ms" миллисекунд
|
||||
bool startOnSelf(PISystemTime interval = PISystemTime::fromSeconds(1.));
|
||||
|
||||
//! \~english Stop monitoring
|
||||
//! \~russian Останавливает мониторинг
|
||||
void stop();
|
||||
|
||||
|
||||
//! \~english Returns monitoring process PID
|
||||
//! \~russian Возвращает PID наблюдаемого процесса
|
||||
int pID() const { return pID_; }
|
||||
|
||||
//! \~english Returns monitoring process statistics
|
||||
//! \~russian Возвращает статистику наблюдаемого процесса
|
||||
ProcessStats statistic() const;
|
||||
|
||||
//! \~english Returns monitoring process threads statistics
|
||||
//! \~russian Возвращает статистику потоков наблюдаемого процесса
|
||||
PIVector<ThreadStats> threadsStatistic() const;
|
||||
|
||||
void setStatistic(const ProcessStats & s);
|
||||
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
static ullong totalRAM();
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
static ullong freeRAM();
|
||||
|
||||
//! \~english
|
||||
//! \~russian
|
||||
static ullong usedRAM();
|
||||
|
||||
|
||||
private:
|
||||
void run() override;
|
||||
void gatherThread(llong id);
|
||||
float calcThreadUsage(PISystemTime & t_new, PISystemTime & t_old);
|
||||
|
||||
ProcessStats stat;
|
||||
PIVector<ThreadStats> cur_ts;
|
||||
PIMap<llong, ThreadStats> last_tm, cur_tm;
|
||||
PIMap<llong, PIString> tbid;
|
||||
mutable PIMutex stat_mutex;
|
||||
int pID_, page_size, cpu_count, cycle;
|
||||
#ifndef MICRO_PIP
|
||||
PRIVATE_DECLARATION(PIP_EXPORT)
|
||||
#endif
|
||||
|
||||
class PIP_EXPORT Pool {
|
||||
friend class PISystemMonitor;
|
||||
|
||||
public:
|
||||
static Pool * instance();
|
||||
PISystemMonitor * getByPID(int pID);
|
||||
|
||||
private:
|
||||
void add(PISystemMonitor * sm);
|
||||
void remove(PISystemMonitor * sm);
|
||||
PIMap<int, PISystemMonitor *> sysmons;
|
||||
PIMutex mutex;
|
||||
};
|
||||
};
|
||||
|
||||
//! \relatesalso PICout
|
||||
//! \~english Output operator to \a PICout
|
||||
//! \~russian Оператор вывода в \a PICout
|
||||
inline PICout operator<<(PICout s, const PISystemMonitor::ThreadStats & v) {
|
||||
s.saveAndSetControls(0);
|
||||
s << "ThreadInfo(\"" << v.name << "\", created " << v.created << ", work " << v.work_time.toMilliseconds() << " ms"
|
||||
<< ", kernel " << v.kernel_time.toMilliseconds() << " ms"
|
||||
<< ", user " << v.user_time.toMilliseconds() << " ms"
|
||||
<< ")\n";
|
||||
s.restoreControls();
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
//! \relatesalso PIBinaryStream
|
||||
//! \~english Store operator.
|
||||
//! \~russian Оператор сохранения.
|
||||
BINARY_STREAM_WRITE(PISystemMonitor::ProcessStats) {
|
||||
s << PIMemoryBlock(&v, sizeof(PISystemMonitor::ProcessStatsFixed)) << v.exec_name << v.state;
|
||||
return s;
|
||||
}
|
||||
|
||||
//! \relatesalso PIBinaryStream
|
||||
//! \~english Restore operator.
|
||||
//! \~russian Оператор извлечения.
|
||||
BINARY_STREAM_READ(PISystemMonitor::ProcessStats) {
|
||||
s >> PIMemoryBlock(&v, sizeof(PISystemMonitor::ProcessStatsFixed)) >> v.exec_name >> v.state;
|
||||
v.makeStrings();
|
||||
return s;
|
||||
}
|
||||
|
||||
//! \relatesalso PIBinaryStream
|
||||
//! \~english Store operator.
|
||||
//! \~russian Оператор сохранения.
|
||||
BINARY_STREAM_WRITE(PISystemMonitor::ThreadStats) {
|
||||
s << PIMemoryBlock(&v, sizeof(PISystemMonitor::ThreadStatsFixed)) << v.name;
|
||||
return s;
|
||||
}
|
||||
|
||||
//! \relatesalso PIBinaryStream
|
||||
//! \~english Restore operator.
|
||||
//! \~russian Оператор извлечения.
|
||||
BINARY_STREAM_READ(PISystemMonitor::ThreadStats) {
|
||||
s >> PIMemoryBlock(&v, sizeof(PISystemMonitor::ThreadStatsFixed)) >> v.name;
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif // PISYSTEMMONITOR_H
|
||||
Reference in New Issue
Block a user