1 // Copyright 2023 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14
15 #pragma once
16 #include <cstddef>
17 #include <string>
18
19 #include "pw_bluetooth_sapphire/internal/host/common/to_string.h"
20 #include "pw_log/log.h"
21
22 // Logging utilities for the host library. This provides a common abstraction
23 // over Zircon DDK debug utilities (used when the host stack code runs in a
24 // driver) and printf (when it's used in unit tests and command-line tools).
25 //
26 // USAGE:
27 //
28 // Functions have been provided to check if logging has been enabled at a
29 // certain severity and to log a message using a tag, file name, and line
30 // number:
31 //
32 // if (IsLogLevelEnabled(LogSeverity::TRACE)) {
33 // LogMessage(__FILE__, __LINE__, LogSeverity::TRACE, "bt-host", "oops:
34 // %d", foo);
35 // }
36 //
37 // or using the bt_log convenience macro:
38 //
39 // bt_log(DEBUG, "bt-host", "oops: %d", foo);
40 //
41 // DRIVER MODE:
42 //
43 // By default, the log messages use <lib/ddk/debug.h> as its backend. In this
44 // mode the ERROR, WARN, INFO, DEBUG and TRACE severity levels directly
45 // correspond to the DDK severity levels. Log levels are supplied to the kernel
46 // commandline, e.g. to disable INFO level and enable TRACE level messages in
47 // the bt-host driver use the following:
48 //
49 // driver.bthost.log=-info,+trace
50 //
51 // In driver mode, the "tag" argument to bt_log is informational and gets
52 // included in the log message.
53 //
54 // (refer to
55 // https://fuchsia.dev/fuchsia-src/reference/kernel/kernel_cmdline#drivernamelogflags
56 // for all supported DDK debug log flags).
57 //
58 // PRINTF MODE:
59 //
60 // When the host stack code is run outside a driver log messages can be routed
61 // to stdout via printf instead of driver_logf. To enable this mode, call the
62 // UsePrintf() function at process start-up:
63 //
64 // int main() {
65 // bt::UsePrintf(bt::LogSeverity::ERROR);
66 //
67 // ...do stuff...
68 //
69 // return 0;
70 // }
71 //
72 // The |min_severity| parameter determines the smallest severity level that will
73 // be allowed. For example, passing LogSeverity::INFO will enable INFO, WARN,
74 // and ERROR severity levels.
75 //
76 // The UsePrintf() function is NOT thread-safe. This should be called EARLY
77 // and ONLY ONCE during initialization. Once the printf mode is enabled it
78 // cannot be toggled back to driver mode.
79 //
80 // CAVEATS:
81 //
82 // Since the logging mode is determined at run-time and not compile-time (due
83 // to build dependency reasons) users of these utilities will need to link a
84 // symbol for |__zircon_driver_rec__|. While this symbol will remain unused in
85 // printf-mode it is needed to pass compilation if the target is not a driver.
86 // Use the PW_LOG_DECLARE_FAKE_DRIVER macro for this purpose:
87 //
88 // PW_LOG_DECLARE_FAKE_DRIVER();
89 //
90 // int main() {
91 // bt::UsePrintf(bt::LogSeverity::TRACE);
92 // }
93
94 namespace bt {
95
96 // Log severity levels used by the host library, following the convention of
97 // <lib/ddk/debug.h>
98 enum class LogSeverity : int {
99 // Indicates unexpected failures.
100 ERROR = PW_LOG_LEVEL_ERROR,
101
102 // Indicates a situation that is not an error but may be indicative of an
103 // impending problem.
104 WARN = PW_LOG_LEVEL_WARN,
105
106 // Terse information messages for startup, shutdown, or other infrequent state
107 // changes.
108 INFO = PW_LOG_LEVEL_INFO,
109
110 // Verbose messages for transactions and state changes
111 DEBUG = PW_LOG_LEVEL_DEBUG,
112
113 // Legacy. Pigweed doesn't support TRACE, so we map it to DEBUG.
114 TRACE = PW_LOG_LEVEL_DEBUG,
115 };
116
117 constexpr size_t kNumLogSeverities = 5;
118
119 bool IsPrintfLogLevelEnabled(LogSeverity severity);
120
121 unsigned int GetPwLogFlags(LogSeverity level);
122
123 void UsePrintf(LogSeverity min_severity);
124
125 namespace internal {
126
127 // No-op function used to check consistency between format string and arguments.
128 PW_PRINTF_FORMAT(1, 2)
CheckFormat(const char * fmt,...)129 constexpr void CheckFormat([[maybe_unused]] const char* fmt, ...) {}
130
131 } // namespace internal
132 } // namespace bt
133
134 // This macro should be kept as small as possible to reduce binary size.
135 // This macro should not wrap its contents in a lambda, as it breaks logs using
136 // __FUNCTION__.
137 // TODO(fxbug.dev/42086925): Due to limitations, |tag| is processed by
138 // printf-style formatters as a format string, so check that |tag| does not
139 // specify any additional args.
140 #define bt_log(level, tag, /*fmt*/...) \
141 PW_LOG(static_cast<int>(bt::LogSeverity::level), \
142 PW_LOG_LEVEL, \
143 tag, \
144 GetPwLogFlags(bt::LogSeverity::level), \
145 __VA_ARGS__); \
146 ::bt::internal::CheckFormat(tag)
147