uboot/cmd/log.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2017 Google, Inc
   4 * Written by Simon Glass <sjg@chromium.org>
   5 */
   6
   7#include <common.h>
   8#include <command.h>
   9#include <dm.h>
  10#include <getopt.h>
  11#include <log.h>
  12#include <malloc.h>
  13#include <asm/global_data.h>
  14
  15static char log_fmt_chars[LOGF_COUNT] = "clFLfm";
  16
  17static enum log_level_t parse_log_level(char *const arg)
  18{
  19        enum log_level_t ret;
  20        ulong level;
  21
  22        if (!strict_strtoul(arg, 10, &level)) {
  23                if (level > _LOG_MAX_LEVEL) {
  24                        printf("Only log levels <= %d are supported\n",
  25                               _LOG_MAX_LEVEL);
  26                        return LOGL_NONE;
  27                }
  28                return level;
  29        }
  30
  31        ret = log_get_level_by_name(arg);
  32        if (ret == LOGL_NONE)
  33                printf("Unknown log level \"%s\"\n", arg);
  34        return ret;
  35}
  36
  37static int do_log_level(struct cmd_tbl *cmdtp, int flag, int argc,
  38                        char *const argv[])
  39{
  40        enum log_level_t log_level;
  41
  42        if (argc > 1) {
  43                log_level = parse_log_level(argv[1]);
  44
  45                if (log_level == LOGL_NONE)
  46                        return CMD_RET_FAILURE;
  47                gd->default_log_level = log_level;
  48        } else {
  49                for (log_level = LOGL_FIRST; log_level <= _LOG_MAX_LEVEL;
  50                     log_level++)
  51                        printf("%s%s\n", log_get_level_name(log_level),
  52                               log_level == gd->default_log_level ?
  53                               " (default)" : "");
  54        }
  55
  56        return CMD_RET_SUCCESS;
  57}
  58
  59static int do_log_categories(struct cmd_tbl *cmdtp, int flag, int argc,
  60                             char *const argv[])
  61{
  62        enum log_category_t cat;
  63        const char *name;
  64
  65        for (cat = LOGC_FIRST; cat < LOGC_COUNT; cat++) {
  66                name = log_get_cat_name(cat);
  67                /*
  68                 * Invalid category names (e.g. <invalid> or <missing>) begin
  69                 * with '<'.
  70                 */
  71                if (name[0] == '<')
  72                        continue;
  73                printf("%s\n", name);
  74        }
  75
  76        return CMD_RET_SUCCESS;
  77}
  78
  79static int do_log_drivers(struct cmd_tbl *cmdtp, int flag, int argc,
  80                          char *const argv[])
  81{
  82        struct log_device *ldev;
  83
  84        list_for_each_entry(ldev, &gd->log_head, sibling_node)
  85                printf("%s\n", ldev->drv->name);
  86
  87        return CMD_RET_SUCCESS;
  88}
  89
  90static int do_log_filter_list(struct cmd_tbl *cmdtp, int flag, int argc,
  91                              char *const argv[])
  92{
  93        int opt;
  94        const char *drv_name = "console";
  95        struct getopt_state gs;
  96        struct log_filter *filt;
  97        struct log_device *ldev;
  98
  99        getopt_init_state(&gs);
 100        while ((opt = getopt(&gs, argc, argv, "d:")) > 0) {
 101                switch (opt) {
 102                case 'd':
 103                        drv_name = gs.arg;
 104                        break;
 105                default:
 106                        return CMD_RET_USAGE;
 107                }
 108        }
 109
 110        if (gs.index != argc)
 111                return CMD_RET_USAGE;
 112
 113        ldev = log_device_find_by_name(drv_name);
 114        if (!ldev) {
 115                printf("Could not find log device for \"%s\"\n", drv_name);
 116                return CMD_RET_FAILURE;
 117        }
 118
 119        /*      <3> < 6  > <2+1 + 7 > <      16      > < unbounded... */
 120        printf("num policy level            categories files\n");
 121        list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
 122                printf("%3d %6.6s %s %-7.7s ", filt->filter_num,
 123                       filt->flags & LOGFF_DENY ? "deny" : "allow",
 124                       filt->flags & LOGFF_LEVEL_MIN ? ">=" : "<=",
 125                       log_get_level_name(filt->level));
 126
 127                if (filt->flags & LOGFF_HAS_CAT) {
 128                        int i;
 129
 130                        if (filt->cat_list[0] != LOGC_END)
 131                                printf("%16.16s %s\n",
 132                                       log_get_cat_name(filt->cat_list[0]),
 133                                       filt->file_list ? filt->file_list : "");
 134
 135                        for (i = 1; i < LOGF_MAX_CATEGORIES &&
 136                                    filt->cat_list[i] != LOGC_END; i++)
 137                                printf("%21c %16.16s\n", ' ',
 138                                       log_get_cat_name(filt->cat_list[i]));
 139                } else {
 140                        printf("%16c %s\n", ' ',
 141                               filt->file_list ? filt->file_list : "");
 142                }
 143        }
 144
 145        return CMD_RET_SUCCESS;
 146}
 147
 148static int do_log_filter_add(struct cmd_tbl *cmdtp, int flag, int argc,
 149                             char *const argv[])
 150{
 151        bool level_set = false;
 152        bool print_num = false;
 153        bool type_set = false;
 154        char *file_list = NULL;
 155        const char *drv_name = "console";
 156        int opt, err;
 157        int cat_count = 0;
 158        int flags = 0;
 159        enum log_category_t cat_list[LOGF_MAX_CATEGORIES + 1];
 160        enum log_level_t level = LOGL_MAX;
 161        struct getopt_state gs;
 162
 163        getopt_init_state(&gs);
 164        while ((opt = getopt(&gs, argc, argv, "Ac:d:Df:l:L:p")) > 0) {
 165                switch (opt) {
 166                case 'A':
 167#define do_type() do { \
 168                        if (type_set) { \
 169                                printf("Allow or deny set twice\n"); \
 170                                return CMD_RET_USAGE; \
 171                        } \
 172                        type_set = true; \
 173} while (0)
 174                        do_type();
 175                        break;
 176                case 'c': {
 177                        enum log_category_t cat;
 178
 179                        if (cat_count >= LOGF_MAX_CATEGORIES) {
 180                                printf("Too many categories\n");
 181                                return CMD_RET_FAILURE;
 182                        }
 183
 184                        cat = log_get_cat_by_name(gs.arg);
 185                        if (cat == LOGC_NONE) {
 186                                printf("Unknown category \"%s\"\n", gs.arg);
 187                                return CMD_RET_FAILURE;
 188                        }
 189
 190                        cat_list[cat_count++] = cat;
 191                        break;
 192                }
 193                case 'd':
 194                        drv_name = gs.arg;
 195                        break;
 196                case 'D':
 197                        do_type();
 198                        flags |= LOGFF_DENY;
 199                        break;
 200                case 'f':
 201                        file_list = gs.arg;
 202                        break;
 203                case 'l':
 204#define do_level() do { \
 205                        if (level_set) { \
 206                                printf("Log level set twice\n"); \
 207                                return CMD_RET_USAGE; \
 208                        } \
 209                        level = parse_log_level(gs.arg); \
 210                        if (level == LOGL_NONE) \
 211                                return CMD_RET_FAILURE; \
 212                        level_set = true; \
 213} while (0)
 214                        do_level();
 215                        break;
 216                case 'L':
 217                        do_level();
 218                        flags |= LOGFF_LEVEL_MIN;
 219                        break;
 220                case 'p':
 221                        print_num = true;
 222                        break;
 223                default:
 224                        return CMD_RET_USAGE;
 225                }
 226        }
 227
 228        if (gs.index != argc)
 229                return CMD_RET_USAGE;
 230
 231        cat_list[cat_count] = LOGC_END;
 232        err = log_add_filter_flags(drv_name, cat_count ? cat_list : NULL, level,
 233                                   file_list, flags);
 234        if (err < 0) {
 235                printf("Could not add filter (err = %d)\n", err);
 236                return CMD_RET_FAILURE;
 237        } else if (print_num) {
 238                printf("%d\n", err);
 239        }
 240
 241        return CMD_RET_SUCCESS;
 242}
 243
 244static int do_log_filter_remove(struct cmd_tbl *cmdtp, int flag, int argc,
 245                                char *const argv[])
 246{
 247        bool all = false;
 248        int opt, err;
 249        ulong filter_num;
 250        const char *drv_name = "console";
 251        struct getopt_state gs;
 252
 253        getopt_init_state(&gs);
 254        while ((opt = getopt(&gs, argc, argv, "ad:")) > 0) {
 255                switch (opt) {
 256                case 'a':
 257                        all = true;
 258                        break;
 259                case 'd':
 260                        drv_name = gs.arg;
 261                        break;
 262                default:
 263                        return CMD_RET_USAGE;
 264                }
 265        }
 266
 267        if (all) {
 268                struct log_filter *filt, *tmp_filt;
 269                struct log_device *ldev;
 270
 271                if (gs.index != argc)
 272                        return CMD_RET_USAGE;
 273
 274                ldev = log_device_find_by_name(drv_name);
 275                if (!ldev) {
 276                        printf("Could not find log device for \"%s\"\n",
 277                               drv_name);
 278                        return CMD_RET_FAILURE;
 279                }
 280
 281                list_for_each_entry_safe(filt, tmp_filt, &ldev->filter_head,
 282                                         sibling_node) {
 283                        list_del(&filt->sibling_node);
 284                        free(filt);
 285                }
 286        } else {
 287                if (gs.index + 1 != argc)
 288                        return CMD_RET_USAGE;
 289
 290                if (strict_strtoul(argv[gs.index], 10, &filter_num)) {
 291                        printf("Invalid filter number \"%s\"\n", argv[gs.index]);
 292                        return CMD_RET_FAILURE;
 293                }
 294
 295                err = log_remove_filter(drv_name, filter_num);
 296                if (err) {
 297                        printf("Could not remove filter (err = %d)\n", err);
 298                        return CMD_RET_FAILURE;
 299                }
 300        }
 301
 302        return CMD_RET_SUCCESS;
 303}
 304
 305static int do_log_format(struct cmd_tbl *cmdtp, int flag, int argc,
 306                         char *const argv[])
 307{
 308        int i;
 309
 310        if (argc > 1) {
 311                const char *str = argv[1];
 312
 313                if (!strcmp(str, "default")) {
 314                        gd->log_fmt = log_get_default_format();
 315                } else if (!strcmp(str, "all")) {
 316                        gd->log_fmt = LOGF_ALL;
 317                } else {
 318                        gd->log_fmt = 0;
 319                        for (; *str; str++) {
 320                                char *ptr = strchr(log_fmt_chars, *str);
 321
 322                                if (!ptr) {
 323                                        printf("Invalid log char '%c'\n", *str);
 324                                        return CMD_RET_FAILURE;
 325                                }
 326                                gd->log_fmt |= 1 << (ptr - log_fmt_chars);
 327                        }
 328                }
 329        } else {
 330                printf("Log format: ");
 331                for (i = 0; i < LOGF_COUNT; i++) {
 332                        if (gd->log_fmt & (1 << i))
 333                                printf("%c", log_fmt_chars[i]);
 334                }
 335                printf("\n");
 336        }
 337
 338        return 0;
 339}
 340
 341static int do_log_rec(struct cmd_tbl *cmdtp, int flag, int argc,
 342                      char *const argv[])
 343{
 344        enum log_category_t cat;
 345        enum log_level_t level;
 346        const char *file;
 347        uint line;
 348        const char *func;
 349        const char *msg;
 350        char *end;
 351
 352        if (argc < 7)
 353                return CMD_RET_USAGE;
 354        cat = log_get_cat_by_name(argv[1]);
 355        level = dectoul(argv[2], &end);
 356        if (end == argv[2]) {
 357                level = log_get_level_by_name(argv[2]);
 358
 359                if (level == LOGL_NONE) {
 360                        printf("Invalid log level '%s'\n", argv[2]);
 361                        return CMD_RET_USAGE;
 362                }
 363        }
 364        if (level >= LOGL_MAX) {
 365                printf("Invalid log level %u\n", level);
 366                return CMD_RET_USAGE;
 367        }
 368        file = argv[3];
 369        line = dectoul(argv[4], NULL);
 370        func = argv[5];
 371        msg = argv[6];
 372        if (_log(cat, level, file, line, func, "%s\n", msg))
 373                return CMD_RET_FAILURE;
 374
 375        return 0;
 376}
 377
 378#ifdef CONFIG_SYS_LONGHELP
 379static char log_help_text[] =
 380        "level [<level>] - get/set log level\n"
 381        "categories - list log categories\n"
 382        "drivers - list log drivers\n"
 383        "log filter-list [OPTIONS] - list all filters for a log driver\n"
 384        "\t-d <driver> - Specify the log driver to list filters from; defaults\n"
 385        "\t              to console\n"
 386        "log filter-add [OPTIONS] - add a new filter to a driver\n"
 387        "\t-A - Allow messages matching this filter; mutually exclusive with -D\n"
 388        "\t     This is the default.\n"
 389        "\t-c <category> - Category to match; may be specified multiple times\n"
 390        "\t-d <driver> - Specify the log driver to add the filter to; defaults\n"
 391        "\t              to console\n"
 392        "\t-D - Deny messages matching this filter; mutually exclusive with -A\n"
 393        "\t-f <files_list> - A comma-separated list of files to match\n"
 394        "\t-l <level> - Match log levels less than or equal to <level>;\n"
 395        "\t             mutually-exclusive with -L\n"
 396        "\t-L <level> - Match log levels greather than or equal to <level>;\n"
 397        "\t             mutually-exclusive with -l\n"
 398        "\t-p - Print the filter number on success\n"
 399        "log filter-remove [OPTIONS] [<num>] - Remove filter number <num>\n"
 400        "\t-a - Remove ALL filters\n"
 401        "\t-d <driver> - Specify the log driver to remove the filter from;\n"
 402        "\t              defaults to console\n"
 403        "log format <fmt> - set log output format. <fmt> is a string where\n"
 404        "\teach letter indicates something that should be displayed:\n"
 405        "\tc=category, l=level, F=file, L=line number, f=function, m=msg\n"
 406        "\tor 'default', or 'all' for all\n"
 407        "log rec <category> <level> <file> <line> <func> <message> - "
 408                "output a log record"
 409        ;
 410#endif
 411
 412U_BOOT_CMD_WITH_SUBCMDS(log, "log system", log_help_text,
 413        U_BOOT_SUBCMD_MKENT(level, 2, 1, do_log_level),
 414        U_BOOT_SUBCMD_MKENT(categories, 1, 1, do_log_categories),
 415        U_BOOT_SUBCMD_MKENT(drivers, 1, 1, do_log_drivers),
 416        U_BOOT_SUBCMD_MKENT(filter-list, 3, 1, do_log_filter_list),
 417        U_BOOT_SUBCMD_MKENT(filter-add, CONFIG_SYS_MAXARGS, 1,
 418                            do_log_filter_add),
 419        U_BOOT_SUBCMD_MKENT(filter-remove, 4, 1, do_log_filter_remove),
 420        U_BOOT_SUBCMD_MKENT(format, 2, 1, do_log_format),
 421        U_BOOT_SUBCMD_MKENT(rec, 7, 1, do_log_rec),
 422);
 423