1 /*
2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <atomic>
20 #include <format>
21 #include <sstream>
22 #include <string_view>
23 
24 #ifndef LOG_TAG
25 #define LOG_TAG "bluetooth"
26 #endif  // LOG_TAG
27 
28 namespace bluetooth::log_internal {
29 
30 /// Android framework log priority levels.
31 /// They are defined in system/logging/liblog/include/android/log.h by
32 /// the Android Framework code.
33 enum Level {
34   kVerbose = 2,
35   kDebug = 3,
36   kInfo = 4,
37   kWarn = 5,
38   kError = 6,
39   kFatal = 7,
40 };
41 
42 /// Information about the location a log is printed from.
43 /// Passing this parameter by default value will fill in
44 /// the correct information.
45 struct source_location {
46   source_location(char const* file_name = __builtin_FILE(), int line = __builtin_LINE(),
47                   char const* function_name = __builtin_FUNCTION())
linesource_location48       : line(line), file_name(file_name), function_name(function_name) {}
49 
50   int line;
51   char const* file_name;
52   char const* function_name;
53 };
54 
55 /// Write a single log line.
56 /// The implementation of this function is dependent on the backend.
57 void vlog(Level level, char const* tag, source_location location, std::string_view fmt,
58           std::format_args vargs);
59 
60 /// Capture invalid parameter values that would cause runtime
61 /// formatting errors.
62 template <class T>
format_replace(T & arg)63 [[maybe_unused]] static inline T& format_replace(T& arg) {
64   return arg;
65 }
66 
67 /// Specialization of format_replace for nullptr string parameters.
68 template <>
format_replace(char const * & arg)69 char const*& format_replace(char const*& arg) {
70   static char const* nullptr_str = "(nullptr)";
71   if (arg) {
72     return arg;
73   }
74   return nullptr_str;
75 }
76 
77 /// Specialization of format_replace for nullptr string parameters.
78 template <>
format_replace(char * & arg)79 char*& format_replace(char*& arg) {
80   static char* nullptr_str = (char*)"(nullptr)";
81   if (arg) {
82     return arg;
83   }
84   return nullptr_str;
85 }
86 
87 template <Level level, typename... T>
88 struct log {
89   log(std::format_string<T...> fmt, T&&... args, source_location location = source_location()) {
90     vlog(level, LOG_TAG, location, fmt.get(), std::make_format_args(format_replace(args)...));
91   }
92 };
93 
94 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
95 
96 template <int level, typename... T>
97 log(std::format_string<T...>, T&&...) -> log<level, T...>;
98 
99 #endif
100 
101 }  // namespace bluetooth::log_internal
102 
103 namespace bluetooth::log {
104 
105 #if (__cplusplus >= 202002L && defined(__GNUC__) && !defined(__clang__))
106 
107 template <typename... T>
108 using error = log_internal::log<log_internal::kError, T...>;
109 template <typename... T>
110 using warning = log_internal::log<log_internal::kWarning, T...>;
111 template <typename... T>
112 using info = log_internal::log<log_internal::kInfo, T...>;
113 template <typename... T>
114 using debug = log_internal::log<log_internal::kDebug, T...>;
115 template <typename... T>
116 using verbose = log_internal::log<log_internal::kVerbose, T...>;
117 
118 #else
119 
120 template <typename... T>
121 struct error : log_internal::log<log_internal::kError, T...> {
122   using log_internal::log<log_internal::kError, T...>::log;
123 };
124 template <typename... T>
125 struct warn : log_internal::log<log_internal::kWarn, T...> {
126   using log_internal::log<log_internal::kWarn, T...>::log;
127 };
128 template <typename... T>
129 struct info : log_internal::log<log_internal::kInfo, T...> {
130   using log_internal::log<log_internal::kInfo, T...>::log;
131 };
132 template <typename... T>
133 struct debug : log_internal::log<log_internal::kDebug, T...> {
134   using log_internal::log<log_internal::kDebug, T...>::log;
135 };
136 template <typename... T>
137 struct verbose : log_internal::log<log_internal::kVerbose, T...> {
138   using log_internal::log<log_internal::kVerbose, T...>::log;
139 };
140 
141 template <typename... T>
142 error(std::format_string<T...>, T&&...) -> error<T...>;
143 template <typename... T>
144 warn(std::format_string<T...>, T&&...) -> warn<T...>;
145 template <typename... T>
146 info(std::format_string<T...>, T&&...) -> info<T...>;
147 template <typename... T>
148 debug(std::format_string<T...>, T&&...) -> debug<T...>;
149 template <typename... T>
150 verbose(std::format_string<T...>, T&&...) -> verbose<T...>;
151 
152 #endif  // GCC / C++20
153 
154 [[noreturn]] [[maybe_unused]] static void fatal(
155         std::format_string<> fmt,
156         log_internal::source_location location = log_internal::source_location()) {
157   vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(), std::make_format_args());
158   std::abort();  // Enforce [[noreturn]]
159 }
160 
161 template <typename T0>
162 [[noreturn]] [[maybe_unused]] static void fatal(
163         std::format_string<T0> fmt, T0&& arg0,
164         log_internal::source_location location = log_internal::source_location()) {
165   vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
166        std::make_format_args(log_internal::format_replace(arg0)));
167   std::abort();  // Enforce [[noreturn]]
168 }
169 
170 template <typename T0, typename T1>
171 [[noreturn]] [[maybe_unused]] static void fatal(
172         std::format_string<T0, T1> fmt, T0&& arg0, T1&& arg1,
173         log_internal::source_location location = log_internal::source_location()) {
174   vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
175        std::make_format_args(log_internal::format_replace(arg0),
176                              log_internal::format_replace(arg1)));
177   std::abort();  // Enforce [[noreturn]]
178 }
179 
180 template <typename T0, typename T1, typename T2>
181 [[noreturn]] [[maybe_unused]] static void fatal(
182         std::format_string<T0, T1, T2> fmt, T0&& arg0, T1&& arg1, T2&& arg2,
183         log_internal::source_location location = log_internal::source_location()) {
184   vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
185        std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
186                              log_internal::format_replace(arg2)));
187   std::abort();  // Enforce [[noreturn]]
188 }
189 
190 template <typename T0, typename T1, typename T2, typename T3>
191 [[noreturn]] [[maybe_unused]] static void fatal(
192         std::format_string<T0, T1, T2, T3> fmt, T0&& arg0, T1&& arg1, T2&& arg2, T3&& arg3,
193         log_internal::source_location location = log_internal::source_location()) {
194   vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
195        std::make_format_args(log_internal::format_replace(arg0), log_internal::format_replace(arg1),
196                              log_internal::format_replace(arg2),
197                              log_internal::format_replace(arg3)));
198   std::abort();  // Enforce [[noreturn]]
199 }
200 
201 template <typename... T>
202 struct assert_that {
203   assert_that(bool cond, std::format_string<T...> fmt, T&&... args,
204               log_internal::source_location location = log_internal::source_location()) {
205     if (!cond) {
206       vlog(log_internal::kFatal, LOG_TAG, location, fmt.get(),
207            std::make_format_args(log_internal::format_replace(args)...));
208     }
209   }
210 };
211 
212 template <typename... T>
213 assert_that(bool, std::format_string<T...>, T&&...) -> assert_that<T...>;
214 
215 }  // namespace bluetooth::log
216 
217 namespace std {
218 
219 /// Helper to format a pointer value as the memory address.
220 /// Use this helper as `std::format("{}", std::format_ptr(value));`.
221 template <typename T>
format_ptr(T * ptr)222 const void* format_ptr(T* ptr) {
223   return reinterpret_cast<const void*>(ptr);
224 }
225 
226 /// Derive formatter for std::atomic<T> types where T is formattable.
227 /// The formatter uses the default memory order `std::memory_order_seq_cst`
228 /// for reading the value.
229 template <typename T, typename CharT>
230 struct formatter<std::atomic<T>, CharT> : formatter<T, CharT> {
231   template <typename Context>
232   auto format(const std::atomic<T>& v, Context& ctx) const -> decltype(ctx.out()) {
233     return formatter<T, CharT>::format(v.load(), ctx);
234   }
235 };
236 
237 /// Default formatter implementation for formatting
238 /// types overloading the ostream `operator<<`.
239 ///
240 /// Enable this formatter in the code by declaring:
241 /// ```
242 /// template<>
243 /// struct std::formatter<T> : ostream_formatter {};
244 /// ```
245 template <typename CharT>
246 struct basic_ostream_formatter : formatter<basic_string_view<CharT>, CharT> {
247   void set_debug_format() = delete;
248 
249   template <typename T, typename Context>
250   auto format(const T& value, Context& ctx) const -> decltype(ctx.out()) {
251     auto&& output = std::basic_stringstream<CharT>();
252     output.imbue(std::locale::classic());  // The default is always unlocalized.
253     output << value;
254     output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
255     return formatter<basic_string_view<CharT>, CharT>::format(output.str(), ctx);
256   }
257 };
258 
259 using ostream_formatter = basic_ostream_formatter<char>;
260 
261 /// Default formatter implementation for formatting
262 /// enum class values to the underlying type.
263 ///
264 /// Enable this formatter in the code by declaring:
265 /// ```
266 /// template<>
267 /// struct std::formatter<EnumT> : enum_formatter<EnumT> {};
268 /// ```
269 template <typename EnumT, class CharT = char>
270 struct enum_formatter : std::formatter<std::underlying_type_t<EnumT>, CharT> {
271   template <class Context>
272   typename Context::iterator format(EnumT value, Context& ctx) const {
273     return std::formatter<std::underlying_type_t<EnumT>, CharT>::format(
274             static_cast<std::underlying_type_t<EnumT>>(value), ctx);
275   }
276 };
277 
278 /// Default formatter implementation for formatting
279 /// values of type T for which a string conversion function
280 /// T_to_str is implemented.
281 ///
282 /// Enable this formatter in the code by declaring:
283 /// ```
284 /// template<>
285 /// struct std::formatter<T> : string_formatter<T, &T_to_str> {};
286 /// ```
287 template <typename T, std::string (*F)(const T&), class CharT = char>
288 struct string_formatter : std::formatter<std::string> {
289   template <class Context>
290   typename Context::iterator format(const T& value, Context& ctx) const {
291     return std::formatter<std::string>::format(F(value), ctx);
292   }
293 };
294 
295 }  // namespace std
296