#include <sys/select.h>
#include <search.h>
#include <wordexp.h>
-#if HAVE_MONITOR_LIBS
-#include <json-c/json.h>
-#include <curses.h>
-#endif
#define WORKAROUND_BZ467568 1 /* PR 6964; XXX: autoconf when able */
}
dbug(2, "sigproc %d (%s)\n", signum, strsignal(signum));
if (signum == SIGQUIT) {
- pending_interrupts += 2;
- break;
+ load_only = 1; /* flag for stp_main_loop */
+ pending_interrupts ++;
} else if (signum == SIGINT || signum == SIGHUP || signum == SIGTERM) {
pending_interrupts ++;
- break;
}
}
/* Notify main thread (interrupts select). */
sa.sa_handler = chld_proc;
sigaction(SIGCHLD, &sa, NULL);
-#if HAVE_MONITOR_LIBS
if (monitor)
{
sa.sa_handler = monitor_winch;
sigaction(SIGWINCH, &sa, NULL);
}
-#endif
/* This signal handler is notified from the signal_thread
whenever a interruptable event is detected. It will
int rstatus;
struct sigaction sa;
-#if HAVE_MONITOR_LIBS
if (monitor)
monitor_cleanup();
-#endif
if (exiting)
return;
int rc;
int maxfd;
struct timeval tv;
-#if HAVE_MONITOR_LIBS
struct timespec ts;
-#endif
struct timespec *timeout = NULL;
fd_set fds;
sigset_t blockset, mainset;
rc = send_request(STP_READY, NULL, 0);
if (rc != 0) {
perror ("Unable to send STP_READY");
- cleanup_and_exit (1, rc);
+ cleanup_and_exit(0, rc);
}
flags = fcntl(control_channel, F_GETFL);
pthread_sigmask(SIG_BLOCK, &blockset, &mainset);
}
-#if HAVE_MONITOR_LIBS
/* In monitor mode, we must timeout pselect to poll the monitor
* interface. */
if (monitor)
ts.tv_nsec = 500*1000*1000;
timeout = &ts;
}
-#endif
/* handle messages from control channel */
while (1) {
-#if HAVE_MONITOR_LIBS
if (monitor)
{
monitor_input();
monitor_render();
}
-#endif
if (pending_interrupts) {
int btype = STP_EXIT;
int rc = write(control_channel, &btype, sizeof(btype));
dbug(2, "signal-triggered %d exit rc %d\n", pending_interrupts, rc);
- if (pending_interrupts >= 2) {
- cleanup_and_exit (1, 0);
- }
+ cleanup_and_exit (load_only /* = detach */, 0);
}
-
/* If the runtime does not implement select() on the command
filehandle, we have to poll periodically. The polling interval can
be relatively large, since we don't receive EAGAIN during the
FD_ZERO(&fds);
FD_SET(control_channel, &fds);
maxfd = control_channel;
-#if HAVE_MONITOR_LIBS
if (monitor) {
FD_SET(STDIN_FILENO, &fds);
FD_SET(monitor_pfd[0], &fds);
if (monitor_pfd[0] > maxfd)
maxfd = monitor_pfd[0];
}
-#endif
res = pselect(maxfd + 1, &fds, NULL, NULL, timeout, &mainset);
if (res < 0 && errno != EINTR)
{
if (strncmp(recvbuf.payload.data, "WARNING: ", 9) == 0) {
if (suppress_warnings) break;
if (verbose) { /* don't eliminate duplicates */
- /* trim "WARNING: " */
- warn("%.*s", (int) nb-9, recvbuf.payload.data+9);
+ if (monitor)
+ monitor_remember_output_line (recvbuf.payload.data, nb);
+ else
+ /* trim "WARNING: " */
+ warn("%.*s", (int) nb-9, recvbuf.payload.data+9);
break;
} else { /* eliminate duplicates */
static void *seen = 0;
if (! dupstr) {
/* OOM, should not happen. */
- /* trim "WARNING: " */
- warn("%.*s", (int) nb-9, recvbuf.payload.data+9);
+ if (monitor)
+ monitor_remember_output_line (recvbuf.payload.data, nb);
+ else
+ /* trim "WARNING: " */
+ warn("%.*s", (int) nb-9, recvbuf.payload.data+9);
break;
}
retval = tfind (dupstr, & seen, (int (*)(const void*, const void*))strcmp);
if (! retval) { /* new message */
- /* trim "WARNING: " */
- warn("%.*s", strlen(dupstr)-9, dupstr+9);
+ if (monitor)
+ monitor_remember_output_line (recvbuf.payload.data, nb);
+ else
+ /* trim "WARNING: " */
+ warn("%.*s", strlen(dupstr)-9, dupstr+9);
/* We set a maximum for stored warning messages,
to prevent a misbehaving script/environment
/* Note that "ERROR:" should not be translated, since it is
* part of the module cmd protocol. */
} else if (strncmp(recvbuf.payload.data, "ERROR: ", 7) == 0) {
- /* trim "ERROR: " */
- err("%.*s", (int) nb-7, recvbuf.payload.data+7);
+ if (monitor)
+ monitor_remember_output_line (recvbuf.payload.data, nb);
+ else
+ /* trim "ERROR: " */
+ err("%.*s", (int) nb-7, recvbuf.payload.data+7);
error_detected = 1;
} else { /* neither warning nor error */
- eprintf("%.*s", (int) nb, recvbuf.payload.data);
+ if (monitor)
+ monitor_remember_output_line (recvbuf.payload.data, nb);
+ else
+ eprintf("%.*s", (int) nb, recvbuf.payload.data);
}
break;
case STP_EXIT:
{
/* module asks us to unload it and exit */
dbug(2, "got STP_EXIT\n");
- cleanup_and_exit(0, error_detected);
+ if (monitor)
+ monitor_exited();
+ else
+ cleanup_and_exit(0, error_detected);
+ /* monitor mode exit handled elsewhere, later. */
break;
}
case STP_REQUEST_EXIT:
rc = send_request(STP_START, &ts, sizeof(ts));
if (rc != 0) {
perror ("Unable to send STP_START");
- cleanup_and_exit (1, rc);
+ cleanup_and_exit(0, rc);
}
if (load_only)
cleanup_and_exit(1, 0);
#define MAX_COLS 256
#define MAX_DATA 8192
#define MAX_HISTORY 8192
+#define MAX_LINELENGTH 4096
#define MIN(X,Y) (((X) < (Y)) ? (X) : (Y))
#define MAX(X,Y) (((X) > (Y)) ? (X) : (Y))
typedef struct History_Queue
{
- char *buf[MAX_HISTORY];
- size_t allocated[MAX_HISTORY];
- int front;
- int back;
+ char *lines[MAX_HISTORY]; /* each malloc/strdup'd(). */
+ char linebuf[MAX_LINELENGTH]; /* beyond this length, we cut up lines */
+ int linebuf_ptr; /* index into linebuf[] where next line piece should be read */
+ int oldest;
+ int newest;
int count;
} History_Queue;
static enum state
{
normal,
+ exited,
insert,
- help
+ help,
+ exited_help,
} monitor_state;
static History_Queue h_queue;
static time_t elapsed_time = 0;
static time_t start_time = 0;
static int resized = 0;
-static int input = 0;
+static int input = 0; /* fresh input received in monitor_input() */
static int rendered = 0;
/* Forward declarations */
void monitor_render(void)
{
FILE *monitor_fp;
- FILE *output_fp;
char path[PATH_MAX];
time_t current_time = time(NULL);
int monitor_x, monitor_y, max_cols, max_rows, cur_y, cur_x;
-
+
if (resized)
handle_resize();
- output_fp = fdopen(monitor_pfd[0], "r");
-
- /* Render normal systemtap output */
- if (output_fp && monitor_set)
- {
- int i;
- int bytes;
-
- while ((bytes = getline(&h_queue.buf[h_queue.back],
- &h_queue.allocated[h_queue.back], output_fp)) != -1)
- {
- h_queue.back = (h_queue.back+1) % MAX_HISTORY;
- if (h_queue.count < MAX_HISTORY)
- h_queue.count++;
- else
- h_queue.front = (h_queue.front+1) % MAX_HISTORY;
- }
-
- wclear(monitor_output);
- for (i = 0; i < h_queue.count-output_scroll; i++)
- wprintw(monitor_output, "%s", h_queue.buf[(h_queue.front+i) % MAX_HISTORY]);
-
- wrefresh(monitor_output);
- }
+ /* Render previously recorded output */
+ wclear(monitor_output);
+ getmaxyx(monitor_output, monitor_y, monitor_x);
+ for (int i = 0; i < MIN(monitor_y, h_queue.count-output_scroll); i++)
+ wprintw(monitor_output, "%s", h_queue.lines[(h_queue.oldest+i) % MAX_HISTORY]);
+ wrefresh(monitor_output);
if (!input && rendered && (elapsed_time = current_time - start_time) < monitor_interval)
return;
mvwprintw(monitor_status, max_rows-1, 0, "press h to go back\n");
wrefresh(monitor_status);
}
- else
+ if (monitor_state == exited_help)
+ {
+ /* Render help page */
+ rendered = 0;
+ wclear(monitor_status);
+ wprintw(monitor_status, "EXITED MONITOR MODE COMMANDS\n");
+ wprintw(monitor_status, "h - Display help page.\n");
+ wprintw(monitor_status, "s - Rotate sort columns for probes.\n");
+ wprintw(monitor_status, "q - Quit script.\n");
+ wprintw(monitor_status, "j/DownArrow - Scroll down the probe list.\n");
+ wprintw(monitor_status, "k/UpArrow - Scroll up the probe list.\n");
+ wprintw(monitor_status, "d/PageDown - Scroll down the output by one page.\n");
+ wprintw(monitor_status, "u/PageUp - Scroll up the probe list by one page.\n");
+ mvwprintw(monitor_status, max_rows-1, 0, "press h to go back\n");
+ wrefresh(monitor_status);
+ }
+ else if (monitor_state == normal)
{
/* Render monitor mode statistics */
if (sprintf_chk(path, "/proc/systemtap/%s/monitor_status", modname))
if (monitor_state == insert)
mvwprintw(monitor_status, max_rows-1, 0, "enter probe index: %s\n", probe);
- else
+ else if (monitor_state == normal)
mvwprintw(monitor_status, max_rows-1, 0,
"press h for help\n");
wmove(monitor_status, 0, 0);
json_object_put(jso);
}
}
+ else /* exited? */
+ {
+ mvwprintw(monitor_status, max_rows-1, 0,
+ "EXITED: press h for help, q to quit\n");
+ wrefresh(monitor_status);
+ }
}
+
+
+void monitor_remember_output_line(const char* buf, const size_t bytes)
+{
+ free (h_queue.lines[h_queue.newest]);
+ h_queue.lines[h_queue.newest] = strndup(buf, bytes);
+ h_queue.newest = (h_queue.newest+1) % MAX_HISTORY;
+
+ if (h_queue.count < MAX_HISTORY)
+ h_queue.count++; /* and h_queue.oldest stays at 0 */
+ else
+ h_queue.oldest = (h_queue.oldest+1) % MAX_HISTORY;
+}
+
+
+
+void monitor_exited(void)
+{
+ monitor_state = exited;
+ input = 1;
+}
+
+
void monitor_input(void)
{
static int i = 0;
int ch;
int max_rows, max_cols;
+ /* NB: monitor_pfd[0] is the read side, O_NONBLOCK, of the pipe
+ that collects/serializes all the per-cpu outputs. We can't
+ use stdio calls. */
+
+ /* Collect normal systemtap output */
+ while (monitor_set)
+ {
+ ssize_t bytes = read(monitor_pfd[0],
+ h_queue.linebuf + h_queue.linebuf_ptr,
+ MAX_LINELENGTH - h_queue.linebuf_ptr);
+ if (bytes <= 0)
+ break;
+
+ /* Start scanning the linebuf[] for lines - \n.
+ Plop each one found into the h_queue.lines[] ring. */
+ char *p = h_queue.linebuf; /* scan position */
+ char *p_end = h_queue.linebuf + h_queue.linebuf_ptr + bytes; /* one past last byte */
+ char *line = p;
+ while (p <= p_end)
+ {
+ if (*p == '\n') /* got a line */
+ {
+ monitor_remember_output_line(line, (p-line)+1); /* strlen, including \n */
+ line = p+1;
+ }
+ p ++;
+ }
+
+ if (line != p)
+ {
+ /* Move trailing partial line (if any) to front of buffer. */
+ memmove (h_queue.linebuf, line, (p_end - line));
+ h_queue.linebuf_ptr = (p_end - line);
+ }
+ else
+ {
+ /* No line found in entire buffer! Pretend it was all one line. */
+ monitor_remember_output_line(line, (p_end - line));
+ h_queue.linebuf_ptr = 0;
+ }
+ }
+
+
switch (monitor_state)
{
- case normal:
+ case normal:
+ case exited:
ch = getch();
switch (ch)
{
write_command("pause", 5);
break;
case 'q':
- write_command("quit", 4);
+ if (monitor_state == exited)
+ cleanup_and_exit(0, 0 /* error_detected unavailable here */ );
+ else
+ write_command("quit", 4);
break;
case 't':
monitor_state = insert;
break;
case 'h':
- monitor_state = help;
+ monitor_state = (monitor_state == exited) ? exited_help : help;
break;
}
if (ch != ERR)
}
break;
case help:
+ case exited_help:
ch = getch();
if(ch == 'h')
- monitor_state = normal;
+ monitor_state = (monitor_state == exited_help) ? exited : normal;
break;
}
}
}
while ((rc = read(relay_fd[cpu], buf, sizeof(buf))) > 0) {
+ int wbytes = rc;
+ char *wbuf = buf;
+
/* Switching file */
pthread_mutex_lock(&mutex[cpu]);
if ((fsize_max && ((wsize + rc) > fsize_max)) ||
}
pthread_mutex_unlock(&mutex[cpu]);
- /* Prevent pipe overflow after closing */
- if (monitor && monitor_end)
- return 0;
- if (write(out_fd[cpu], buf, rc) != rc) {
- if (errno != EPIPE)
- perr("Couldn't write to output %d for cpu %d, exiting.", out_fd[cpu], cpu);
- goto error_out;
- }
- wsize += rc;
+ /* Copy loop. Must repeat write(2) in case of a pipe overflow
+ or other transient fullness. */
+ while (wbytes > 0) {
+ rc = write(out_fd[cpu], wbuf, wbytes);
+ if (rc <= 0) {
+ perr("Couldn't write to output %d for cpu %d, exiting.",
+ out_fd[cpu], cpu);
+ goto error_out;
+ }
+ wbytes -= rc;
+ wbuf += rc;
+ }
+ wsize += wbytes;
}
} while (!stop_threads);
dbug(3, "exiting thread for cpu %d\n", cpu);
perr("Couldn't create pipe");
return -1;
}
- fcntl(monitor_pfd[0], F_SETFL, O_NONBLOCK);
- fcntl(monitor_pfd[1], F_SETFL, O_NONBLOCK);
+ fcntl(monitor_pfd[0], F_SETFL, O_NONBLOCK); /* read end */
+ /* NB: leave write end of pipe normal blocking mode, since
+ that's the same mode as for STDOUT_FILENO. */
+ /* fcntl(monitor_pfd[1], F_SETFL, O_NONBLOCK); */
#ifdef HAVE_F_SETPIPE_SZ
- fcntl(monitor_pfd[1], F_SETPIPE_SZ, 8*65536);
+ /* Make it less likely for the pipe to be full. */
+ /* fcntl(monitor_pfd[1], F_SETPIPE_SZ, 8*65536); */
#endif
monitor_set = 1;
out_fd[avail_cpus[0]] = monitor_pfd[1];
void closefrom(int lowfd);
/* monitor.c function */
+#ifdef HAVE_MONITOR_LIBS
void monitor_winch(int signum);
void monitor_setup(void);
void monitor_cleanup(void);
void monitor_render(void);
void monitor_input(void);
+void monitor_exited(void);
+void monitor_remember_output_line(const char* buf, const size_t bytes);
+#else
+inline void monitor_winch(int signum) {(void) signum;}
+inline void monitor_setup(void) {}
+inline void monitor_cleanup(void) {}
+inline void monitor_render(void) {}
+inline void monitor_input(void) {}
+inline void monitor_exited(void) {}
+inline void monitor_remember_output_line(const char* buf, const size_t bytes) {(void)buf; (void)bytes;}
+#endif
/*
* variables