Commit 4648330c9d6e17a197b0997c0868b384f08d1d77

Luke Dashjr 2012-04-24T21:46:48

Support for share logging

diff --git a/README b/README
index 519cb81..9ae0596 100644
--- a/README
+++ b/README
@@ -156,6 +156,7 @@ Options for both config file and command line:
 --scan-time|-s <arg> Upper bound on time spent scanning current work, in seconds (default: 60)
 --sched-start <arg> Set a time of day in HH:MM to start mining (a once off without a stop time)
 --sched-stop <arg>  Set a time of day in HH:MM to stop mining (will quit without a start time)
+--sharelog <arg>    Append share log to file
 --shares <arg>      Quit after mining N shares (default: unlimited)
 --socks-proxy <arg> Set socks4 proxy (host:port)
 --submit-stale      Submit shares even if they would normally be considered stale
@@ -415,10 +416,32 @@ if you use
 ./cgminer -o xxx -u yyy -p zzz 2>logfile.txt
 it will log to a file called logfile.txt and otherwise work the same.
 
-
 There is also the -m option on linux which will spawn a command of your choice
 and pipe the output directly to that command.
 
+
+If you start cgminer with the --sharelog option, you can get detailed
+information for each share found. The argument to the option may be "-" for
+standard output (not advisable with the ncurses UI), any valid positive number
+for that file descriptor, or a filename.
+
+To log share data to a file named "share.log", you can use either:
+./cgminer --sharelog 50 -o xxx -u yyy -p zzz 50>share.log
+./cgminer --sharelog share.log -o xxx -u yyy -p zzz
+
+For every share found, data will be logged in a CSV (Comma Separated Value)
+format:
+    timestamp,disposition,target,pool,dev,thr,sharehash,sharedata
+For example (this is wrapped, but it's all on one line for real):
+    1335313090,reject,
+    ffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000,
+    http://localhost:8337,GPU0,0,
+    6f983c918f3299b58febf95ec4d0c7094ed634bc13754553ec34fc3800000000,
+    00000001a0980aff4ce4a96d53f4b89a2d5f0e765c978640fe24372a000001c5
+    000000004a4366808f81d44f26df3d69d7dc4b3473385930462d9ab707b50498
+    f681634a4f1f63d01a0cd43fb338000000000080000000000000000000000000
+    0000000000000000000000000000000000000000000000000000000080020000
+
 ---
 OVERCLOCKING WARNING AND INFORMATION
 
diff --git a/cgminer.c b/cgminer.c
index 0cca83e..341dff8 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -323,6 +323,61 @@ static void applog_and_exit(const char *fmt, ...)
 	exit(1);
 }
 
+static pthread_mutex_t sharelog_lock;
+FILE *sharelog_file = NULL;
+
+static void
+sharelog(const char*disposition, const struct work*work) {
+	if (!sharelog_file)
+		return;
+
+	int thr_id = work->thr_id;
+	struct cgpu_info *cgpu = thr_info[thr_id].cgpu;
+	struct pool *pool = work->pool;
+	unsigned long int t = (unsigned long int)work->share_found_time;
+	char *target = bin2hex(work->target, sizeof(work->target));
+	if (unlikely(!target)) {
+		applog(LOG_ERR, "sharelog target OOM");
+		return;
+	}
+	char *hash = bin2hex(work->hash, sizeof(work->hash));
+	if (unlikely(!hash)) {
+		free(target);
+		applog(LOG_ERR, "sharelog hash OOM");
+		return;
+	}
+	char *data = bin2hex(work->data, sizeof(work->data));
+	if (unlikely(!data)) {
+		free(target);
+		free(hash);
+		applog(LOG_ERR, "sharelog data OOM");
+		return;
+	}
+
+	// timestamp,disposition,target,pool,dev,thr,sharehash,sharedata
+	char s[1024];
+	int rv;
+	rv = snprintf(s, sizeof(s), "%lu,%s,%s,%s,%s%u,%u,%s,%s\n", t, disposition, target, pool->rpc_url, cgpu->api->name, cgpu->device_id, thr_id, hash, data);
+	free(target);
+	free(hash);
+	free(data);
+	if (rv >= sizeof(s))
+		s[sizeof(s) - 1] = '\0';
+	else
+	if (rv < 0) {
+		applog(LOG_ERR, "sharelog printf error");
+		return;
+	}
+
+	size_t ret;
+	mutex_lock(&sharelog_lock);
+	ret = fwrite(s, rv, 1, sharelog_file);
+	fflush(sharelog_file);
+	mutex_unlock(&sharelog_lock);
+	if (1 != ret)
+		applog(LOG_ERR, "sharelog fwrite error");
+}
+
 static void add_pool(void)
 {
 	struct pool *pool;
@@ -552,6 +607,28 @@ static char *set_schedtime(const char *arg, struct schedtime *st)
 	return NULL;
 }
 
