summaryrefslogtreecommitdiffstats
path: root/xmrstak/misc
diff options
context:
space:
mode:
Diffstat (limited to 'xmrstak/misc')
-rw-r--r--xmrstak/misc/configEditor.hpp57
-rw-r--r--xmrstak/misc/console.cpp227
-rw-r--r--xmrstak/misc/console.hpp47
-rw-r--r--xmrstak/misc/environment.hpp46
-rw-r--r--xmrstak/misc/executor.cpp1005
-rw-r--r--xmrstak/misc/executor.hpp186
-rw-r--r--xmrstak/misc/jext.hpp13
-rw-r--r--xmrstak/misc/telemetry.cpp107
-rw-r--r--xmrstak/misc/telemetry.hpp23
-rw-r--r--xmrstak/misc/thdq.hpp49
10 files changed, 1760 insertions, 0 deletions
diff --git a/xmrstak/misc/configEditor.hpp b/xmrstak/misc/configEditor.hpp
new file mode 100644
index 0000000..80607ff
--- /dev/null
+++ b/xmrstak/misc/configEditor.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <atomic>
+#include <string>
+#include <fstream>
+#include <streambuf>
+#include <regex>
+
+
+namespace xmrstak
+{
+
+struct ConfigEditor
+{
+ std::string m_fileContent;
+
+ ConfigEditor()
+ {
+
+ }
+
+ static bool file_exist( const std::string filename)
+ {
+ std::ifstream fstream(filename);
+ return fstream.good();
+ }
+
+ void set( const std::string && content)
+ {
+ m_fileContent = content;
+ }
+
+ bool load(const std::string filename)
+ {
+ std::ifstream fstream(filename);
+ m_fileContent = std::string(
+ (std::istreambuf_iterator<char>(fstream)),
+ std::istreambuf_iterator<char>()
+ );
+ return fstream.good();
+ }
+
+ void write(const std::string filename)
+ {
+ std::ofstream out(filename);
+ out << m_fileContent;
+ out.close();
+ }
+
+ void replace(const std::string search, const std::string substring)
+ {
+ m_fileContent = std::regex_replace(m_fileContent, std::regex(search), substring);
+ }
+
+};
+
+} // namepsace xmrstak
diff --git a/xmrstak/misc/console.cpp b/xmrstak/misc/console.cpp
new file mode 100644
index 0000000..0c73b1d
--- /dev/null
+++ b/xmrstak/misc/console.cpp
@@ -0,0 +1,227 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining
+ * it with OpenSSL (or a modified version of that library), containing parts
+ * covered by the terms of OpenSSL License and SSLeay License, the licensors
+ * of this Program grant you additional permission to convey the resulting work.
+ *
+ */
+
+#include "console.h"
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <cstdlib>
+
+#ifdef _WIN32
+#include <windows.h>
+
+int get_key()
+{
+ DWORD mode, rd;
+ HANDLE h;
+
+ if ((h = GetStdHandle(STD_INPUT_HANDLE)) == NULL)
+ return -1;
+
+ GetConsoleMode( h, &mode );
+ SetConsoleMode( h, mode & ~(ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT) );
+
+ int c = 0;
+ ReadConsole( h, &c, 1, &rd, NULL );
+ SetConsoleMode( h, mode );
+
+ return c;
+}
+
+void set_colour(out_colours cl)
+{
+ WORD attr = 0;
+
+ switch(cl)
+ {
+ case K_RED:
+ attr = FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ case K_GREEN:
+ attr = FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ case K_BLUE:
+ attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY;
+ break;
+ case K_YELLOW:
+ attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ case K_CYAN:
+ attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ case K_MAGENTA:
+ attr = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY;
+ break;
+ case K_WHITE:
+ attr = FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
+ break;
+ default:
+ break;
+ }
+
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), attr);
+}
+
+void reset_colour()
+{
+ SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
+}
+
+#else
+#include <termios.h>
+#include <unistd.h>
+#include <stdio.h>
+
+int get_key()
+{
+ struct termios oldattr, newattr;
+ int ch;
+ tcgetattr( STDIN_FILENO, &oldattr );
+ newattr = oldattr;
+ newattr.c_lflag &= ~( ICANON | ECHO );
+ tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
+ ch = getchar();
+ tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
+ return ch;
+}
+
+void set_colour(out_colours cl)
+{
+ switch(cl)
+ {
+ case K_RED:
+ fputs("\x1B[1;31m", stdout);
+ break;
+ case K_GREEN:
+ fputs("\x1B[1;32m", stdout);
+ break;
+ case K_BLUE:
+ fputs("\x1B[1;34m", stdout);
+ break;
+ case K_YELLOW:
+ fputs("\x1B[1;33m", stdout);
+ break;
+ case K_CYAN:
+ fputs("\x1B[1;36m", stdout);
+ break;
+ case K_MAGENTA:
+ fputs("\x1B[1;35m", stdout);
+ break;
+ case K_WHITE:
+ fputs("\x1B[1;37m", stdout);
+ break;
+ default:
+ break;
+ }
+}
+
+void reset_colour()
+{
+ fputs("\x1B[0m", stdout);
+}
+#endif // _WIN32
+
+inline void comp_localtime(const time_t* ctime, tm* stime)
+{
+#ifdef _WIN32
+ localtime_s(stime, ctime);
+#else
+ localtime_r(ctime, stime);
+#endif // __WIN32
+}
+
+printer::printer()
+{
+ verbose_level = LINF;
+ logfile = nullptr;
+}
+
+bool printer::open_logfile(const char* file)
+{
+ logfile = fopen(file, "ab+");
+ return logfile != nullptr;
+}
+
+void printer::print_msg(verbosity verbose, const char* fmt, ...)
+{
+ if(verbose > verbose_level)
+ return;
+
+ char buf[1024];
+ size_t bpos;
+ tm stime;
+
+ time_t now = time(nullptr);
+ comp_localtime(&now, &stime);
+ strftime(buf, sizeof(buf), "[%F %T] : ", &stime);
+ bpos = strlen(buf);
+
+ va_list args;
+ va_start(args, fmt);
+ vsnprintf(buf+bpos, sizeof(buf)-bpos, fmt, args);
+ va_end(args);
+ bpos = strlen(buf);
+
+ if(bpos+2 >= sizeof(buf))
+ return;
+
+ buf[bpos] = '\n';
+ buf[bpos+1] = '\0';
+
+ std::unique_lock<std::mutex> lck(print_mutex);
+ fputs(buf, stdout);
+
+ if(logfile != nullptr)
+ {
+ fputs(buf, logfile);
+ fflush(logfile);
+ }
+}
+
+void printer::print_str(const char* str)
+{
+ std::unique_lock<std::mutex> lck(print_mutex);
+ fputs(str, stdout);
+
+ if(logfile != nullptr)
+ {
+ fputs(str, logfile);
+ fflush(logfile);
+ }
+}
+
+//Do a press any key for the windows folk. *insert any key joke here*
+#ifdef _WIN32
+void win_exit()
+{
+ printer::inst()->print_str("Press any key to exit.");
+ get_key();
+ std::exit(1);
+}
+
+#else
+void win_exit() {
+ std::exit(1);
+}
+#endif // _WIN32
diff --git a/xmrstak/misc/console.hpp b/xmrstak/misc/console.hpp
new file mode 100644
index 0000000..ac2ed3c
--- /dev/null
+++ b/xmrstak/misc/console.hpp
@@ -0,0 +1,47 @@
+#pragma once
+#include <mutex>
+#include "Environment.hpp"
+
+enum out_colours { K_RED, K_GREEN, K_BLUE, K_YELLOW, K_CYAN, K_MAGENTA, K_WHITE, K_NONE };
+
+// Warning - on Linux get_key will detect control keys, but not on Windows.
+// We will only use it for alphanum keys anyway.
+int get_key();
+
+void set_colour(out_colours cl);
+void reset_colour();
+
+// on MSVC sizeof(long int) = 4, gcc sizeof(long int) = 8, this is the workaround
+// now we can use %llu on both compilers
+inline long long unsigned int int_port(size_t i)
+{
+ return i;
+}
+
+enum verbosity : size_t { L0 = 0, L1 = 1, L2 = 2, L3 = 3, L4 = 4, LINF = 100};
+
+class printer
+{
+public:
+ static inline printer* inst()
+ {
+ auto& env = xmrstak::Environment::inst();
+ if(env.pPrinter == nullptr)
+ env.pPrinter = new printer;
+ return env.pPrinter;
+ };
+
+ inline void set_verbose_level(size_t level) { verbose_level = (verbosity)level; }
+ void print_msg(verbosity verbose, const char* fmt, ...);
+ void print_str(const char* str);
+ bool open_logfile(const char* file);
+
+private:
+ printer();
+
+ std::mutex print_mutex;
+ verbosity verbose_level;
+ FILE* logfile;
+};
+
+void win_exit();
diff --git a/xmrstak/misc/environment.hpp b/xmrstak/misc/environment.hpp
new file mode 100644
index 0000000..15f8cce
--- /dev/null
+++ b/xmrstak/misc/environment.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+class printer;
+class jconf;
+class executor;
+
+namespace xmrstak
+{
+
+class GlobalStates;
+class Params;
+
+struct Environment
+{
+
+ static Environment& inst()
+ {
+ static Environment env;
+ return env;
+ }
+
+ Environment& operator=(const Environment& env)
+ {
+ this->pPrinter = env.pPrinter;
+ this->pGlobalStates = env.pGlobalStates;
+ this->pJconfConfig = env.pJconfConfig;
+ this->pExecutor = env.pExecutor;
+ this->pParams = env.pParams;
+ return *this;
+ }
+
+
+ Environment() : pPrinter(nullptr), pGlobalStates(nullptr)
+ {
+ }
+
+
+ printer* pPrinter;
+ GlobalStates* pGlobalStates;
+ jconf* pJconfConfig;
+ executor* pExecutor;
+ Params* pParams;
+
+};
+
+} // namepsace xmrstak
diff --git a/xmrstak/misc/executor.cpp b/xmrstak/misc/executor.cpp
new file mode 100644
index 0000000..64f3d40
--- /dev/null
+++ b/xmrstak/misc/executor.cpp
@@ -0,0 +1,1005 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining
+ * it with OpenSSL (or a modified version of that library), containing parts
+ * covered by the terms of OpenSSL License and SSLeay License, the licensors
+ * of this Program grant you additional permission to convey the resulting work.
+ *
+ */
+
+#include <thread>
+#include <string>
+#include <cmath>
+#include <algorithm>
+#include <assert.h>
+#include <time.h>
+#include "executor.h"
+#include "jpsock.h"
+
+#include "telemetry.h"
+#include "backend/miner_work.h"
+#include "backend/GlobalStates.hpp"
+#include "backend/BackendConnector.hpp"
+
+#include "jconf.h"
+#include "console.h"
+#include "donate-level.h"
+#include "webdesign.h"
+
+#ifdef _WIN32
+#define strncasecmp _strnicmp
+#endif // _WIN32
+
+executor::executor()
+{
+}
+
+void executor::push_timed_event(ex_event&& ev, size_t sec)
+{
+ std::unique_lock<std::mutex> lck(timed_event_mutex);
+ lTimedEvents.emplace_back(std::move(ev), sec_to_ticks(sec));
+}
+
+void executor::ex_clock_thd()
+{
+ size_t iSwitchPeriod = sec_to_ticks(iDevDonatePeriod);
+ size_t iDevPortion = (size_t)floor(((double)iSwitchPeriod) * fDevDonationLevel);
+
+ //No point in bothering with less than 10 sec
+ if(iDevPortion < sec_to_ticks(10))
+ iDevPortion = 0;
+
+ //Add 2 seconds to compensate for connect
+ if(iDevPortion != 0)
+ iDevPortion += sec_to_ticks(2);
+
+ while (true)
+ {
+ std::this_thread::sleep_for(std::chrono::milliseconds(size_t(iTickTime)));
+
+ push_event(ex_event(EV_PERF_TICK));
+
+ // Service timed events
+ std::unique_lock<std::mutex> lck(timed_event_mutex);
+ std::list<timed_event>::iterator ev = lTimedEvents.begin();
+ while (ev != lTimedEvents.end())
+ {
+ ev->ticks_left--;
+ if(ev->ticks_left == 0)
+ {
+ push_event(std::move(ev->event));
+ ev = lTimedEvents.erase(ev);
+ }
+ else
+ ev++;
+ }
+ lck.unlock();
+
+ if(iDevPortion == 0)
+ continue;
+
+ iSwitchPeriod--;
+ if(iSwitchPeriod == 0)
+ {
+ push_event(ex_event(EV_SWITCH_POOL, usr_pool_id));
+ iSwitchPeriod = sec_to_ticks(iDevDonatePeriod);
+ }
+ else if(iSwitchPeriod == iDevPortion)
+ {
+ push_event(ex_event(EV_SWITCH_POOL, dev_pool_id));
+ }
+ }
+}
+
+void executor::sched_reconnect()
+{
+ iReconnectAttempts++;
+ size_t iLimit = jconf::inst()->GetGiveUpLimit();
+ if(iLimit != 0 && iReconnectAttempts > iLimit)
+ {
+ printer::inst()->print_msg(L0, "Give up limit reached. Exitting.");
+ exit(0);
+ }
+
+ long long unsigned int rt = jconf::inst()->GetNetRetry();
+ printer::inst()->print_msg(L1, "Pool connection lost. Waiting %lld s before retry (attempt %llu).",
+ rt, int_port(iReconnectAttempts));
+
+ auto work = xmrstak::miner_work();
+ xmrstak::GlobalStates::inst().switch_work(work);
+
+ push_timed_event(ex_event(EV_RECONNECT, usr_pool_id), rt);
+}
+
+void executor::log_socket_error(std::string&& sError)
+{
+ vSocketLog.emplace_back(std::move(sError));
+ printer::inst()->print_msg(L1, "SOCKET ERROR - %s", vSocketLog.back().msg.c_str());
+}
+
+void executor::log_result_error(std::string&& sError)
+{
+ size_t i = 1, ln = vMineResults.size();
+ for(; i < ln; i++)
+ {
+ if(vMineResults[i].compare(sError))
+ {
+ vMineResults[i].increment();
+ break;
+ }
+ }
+
+ if(i == ln) //Not found
+ vMineResults.emplace_back(std::move(sError));
+ else
+ sError.clear();
+}
+
+void executor::log_result_ok(uint64_t iActualDiff)
+{
+ iPoolHashes += iPoolDiff;
+
+ size_t ln = iTopDiff.size() - 1;
+ if(iActualDiff > iTopDiff[ln])
+ {
+ iTopDiff[ln] = iActualDiff;
+ std::sort(iTopDiff.rbegin(), iTopDiff.rend());
+ }
+
+ vMineResults[0].increment();
+}
+
+jpsock* executor::pick_pool_by_id(size_t pool_id)
+{
+ assert(pool_id != invalid_pool_id);
+
+ if(pool_id == dev_pool_id)
+ return dev_pool;
+ else
+ return usr_pool;
+}
+
+void executor::on_sock_ready(size_t pool_id)
+{
+ jpsock* pool = pick_pool_by_id(pool_id);
+
+ if(pool_id == dev_pool_id)
+ {
+ if(!pool->cmd_login("", ""))
+ pool->disconnect();
+
+ current_pool_id = dev_pool_id;
+ printer::inst()->print_msg(L1, "Dev pool logged in. Switching work.");
+ return;
+ }
+
+ printer::inst()->print_msg(L1, "Connected. Logging in...");
+
+ if (!pool->cmd_login(jconf::inst()->GetWalletAddress(), jconf::inst()->GetPoolPwd()))
+ {
+ if(!pool->have_sock_error())
+ {
+ log_socket_error(pool->get_call_error());
+ pool->disconnect();
+ }
+ }
+ else
+ {
+ iReconnectAttempts = 0;
+ reset_stats();
+ }
+}
+
+void executor::on_sock_error(size_t pool_id, std::string&& sError)
+{
+ jpsock* pool = pick_pool_by_id(pool_id);
+
+ if(pool_id == dev_pool_id)
+ {
+ pool->disconnect();
+
+ if(current_pool_id != dev_pool_id)
+ return;
+
+ printer::inst()->print_msg(L1, "Dev pool connection error. Switching work.");
+ on_switch_pool(usr_pool_id);
+ return;
+ }
+
+ log_socket_error(std::move(sError));
+ pool->disconnect();
+ sched_reconnect();
+}
+
+void executor::on_pool_have_job(size_t pool_id, pool_job& oPoolJob)
+{
+ if(pool_id != current_pool_id)
+ return;
+
+ jpsock* pool = pick_pool_by_id(pool_id);
+
+ xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
+ oPoolJob.iWorkLen, oPoolJob.iResumeCnt, oPoolJob.iTarget,
+ pool_id);
+
+ oWork.iTarget32 = oPoolJob.iTarget32;
+
+ xmrstak::GlobalStates::inst().switch_work(oWork);
+
+ if(pool_id == dev_pool_id)
+ return;
+
+ if(iPoolDiff != pool->get_current_diff())
+ {
+ iPoolDiff = pool->get_current_diff();
+ printer::inst()->print_msg(L2, "Difficulty changed. Now: %llu.", int_port(iPoolDiff));
+ }
+
+ printer::inst()->print_msg(L3, "New block detected.");
+}
+
+void executor::on_miner_result(size_t pool_id, job_result& oResult)
+{
+ jpsock* pool = pick_pool_by_id(pool_id);
+
+ if(pool_id == dev_pool_id)
+ {
+ //Ignore errors silently
+ if(pool->is_running() && pool->is_logged_in())
+ pool->cmd_submit(oResult.sJobID, oResult.iNonce, oResult.bResult);
+
+ return;
+ }
+
+ if (!pool->is_running() || !pool->is_logged_in())
+ {
+ log_result_error("[NETWORK ERROR]");
+ return;
+ }
+
+ using namespace std::chrono;
+ size_t t_start = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count();
+ bool bResult = pool->cmd_submit(oResult.sJobID, oResult.iNonce, oResult.bResult);
+ size_t t_len = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count() - t_start;
+
+ if(t_len > 0xFFFF)
+ t_len = 0xFFFF;
+ iPoolCallTimes.push_back((uint16_t)t_len);
+
+ if(bResult)
+ {
+ uint64_t* targets = (uint64_t*)oResult.bResult;
+ log_result_ok(jpsock::t64_to_diff(targets[3]));
+ printer::inst()->print_msg(L3, "Result accepted by the pool.");
+ }
+ else
+ {
+ if(!pool->have_sock_error())
+ {
+ printer::inst()->print_msg(L3, "Result rejected by the pool.");
+
+ std::string error = pool->get_call_error();
+
+ if(strncasecmp(error.c_str(), "Unauthenticated", 15) == 0)
+ {
+ printer::inst()->print_msg(L2, "Your miner was unable to find a share in time. Either the pool difficulty is too high, or the pool timeout is too low.");
+ pool->disconnect();
+ }
+
+ log_result_error(std::move(error));
+ }
+ else
+ log_result_error("[NETWORK ERROR]");
+ }
+}
+
+void executor::on_reconnect(size_t pool_id)
+{
+ jpsock* pool = pick_pool_by_id(pool_id);
+
+ std::string error;
+ if(pool_id == dev_pool_id)
+ return;
+
+ printer::inst()->print_msg(L1, "Connecting to pool %s ...", jconf::inst()->GetPoolAddress());
+
+ if(!pool->connect(jconf::inst()->GetPoolAddress(), error))
+ {
+ log_socket_error(std::move(error));
+ sched_reconnect();
+ }
+}
+
+void executor::on_switch_pool(size_t pool_id)
+{
+ if(pool_id == current_pool_id)
+ return;
+
+ jpsock* pool = pick_pool_by_id(pool_id);
+ if(pool_id == dev_pool_id)
+ {
+ std::string error;
+
+ // If it fails, it fails, we carry on on the usr pool
+ // as we never receive further events
+ printer::inst()->print_msg(L1, "Connecting to dev pool...");
+ const char* dev_pool_addr = jconf::inst()->GetTlsSetting() ? "donate.xmr-stak.net:6666" : "donate.xmr-stak.net:3333";
+ if(!pool->connect(dev_pool_addr, error))
+ printer::inst()->print_msg(L1, "Error connecting to dev pool. Staying with user pool.");
+ }
+ else
+ {
+ printer::inst()->print_msg(L1, "Switching back to user pool.");
+
+ current_pool_id = pool_id;
+ pool_job oPoolJob;
+
+ if(!pool->get_current_job(oPoolJob))
+ {
+ pool->disconnect();
+ return;
+ }
+
+ xmrstak::miner_work oWork(oPoolJob.sJobID, oPoolJob.bWorkBlob,
+ oPoolJob.iWorkLen, oPoolJob.iResumeCnt, oPoolJob.iTarget,
+ pool_id);
+
+ oWork.iTarget32 = oPoolJob.iTarget32;
+
+ xmrstak::GlobalStates::inst().switch_work(oWork);
+
+ if(dev_pool->is_running())
+ push_timed_event(ex_event(EV_DEV_POOL_EXIT), 5);
+ }
+}
+
+void executor::ex_main()
+{
+ assert(1000 % iTickTime == 0);
+
+ xmrstak::miner_work oWork = xmrstak::miner_work();
+
+ // \todo collect all backend threads
+ pvThreads = xmrstak::BackendConnector::thread_starter(oWork);
+
+ if(pvThreads->size()==0)
+ {
+ printer::inst()->print_msg(L1, "ERROR: No miner backend enabled.");
+ win_exit();
+ }
+
+ telem = new xmrstak::telemetry(pvThreads->size());
+
+ current_pool_id = usr_pool_id;
+ usr_pool = new jpsock(usr_pool_id, jconf::inst()->GetTlsSetting());
+ dev_pool = new jpsock(dev_pool_id, jconf::inst()->GetTlsSetting());
+
+ ex_event ev;
+ std::thread clock_thd(&executor::ex_clock_thd, this);
+
+ //This will connect us to the pool for the first time
+ push_event(ex_event(EV_RECONNECT, usr_pool_id));
+
+ // Place the default success result at position 0, it needs to
+ // be here even if our first result is a failure
+ vMineResults.emplace_back();
+
+ // If the user requested it, start the autohash printer
+ if(jconf::inst()->GetVerboseLevel() >= 4)
+ push_timed_event(ex_event(EV_HASHRATE_LOOP), jconf::inst()->GetAutohashTime());
+
+ size_t cnt = 0, i;
+ while (true)
+ {
+ ev = oEventQ.pop();
+ switch (ev.iName)
+ {
+ case EV_SOCK_READY:
+ on_sock_ready(ev.iPoolId);
+ break;
+
+ case EV_SOCK_ERROR:
+ on_sock_error(ev.iPoolId, std::move(ev.sSocketError));
+ break;
+
+ case EV_POOL_HAVE_JOB:
+ on_pool_have_job(ev.iPoolId, ev.oPoolJob);
+ break;
+
+ case EV_MINER_HAVE_RESULT:
+ on_miner_result(ev.iPoolId, ev.oJobResult);
+ break;
+
+ case EV_RECONNECT:
+ on_reconnect(ev.iPoolId);
+ break;
+
+ case EV_SWITCH_POOL:
+ on_switch_pool(ev.iPoolId);
+ break;
+
+ case EV_DEV_POOL_EXIT:
+ dev_pool->disconnect();
+ break;
+
+ case EV_PERF_TICK:
+ for (i = 0; i < pvThreads->size(); i++)
+ telem->push_perf_value(i, pvThreads->at(i)->iHashCount.load(std::memory_order_relaxed),
+ pvThreads->at(i)->iTimestamp.load(std::memory_order_relaxed));
+
+ if((cnt++ & 0xF) == 0) //Every 16 ticks
+ {
+ double fHps = 0.0;
+ double fTelem;
+ bool normal = true;
+
+ for (i = 0; i < pvThreads->size(); i++)
+ {
+ fTelem = telem->calc_telemetry_data(2500, i);
+ if(std::isnormal(fTelem))
+ {
+ fHps += fTelem;
+ }
+ else
+ {
+ normal = false;
+ break;
+ }
+ }
+
+ if(normal && fHighestHps < fHps)
+ fHighestHps = fHps;
+ }
+ break;
+
+ case EV_USR_HASHRATE:
+ case EV_USR_RESULTS:
+ case EV_USR_CONNSTAT:
+ print_report(ev.iName);
+ break;
+
+ case EV_HTML_HASHRATE:
+ case EV_HTML_RESULTS:
+ case EV_HTML_CONNSTAT:
+ case EV_HTML_JSON:
+ http_report(ev.iName);
+ break;
+
+ case EV_HASHRATE_LOOP:
+ print_report(EV_USR_HASHRATE);
+ push_timed_event(ex_event(EV_HASHRATE_LOOP), jconf::inst()->GetAutohashTime());
+ break;
+
+ case EV_INVALID_VAL:
+ default:
+ assert(false);
+ break;
+ }
+ }
+}
+
+inline const char* hps_format(double h, char* buf, size_t l)
+{
+ if(std::isnormal(h) || h == 0.0)
+ {
+ snprintf(buf, l, " %03.1f", h);
+ return buf;
+ }
+ else
+ return " (na)";
+}
+
+void executor::hashrate_report(std::string& out)
+{
+ char num[32];
+ size_t nthd = pvThreads->size();
+
+ out.reserve(256 + nthd * 64);
+
+ double fTotal[3] = { 0.0, 0.0, 0.0};
+ size_t i;
+
+ out.append("HASHRATE REPORT\n");
+ out.append("| ID | 10s | 60s | 15m |");
+ if(nthd != 1)
+ out.append(" ID | 10s | 60s | 15m |\n");
+ else
+ out.append(1, '\n');
+
+ for (i = 0; i < nthd; i++)
+ {
+ double fHps[3];
+
+ fHps[0] = telem->calc_telemetry_data(10000, i);
+ fHps[1] = telem->calc_telemetry_data(60000, i);
+ fHps[2] = telem->calc_telemetry_data(900000, i);
+
+ snprintf(num, sizeof(num), "| %2u |", (unsigned int)i);
+ out.append(num);
+ out.append(hps_format(fHps[0], num, sizeof(num))).append(" |");
+ out.append(hps_format(fHps[1], num, sizeof(num))).append(" |");
+ out.append(hps_format(fHps[2], num, sizeof(num))).append(1, ' ');
+
+ fTotal[0] += fHps[0];
+ fTotal[1] += fHps[1];
+ fTotal[2] += fHps[2];
+
+ if((i & 0x1) == 1) //Odd i's
+ out.append("|\n");
+ }
+
+ if((i & 0x1) == 1) //We had odd number of threads
+ out.append("|\n");
+
+ if(nthd != 1)
+ out.append("-----------------------------------------------------\n");
+ else
+ out.append("---------------------------\n");
+
+ out.append("Totals: ");
+ out.append(hps_format(fTotal[0], num, sizeof(num)));
+ out.append(hps_format(fTotal[1], num, sizeof(num)));
+ out.append(hps_format(fTotal[2], num, sizeof(num)));
+ out.append(" H/s\nHighest: ");
+ out.append(hps_format(fHighestHps, num, sizeof(num)));
+ out.append(" H/s\n");
+}
+
+char* time_format(char* buf, size_t len, std::chrono::system_clock::time_point time)
+{
+ time_t ctime = std::chrono::system_clock::to_time_t(time);
+ tm stime;
+
+ /*
+ * Oh for god's sake... this feels like we are back to the 90's...
+ * and don't get me started on lack strcpy_s because NIH - use non-standard strlcpy...
+ * And of course C++ implements unsafe version because... reasons
+ */
+
+#ifdef _WIN32
+ localtime_s(&stime, &ctime);
+#else
+ localtime_r(&ctime, &stime);
+#endif // __WIN32
+ strftime(buf, len, "%F %T", &stime);
+
+ return buf;
+}
+
+void executor::result_report(std::string& out)
+{
+ char num[128];
+ char date[32];
+
+ out.reserve(1024);
+
+ size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
+ size_t ln = vMineResults.size();
+
+ for(size_t i=1; i < ln; i++)
+ iTotalRes += vMineResults[i].count;
+
+ out.append("RESULT REPORT\n");
+ if(iTotalRes == 0)
+ {
+ out.append("You haven't found any results yet.\n");
+ return;
+ }
+
+ double dConnSec;
+ {
+ using namespace std::chrono;
+ dConnSec = (double)duration_cast<seconds>(system_clock::now() - tPoolConnTime).count();
+ }
+
+ snprintf(num, sizeof(num), " (%.1f %%)\n", 100.0 * iGoodRes / iTotalRes);
+
+ out.append("Difficulty : ").append(std::to_string(iPoolDiff)).append(1, '\n');
+ out.append("Good results : ").append(std::to_string(iGoodRes)).append(" / ").
+ append(std::to_string(iTotalRes)).append(num);
+
+ if(iPoolCallTimes.size() != 0)
+ {
+ // Here we use iPoolCallTimes since it also gets reset when we disconnect
+ snprintf(num, sizeof(num), "%.1f sec\n", dConnSec / iPoolCallTimes.size());
+ out.append("Avg result time : ").append(num);
+ }
+ out.append("Pool-side hashes : ").append(std::to_string(iPoolHashes)).append(2, '\n');
+ out.append("Top 10 best results found:\n");
+
+ for(size_t i=0; i < 10; i += 2)
+ {
+ snprintf(num, sizeof(num), "| %2llu | %16llu | %2llu | %16llu |\n",
+ int_port(i), int_port(iTopDiff[i]), int_port(i+1), int_port(iTopDiff[i+1]));
+ out.append(num);
+ }
+
+ out.append("\nError details:\n");
+ if(ln > 1)
+ {
+ out.append("| Count | Error text | Last seen |\n");
+ for(size_t i=1; i < ln; i++)
+ {
+ snprintf(num, sizeof(num), "| %5llu | %-32.32s | %s |\n", int_port(vMineResults[i].count),
+ vMineResults[i].msg.c_str(), time_format(date, sizeof(date), vMineResults[i].time));
+ out.append(num);
+ }
+ }
+ else
+ out.append("Yay! No errors.\n");
+}
+
+void executor::connection_report(std::string& out)
+{
+ char num[128];
+ char date[32];
+
+ out.reserve(512);
+
+ jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+
+ out.append("CONNECTION REPORT\n");
+ out.append("Pool address : ").append(jconf::inst()->GetPoolAddress()).append(1, '\n');
+ if (pool->is_running() && pool->is_logged_in())
+ out.append("Connected since : ").append(time_format(date, sizeof(date), tPoolConnTime)).append(1, '\n');
+ else
+ out.append("Connected since : <not connected>\n");
+
+ size_t n_calls = iPoolCallTimes.size();
+ if (n_calls > 1)
+ {
+ //Not-really-but-good-enough median
+ std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
+ out.append("Pool ping time : ").append(std::to_string(iPoolCallTimes[n_calls/2])).append(" ms\n");
+ }
+ else
+ out.append("Pool ping time : (n/a)\n");
+
+ out.append("\nNetwork error log:\n");
+ size_t ln = vSocketLog.size();
+ if(ln > 0)
+ {
+ out.append("| Date | Error text |\n");
+ for(size_t i=0; i < ln; i++)
+ {
+ snprintf(num, sizeof(num), "| %s | %-54.54s |\n",
+ time_format(date, sizeof(date), vSocketLog[i].time), vSocketLog[i].msg.c_str());
+ out.append(num);
+ }
+ }
+ else
+ out.append("Yay! No errors.\n");
+}
+
+void executor::print_report(ex_event_name ev)
+{
+ std::string out;
+ switch(ev)
+ {
+ case EV_USR_HASHRATE:
+ hashrate_report(out);
+ break;
+
+ case EV_USR_RESULTS:
+ result_report(out);
+ break;
+
+ case EV_USR_CONNSTAT:
+ connection_report(out);
+ break;
+ default:
+ assert(false);
+ break;
+ }
+
+ printer::inst()->print_str(out.c_str());
+}
+
+void executor::http_hashrate_report(std::string& out)
+{
+ char num_a[32], num_b[32], num_c[32], num_d[32];
+ char buffer[4096];
+ size_t nthd = pvThreads->size();
+
+ out.reserve(4096);
+
+ snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Hashrate Report", "Hashrate Report");
+ out.append(buffer);
+
+ snprintf(buffer, sizeof(buffer), sHtmlHashrateBodyHigh, (unsigned int)nthd + 3);
+ out.append(buffer);
+
+ double fTotal[3] = { 0.0, 0.0, 0.0};
+ for(size_t i=0; i < nthd; i++)
+ {
+ double fHps[3];
+
+ fHps[0] = telem->calc_telemetry_data(2500, i);
+ fHps[1] = telem->calc_telemetry_data(60000, i);
+ fHps[2] = telem->calc_telemetry_data(900000, i);
+
+ num_a[0] = num_b[0] = num_c[0] ='\0';
+ hps_format(fHps[0], num_a, sizeof(num_a));
+ hps_format(fHps[1], num_b, sizeof(num_b));
+ hps_format(fHps[2], num_c, sizeof(num_c));
+
+ fTotal[0] += fHps[0];
+ fTotal[1] += fHps[1];
+ fTotal[2] += fHps[2];
+
+ snprintf(buffer, sizeof(buffer), sHtmlHashrateTableRow, (unsigned int)i, num_a, num_b, num_c);
+ out.append(buffer);
+ }
+
+ num_a[0] = num_b[0] = num_c[0] = num_d[0] ='\0';
+ hps_format(fTotal[0], num_a, sizeof(num_a));
+ hps_format(fTotal[1], num_b, sizeof(num_b));
+ hps_format(fTotal[2], num_c, sizeof(num_c));
+ hps_format(fHighestHps, num_d, sizeof(num_d));
+
+ snprintf(buffer, sizeof(buffer), sHtmlHashrateBodyLow, num_a, num_b, num_c, num_d);
+ out.append(buffer);
+}
+
+void executor::http_result_report(std::string& out)
+{
+ char date[128];
+ char buffer[4096];
+
+ out.reserve(4096);
+
+ snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Result Report", "Result Report");
+ out.append(buffer);
+
+ size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
+ size_t ln = vMineResults.size();
+
+ for(size_t i=1; i < ln; i++)
+ iTotalRes += vMineResults[i].count;
+
+ double fGoodResPrc = 0.0;
+ if(iTotalRes > 0)
+ fGoodResPrc = 100.0 * iGoodRes / iTotalRes;
+
+ double fAvgResTime = 0.0;
+ if(iPoolCallTimes.size() > 0)
+ {
+ using namespace std::chrono;
+ fAvgResTime = ((double)duration_cast<seconds>(system_clock::now() - tPoolConnTime).count())
+ / iPoolCallTimes.size();
+ }
+
+ snprintf(buffer, sizeof(buffer), sHtmlResultBodyHigh,
+ iPoolDiff, iGoodRes, iTotalRes, fGoodResPrc, fAvgResTime, iPoolHashes,
+ int_port(iTopDiff[0]), int_port(iTopDiff[1]), int_port(iTopDiff[2]), int_port(iTopDiff[3]),
+ int_port(iTopDiff[4]), int_port(iTopDiff[5]), int_port(iTopDiff[6]), int_port(iTopDiff[7]),
+ int_port(iTopDiff[8]), int_port(iTopDiff[9]));
+
+ out.append(buffer);
+
+ for(size_t i=1; i < vMineResults.size(); i++)
+ {
+ snprintf(buffer, sizeof(buffer), sHtmlResultTableRow, vMineResults[i].msg.c_str(),
+ int_port(vMineResults[i].count), time_format(date, sizeof(date), vMineResults[i].time));
+ out.append(buffer);
+ }
+
+ out.append(sHtmlResultBodyLow);
+}
+
+void executor::http_connection_report(std::string& out)
+{
+ char date[128];
+ char buffer[4096];
+
+ out.reserve(4096);
+
+ snprintf(buffer, sizeof(buffer), sHtmlCommonHeader, "Connection Report", "Connection Report");
+ out.append(buffer);
+
+ jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+ const char* cdate = "not connected";
+ if (pool->is_running() && pool->is_logged_in())
+ cdate = time_format(date, sizeof(date), tPoolConnTime);
+
+ size_t n_calls = iPoolCallTimes.size();
+ unsigned int ping_time = 0;
+ if (n_calls > 1)
+ {
+ //Not-really-but-good-enough median
+ std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
+ ping_time = iPoolCallTimes[n_calls/2];
+ }
+
+ snprintf(buffer, sizeof(buffer), sHtmlConnectionBodyHigh,
+ jconf::inst()->GetPoolAddress(),
+ cdate, ping_time);
+ out.append(buffer);
+
+
+ for(size_t i=0; i < vSocketLog.size(); i++)
+ {
+ snprintf(buffer, sizeof(buffer), sHtmlConnectionTableRow,
+ time_format(date, sizeof(date), vSocketLog[i].time), vSocketLog[i].msg.c_str());
+ out.append(buffer);
+ }
+
+ out.append(sHtmlConnectionBodyLow);
+}
+
+inline const char* hps_format_json(double h, char* buf, size_t l)
+{
+ if(std::isnormal(h) || h == 0.0)
+ {
+ snprintf(buf, l, "%.1f", h);
+ return buf;
+ }
+ else
+ return "null";
+}
+
+void executor::http_json_report(std::string& out)
+{
+ const char *a, *b, *c;
+ char num_a[32], num_b[32], num_c[32];
+ char hr_buffer[64];
+ std::string hr_thds, res_error, cn_error;
+
+ size_t nthd = pvThreads->size();
+ double fTotal[3] = { 0.0, 0.0, 0.0};
+ hr_thds.reserve(nthd * 32);
+
+ for(size_t i=0; i < nthd; i++)
+ {
+ if(i != 0) hr_thds.append(1, ',');
+
+ double fHps[3];
+ fHps[0] = telem->calc_telemetry_data(2500, i);
+ fHps[1] = telem->calc_telemetry_data(60000, i);
+ fHps[2] = telem->calc_telemetry_data(900000, i);
+
+ fTotal[0] += fHps[0];
+ fTotal[1] += fHps[1];
+ fTotal[2] += fHps[2];
+
+ a = hps_format_json(fHps[0], num_a, sizeof(num_a));
+ b = hps_format_json(fHps[1], num_b, sizeof(num_b));
+ c = hps_format_json(fHps[2], num_c, sizeof(num_c));
+ snprintf(hr_buffer, sizeof(hr_buffer), sJsonApiThdHashrate, a, b, c);
+ hr_thds.append(hr_buffer);
+ }
+
+ a = hps_format_json(fTotal[0], num_a, sizeof(num_a));
+ b = hps_format_json(fTotal[1], num_b, sizeof(num_b));
+ c = hps_format_json(fTotal[2], num_c, sizeof(num_c));
+ snprintf(hr_buffer, sizeof(hr_buffer), sJsonApiThdHashrate, a, b, c);
+
+ a = hps_format_json(fHighestHps, num_a, sizeof(num_a));
+
+ size_t iGoodRes = vMineResults[0].count, iTotalRes = iGoodRes;
+ size_t ln = vMineResults.size();
+
+ for(size_t i=1; i < ln; i++)
+ iTotalRes += vMineResults[i].count;
+
+ jpsock* pool = pick_pool_by_id(dev_pool_id + 1);
+
+ size_t iConnSec = 0;
+ if(pool->is_running() && pool->is_logged_in())
+ {
+ using namespace std::chrono;
+ iConnSec = duration_cast<seconds>(system_clock::now() - tPoolConnTime).count();
+ }
+
+ double fAvgResTime = 0.0;
+ if(iPoolCallTimes.size() > 0)
+ fAvgResTime = double(iConnSec) / iPoolCallTimes.size();
+
+ res_error.reserve((vMineResults.size() - 1) * 128);
+ char buffer[256];
+ for(size_t i=1; i < vMineResults.size(); i++)
+ {
+ using namespace std::chrono;
+ if(i != 1) res_error.append(1, ',');
+
+ snprintf(buffer, sizeof(buffer), sJsonApiResultError, int_port(vMineResults[i].count),
+ int_port(duration_cast<seconds>(vMineResults[i].time.time_since_epoch()).count()),
+ vMineResults[i].msg.c_str());
+ res_error.append(buffer);
+ }
+
+ size_t n_calls = iPoolCallTimes.size();
+ size_t iPoolPing = 0;
+ if (n_calls > 1)
+ {
+ //Not-really-but-good-enough median
+ std::nth_element(iPoolCallTimes.begin(), iPoolCallTimes.begin() + n_calls/2, iPoolCallTimes.end());
+ iPoolPing = iPoolCallTimes[n_calls/2];
+ }
+
+ cn_error.reserve(vSocketLog.size() * 128);
+ for(size_t i=0; i < vSocketLog.size(); i++)
+ {
+ using namespace std::chrono;
+ if(i != 0) cn_error.append(1, ',');
+
+ snprintf(buffer, sizeof(buffer), sJsonApiConnectionError,
+ int_port(duration_cast<seconds>(vMineResults[i].time.time_since_epoch()).count()),
+ vSocketLog[i].msg.c_str());
+ cn_error.append(buffer);
+ }
+
+ size_t bb_size = 1024 + hr_thds.size() + res_error.size() + cn_error.size();
+ std::unique_ptr<char[]> bigbuf( new char[ bb_size ] );
+
+ int bb_len = snprintf(bigbuf.get(), bb_size, sJsonApiFormat,
+ hr_thds.c_str(), hr_buffer, a,
+ int_port(iPoolDiff), int_port(iGoodRes), int_port(iTotalRes), fAvgResTime, int_port(iPoolHashes),
+ int_port(iTopDiff[0]), int_port(iTopDiff[1]), int_port(iTopDiff[2]), int_port(iTopDiff[3]), int_port(iTopDiff[4]),
+ int_port(iTopDiff[5]), int_port(iTopDiff[6]), int_port(iTopDiff[7]), int_port(iTopDiff[8]), int_port(iTopDiff[9]),
+ res_error.c_str(), jconf::inst()->GetPoolAddress(), int_port(iConnSec), int_port(iPoolPing), cn_error.c_str());
+
+ out = std::string(bigbuf.get(), bigbuf.get() + bb_len);
+}
+
+void executor::http_report(ex_event_name ev)
+{
+ assert(pHttpString != nullptr);
+
+ switch(ev)
+ {
+ case EV_HTML_HASHRATE:
+ http_hashrate_report(*pHttpString);
+ break;
+
+ case EV_HTML_RESULTS:
+ http_result_report(*pHttpString);
+ break;
+
+ case EV_HTML_CONNSTAT:
+ http_connection_report(*pHttpString);
+ break;
+
+ case EV_HTML_JSON:
+ http_json_report(*pHttpString);
+ break;
+
+ default:
+ assert(false);
+ break;
+ }
+
+ httpReady.set_value();
+}
+
+void executor::get_http_report(ex_event_name ev_id, std::string& data)
+{
+ std::lock_guard<std::mutex> lck(httpMutex);
+
+ assert(pHttpString == nullptr);
+ assert(ev_id == EV_HTML_HASHRATE || ev_id == EV_HTML_RESULTS
+ || ev_id == EV_HTML_CONNSTAT || ev_id == EV_HTML_JSON);
+
+ pHttpString = &data;
+ httpReady = std::promise<void>();
+ std::future<void> ready = httpReady.get_future();
+
+ push_event(ex_event(ev_id));
+
+ ready.wait();
+ pHttpString = nullptr;
+}
diff --git a/xmrstak/misc/executor.hpp b/xmrstak/misc/executor.hpp
new file mode 100644
index 0000000..a3a0828
--- /dev/null
+++ b/xmrstak/misc/executor.hpp
@@ -0,0 +1,186 @@
+#pragma once
+#include "thdq.hpp"
+#include "msgstruct.h"
+#include <atomic>
+#include <array>
+#include <list>
+#include <future>
+#include "telemetry.h"
+#include "backend/IBackend.hpp"
+#include "Environment.hpp"
+
+class jpsock;
+
+
+namespace xmrstak
+{
+namespace cpu
+{
+class minethd;
+
+} // namespace cpu
+} // namepsace xmrstak
+
+class executor
+{
+public:
+ static executor* inst()
+ {
+ auto& env = xmrstak::Environment::inst();
+ if(env.pExecutor == nullptr)
+ env.pExecutor = new executor;
+ return env.pExecutor;
+ };
+
+ void ex_start(bool daemon) { daemon ? ex_main() : std::thread(&executor::ex_main, this).detach(); }
+
+ void get_http_report(ex_event_name ev_id, std::string& data);
+
+ inline void push_event(ex_event&& ev) { oEventQ.push(std::move(ev)); }
+ void push_timed_event(ex_event&& ev, size_t sec);
+ void log_result_error(std::string&& sError);
+
+ constexpr static size_t invalid_pool_id = 0;
+ constexpr static size_t dev_pool_id = 1;
+ constexpr static size_t usr_pool_id = 2;
+
+private:
+ struct timed_event
+ {
+ ex_event event;
+ size_t ticks_left;
+
+ timed_event(ex_event&& ev, size_t ticks) : event(std::move(ev)), ticks_left(ticks) {}
+ };
+
+ // In miliseconds, has to divide a second (1000ms) into an integer number
+ constexpr static size_t iTickTime = 500;
+
+ // Dev donation time period in seconds. 100 minutes by default.
+ // We will divide up this period according to the config setting
+ constexpr static size_t iDevDonatePeriod = 100 * 60;
+
+ std::list<timed_event> lTimedEvents;
+ std::mutex timed_event_mutex;
+ thdq<ex_event> oEventQ;
+
+ xmrstak::telemetry* telem;
+ std::vector<xmrstak::IBackend*>* pvThreads;
+
+ size_t current_pool_id;
+
+ jpsock* usr_pool;
+ jpsock* dev_pool;
+
+ jpsock* pick_pool_by_id(size_t pool_id);
+
+ bool is_dev_time;
+
+ executor();
+
+ void ex_main();
+
+ void ex_clock_thd();
+ void pool_connect(jpsock* pool);
+
+ void hashrate_report(std::string& out);
+ void result_report(std::string& out);
+ void connection_report(std::string& out);
+
+ void http_hashrate_report(std::string& out);
+ void http_result_report(std::string& out);
+ void http_connection_report(std::string& out);
+ void http_json_report(std::string& out);
+
+ void http_report(ex_event_name ev);
+ void print_report(ex_event_name ev);
+
+ std::string* pHttpString = nullptr;
+ std::promise<void> httpReady;
+ std::mutex httpMutex;
+
+ size_t iReconnectAttempts = 0;
+
+ struct sck_error_log
+ {
+ std::chrono::system_clock::time_point time;
+ std::string msg;
+
+ sck_error_log(std::string&& err) : msg(std::move(err))
+ {
+ time = std::chrono::system_clock::now();
+ }
+ };
+ std::vector<sck_error_log> vSocketLog;
+
+ // Element zero is always the success element.
+ // Keep in mind that this is a tally and not a log like above
+ struct result_tally
+ {
+ std::chrono::system_clock::time_point time;
+ std::string msg;
+ size_t count;
+
+ result_tally() : msg("[OK]"), count(0)
+ {
+ time = std::chrono::system_clock::now();
+ }
+
+ result_tally(std::string&& err) : msg(std::move(err)), count(1)
+ {
+ time = std::chrono::system_clock::now();
+ }
+
+ void increment()
+ {
+ count++;
+ time = std::chrono::system_clock::now();
+ }
+
+ bool compare(std::string& err)
+ {
+ if(msg == err)
+ return true;
+ else
+ return false;
+ }
+ };
+ std::vector<result_tally> vMineResults;
+
+ //More result statistics
+ std::array<size_t, 10> iTopDiff { { } }; //Initialize to zero
+
+ std::chrono::system_clock::time_point tPoolConnTime;
+ size_t iPoolHashes = 0;
+ uint64_t iPoolDiff = 0;
+
+ // Set it to 16 bit so that we can just let it grow
+ // Maximum realistic growth rate - 5MB / month
+ std::vector<uint16_t> iPoolCallTimes;
+
+ //Those stats are reset if we disconnect
+ inline void reset_stats()
+ {
+ iPoolCallTimes.clear();
+ tPoolConnTime = std::chrono::system_clock::now();
+ iPoolHashes = 0;
+ iPoolDiff = 0;
+ }
+
+ double fHighestHps = 0.0;
+
+ void log_socket_error(std::string&& sError);
+ void log_result_ok(uint64_t iActualDiff);
+
+ void sched_reconnect();
+
+ void on_sock_ready(size_t pool_id);
+ void on_sock_error(size_t pool_id, std::string&& sError);
+ void on_pool_have_job(size_t pool_id, pool_job& oPoolJob);
+ void on_miner_result(size_t pool_id, job_result& oResult);
+ void on_reconnect(size_t pool_id);
+ void on_switch_pool(size_t pool_id);
+
+ inline size_t sec_to_ticks(size_t sec) { return sec * (1000 / iTickTime); }
+};
+
diff --git a/xmrstak/misc/jext.hpp b/xmrstak/misc/jext.hpp
new file mode 100644
index 0000000..dce73a0
--- /dev/null
+++ b/xmrstak/misc/jext.hpp
@@ -0,0 +1,13 @@
+#pragma once
+
+using namespace rapidjson;
+
+/* This macro brings rapidjson more in line with other libs */
+inline const Value* GetObjectMember(const Value& obj, const char* key)
+{
+ Value::ConstMemberIterator itr = obj.FindMember(key);
+ if (itr != obj.MemberEnd())
+ return &itr->value;
+ else
+ return nullptr;
+}
diff --git a/xmrstak/misc/telemetry.cpp b/xmrstak/misc/telemetry.cpp
new file mode 100644
index 0000000..fafccd5
--- /dev/null
+++ b/xmrstak/misc/telemetry.cpp
@@ -0,0 +1,107 @@
+/*
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GNU GPL version 3 section 7
+ *
+ * If you modify this Program, or any covered work, by linking or combining
+ * it with OpenSSL (or a modified version of that library), containing parts
+ * covered by the terms of OpenSSL License and SSLeay License, the licensors
+ * of this Program grant you additional permission to convey the resulting work.
+ *
+ */
+
+#include <cmath>
+#include <cstring>
+#include <chrono>
+#include "telemetry.h"
+
+namespace xmrstak
+{
+
+telemetry::telemetry(size_t iThd)
+{
+ ppHashCounts = new uint64_t*[iThd];
+ ppTimestamps = new uint64_t*[iThd];
+ iBucketTop = new uint32_t[iThd];
+
+ for (size_t i = 0; i < iThd; i++)
+ {
+ ppHashCounts[i] = new uint64_t[iBucketSize];
+ ppTimestamps[i] = new uint64_t[iBucketSize];
+ iBucketTop[i] = 0;
+ memset(ppHashCounts[i], 0, sizeof(uint64_t) * iBucketSize);
+ memset(ppTimestamps[i], 0, sizeof(uint64_t) * iBucketSize);
+ }
+}
+
+double telemetry::calc_telemetry_data(size_t iLastMilisec, size_t iThread)
+{
+ using namespace std::chrono;
+ uint64_t iTimeNow = time_point_cast<milliseconds>(high_resolution_clock::now()).time_since_epoch().count();
+
+ uint64_t iEarliestHashCnt = 0;
+ uint64_t iEarliestStamp = 0;
+ uint64_t iLastestStamp = 0;
+ uint64_t iLastestHashCnt = 0;
+ bool bHaveFullSet = false;
+
+ //Start at 1, buckettop points to next empty
+ for (size_t i = 1; i < iBucketSize; i++)
+ {
+ size_t idx = (iBucketTop[iThread] - i) & iBucketMask; //overflow expected here
+
+ if (ppTimestamps[iThread][idx] == 0)
+ break; //That means we don't have the data yet
+
+ if (iLastestStamp == 0)
+ {
+ iLastestStamp = ppTimestamps[iThread][idx];
+ iLastestHashCnt = ppHashCounts[iThread][idx];
+ }
+
+ if (iTimeNow - ppTimestamps[iThread][idx] > iLastMilisec)
+ {
+ bHaveFullSet = true;
+ break; //We are out of the requested time period
+ }
+
+ iEarliestStamp = ppTimestamps[iThread][idx];
+ iEarliestHashCnt = ppHashCounts[iThread][idx];
+ }
+
+ if (!bHaveFullSet || iEarliestStamp == 0 || iLastestStamp == 0)
+ return nan("");
+
+ //Don't think that can happen, but just in case
+ if (iLastestStamp - iEarliestStamp == 0)
+ return nan("");
+
+ double fHashes, fTime;
+ fHashes = iLastestHashCnt - iEarliestHashCnt;
+ fTime = iLastestStamp - iEarliestStamp;
+ fTime /= 1000.0;
+
+ return fHashes / fTime;
+}
+
+void telemetry::push_perf_value(size_t iThd, uint64_t iHashCount, uint64_t iTimestamp)
+{
+ size_t iTop = iBucketTop[iThd];
+ ppHashCounts[iThd][iTop] = iHashCount;
+ ppTimestamps[iThd][iTop] = iTimestamp;
+
+ iBucketTop[iThd] = (iTop + 1) & iBucketMask;
+}
+
+} // namepsace xmrstak
diff --git a/xmrstak/misc/telemetry.hpp b/xmrstak/misc/telemetry.hpp
new file mode 100644
index 0000000..0538090
--- /dev/null
+++ b/xmrstak/misc/telemetry.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <cstdint>
+
+namespace xmrstak
+{
+
+class telemetry
+{
+public:
+ telemetry(size_t iThd);
+ void push_perf_value(size_t iThd, uint64_t iHashCount, uint64_t iTimestamp);
+ double calc_telemetry_data(size_t iLastMilisec, size_t iThread);
+
+private:
+ constexpr static size_t iBucketSize = 2 << 11; //Power of 2 to simplify calculations
+ constexpr static size_t iBucketMask = iBucketSize - 1;
+ uint32_t* iBucketTop;
+ uint64_t** ppHashCounts;
+ uint64_t** ppTimestamps;
+};
+
+} // namepsace xmrstak
diff --git a/xmrstak/misc/thdq.hpp b/xmrstak/misc/thdq.hpp
new file mode 100644
index 0000000..248c807
--- /dev/null
+++ b/xmrstak/misc/thdq.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <queue>
+#include <thread>
+#include <mutex>
+#include <condition_variable>
+
+template <typename T>
+class thdq
+{
+public:
+ T pop()
+ {
+ std::unique_lock<std::mutex> mlock(mutex_);
+ while (queue_.empty()) { cond_.wait(mlock); }
+ auto item = std::move(queue_.front());
+ queue_.pop();
+ return item;
+ }
+
+ void pop(T& item)
+ {
+ std::unique_lock<std::mutex> mlock(mutex_);
+ while (queue_.empty()) { cond_.wait(mlock); }
+ item = queue_.front();
+ queue_.pop();
+ }
+
+ void push(const T& item)
+ {
+ std::unique_lock<std::mutex> mlock(mutex_);
+ queue_.push(item);
+ mlock.unlock();
+ cond_.notify_one();
+ }
+
+ void push(T&& item)
+ {
+ std::unique_lock<std::mutex> mlock(mutex_);
+ queue_.push(std::move(item));
+ mlock.unlock();
+ cond_.notify_one();
+ }
+
+private:
+ std::queue<T> queue_;
+ std::mutex mutex_;
+ std::condition_variable cond_;
+};
OpenPOWER on IntegriCloud