Software APIs
log.h
Go to the documentation of this file.
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
5 #ifndef OPENTITAN_SW_DEVICE_LIB_RUNTIME_LOG_H_
6 #define OPENTITAN_SW_DEVICE_LIB_RUNTIME_LOG_H_
7 
8 #include <stdbool.h>
9 #include <stdint.h>
10 
13 
14 /**
15  * @file
16  * @brief Generic logging APIs
17  *
18  * The logging APIs below take a format string with a variable number of
19  * arguments for the type specifiers. The APIs are designed to provide a way
20  * for attaching the log severity, file name, and line number
21  * information along with the message to provide an easier path to debug.
22  * These parameters form a log prefix, which is prepended to the actual
23  * message being printed. The following is a brief description of these:
24  *
25  * log_type: Severity of the message: info, warning, error or fatal
26  *
27  * file name: Name of the file using __FILE__
28  *
29  * line number: Line where the print originated using __LINE__
30  *
31  * Log macros support OpenTitan formatting specifiers; see print.h for
32  * details the subset of C specifier syntax supported.
33  *
34  * The precise mechanism for logging is dependent on the target device. On core
35  * devices, like Verilator, logs are printed using whatever `stdout` is set to
36  * in print.h. DV testbenches may use an alternative, more efficient mechanism.
37  *
38  * In DV mode, some format specifiers may be unsupported, such as %s.
39  */
40 
41 /**
42  * Log severities available.
43  *
44  * Additional log severities can be added as necessary.
45  */
46 typedef enum log_severity {
47  kLogSeverityInfo,
48  kLogSeverityWarn,
49  kLogSeverityError,
50  kLogSeverityFatal,
52 
53 /**
54  * Represents log metadata used to format a log line.
55  *
56  * Any modification to this struct must be made with caution due to external
57  * assumptions. A post-processing script parses the ELF file and extracts the
58  * log fields. The said script uses 20-byte size as the delimiter to collect the
59  * log fields. Any changes to this struct must be accompanied with the updates
60  * to the script, located here:
61  * util/device_sw_utils/extract_sw_logs.py.
62  */
63 typedef struct log_fields {
64  /**
65  * Indicates the severity of the LOG.
66  */
68  /**
69  * Name of the file at which a LOG line occurs, e.g. `__FILE__`. There
70  * are no requirements for this string, other than that it be some kind of
71  * UNIX-like pathname.
72  */
73  const char *file_name;
74  /**
75  * Indicates the line number at which the LOG line occurs, e.g., `__LINE__`.
76  */
77  uint32_t line;
78  /**
79  * Indicates the number of arguments passed to the format string.
80  *
81  * This value used only in DV mode, and is ignored by non-DV logging.
82  */
83  uint32_t nargs;
84  /**
85  * The actual format string.
86  */
87  const char *format;
89 
90 // Internal functions exposed only for access by macros. Their
91 // real doxygen can be found in log.c.
92 /**
93  * Implementation detail.
94  */
95 void base_log_internal_core(const log_fields_t *log, ...);
96 /**
97  * Implementation detail.
98  */
99 void base_log_internal_dv(const log_fields_t *log, uint32_t nargs, ...);
100 
101 extern char _dv_log_offset[];
102 
103 /**
104  * A macro that wraps the `OT_FAIL_IF_64_BIT` macro, providing the name
105  * of the LOG macro for better error messages.
106  *
107  * @param arg an arg/expression to check
108  */
109 #define OT_FAIL_IF_64_BIT_LOG(arg) OT_FAIL_IF_64_BIT(arg, LOG)
110 
111 /**
112  * A macro that checks the variable arguments of the `LOG` function are valid
113  * at compile time, by asserting that each of the argument is not a standard
114  * C integer type with a width of 64 bits. Any such invalid argument will
115  * cause a relevant error via a static assertion.
116  *
117  * @param ... the variable args list
118  */
119 #define OT_CHECK_VALID_LOG_ARGS(...) \
120  OT_VA_FOR_EACH(OT_FAIL_IF_64_BIT_LOG, ##__VA_ARGS__)
121 
122 /**
123  * Basic logging macro that all other logging macros delegate to.
124  *
125  * Prefer to use a LOG function with a specified severity, instead.
126  *
127  * @param severity a severity of type `log_severity_t`.
128  * @param format a format string, as described in print.h. This must be a
129  * string literal.
130  * @param ... format parameters matching the format string.
131  */
132 #define LOG(severity, format, ...) \
133  do { \
134  OT_CHECK_VALID_LOG_ARGS(__VA_ARGS__); \
135  if (kDeviceLogBypassUartAddress != 0) { \
136  /* clang-format off */ \
137  /* Put DV-only log constants in .logs.* sections, which
138  * the linker will dutifully discard.
139  * Unfortunately, clang-format really mangles these
140  * declarations, so we format them manually. */ \
141  __attribute__((section(".logs.fields"))) \
142  static const log_fields_t kLogFields = \
143  LOG_MAKE_FIELDS_(severity, format, ##__VA_ARGS__); \
144  base_log_internal_dv((const log_fields_t*)((char*)&kLogFields + (uintptr_t)&_dv_log_offset), \
145  OT_VA_ARGS_COUNT(format, ##__VA_ARGS__), \
146  ##__VA_ARGS__); /* clang-format on */ \
147  } else { \
148  static const log_fields_t log_fields = \
149  LOG_MAKE_FIELDS_(severity, format, ##__VA_ARGS__); \
150  base_log_internal_core(&log_fields, ##__VA_ARGS__); \
151  } \
152  } while (false)
153 
154 /**
155  * Implementation detail of `LOG`.
156  */
157 #define LOG_MAKE_FIELDS_(_severity, _format, ...) \
158  { \
159  .severity = _severity, .file_name = "" __FILE__ "", .line = __LINE__, \
160  .nargs = OT_VA_ARGS_COUNT(_format, ##__VA_ARGS__), .format = _format, \
161  }
162 
163 /**
164  * Log an informational message.
165  *
166  * @param severity a severity of type `log_severity_t`.
167  * @param format a format string, as described in print.h. This must be a string
168  * literal.
169  * @param ... format parameters matching the format string.
170  */
171 #define LOG_INFO(...) LOG(kLogSeverityInfo, __VA_ARGS__)
172 
173 /**
174  * Log a warning
175  *
176  * @param severity a severity of type `log_severity_t`.
177  * @param format a format string, as described in print.h. This must be a string
178  * literal.
179  * @param ... format parameters matching the format string.
180  */
181 #define LOG_WARNING(...) LOG(kLogSeverityWarn, __VA_ARGS__)
182 
183 /**
184  * Log a non-fatal error.
185  *
186  * @param severity a severity of type `log_severity_t`.
187  * @param format a format string, as described in print.h. This must be a string
188  * literal.
189  * @param ... format parameters matching the format string.
190  */
191 #define LOG_ERROR(...) LOG(kLogSeverityError, __VA_ARGS__)
192 
193 /**
194  * Log a fatal error.
195  *
196  * @param severity a severity of type `log_severity_t`.
197  * @param format a format string, as described in print.h. This must be a string
198  * literal.
199  * @param ... format parameters matching the format string.
200  *
201  * It is the user's responsibility to follow this up with a call to `abort()` to
202  * immediately stop the execution.
203  */
204 #define LOG_FATAL(...) LOG(kLogSeverityFatal, __VA_ARGS__)
205 
206 #endif // OPENTITAN_SW_DEVICE_LIB_RUNTIME_LOG_H_