Allow file as a top source, it works with socket now.
This will allow top monitoring using socket/ssh tunnelling of system without local symbols. client: pmcstat -R <ip>:<port> -T -r <symbolspath> monitored device: pmcstat -Sinstructions -O <ip>:<port> - Move the file read in the event loop - Initialize and clean log in all cases - Preserve global stats value during top refresh - Fix the rtld/line resolver that ignore '-r' prefix - Support socket for '-R' (server mode) - Display the statistics when exiting top mode
This commit is contained in:
parent
2b4a50c383
commit
94897ca080
@ -169,8 +169,7 @@ pmcstat_cleanup(void)
|
||||
args.pa_logparser = NULL;
|
||||
}
|
||||
|
||||
if (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
|
||||
pmcstat_shutdown_logging();
|
||||
pmcstat_shutdown_logging();
|
||||
}
|
||||
|
||||
void
|
||||
@ -559,7 +558,7 @@ main(int argc, char **argv)
|
||||
int do_print;
|
||||
size_t dummy;
|
||||
int graphdepth;
|
||||
int pipefd[2];
|
||||
int pipefd[2], rfd;
|
||||
int use_cumulative_counts;
|
||||
short cf, cb;
|
||||
uint32_t cpumask;
|
||||
@ -1001,11 +1000,6 @@ main(int argc, char **argv)
|
||||
(args.pa_flags & FLAG_READ_LOGFILE) == 0)
|
||||
errx(EX_USAGE, "ERROR: option -M is only used with -g/-R.");
|
||||
|
||||
/* -T is incompatible with -R (replay logfile is a TODO) */
|
||||
if ((args.pa_flags & FLAG_DO_TOP) &&
|
||||
(args.pa_flags & FLAG_READ_LOGFILE))
|
||||
errx(EX_USAGE, "ERROR: option -T is incompatible with -R.");
|
||||
|
||||
/*
|
||||
* Disallow textual output of sampling PMCs if counting PMCs
|
||||
* have also been asked for, mostly because the combined output
|
||||
@ -1066,7 +1060,22 @@ main(int argc, char **argv)
|
||||
graphfilename);
|
||||
}
|
||||
|
||||
/* if we've been asked to process a log file, do that and exit */
|
||||
/* if we've been asked to process a log file, skip init */
|
||||
if ((args.pa_flags & FLAG_READ_LOGFILE) == 0) {
|
||||
if (pmc_init() < 0)
|
||||
err(EX_UNAVAILABLE,
|
||||
"ERROR: Initialization of the pmc(3) library failed");
|
||||
|
||||
if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
|
||||
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
|
||||
"on CPU %d", 0);
|
||||
}
|
||||
|
||||
/* Allocate a kqueue */
|
||||
if ((pmcstat_kq = kqueue()) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot allocate kqueue");
|
||||
|
||||
/* Setup the logfile as the source. */
|
||||
if (args.pa_flags & FLAG_READ_LOGFILE) {
|
||||
/*
|
||||
* Print the log in textual form if we haven't been
|
||||
@ -1076,28 +1085,17 @@ main(int argc, char **argv)
|
||||
args.pa_flags |= FLAG_DO_PRINT;
|
||||
|
||||
pmcstat_initialize_logging();
|
||||
args.pa_logfd = pmcstat_open_log(args.pa_inputpath,
|
||||
rfd = pmcstat_open_log(args.pa_inputpath,
|
||||
PMCSTAT_OPEN_FOR_READ);
|
||||
if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
|
||||
if ((args.pa_logparser = pmclog_open(rfd)) == NULL)
|
||||
err(EX_OSERR, "ERROR: Cannot create parser");
|
||||
pmcstat_process_log();
|
||||
pmcstat_shutdown_logging();
|
||||
exit(EX_OK);
|
||||
if (fcntl(rfd, F_SETFL, O_NONBLOCK) < 0)
|
||||
err(EX_OSERR, "ERROR: fcntl(2) failed");
|
||||
EV_SET(&kev, rfd, EVFILT_READ, EV_ADD,
|
||||
0, 0, NULL);
|
||||
if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot register kevent");
|
||||
}
|
||||
|
||||
/* otherwise, we've been asked to collect data */
|
||||
if (pmc_init() < 0)
|
||||
err(EX_UNAVAILABLE,
|
||||
"ERROR: Initialization of the pmc(3) library failed");
|
||||
|
||||
if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
|
||||
err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
|
||||
"on CPU %d", 0);
|
||||
|
||||
/* Allocate a kqueue */
|
||||
if ((pmcstat_kq = kqueue()) < 0)
|
||||
err(EX_OSERR, "ERROR: Cannot allocate kqueue");
|
||||
|
||||
/*
|
||||
* Configure the specified log file or setup a default log
|
||||
* consumer via a pipe.
|
||||
@ -1140,6 +1138,7 @@ main(int argc, char **argv)
|
||||
(args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE);
|
||||
|
||||
/*
|
||||
if (args.pa_flags & FLAG_READ_LOGFILE) {
|
||||
* Allocate PMCs.
|
||||
*/
|
||||
|
||||
@ -1272,10 +1271,8 @@ main(int argc, char **argv)
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
pmcstat_start_process();
|
||||
|
||||
/* initialize logging if printing the configured log */
|
||||
if ((args.pa_flags & (FLAG_DO_PRINT | FLAG_DO_TOP)) &&
|
||||
(args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)))
|
||||
pmcstat_initialize_logging();
|
||||
/* initialize logging */
|
||||
pmcstat_initialize_logging();
|
||||
|
||||
/* Handle SIGINT using the kqueue loop */
|
||||
sa.sa_handler = SIG_IGN;
|
||||
@ -1338,16 +1335,13 @@ main(int argc, char **argv)
|
||||
|
||||
switch (kev.filter) {
|
||||
case EVFILT_PROC: /* target has exited */
|
||||
if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
|
||||
FLAG_HAS_PIPE))
|
||||
runstate = pmcstat_close_log();
|
||||
else
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
runstate = pmcstat_close_log();
|
||||
do_print = 1;
|
||||
break;
|
||||
|
||||
case EVFILT_READ: /* log file data is present */
|
||||
if (kev.ident == (unsigned)fileno(stdin)) {
|
||||
if (kev.ident == (unsigned)fileno(stdin) &&
|
||||
(args.pa_flags & FLAG_DO_TOP)) {
|
||||
if (pmcstat_keypress_log())
|
||||
runstate = pmcstat_close_log();
|
||||
} else
|
||||
@ -1370,15 +1364,8 @@ main(int argc, char **argv)
|
||||
* of its targets, or if logfile
|
||||
* writes encounter an error.
|
||||
*/
|
||||
if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
|
||||
FLAG_HAS_PIPE)) {
|
||||
runstate = pmcstat_close_log();
|
||||
if (args.pa_flags &
|
||||
(FLAG_DO_PRINT|FLAG_DO_ANALYSIS))
|
||||
pmcstat_process_log();
|
||||
}
|
||||
runstate = pmcstat_close_log();
|
||||
do_print = 1; /* print PMCs at exit */
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
} else if (kev.ident == SIGINT) {
|
||||
/* Kill the child process if we started it */
|
||||
if (args.pa_flags & FLAG_HAS_COMMANDLINE)
|
||||
@ -1386,7 +1373,7 @@ main(int argc, char **argv)
|
||||
/* Close the pipe to self, if present. */
|
||||
if (args.pa_flags & FLAG_HAS_PIPE)
|
||||
(void) close(pipefd[READPIPEFD]);
|
||||
runstate = PMCSTAT_FINISHED;
|
||||
runstate = pmcstat_close_log();
|
||||
} else if (kev.ident == SIGWINCH) {
|
||||
if (ioctl(fileno(args.pa_printfile),
|
||||
TIOCGWINSZ, &ws) < 0)
|
||||
|
@ -141,6 +141,7 @@ struct pmcstat_image_hash_list pmcstat_image_hash[PMCSTAT_NHASH];
|
||||
struct pmcstat_process_hash_list pmcstat_process_hash[PMCSTAT_NHASH];
|
||||
|
||||
struct pmcstat_stats pmcstat_stats; /* statistics */
|
||||
int ps_samples_period; /* samples count between top refresh. */
|
||||
|
||||
struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */
|
||||
|
||||
@ -247,7 +248,7 @@ static int pmcstat_string_compute_hash(const char *_string);
|
||||
static void pmcstat_string_initialize(void);
|
||||
static int pmcstat_string_lookup_hash(pmcstat_interned_string _is);
|
||||
static void pmcstat_string_shutdown(void);
|
||||
static void pmcstat_stats_reset(void);
|
||||
static void pmcstat_stats_reset(int _reset_global);
|
||||
|
||||
/*
|
||||
* A simple implementation of interned strings. Each interned string
|
||||
@ -276,7 +277,7 @@ int pmcstat_npmcs;
|
||||
int pmcstat_pause;
|
||||
|
||||
static void
|
||||
pmcstat_stats_reset(void)
|
||||
pmcstat_stats_reset(int reset_global)
|
||||
{
|
||||
struct pmcstat_pmcrecord *pr;
|
||||
|
||||
@ -285,9 +286,11 @@ pmcstat_stats_reset(void)
|
||||
pr->pr_samples = 0;
|
||||
pr->pr_dubious_frames = 0;
|
||||
}
|
||||
ps_samples_period = 0;
|
||||
|
||||
/* Flush global stats. */
|
||||
bzero(&pmcstat_stats, sizeof(struct pmcstat_stats));
|
||||
if (reset_global)
|
||||
bzero(&pmcstat_stats, sizeof(struct pmcstat_stats));
|
||||
}
|
||||
|
||||
/*
|
||||
@ -606,7 +609,7 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image)
|
||||
GElf_Phdr ph;
|
||||
GElf_Shdr sh;
|
||||
enum pmcstat_image_type image_type;
|
||||
char buffer[PATH_MAX];
|
||||
char buffer[PATH_MAX], rtldpath[PATH_MAX];
|
||||
|
||||
assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
|
||||
|
||||
@ -686,9 +689,10 @@ pmcstat_image_get_elf_params(struct pmcstat_image *image)
|
||||
buffer, elf_errmsg(-1));
|
||||
goto done;
|
||||
}
|
||||
snprintf(rtldpath, sizeof(rtldpath), "%s%s",
|
||||
args.pa_fsroot, elfbase + ph.p_offset);
|
||||
image->pi_dynlinkerpath =
|
||||
pmcstat_string_intern(elfbase +
|
||||
ph.p_offset);
|
||||
pmcstat_string_intern(rtldpath);
|
||||
break;
|
||||
case PT_LOAD:
|
||||
if (ph.p_offset == 0)
|
||||
@ -944,11 +948,13 @@ pmcstat_image_addr2line(struct pmcstat_image *image, uintfptr_t addr,
|
||||
int fd;
|
||||
|
||||
if (image->pi_addr2line == NULL) {
|
||||
snprintf(imagepath, sizeof(imagepath), "%s.symbols",
|
||||
snprintf(imagepath, sizeof(imagepath), "%s%s.symbols",
|
||||
args.pa_fsroot,
|
||||
pmcstat_string_unintern(image->pi_fullpath));
|
||||
fd = open(imagepath, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
snprintf(imagepath, sizeof(imagepath), "%s",
|
||||
snprintf(imagepath, sizeof(imagepath), "%s%s",
|
||||
args.pa_fsroot,
|
||||
pmcstat_string_unintern(image->pi_fullpath));
|
||||
} else
|
||||
close(fd);
|
||||
@ -1397,6 +1403,7 @@ pmcstat_analyze_log(void)
|
||||
* bin inside this.
|
||||
*/
|
||||
pmcstat_stats.ps_samples_total++;
|
||||
ps_samples_period++;
|
||||
|
||||
pc = ev.pl_u.pl_s.pl_pc;
|
||||
pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid,
|
||||
@ -1423,6 +1430,7 @@ pmcstat_analyze_log(void)
|
||||
|
||||
case PMCLOG_TYPE_CALLCHAIN:
|
||||
pmcstat_stats.ps_samples_total++;
|
||||
ps_samples_period++;
|
||||
|
||||
cpuflags = ev.pl_u.pl_cc.pl_cpuflags;
|
||||
cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags);
|
||||
@ -1689,8 +1697,15 @@ pmcstat_print_log(void)
|
||||
int
|
||||
pmcstat_close_log(void)
|
||||
{
|
||||
if (pmc_flush_logfile() < 0)
|
||||
err(EX_OSERR, "ERROR: logging failed");
|
||||
/* If a local logfile is configured ask the kernel to stop
|
||||
* and flush data. Kernel will close the file when data is flushed
|
||||
* so keep the status to EXITING.
|
||||
*/
|
||||
if (args.pa_logfd != -1) {
|
||||
if (pmc_flush_logfile() < 0)
|
||||
err(EX_OSERR, "ERROR: logging failed");
|
||||
}
|
||||
|
||||
return (args.pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
|
||||
PMCSTAT_FINISHED);
|
||||
}
|
||||
@ -1707,7 +1722,7 @@ pmcstat_close_log(void)
|
||||
int
|
||||
pmcstat_open_log(const char *path, int mode)
|
||||
{
|
||||
int error, fd;
|
||||
int error, fd, cfd;
|
||||
size_t hlen;
|
||||
const char *p, *errstr;
|
||||
struct addrinfo hints, *res, *res0;
|
||||
@ -1728,7 +1743,7 @@ pmcstat_open_log(const char *path, int mode)
|
||||
*/
|
||||
if (path[0] == '-' && path[1] == '\0')
|
||||
fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
|
||||
else if (mode == PMCSTAT_OPEN_FOR_WRITE && path[0] != '/' &&
|
||||
else if (path[0] != '/' &&
|
||||
path[0] != '.' && strchr(path, ':') != NULL) {
|
||||
|
||||
p = strrchr(path, ':');
|
||||
@ -1757,11 +1772,29 @@ pmcstat_open_log(const char *path, int mode)
|
||||
errstr = strerror(errno);
|
||||
continue;
|
||||
}
|
||||
if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
errstr = strerror(errno);
|
||||
if (mode == PMCSTAT_OPEN_FOR_READ) {
|
||||
if (bind(fd, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
errstr = strerror(errno);
|
||||
(void) close(fd);
|
||||
fd = -1;
|
||||
continue;
|
||||
}
|
||||
listen(fd, 1);
|
||||
cfd = accept(fd, NULL, NULL);
|
||||
(void) close(fd);
|
||||
fd = -1;
|
||||
continue;
|
||||
if (cfd < 0) {
|
||||
errstr = strerror(errno);
|
||||
fd = -1;
|
||||
break;
|
||||
}
|
||||
fd = cfd;
|
||||
} else {
|
||||
if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
|
||||
errstr = strerror(errno);
|
||||
(void) close(fd);
|
||||
fd = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
errstr = NULL;
|
||||
break;
|
||||
@ -1831,9 +1864,8 @@ pmcstat_refresh_top(void)
|
||||
pmcstat_pmcinfilter);
|
||||
|
||||
/* Format samples count. */
|
||||
if (pmcstat_stats.ps_samples_total > 0)
|
||||
v = (pmcpr->pr_samples * 100.0) /
|
||||
pmcstat_stats.ps_samples_total;
|
||||
if (ps_samples_period > 0)
|
||||
v = (pmcpr->pr_samples * 100.0) / ps_samples_period;
|
||||
else
|
||||
v = 0.;
|
||||
v_attrs = PMCSTAT_ATTRPERCENT(v);
|
||||
@ -1870,7 +1902,7 @@ pmcstat_changefilter(void)
|
||||
|
||||
do {
|
||||
pmcr = pmcstat_pmcindex_to_pmcr(pmcstat_pmcinfilter);
|
||||
if (pmcr == pmcr->pr_merge)
|
||||
if (pmcr == NULL || pmcr == pmcr->pr_merge)
|
||||
break;
|
||||
|
||||
pmcstat_pmcinfilter++;
|
||||
@ -1913,7 +1945,7 @@ pmcstat_keypress_log(void)
|
||||
*/
|
||||
if (plugins[args.pa_plugin].pl_shutdown != NULL)
|
||||
plugins[args.pa_plugin].pl_shutdown(NULL);
|
||||
pmcstat_stats_reset();
|
||||
pmcstat_stats_reset(0);
|
||||
if (plugins[args.pa_plugin].pl_init != NULL)
|
||||
plugins[args.pa_plugin].pl_init();
|
||||
|
||||
@ -1934,7 +1966,7 @@ pmcstat_keypress_log(void)
|
||||
} while (plugins[args.pa_plugin].pl_topdisplay == NULL);
|
||||
|
||||
/* Open new plugin. */
|
||||
pmcstat_stats_reset();
|
||||
pmcstat_stats_reset(0);
|
||||
if (plugins[args.pa_plugin].pl_init != NULL)
|
||||
plugins[args.pa_plugin].pl_init();
|
||||
wprintw(w, "switching to plugin %s",
|
||||
@ -1984,7 +2016,7 @@ pmcstat_display_log(void)
|
||||
if (args.pa_topmode == PMCSTAT_TOP_DELTA) {
|
||||
if (plugins[args.pa_plugin].pl_shutdown != NULL)
|
||||
plugins[args.pa_plugin].pl_shutdown(NULL);
|
||||
pmcstat_stats_reset();
|
||||
pmcstat_stats_reset(0);
|
||||
if (plugins[args.pa_plugin].pl_init != NULL)
|
||||
plugins[args.pa_plugin].pl_init();
|
||||
}
|
||||
@ -2128,8 +2160,7 @@ pmcstat_shutdown_logging(void)
|
||||
N, pmcstat_stats.ps_##V); \
|
||||
} while (0)
|
||||
|
||||
if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS) &&
|
||||
(args.pa_flags & FLAG_DO_TOP) == 0) {
|
||||
if (args.pa_verbosity >= 1 && (args.pa_flags & FLAG_DO_ANALYSIS)) {
|
||||
(void) fprintf(args.pa_printfile, "CONVERSION STATISTICS:\n");
|
||||
PRINT("#exec/a.out", exec_aout);
|
||||
PRINT("#exec/elf", exec_elf);
|
||||
|
Loading…
Reference in New Issue
Block a user