Commit 976e27c4cb4f454edb948311ae11df79a6c26056

Kano 2012-05-05T15:04:15

API add getwork stats to cgminer - accesable from API 'stats'

diff --git a/README b/README
index 4769e9d..24b1408 100644
--- a/README
+++ b/README
@@ -801,6 +801,13 @@ The list of requests - a (*) means it requires privileged access - and replies a
  restart (*)   none           There is no status section but just a single "RESTART"
                               reply before cgminer restarts
 
+ stats         STATS          Each device or pool that has 1 or more getworks
+                              with a list of stats regarding getwork times
+                              The values returned by this may change in future
+                              versions thus would not normally be displayed
+                              except when trying to determine performance and
+                              issues with pool perforamce
+
 When you enable, disable or restart a GPU or PGA, you will also get Thread messages
 in the cgminer status window
 
diff --git a/api.c b/api.c
index 6c88437..ea3eeb3 100644
--- a/api.c
+++ b/api.c
@@ -232,6 +232,7 @@ static const char *OSINFO =
 #define _DEVDETAILS	"DEVDETAILS"
 #define _BYE		"BYE"
 #define _RESTART	"RESTART"
+#define _MINESTATS	"STATS"
 
 static const char ISJSON = '{';
 #define JSON0		"{"
@@ -265,6 +266,7 @@ static const char ISJSON = '{';
 #define JSON_BYE	JSON1 _BYE JSON1
 #define JSON_RESTART	JSON1 _RESTART JSON1
 #define JSON_CLOSE	JSON3
+#define JSON_MINESTATS	JSON1 _MINESTATS JSON2
 #define JSON_END	JSON4
 
 static const char *JSON_COMMAND = "command";
@@ -351,6 +353,7 @@ static const char *JSON_PARAMETER = "parameter";
 #define MSG_ACTPOOL 67
 #define MSG_REMPOOL 68
 #define MSG_DEVDETAILS 69
+#define MSG_MINESTATS 70
 
 enum code_severity {
 	SEVERITY_ERR,
@@ -476,6 +479,7 @@ struct CODES {
  { SEVERITY_SUCC,  MSG_REMPOOL, PARAM_BOTH,	"Removed pool %d:'%s'" },
  { SEVERITY_SUCC,  MSG_NOTIFY,	PARAM_NONE,	"Notify" },
  { SEVERITY_SUCC,  MSG_DEVDETAILS,PARAM_NONE,	"Device Details" },
+ { SEVERITY_SUCC,  MSG_MINESTATS,PARAM_NONE,	"CGMiner stats" },
  { SEVERITY_FAIL, 0, 0, NULL }
 };
 
@@ -2000,6 +2004,61 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
 	ptr = NULL;
 }
 
