diff --git a/Makefile.am b/Makefile.am index c57383fd..5fd9ebd8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ cgminer_SOURCES = elist.h miner.h compat.h \ sha256_cryptopp.c sha256_sse2_amd64.c cgminer_LDFLAGS = $(PTHREAD_FLAGS) -cgminer_LDADD = @LIBCURL@ @JANSSON_LIBS@ @PTHREAD_LIBS@ @OPENCL_LIBS@ lib/libgnu.a ccan/libccan.a +cgminer_LDADD = @LIBCURL@ @JANSSON_LIBS@ @PTHREAD_LIBS@ @OPENCL_LIBS@ @NCURSES_LIBS@ lib/libgnu.a ccan/libccan.a cgminer_CPPFLAGS = @LIBCURL_CPPFLAGS@ -I$(top_builddir)/lib -I$(top_srcdir)/lib if HAVE_x86_64 diff --git a/README b/README index 70eb6123..38273d6a 100644 --- a/README +++ b/README @@ -7,6 +7,7 @@ Dependencies: libcurl http://curl.haxx.se/libcurl/ jansson http://www.digip.org/jansson/ (jansson is included in-tree and an installed one may conflict) + libncurses5-dev (or libpdcurses on WIN32) Basic *nix build instructions: To build with GPU mining support: diff --git a/configure.ac b/configure.ac index aea69bde..04d93699 100644 --- a/configure.ac +++ b/configure.ac @@ -60,6 +60,7 @@ LIBS=$SAVED_LIBS AC_CHECK_LIB(jansson, json_loads, request_jansson=false, request_jansson=true) AC_CHECK_LIB(pthread, pthread_create, PTHREAD_LIBS=-lpthread) +AC_CHECK_LIB(ncurses, addstr, NCURSES_LIBS=-lncurses) AM_CONDITIONAL([WANT_JANSSON], [test x$request_jansson = xtrue]) AM_CONDITIONAL([HAVE_WINDOWS], [test x$have_win32 = xtrue]) @@ -148,6 +149,7 @@ AC_SUBST(OPENCL_LIBS) AC_SUBST(JANSSON_LIBS) AC_SUBST(PTHREAD_FLAGS) AC_SUBST(PTHREAD_LIBS) +AC_SUBST(NCURSES_LIBS) AC_CONFIG_FILES([ Makefile diff --git a/main.c b/main.c index b260a85a..b4c64ac1 100644 --- a/main.c +++ b/main.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "compat.h" #include "miner.h" #include "findnonce.h" @@ -507,13 +508,108 @@ err_out: return false; } -static double total_secs; +static inline int gpu_from_thr_id(int thr_id) +{ + return thr_id % nDevs; +} + +static inline int cpu_from_thr_id(int thr_id) +{ + return (thr_id - gpu_threads) % num_processors; +} + +static WINDOW * mainwin; +static double total_secs = 0.1; static char statusline[256]; +static int cpucursor, gpucursor, logstart, logcursor; +static bool curses_active = false; +static struct cgpu_info *gpus, *cpus; -static inline void print_status(void) +static inline void print_status(int thr_id) { - printf("%s\r", statusline); - fflush(stdout); + int x; + + if (unlikely(!curses_active)) + return; + getyx(mainwin, logcursor, x); + + move(2,0); + printw("Totals: %s", statusline); + clrtoeol(); + + if (thr_id && thr_id < gpu_threads) { + int gpu = gpu_from_thr_id(thr_id); + struct cgpu_info *cgpu = &gpus[gpu]; + + move(gpucursor + gpu, 0); + printw("GPU %d: [%.1f Mh/s] [Q:%d A:%d R:%d HW:%d E:%.0f%% U:%.2f/m] ", + gpu, cgpu->total_mhashes / total_secs, + cgpu->getworks, cgpu->accepted, cgpu->rejected, cgpu->hw_errors, + cgpu->efficiency, cgpu->utility); + clrtoeol(); + } else if (thr_id && thr_id >= gpu_threads) { + int cpu = cpu_from_thr_id(thr_id); + struct cgpu_info *cgpu = &cpus[cpu]; + + move(cpucursor + cpu, 0); + printw("CPU %d: [%.1f Mh/s] [Q:%d A:%d R:%d HW:%d E:%.0f%% U:%.2f/m]", + cpu, cgpu->total_mhashes / total_secs, + cgpu->getworks, cgpu->accepted, cgpu->rejected, cgpu->hw_errors, + cgpu->efficiency, cgpu->utility); + clrtoeol(); + } + + move(logcursor, 0); + refresh(); +} + +static void refresh_display(void) +{ + int i, x, maxy; + + if (unlikely(!curses_active)) + return; + getyx(mainwin, logcursor, x); + + move(0,0); + attron(A_BOLD); + printw(PROGRAM_NAME " version " VERSION); + attroff(A_BOLD); + clrtoeol(); + move(1, 0); + clrtoeol(); + hline('-', 80); + move(3, 0); + clrtoeol(); + hline('-', 80); + move(logstart, 0); + clrtoeol(); + hline('-', 80); + move(logcursor, 0); + + for (i = 0; i < mining_threads; i++) + print_status(i); + + move(logcursor, 0); + redrawwin(mainwin); +} + +void log_curses(const char *f, va_list ap) +{ + int i, x, maxy; + + if (unlikely(!curses_active)) + return; + vwprintw(mainwin, f, ap); + clrtoeol(); + getyx(mainwin, logcursor, x); + + /* Scroll log output downwards */ + getmaxyx(mainwin, maxy, x); + if (logcursor >= maxy - 1) + refresh_display(); + else + refresh(); } static bool submit_fail = false; @@ -526,7 +622,6 @@ static bool submit_upstream_work(const struct work *work) bool rc = false; struct cgpu_info *cgpu = thr_info[work->thr_id].cgpu; CURL *curl = curl_easy_init(); - double utility, efficiency; if (unlikely(!curl)) { applog(LOG_ERR, "CURL initialisation failed"); @@ -573,29 +668,26 @@ static bool submit_upstream_work(const struct work *work) if (opt_debug) applog(LOG_DEBUG, "PROOF OF WORK RESULT: true (yay!!!)"); if (!opt_quiet) - printf("[Accepted] "); + applog(LOG_WARNING, "Share accepted from %sPU %d", + cgpu->is_gpu? "G" : "C", cgpu->cpu_gpu); } else { cgpu->rejected++; rejected++; if (opt_debug) applog(LOG_DEBUG, "PROOF OF WORK RESULT: false (booooo)"); if (!opt_quiet) - printf("[Rejected] "); + applog(LOG_WARNING, "Share rejected from %sPU %d", + cgpu->is_gpu? "G" : "C", cgpu->cpu_gpu); } - utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; - efficiency = cgpu->getworks ? cgpu->accepted * 100.0 / cgpu->getworks : 0.0; + cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + cgpu->efficiency = cgpu->getworks ? cgpu->accepted * 100.0 / cgpu->getworks : 0.0; - if (!opt_quiet) { - printf("[%sPU %d] [%.1f Mh/s] [Q:%d A:%d R:%d HW:%d E:%.0f%% U:%.2f/m] \n", - cgpu->is_gpu? "G" : "C", cgpu->cpu_gpu, cgpu->total_mhashes / total_secs, - cgpu->getworks, cgpu->accepted, cgpu->rejected, cgpu->hw_errors, - efficiency, utility); - print_status(); - } + if (!opt_quiet) + print_status(work->thr_id); applog(LOG_INFO, "%sPU %d Requested:%d Accepted:%d Rejected:%d HW errors:%d Efficiency:%.0f%% Utility:%.2f/m", - cgpu->is_gpu? "G" : "C", cgpu->cpu_gpu, cgpu->getworks, cgpu->accepted, cgpu->rejected, cgpu->hw_errors, efficiency, utility - ); + cgpu->is_gpu? "G" : "C", cgpu->cpu_gpu, cgpu->getworks, cgpu->accepted, + cgpu->rejected, cgpu->hw_errors, cgpu->efficiency, cgpu->utility); json_decref(val); @@ -960,7 +1052,7 @@ static void hashmeter(int thr_id, struct timeval *diff, sprintf(statusline, "[(%ds):%.1f (avg):%.1f Mh/s] [Q:%d A:%d R:%d HW:%d E:%.0f%% U:%.2f/m] ", opt_log_interval, rolling_local / local_secs, total_mhashes_done / total_secs, getwork_requested, accepted, rejected, hw_errors, efficiency, utility); - print_status(); + print_status(thr_id); applog(LOG_INFO, "[Rate (%ds):%.1f (avg):%.2f Mhash/s] [Requested:%d Accepted:%d Rejected:%d HW errors:%d Efficiency:%.0f%% Utility:%.2f/m]", opt_log_interval, rolling_local / local_secs, total_mhashes_done / total_secs, getwork_requested, accepted, rejected, hw_errors, efficiency, utility); @@ -1183,11 +1275,6 @@ bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce) return submit_work_sync(thr, work); } -static inline int cpu_from_thr_id(int thr_id) -{ - return (thr_id - gpu_threads) % num_processors; -} - static void *miner_thread(void *userdata) { struct thr_info *mythr = userdata; @@ -1404,11 +1491,6 @@ static inline cl_int queue_kernel_parameters(_clState *clState, dev_blk_ctx *blk return status; } -static inline int gpu_from_thr_id(int thr_id) -{ - return thr_id % nDevs; -} - static void *gpuminer_thread(void *userdata) { const unsigned long cycle = opt_log_interval / 5 ? : 1; @@ -1661,7 +1743,6 @@ int main (int argc, char *argv[]) struct thr_info *thr; unsigned int i, j = 0; char name[32]; - struct cgpu_info *gpus = NULL, *cpus = NULL; if (unlikely(pthread_mutex_init(&time_lock, NULL))) return 1; @@ -1726,7 +1807,12 @@ int main (int argc, char *argv[]) opt_n_threads = num_processors; } + logcursor = 4; mining_threads = opt_n_threads + gpu_threads; + gpucursor = logcursor; + cpucursor = gpucursor + total_devices + 1; + logstart = cpucursor + (opt_n_threads ? num_processors : 0); + logcursor = logstart + 1; if (!rpc_userpass) { if (!rpc_user || !rpc_pass) { @@ -1907,6 +1993,17 @@ int main (int argc, char *argv[]) total_mhashes_done = 0; pthread_mutex_unlock(&hash_lock); + /* Set up the ncurses interface */ + if ((mainwin = initscr()) == NULL) { + applog(LOG_ERR, "Failed to initscr"); + return 1; + } + idlok(mainwin, true); + scrollok(mainwin, true); + curses_active = true; + move(logcursor, 0); + refresh_display(); + /* main loop - simply wait for workio thread to exit */ pthread_join(thr_info[work_thr_id].pth, NULL); curl_global_cleanup(); @@ -1917,6 +2014,10 @@ int main (int argc, char *argv[]) applog(LOG_INFO, "workio thread dead, exiting."); + delwin(mainwin); + endwin(); + refresh(); + return 0; } diff --git a/miner.h b/miner.h index a83553ff..1d4e1b21 100644 --- a/miner.h +++ b/miner.h @@ -135,6 +135,8 @@ struct cgpu_info { double local_mhashes; double total_mhashes; unsigned int getworks; + double efficiency; + double utility; }; struct thr_info { @@ -267,6 +269,7 @@ struct work { bool submit_nonce(struct thr_info *thr, struct work *work, uint32_t nonce); +extern void log_curses(const char *f, va_list ap); extern void vapplog(int prio, const char *fmt, va_list ap); extern void applog(int prio, const char *fmt, ...); extern struct thread_q *tq_new(void); diff --git a/util.c b/util.c index 04f23b80..90777545 100644 --- a/util.c +++ b/util.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "miner.h" #include "elist.h" @@ -68,7 +69,7 @@ void vapplog(int prio, const char *fmt, va_list ap) #endif else if (opt_log_output || prio == LOG_WARNING || prio == LOG_ERR) { char *f; - int len, i, extra = 0; + int len; struct timeval tv = { }; struct tm tm, *tm_p; @@ -80,10 +81,8 @@ void vapplog(int prio, const char *fmt, va_list ap) pthread_mutex_unlock(&time_lock); len = 40 + strlen(fmt) + 2; - if (len < 80) - extra = 80 - len; - f = alloca(len + extra); - sprintf(f, "[%d-%02d-%02d %02d:%02d:%02d] %s", + f = alloca(len); + sprintf(f, "[%d-%02d-%02d %02d:%02d:%02d] %s\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, @@ -91,11 +90,12 @@ void vapplog(int prio, const char *fmt, va_list ap) tm.tm_min, tm.tm_sec, fmt); - vfprintf(stderr, f, ap); /* atomic write to stderr */ - for (i = 0; i < extra; i++) - fprintf(stderr, " "); - fprintf(stderr, "\n"); - fflush(stderr); + /* Only output to stderr if it's not going to the screen as well */ + if (opt_log_output && !isatty(fileno((FILE *)stderr))) { + vfprintf(stderr, f, ap); /* atomic write to stderr */ + fflush(stderr); + } + log_curses(f, ap); } }