*/
-#include <common.h>
+#include <display_options.h>
#include <log.h>
#include <malloc.h>
+#include <asm/global_data.h>
#include <dm/uclass.h>
DECLARE_GLOBAL_DATA_PTR;
"bloblist",
"devres",
"acpi",
+ "boot",
+ "event",
+ "fs",
+ "expo",
};
_Static_assert(ARRAY_SIZE(log_cat_name) == LOGC_COUNT - LOGC_NONE,
*
* @ldev: Log device to check
* @rec: Log record to check
- * @return true if @rec is not blocked by the filters in @ldev, false if it is
+ * Return: true if @rec is not blocked by the filters in @ldev, false if it is
*/
static bool log_passes_filters(struct log_device *ldev, struct log_rec *rec)
{
struct log_filter *filt;
- if (rec->force_debug)
+ if (rec->flags & LOGRECF_FORCE_DEBUG)
return true;
/* If there are no filters, filter on the default log level */
}
list_for_each_entry(filt, &ldev->filter_head, sibling_node) {
- if (rec->level > filt->max_level)
+ if (filt->flags & LOGFF_LEVEL_MIN) {
+ if (rec->level < filt->level)
+ continue;
+ } else if (rec->level > filt->level) {
continue;
+ }
+
if ((filt->flags & LOGFF_HAS_CAT) &&
!log_has_cat(filt->cat_list, rec->cat))
continue;
+
if (filt->file_list &&
!log_has_file(filt->file_list, rec->file))
continue;
- return true;
+
+ if (filt->flags & LOGFF_DENY)
+ return false;
+ else
+ return true;
}
return false;
* @rec: log record to dispatch
* Return: 0 msg sent, 1 msg not sent while already dispatching another msg
*/
-static int log_dispatch(struct log_rec *rec)
+static int log_dispatch(struct log_rec *rec, const char *fmt, va_list args)
{
struct log_device *ldev;
+ char buf[CONFIG_SYS_CBSIZE];
/*
* When a log driver writes messages (e.g. via the network stack) this
gd->processing_msg = true;
list_for_each_entry(ldev, &gd->log_head, sibling_node) {
if ((ldev->flags & LOGDF_ENABLE) &&
- log_passes_filters(ldev, rec))
+ log_passes_filters(ldev, rec)) {
+ if (!rec->msg) {
+ int len;
+
+ len = vsnprintf(buf, sizeof(buf), fmt, args);
+ rec->msg = buf;
+ gd->log_cont = len && buf[len - 1] != '\n';
+ }
ldev->drv->emit(ldev, rec);
+ }
}
gd->processing_msg = false;
return 0;
int _log(enum log_category_t cat, enum log_level_t level, const char *file,
int line, const char *func, const char *fmt, ...)
{
- char buf[CONFIG_SYS_CBSIZE];
struct log_rec rec;
va_list args;
+ if (!gd)
+ return -ENOSYS;
+
/* Check for message continuation */
if (cat == LOGC_CONT)
cat = gd->logc_prev;
rec.cat = cat;
rec.level = level & LOGL_LEVEL_MASK;
- rec.force_debug = level & LOGL_FORCE_DEBUG;
+ rec.flags = 0;
+ if (level & LOGL_FORCE_DEBUG)
+ rec.flags |= LOGRECF_FORCE_DEBUG;
+ if (gd->log_cont)
+ rec.flags |= LOGRECF_CONT;
rec.file = file;
rec.line = line;
rec.func = func;
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
- rec.msg = buf;
- if (!gd || !(gd->flags & GD_FLG_LOG_READY)) {
- if (gd)
- gd->log_drop_count++;
+ rec.msg = NULL;
+
+ if (!(gd->flags & GD_FLG_LOG_READY)) {
+ gd->log_drop_count++;
+
+ /* display dropped traces with console puts and DEBUG_UART */
+ if (rec.level <= CONFIG_LOG_DEFAULT_LEVEL ||
+ rec.flags & LOGRECF_FORCE_DEBUG) {
+ char buf[CONFIG_SYS_CBSIZE];
+
+ va_start(args, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, args);
+ puts(buf);
+ va_end(args);
+ }
+
return -ENOSYS;
}
- if (!log_dispatch(&rec)) {
+ va_start(args, fmt);
+ if (!log_dispatch(&rec, fmt, args)) {
gd->logc_prev = cat;
gd->logl_prev = level;
}
+ va_end(args);
return 0;
}
-int log_add_filter(const char *drv_name, enum log_category_t cat_list[],
- enum log_level_t max_level, const char *file_list)
+#define MAX_LINE_LENGTH_BYTES 64
+#define DEFAULT_LINE_LENGTH_BYTES 16
+
+int _log_buffer(enum log_category_t cat, enum log_level_t level,
+ const char *file, int line, const char *func, ulong addr,
+ const void *data, uint width, uint count, uint linelen)
+{
+ if (linelen * width > MAX_LINE_LENGTH_BYTES)
+ linelen = MAX_LINE_LENGTH_BYTES / width;
+ if (linelen < 1)
+ linelen = DEFAULT_LINE_LENGTH_BYTES / width;
+
+ while (count) {
+ uint thislinelen;
+ char buf[HEXDUMP_MAX_BUF_LENGTH(width * linelen)];
+
+ thislinelen = hexdump_line(addr, data, width, count, linelen,
+ buf, sizeof(buf));
+ assert(thislinelen >= 0);
+ _log(cat, level, file, line, func, "%s\n", buf);
+
+ /* update references */
+ data += thislinelen * width;
+ addr += thislinelen * width;
+ count -= thislinelen;
+ }
+
+ return 0;
+}
+
+int log_add_filter_flags(const char *drv_name, enum log_category_t cat_list[],
+ enum log_level_t level, const char *file_list,
+ int flags)
{
struct log_filter *filt;
struct log_device *ldev;
if (!filt)
return -ENOMEM;
+ filt->flags = flags;
if (cat_list) {
filt->flags |= LOGFF_HAS_CAT;
for (i = 0; ; i++) {
break;
}
}
- filt->max_level = max_level;
+ filt->level = level;
if (file_list) {
filt->file_list = strdup(file_list);
if (!filt->file_list) {
}
}
filt->filter_num = ldev->next_filter_num++;
- list_add_tail(&filt->sibling_node, &ldev->filter_head);
+ /* Add deny filters to the beginning of the list */
+ if (flags & LOGFF_DENY)
+ list_add(&filt->sibling_node, &ldev->filter_head);
+ else
+ list_add_tail(&filt->sibling_node, &ldev->filter_head);
return filt->filter_num;
* log_find_device_by_drv() - Find a device by its driver
*
* @drv: Log driver
- * @return Device associated with that driver, or NULL if not found
+ * Return: Device associated with that driver, or NULL if not found
*/
static struct log_device *log_find_device_by_drv(struct log_driver *drv)
{
return 0;
}
+void log_fixup_for_gd_move(struct global_data *new_gd)
+{
+ new_gd->log_head.prev->next = &new_gd->log_head;
+}
+
int log_init(void)
{
struct log_driver *drv = ll_entry_start(struct log_driver, log_driver);
/*
* We cannot add runtime data to the driver since it is likely stored
* in rodata. Instead, set up a 'device' corresponding to each driver.
- * We only support having a single device.
+ * We only support having a single device for each driver.
*/
INIT_LIST_HEAD((struct list_head *)&gd->log_head);
while (drv < end) {