+static int itemstats(int i, char *id, struct cgminer_stats *stats, bool isjson)
+{
+	char buf[BUFSIZ];
+
+	if (stats->getwork_calls)
+	{
+		sprintf(buf, isjson
+			? "%s{\"STATS\":%d,\"ID\":\"%s\",\"Elapsed\":%.0f,\"Calls\":%d,\"Wait\":%ld.%06ld,\"Max\":%ld.%06ld,\"Min\":%ld.%06ld}"
+			: "%sSTATS=%d,ID=%s,Elapsed=%.0f,Calls=%d,Wait=%ld.%06ld,Max=%ld.%06ld,Min=%ld.%06ld" SEPSTR,
+			(isjson && (i > 0)) ? COMMA : BLANK,
+			i, id, total_secs, stats->getwork_calls,
+			stats->getwork_wait.tv_sec, stats->getwork_wait.tv_usec,
+			stats->getwork_wait_max.tv_sec, stats->getwork_wait_max.tv_usec,
+			stats->getwork_wait_min.tv_sec, stats->getwork_wait_min.tv_usec);
+
+		strcat(io_buffer, buf);
+
+		i++;
+	}
+
+	return i;
+}
+static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
+{
+	char buf[BUFSIZ];
+	int i, j;
+
+	strcpy(io_buffer, message(MSG_MINESTATS, 0, NULL, isjson));
+
+	if (isjson) {
+		strcat(io_buffer, COMMA);
+		strcat(io_buffer, JSON_MINESTATS);
+	}
+
+	i = 0;
+	for (j = 0; j < total_devices; j++) {
+		struct cgpu_info *cgpu = devices[j];
+
+		if (cgpu && cgpu->api) {
+			sprintf(buf, "%s%d", cgpu->api->name, cgpu->device_id);
+			i = itemstats(i, buf, &(cgpu->cgminer_stats), isjson);
+		}
+	}
+
+	for (j = 0; j < total_pools; j++) {
+		struct pool *pool = pools[j];
+
+		sprintf(buf, "POOL%d", j);
+		i = itemstats(i, buf, &(pool->cgminer_stats), isjson);
+	}
+
+	if (isjson)
+		strcat(io_buffer, JSON_CLOSE);
+}
+
 struct CMDS {
 	char *name;
 	void (*func)(SOCKETTYPE, char *, bool);
@@ -2041,6 +2100,7 @@ struct CMDS {
 	{ "notify",		notify,		false },
 	{ "devdetails",		devdetails,	false },
 	{ "restart",		dorestart,	true },
+	{ "stats",		minerstats,	false },
 	{ NULL,			NULL,		false }
 };
 
diff --git a/cgminer.c b/cgminer.c
index 60eb842..d5999f2 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -3717,6 +3717,9 @@ void *miner_thread(void *userdata)
 	const int thr_id = mythr->id;
 	struct cgpu_info *cgpu = mythr->cgpu;
 	struct device_api *api = cgpu->api;
+	struct cgminer_stats *dev_stats = &(cgpu->cgminer_stats);
+	struct cgminer_stats *pool_stats;
+	struct timeval getwork_start;
 
 	/* Try to cycle approximately 5 times before each log update */
 	const unsigned long def_cycle = opt_log_interval / 5 ? : 1;
@@ -3732,6 +3735,8 @@ void *miner_thread(void *userdata)
 	bool requested = false;
 	pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 
+	gettimeofday(&getwork_start, NULL);
+
 	if (api->thread_init && !api->thread_init(mythr)) {
 		cgpu->device_last_not_well = time(NULL);
 		cgpu->device_not_well_reason = REASON_THREAD_FAIL_INIT;
@@ -3770,7 +3775,40 @@ void *miner_thread(void *userdata)
 		do {
 			gettimeofday(&tv_start, NULL);
 
+			timersub(&tv_start, &getwork_start, &getwork_start);
+
+			timeradd(&getwork_start,
+				&(dev_stats->getwork_wait),
+				&(dev_stats->getwork_wait));
+			if (timercmp(&getwork_start, &(dev_stats->getwork_wait_max), >)) {
+				dev_stats->getwork_wait_max.tv_sec = getwork_start.tv_sec;
+				dev_stats->getwork_wait_max.tv_usec = getwork_start.tv_usec;
+			}
+			if (timercmp(&getwork_start, &(dev_stats->getwork_wait_min), <)) {
+				dev_stats->getwork_wait_min.tv_sec = getwork_start.tv_sec;
+				dev_stats->getwork_wait_min.tv_usec = getwork_start.tv_usec;
+			}
+			dev_stats->getwork_calls++;
+
+			pool_stats = &(work->pool->cgminer_stats);
+
+			timeradd(&getwork_start,
+				&(pool_stats->getwork_wait),
+				&(pool_stats->getwork_wait));
+			if (timercmp(&getwork_start, &(pool_stats->getwork_wait_max), >)) {
+				pool_stats->getwork_wait_max.tv_sec = getwork_start.tv_sec;
+				pool_stats->getwork_wait_max.tv_usec = getwork_start.tv_usec;
+			}
+			if (timercmp(&getwork_start, &(pool_stats->getwork_wait_min), <)) {
+				pool_stats->getwork_wait_min.tv_sec = getwork_start.tv_sec;
+				pool_stats->getwork_wait_min.tv_usec = getwork_start.tv_usec;
+			}
+			pool_stats->getwork_calls++;
+
 			hashes = api->scanhash(mythr, work, work->blk.nonce + max_nonce);
+
+			gettimeofday(&getwork_start, NULL);
+
 			if (unlikely(work_restart[thr_id].restart)) {
 
 				/* Apart from device_thread 0, we stagger the
@@ -4899,6 +4937,9 @@ int main(int argc, char *argv[])
 
 	load_temp_cutoffs();
 
+	for (i = 0; i < total_devices; ++i)
+		devices[i]->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+
 	logstart += total_devices;
 	logcursor = logstart + 1;
 
@@ -4917,6 +4958,8 @@ int main(int argc, char *argv[])
 	for (i = 0; i < total_pools; i++) {
 		struct pool *pool = pools[i];
 
+		pool->cgminer_stats.getwork_wait_min.tv_sec = MIN_SEC_UNSET;
+
 		if (!pool->rpc_userpass) {
 			if (!pool->rpc_user || !pool->rpc_pass)
 				quit(1, "No login credentials supplied for pool %u %s", i, pool->rpc_url);
diff --git a/miner.h b/miner.h
index a965ecc..4c58bfd 100644
--- a/miner.h
+++ b/miner.h
@@ -252,6 +252,15 @@ enum dev_reason {
 #define REASON_DEV_THERMAL_CUTOFF_STR	"Device reached thermal cutoff"
 #define REASON_UNKNOWN_STR		"Unknown reason - code bug"
 
+#define MIN_SEC_UNSET 99999999
+
+struct cgminer_stats {
+	uint32_t getwork_calls;
+	struct timeval getwork_wait;
+	struct timeval getwork_wait_max;
+	struct timeval getwork_wait_min;
+};
+
 struct cgpu_info {
 	int cgminer_id;
 	struct device_api *api;
@@ -324,6 +333,8 @@ struct cgpu_info {
 	int dev_nostart_count;
 	int dev_over_heat_count;	// It's a warning but worth knowing
 	int dev_thermal_cutoff_count;
+
+	struct cgminer_stats cgminer_stats;
 };
 
 extern bool add_cgpu(struct cgpu_info*);
@@ -653,6 +664,8 @@ struct pool {
 	struct list_head curlring;
 
 	time_t last_share_time;
+
+	struct cgminer_stats cgminer_stats;
 };
 
 struct work {