staplog.c - systemtap
Global variables defined
Data types defined
Functions defined
Macros defined
Source code
#if !defined(X86) && !defined(X86_64) && !defined(ALPHA) && !defined(PPC) && \
!defined(IA64) && !defined(PPC64) && !defined(S390) && !defined(S390X) && \
!defined(ARM) && !defined(ARM64)
#if defined(__alpha__)
#define ALPHA
#elif defined(__x86_64__)
#define X86_64
#elif defined(__i386__)
#define X86
#elif defined(__powerpc64__)
#define PPC64
#elif defined(__powerpc__)
#define PPC
#elif defined(__ia64__)
#define IA64
#elif defined(__s390x__)
#define S390X
#elif defined(__s390__)
#define S390
#elif defined(__aarch64__)
#define ARM64
#elif defined(__arm__)
#define ARM
#else
#warn "unknown architecture for crash/staplog support"
#endif
#endif
#include <crash/defs.h>
struct rchan_offsets {
long subbuf_size;
long n_subbufs;
long buf;
long buf_start;
long buf_offset;
long buf_subbufs_produced;
long buf_padding;
};
struct fake_rchan_buf {
void *start;
size_t offset;
size_t subbufs_produced;
size_t *padding;
};
struct fake_rchan {
size_t subbuf_size;
size_t n_subbufs;
};
struct per_cpu_data {
struct fake_rchan_buf buf;
};
static struct rchan_offsets rchan_offsets;
static struct fake_rchan chan;
static struct per_cpu_data per_cpu[NR_CPUS];
static FILE *outfp = NULL;
static char *subbuf = NULL;
static int is_global = 0;
static int old_format = 0;
static int retrieve_all = 0;
void cmd_staplog(void);
void cmd_staplog_cleanup(void);
char *help_staplog[];
char *help_staplog_cleanup[];
static struct command_table_entry command_table[] = {
{"staplog", cmd_staplog, help_staplog, 0},
{"staplog_cleanup", cmd_staplog_cleanup, help_staplog_cleanup, CLEANUP},
{NULL, NULL, NULL, 0},
};
static void get_rchan_offsets(void)
{
rchan_offsets.subbuf_size = MEMBER_OFFSET("rchan", "subbuf_size");
if (rchan_offsets.subbuf_size < 0)
goto ERR;
rchan_offsets.n_subbufs = MEMBER_OFFSET("rchan", "n_subbufs");
if (rchan_offsets.n_subbufs < 0)
goto ERR;
rchan_offsets.buf = MEMBER_OFFSET("rchan", "buf");
if (rchan_offsets.buf < 0)
goto ERR;
rchan_offsets.buf_start = MEMBER_OFFSET("rchan_buf", "start");
if (rchan_offsets.buf_start < 0)
goto ERR;
rchan_offsets.buf_offset = MEMBER_OFFSET("rchan_buf", "offset");
if (rchan_offsets.buf_offset < 0)
goto ERR;
rchan_offsets.buf_subbufs_produced
= MEMBER_OFFSET("rchan_buf", "subbufs_produced");
if (rchan_offsets.buf_subbufs_produced < 0)
goto ERR;
rchan_offsets.buf_padding = MEMBER_OFFSET("rchan_buf", "padding");
if (rchan_offsets.buf_padding < 0)
goto ERR;
return;
ERR:
error(FATAL, "cannot get rchan offset\n");
}
static ulong get_rchan(ulong rchan_addr)
{
ulong rchan;
readmem(rchan_addr, KVADDR, &rchan, sizeof(void*),
"rchan", FAULT_ON_ERROR);
if (old_format == 1) {
readmem(rchan + rchan_offsets.subbuf_size,
KVADDR, &chan.subbuf_size, sizeof(unsigned),
"rchan.subbuf_size", FAULT_ON_ERROR);
readmem(rchan + rchan_offsets.n_subbufs,
KVADDR, &chan.n_subbufs, sizeof(unsigned),
"rchan.n_subbufs", FAULT_ON_ERROR);
} else {
readmem(rchan + rchan_offsets.subbuf_size,
KVADDR, &chan.subbuf_size, sizeof(size_t),
"rchan.subbuf_size", FAULT_ON_ERROR);
readmem(rchan + rchan_offsets.n_subbufs,
KVADDR, &chan.n_subbufs, sizeof(size_t),
"rchan.n_subbufs", FAULT_ON_ERROR);
}
return rchan;
}
static void get_rchan_buf(int cpu, ulong rchan)
{
ulong rchan_buf;
struct per_cpu_data *pcd;
pcd = &per_cpu[cpu];
readmem(rchan + rchan_offsets.buf + sizeof(void*) * cpu,
KVADDR, &rchan_buf, sizeof(void*),
"rchan.buf", FAULT_ON_ERROR);
readmem(rchan_buf + rchan_offsets.buf_start,
KVADDR, &pcd->buf.start, sizeof(void*),
"rchan.buf.start", FAULT_ON_ERROR);
if (old_format == 1) {
readmem(rchan_buf + rchan_offsets.buf_offset,
KVADDR, &pcd->buf.offset, sizeof(unsigned),
"rchan.buf.offset", FAULT_ON_ERROR);
readmem(rchan_buf + rchan_offsets.buf_subbufs_produced,
KVADDR, &pcd->buf.subbufs_produced, sizeof(int32_t),
"rchan.buf.subbufs_produced", FAULT_ON_ERROR);
readmem(rchan_buf + rchan_offsets.buf_padding,
KVADDR, &pcd->buf.padding, sizeof(unsigned*),
"rchan.buf.padding", FAULT_ON_ERROR);
} else {
readmem(rchan_buf + rchan_offsets.buf_offset,
KVADDR, &pcd->buf.offset, sizeof(size_t),
"rchan.buf.offset", FAULT_ON_ERROR);
readmem(rchan_buf + rchan_offsets.buf_subbufs_produced,
KVADDR, &pcd->buf.subbufs_produced, sizeof(size_t),
"rchan.buf.subbufs_produced", FAULT_ON_ERROR);
readmem(rchan_buf + rchan_offsets.buf_padding,
KVADDR, &pcd->buf.padding, sizeof(size_t*),
"rchan.buf.padding", FAULT_ON_ERROR);
}
return;
}
static ulong get_rchan_addr(ulong stp_relay_data)
{
long offset;
if ((offset = MEMBER_OFFSET("_stp_relay_data_type", "flushing")) > 0) {
old_format = 1;
}
if ((offset = MEMBER_OFFSET("_stp_relay_data_type", "rchan")) < 0) {
error(WARNING, "The debuginfo of the trace module hasn't been loaded.\n"
"You may not be able to retrieve the correct trace data.\n");
offset = 0;
}
return (stp_relay_data + (ulong)offset);
}
static int check_global_buffer(ulong rchan)
{
int cpu;
ulong rchan_buf[2];
for (cpu = 0; cpu < 2; cpu++) {
readmem(rchan + rchan_offsets.buf + sizeof(void*) * cpu,
KVADDR, &rchan_buf[cpu], sizeof(void*),
"rchan.buf", FAULT_ON_ERROR);
}
if (rchan_buf[0] == rchan_buf[1])
return 1;
return 0;
}
static void setup_global_data(char *module)
{
int i;
ulong stp_relay_data = 0;
ulong stp_rchan_addr = 0;
ulong rchan;
stp_relay_data = symbol_value_module("_stp_relay_data", module);
if (stp_relay_data == 0) {
error(FATAL,
"Failed to find _stp_relay_data in module '%s'.\n",
module);
}
stp_rchan_addr = get_rchan_addr(stp_relay_data);
if (stp_rchan_addr == 0) {
error(FATAL,
"Failed to find '_stp_relay_data' in module '%s'.\n",
module);
}
rchan = get_rchan(stp_rchan_addr);
for (i = 0; i < kt->cpus; i++)
get_rchan_buf(i, rchan);
if (kt->cpus > 1) {
is_global = check_global_buffer(rchan);
}
return;
}
static void create_output_filename(char *buf, int len, int cpu)
{
if (is_global) {
snprintf(buf, len, "global");
} else {
snprintf(buf, len, "cpu%d", cpu);
}
}
static void create_output_dir(const char *dirname)
{
DIR *dir;
dir = opendir(dirname);
if (dir) {
closedir(dir);
} else {
if (mkdir(dirname, S_IRWXU) < 0) {
error(FATAL, "cannot create log directory '%s\n'", dirname);
}
}
}
static FILE *open_output_file(const char *dname, const char *fname)
{
FILE *filp = NULL;
char *output_file;
output_file = GETBUF(sizeof(char) * (strlen(dname) + strlen(fname) + 2));
if (output_file == NULL) {
error(FATAL,
"cannot allocate memory for logfile name '%s%s'\n",
dname, fname);
}
create_output_dir(dname);
sprintf(output_file,"%s/%s", dname, fname);
filp = fopen(output_file, "w");
if (!filp) {
error(FATAL, "cannot create log file '%s'\n", output_file);
}
FREEBUF(output_file);
return filp;
}
#define MAX_FNAME 128
static void output_cpu_logs(char *dirname)
{
int i;
struct per_cpu_data *pcd;
size_t n, idx, start, end, len;
size_t padding;
char *source, fname[MAX_FNAME + 1];
subbuf = GETBUF(chan.subbuf_size);
if (!subbuf) {
error(FATAL, "cannot allocate memory\n");
}
for (i = 0; i < kt->cpus; i++) {
pcd = &per_cpu[i];
if (pcd->buf.subbufs_produced == 0 && pcd->buf.offset == 0) {
if (is_global == 1) {
error(WARNING, "There is no data in the relay buffer.\n");
break;
} else {
error(WARNING, "[cpu:%d]There is no data in the relay buffer.\n", i);
continue;
}
}
end = pcd->buf.subbufs_produced + 1;
if (pcd->buf.subbufs_produced >= chan.n_subbufs) {
start = end - chan.n_subbufs;
} else {
start = 0;
}
create_output_filename(fname, MAX_FNAME, i);
fprintf(fp, "--- generating '%s/%s' ---\n", dirname, fname);
fprintf(fp, " subbufs ready on relayfs:%ld\n", (long)end);
fprintf(fp, " n_subbufs:%ld, read subbuf from:%ld(%ld) "
"to:%ld(%ld) (offset:0-%ld)\n\n",
(long)chan.n_subbufs,
(long)start,
(long)(start % chan.n_subbufs),
(long)end-1,
(long)((end-1) % chan.n_subbufs),
(long) pcd->buf.offset);
outfp = open_output_file(dirname, fname);
for (n = start; n < end; n++) {
idx = n % chan.n_subbufs;
source = pcd->buf.start + idx * chan.subbuf_size;
if (old_format == 1) {
readmem((ulong)pcd->buf.padding + sizeof(unsigned) * idx,
KVADDR, &padding, sizeof(unsigned),
"padding", FAULT_ON_ERROR);
} else {
readmem((ulong)pcd->buf.padding + sizeof(padding) * idx,
KVADDR, &padding, sizeof(padding),
"padding", FAULT_ON_ERROR);
}
if (n == end - 1) {
len = pcd->buf.offset;
} else {
len = chan.subbuf_size;
}
if (old_format == 1) {
source += sizeof(unsigned int);
len -= sizeof(unsigned int) + padding;
} else {
len -= padding;
}
if (len > 0) {
readmem((ulong)source, KVADDR, subbuf, len,
"subbuf", FAULT_ON_ERROR);
if (fwrite(subbuf, len, 1, outfp) != 1) {
error(FATAL, "cannot write log data\n");
}
}
}
fclose(outfp);
outfp = NULL;
if (retrieve_all == 1 && start != 0) {
strncat(fname, ".may_broken", MAX_FNAME);
fprintf(fp, "--- generating '%s/%s' ---\n", dirname, fname);
fprintf(fp, " read subbuf %ld(%ld) (offset:%ld-%ld)\n",
(long)start-1,
(long)((start-1) % chan.n_subbufs),
(long)pcd->buf.offset,
(long)chan.subbuf_size);
outfp = open_output_file(dirname, fname);
idx = (start - 1) % chan.n_subbufs;
source = pcd->buf.start + idx * chan.subbuf_size +
pcd->buf.offset;
len = chan.subbuf_size - pcd->buf.offset;
if (len) {
readmem((ulong)source, KVADDR, subbuf, len,
"may_broken_subbuf", FAULT_ON_ERROR);
if (fwrite(subbuf, len, 1, outfp) != 1) {
error(FATAL,
"cannot write log data(may_broken)\n");
}
}
fclose(outfp);
outfp = NULL;
}
if (is_global == 1)
break;
}
if (subbuf) {
FREEBUF(subbuf);
subbuf = NULL;
}
return;
}
void cmd_staplog(void)
{
int c;
char *module = NULL;
char *dirname = NULL;
while ((c = getopt(argcnt, args, "+ao:")) != EOF) {
switch (c) {
case 'a':
retrieve_all = 1;
break;
case 'o':
dirname = optarg;
break;
default:
argerrs++;
break;
}
}
module = args[optind];
if (!module || argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (dirname == NULL && module != NULL)
dirname = module;
setup_global_data(module);
assert(dirname);
output_cpu_logs(dirname);
return;
}
void cmd_staplog_cleanup(void)
{
if (outfp) {
fclose(outfp);
outfp = NULL;
}
return;
}
char *help_staplog[] = {
"systemtaplog",
"Retrieve SystemTap log data",
"[-a] [-o dir_name] module_name",
" Retrieve SystemTap's log data and write them to files.\n",
" All valid SystemTap's log data made by the trace module which name",
" is 'module_name' are written into log files. This command starts",
" to retrieve log data from the subbuffer which is next to current",
" written subbuffer. Therefore some old data in the current written",
" subbuffer may not be retrieved. But -a option retrieves these data",
" and write them into another log file which have the special ",
" postfix `.may_broken`.",
" If you don't use -o option, the log files are created in",
" `module_name` directory. The name of each log file is cpu0, cpu1..",
" ...cpuN. This command doesn't change the log data format, but remove",
" only padding.",
"",
" -a Retrieve the old data which is recorded in",
" current written subbuffer and create another files",
" which have the special postfix `.may_broken`",
" for these data.",
" -o file_name Specify the output directory.",
NULL,
};
char *help_staplog_cleanup[] = {
"systemtaplog cleanup (hidden)",
"Cleanup command for staplog",
"",
" This command is called during restore_sanity() prior to each ",
" command prompt to close the files which was opened and failed to",
" close by staplog command.",
NULL,
};
static void __attribute__ ((constructor)) _init(void)
{
get_rchan_offsets();
register_extension(command_table);
return;
}
static void __attribute__ ((destructor)) _fini(void)
{
return;
}