+static char*
+set_sharelog(char *arg) {
+	char *r = "";
+	long int i = strtol(arg, &r, 10);
+
+	if ((!*r) && i >= 0 && i <= INT_MAX) {
+		sharelog_file = fdopen((int)i, "a");
+		if (!sharelog_file)
+			applog(LOG_ERR, "Failed to open fd %u for share log", (unsigned int)i);
+	} else if (!strcmp(arg, "-")) {
+		sharelog_file = stdout;
+		if (!sharelog_file)
+			applog(LOG_ERR, "Standard output missing for share log");
+	} else {
+		sharelog_file = fopen(arg, "a");
+		if (!sharelog_file)
+			applog(LOG_ERR, "Failed to open %s for share log", arg);
+	}
+
+	return NULL;
+}
+
 static char *temp_cutoff_str = NULL;
 
 char *set_temp_cutoff(char *arg)
@@ -823,6 +900,9 @@ static struct opt_table opt_config_table[] = {
 	OPT_WITH_ARG("--sched-stop",
 		     set_schedtime, NULL, &schedstop,
 		     "Set a time of day in HH:MM to stop mining (will quit without a start time)"),
+	OPT_WITH_ARG("--sharelog",
+		     set_sharelog, NULL, NULL,
+		     "Append share log to file"),
 	OPT_WITH_ARG("--shares",
 		     opt_set_intval, NULL, &opt_shares,
 		     "Quit after mining N shares (default: unlimited)"),
@@ -1606,6 +1686,7 @@ static bool submit_upstream_work(const struct work *work)
 				applog(LOG_NOTICE, "Accepted %s %s %d thread %d",
 				       hashshow, cgpu->api->name, cgpu->device_id, thr_id);
 		}
+		sharelog("accept", work);
 		if (opt_shares && total_accepted >= opt_shares) {
 			applog(LOG_WARNING, "Successfully mined %d accepted shares as requested and exiting.", opt_shares);
 			kill_work();
@@ -1618,6 +1699,7 @@ static bool submit_upstream_work(const struct work *work)
 		applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)");
 		if (!QUIET) {
 			char where[17];
+			char disposition[36] = "reject";
 			char reason[32];
 
 			if (total_pools > 1)
@@ -1635,11 +1717,14 @@ static bool submit_upstream_work(const struct work *work)
 				reason[0] = ' '; reason[1] = '(';
 				memcpy(2 + reason, reasontmp, reasonLen);
 				reason[reasonLen + 2] = ')'; reason[reasonLen + 3] = '\0';
+				memcpy(disposition + 7, reasontmp, reasonLen);
+				disposition[6] = ':'; disposition[reasonLen + 7] = '\0';
 			} else
 				strcpy(reason, "");
 
 			applog(LOG_NOTICE, "Rejected %s %s %d thread %d%s%s",
 			       hashshow, cgpu->api->name, cgpu->device_id, thr_id, where, reason);
+			sharelog(disposition, work);
 		}
 	}
 
@@ -2011,6 +2096,7 @@ static void *submit_work_thread(void *userdata)
 			applog(LOG_NOTICE, "Stale share detected, submitting as pool requested");
 		else {
 			applog(LOG_NOTICE, "Stale share detected, discarding");
+			sharelog("discard", work);
 			total_stale++;
 			pool->stale_shares++;
 			goto out;
@@ -3484,6 +3570,7 @@ bool submit_work_sync(struct thr_info *thr, const struct work *work_in)
 	wc->cmd = WC_SUBMIT_WORK;
 	wc->thr = thr;
 	memcpy(wc->u.work, work_in, sizeof(*work_in));
+	wc->u.work->share_found_time = time(NULL);
 
 	applog(LOG_DEBUG, "Pushing submit work to work thread");
 
@@ -4515,6 +4602,7 @@ int main(int argc, char *argv[])
 	mutex_init(&curses_lock);
 #endif
 	mutex_init(&control_lock);
+	mutex_init(&sharelog_lock);
 	rwlock_init(&blk_lock);
 	rwlock_init(&netacc_lock);
 
diff --git a/miner.h b/miner.h
index 81d535e..8669e81 100644
--- a/miner.h
+++ b/miner.h
@@ -655,6 +655,8 @@ struct work {
 	unsigned int	work_block;
 	int		id;
 	UT_hash_handle hh;
+
+	time_t share_found_time;
 };
 
 extern void get_datestamp(char *, struct timeval *);