6 static const char *rev =
"$Revision: 1.19 $$Date: 2018/06/28 21:14:28 $";
13 usage: %s --cmd=<cmd>\n\
14 examples: %s --cmd='sleep 25' # this will take about a minute\n\
15 %s --cmd='taskset -c 4 dd if=/dev/zero of=/dev/sdf bs=50M count=500 oflag=direct' --disk=sdf\n\
16 %s --cmd='dd if=/dev/zero of=t.dat count=500' --stat='md*MB/s?/proc/diskstats?/md[0-3]/?$10?(1.0/2048)?yes'\n\
17 gnuplot -e png=0 `/bin/ls -t periodic_*_stats.out|head -1`\n\
18 For each cmd, record CPU info. Additionally, record total system CPU\n\
19 (can be >100 w/ multicore), file system Cached and Dirty %%, plus any\n\
20 other stats specified by --stats options\n\
22 --cmd= can have multiple\n\
23 --Cmd= run cmd, but dont graph CPU (can have multiple)\n\
24 --pid= graph comma or space sep. list of pids (getting cmd from /proc/)\n\
25 --disk= automatically build --stat for disk(s)\n\
26 --stat= desc?file?linespec?fieldspec?multiplier?rate\n\
27 builtin = CPUnode,Cached,Dirty\n\
28 cmd-builtin = CPUcmdN, CPU+cmdN\n\
29 --out-dir= output dir (for 2+ output files; stats and cmd+)\n\
31 --period= (float) default=%s\n\
32 --pre= the number of periods wait before exec of --cmd\n\
33 --post= the number of periods to loop after cmd exits\n\
34 --graph= add to graph any possibly included non-graphed metrics\n\
36 --no-defaults for no default stats (ie. cpu-parent,cpu-children)\n\
37 --init= default dropcache if root. NOT implemented yet\n\
44 --sys-iowait,-w include the system iowait on the graph\n\
45 --cmd-iowait include cmd iowait on the graph\n\
48 basename(argv[0]), basename(argv[0]), basename(argv[0]), basename(argv[0]), opt_period
65 #include <sys/utsname.h>
75 #define TRACE_NAME "periodic_cmd_stats"
81 static void trace_ap(
const char *msg, va_list ap)
84 unsigned len = strlen(msg);
85 len = len < (
sizeof(m_) - 2) ? len : (
sizeof(m_) - 2);
86 memcpy(m_, msg, len + 1);
87 if (m_[len - 1] !=
'\n') memcpy(&(m_[len]),
"\n", 2);
91 static void trace_p(
const char *msg, ...) __attribute__((format(printf, 1, 2)));
92 static
void trace_p(const
char *msg, ...)
99 static void trace_p(
const std::string msg, ...)
103 trace_ap(msg.c_str(), ap);
106 #define TRACE(lvl, ...) \
108 if (lvl <= 1) trace_p(__VA_ARGS__); \
110 #define TRACE_CNTL(xyzz, ...)
115 char *opt_init = NULL;
116 std::vector<std::string> opt_cmd;
117 std::vector<std::string> opt_Cmd;
119 std::string opt_disk;
120 std::string opt_stats;
121 std::string opt_outdir(
"");
122 std::string opt_graph(
"CPUnode,Cached,Dirty,Free");
123 const char *opt_period =
"5.0";
124 std::string opt_comment;
132 int opt_sys_iowait = 0;
133 int opt_cmd_iowait = 0;
136 std::vector<pid_t> g_pid_vec;
138 void charreplace(
char *instr,
char oldc,
char newc)
148 void parse_args(
int argc,
char *argv[])
155 static struct option long_options[] = {
157 {
"help", no_argument, 0,
'h'},
158 {
"init", required_argument, 0,
'i'},
159 {
"cmd", required_argument, 0,
'c'},
160 {
"Cmd", required_argument, 0,
'C'},
161 {
"disk", required_argument, 0,
'd'},
162 {
"stat", required_argument, 0,
's'},
163 {
"out-dir", required_argument, 0,
'o'},
164 {
"period", required_argument, 0,
'p'},
165 {
"sys-iowait", no_argument, 0,
'w'},
166 {
"fault", no_argument, 0,
'f'},
167 {
"pid", required_argument, 0,
'P'},
168 {
"ymax", required_argument, 0, 1},
169 {
"yincr", required_argument, 0, 2},
170 {
"y2max", required_argument, 0, 3},
171 {
"y2incr", required_argument, 0, 4},
172 {
"pre", required_argument, 0, 5},
173 {
"post", required_argument, 0, 6},
174 {
"graph", required_argument, 0, 7},
175 {
"yrange", required_argument, 0, 8},
176 {
"comment", required_argument, 0, 9},
177 {
"cmd-iowait", no_argument, 0, 10},
179 opt = getopt_long(argc, argv,
"?hvqVi:c:C:d:s:o:p:P:wf",
181 if (opt == -1)
break;
203 opt_cmd.push_back(optarg);
206 opt_Cmd.push_back(optarg);
210 opt_disk = opt_disk +
"," + optarg;
215 if (opt_stats.size())
216 opt_stats = opt_stats +
"," + optarg;
221 opt_outdir = std::string(optarg) +
"/";
233 charreplace(optarg,
' ',
',');
235 opt_pid = opt_pid +
"," + optarg;
240 opt_ymax = strtoul(optarg, NULL, 0);
243 opt_yincr = strtoul(optarg, NULL, 0);
246 opt_y2max = strtoul(optarg, NULL, 0);
249 opt_y2incr = strtoul(optarg, NULL, 0);
252 opt_pre = strtoul(optarg, NULL, 0);
255 opt_post = strtoul(optarg, NULL, 0);
258 opt_graph += std::string(
",") + optarg;
261 opt_ymin = strtoul(optarg, NULL, 0);
262 cp = strstr(optarg,
":") + 1;
263 opt_ymax = strtoul(cp, NULL, 0);
264 if ((cp = strstr(cp,
":")))
267 opt_yincr = strtoul(strstr(cp,
":") + 1, NULL, 0);
270 opt_yincr = (opt_ymax - opt_ymin) / 5;
273 opt_comment = optarg;
279 printf(
"?? getopt returned character code 0%o ??\n", opt);
285 void perror_exit(
const char *msg, ...)
290 vsnprintf(buf,
sizeof(buf), msg, ap);
304 pid_t fork_execv(
int close_start,
int close_cnt,
int sleepB4exec_us,
int iofd[3],
const char *cmd,
char *
const argv[],
char *
const env[])
308 for (
auto ii = 0; ii < 3; ++ii)
310 lcl_iofd[ii] = iofd[ii];
314 iofd[ii] = ii == 0 ? pipes[ii][1] : pipes[ii][0];
322 if (lcl_iofd[0] == -1)
325 int fd = dup2(pipes[0][0], 0);
326 TRACE(3,
"fork_execv dupped(%d) onto %d (should be 0)", pipes[0][0], fd);
333 usleep(sleepB4exec_us);
334 TRACE(1,
"fork_execv sleep complete. sleepB4exec_us=%d sts=%d", sleepB4exec_us, 0 );
336 for (
auto ii = 1; ii < 3; ++ii)
338 if (lcl_iofd[ii] == -1)
341 int fd = dup2(pipes[ii][1], ii);
342 TRACE(3,
"fork_execv dupped(%d) onto %d (should be %d)", pipes[ii][1], fd, ii);
345 else if (lcl_iofd[ii] != ii)
347 int fd = dup2(lcl_iofd[ii], ii);
348 TRACE(3,
"fork_execv dupped(%d) onto %d (should be %d)", pipes[ii][1], fd, ii);
351 for (
auto ii = close_start; ii < (close_start + close_cnt); ++ii)
354 execve(cmd, argv, env);
361 for (
auto ii = 0; ii < 3; ++ii)
362 if (lcl_iofd[ii] == -1)
363 close(ii == 0 ? pipes[ii][0] : pipes[ii][1]);
366 TRACE(3,
"fork_execv pid=%d", pid);
370 uint64_t swapPtr(
void *X)
372 uint64_t x = (uint64_t)X;
373 x = (x & 0x00000000ffffffff) << 32 | (x & 0xffffffff00000000) >> 32;
374 x = (x & 0x0000ffff0000ffff) << 16 | (x & 0xfff0000fffff0000) >> 16;
375 x = (x & 0x00ff00ff00ff00ff) << 8 | (x & 0xff00ff00ff00ff00) >> 8;
398 static int g_devnullfd = -1;
401 std::string AWK(std::string
const &awk_cmd,
const char *file,
const char *input)
404 ssize_t bytes = 0, tot_bytes = 0;
405 char *
const argv_[4] = {(
char *)
"/bin/gawk",
406 (
char *)awk_cmd.c_str(),
412 if (g_devnullfd == -1)
413 g_devnullfd = open(
"/dev/null", O_WRONLY);
419 int iofd[3] = {infd, -1, 2};
420 TRACE(3,
"AWK b4 fork_execv input=%p", (
void *)input);
423 pid = fork_execv(0, 0 , 0, iofd,
"/bin/gawk", argv_, env);
426 int xx = strlen(input);
427 int sts = write(iofd[0], input, xx);
429 perror(
"write AWK stdin");
431 while ((bytes = read(iofd[1], &readbuf[tot_bytes],
sizeof(readbuf) - tot_bytes)) != 0)
433 TRACE(3,
"AWK while bytes=read > 0 bytes=%zd readbuf=0x%016lx errno=%d", bytes, swapPtr(&readbuf[tot_bytes]), errno);
436 if (errno == EINTR)
continue;
441 TRACE(3,
"AWK after read tot=" + std::to_string((
long long unsigned)tot_bytes) +
" bytes=" + std::to_string((
long long unsigned)bytes) +
" input=" + std::string(input));
445 while ((bytes = read(iofd[1], &readbuf[tot_bytes],
sizeof(readbuf) - tot_bytes)) > 0)
447 TRACE(3,
"AWK after read tot=%zd bytes=%zd [0]=0x%x input=%p", tot_bytes, bytes, readbuf[0], (
void *)input);
449 readbuf[tot_bytes >= 0 ? tot_bytes : 0] =
'\0';
451 TRACE(3,
"AWK after close child stdout. child pid=%d", pid);
454 pid_t done_pid = waitpid(pid,&status,0);
455 TRACE( 3,
"AWK after wait pid=%d done_pid=%d status=%d(0x%x)"
456 , pid, done_pid, status, status );
458 return std::string(readbuf);
462 void string_addto_vector(std::string &instr, std::vector<std::string> &outvec,
char delim)
464 std::stringstream ss(instr);
468 std::getline(ss, substr, delim);
469 outvec.push_back(substr);
473 uint64_t gettimeofday_us(
void)
476 gettimeofday(&tv, NULL);
481 return (uint64_t)tv.tv_sec * 1000000 + tv.tv_usec;
484 #define DATA_START " DATA START"
485 #define GNUPLOT_PREFIX (const char *) \
487 #!/usr/bin/env gnuplot\n\
490 # gnuplot -e 'ymin=400;ymax=1400' ./$0\n\
492 # gnuplot -e 'duration_s=35;set multiplot' ./gnuplot.gnuplot ./gnuplot.1.gnuplot -e 'set nomultiplot;pause -1'\n\
493 if(!exists('ARG0')) ARG0='' # for version 4, use: gnuplot -e ARG0=hello\n\
494 print 'ARG0=',ARG0 # ARG0.. automatically define in gnuplot version 5+\n\
495 if(!exists('ymin')) ymin=%d\n\
496 if(!exists('ymax')) ymax=%d\n\
497 if(!exists('yincr')) yincr=%d\n\
498 if(!exists('y2max')) y2max=%d\n\
499 if(!exists('y2incr')) y2incr=%d\n\
500 if(!exists('png')) png=1\n\
501 if(!exists('duration_s')) duration_s=0\n\
502 if(!exists('width')) width=512\n\
503 if(!exists('height')) height=384\n\
504 thisPid=system('echo `ps -p$$ -oppid=`')\n\
505 thisFile=system('ls -l /proc/'.thisPid.\"/fd | grep -v pipe: | tail -1 | sed -e 's/.*-> //'\")\n\
507 set title \"Disk Write Rate and %%CPU vs. time\\n%s %s %s%s\" # cmd and/or comment at end\n\
509 tfmt='%%Y-%%m-%%dT%%H:%%M:%%S' # try to use consistent format\n\
510 set timefmt '%%Y-%%m-%%dT%%H:%%M:%%S'\n\
512 set grid xtics back\n\
513 xstart=system(\"awk '/^....-..-..T/{print$1;exit}' \".thisFile)\n\
514 xend=system(\"awk 'END{print$1}' \".thisFile)\n\
515 print 'xstart='.xstart.' xend='.xend.' duration=',strptime(tfmt,xend)-strptime(tfmt,xstart)\n\
516 if(duration_s>0) end_t=strptime(tfmt,xstart)+duration_s; else end_t=strptime(tfmt,xend)\n\
517 set xrange [xstart:end_t]\n\
520 set ytics nomirror\n\
521 if(ymax==0) set yrange [ymin:*];\\\n\
522 else set yrange [ymin:ymax];set ytics yincr\n\
523 set grid ytics back\n\
525 set y2label '%%CPU, %%MemTotal'\n\
526 set y2tics autofreq\n\
527 if(y2max==0) set y2range [0:*];\\\n\
528 else set y2range [0:y2max];set y2tics y2incr\n\
531 if(png==1) set terminal png size width,height;\\\n\
532 pngfile=system( 'echo `basename '.thisFile.' .out`.png' );\\\n\
533 set output pngfile;\\\n\
534 else set terminal x11 size width,height\n\
536 plot \"< awk '/^#" DATA_START "/,/NEVER HAPPENS/' \".thisFile "
538 void sigchld_sigaction(
int signo, siginfo_t *info,
void *context __attribute__((__unused__)))
541 for (
size_t ii = 0; ii < g_pid_vec.size(); ++ii)
543 pid_t pid = g_pid_vec[ii];
544 if (pid == info->si_pid)
546 TRACE(2,
"sigchld_sigaction signo=%d status=%d(0x%x) code=%d(0x%x) sending_pid=%d", signo, info->si_status, info->si_status, info->si_code, info->si_code, info->si_pid);
550 TRACE(3,
"sigchld_sigaction signo=%d status=%d(0x%x) code=%d(0x%x) sending_pid=%d", signo, info->si_status, info->si_status, info->si_code, info->si_code, info->si_pid);
553 void read_proc_file(
const char *file,
char *buffer,
int buffer_size)
555 TRACE(4,
"read_proc_file b4 open proc file" + std::string(file));
556 int fd = open(file, O_RDONLY);
557 int offset = 0, sts = 0;
560 sts = read(fd, &buffer[offset], buffer_size - offset);
568 buffer[sts + offset] =
'\0';
570 TRACE(4,
"read_proc_file after close " + std::string(file) +
" read=%d offset=%d", sts, offset);
573 pid_t check_pid_vec(
void)
575 for (
size_t ii = 0; ii < g_pid_vec.size();)
577 pid_t pid = g_pid_vec[ii];
579 pid_t pp = waitpid(pid, &status, WNOHANG);
580 TRACE(3,
"check_pid_vec %d=waitpid(pid=%d) errno=%d", pp, pid, errno);
582 g_pid_vec.erase(g_pid_vec.begin() + ii);
585 if (errno == ECHILD && kill(pid, 0) == 0)
590 g_pid_vec.erase(g_pid_vec.begin() + ii);
595 if (g_pid_vec.size() == 0)
603 TRACE(1,
"atexit cleanup g_pid_vec.size()=%zd\n", g_pid_vec.size());
604 for (std::vector<pid_t>::iterator pid = g_pid_vec.begin(); pid != g_pid_vec.end(); ++pid)
609 #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L))
610 #pragma GCC diagnostic push
611 #pragma GCC diagnostic ignored "-Wunused-parameter"
613 void sigint_sigaction(
int signo, siginfo_t *info,
void *context)
618 #if (defined(__cplusplus) && (__cplusplus >= 201103L)) || (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L))
619 #pragma GCC diagnostic pop
622 int main(
int argc,
char *argv[])
625 int post_periods_completed = 0;
626 parse_args(argc, argv);
627 if ((argc - optind) != 0 || (opt_cmd.size() == 0 && opt_pid.size() == 0))
630 printf(
"unexpected argument(s) %d!=0\n", argc - optind);
631 for (ii = 0; (optind + ii) < argc; ++ii)
632 printf(
"arg%d=%s\n", ii + 1, argv[optind + ii]);
637 std::vector<std::string> graphs;
638 string_addto_vector(opt_graph, graphs,
',');
640 char motherboard[1024] = {0};
643 FILE *fp = popen(
"dmidecode | grep -m2 'Product Name:' | tail -1",
"r");
644 fread(motherboard, 1,
sizeof(motherboard), fp);
647 TRACE(1,
"main - motherboard=" + std::string(motherboard));
655 struct sigaction sigaction_s;
660 sigaction_s.sa_sigaction = sigchld_sigaction;
661 sigaction_s.sa_flags = SA_SIGINFO | SA_NOCLDWAIT;
663 sigaction_s.sa_handler = SIG_IGN;
664 sigaction_s.sa_flags = SA_NOCLDWAIT;
666 sigemptyset(&sigaction_s.sa_mask);
667 sigaction(SIGCHLD, &sigaction_s, NULL);
669 sigaction_s.sa_sigaction = sigint_sigaction;
670 sigaction_s.sa_flags = SA_SIGINFO;
671 sigaction(SIGINT, &sigaction_s, NULL);
675 long long unsigned concurentThreadsSupported = sysconf(_SC_NPROCESSORS_ONLN);
677 TRACE(0,
"main concurentThreadsSupported=%u opt_stats=" + opt_stats, concurentThreadsSupported);
680 gettimeofday(&tv, NULL);
681 strftime(run_time,
sizeof(run_time),
"%FT%H%M%S", localtime(&tv.tv_sec));
682 TRACE(0,
"main run_time=" + std::string(run_time));
688 if ((dot = strchr(ubuf.nodename,
'.')) != NULL)
690 std::string hostname(ubuf.nodename);
691 TRACE(1,
"release=" + std::string(ubuf.release) +
" version=" + std::string(ubuf.version));
694 std::string memKB = AWK(
"NR==1{print$2;exit}",
"/proc/meminfo", NULL);
695 memKB = memKB.substr(0, memKB.size() - 1);
697 std::string dat_file_out(opt_outdir +
"periodic_" + run_time +
"_" + hostname +
"_stats.out");
699 double period = atof(opt_period);
703 std::vector<std::string> pidfile;
705 std::vector<std::string> stats;
709 for (
size_t ii = 0; ii < opt_cmd.size(); ++ii)
711 char cmd_file_out[1024];
712 snprintf(cmd_file_out,
sizeof(cmd_file_out),
"%speriodic_%s_%s_cmd%zd.out", opt_outdir.c_str(), run_time, hostname.c_str(), ii);
713 int fd = open(cmd_file_out, O_WRONLY | O_CREAT, 0666);
714 TRACE(0,
"main fd=%d opt_cmd=" + opt_cmd[ii] +
" cmd_file_out=" + std::string(cmd_file_out), fd);
715 int iofd[3] = {0, fd, fd};
716 char *
const argv_[4] = {(
char *)
"/bin/sh",
718 (
char *)opt_cmd[ii].c_str(),
720 g_pid_vec.push_back(fork_execv(0, 0, (
int)(period * opt_pre * 1e6), iofd,
"/bin/sh", argv_, NULL));
722 std::string pidstr = std::to_string((
long long int)g_pid_vec[ii]);
723 pidfile.push_back(
"/proc/" + pidstr +
"/stat");
725 char desc[128], ss[1024];
727 snprintf(ss,
sizeof(ss),
"CPUcmd%zd?%s?NR==1?$14+$15?1?yes", ii, pidfile[ii].c_str());
730 snprintf(desc,
sizeof(desc),
"CPU+cmd%zd", ii);
731 graphs.push_back(desc);
732 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$14+$15+16+$17?1?yes", desc, pidfile[ii].c_str());
735 snprintf(desc,
sizeof(desc),
"WaitBlkIOcmd%zd", ii);
736 if (opt_cmd_iowait) graphs.push_back(desc);
737 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$42?1?yes", desc, pidfile[ii].c_str());
740 snprintf(desc,
sizeof(desc),
"Faultcmd%zd", ii);
741 if (opt_fault) graphs.push_back(desc);
742 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$10+$11+$12+$13?4096.0/1048576?yes", desc, pidfile[ii].c_str());
745 for (
size_t ii = 0; ii < opt_Cmd.size(); ++ii)
747 char cmd_file_out[1024];
748 snprintf(cmd_file_out,
sizeof(cmd_file_out),
"%speriodic_%s_%s_cmd%zd.out", opt_outdir.c_str(), run_time, hostname.c_str(), ii + opt_cmd.size());
749 int fd = open(cmd_file_out, O_WRONLY | O_CREAT, 0666);
750 TRACE(0,
"main fd=%d opt_Cmd=" + opt_Cmd[ii] +
" cmd_file_out=" + std::string(cmd_file_out), fd);
751 int iofd[3] = {0, fd, fd};
752 char *
const argv_[4] = {(
char *)
"/bin/sh",
754 (
char *)opt_Cmd[ii].c_str(),
756 g_pid_vec.push_back(fork_execv(0, 0, (
int)(period * opt_pre * 1e6), iofd,
"/bin/sh", argv_, NULL));
758 std::string pidstr = std::to_string((
long long int)g_pid_vec[ii]);
759 pidfile.push_back(
"/proc/" + pidstr +
"/stat");
761 char desc[128], ss[1024];
762 snprintf(desc,
sizeof(desc),
"CPU+cmd%zd", ii + opt_cmd.size());
763 snprintf(ss,
sizeof(ss),
"CPUcmd%zd?%s?NR==1?$14+$15?1?yes", ii + opt_cmd.size(), pidfile[ii].c_str());
765 snprintf(ss,
sizeof(ss),
"CPU+cmd%zd?%s?NR==1?$14+$15+16+$17?1?yes", ii + opt_cmd.size(), pidfile[ii].c_str());
769 std::vector<std::string> pids;
771 string_addto_vector(opt_pid, pids,
',');
772 for (
size_t ii = 0; ii < pids.size(); ++ii)
774 g_pid_vec.push_back(std::stoi(pids[ii]));
775 TRACE(1,
"pid=%s g_pid_vec.size()=%ld", pids[ii].c_str(), g_pid_vec.size());
776 pidfile.push_back(
"/proc/" + pids[ii] +
"/stat");
777 char desc[128], ss[1024];
779 snprintf(ss,
sizeof(ss),
"CPUpid%zd?%s?NR==1?$14+$15?1?yes", ii, pidfile[ii].c_str());
782 std::ifstream t(
"/proc/" + pids[ii] +
"/comm");
783 std::string comm((std::istreambuf_iterator<char>(t)),
784 std::istreambuf_iterator<char>());
785 comm = comm.substr(0, comm.size() - 1);
787 snprintf(desc,
sizeof(desc),
"CPU+pid%zd_%s", ii, comm.c_str());
788 graphs.push_back(desc);
789 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$14+$15+16+$17?1?yes", desc, pidfile[ii].c_str());
792 snprintf(desc,
sizeof(desc),
"WaitBlkIOpid%zd", ii);
793 if (opt_cmd_iowait) graphs.push_back(desc);
794 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$42?1?yes", desc, pidfile[ii].c_str());
797 snprintf(desc,
sizeof(desc),
"Faultpid%zd", ii);
798 if (opt_fault) graphs.push_back(desc);
799 snprintf(ss,
sizeof(ss),
"%s?%s?NR==1?$10+$11+$12+$13?4096.0/1048576?yes", desc, pidfile[ii].c_str());
803 stats.push_back(
"CPUnode");
804 stats.push_back(
"IOWait");
805 if (opt_sys_iowait) { graphs.push_back(
"IOWait"); }
806 stats.push_back(
"Cached");
807 stats.push_back(
"Dirty");
808 stats.push_back(
"Free");
812 std::vector<std::string> tmp;
813 string_addto_vector(opt_disk, tmp,
',');
814 for (std::vector<std::string>::iterator dk = tmp.begin(); dk != tmp.end(); ++dk)
819 std::string statstr = *dk +
"_wrMB/s?/proc/diskstats?/" + *dk +
"/?$10?(1.0/2048)?yes";
820 stats.push_back(statstr);
821 std::vector<std::string> stat_spec;
822 string_addto_vector(statstr, stat_spec,
'?');
823 graphs.push_back(stat_spec[s_desc]);
825 statstr = *dk +
"_rdMB/s?/proc/diskstats?/" + *dk +
"/?$6?(1.0/2048)?yes";
826 stats.push_back(statstr);
828 string_addto_vector(statstr, stat_spec,
'?');
833 if (opt_stats.size())
835 std::vector<std::string> tmp_stats;
836 string_addto_vector(opt_stats, tmp_stats,
',');
837 for (std::vector<std::string>::iterator st = tmp_stats.begin();
838 st != tmp_stats.end(); ++st)
840 stats.push_back(*st);
841 std::vector<std::string> stat_spec;
842 string_addto_vector(*st, stat_spec,
'?');
843 graphs.push_back(stat_spec[s_desc]);
847 std::vector<long> pre_vals;
848 std::vector<double> multipliers;
849 std::vector<std::vector<std::string>> spec2(stats.size());
850 std::vector<std::string> awkCmd;
852 std::string header_str(
"#" DATA_START
"\n#_______time_______");
854 int outfd = open(dat_file_out.c_str(), O_WRONLY | O_CREAT, 0777);
856 FILE *outfp = fdopen(outfd,
"w");
858 std::string cmd_comment(
"");
860 cmd_comment +=
"\\ncmd: " + opt_cmd[0];
861 if (opt_comment.size())
862 cmd_comment +=
"\\ncomment: " + opt_comment;
863 fprintf(outfp, GNUPLOT_PREFIX, opt_ymin, opt_ymax, opt_yincr, opt_y2max, opt_y2incr, run_time, hostname.c_str(), ubuf.release, cmd_comment.c_str(),
"disk write MB/s");
865 uint64_t t_start = gettimeofday_us();
868 bool first_graph_spec_added =
false;
869 for (
size_t ii = 0; ii < stats.size(); ++ii)
871 std::vector<std::string> stat_spec;
872 string_addto_vector(stats[ii], stat_spec,
'?');
873 if (stat_spec[s_desc] ==
"CPUnode" && stat_spec.size() == 1)
875 stats[ii] +=
"?/proc/stat?/^cpu[^0-9]/?$2+$3+$4+$6+$7+$8+$9?1.0/" + std::to_string(concurentThreadsSupported) +
"?yes";
876 else if (stat_spec[s_desc] ==
"IOWait" && stat_spec.size() == 1)
877 stats[ii] +=
"?/proc/stat?/^cpu[^0-9]/?$6?1.0/" + std::to_string(concurentThreadsSupported) +
"?yes";
878 else if (stat_spec[s_desc] ==
"Cached" && stat_spec.size() == 1)
879 stats[ii] +=
"?/proc/meminfo?/^(Cached|Buffers):/?$2?1?no";
880 else if (stat_spec[s_desc] ==
"Dirty" && stat_spec.size() == 1)
881 stats[ii] +=
"?/proc/meminfo?/^Dirty:/?$2?1?no";
882 else if (stat_spec[s_desc] ==
"Free" && stat_spec.size() == 1)
883 stats[ii] +=
"?/proc/meminfo?/^MemFree:/?$2?1?no";
885 header_str +=
" " + stat_spec[s_desc];
887 string_addto_vector(stats[ii], spec2[ii],
'?');
889 snprintf(awk_cmd,
sizeof(awk_cmd),
"%s{vv+=%s}END{print vv}"
892 spec2[ii][s_linespec].c_str(), spec2[ii][s_fieldspec].c_str());
893 awkCmd.push_back(awk_cmd);
895 std::string stat = AWK(awkCmd.back(), spec2[ii][s_file].c_str(), NULL);
897 pre_vals.push_back(atol(stat.c_str()));
898 multipliers.push_back(atof(AWK(
"BEGIN{print " + spec2[ii][s_multiplier] +
"}",
"/dev/null", NULL).c_str()));
900 for (
size_t jj = 0; jj < graphs.size(); ++jj)
901 if (graphs[jj] == stat_spec[s_desc])
903 if (first_graph_spec_added) fprintf(outfp,
",\\\n '' ");
904 if (strncmp(stat_spec[s_desc].c_str(),
"CPU", 3) == 0)
905 fprintf(outfp,
"using 1:%zd title '%s' w linespoints axes x1y2", ii + 2, stat_spec[s_desc].c_str());
906 else if (stat_spec[s_desc] ==
"Cached" || stat_spec[s_desc] ==
"Dirty" || stat_spec[s_desc] ==
"Free")
907 fprintf(outfp,
"using 1:($%zd/%s*100) title '%s%%' w linespoints axes x1y2", ii + 2, memKB.c_str(), stat_spec[s_desc].c_str());
908 else if (stat_spec[s_desc].substr(0, 6) ==
"CPUcmd" || stat_spec[s_desc].substr(0, 6) ==
"CPU+cm")
909 fprintf(outfp,
"using 1:%zd title '%s' w linespoints axes x1y2", ii + 2, stat_spec[s_desc].c_str());
910 else if (stat_spec[s_desc].substr(0, 12) ==
"WaitBlkIOcmd")
911 fprintf(outfp,
"using 1:%zd title '%s' w linespoints axes x1y2", ii + 2, stat_spec[s_desc].c_str());
913 fprintf(outfp,
"using 1:%zd title '%s' w linespoints axes x1y1", ii + 2, stat_spec[s_desc].c_str());
914 first_graph_spec_added =
true;
917 header_str +=
" #\n";
920 "\nif(png==0) pause -1 'Press Enter/Return or ^C to finish'\n\
924 fprintf(outfp,
"cmds:\n");
925 for (
size_t ii = 0; ii < opt_cmd.size(); ++ii)
927 std::string ss = opt_cmd[ii] +
"\n";
928 fprintf(outfp,
"%s", ss.c_str());
932 fprintf(outfp,
"stats:\n");
933 for (
size_t ii = 0; ii < stats.size(); ++ii)
935 std::string ss = stats[ii] +
"\n";
936 fprintf(outfp,
"%s", ss.c_str());
940 fprintf(outfp,
"%s", header_str.c_str());
943 std::string tmpdbg(
"main lp=%d done stat%zd=%ld rate=%f ");
945 char proc_stats[8192];
952 int64_t t_sleep = (t_start + (uint64_t)(period * 1e6)) - gettimeofday_us();
955 int sts = usleep(t_sleep);
956 TRACE(3,
"main usleep sts=%d errno=%d", sts, errno);
962 for (lp = 2; lp < MAX_LP; ++lp)
965 gettimeofday(&tv, NULL);
966 strftime(str,
sizeof(str),
"%FT%T", localtime(&tv.tv_sec));
968 fprintf(outfp,
"%s", str);
969 std::string prv_file(
"");
970 for (
size_t ii = 0; ii < stats.size(); ++ii)
972 TRACE(3,
"main lp=%d start stat%zd", lp, ii);
973 char const *awk_file;
974 if (ii < (2 * opt_cmd.size()))
980 read_proc_file(pidfile[ii / 2].c_str(), proc_stats,
sizeof(proc_stats));
985 else if (spec2[ii][s_file] != prv_file)
987 prv_file = spec2[ii][s_file];
988 read_proc_file(spec2[ii][s_file].c_str(), proc_stats,
sizeof(proc_stats));
993 std::string stat_str = AWK(awkCmd[ii], awk_file, awk_in);
995 long stat = atol(stat_str.c_str());
997 if (spec2[ii][s_rate] ==
"yes")
1000 if (stat_str !=
"\n")
1001 rate = (stat - pre_vals[ii]) * multipliers[ii] / period;
1004 TRACE(3, tmpdbg +
"stat_str[0]=0x%x stat_str.size()=%zd", lp, ii, stat, rate, stat_str[0], stat_str.size());
1005 fprintf(outfp,
" %.2f", rate);
1006 if (rate < 0.0 && spec2[ii][s_file] ==
"/proc/diskstats")
1008 TRACE(0,
"main stat:" + spec2[ii][s_desc] +
" rate=%f pre_val=%ld stat=%ld stat_str=\"" + stat_str +
"\" awkCmd=" + awkCmd[ii] +
" proc_diskstats=" + proc_stats, rate, pre_vals[ii], stat);
1011 pre_vals[ii] = stat;
1015 TRACE(3,
"main lp=%d done stat%zd=%ld", lp, ii, stat);
1016 fprintf(outfp,
" %.2f", stat * multipliers[ii]);
1019 fprintf(outfp,
"\n");
1022 int64_t t_sleep = (t_start + (uint64_t)(period * lp * 1000000)) - gettimeofday_us();
1025 int sts = usleep(t_sleep);
1026 TRACE(3,
"main usleep sts=%d errno=%d", sts, errno);
1030 pp = check_pid_vec();
1031 TRACE(2,
"main pp=%d t_sleep=%ld", pp, t_sleep);
1034 if (post_periods_completed == 0)
1035 TRACE(1,
"main processes complete - waiting %d post periods", opt_post);
1036 if (post_periods_completed++ == opt_post)
1042 fprintf(outfp,
"# MAX_LP abort\n");
1048 TRACE(0,
"main done/complete/returning");