diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-03-20 13:38:29 -0400 |
---|---|---|
committer | Masami Hiramatsu <mhiramat@redhat.com> | 2009-03-20 13:38:29 -0400 |
commit | 04ae1b090781725631ba3477ff77721b012cdaba (patch) | |
tree | 36249f31536ce30c49f66a5afb70b9f3d9bb2ce4 | |
parent | dc6e1d97c43aa173549ad00bb78d7ea86ec90c95 (diff) | |
download | systemtap-steved-04ae1b090781725631ba3477ff77721b012cdaba.tar.gz systemtap-steved-04ae1b090781725631ba3477ff77721b012cdaba.tar.xz systemtap-steved-04ae1b090781725631ba3477ff77721b012cdaba.zip |
PR9821: staprun supports subset of strftime.
Add strftime subset format support for output file name to systemtap.
This format will be evaluated when opening a new output file.
-rw-r--r-- | main.cxx | 4 | ||||
-rw-r--r-- | runtime/staprun/common.c | 119 | ||||
-rw-r--r-- | runtime/staprun/relay.c | 89 | ||||
-rw-r--r-- | runtime/staprun/relay_old.c | 50 | ||||
-rw-r--r-- | runtime/staprun/staprun.h | 6 | ||||
-rw-r--r-- | stap.1.in | 2 | ||||
-rw-r--r-- | staprun.8.in | 4 |
7 files changed, 240 insertions, 34 deletions
@@ -108,7 +108,9 @@ usage (systemtap_session& s, int exitcode) << " " << s.kernel_build_tree << endl << " -m MODULE set probe module name, instead of " << endl << " " << s.module_name << endl - << " -o FILE send script output to file, instead of stdout" << endl + << " -o FILE send script output to file, instead of stdout. This supports" << endl + << " a subset of strftime(3) (%%,%C,%Y,%y,%m,%d,%e,%F,%H,%I,%j,%k," << endl + << " %l,%M,%S,%R,%T,%u,%w) for FILE." << endl << " -c CMD start the probes, run CMD, and exit when it finishes" << endl << " -x PID sets target() to PID" << endl << " -F run as on-file flight recorder with -o." << endl diff --git a/runtime/staprun/common.c b/runtime/staprun/common.c index 194488ef..8200ec9d 100644 --- a/runtime/staprun/common.c +++ b/runtime/staprun/common.c @@ -53,6 +53,113 @@ static char *get_abspath(char *path) return path_buf; } +int stap_strfloctime(char *buf, size_t max, const char *fmt, time_t t) +{ + char *c = buf; + const char *c2 = fmt, *end = buf + max; + int ret, num; + struct tm tm; + if (buf == NULL || fmt == NULL || max <= 1) + return -EINVAL; + localtime_r(&t, &tm); + + while (*c2 != '\0'){ + if (c + 1 >= end) + return -EINVAL; + if (*c2 != '%') { + *c++ = *c2++; + continue; + } + c2++; + switch (*c2++) { + case '%': + *c++ = '%'; + break; + case 'Y': + num = tm.tm_year + 1900; + goto numbering; + case 'y': + num = tm.tm_year % 100; + goto numbering02; + case 'C': + num = ((tm.tm_year + 1900 - 1) / 100) + 1; + goto numbering; + case 'm': + num = tm.tm_mon + 1; + goto numbering02; + case 'd': + num = tm.tm_mday; + goto numbering02; + case 'e': + num = tm.tm_mday; + goto numbering; + case 'F': + ret = snprintf(c, end - c, "%d-%02d-%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + if (ret < 0) return ret; + c += ret; + break; + case 'H': + num = tm.tm_hour; + goto numbering02; + case 'I': + num = tm.tm_hour % 12; + if (num == 0) num = 12; + goto numbering02; + case 'j': + ret = snprintf(c, end - c, "%03d", tm.tm_yday); + if (ret < 0) return ret; + c += ret; + break; + case 'k': + num = tm.tm_hour; + goto numbering; + case 'l': + num = tm.tm_hour % 12; + if (num == 0) num = 12; + goto numbering; + case 'M': + num = tm.tm_min; + goto numbering02; + case 'S': + num = tm.tm_sec; + goto numbering02; + case 'R': + ret = snprintf(c, end - c, "%02d:%02d", + tm.tm_hour, tm.tm_min); + if (ret < 0) return ret; + c += ret; + break; + case 'T': + ret = snprintf(c, end - c, "%02d:%02d:%02d", + tm.tm_hour, tm.tm_min, tm.tm_sec); + if (ret < 0) return ret; + c += ret; + break; + case 'u': + num = tm.tm_wday == 0 ? 7 : tm.tm_wday; + goto numbering; + case 'w': + num = tm.tm_wday; + goto numbering; + default: + return -EINVAL; + } + continue; +numbering: + ret = snprintf(c, end - c, "%d", num); + if (ret < 0) return ret; + c += ret; + continue; +numbering02: + ret = snprintf(c, end - c, "%02d", num); + if (ret < 0) return ret; + c += ret; + } + *c = '\0'; + return c - buf; +} + void parse_args(int argc, char **argv) { int c; @@ -125,11 +232,19 @@ void parse_args(int argc, char **argv) } } if (outfile_name) { + char tmp[PATH_MAX]; + int ret; outfile_name = get_abspath(outfile_name); if (outfile_name == NULL) { err("File name is too long.\n"); usage(argv[0]); } + ret = stap_strfloctime(tmp, PATH_MAX - 18, /* = _cpuNNN.SSSSSSSSSS */ + outfile_name, time(NULL)); + if (ret < 0) { + err("Filename format is invalid or too long.\n"); + usage(argv[0]); + } } if (attach_mod && load_only) { err("You can't specify the '-A' and '-L' options together.\n"); @@ -191,7 +306,9 @@ void usage(char *prog) err(" exit when it does. The '_stp_target' variable\n"); err(" will contain the pid for the command.\n"); err("-x pid Sets the '_stp_target' variable to pid.\n"); - err("-o FILE Send output to FILE.\n"); + err("-o FILE Send output to FILE. This supports a subset of\n"); + err(" strftime(3) (%%%%,%%C,%%Y,%%y,%%m,%%d,%%e,%%F,%%H,%%I\n"); + err(" %%j,%%k,%%l,%%M,%%S,%%R,%%T,%%u,%%w) for FILE.\n"); err("-b buffer size The systemtap module specifies a buffer size.\n"); err(" Setting one here will override that value. The\n"); err(" value should be an integer between 1 and 4095 \n"); diff --git a/runtime/staprun/relay.c b/runtime/staprun/relay.c index 891913b0..50f295b5 100644 --- a/runtime/staprun/relay.c +++ b/runtime/staprun/relay.c @@ -17,6 +17,9 @@ static pthread_t reader[NR_CPUS]; static int relay_fd[NR_CPUS]; static int bulkmode = 0; static volatile int stop_threads = 0; +static time_t *time_backlog[NR_CPUS]; +static int backlog_order=0; +#define BACKLOG_MASK ((1 << backlog_order) - 1) /* * ppoll exists in glibc >= 2.4 @@ -44,20 +47,52 @@ static int ppoll(struct pollfd *fds, nfds_t nfds, } #endif -int make_outfile_name(char *buf, int max, int fnum, int cpu) +int init_backlog(int cpu) { + int order = 0; + if (!fnum_max) + return 0; + while (fnum_max >> order) order++; + if (fnum_max == 1<<(order-1)) order--; + time_backlog[cpu] = (time_t *)calloc(1<<order, sizeof(time_t)); + if (time_backlog[cpu] == NULL) { + _err("Memory allocation failed\n"); + return -1; + } + backlog_order = order; + return 0; +} + +void write_backlog(int cpu, int fnum, time_t t) +{ + time_backlog[cpu][fnum & BACKLOG_MASK] = t; +} + +time_t read_backlog(int cpu, int fnum) +{ + return time_backlog[cpu][fnum & BACKLOG_MASK]; +} + +int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t) +{ + int len; + len = stap_strfloctime(buf, max, outfile_name, t); + if (len < 0) { + err("Invalid FILE name format\n"); + return -1; + } if (bulkmode) { /* special case: for testing we sometimes want to write to /dev/null */ if (strcmp(outfile_name, "/dev/null") == 0) { strcpy(buf, "/dev/null"); } else { - if (snprintf_chk(buf, max, "%s_cpu%d.%d", - outfile_name, cpu, fnum)) + if (snprintf_chk(&buf[len], PATH_MAX - len, + "_cpu%d.%d", cpu, fnum)) return -1; } } else { /* stream mode */ - if (snprintf_chk(buf, max, "%s.%d", outfile_name, fnum)) + if (snprintf_chk(&buf[len], PATH_MAX - len, ".%d", fnum)) return -1; } return 0; @@ -66,19 +101,25 @@ int make_outfile_name(char *buf, int max, int fnum, int cpu) static int open_outfile(int fnum, int cpu, int remove_file) { char buf[PATH_MAX]; + time_t t; if (!outfile_name) { _err("-S is set without -o. Please file a bug report.\n"); return -1; } - if (remove_file) { - /* remove oldest file */ - if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, cpu) < 0) - return -1; - remove(buf); /* don't care */ + time(&t); + if (fnum_max) { + if (remove_file) { + /* remove oldest file */ + if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, + cpu, read_backlog(cpu, fnum - fnum_max)) < 0) + return -1; + remove(buf); /* don't care */ + } + write_backlog(cpu, fnum, t); } - if (make_outfile_name(buf, PATH_MAX, fnum, cpu) < 0) + if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0) return -1; out_fd[cpu] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666); if (out_fd[cpu] < 0) { @@ -178,7 +219,7 @@ static void *reader_thread(void *data) */ int init_relayfs(void) { - int i; + int i, len; struct statfs st; char rqbuf[128]; char buf[PATH_MAX], relay_filebase[PATH_MAX]; @@ -227,9 +268,12 @@ int init_relayfs(void) if (fsize_max) { /* switch file mode */ - for (i = 0; i < ncpus; i++) - if (open_outfile(0, i, 0) < 0) + for (i = 0; i < ncpus; i++) { + if (init_backlog(i) < 0) return -1; + if (open_outfile(0, i, 0) < 0) + return -1; + } } else if (bulkmode) { for (i = 0; i < ncpus; i++) { if (outfile_name) { @@ -237,7 +281,14 @@ int init_relayfs(void) if (strcmp(outfile_name, "/dev/null") == 0) { strcpy(buf, "/dev/null"); } else { - if (sprintf_chk(buf, "%s_%d", outfile_name, i)) + len = stap_strfloctime(buf, PATH_MAX, + outfile_name, time(NULL)); + if (len < 0) { + err("Invalid FILE name format\n"); + return -1; + } + if (snprintf_chk(&buf[len], + PATH_MAX - len, "_%d", i)) return -1; } } else { @@ -256,9 +307,15 @@ int init_relayfs(void) } else { /* stream mode */ if (outfile_name) { - out_fd[0] = open (outfile_name, O_CREAT|O_TRUNC|O_WRONLY, 0666); + len = stap_strfloctime(buf, PATH_MAX, + outfile_name, time(NULL)); + if (len < 0) { + err("Invalid FILE name format\n"); + return -1; + } + out_fd[0] = open (buf, O_CREAT|O_TRUNC|O_WRONLY, 0666); if (out_fd[0] < 0) { - perr("Couldn't open output file %s", outfile_name); + perr("Couldn't open output file %s", buf); return -1; } if (set_clexec(out_fd[i]) < 0) diff --git a/runtime/staprun/relay_old.c b/runtime/staprun/relay_old.c index 25ba93bf..ef8fd0da 100644 --- a/runtime/staprun/relay_old.c +++ b/runtime/staprun/relay_old.c @@ -81,15 +81,20 @@ void close_oldrelayfs(int detach) static int open_oldoutfile(int fnum, int cpu, int remove_file) { char buf[PATH_MAX]; + time_t t; if (outfile_name) { - if (remove_file) { - /* remove oldest file */ - if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, - cpu) < 0) - return -1; - remove(buf); /* don't care */ + time(&t); + if (fnum_max) { + if (remove_file) { + /* remove oldest file */ + if (make_outfile_name(buf, PATH_MAX, fnum - fnum_max, + cpu, read_backlog(cpu, fnum - fnum_max)) < 0) + return -1; + remove(buf); /* don't care */ + } + write_backlog(cpu, fnum, t); } - if (make_outfile_name(buf, PATH_MAX, fnum, cpu) < 0) + if (make_outfile_name(buf, PATH_MAX, fnum, cpu, t) < 0) return -1; } else if (bulkmode) { if (sprintf_chk(buf, "stpd_cpu%d.%d", cpu, fnum)) @@ -143,6 +148,8 @@ static int open_relayfs_files(int cpu, const char *relay_filebase, const char *p } if (fsize_max) { + if (init_backlog(cpu) < 0) + goto err2; if (open_oldoutfile(0, cpu, 0) < 0) goto err2; goto opened; @@ -153,12 +160,18 @@ static int open_relayfs_files(int cpu, const char *relay_filebase, const char *p if (strcmp(outfile_name, "/dev/null") == 0) { strcpy(tmp, "/dev/null"); } else { - if (sprintf_chk(tmp, "%s_%d", outfile_name, cpu)) - goto err1; + int len; + len = stap_strfloctime(tmp, PATH_MAX, outfile_name, time(NULL)); + if (len < 0) { + err("Invalid FILE name format\n"); + goto err2; + } + if (snprintf_chk(&tmp[len], PATH_MAX - len, "_%d", cpu)) + goto err2; } } else { if (sprintf_chk(tmp, "stpd_cpu%d", cpu)) - goto err1; + goto err2; } if((percpu_tmpfile[cpu] = fopen(tmp, "w+")) == NULL) { @@ -334,13 +347,22 @@ int init_oldrelayfs(void) bulkmode = 1; if (!bulkmode) { - if (fsize_max) + int len; + char tmp[PATH_MAX]; + if (fsize_max) { + if (init_backlog(0)) + return -1; return open_oldoutfile(0, 0, 0); - + } if (outfile_name) { - out_fd[0] = open (outfile_name, O_CREAT|O_TRUNC|O_WRONLY, 0666); + len = stap_strfloctime(tmp, PATH_MAX, outfile_name, time(NULL)); + if (len < 0) { + err("Invalid FILE name format\n"); + return -1; + } + out_fd[0] = open (tmp, O_CREAT|O_TRUNC|O_WRONLY, 0666); if (out_fd[0] < 0 || set_clexec(out_fd[0]) < 0) { - perr("Couldn't open output file '%s'", outfile_name); + perr("Couldn't open output file '%s'", tmp); return -1; } } else diff --git a/runtime/staprun/staprun.h b/runtime/staprun/staprun.h index 6d0f9179..acc533b2 100644 --- a/runtime/staprun/staprun.h +++ b/runtime/staprun/staprun.h @@ -119,7 +119,10 @@ int init_oldrelayfs(void); void close_oldrelayfs(int); int write_realtime_data(void *data, ssize_t nb); void setup_signals(void); -int make_outfile_name(char *buf, int max, int fnum, int cpu); +int make_outfile_name(char *buf, int max, int fnum, int cpu, time_t t); +int init_backlog(int cpu); +void write_backlog(int cpu, int fnum, time_t t); +time_t read_backlog(int cpu, int fnum); /* staprun_funcs.c */ void setup_staprun_signals(void); const char *moderror(int err); @@ -131,6 +134,7 @@ void start_symbol_thread(void); void stop_symbol_thread(void); /* common.c functions */ +int stap_strfloctime(char *buf, size_t max, const char *fmt, time_t t); void parse_args(int argc, char **argv); void usage(char *prog); void parse_modpath(const char *); @@ -176,6 +176,8 @@ even if they do not have an explicit probe placed into them. .BI \-o " FILE" Send standard output to named file. In bulk mode, percpu files will start with FILE_ (FILE_cpu with -F) followed by the cpu number. +This supports a subset of strftime(3) (%%, %C, %Y, %y, %m, %d, %e, %F, +%H, %I, %j, %l, %M, %S, %R, %T, %u, %w) for FILE. .TP .BI \-c " CMD" Start the probes, run CMD, and exit when CMD finishes. diff --git a/staprun.8.in b/staprun.8.in index 68b5c947..01ef2320 100644 --- a/staprun.8.in +++ b/staprun.8.in @@ -53,7 +53,9 @@ The '_stp_target' variable will be set to PID. .B \-o FILE Send output to FILE. If the module uses bulk mode, the output will be in percpu files FILE_x(FILE_cpux in backgroud and bulk mode) -where 'x' is the cpu number. +where 'x' is the cpu number. This supports a subset of strftime(3) +(%%, %C, %Y, %y, %m, %d, %e, %F, %H, %I, %j, %l, %M, %S, %R, %T, %u, %w) +for FILE. .TP .B \-b BUFFER_SIZE The systemtap module will specify a buffer size. |