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