From 5578f58bdc65d8a2d49b34be21f979bd1fcb0093 Mon Sep 17 00:00:00 2001 From: Boris Protopopov Date: Tue, 30 Jun 2015 17:47:15 -0400 Subject: Add a script to display SPL slab cache statistics Useful when looking for the info on ZFS/SPL related memory consumption. Signed-off-by: Boris Protopopov Signed-off-by: Brian Behlendorf Closes #460 --- cmd/Makefile.am | 12 +- cmd/splat.c | 836 ------------------------------------------------ cmd/splat.h | 70 ---- cmd/splat/Makefile.am | 11 + cmd/splat/splat.c | 836 ++++++++++++++++++++++++++++++++++++++++++++++++ cmd/splat/splat.h | 70 ++++ cmd/splslab/Makefile.am | 2 + cmd/splslab/splslab.py | 202 ++++++++++++ configure.ac | 2 + rpm/generic/spl.spec.in | 1 + 10 files changed, 1125 insertions(+), 917 deletions(-) delete mode 100644 cmd/splat.c delete mode 100644 cmd/splat.h create mode 100644 cmd/splat/Makefile.am create mode 100644 cmd/splat/splat.c create mode 100644 cmd/splat/splat.h create mode 100644 cmd/splslab/Makefile.am create mode 100755 cmd/splslab/splslab.py diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 01afdcf25..63a3c76f9 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,11 +1 @@ -include $(top_srcdir)/config/Rules.am - -DEFAULT_INCLUDES += \ - -I$(top_srcdir)/lib - -sbin_PROGRAMS = splat - -splat_SOURCES = splat.c -splat_LDFLAGS = $(top_builddir)/lib/libcommon.la - -EXTRA_DIST = splat.h +SUBDIRS = splat splslab diff --git a/cmd/splat.c b/cmd/splat.c deleted file mode 100644 index 92962393d..000000000 --- a/cmd/splat.c +++ /dev/null @@ -1,836 +0,0 @@ -/*****************************************************************************\ - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf . - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see . - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see . - ***************************************************************************** - * Solaris Porting LAyer Tests (SPLAT) User Space Interface. -\*****************************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "splat.h" - -#undef ioctl - -static const char shortOpts[] = "hvlat:xc"; -static const struct option longOpts[] = { - { "help", no_argument, 0, 'h' }, - { "verbose", no_argument, 0, 'v' }, - { "list", no_argument, 0, 'l' }, - { "all", no_argument, 0, 'a' }, - { "test", required_argument, 0, 't' }, - { "exit", no_argument, 0, 'x' }, - { "nocolor", no_argument, 0, 'c' }, - { 0, 0, 0, 0 } -}; - -#define VERSION_SIZE 64 - -static List subsystems; /* Subsystem/tests */ -static int splatctl_fd; /* Control file descriptor */ -static char splat_version[VERSION_SIZE]; /* Kernel version string */ -static char *splat_buffer = NULL; /* Scratch space area */ -static int splat_buffer_size = 0; /* Scratch space size */ - - -static void test_list(List, int); -static int dev_clear(void); -static void subsystem_fini(subsystem_t *); -static void test_fini(test_t *); - - -static int usage(void) { - fprintf(stderr, "usage: splat [hvla] [-t >]\n"); - fprintf(stderr, - " --help -h This help\n" - " --verbose -v Increase verbosity\n" - " --list -l List all tests in all subsystems\n" - " --all -a Run all tests in all subsystems\n" - " --test -t Run 'test' in subsystem 'sub'\n" - " --exit -x Exit on first test error\n" - " --nocolor -c Do not colorize output\n"); - fprintf(stderr, "\n" - "Examples:\n" - " splat -t kmem:all # Runs all kmem tests\n" - " splat -t taskq:0x201 # Run taskq test 0x201\n"); - - return 0; -} - -static subsystem_t *subsystem_init(splat_user_t *desc) -{ - subsystem_t *sub; - - sub = (subsystem_t *)malloc(sizeof(*sub)); - if (sub == NULL) - return NULL; - - memcpy(&sub->sub_desc, desc, sizeof(*desc)); - - sub->sub_tests = list_create((ListDelF)test_fini); - if (sub->sub_tests == NULL) { - free(sub); - return NULL; - } - - return sub; -} - -static void subsystem_fini(subsystem_t *sub) -{ - assert(sub != NULL); - free(sub); -} - -static int subsystem_setup(void) -{ - splat_cfg_t *cfg; - int i, rc, size, cfg_size; - subsystem_t *sub; - splat_user_t *desc; - - /* Aquire the number of registered subsystems */ - cfg_size = sizeof(*cfg); - cfg = (splat_cfg_t *)malloc(cfg_size); - if (cfg == NULL) - return -ENOMEM; - - memset(cfg, 0, cfg_size); - cfg->cfg_magic = SPLAT_CFG_MAGIC; - cfg->cfg_cmd = SPLAT_CFG_SUBSYSTEM_COUNT; - - rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); - if (rc) { - fprintf(stderr, "Ioctl() error 0x%lx / %d: %d\n", - (unsigned long)SPLAT_CFG, cfg->cfg_cmd, errno); - free(cfg); - return rc; - } - - size = cfg->cfg_rc1; - free(cfg); - - /* Based on the newly acquired number of subsystems allocate - * memory to get the descriptive information for them all. */ - cfg_size = sizeof(*cfg) + size * sizeof(splat_user_t); - cfg = (splat_cfg_t *)malloc(cfg_size); - if (cfg == NULL) - return -ENOMEM; - - memset(cfg, 0, cfg_size); - cfg->cfg_magic = SPLAT_CFG_MAGIC; - cfg->cfg_cmd = SPLAT_CFG_SUBSYSTEM_LIST; - cfg->cfg_data.splat_subsystems.size = size; - - rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); - if (rc) { - fprintf(stderr, "Ioctl() error %lu / %d: %d\n", - (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); - free(cfg); - return rc; - } - - /* Add the new subsystems in to the global list */ - size = cfg->cfg_rc1; - for (i = 0; i < size; i++) { - desc = &(cfg->cfg_data.splat_subsystems.descs[i]); - - sub = subsystem_init(desc); - if (sub == NULL) { - fprintf(stderr, "Error initializing subsystem: %s\n", - desc->name); - free(cfg); - return -ENOMEM; - } - - list_append(subsystems, sub); - } - - free(cfg); - return 0; -} - -static void subsystem_list(List l, int indent) -{ - ListIterator i; - subsystem_t *sub; - - fprintf(stdout, - "------------------------------ " - "Available SPLAT Tests " - "------------------------------\n"); - - i = list_iterator_create(l); - - while ((sub = list_next(i))) { - fprintf(stdout, "%*s0x%0*x %-*s ---- %s ----\n", - indent, "", - 4, sub->sub_desc.id, - SPLAT_NAME_SIZE + 7, sub->sub_desc.name, - sub->sub_desc.desc); - test_list(sub->sub_tests, indent + 7); - } - - list_iterator_destroy(i); -} - -static test_t *test_init(subsystem_t *sub, splat_user_t *desc) -{ - test_t *test; - - test = (test_t *)malloc(sizeof(*test)); - if (test == NULL) - return NULL; - - test->test_sub = sub; - memcpy(&test->test_desc, desc, sizeof(*desc)); - - return test; -} - -static void test_fini(test_t *test) -{ - assert(test != NULL); - free(test); -} - -static int test_setup(subsystem_t *sub) -{ - splat_cfg_t *cfg; - int i, rc, size; - test_t *test; - splat_user_t *desc; - - /* Aquire the number of registered tests for the give subsystem */ - cfg = (splat_cfg_t *)malloc(sizeof(*cfg)); - if (cfg == NULL) - return -ENOMEM; - - memset(cfg, 0, sizeof(*cfg)); - cfg->cfg_magic = SPLAT_CFG_MAGIC; - cfg->cfg_cmd = SPLAT_CFG_TEST_COUNT; - cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */ - - rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); - if (rc) { - fprintf(stderr, "Ioctl() error %lu / %d: %d\n", - (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); - free(cfg); - return rc; - } - - size = cfg->cfg_rc1; - free(cfg); - - /* Based on the newly aquired number of tests allocate enough - * memory to get the descriptive information for them all. */ - cfg = (splat_cfg_t *)malloc(sizeof(*cfg) + size*sizeof(splat_user_t)); - if (cfg == NULL) - return -ENOMEM; - - memset(cfg, 0, sizeof(*cfg) + size * sizeof(splat_user_t)); - cfg->cfg_magic = SPLAT_CFG_MAGIC; - cfg->cfg_cmd = SPLAT_CFG_TEST_LIST; - cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */ - cfg->cfg_data.splat_tests.size = size; - - rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); - if (rc) { - fprintf(stderr, "Ioctl() error %lu / %d: %d\n", - (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); - free(cfg); - return rc; - } - - /* Add the new tests in to the relevant subsystems */ - size = cfg->cfg_rc1; - for (i = 0; i < size; i++) { - desc = &(cfg->cfg_data.splat_tests.descs[i]); - - test = test_init(sub, desc); - if (test == NULL) { - fprintf(stderr, "Error initializing test: %s\n", - desc->name); - free(cfg); - return -ENOMEM; - } - - list_append(sub->sub_tests, test); - } - - free(cfg); - return 0; -} - -static test_t *test_copy(test_t *test) -{ - return test_init(test->test_sub, &test->test_desc); -} - -static void test_list(List l, int indent) -{ - ListIterator i; - test_t *test; - - i = list_iterator_create(l); - - while ((test = list_next(i))) - fprintf(stdout, "%*s0x%0*x %-*s %s\n", - indent, "", 04, test->test_desc.id, - SPLAT_NAME_SIZE, test->test_desc.name, - test->test_desc.desc); - - list_iterator_destroy(i); -} - -static test_t *test_find(char *sub_str, char *test_str) -{ - ListIterator si, ti; - subsystem_t *sub; - test_t *test; - __u32 sub_num, test_num; - - /* - * No error checking here because it may not be a number, it's - * perfectly OK for it to be a string. Since we're just using - * it for comparison purposes this is all very safe. - */ - sub_num = strtoul(sub_str, NULL, 0); - test_num = strtoul(test_str, NULL, 0); - - si = list_iterator_create(subsystems); - - while ((sub = list_next(si))) { - - if (strncmp(sub->sub_desc.name, sub_str, SPLAT_NAME_SIZE) && - sub->sub_desc.id != sub_num) - continue; - - ti = list_iterator_create(sub->sub_tests); - - while ((test = list_next(ti))) { - - if (!strncmp(test->test_desc.name, test_str, - SPLAT_NAME_SIZE) || test->test_desc.id==test_num) { - list_iterator_destroy(ti); - list_iterator_destroy(si); - return test; - } - } - - list_iterator_destroy(ti); - } - - list_iterator_destroy(si); - - return NULL; -} - -static int test_add(cmd_args_t *args, test_t *test) -{ - test_t *tmp; - - tmp = test_copy(test); - if (tmp == NULL) - return -ENOMEM; - - list_append(args->args_tests, tmp); - return 0; -} - -static int test_add_all(cmd_args_t *args) -{ - ListIterator si, ti; - subsystem_t *sub; - test_t *test; - int rc; - - si = list_iterator_create(subsystems); - - while ((sub = list_next(si))) { - ti = list_iterator_create(sub->sub_tests); - - while ((test = list_next(ti))) { - if ((rc = test_add(args, test))) { - list_iterator_destroy(ti); - list_iterator_destroy(si); - return rc; - } - } - - list_iterator_destroy(ti); - } - - list_iterator_destroy(si); - - return 0; -} - -static int test_run(cmd_args_t *args, test_t *test) -{ - subsystem_t *sub = test->test_sub; - splat_cmd_t *cmd; - int rc, cmd_size; - - dev_clear(); - - cmd_size = sizeof(*cmd); - cmd = (splat_cmd_t *)malloc(cmd_size); - if (cmd == NULL) - return -ENOMEM; - - memset(cmd, 0, cmd_size); - cmd->cmd_magic = SPLAT_CMD_MAGIC; - cmd->cmd_subsystem = sub->sub_desc.id; - cmd->cmd_test = test->test_desc.id; - cmd->cmd_data_size = 0; /* Unused feature */ - - fprintf(stdout, "%*s:%-*s ", - SPLAT_NAME_SIZE, sub->sub_desc.name, - SPLAT_NAME_SIZE, test->test_desc.name); - fflush(stdout); - rc = ioctl(splatctl_fd, SPLAT_CMD, cmd); - if (args->args_do_color) { - fprintf(stdout, "%s %s\n", rc ? - COLOR_RED "Fail" COLOR_RESET : - COLOR_GREEN "Pass" COLOR_RESET, - rc ? strerror(errno) : ""); - } else { - fprintf(stdout, "%s %s\n", rc ? - "Fail" : "Pass", - rc ? strerror(errno) : ""); - } - fflush(stdout); - free(cmd); - - if ((args->args_verbose == 1 && rc) || - (args->args_verbose >= 2)) { - if ((rc = read(splatctl_fd, splat_buffer, - splat_buffer_size - 1)) < 0) { - fprintf(stdout, "Error reading results: %d\n", rc); - } else { - fprintf(stdout, "\n%s\n", splat_buffer); - fflush(stdout); - } - } - - return rc; -} - -static int tests_run(cmd_args_t *args) -{ - ListIterator i; - test_t *test; - int rc; - - fprintf(stdout, - "------------------------------ " - "Running SPLAT Tests " - "------------------------------\n"); - - i = list_iterator_create(args->args_tests); - - while ((test = list_next(i))) { - rc = test_run(args, test); - if (rc && args->args_exit_on_error) { - list_iterator_destroy(i); - return rc; - } - } - - list_iterator_destroy(i); - return 0; -} - -static int args_parse_test(cmd_args_t *args, char *str) -{ - ListIterator si, ti; - subsystem_t *s; - test_t *t; - char *sub_str, *test_str; - int sub_num, test_num; - int sub_all = 0, test_all = 0; - int rc, flag = 0; - - test_str = strchr(str, ':'); - if (test_str == NULL) { - fprintf(stderr, "Test must be of the " - "form \n"); - return -EINVAL; - } - - sub_str = str; - test_str[0] = '\0'; - test_str = test_str + 1; - - sub_num = strtol(sub_str, NULL, 0); - test_num = strtol(test_str, NULL, 0); - - if (!strncasecmp(sub_str, "all", strlen(sub_str)) || (sub_num == -1)) - sub_all = 1; - - if (!strncasecmp(test_str,"all",strlen(test_str)) || (test_num == -1)) - test_all = 1; - - si = list_iterator_create(subsystems); - - if (sub_all) { - if (test_all) { - /* Add all tests from all subsystems */ - while ((s = list_next(si))) { - ti = list_iterator_create(s->sub_tests); - while ((t = list_next(ti))) { - if ((rc = test_add(args, t))) { - list_iterator_destroy(ti); - goto error_run; - } - } - list_iterator_destroy(ti); - } - } else { - /* Add a specific test from all subsystems */ - while ((s = list_next(si))) { - if ((t=test_find(s->sub_desc.name,test_str))) { - if ((rc = test_add(args, t))) - goto error_run; - - flag = 1; - } - } - - if (!flag) - fprintf(stderr, "No tests '%s:%s' could be " - "found\n", sub_str, test_str); - } - } else { - if (test_all) { - /* Add all tests from a specific subsystem */ - while ((s = list_next(si))) { - if (strncasecmp(sub_str, s->sub_desc.name, - strlen(sub_str))) - continue; - - ti = list_iterator_create(s->sub_tests); - while ((t = list_next(ti))) { - if ((rc = test_add(args, t))) { - list_iterator_destroy(ti); - goto error_run; - } - } - list_iterator_destroy(ti); - } - } else { - /* Add a specific test from a specific subsystem */ - if ((t = test_find(sub_str, test_str))) { - if ((rc = test_add(args, t))) - goto error_run; - } else { - fprintf(stderr, "Test '%s:%s' could not be " - "found\n", sub_str, test_str); - return -EINVAL; - } - } - } - - list_iterator_destroy(si); - - return 0; - -error_run: - list_iterator_destroy(si); - - fprintf(stderr, "Test '%s:%s' not added to run list: %d\n", - sub_str, test_str, rc); - - return rc; -} - -static void args_fini(cmd_args_t *args) -{ - assert(args != NULL); - - if (args->args_tests != NULL) - list_destroy(args->args_tests); - - free(args); -} - -static cmd_args_t * -args_init(int argc, char **argv) -{ - cmd_args_t *args; - int c, rc; - - if (argc == 1) { - usage(); - return (cmd_args_t *) NULL; - } - - /* Configure and populate the args structures */ - args = malloc(sizeof(*args)); - if (args == NULL) - return NULL; - - memset(args, 0, sizeof(*args)); - args->args_verbose = 0; - args->args_do_list = 0; - args->args_do_all = 0; - args->args_do_color = 1; - args->args_exit_on_error = 0; - args->args_tests = list_create((ListDelF)test_fini); - if (args->args_tests == NULL) { - args_fini(args); - return NULL; - } - - while ((c = getopt_long(argc, argv, shortOpts, longOpts, NULL)) != -1){ - switch (c) { - case 'v': args->args_verbose++; break; - case 'l': args->args_do_list = 1; break; - case 'a': args->args_do_all = 1; break; - case 'c': args->args_do_color = 0; break; - case 'x': args->args_exit_on_error = 1; break; - case 't': - if (args->args_do_all) { - fprintf(stderr, "Option -t is " - "useless when used with -a\n"); - args_fini(args); - return NULL; - } - - rc = args_parse_test(args, argv[optind - 1]); - if (rc) { - args_fini(args); - return NULL; - } - break; - case 'h': - case '?': - usage(); - args_fini(args); - return NULL; - default: - fprintf(stderr, "Unknown option '%s'\n", - argv[optind - 1]); - break; - } - } - - return args; -} - -static int -dev_clear(void) -{ - splat_cfg_t cfg; - int rc; - - memset(&cfg, 0, sizeof(cfg)); - cfg.cfg_magic = SPLAT_CFG_MAGIC; - cfg.cfg_cmd = SPLAT_CFG_BUFFER_CLEAR; - cfg.cfg_arg1 = 0; - - rc = ioctl(splatctl_fd, SPLAT_CFG, &cfg); - if (rc) - fprintf(stderr, "Ioctl() error %lu / %d: %d\n", - (unsigned long) SPLAT_CFG, cfg.cfg_cmd, errno); - - lseek(splatctl_fd, 0, SEEK_SET); - - return rc; -} - -static int -dev_size(int size) -{ - splat_cfg_t cfg; - int rc; - - memset(&cfg, 0, sizeof(cfg)); - cfg.cfg_magic = SPLAT_CFG_MAGIC; - cfg.cfg_cmd = SPLAT_CFG_BUFFER_SIZE; - cfg.cfg_arg1 = size; - - rc = ioctl(splatctl_fd, SPLAT_CFG, &cfg); - if (rc) { - fprintf(stderr, "Ioctl() error %lu / %d: %d\n", - (unsigned long) SPLAT_CFG, cfg.cfg_cmd, errno); - return rc; - } - - return cfg.cfg_rc1; -} - -static void -dev_fini(void) -{ - if (splat_buffer) - free(splat_buffer); - - if (splatctl_fd != -1) { - if (close(splatctl_fd) == -1) { - fprintf(stderr, "Unable to close %s: %d\n", - SPLAT_DEV, errno); - } - } -} - -static int -dev_init(void) -{ - ListIterator i; - subsystem_t *sub; - int rc; - - splatctl_fd = open(SPLAT_DEV, O_RDONLY); - if (splatctl_fd == -1) { - fprintf(stderr, "Unable to open %s: %d\n" - "Is the splat module loaded?\n", SPLAT_DEV, errno); - rc = errno; - goto error; - } - - /* Determine kernel module version string */ - memset(splat_version, 0, VERSION_SIZE); - if ((rc = read(splatctl_fd, splat_version, VERSION_SIZE - 1)) == -1) - goto error; - - if ((rc = dev_clear())) - goto error; - - if ((rc = dev_size(0)) < 0) - goto error; - - splat_buffer_size = rc; - splat_buffer = (char *)malloc(splat_buffer_size); - if (splat_buffer == NULL) { - rc = -ENOMEM; - goto error; - } - - memset(splat_buffer, 0, splat_buffer_size); - - /* Determine available subsystems */ - if ((rc = subsystem_setup()) != 0) - goto error; - - /* Determine available tests for all subsystems */ - i = list_iterator_create(subsystems); - - while ((sub = list_next(i))) { - if ((rc = test_setup(sub)) != 0) { - list_iterator_destroy(i); - goto error; - } - } - - list_iterator_destroy(i); - return 0; - -error: - if (splatctl_fd != -1) { - if (close(splatctl_fd) == -1) { - fprintf(stderr, "Unable to close %s: %d\n", - SPLAT_DEV, errno); - } - } - - return rc; -} - -int -init(void) -{ - int rc = 0; - - /* Allocate the subsystem list */ - subsystems = list_create((ListDelF)subsystem_fini); - if (subsystems == NULL) - rc = ENOMEM; - - return rc; -} - -void -fini(void) -{ - list_destroy(subsystems); -} - - -int -main(int argc, char **argv) -{ - cmd_args_t *args = NULL; - int rc = 0; - - /* General init */ - if ((rc = init())) - return rc; - - /* Device specific init */ - if ((rc = dev_init())) - goto out; - - /* Argument init and parsing */ - if ((args = args_init(argc, argv)) == NULL) { - rc = -1; - goto out; - } - - /* Generic kernel version string */ - if (args->args_verbose) - fprintf(stdout, "%s", splat_version); - - /* Print the available test list and exit */ - if (args->args_do_list) { - subsystem_list(subsystems, 0); - goto out; - } - - /* Add all available test to the list of tests to run */ - if (args->args_do_all) { - if ((rc = test_add_all(args))) - goto out; - } - - /* Run all the requested tests */ - if ((rc = tests_run(args))) - goto out; - -out: - if (args != NULL) - args_fini(args); - - dev_fini(); - fini(); - return rc; -} diff --git a/cmd/splat.h b/cmd/splat.h deleted file mode 100644 index 5b838af3a..000000000 --- a/cmd/splat.h +++ /dev/null @@ -1,70 +0,0 @@ -/*****************************************************************************\ - * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. - * Copyright (C) 2007 The Regents of the University of California. - * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). - * Written by Brian Behlendorf . - * UCRL-CODE-235197 - * - * This file is part of the SPL, Solaris Porting Layer. - * For details, see . - * - * The SPL is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * The SPL is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License along - * with the SPL. If not, see . -\*****************************************************************************/ - -#ifndef _SPLAT_H -#define _SPLAT_H - -#include "list.h" -#include "../include/splat-ctl.h" - -#define DEV_NAME "/dev/splatctl" -#define COLOR_BLACK "\033[0;30m" -#define COLOR_DK_GRAY "\033[1;30m" -#define COLOR_BLUE "\033[0;34m" -#define COLOR_LT_BLUE "\033[1;34m" -#define COLOR_GREEN "\033[0;32m" -#define COLOR_LT_GREEN "\033[1;32m" -#define COLOR_CYAN "\033[0;36m" -#define COLOR_LT_CYAN "\033[1;36m" -#define COLOR_RED "\033[0;31m" -#define COLOR_LT_RED "\033[1;31m" -#define COLOR_PURPLE "\033[0;35m" -#define COLOR_LT_PURPLE "\033[1;35m" -#define COLOR_BROWN "\033[0;33m" -#define COLOR_YELLOW "\033[1;33m" -#define COLOR_LT_GRAY "\033[0;37m" -#define COLOR_WHITE "\033[1;37m" -#define COLOR_RESET "\033[0m" - -typedef struct subsystem { - splat_user_t sub_desc; /* Subsystem description */ - List sub_tests; /* Assocated subsystem tests list */ -} subsystem_t; - -typedef struct test { - splat_user_t test_desc; /* Test description */ - subsystem_t *test_sub; /* Parent subsystem */ -} test_t; - -typedef struct cmd_args { - int args_verbose; /* Verbose flag */ - int args_do_list; /* Display all tests flag */ - int args_do_all; /* Run all tests flag */ - int args_do_color; /* Colorize output */ - int args_exit_on_error; /* Exit on first error flag */ - List args_tests; /* Requested subsystems/tests */ -} cmd_args_t; - -#endif /* _SPLAT_H */ - diff --git a/cmd/splat/Makefile.am b/cmd/splat/Makefile.am new file mode 100644 index 000000000..01afdcf25 --- /dev/null +++ b/cmd/splat/Makefile.am @@ -0,0 +1,11 @@ +include $(top_srcdir)/config/Rules.am + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/lib + +sbin_PROGRAMS = splat + +splat_SOURCES = splat.c +splat_LDFLAGS = $(top_builddir)/lib/libcommon.la + +EXTRA_DIST = splat.h diff --git a/cmd/splat/splat.c b/cmd/splat/splat.c new file mode 100644 index 000000000..92962393d --- /dev/null +++ b/cmd/splat/splat.c @@ -0,0 +1,836 @@ +/*****************************************************************************\ + * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. + * Copyright (C) 2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Brian Behlendorf . + * UCRL-CODE-235197 + * + * This file is part of the SPL, Solaris Porting Layer. + * For details, see . + * + * The SPL is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * The SPL is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with the SPL. If not, see . + ***************************************************************************** + * Solaris Porting LAyer Tests (SPLAT) User Space Interface. +\*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "splat.h" + +#undef ioctl + +static const char shortOpts[] = "hvlat:xc"; +static const struct option longOpts[] = { + { "help", no_argument, 0, 'h' }, + { "verbose", no_argument, 0, 'v' }, + { "list", no_argument, 0, 'l' }, + { "all", no_argument, 0, 'a' }, + { "test", required_argument, 0, 't' }, + { "exit", no_argument, 0, 'x' }, + { "nocolor", no_argument, 0, 'c' }, + { 0, 0, 0, 0 } +}; + +#define VERSION_SIZE 64 + +static List subsystems; /* Subsystem/tests */ +static int splatctl_fd; /* Control file descriptor */ +static char splat_version[VERSION_SIZE]; /* Kernel version string */ +static char *splat_buffer = NULL; /* Scratch space area */ +static int splat_buffer_size = 0; /* Scratch space size */ + + +static void test_list(List, int); +static int dev_clear(void); +static void subsystem_fini(subsystem_t *); +static void test_fini(test_t *); + + +static int usage(void) { + fprintf(stderr, "usage: splat [hvla] [-t >]\n"); + fprintf(stderr, + " --help -h This help\n" + " --verbose -v Increase verbosity\n" + " --list -l List all tests in all subsystems\n" + " --all -a Run all tests in all subsystems\n" + " --test -t Run 'test' in subsystem 'sub'\n" + " --exit -x Exit on first test error\n" + " --nocolor -c Do not colorize output\n"); + fprintf(stderr, "\n" + "Examples:\n" + " splat -t kmem:all # Runs all kmem tests\n" + " splat -t taskq:0x201 # Run taskq test 0x201\n"); + + return 0; +} + +static subsystem_t *subsystem_init(splat_user_t *desc) +{ + subsystem_t *sub; + + sub = (subsystem_t *)malloc(sizeof(*sub)); + if (sub == NULL) + return NULL; + + memcpy(&sub->sub_desc, desc, sizeof(*desc)); + + sub->sub_tests = list_create((ListDelF)test_fini); + if (sub->sub_tests == NULL) { + free(sub); + return NULL; + } + + return sub; +} + +static void subsystem_fini(subsystem_t *sub) +{ + assert(sub != NULL); + free(sub); +} + +static int subsystem_setup(void) +{ + splat_cfg_t *cfg; + int i, rc, size, cfg_size; + subsystem_t *sub; + splat_user_t *desc; + + /* Aquire the number of registered subsystems */ + cfg_size = sizeof(*cfg); + cfg = (splat_cfg_t *)malloc(cfg_size); + if (cfg == NULL) + return -ENOMEM; + + memset(cfg, 0, cfg_size); + cfg->cfg_magic = SPLAT_CFG_MAGIC; + cfg->cfg_cmd = SPLAT_CFG_SUBSYSTEM_COUNT; + + rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); + if (rc) { + fprintf(stderr, "Ioctl() error 0x%lx / %d: %d\n", + (unsigned long)SPLAT_CFG, cfg->cfg_cmd, errno); + free(cfg); + return rc; + } + + size = cfg->cfg_rc1; + free(cfg); + + /* Based on the newly acquired number of subsystems allocate + * memory to get the descriptive information for them all. */ + cfg_size = sizeof(*cfg) + size * sizeof(splat_user_t); + cfg = (splat_cfg_t *)malloc(cfg_size); + if (cfg == NULL) + return -ENOMEM; + + memset(cfg, 0, cfg_size); + cfg->cfg_magic = SPLAT_CFG_MAGIC; + cfg->cfg_cmd = SPLAT_CFG_SUBSYSTEM_LIST; + cfg->cfg_data.splat_subsystems.size = size; + + rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); + if (rc) { + fprintf(stderr, "Ioctl() error %lu / %d: %d\n", + (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); + free(cfg); + return rc; + } + + /* Add the new subsystems in to the global list */ + size = cfg->cfg_rc1; + for (i = 0; i < size; i++) { + desc = &(cfg->cfg_data.splat_subsystems.descs[i]); + + sub = subsystem_init(desc); + if (sub == NULL) { + fprintf(stderr, "Error initializing subsystem: %s\n", + desc->name); + free(cfg); + return -ENOMEM; + } + + list_append(subsystems, sub); + } + + free(cfg); + return 0; +} + +static void subsystem_list(List l, int indent) +{ + ListIterator i; + subsystem_t *sub; + + fprintf(stdout, + "------------------------------ " + "Available SPLAT Tests " + "------------------------------\n"); + + i = list_iterator_create(l); + + while ((sub = list_next(i))) { + fprintf(stdout, "%*s0x%0*x %-*s ---- %s ----\n", + indent, "", + 4, sub->sub_desc.id, + SPLAT_NAME_SIZE + 7, sub->sub_desc.name, + sub->sub_desc.desc); + test_list(sub->sub_tests, indent + 7); + } + + list_iterator_destroy(i); +} + +static test_t *test_init(subsystem_t *sub, splat_user_t *desc) +{ + test_t *test; + + test = (test_t *)malloc(sizeof(*test)); + if (test == NULL) + return NULL; + + test->test_sub = sub; + memcpy(&test->test_desc, desc, sizeof(*desc)); + + return test; +} + +static void test_fini(test_t *test) +{ + assert(test != NULL); + free(test); +} + +static int test_setup(subsystem_t *sub) +{ + splat_cfg_t *cfg; + int i, rc, size; + test_t *test; + splat_user_t *desc; + + /* Aquire the number of registered tests for the give subsystem */ + cfg = (splat_cfg_t *)malloc(sizeof(*cfg)); + if (cfg == NULL) + return -ENOMEM; + + memset(cfg, 0, sizeof(*cfg)); + cfg->cfg_magic = SPLAT_CFG_MAGIC; + cfg->cfg_cmd = SPLAT_CFG_TEST_COUNT; + cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */ + + rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); + if (rc) { + fprintf(stderr, "Ioctl() error %lu / %d: %d\n", + (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); + free(cfg); + return rc; + } + + size = cfg->cfg_rc1; + free(cfg); + + /* Based on the newly aquired number of tests allocate enough + * memory to get the descriptive information for them all. */ + cfg = (splat_cfg_t *)malloc(sizeof(*cfg) + size*sizeof(splat_user_t)); + if (cfg == NULL) + return -ENOMEM; + + memset(cfg, 0, sizeof(*cfg) + size * sizeof(splat_user_t)); + cfg->cfg_magic = SPLAT_CFG_MAGIC; + cfg->cfg_cmd = SPLAT_CFG_TEST_LIST; + cfg->cfg_arg1 = sub->sub_desc.id; /* Subsystem of interest */ + cfg->cfg_data.splat_tests.size = size; + + rc = ioctl(splatctl_fd, SPLAT_CFG, cfg); + if (rc) { + fprintf(stderr, "Ioctl() error %lu / %d: %d\n", + (unsigned long) SPLAT_CFG, cfg->cfg_cmd, errno); + free(cfg); + return rc; + } + + /* Add the new tests in to the relevant subsystems */ + size = cfg->cfg_rc1; + for (i = 0; i < size; i++) { + desc = &(cfg->cfg_data.splat_tests.descs[i]); + + test = test_init(sub, desc); + if (test == NULL) { + fprintf(stderr, "Error initializing test: %s\n", + desc->name); + free(cfg); + return -ENOMEM; + } + + list_append(sub->sub_tests, test); + } + + free(cfg); + return 0; +} + +static test_t *test_copy(test_t *test) +{ + return test_init(test->test_sub, &test->test_desc); +} + +static void test_list(List l, int indent) +{ + ListIterator i; + test_t *test; + + i = list_iterator_create(l); + + while ((test = list_next(i))) + fprintf(stdout, "%*s0x%0*x %-*s %s\n", + indent, "", 04, test->test_desc.id, + SPLAT_NAME_SIZE, test->test_desc.name, + test->test_desc.desc); + + list_iterator_destroy(i); +} + +static test_t *test_find(char *sub_str, char *test_str) +{ + ListIterator si, ti; + subsystem_t *sub; + test_t *test; + __u32 sub_num, test_num; + + /* + * No error checking here because it may not be a number, it's + * perfectly OK for it to be a string. Since we're just using + * it for comparison purposes this is all very safe. + */ + sub_num = strtoul(sub_str, NULL, 0); + test_num = strtoul(test_str, NULL, 0); + + si = list_iterator_create(subsystems); + + while ((sub = list_next(si))) { + + if (strncmp(sub->sub_desc.name, sub_str, SPLAT_NAME_SIZE) && + sub->sub_desc.id != sub_num) + continue; + + ti = list_iterator_create(sub->sub_tests); + + while ((test = list_next(ti))) { + + if (!strncmp(test->test_desc.name, test_str, + SPLAT_NAME_SIZE) || test->test_desc.id==test_num) { + list_iterator_destroy(ti); + list_iterator_destroy(si); + return test; + } + } + + list_iterator_destroy(ti); + } + + list_iterator_destroy(si); + + return NULL; +} + +static int test_add(cmd_args_t *args, test_t *test) +{ + test_t *tmp; + + tmp = test_copy(test); + if (tmp == NULL) + return -ENOMEM; + + list_append(args->args_tests, tmp); + return 0; +} + +static int test_add_all(cmd_args_t *args) +{ + ListIterator si, ti; + subsystem_t *sub; + test_t *test; + int rc; + + si = list_iterator_create(subsystems); + + while ((sub = list_next(si))) { + ti = list_iterator_create(sub->sub_tests); + + while ((test = list_next(ti))) { + if ((rc = test_add(args, test))) { + list_iterator_destroy(ti); + list_iterator_destroy(si); + return rc; + } + } + + list_iterator_destroy(ti); + } + + list_iterator_destroy(si); + + return 0; +} + +static int test_run(cmd_args_t *args, test_t *test) +{ + subsystem_t *sub = test->test_sub; + splat_cmd_t *cmd; + int rc, cmd_size; + + dev_clear(); + + cmd_size = sizeof(*cmd); + cmd = (splat_cmd_t *)malloc(cmd_size); + if (cmd == NULL) + return -ENOMEM; + + memset(cmd, 0, cmd_size); + cmd->cmd_magic = SPLAT_CMD_MAGIC; + cmd->cmd_subsystem = sub->sub_desc.id; + cmd->cmd_test = test->test_desc.id; + cmd->cmd_data_size = 0; /* Unused feature */ + + fprintf(stdout, "%*s:%-*s ", + SPLAT_NAME_SIZE, sub->sub_desc.name, + SPLAT_NAME_SIZE, test->test_desc.name); + fflush(stdout); + rc = ioctl(splatctl_fd, SPLAT_CMD, cmd); + if (args->args_do_color) { + fprintf(stdout, "%s %s\n", rc ? + COLOR_RED "Fail" COLOR_RESET : + COLOR_GREEN "Pass" COLOR_RESET, + rc ? strerror(errno) : ""); + } else { + fprintf(stdout, "%s %s\n", rc ? + "Fail" : "Pass", + rc ? strerror(errno) : ""); + } + fflush(stdout); + free(cmd); + + if ((args->args_verbose == 1 && rc) || + (args->args_verbose >= 2)) { + if ((rc = read(splatctl_fd, splat_buffer, + splat_buffer_size - 1)) < 0) { + fprintf(stdout, "Error reading results: %d\n", rc); + } else { + fprintf(stdout, "\n%s\n", splat_buffer); + fflush(stdout); + } + } + + return rc; +} + +static int tests_run(cmd_args_t *args) +{ + ListIterator i; + test_t *test; + int rc; + + fprintf(stdout, + "------------------------------ " + "Running SPLAT Tests " + "------------------------------\n"); + + i = list_iterator_create(args->args_tests); + + while ((test = list_next(i))) { + rc = test_run(args, test); + if (rc && args->args_exit_on_error) { + list_iterator_destroy(i); + return rc; + } + } + + list_iterator_destroy(i); + return 0; +} + +static int args_parse_test(cmd_args_t *args, char *str) +{ + ListIterator si, ti; + subsystem_t *s; + test_t *t; + char *sub_str, *test_str; + int sub_num, test_num; + int sub_all = 0, test_all = 0; + int rc, flag = 0; + + test_str = strchr(str, ':'); + if (test_str == NULL) { + fprintf(stderr, "Test must be of the " + "form \n"); + return -EINVAL; + } + + sub_str = str; + test_str[0] = '\0'; + test_str = test_str + 1; + + sub_num = strtol(sub_str, NULL, 0); + test_num = strtol(test_str, NULL, 0); + + if (!strncasecmp(sub_str, "all", strlen(sub_str)) || (sub_num == -1)) + sub_all = 1; + + if (!strncasecmp(test_str,"all",strlen(test_str)) || (test_num == -1)) + test_all = 1; + + si = list_iterator_create(subsystems); + + if (sub_all) { + if (test_all) { + /* Add all tests from all subsystems */ + while ((s = list_next(si))) { + ti = list_iterator_create(s->sub_tests); + while ((t = list_next(ti))) { + if ((rc = test_add(args, t))) { + list_iterator_destroy(ti); + goto error_run; + } + } + list_iterator_destroy(ti); + } + } else { + /* Add a specific test from all subsystems */ + while ((s = list_next(si))) { + if ((t=test_find(s->sub_desc.name,test_str))) { + if ((rc = test_add(args, t))) + goto error_run; + + flag = 1; + } + } + + if (!flag) + fprintf(stderr, "No tests '%s:%s' could be " + "found\n", sub_str, test_str); + } + } else { + if (test_all) { + /* Add all tests from a specific subsystem */ + while ((s = list_next(si))) { + if (strncasecmp(sub_str, s->sub_desc.name, + strlen(sub_str))) + continue; + + ti = list_iterator_create(s->sub_tests); + while ((t = list_next(ti))) { + if ((rc = test_add(args, t))) { + list_iterator_destroy(ti); + goto error_run; + } + } + list_iterator_destroy(ti); + } + } else { + /* Add a specific test from a specific subsystem */ + if ((t = test_find(sub_str, test_str))) { + if ((rc = test_add(args, t))) + goto error_run; + } else { + fprintf(stderr, "Test '%s:%s' could not be " + "found\n", sub_str, test_str); + return -EINVAL; + } + } + } + + list_iterator_destroy(si); + + return 0; + +error_run: + list_iterator_destroy(si); + + fprintf(stderr, "Test '%s:%s' not added to run list: %d\n", + sub_str, test_str, rc); + + return rc; +} + +static void args_fini(cmd_args_t *args) +{ + assert(args != NULL); + + if (args->args_tests != NULL) + list_destroy(args->args_tests); + + free(args); +} + +static cmd_args_t * +args_init(int argc, char **argv) +{ + cmd_args_t *args; + int c, rc; + + if (argc == 1) { + usage(); + return (cmd_args_t *) NULL; + } + + /* Configure and populate the args structures */ + args = malloc(sizeof(*args)); + if (args == NULL) + return NULL; + + memset(args, 0, sizeof(*args)); + args->args_verbose = 0; + args->args_do_list = 0; + args->args_do_all = 0; + args->args_do_color = 1; + args->args_exit_on_error = 0; + args->args_tests = list_create((ListDelF)test_fini); + if (args->args_tests == NULL) { + args_fini(args); + return NULL; + } + + while ((c = getopt_long(argc, argv, shortOpts, longOpts, NULL)) != -1){ + switch (c) { + case 'v': args->args_verbose++; break; + case 'l': args->args_do_list = 1; break; + case 'a': args->args_do_all = 1; break; + case 'c': args->args_do_color = 0; break; + case 'x': args->args_exit_on_error = 1; break; + case 't': + if (args->args_do_all) { + fprintf(stderr, "Option -t is " + "useless when used with -a\n"); + args_fini(args); + return NULL; + } + + rc = args_parse_test(args, argv[optind - 1]); + if (rc) { + args_fini(args); + return NULL; + } + break; + case 'h': + case '?': + usage(); + args_fini(args); + return NULL; + default: + fprintf(stderr, "Unknown option '%s'\n", + argv[optind - 1]); + break; + } + } + + return args; +} + +static int +dev_clear(void) +{ + splat_cfg_t cfg; + int rc; + + memset(&cfg, 0, sizeof(cfg)); + cfg.cfg_magic = SPLAT_CFG_MAGIC; + cfg.cfg_cmd = SPLAT_CFG_BUFFER_CLEAR; + cfg.cfg_arg1 = 0; + + rc = ioctl(splatctl_fd, SPLAT_CFG, &cfg); + if (rc) + fprintf(stderr, "Ioctl() error %lu / %d: %d\n", + (unsigned long) SPLAT_CFG, cfg.cfg_cmd, errno); + + lseek(splatctl_fd, 0, SEEK_SET); + + return rc; +} + +static int +dev_size(int size) +{ + splat_cfg_t cfg; + int rc; + + memset(&cfg, 0, sizeof(cfg)); + cfg.cfg_magic = SPLAT_CFG_MAGIC; + cfg.cfg_cmd = SPLAT_CFG_BUFFER_SIZE; + cfg.cfg_arg1 = size; + + rc = ioctl(splatctl_fd, SPLAT_CFG, &cfg); + if (rc) { + fprintf(stderr, "Ioctl() error %lu / %d: %d\n", + (unsigned long) SPLAT_CFG, cfg.cfg_cmd, errno); + return rc; + } + + return cfg.cfg_rc1; +} + +static void +dev_fini(void) +{ + if (splat_buffer) + free(splat_buffer); + + if (splatctl_fd != -1) { + if (close(splatctl_fd) == -1) { + fprintf(stderr, "Unable to close %s: %d\n", + SPLAT_DEV, errno); + } + } +} + +static int +dev_init(void) +{ + ListIterator i; + subsystem_t *sub; + int rc; + + splatctl_fd = open(SPLAT_DEV, O_RDONLY); + if (splatctl_fd == -1) { + fprintf(stderr, "Unable to open %s: %d\n" + "Is the splat module loaded?\n", SPLAT_DEV, errno); + rc = errno; + goto error; + } + + /* Determine kernel module version string */ + memset(splat_version, 0, VERSION_SIZE); + if ((rc = read(splatctl_fd, splat_version, VERSION_SIZE - 1)) == -1) + goto error; + + if ((rc = dev_clear())) + goto error; + + if ((rc = dev_size(0)) < 0) + goto error; + + splat_buffer_size = rc; + splat_buffer = (char *)malloc(splat_buffer_size); + if (splat_buffer == NULL) { + rc = -ENOMEM; + goto error; + } + + memset(splat_buffer, 0, splat_buffer_size); + + /* Determine available subsystems */ + if ((rc = subsystem_setup()) != 0) + goto error; + + /* Determine available tests for all subsystems */ + i = list_iterator_create(subsystems); + + while ((sub = list_next(i))) { + if ((rc = test_setup(sub)) != 0) { + list_iterator_destroy(i); + goto error; + } + } + + list_iterator_destroy(i); + return 0; + +error: + if (splatctl_fd != -1) { + if (close(splatctl_fd) == -1) { + fprintf(stderr, "Unable to close %s: %d\n", + SPLAT_DEV, errno); + } + } + + return rc; +} + +int +init(void) +{ + int rc = 0; + + /* Allocate the subsystem list */ + subsystems = list_create((ListDelF)subsystem_fini); + if (subsystems == NULL) + rc = ENOMEM; + + return rc; +} + +void +fini(void) +{ + list_destroy(subsystems); +} + + +int +main(int argc, char **argv) +{ + cmd_args_t *args = NULL; + int rc = 0; + + /* General init */ + if ((rc = init())) + return rc; + + /* Device specific init */ + if ((rc = dev_init())) + goto out; + + /* Argument init and parsing */ + if ((args = args_init(argc, argv)) == NULL) { + rc = -1; + goto out; + } + + /* Generic kernel version string */ + if (args->args_verbose) + fprintf(stdout, "%s", splat_version); + + /* Print the available test list and exit */ + if (args->args_do_list) { + subsystem_list(subsystems, 0); + goto out; + } + + /* Add all available test to the list of tests to run */ + if (args->args_do_all) { + if ((rc = test_add_all(args))) + goto out; + } + + /* Run all the requested tests */ + if ((rc = tests_run(args))) + goto out; + +out: + if (args != NULL) + args_fini(args); + + dev_fini(); + fini(); + return rc; +} diff --git a/cmd/splat/splat.h b/cmd/splat/splat.h new file mode 100644 index 000000000..5b838af3a --- /dev/null +++ b/cmd/splat/splat.h @@ -0,0 +1,70 @@ +/*****************************************************************************\ + * Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC. + * Copyright (C) 2007 The Regents of the University of California. + * Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER). + * Written by Brian Behlendorf . + * UCRL-CODE-235197 + * + * This file is part of the SPL, Solaris Porting Layer. + * For details, see . + * + * The SPL is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * The SPL is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with the SPL. If not, see . +\*****************************************************************************/ + +#ifndef _SPLAT_H +#define _SPLAT_H + +#include "list.h" +#include "../include/splat-ctl.h" + +#define DEV_NAME "/dev/splatctl" +#define COLOR_BLACK "\033[0;30m" +#define COLOR_DK_GRAY "\033[1;30m" +#define COLOR_BLUE "\033[0;34m" +#define COLOR_LT_BLUE "\033[1;34m" +#define COLOR_GREEN "\033[0;32m" +#define COLOR_LT_GREEN "\033[1;32m" +#define COLOR_CYAN "\033[0;36m" +#define COLOR_LT_CYAN "\033[1;36m" +#define COLOR_RED "\033[0;31m" +#define COLOR_LT_RED "\033[1;31m" +#define COLOR_PURPLE "\033[0;35m" +#define COLOR_LT_PURPLE "\033[1;35m" +#define COLOR_BROWN "\033[0;33m" +#define COLOR_YELLOW "\033[1;33m" +#define COLOR_LT_GRAY "\033[0;37m" +#define COLOR_WHITE "\033[1;37m" +#define COLOR_RESET "\033[0m" + +typedef struct subsystem { + splat_user_t sub_desc; /* Subsystem description */ + List sub_tests; /* Assocated subsystem tests list */ +} subsystem_t; + +typedef struct test { + splat_user_t test_desc; /* Test description */ + subsystem_t *test_sub; /* Parent subsystem */ +} test_t; + +typedef struct cmd_args { + int args_verbose; /* Verbose flag */ + int args_do_list; /* Display all tests flag */ + int args_do_all; /* Run all tests flag */ + int args_do_color; /* Colorize output */ + int args_exit_on_error; /* Exit on first error flag */ + List args_tests; /* Requested subsystems/tests */ +} cmd_args_t; + +#endif /* _SPLAT_H */ + diff --git a/cmd/splslab/Makefile.am b/cmd/splslab/Makefile.am new file mode 100644 index 000000000..b18d52d7e --- /dev/null +++ b/cmd/splslab/Makefile.am @@ -0,0 +1,2 @@ +bin_SCRIPTS = splslab.py +EXTRA_DIST = $(bin_SCRIPTS) diff --git a/cmd/splslab/splslab.py b/cmd/splslab/splslab.py new file mode 100755 index 000000000..160fb2776 --- /dev/null +++ b/cmd/splslab/splslab.py @@ -0,0 +1,202 @@ +#!/usr/bin/python + +import sys +import time +import getopt +import re +import signal +from collections import defaultdict + +class Stat: + # flag definitions based on the kmem.h + NOTOUCH = 1 + NODEBUG = 2 + KMEM = 32 + VMEM = 64 + SLAB = 128 + OFFSLAB = 256 + NOEMERGENCY = 512 + DEADLOCKED = 16384 + GROWING = 32768 + REAPING = 65536 + DESTROY = 131072 + + fdefs = { + NOTOUCH : "NTCH", + NODEBUG : "NDBG", + KMEM : "KMEM", + VMEM : "VMEM", + SLAB : "SLAB", + OFFSLAB : "OFSL", + NOEMERGENCY : "NEMG", + DEADLOCKED : "DDLK", + GROWING : "GROW", + REAPING : "REAP", + DESTROY : "DSTR" + } + + def __init__(self, name, flags, size, alloc, slabsize, objsize): + self._name = name + self._flags = self.f2str(flags) + self._size = size + self._alloc = alloc + self._slabsize = slabsize + self._objsize = objsize + + def f2str(self, flags): + fstring = '' + for k in Stat.fdefs.keys(): + if flags & k: + fstring = fstring + Stat.fdefs[k] + '|' + + fstring = fstring[:-1] + return fstring + +class CumulativeStat: + def __init__(self, skey="a"): + self._size = 0 + self._alloc = 0 + self._pct = 0 + self._skey = skey + self._regexp = \ + re.compile('(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+(\w+)\s+'); + self._stats = defaultdict(list) + + # Add another stat to the dictionary and re-calculate the totals + def add(self, s): + key = 0 + if self._skey == "a": + key = s._alloc + else: + key = s._size + self._stats[key].append(s) + self._size = self._size + s._size + self._alloc = self._alloc + s._alloc + if self._size: + self._pct = self._alloc * 100 / self._size + else: + self._pct = 0 + + # Parse the slab info in the procfs + # Calculate cumulative stats + def slab_update(self): + k = [line.strip() for line in open('/proc/spl/kmem/slab')] + + if not k: + sys.stderr.write("No SPL slab stats found\n") + sys.exit(1) + + del k[0:2] + + for s in k: + if not s: + continue + m = self._regexp.match(s) + if m: + self.add(Stat(m.group(1), int(m.group(2),16), int(m.group(3)), + int(m.group(4)), int(m.group(5)), int(m.group(6)))) + else: + sys.stderr.write("Error: unexpected input format\n" % s) + exit(-1) + + def show_header(self): + sys.stdout.write("\n%25s %20s %15s %15s %15s %15s\n\n" % \ + ("cache name", "flags", "size", "alloc", "slabsize", "objsize")) + + # Show up to the number of 'rows' of output sorted in descending order + # by the key specified earlier; if rows == 0, all rows are shown + def show(self, rows): + self.show_header() + i = 1 + done = False + for k in reversed(sorted(self._stats.keys())): + for s in self._stats[k]: + sys.stdout.write("%25s %20s %15d %15d %15d %15d\n" % \ + (s._name, s._flags, s._size, s._alloc, \ + s._slabsize, s._objsize)) + i = i + 1 + if rows != 0 and i > rows: + done = True + break + if done: + break + sys.stdout.write("%25s %36d %15d (%d%%)\n\n" % \ + ("Totals:", self._size, self._alloc, self._pct)) + +def usage(): + cmd = "Usage: splslab.py [-n|--num-rows] number [-s|--sort-by] " + \ + "[interval] [count]"; + sys.stderr.write("%s\n" % cmd) + sys.stderr.write("\t-h : print help\n") + sys.stderr.write("\t-n : --num-rows N : limit output to N top " + + "largest slabs (default: all)\n") + sys.stderr.write("\t-s : --sort-by key : sort output in descending " + + "order by total size (s)\n\t\tor allocated size (a) " + + "(default: a)\n") + sys.stderr.write("\tinterval : repeat every interval seconds\n") + sys.stderr.write("\tcount : output statistics count times and exit\n") + + +def main(): + + rows = 0 + count = 0 + skey = "a" + interval = 1 + + signal.signal(signal.SIGINT, signal.SIG_DFL) + + try: + opts, args = getopt.getopt( + sys.argv[1:], + "n:s:h", + [ + "num-rows", + "sort-by", + "help" + ] + ) + except getopt.error as e: + sys.stderr.write("Error: %s\n" % e.msg) + usage() + exit(-1) + + i = 1 + for opt, arg in opts: + if opt in ('-n', '--num-rows'): + rows = int(arg) + i = i + 2 + elif opt in ('-s', '--sort-by'): + if arg != "s" and arg != "a": + sys.stderr.write("Error: invalid sorting key \"%s\"\n" % arg) + usage() + exit(-1) + skey = arg + i = i + 2 + elif opt in ('-h', '--help'): + usage() + exit(0) + else: + break + + args = sys.argv[i:] + + interval = int(args[0]) if len(args) else interval + count = int(args[1]) if len(args) > 1 else count + + i = 0 + while True: + cs = CumulativeStat(skey) + cs.slab_update() + cs.show(rows) + + i = i + 1 + if count and i >= count: + break + + time.sleep(interval) + + return 0 + +if __name__ == '__main__': + main() diff --git a/configure.ac b/configure.ac index efeb243cb..70735ce2c 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,8 @@ AC_CONFIG_FILES([ man/man5/Makefile lib/Makefile cmd/Makefile + cmd/splat/Makefile + cmd/splslab/Makefile module/Makefile module/spl/Makefile module/splat/Makefile diff --git a/rpm/generic/spl.spec.in b/rpm/generic/spl.spec.in index 48eafe670..43bbd98f4 100644 --- a/rpm/generic/spl.spec.in +++ b/rpm/generic/spl.spec.in @@ -33,6 +33,7 @@ make install DESTDIR=%{?buildroot} %files %doc AUTHORS COPYING DISCLAIMER +%{_bindir}/* %{_sbindir}/* %{_mandir}/man1/* %{_mandir}/man5/* -- cgit v1.2.3