1 /* 2 * Logging support 3 * 4 * Copyright (c) 2017 Google, Inc 5 * Written by Simon Glass <sjg@chromium.org> 6 * 7 * SPDX-License-Identifier: GPL-2.0+ 8 */ 9 10 #include <common.h> 11 #include <log.h> 12 #include <malloc.h> 13 #include <dm/uclass.h> 14 15 DECLARE_GLOBAL_DATA_PTR; 16 17 static const char *log_cat_name[LOGC_COUNT - LOGC_NONE] = { 18 "none", 19 "arch", 20 "board", 21 "core", 22 "driver-model", 23 "device-tree", 24 "efi", 25 }; 26 27 static const char *log_level_name[LOGL_COUNT] = { 28 "EMERG", 29 "ALERT", 30 "CRIT", 31 "ERR", 32 "WARNING", 33 "NOTICE", 34 "INFO", 35 "DEBUG", 36 "CONTENT", 37 "IO", 38 }; 39 40 const char *log_get_cat_name(enum log_category_t cat) 41 { 42 if (cat > LOGC_COUNT) 43 return "invalid"; 44 if (cat >= LOGC_NONE) 45 return log_cat_name[cat - LOGC_NONE]; 46 47 return uclass_get_name((enum uclass_id)cat); 48 } 49 50 enum log_category_t log_get_cat_by_name(const char *name) 51 { 52 enum uclass_id id; 53 int i; 54 55 for (i = LOGC_NONE; i < LOGC_COUNT; i++) 56 if (!strcmp(name, log_cat_name[i - LOGC_NONE])) 57 return i; 58 id = uclass_get_by_name(name); 59 if (id != UCLASS_INVALID) 60 return (enum log_category_t)id; 61 62 return LOGC_NONE; 63 } 64 65 const char *log_get_level_name(enum log_level_t level) 66 { 67 if (level >= LOGL_COUNT) 68 return "INVALID"; 69 return log_level_name[level]; 70 } 71 72 enum log_level_t log_get_level_by_name(const char *name) 73 { 74 int i; 75 76 for (i = 0; i < LOGL_COUNT; i++) { 77 if (!strcasecmp(log_level_name[i], name)) 78 return i; 79 } 80 81 return LOGL_NONE; 82 } 83 84 static struct log_device *log_device_find_by_name(const char *drv_name) 85 { 86 struct log_device *ldev; 87 88 list_for_each_entry(ldev, &gd->log_head, sibling_node) { 89 if (!strcmp(drv_name, ldev->drv->name)) 90 return ldev; 91 } 92 93 return NULL; 94 } 95 96 /** 97 * log_has_cat() - check if a log category exists within a list 98 * 99 * @cat_list: List of categories to check, at most LOGF_MAX_CATEGORIES entries 100 * long, terminated by LC_END if fewer 101 * @cat: Category to search for 102 * @return true if @cat is in @cat_list, else false 103 */ 104 static bool log_has_cat(enum log_category_t cat_list[], enum log_category_t cat) 105 { 106 int i; 107 108 for (i = 0; i < LOGF_MAX_CATEGORIES && cat_list[i] != LOGC_END; i++) { 109 if (cat_list[i] == cat) 110 return true; 111 } 112 113 return false; 114 } 115 116 /** 117 * log_has_file() - check if a file is with a list 118 * 119 * @file_list: List of files to check, separated by comma 120 * @file: File to check for. This string is matched against the end of each 121 * file in the list, i.e. ignoring any preceding path. The list is 122 * intended to consist of relative pathnames, e.g. common/main.c,cmd/log.c 123 * @return true if @file is in @file_list, else false 124 */ 125 static bool log_has_file(const char *file_list, const char *file) 126 { 127 int file_len = strlen(file); 128 const char *s, *p; 129 int substr_len; 130 131 for (s = file_list; *s; s = p + (*p != '\0')) { 132 p = strchrnul(s, ','); 133 substr_len = p - s; 134 if (file_len >= substr_len && 135 !strncmp(file + file_len - substr_len, s, substr_len)) 136 return true; 137 } 138 139 return false; 140 } 141 142 /** 143 * log_passes_filters() - check if a log record passes the filters for a device 144 * 145 * @ldev: Log device to check 146 * @rec: Log record to check 147 * @return true if @rec is not blocked by the filters in @ldev, false if it is 148 */ 149 static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec) 150 { 151 struct log_filter *filt; 152 153 /* If there are no filters, filter on the default log level */ 154 if (list_empty(&ldev->filter_head)) { 155 if (rec->level > gd->default_log_level) 156 return false; 157 return true; 158 } 159 160 list_for_each_entry(filt, &ldev->filter_head, sibling_node) { 161 if (rec->level > filt->max_level) 162 continue; 163 if ((filt->flags & LOGFF_HAS_CAT) && 164 !log_has_cat(filt->cat_list, rec->cat)) 165 continue; 166 if (filt->file_list && 167 !log_has_file(filt->file_list, rec->file)) 168 continue; 169 return true; 170 } 171 172 return false; 173 } 174 175 /** 176 * log_dispatch() - Send a log record to all log devices for processing 177 * 178 * The log record is sent to each log device in turn, skipping those which have 179 * filters which block the record 180 * 181 * @rec: Log record to dispatch 182 * @return 0 (meaning success) 183 */ 184 static int log_dispatch(struct log_rec *rec) 185 { 186 struct log_device *ldev; 187 188 list_for_each_entry(ldev, &gd->log_head, sibling_node) { 189 if (log_passes_filters(ldev, rec)) 190 ldev->drv->emit(ldev, rec); 191 } 192 193 return 0; 194 } 195 196 int _log(enum log_category_t cat, enum log_level_t level, const char *file, 197 int line, const char *func, const char *fmt, ...) 198 { 199 char buf[CONFIG_SYS_CBSIZE]; 200 struct log_rec rec; 201 va_list args; 202 203 rec.cat = cat; 204 rec.level = level; 205 rec.file = file; 206 rec.line = line; 207 rec.func = func; 208 va_start(args, fmt); 209 vsnprintf(buf, sizeof(buf), fmt, args); 210 va_end(args); 211 rec.msg = buf; 212 if (!gd || !(gd->flags & GD_FLG_LOG_READY)) { 213 if (gd) 214 gd->log_drop_count++; 215 return -ENOSYS; 216 } 217 log_dispatch(&rec); 218 219 return 0; 220 } 221 222 int log_add_filter(const char *drv_name, enum log_category_t cat_list[], 223 enum log_level_t max_level, const char *file_list) 224 { 225 struct log_filter *filt; 226 struct log_device *ldev; 227 int i; 228 229 ldev = log_device_find_by_name(drv_name); 230 if (!ldev) 231 return -ENOENT; 232 filt = (struct log_filter *)calloc(1, sizeof(*filt)); 233 if (!filt) 234 return -ENOMEM; 235 236 if (cat_list) { 237 filt->flags |= LOGFF_HAS_CAT; 238 for (i = 0; ; i++) { 239 if (i == ARRAY_SIZE(filt->cat_list)) 240 return -ENOSPC; 241 filt->cat_list[i] = cat_list[i]; 242 if (cat_list[i] == LOGC_END) 243 break; 244 } 245 } 246 filt->max_level = max_level; 247 if (file_list) { 248 filt->file_list = strdup(file_list); 249 if (!filt->file_list) 250 goto nomem; 251 } 252 filt->filter_num = ldev->next_filter_num++; 253 list_add_tail(&filt->sibling_node, &ldev->filter_head); 254 255 return filt->filter_num; 256 257 nomem: 258 free(filt); 259 return -ENOMEM; 260 } 261 262 int log_remove_filter(const char *drv_name, int filter_num) 263 { 264 struct log_filter *filt; 265 struct log_device *ldev; 266 267 ldev = log_device_find_by_name(drv_name); 268 if (!ldev) 269 return -ENOENT; 270 271 list_for_each_entry(filt, &ldev->filter_head, sibling_node) { 272 if (filt->filter_num == filter_num) { 273 list_del(&filt->sibling_node); 274 free(filt); 275 276 return 0; 277 } 278 } 279 280 return -ENOENT; 281 } 282 283 int log_init(void) 284 { 285 struct log_driver *drv = ll_entry_start(struct log_driver, log_driver); 286 const int count = ll_entry_count(struct log_driver, log_driver); 287 struct log_driver *end = drv + count; 288 289 /* 290 * We cannot add runtime data to the driver since it is likely stored 291 * in rodata. Instead, set up a 'device' corresponding to each driver. 292 * We only support having a single device. 293 */ 294 INIT_LIST_HEAD((struct list_head *)&gd->log_head); 295 while (drv < end) { 296 struct log_device *ldev; 297 298 ldev = calloc(1, sizeof(*ldev)); 299 if (!ldev) { 300 debug("%s: Cannot allocate memory\n", __func__); 301 return -ENOMEM; 302 } 303 INIT_LIST_HEAD(&ldev->filter_head); 304 ldev->drv = drv; 305 list_add_tail(&ldev->sibling_node, 306 (struct list_head *)&gd->log_head); 307 drv++; 308 } 309 gd->flags |= GD_FLG_LOG_READY; 310 gd->default_log_level = LOGL_INFO; 311 gd->log_fmt = LOGF_DEFAULT; 312 313 return 0; 314 } 315