citrun

watch C/C++ source code execute
Log | Files | Refs | LICENSE

term.cc (5404B)


      1 //
      2 // Original idea from Max Zunti.
      3 //
      4 #include <cassert>
      5 #include <csignal>	// sigaction
      6 #include <cstdlib>	// exit
      7 #include <err.h>	// errx
      8 #include <iostream>
      9 #include <ncurses.h>
     10 #include <queue>
     11 #include <time.h>	// clock_gettime, nanosleep
     12 
     13 #include "process_dir.h" // ProcessDir
     14 #include "shm.h"
     15 
     16 class CursesViewer {
     17 public:
     18 	CursesViewer(ProcessDir &pdir);
     19 	void loop();
     20 
     21 private:
     22 	int get_keyboard();
     23 	void draw();
     24 	void print_statusbar();
     25 	void update_execs();
     26 	void update_sleep();
     27 
     28 	ProcessDir	 m_pdir;
     29 	ProcessFile	*m_cur_pfile;
     30 	std::queue<uint64_t> m_execution_history;
     31 	std::queue<struct timespec> m_frame_deltas;
     32 	TranslationUnit	 m_cur_tu;
     33 	struct timespec	 m_floating_avg;
     34 	struct timespec	 m_last_frame;
     35 	struct timespec	 m_sleep;
     36 	uint64_t	 m_exec_floating_avg;
     37 	uint64_t	 m_total_executions;
     38 	int		 m_fps;
     39 	int		 m_eps;
     40 	int		 m_tu;
     41 	int		 m_offset;
     42 	int		 m_size_y;
     43 	int		 m_size_x;
     44 };
     45 
     46 CursesViewer::CursesViewer(ProcessDir &pdir) :
     47 	m_pdir(pdir),
     48 	m_floating_avg({ 1, 0 }),
     49 	m_sleep({ 0, 1 * 1000 * 1000 * 1000 / 66 }),
     50 	m_exec_floating_avg(0),
     51 	m_total_executions(0),
     52 	m_fps(0),
     53 	m_eps(0),
     54 	m_tu(0),
     55 	m_offset(0)
     56 {
     57 	getmaxyx(stdscr, m_size_y, m_size_x);
     58 
     59 	m_cur_pfile = &m_pdir.m_procfiles[0];
     60 	m_cur_tu = m_cur_pfile->m_tus[0];
     61 
     62 	struct timespec one_thirtythird = { 0, 1 * 1000 * 1000 * 1000 / 33 };
     63 	for (int i = 0; i < 33; i++) {
     64 		m_frame_deltas.push(one_thirtythird);
     65 		m_execution_history.push(0);
     66 	}
     67 }
     68 
     69 void
     70 CursesViewer::loop()
     71 {
     72 #ifndef __APPLE__
     73 	clock_gettime(CLOCK_UPTIME, &m_last_frame);
     74 #endif
     75 
     76 	// Make getch() non-blocking.
     77 	nodelay(stdscr, true);
     78 
     79 	while (1) {
     80 		assert(m_frame_deltas.size() == 33);
     81 		assert(m_execution_history.size() == 33);
     82 
     83 		erase();
     84 		if (get_keyboard())
     85 			return;
     86 		draw();
     87 		update_execs();
     88 		update_sleep();
     89 	}
     90 }
     91 
     92 int
     93 CursesViewer::get_keyboard()
     94 {
     95 	// Non-blocking due to nodelay().
     96 	int ch = getch();
     97 
     98 	if (ch == 'q')
     99 		return 1;
    100 	else if (ch == 'l' && m_tu < (m_cur_pfile->m_tus.size() - 1))
    101 		m_tu++;
    102 	else if (ch == 'h' && m_tu > 0)
    103 		m_tu--;
    104 
    105 	m_cur_tu = m_cur_pfile->m_tus[m_tu];
    106 
    107 	if (ch == 'j' && m_offset < (m_cur_tu.num_lines - m_size_y - 1))
    108 		m_offset++;
    109 	else if (ch == 'k' && m_offset > 0)
    110 		m_offset--;
    111 
    112 	return 0;
    113 }
    114 
    115 void
    116 CursesViewer::draw()
    117 {
    118 	int upper_bound = m_size_y - 2 + m_offset;
    119 
    120 	for (int i = m_offset; i < upper_bound && i < m_cur_tu.num_lines; i++) {
    121 		uint64_t e = m_cur_tu.exec_counts[i] - m_cur_tu.exec_counts_last[i];
    122 
    123 		std::string l = m_cur_tu.source[i];
    124 
    125 		m_total_executions += e;
    126 
    127 		int color = 0;
    128 		if (e > 100000)
    129 			color = 5;
    130 		else if (e > 10000)
    131 			color = 4;
    132 		else if (e > 1000 )
    133 			color = 3;
    134 		else if (e > 100)
    135 			color = 2;
    136 		else if (e > 0)
    137 			color = 1;
    138 
    139 		if (color != 0)
    140 			attron(COLOR_PAIR(color));
    141 
    142 		printw("%s\n", l.c_str());
    143 
    144 		if (color != 0)
    145 			attroff(COLOR_PAIR(color));
    146 	}
    147 
    148 	m_cur_pfile->save_executions();
    149 	print_statusbar();
    150 	refresh();
    151 }
    152 
    153 void
    154 CursesViewer::print_statusbar()
    155 {
    156 	move(m_size_y - 1, 0);
    157 	clrtoeol();
    158 
    159 	for (int i = 1; i <= 5; i++) {
    160 		attron(COLOR_PAIR(i));
    161 		printw("<<");
    162 		attroff(COLOR_PAIR(i));
    163 	}
    164 
    165 	printw(" [%s] [%s] [%i/%i] [%i fps] [%ik execs/s]",
    166 		m_cur_pfile->m_progname.c_str(),
    167 		m_cur_tu.comp_file_path.c_str(),
    168 		m_tu + 1, m_cur_pfile->m_tus.size(),
    169 		m_fps,
    170 		m_eps / 1000);
    171 
    172 	printw("\n");
    173 }
    174 
    175 void
    176 CursesViewer::update_execs()
    177 {
    178 	// Push on new data and pop old data off. Subtracts the oldest
    179 	// execution count from the floating average.
    180 	m_exec_floating_avg += m_total_executions;
    181 	m_execution_history.push(m_total_executions);
    182 
    183 	m_exec_floating_avg -= m_execution_history.front();
    184 	m_execution_history.pop();
    185 
    186 	m_eps = m_exec_floating_avg / 33;
    187 	m_total_executions = 0;
    188 }
    189 
    190 void
    191 CursesViewer::update_sleep()
    192 {
    193 #ifndef __APPLE__
    194 	struct timespec tmp, delta;
    195 	struct timespec one = { 1, 0 };
    196 	struct timespec shift = { 0, 50 * 1000 };
    197 
    198 	// Get last frames duration and update last_frame time.
    199 	clock_gettime(CLOCK_UPTIME, &tmp);
    200 	timespecsub(&tmp, &m_last_frame, &delta);
    201 	m_last_frame = tmp;
    202 
    203 	// Pop oldest off and push newest on to frame_delta's queue.
    204 	m_frame_deltas.push(delta);
    205 	tmp = m_frame_deltas.front();
    206 	m_frame_deltas.pop();
    207 
    208 	// Remove oldest time and add newest time to floating_avg.
    209 	timespecsub(&m_floating_avg, &tmp, &m_floating_avg);
    210 	timespecadd(&m_floating_avg, &delta, &m_floating_avg);
    211 
    212 	m_fps = 33 * 1000 / (m_floating_avg.tv_sec * 1000 + m_floating_avg.tv_nsec / (1000 * 1000));
    213 
    214 	if (timespeccmp(&m_floating_avg, &one, <))
    215 		// We're executing too fast. Increase sleep.
    216 		timespecadd(&m_sleep, &shift, &m_sleep);
    217 	else if (timespeccmp(&m_floating_avg, &one, !=) && timespeccmp(&m_sleep, &shift, >))
    218 		// Floating avg is > 1.0 but we can still subtract at
    219 		// least shift.
    220 		timespecsub(&m_sleep, &shift, &m_sleep);
    221 
    222 #endif
    223 	nanosleep(&m_sleep, NULL);
    224 }
    225 
    226 int
    227 main(int argc, char *argv[])
    228 {
    229 	ProcessDir pdir;
    230 
    231 	initscr();
    232 	if (has_colors() == FALSE) {
    233 		endwin();
    234 		printf("Your terminal does not support color\n");
    235 		std::exit(1);
    236 	}
    237 	start_color();
    238 	init_pair(1, COLOR_RED, COLOR_BLACK);
    239 	init_pair(2, COLOR_YELLOW, COLOR_BLACK);
    240 	init_pair(3, COLOR_GREEN, COLOR_BLACK);
    241 	init_pair(4, COLOR_CYAN, COLOR_BLACK);
    242 	init_pair(5, COLOR_MAGENTA, COLOR_BLACK);
    243 
    244 	printw(">> Welcome to C It Run!\n");
    245 	printw(">> Waiting for programs..\n");
    246 
    247 	refresh();
    248 
    249 	pdir.scan();
    250 	while (pdir.m_procfiles.size() == 0) {
    251 		sleep(1);
    252 		pdir.scan();
    253 	}
    254 
    255 	CursesViewer viewer(pdir);
    256 	viewer.loop();
    257 
    258 	endwin();
    259 
    260 	return 0;
    261 }