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 }