X-Git-Url: https://git.whamcloud.com/?p=fs%2Flustre-release.git;a=blobdiff_plain;f=lnet%2Futils%2Fdebug.c;h=418fdbc96cc5b3d3b7438429ff465628bcc1e4b4;hp=b6bfec0702835dc9c6720f6e484ecb24f241e4d2;hb=e7516aebc27066b2ab4e00f8a3c4ff2bd5946339;hpb=cd9c585e8c7bdd6cfd802be64ef277dfd466be17 diff --git a/lnet/utils/debug.c b/lnet/utils/debug.c index b6bfec0..418fdbc 100644 --- a/lnet/utils/debug.c +++ b/lnet/utils/debug.c @@ -24,55 +24,63 @@ */ #define __USE_FILE_OFFSET64 +#define _GNU_SOURCE #include +#ifdef HAVE_NETDB_H #include +#endif #include #include +#ifdef HAVE_SYS_IOCTL_H +#include +#endif +#ifndef _IOWR +#include "ioctl.h" +#endif #include #include #include -#include -#ifndef __CYGWIN__ -# include -#endif #include #include #include #include #include - -#include - -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) -#define BUG() /* workaround for module.h includes */ -#include -#endif +#include #include #include +#include #include "parser.h" +#include + static char rawbuf[8192]; static char *buf = rawbuf; static int max = 8192; -//static int g_pfd = -1; +/*static int g_pfd = -1;*/ static int subsystem_mask = ~0; static int debug_mask = ~0; #define MAX_MARK_SIZE 100 static const char *portal_debug_subsystems[] = - {"undefined", "mdc", "mds", "osc", "ost", "class", "log", "llite", - "rpc", "mgmt", "portals", "libcfs", "socknal", "qswnal", "pinger", - "filter", "ptlbd", "echo", "ldlm", "lov", "gmnal", "router", "cobd", - "ibnal", NULL}; + {"undefined", "mdc", "mds", "osc", + "ost", "class", "log", "llite", + "rpc", "mgmt", "portals", "nal", + "pinger", "filter", "ptlbd", "echo", + "ldlm", "lov", "router", "cobd", + "sm", "asobd", "confobd", "lmv", + "cmobd", "sec", NULL}; static const char *portal_debug_masks[] = - {"trace", "inode", "super", "ext2", "malloc", "cache", "info", "ioctl", - "blocks", "net", "warning", "buffs", "other", "dentry", "portals", - "page", "dlmtrace", "error", "emerg", "ha", "rpctrace", "vfstrace", - "reada", NULL}; + {"trace", "inode", "super", "ext2", + "malloc", "cache", "info", "ioctl", + "blocks", "net", "warning", "buffs", + "other", "dentry", "portals", "page", + "dlmtrace", "error", "emerg", "ha", + "rpctrace", "vfstrace", "reada", "mmap", + "config", "console", "quota", "sec", NULL}; struct debug_daemon_cmd { char *cmd; @@ -82,8 +90,6 @@ struct debug_daemon_cmd { static const struct debug_daemon_cmd portal_debug_daemon_cmd[] = { {"start", DEBUG_DAEMON_START}, {"stop", DEBUG_DAEMON_STOP}, - {"pause", DEBUG_DAEMON_PAUSE}, - {"continue", DEBUG_DAEMON_CONTINUE}, {0, 0} }; @@ -227,230 +233,364 @@ int jt_dbg_list(int argc, char **argv) return 0; } -/* if 'raw' is true, don't strip the debug information from the front of the - * lines */ -static void dump_buffer(FILE *fd, char *buf, int size, int raw) +/* all strings nul-terminated; only the struct and hdr need to be freed */ +struct dbg_line { + struct ptldebug_header *hdr; + char *file; + char *fn; + char *text; + struct list_head chain; +}; + +/* nurr. */ +static void list_add_ordered(struct dbg_line *new, struct list_head *head) { - char *p, *z; - unsigned long subsystem, debug, dropped = 0, kept = 0; + struct list_head *pos; + struct dbg_line *curr; + + list_for_each(pos, head) { + curr = list_entry(pos, struct dbg_line, chain); - while (size) { - p = memchr(buf, '\n', size); - if (!p) + if (curr->hdr->ph_sec < new->hdr->ph_sec) + continue; + if (curr->hdr->ph_sec == new->hdr->ph_sec && + curr->hdr->ph_usec < new->hdr->ph_usec) + continue; + + list_add(&new->chain, pos->prev); + return; + } + list_add_tail(&new->chain, head); +} + +static void print_saved_records(struct list_head *list, FILE *out) +{ + struct list_head *pos, *tmp; + + list_for_each_safe(pos, tmp, list) { + struct dbg_line *line; + struct ptldebug_header *hdr; + + line = list_entry(pos, struct dbg_line, chain); + list_del(&line->chain); + + hdr = line->hdr; + fprintf(out, "%06x:%06x:%u:%u.%06Lu:%u:%u:%u:(%s:%u:%s()) %s", + hdr->ph_subsys, hdr->ph_mask, hdr->ph_cpu_id, + hdr->ph_sec, (unsigned long long)hdr->ph_usec, + hdr->ph_stack, hdr->ph_pid, hdr->ph_extern_pid, + line->file, hdr->ph_line_num, line->fn, line->text); + free(line->hdr); + free(line); + } +} + +static int parse_buffer(FILE *in, FILE *out) +{ + struct dbg_line *line; + struct ptldebug_header *hdr; + char buf[4097], *p; + int rc; + unsigned long dropped = 0, kept = 0; + struct list_head chunk_list; + + CFS_INIT_LIST_HEAD(&chunk_list); + + while (1) { + rc = fread(buf, sizeof(hdr->ph_len), 1, in); + if (rc <= 0) break; - subsystem = strtoul(buf, &z, 16); - debug = strtoul(z + 1, &z, 16); - - z++; - /* for some reason %*s isn't working. */ - *p = '\0'; - if ((subsystem_mask & subsystem) && - (!debug || (debug_mask & debug))) { - if (raw) - fprintf(fd, "%s\n", buf); - else - fprintf(fd, "%s\n", z); - //printf("%s\n", buf); - kept++; - } else { - //fprintf(stderr, "dropping line (%lx:%lx): %s\n", subsystem, debug, buf); + + hdr = (void *)buf; + if (hdr->ph_len == 0) + break; + if (hdr->ph_len > 4094) { + fprintf(stderr, "unexpected large record: %d bytes. " + "aborting.\n", + hdr->ph_len); + break; + } + + if (hdr->ph_flags & PH_FLAG_FIRST_RECORD) { + print_saved_records(&chunk_list, out); + assert(list_empty(&chunk_list)); + } + + rc = fread(buf + sizeof(hdr->ph_len), 1, + hdr->ph_len - sizeof(hdr->ph_len), in); + if (rc <= 0) + break; + + if (hdr->ph_mask && + (!(subsystem_mask & hdr->ph_subsys) || + (!(debug_mask & hdr->ph_mask)))) { dropped++; + continue; + } + + line = malloc(sizeof(*line)); + if (line == NULL) { + fprintf(stderr, "malloc failed; printing accumulated " + "records and exiting.\n"); + break; } - *p = '\n'; - p++; - size -= (p - buf); - buf = p; + + line->hdr = malloc(hdr->ph_len + 1); + if (line->hdr == NULL) { + fprintf(stderr, "malloc failed; printing accumulated " + "records and exiting.\n"); + break; + } + + p = (void *)line->hdr; + memcpy(line->hdr, buf, hdr->ph_len); + p[hdr->ph_len] = '\0'; + + p += sizeof(*hdr); + line->file = p; + p += strlen(line->file) + 1; + line->fn = p; + p += strlen(line->fn) + 1; + line->text = p; + + list_add_ordered(line, &chunk_list); + kept++; } + print_saved_records(&chunk_list, out); + printf("Debug log: %lu lines, %lu kept, %lu dropped.\n", dropped + kept, kept, dropped); + return 0; } int jt_dbg_debug_kernel(int argc, char **argv) { - int rc, raw = 1; - FILE *fd = stdout; - const int databuf_size = (6 << 20); - struct portal_ioctl_data data, *newdata; - char *databuf = NULL; + char filename[4096]; + struct stat st; + int rc, raw = 0, fd; + FILE *in, *out = stdout; if (argc > 3) { fprintf(stderr, "usage: %s [file] [raw]\n", argv[0]); return 0; } - if (argc > 1) { - fd = fopen(argv[1], "w"); - if (fd == NULL) { - fprintf(stderr, "fopen(%s) failed: %s\n", argv[1], - strerror(errno)); - return -1; - } - } - if (argc > 2) + if (argc > 2) { raw = atoi(argv[2]); - - databuf = malloc(databuf_size); - if (!databuf) { - fprintf(stderr, "No memory for buffer.\n"); - goto out; + } else if (argc > 1 && (argv[1][0] == '0' || argv[1][0] == '1')) { + raw = atoi(argv[1]); + argc--; } - memset(&data, 0, sizeof(data)); - data.ioc_plen1 = databuf_size; - data.ioc_pbuf1 = databuf; + /* If we are dumping raw (which means no conversion step to ASCII) + * then dump directly to any supplied filename, otherwise this is + * just a temp file and we dump to the real file at convert time. */ + if (argc > 1 && raw) + strcpy(filename, argv[1]); + else + sprintf(filename, "/tmp/lustre-log.%lu.%u",time(NULL),getpid()); - if (portal_ioctl_pack(&data, &buf, max) != 0) { - fprintf(stderr, "portal_ioctl_pack failed.\n"); - goto out; - } + if (stat(filename, &st) == 0 && S_ISREG(st.st_mode)) + unlink(filename); - rc = l_ioctl(PORTALS_DEV_ID, IOC_PORTAL_GET_DEBUG, buf); - if (rc) { - fprintf(stderr, "IOC_PORTAL_GET_DEBUG failed: %s\n", + fd = open("/proc/sys/portals/dump_kernel", O_WRONLY); + if (fd < 0) { + fprintf(stderr, "open(dump_kernel) failed: %s\n", strerror(errno)); - goto out; + return 1; } - newdata = (struct portal_ioctl_data *)buf; - if (newdata->ioc_size > 0) - dump_buffer(fd, databuf, newdata->ioc_size, raw); + rc = write(fd, filename, strlen(filename)); + if (rc != strlen(filename)) { + fprintf(stderr, "write(%s) failed: %s\n", filename, + strerror(errno)); + close(fd); + return 1; + } + close(fd); - out: - if (databuf) - free(databuf); - if (fd != stdout) - fclose(fd); - return 0; -} + if (raw) + return 0; -int jt_dbg_debug_daemon(int argc, char **argv) -{ - int i, rc; - unsigned int cmd = 0; - FILE *fd = stdout; - struct portal_ioctl_data data; + in = fopen(filename, "r"); + if (in == NULL) { + if (errno == ENOENT) /* no dump file created */ + return 0; - if (argc <= 1) { - fprintf(stderr, "usage: %s [start file <#MB>|stop|pause|" - "continue]\n", argv[0]); - return 0; + fprintf(stderr, "fopen(%s) failed: %s\n", filename, + strerror(errno)); + return 1; } - for (i = 0; portal_debug_daemon_cmd[i].cmd != NULL; i++) { - if (strcasecmp(argv[1], portal_debug_daemon_cmd[i].cmd) == 0) { - cmd = portal_debug_daemon_cmd[i].cmdv; - break; + if (argc > 1) { + out = fopen(argv[1], "w"); + if (out == NULL) { + fprintf(stderr, "fopen(%s) failed: %s\n", argv[1], + strerror(errno)); + fclose(in); + return 1; } } - if (portal_debug_daemon_cmd[i].cmd == NULL) { - fprintf(stderr, "usage: %s [start file <#MB>|stop|pause|" - "continue]\n", argv[0]); + + rc = parse_buffer(in, out); + fclose(in); + if (argc > 1) + fclose(out); + if (rc) { + fprintf(stderr, "parse_buffer failed; leaving tmp file %s " + "behind.\n", filename); + } else { + rc = unlink(filename); + if (rc) + fprintf(stderr, "dumped successfully, but couldn't " + "unlink tmp file %s: %s\n", filename, + strerror(errno)); + } + return rc; +} + +int jt_dbg_debug_file(int argc, char **argv) +{ + int fdin; + int fdout; + FILE *in; + FILE *out = stdout; + int rc; + + if (argc > 3 || argc < 2) { + fprintf(stderr, "usage: %s [output]\n", argv[0]); return 0; } - memset(&data, 0, sizeof(data)); - if (cmd == DEBUG_DAEMON_START) { - if (argc < 3) { - fprintf(stderr, "usage: %s [start file <#MB>|stop|" - "pause|continue]\n", argv[0]); - return 0; - } - if (access(argv[2], F_OK) != 0) { - fd = fopen(argv[2], "w"); - if (fd != NULL) { - fclose(fd); - remove(argv[2]); - goto ok; - } - } - if (access(argv[2], W_OK) == 0) - goto ok; - fprintf(stderr, "fopen(%s) failed: %s\n", argv[2], + + fdin = open(argv[1], O_RDONLY | O_LARGEFILE); + if (fdin == -1) { + fprintf(stderr, "open(%s) failed: %s\n", argv[1], strerror(errno)); - return -1; -ok: - data.ioc_inllen1 = strlen(argv[2]) + 1; - data.ioc_inlbuf1 = argv[2]; - data.ioc_misc = 0; - if (argc == 4) { - unsigned long size; - errno = 0; - size = strtoul(argv[3], NULL, 0); - if (errno) { - fprintf(stderr, "file size(%s): error %s\n", - argv[3], strerror(errno)); - return -1; - } - data.ioc_misc = size; - } - } - data.ioc_count = cmd; - if (portal_ioctl_pack(&data, &buf, max) != 0) { - fprintf(stderr, "portal_ioctl_pack failed.\n"); - return -1; + return 1; } - rc = l_ioctl(PORTALS_DEV_ID, IOC_PORTAL_SET_DAEMON, buf); - if (rc < 0) { - fprintf(stderr, "IOC_PORTAL_SET_DEMON failed: %s\n", + in = fdopen(fdin, "r"); + if (in == NULL) { + fprintf(stderr, "fopen(%s) failed: %s\n", argv[1], + strerror(errno)); + close(fdin); + return 1; + } + if (argc > 2) { + fdout = open(argv[2], + O_CREAT | O_TRUNC | O_WRONLY | O_LARGEFILE, + 0600); + if (fdout == -1) { + fprintf(stderr, "open(%s) failed: %s\n", argv[2], strerror(errno)); - return rc; + fclose(in); + return 1; + } + out = fdopen(fdout, "w"); + if (out == NULL) { + fprintf(stderr, "fopen(%s) failed: %s\n", argv[2], + strerror(errno)); + fclose(in); + close(fdout); + return 1; + } } - return 0; + + rc = parse_buffer(in, out); + + fclose(in); + if (out != stdout) + fclose(out); + + return rc; } -int jt_dbg_debug_file(int argc, char **argv) +static int +dbg_write_cmd(int fd, char *str) { - int rc, fd = -1, raw = 1; - FILE *output = stdout; - char *databuf = NULL; - struct stat statbuf; + int len = strlen(str); + int rc = write(fd, str, len); + + return (rc == len ? 0 : 1); +} - if (argc > 4 || argc < 2) { - fprintf(stderr, "usage: %s [output] [raw]\n", argv[0]); - return 0; +const char debug_daemon_usage[] = "usage: %s {start file [MB]|stop}\n"; +#define DAEMON_FILE "/proc/sys/portals/daemon_file" +int jt_dbg_debug_daemon(int argc, char **argv) +{ + int rc; + int fd; + + if (argc <= 1) { + fprintf(stderr, debug_daemon_usage, argv[0]); + return 1; } - fd = open(argv[1], O_RDONLY); + fd = open(DAEMON_FILE, O_WRONLY); if (fd < 0) { - fprintf(stderr, "fopen(%s) failed: %s\n", argv[1], + fprintf(stderr, "open %s failed: %s\n", DAEMON_FILE, strerror(errno)); return -1; } + + rc = -1; + if (strcasecmp(argv[1], "start") == 0) { + if (argc < 3 || argc > 4 || + (argc == 4 && strlen(argv[3]) > 5)) { + fprintf(stderr, debug_daemon_usage, argv[0]); + goto out; + } + if (argc == 4) { + char buf[12]; + const long min_size = 10; + const long max_size = 20480; + long size; + char *end; + + size = strtoul(argv[3], &end, 0); + if (size < min_size || + size > max_size || + *end != 0) { + fprintf(stderr, "size %s invalid, must be in " + "the range %ld-%ld MB\n", argv[3], + min_size, max_size); + goto out; + } + snprintf(buf, sizeof(buf), "size=%ld", size); + rc = dbg_write_cmd(fd, buf); + + if (rc != 0) { + fprintf(stderr, "set %s failed: %s\n", + buf, strerror(errno)); + goto out; + } + } - rc = fstat(fd, &statbuf); - if (rc < 0) { - fprintf(stderr, "fstat failed: %s\n", strerror(errno)); + rc = dbg_write_cmd(fd, argv[2]); + if (rc != 0) { + fprintf(stderr, "start debug_daemon on %s failed: %s\n", + argv[2], strerror(errno)); + goto out; + } + rc = 0; goto out; } - - if (argc >= 3) { - output = fopen(argv[2], "w"); - if (output == NULL) { - fprintf(stderr, "fopen(%s) failed: %s\n", argv[2], + if (strcasecmp(argv[1], "stop") == 0) { + rc = dbg_write_cmd(fd, "stop"); + if (rc != 0) { + fprintf(stderr, "stopping debug_daemon failed: %s\n", strerror(errno)); goto out; } - } - - if (argc == 4) - raw = atoi(argv[3]); - databuf = mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, - MAP_PRIVATE, fd, 0); - if (databuf == NULL) { - fprintf(stderr, "mmap failed: %s\n", strerror(errno)); + rc = 0; goto out; } - dump_buffer(output, databuf, statbuf.st_size, raw); - - out: - if (databuf) - munmap(databuf, statbuf.st_size); - if (output != stdout) - fclose(output); - if (fd > 0) - close(fd); - return 0; + fprintf(stderr, debug_daemon_usage, argv[0]); + rc = -1; +out: + close(fd); + return rc; } int jt_dbg_clear_debug_buf(int argc, char **argv) @@ -524,13 +664,15 @@ int jt_dbg_mark_debug_buf(int argc, char **argv) static struct mod_paths { char *name, *path; } mod_paths[] = { - {"libcfs", "lustre/portals/libcfs"}, - {"portals", "lustre/portals/portals"}, - {"ksocknal", "lustre/portals/knals/socknal"}, - {"kptlrouter", "lustre/portals/router"}, + {"libcfs", "portals/libcfs"}, + {"portals", "portals/portals"}, + {"ksocknal", "portals/knals/socknal"}, + {"kptlrouter", "portals/router"}, {"lvfs", "lustre/lvfs"}, {"obdclass", "lustre/obdclass"}, {"llog_test", "lustre/obdclass"}, + {"ptlrpcs", "lustre/sec"}, + {"ptlrpcs_gss", "lustre/sec/gss"}, {"ptlrpc", "lustre/ptlrpc"}, {"obdext2", "lustre/obdext2"}, {"ost", "lustre/ost"}, @@ -538,30 +680,37 @@ static struct mod_paths { {"mds", "lustre/mds"}, {"mdc", "lustre/mdc"}, {"llite", "lustre/llite"}, + {"ldiskfs", "lustre/ldiskfs"}, {"smfs", "lustre/smfs"}, {"obdecho", "lustre/obdecho"}, {"ldlm", "lustre/ldlm"}, {"obdfilter", "lustre/obdfilter"}, {"extN", "lustre/extN"}, {"lov", "lustre/lov"}, + {"lmv", "lustre/lmv"}, {"fsfilt_ext3", "lustre/lvfs"}, {"fsfilt_extN", "lustre/lvfs"}, {"fsfilt_reiserfs", "lustre/lvfs"}, {"fsfilt_smfs", "lustre/lvfs"}, + {"fsfilt_ldiskfs", "lustre/lvfs"}, {"mds_ext2", "lustre/mds"}, {"mds_ext3", "lustre/mds"}, {"mds_extN", "lustre/mds"}, {"ptlbd", "lustre/ptlbd"}, {"mgmt_svc", "lustre/mgmt"}, {"mgmt_cli", "lustre/mgmt"}, + {"cobd", "lustre/cobd"}, + {"cmobd", "lustre/cmobd"}, + {"confobd", "lustre/obdclass"}, {NULL, NULL} }; -#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) -int jt_dbg_modules(int argc, char **argv) +static int jt_dbg_modules_2_4(int argc, char **argv) { +#ifdef HAVE_LINUX_VERSION_H +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)) struct mod_paths *mp; - char *path = ".."; + char *path = ""; char *kernel = "linux"; if (argc >= 2) @@ -587,19 +736,22 @@ int jt_dbg_modules(int argc, char **argv) printf("query_module(%s) failed: %s\n", mp->name, strerror(errno)); } else { - printf("add-symbol-file %s/%s/%s.o 0x%0lx\n", path, - mp->path, mp->name, + printf("add-symbol-file %s%s%s/%s.o 0x%0lx\n", path, + path[0] ? "/" : "", mp->path, mp->name, info.addr + sizeof(struct module)); } } return 0; +#endif // Headers are 2.6-only +#endif // !HAVE_LINUX_VERSION_H + return -EINVAL; } -#else -int jt_dbg_modules(int argc, char **argv) + +static int jt_dbg_modules_2_5(int argc, char **argv) { struct mod_paths *mp; - char *path = ".."; + char *path = ""; char *kernel = "linux"; const char *proc = "/proc/modules"; char modname[128], others[128]; @@ -629,14 +781,33 @@ int jt_dbg_modules(int argc, char **argv) break; } if (mp->name) { - printf("add-symbol-file %s/%s/%s.o 0x%0lx\n", path, - mp->path, mp->name, modaddr); + printf("add-symbol-file %s%s%s/%s.o 0x%0lx\n", path, + path[0] ? "/" : "", mp->path, mp->name, modaddr); } } return 0; } -#endif /* linux 2.5 */ + +int jt_dbg_modules(int argc, char **argv) +{ + int rc = 0; + struct utsname sysinfo; + + rc = uname(&sysinfo); + if (rc) { + printf("uname() failed: %s\n", strerror(errno)); + return 0; + } + + if (sysinfo.release[2] > '4') { + return jt_dbg_modules_2_5(argc, argv); + } else { + return jt_dbg_modules_2_4(argc, argv); + } + + return 0; +} int jt_dbg_panic(int argc, char **argv) {