1/* 2** Licensed under the GPL v2, see the file LICENSE in this tarball 3** 4** Based on nanotop.c from floppyfw project 5** 6** Contact me: vda.linux@googlemail.com */ 7 8//TODO: 9// simplify code 10// /proc/locks 11// /proc/stat: 12// disk_io: (3,0):(22272,17897,410702,4375,54750) 13// btime 1059401962 14//TODO: use sysinfo libc call/syscall, if appropriate 15// (faster than open/read/close): 16// sysinfo({uptime=15017, loads=[5728, 15040, 16480] 17// totalram=2107416576, freeram=211525632, sharedram=0, bufferram=157204480} 18// totalswap=134209536, freeswap=134209536, procs=157}) 19 20#include <time.h> 21#include "libbb.h" 22 23typedef unsigned long long ullong; 24 25enum { PROC_FILE_SIZE = 4096 }; 26 27typedef struct proc_file { 28 char *file; 29 //const char *name; 30 smallint last_gen; 31} proc_file; 32 33static const char *const proc_name[] = { 34 "stat", // Must match the order of proc_file's! 35 "loadavg", 36 "net/dev", 37 "meminfo", 38 "diskstats", 39 "sys/fs/file-nr" 40}; 41 42struct globals { 43 // Sample generation flip-flop 44 smallint gen; 45 // Linux 2.6? (otherwise assumes 2.4) 46 smallint is26; 47 // 1 if sample delay is not an integer fraction of a second 48 smallint need_seconds; 49 char *cur_outbuf; 50 const char *final_str; 51 int delta; 52 int deltanz; 53 struct timeval tv; 54#define first_proc_file proc_stat 55 proc_file proc_stat; // Must match the order of proc_name's! 56 proc_file proc_loadavg; 57 proc_file proc_net_dev; 58 proc_file proc_meminfo; 59 proc_file proc_diskstats; 60 proc_file proc_sys_fs_filenr; 61}; 62#define G (*ptr_to_globals) 63#define gen (G.gen ) 64#define is26 (G.is26 ) 65#define need_seconds (G.need_seconds ) 66#define cur_outbuf (G.cur_outbuf ) 67#define final_str (G.final_str ) 68#define delta (G.delta ) 69#define deltanz (G.deltanz ) 70#define tv (G.tv ) 71#define proc_stat (G.proc_stat ) 72#define proc_loadavg (G.proc_loadavg ) 73#define proc_net_dev (G.proc_net_dev ) 74#define proc_meminfo (G.proc_meminfo ) 75#define proc_diskstats (G.proc_diskstats ) 76#define proc_sys_fs_filenr (G.proc_sys_fs_filenr) 77#define INIT_G() do { \ 78 PTR_TO_GLOBALS = xzalloc(sizeof(G)); \ 79 cur_outbuf = outbuf; \ 80 final_str = "\n"; \ 81 deltanz = delta = 1000000; \ 82 } while (0) 83 84// We depend on this being a char[], not char* - we take sizeof() of it 85#define outbuf bb_common_bufsiz1 86 87static inline void reset_outbuf(void) 88{ 89 cur_outbuf = outbuf; 90} 91 92static inline int outbuf_count(void) 93{ 94 return cur_outbuf - outbuf; 95} 96 97static void print_outbuf(void) 98{ 99 int sz = cur_outbuf - outbuf; 100 if (sz > 0) { 101 xwrite(1, outbuf, sz); 102 cur_outbuf = outbuf; 103 } 104} 105 106static void put(const char *s) 107{ 108 int sz = strlen(s); 109 if (sz > outbuf + sizeof(outbuf) - cur_outbuf) 110 sz = outbuf + sizeof(outbuf) - cur_outbuf; 111 memcpy(cur_outbuf, s, sz); 112 cur_outbuf += sz; 113} 114 115static void put_c(char c) 116{ 117 if (cur_outbuf < outbuf + sizeof(outbuf)) 118 *cur_outbuf++ = c; 119} 120 121static void put_question_marks(int count) 122{ 123 while (count--) 124 put_c('?'); 125} 126 127static void readfile_z(char *buf, int sz, const char* fname) 128{ 129// open_read_close() will do two reads in order to be sure we are at EOF, 130// and we don't need/want that. 131// sz = open_read_close(fname, buf, sz-1); 132 133 int fd = xopen(fname, O_RDONLY); 134 buf[0] = '\0'; 135 if (fd >= 0) { 136 sz = read(fd, buf, sz-1); 137 if (sz > 0) buf[sz] = '\0'; 138 close(fd); 139 } 140} 141 142static const char* get_file(proc_file *pf) 143{ 144 if (pf->last_gen != gen) { 145 pf->last_gen = gen; 146 // We allocate PROC_FILE_SIZE bytes. This wastes memory, 147 // but allows us to allocate only once (at first sample) 148 // per proc file, and reuse buffer for each sample 149 if (!pf->file) 150 pf->file = xmalloc(PROC_FILE_SIZE); 151 readfile_z(pf->file, PROC_FILE_SIZE, proc_name[pf - &first_proc_file]); 152 } 153 return pf->file; 154} 155 156static inline ullong read_after_slash(const char *p) 157{ 158 p = strchr(p, '/'); 159 if (!p) return 0; 160 return strtoull(p+1, NULL, 10); 161} 162 163enum conv_type { conv_decimal, conv_slash }; 164 165// Reads decimal values from line. Values start after key, for example: 166// "cpu 649369 0 341297 4336769..." - key is "cpu" here. 167// Values are stored in vec[]. arg_ptr has list of positions 168// we are interested in: for example: 1,2,5 - we want 1st, 2nd and 5th value. 169static int vrdval(const char* p, const char* key, 170 enum conv_type conv, ullong *vec, va_list arg_ptr) 171{ 172 int indexline; 173 int indexnext; 174 175 p = strstr(p, key); 176 if (!p) return 1; 177 178 p += strlen(key); 179 indexline = 1; 180 indexnext = va_arg(arg_ptr, int); 181 while (1) { 182 while (*p == ' ' || *p == '\t') p++; 183 if (*p == '\n' || *p == '\0') break; 184 185 if (indexline == indexnext) { // read this value 186 *vec++ = conv==conv_decimal ? 187 strtoull(p, NULL, 10) : 188 read_after_slash(p); 189 indexnext = va_arg(arg_ptr, int); 190 } 191 while (*p > ' ') p++; // skip over value 192 indexline++; 193 } 194 return 0; 195} 196 197// Parses files with lines like "cpu0 21727 0 15718 1813856 9461 10485 0 0": 198// rdval(file_contents, "string_to_find", result_vector, value#, value#...) 199// value# start with 1 200static int rdval(const char* p, const char* key, ullong *vec, ...) 201{ 202 va_list arg_ptr; 203 int result; 204 205 va_start(arg_ptr, vec); 206 result = vrdval(p, key, conv_decimal, vec, arg_ptr); 207 va_end(arg_ptr); 208 209 return result; 210} 211 212// Parses files with lines like "... ... ... 3/148 ...." 213static int rdval_loadavg(const char* p, ullong *vec, ...) 214{ 215 va_list arg_ptr; 216 int result; 217 218 va_start(arg_ptr, vec); 219 result = vrdval(p, "", conv_slash, vec, arg_ptr); 220 va_end(arg_ptr); 221 222 return result; 223} 224 225// Parses /proc/diskstats 226// 1 2 3 4 5 6(rd) 7 8 9 10(wr) 11 12 13 14 227// 3 0 hda 51292 14441 841783 926052 25717 79650 843256 3029804 0 148459 3956933 228// 3 1 hda1 0 0 0 0 <- ignore if only 4 fields 229static int rdval_diskstats(const char* p, ullong *vec) 230{ 231 ullong rd = 0; // to avoid "warning: 'rd' might be used uninitialized" 232 int indexline = 0; 233 vec[0] = 0; 234 vec[1] = 0; 235 while (1) { 236 indexline++; 237 while (*p == ' ' || *p == '\t') p++; 238 if (*p == '\0') break; 239 if (*p == '\n') { 240 indexline = 0; 241 p++; 242 continue; 243 } 244 if (indexline == 6) { 245 rd = strtoull(p, NULL, 10); 246 } else if (indexline == 10) { 247 vec[0] += rd; // TODO: *sectorsize (don't know how to find out sectorsize) 248 vec[1] += strtoull(p, NULL, 10); 249 while (*p != '\n' && *p != '\0') p++; 250 continue; 251 } 252 while (*p > ' ') p++; // skip over value 253 } 254 return 0; 255} 256 257static void scale(ullong ul) 258{ 259 char buf[5]; 260 smart_ulltoa5(ul, buf); 261 put(buf); 262} 263 264 265#define S_STAT(a) \ 266typedef struct a { \ 267 struct s_stat *next; \ 268 void (*collect)(struct a *s); \ 269 const char *label; 270#define S_STAT_END(a) } a; 271 272S_STAT(s_stat) 273S_STAT_END(s_stat) 274 275static void collect_literal(s_stat *s) 276{ 277} 278 279static s_stat* init_literal(void) 280{ 281 s_stat *s = xmalloc(sizeof(s_stat)); 282 s->collect = collect_literal; 283 return (s_stat*)s; 284} 285 286static s_stat* init_delay(const char *param) 287{ 288 delta = bb_strtoi(param, NULL, 0) * 1000; 289 deltanz = delta > 0 ? delta : 1; 290 need_seconds = (1000000%deltanz) != 0; 291 return NULL; 292} 293 294static s_stat* init_cr(const char *param) 295{ 296 final_str = "\r"; 297 return (s_stat*)0; 298} 299 300 301// user nice system idle iowait irq softirq (last 3 only in 2.6) 302//cpu 649369 0 341297 4336769 11640 7122 1183 303//cpuN 649369 0 341297 4336769 11640 7122 1183 304enum { CPU_FIELDCNT = 7 }; 305S_STAT(cpu_stat) 306 ullong old[CPU_FIELDCNT]; 307 int bar_sz; 308 char *bar; 309S_STAT_END(cpu_stat) 310 311 312static void collect_cpu(cpu_stat *s) 313{ 314 ullong data[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 }; 315 unsigned frac[CPU_FIELDCNT] = { 0, 0, 0, 0, 0, 0, 0 }; 316 ullong all = 0; 317 int norm_all = 0; 318 int bar_sz = s->bar_sz; 319 char *bar = s->bar; 320 int i; 321 322 if (rdval(get_file(&proc_stat), "cpu ", data, 1, 2, 3, 4, 5, 6, 7)) { 323 put_question_marks(bar_sz); 324 return; 325 } 326 327 for (i = 0; i < CPU_FIELDCNT; i++) { 328 ullong old = s->old[i]; 329 if (data[i] < old) old = data[i]; //sanitize 330 s->old[i] = data[i]; 331 all += (data[i] -= old); 332 } 333 334 if (all) { 335 for (i = 0; i < CPU_FIELDCNT; i++) { 336 ullong t = bar_sz * data[i]; 337 norm_all += data[i] = t / all; 338 frac[i] = t % all; 339 } 340 341 while (norm_all < bar_sz) { 342 unsigned max = frac[0]; 343 int pos = 0; 344 for (i = 1; i < CPU_FIELDCNT; i++) { 345 if (frac[i] > max) max = frac[i], pos = i; 346 } 347 frac[pos] = 0; //avoid bumping up same value twice 348 data[pos]++; 349 norm_all++; 350 } 351 352 memset(bar, '.', bar_sz); 353 memset(bar, 'S', data[2]); bar += data[2]; //sys 354 memset(bar, 'U', data[0]); bar += data[0]; //usr 355 memset(bar, 'N', data[1]); bar += data[1]; //nice 356 memset(bar, 'D', data[4]); bar += data[4]; //iowait 357 memset(bar, 'I', data[5]); bar += data[5]; //irq 358 memset(bar, 'i', data[6]); bar += data[6]; //softirq 359 } else { 360 memset(bar, '?', bar_sz); 361 } 362 put(s->bar); 363} 364 365 366static s_stat* init_cpu(const char *param) 367{ 368 int sz; 369 cpu_stat *s = xmalloc(sizeof(cpu_stat)); 370 s->collect = collect_cpu; 371 sz = strtol(param, NULL, 0); 372 if (sz < 10) sz = 10; 373 if (sz > 1000) sz = 1000; 374 s->bar = xmalloc(sz+1); 375 s->bar[sz] = '\0'; 376 s->bar_sz = sz; 377 return (s_stat*)s; 378} 379 380 381S_STAT(int_stat) 382 ullong old; 383 int no; 384S_STAT_END(int_stat) 385 386static void collect_int(int_stat *s) 387{ 388 ullong data[1]; 389 ullong old; 390 391 if (rdval(get_file(&proc_stat), "intr", data, s->no)) { 392 put_question_marks(4); 393 return; 394 } 395 396 old = s->old; 397 if (data[0] < old) old = data[0]; //sanitize 398 s->old = data[0]; 399 scale(data[0] - old); 400} 401 402static s_stat* init_int(const char *param) 403{ 404 int_stat *s = xmalloc(sizeof(int_stat)); 405 s->collect = collect_int; 406 if (param[0]=='\0') { 407 s->no = 1; 408 } else { 409 int n = strtoul(param, NULL, 0); 410 s->no = n+2; 411 } 412 return (s_stat*)s; 413} 414 415 416S_STAT(ctx_stat) 417 ullong old; 418S_STAT_END(ctx_stat) 419 420static void collect_ctx(ctx_stat *s) 421{ 422 ullong data[1]; 423 ullong old; 424 425 if (rdval(get_file(&proc_stat), "ctxt", data, 1)) { 426 put_question_marks(4); 427 return; 428 } 429 430 old = s->old; 431 if (data[0] < old) old = data[0]; //sanitize 432 s->old = data[0]; 433 scale(data[0] - old); 434} 435 436static s_stat* init_ctx(const char *param) 437{ 438 ctx_stat *s = xmalloc(sizeof(ctx_stat)); 439 s->collect = collect_ctx; 440 return (s_stat*)s; 441} 442 443 444S_STAT(blk_stat) 445 const char* lookfor; 446 ullong old[2]; 447S_STAT_END(blk_stat) 448 449static void collect_blk(blk_stat *s) 450{ 451 ullong data[2]; 452 int i; 453 454 if (is26) { 455 i = rdval_diskstats(get_file(&proc_diskstats), data); 456 } else { 457 i = rdval(get_file(&proc_stat), s->lookfor, data, 1, 2); 458 // Linux 2.4 reports bio in Kbytes, convert to sectors: 459 data[0] *= 2; 460 data[1] *= 2; 461 } 462 if (i) { 463 put_question_marks(9); 464 return; 465 } 466 467 for (i=0; i<2; i++) { 468 ullong old = s->old[i]; 469 if (data[i] < old) old = data[i]; //sanitize 470 s->old[i] = data[i]; 471 data[i] -= old; 472 } 473 scale(data[0]*512); // TODO: *sectorsize 474 put_c(' '); 475 scale(data[1]*512); 476} 477 478static s_stat* init_blk(const char *param) 479{ 480 blk_stat *s = xmalloc(sizeof(blk_stat)); 481 s->collect = collect_blk; 482 s->lookfor = "page"; 483 return (s_stat*)s; 484} 485 486 487S_STAT(fork_stat) 488 ullong old; 489S_STAT_END(fork_stat) 490 491static void collect_thread_nr(fork_stat *s) 492{ 493 ullong data[1]; 494 495 if (rdval_loadavg(get_file(&proc_loadavg), data, 4)) { 496 put_question_marks(4); 497 return; 498 } 499 scale(data[0]); 500} 501 502static void collect_fork(fork_stat *s) 503{ 504 ullong data[1]; 505 ullong old; 506 507 if (rdval(get_file(&proc_stat), "processes", data, 1)) { 508 put_question_marks(4); 509 return; 510 } 511 512 old = s->old; 513 if (data[0] < old) old = data[0]; //sanitize 514 s->old = data[0]; 515 scale(data[0] - old); 516} 517 518static s_stat* init_fork(const char *param) 519{ 520 fork_stat *s = xmalloc(sizeof(fork_stat)); 521 if (*param == 'n') { 522 s->collect = collect_thread_nr; 523 } else { 524 s->collect = collect_fork; 525 } 526 return (s_stat*)s; 527} 528 529 530S_STAT(if_stat) 531 ullong old[4]; 532 const char *device; 533 char *device_colon; 534S_STAT_END(if_stat) 535 536static void collect_if(if_stat *s) 537{ 538 ullong data[4]; 539 int i; 540 541 if (rdval(get_file(&proc_net_dev), s->device_colon, data, 1, 3, 9, 11)) { 542 put_question_marks(10); 543 return; 544 } 545 546 for (i=0; i<4; i++) { 547 ullong old = s->old[i]; 548 if (data[i] < old) old = data[i]; //sanitize 549 s->old[i] = data[i]; 550 data[i] -= old; 551 } 552 put_c(data[1] ? '*' : ' '); 553 scale(data[0]); 554 put_c(data[3] ? '*' : ' '); 555 scale(data[2]); 556} 557 558static s_stat* init_if(const char *device) 559{ 560 if_stat *s = xmalloc(sizeof(if_stat)); 561 562 if (!device || !device[0]) 563 bb_show_usage(); 564 s->collect = collect_if; 565 566 s->device = device; 567 s->device_colon = xmalloc(strlen(device)+2); 568 strcpy(s->device_colon, device); 569 strcat(s->device_colon, ":"); 570 return (s_stat*)s; 571} 572 573 574S_STAT(mem_stat) 575 char opt; 576S_STAT_END(mem_stat) 577 578// "Memory" value should not include any caches. 579// IOW: neither "ls -laR /" nor heavy read/write activity 580// should affect it. We'd like to also include any 581// long-term allocated kernel-side mem, but it is hard 582// to figure out. For now, bufs, cached & slab are 583// counted as "free" memory 584//2.6.16: 585//MemTotal: 773280 kB 586//MemFree: 25912 kB - genuinely free 587//Buffers: 320672 kB - cache 588//Cached: 146396 kB - cache 589//SwapCached: 0 kB 590//Active: 183064 kB 591//Inactive: 356892 kB 592//HighTotal: 0 kB 593//HighFree: 0 kB 594//LowTotal: 773280 kB 595//LowFree: 25912 kB 596//SwapTotal: 131064 kB 597//SwapFree: 131064 kB 598//Dirty: 48 kB 599//Writeback: 0 kB 600//Mapped: 96620 kB 601//Slab: 200668 kB - takes 7 Mb on my box fresh after boot, 602// but includes dentries and inodes 603// (== can take arbitrary amount of mem) 604//CommitLimit: 517704 kB 605//Committed_AS: 236776 kB 606//PageTables: 1248 kB 607//VmallocTotal: 516052 kB 608//VmallocUsed: 3852 kB 609//VmallocChunk: 512096 kB 610//HugePages_Total: 0 611//HugePages_Free: 0 612//Hugepagesize: 4096 kB 613static void collect_mem(mem_stat *s) 614{ 615 ullong m_total = 0; 616 ullong m_free = 0; 617 ullong m_bufs = 0; 618 ullong m_cached = 0; 619 ullong m_slab = 0; 620 621 if (rdval(get_file(&proc_meminfo), "MemTotal:", &m_total, 1)) { 622 put_question_marks(4); 623 return; 624 } 625 if (s->opt == 'f') { 626 scale(m_total << 10); 627 return; 628 } 629 630 if (rdval(proc_meminfo.file, "MemFree:", &m_free , 1) 631 || rdval(proc_meminfo.file, "Buffers:", &m_bufs , 1) 632 || rdval(proc_meminfo.file, "Cached:", &m_cached, 1) 633 || rdval(proc_meminfo.file, "Slab:", &m_slab , 1) 634 ) { 635 put_question_marks(4); 636 return; 637 } 638 639 m_free += m_bufs + m_cached + m_slab; 640 switch (s->opt) { 641 case 'f': 642 scale(m_free << 10); break; 643 default: 644 scale((m_total - m_free) << 10); break; 645 } 646} 647 648static s_stat* init_mem(const char *param) 649{ 650 mem_stat *s = xmalloc(sizeof(mem_stat)); 651 s->collect = collect_mem; 652 s->opt = param[0]; 653 return (s_stat*)s; 654} 655 656 657S_STAT(swp_stat) 658S_STAT_END(swp_stat) 659 660static void collect_swp(swp_stat *s) 661{ 662 ullong s_total[1]; 663 ullong s_free[1]; 664 if (rdval(get_file(&proc_meminfo), "SwapTotal:", s_total, 1) 665 || rdval(proc_meminfo.file, "SwapFree:" , s_free, 1) 666 ) { 667 put_question_marks(4); 668 return; 669 } 670 scale((s_total[0]-s_free[0]) << 10); 671} 672 673static s_stat* init_swp(const char *param) 674{ 675 swp_stat *s = xmalloc(sizeof(swp_stat)); 676 s->collect = collect_swp; 677 return (s_stat*)s; 678} 679 680 681S_STAT(fd_stat) 682S_STAT_END(fd_stat) 683 684static void collect_fd(fd_stat *s) 685{ 686 ullong data[2]; 687 688 if (rdval(get_file(&proc_sys_fs_filenr), "", data, 1, 2)) { 689 put_question_marks(4); 690 return; 691 } 692 693 scale(data[0] - data[1]); 694} 695 696static s_stat* init_fd(const char *param) 697{ 698 fd_stat *s = xmalloc(sizeof(fd_stat)); 699 s->collect = collect_fd; 700 return (s_stat*)s; 701} 702 703 704S_STAT(time_stat) 705 int prec; 706 int scale; 707S_STAT_END(time_stat) 708 709static void collect_time(time_stat *s) 710{ 711 char buf[sizeof("12:34:56.123456")]; 712 struct tm* tm; 713 int us = tv.tv_usec + s->scale/2; 714 time_t t = tv.tv_sec; 715 716 if (us >= 1000000) { 717 t++; 718 us -= 1000000; 719 } 720 tm = localtime(&t); 721 722 sprintf(buf, "%02d:%02d:%02d", tm->tm_hour, tm->tm_min, tm->tm_sec); 723 if (s->prec) 724 sprintf(buf+8, ".%0*d", s->prec, us / s->scale); 725 put(buf); 726} 727 728static s_stat* init_time(const char *param) 729{ 730 int prec; 731 time_stat *s = xmalloc(sizeof(time_stat)); 732 733 s->collect = collect_time; 734 prec = param[0]-'0'; 735 if (prec < 0) prec = 0; 736 else if (prec > 6) prec = 6; 737 s->prec = prec; 738 s->scale = 1; 739 while (prec++ < 6) 740 s->scale *= 10; 741 return (s_stat*)s; 742} 743 744static void collect_info(s_stat *s) 745{ 746 gen ^= 1; 747 while (s) { 748 put(s->label); 749 s->collect(s); 750 s = s->next; 751 } 752} 753 754 755typedef s_stat* init_func(const char *param); 756 757static const char options[] ALIGN1 = "ncmsfixptbdr"; 758static init_func *const init_functions[] = { 759 init_if, 760 init_cpu, 761 init_mem, 762 init_swp, 763 init_fd, 764 init_int, 765 init_ctx, 766 init_fork, 767 init_time, 768 init_blk, 769 init_delay, 770 init_cr 771}; 772 773int nmeter_main(int argc, char **argv); 774int nmeter_main(int argc, char **argv) 775{ 776 char buf[32]; 777 s_stat *first = NULL; 778 s_stat *last = NULL; 779 s_stat *s; 780 char *cur, *prev; 781 782 INIT_G(); 783 784 xchdir("/proc"); 785 786 if (argc != 2) 787 bb_show_usage(); 788 789 if (open_read_close("version", buf, sizeof(buf)) > 0) 790 is26 = (strstr(buf, " 2.4.")==NULL); 791 792 // Can use argv[1] directly, but this will mess up 793 // parameters as seen by e.g. ps. Making a copy... 794 cur = xstrdup(argv[1]); 795 while (1) { 796 char *param, *p; 797 prev = cur; 798 again: 799 cur = strchr(cur, '%'); 800 if (!cur) 801 break; 802 if (cur[1] == '%') { // %% 803 strcpy(cur, cur+1); 804 cur++; 805 goto again; 806 } 807 *cur++ = '\0'; // overwrite % 808 if (cur[0] == '[') { 809 // format: %[foptstring] 810 cur++; 811 p = strchr(options, cur[0]); 812 param = cur+1; 813 while (cur[0] != ']') { 814 if (!cur[0]) 815 bb_show_usage(); 816 cur++; 817 } 818 *cur++ = '\0'; // overwrite [ 819 } else { 820 // format: %NNNNNNf 821 param = cur; 822 while (cur[0] >= '0' && cur[0] <= '9') 823 cur++; 824 if (!cur[0]) 825 bb_show_usage(); 826 p = strchr(options, cur[0]); 827 *cur++ = '\0'; // overwrite format char 828 } 829 if (!p) 830 bb_show_usage(); 831 s = init_functions[p-options](param); 832 if (s) { 833 s->label = prev; 834 s->next = 0; 835 if (!first) 836 first = s; 837 else 838 last->next = s; 839 last = s; 840 } else { 841 // %NNNNd or %r option. remove it from string 842 strcpy(prev + strlen(prev), cur); 843 cur = prev; 844 } 845 } 846 if (prev[0]) { 847 s = init_literal(); 848 s->label = prev; 849 s->next = 0; 850 if (!first) 851 first = s; 852 else 853 last->next = s; 854 last = s; 855 } 856 857 // Generate first samples but do not print them, they're bogus 858 collect_info(first); 859 reset_outbuf(); 860 if (delta >= 0) { 861 gettimeofday(&tv, NULL); 862 usleep(delta > 1000000 ? 1000000 : delta - tv.tv_usec%deltanz); 863 } 864 865 while (1) { 866 gettimeofday(&tv, NULL); 867 collect_info(first); 868 put(final_str); 869 print_outbuf(); 870 871 // Negative delta -> no usleep at all 872 // This will hog the CPU but you can have REALLY GOOD 873 // time resolution ;) 874 // TODO: detect and avoid useless updates 875 // (like: nothing happens except time) 876 if (delta >= 0) { 877 int rem; 878 // can be commented out, will sacrifice sleep time precision a bit 879 gettimeofday(&tv, NULL); 880 if (need_seconds) 881 rem = delta - ((ullong)tv.tv_sec*1000000 + tv.tv_usec) % deltanz; 882 else 883 rem = delta - tv.tv_usec%deltanz; 884 // Sometimes kernel wakes us up just a tiny bit earlier than asked 885 // Do not go to very short sleep in this case 886 if (rem < delta/128) { 887 rem += delta; 888 } 889 usleep(rem); 890 } 891 } 892 893 /*return 0;*/ 894} 895