blob: cf14ce97b087dd339be1e423aff1f1d78754e395 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LOG_H_
#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LOG_H_
#include <lib/ddk/driver.h>
#include <cstddef>
#include <string>
#include "src/connectivity/bluetooth/core/bt-host/common/to_string.h"
// Logging utilities for the host library. This provides a common abstraction
// over Zircon DDK debug utilities (used when the host stack code runs in a
// driver) and printf (when it's used in unit tests and command-line tools).
//
// USAGE:
//
// Functions have been provided to check if logging has been enabled at a
// certain severity and to log a message using a tag, file name, and line
// number:
//
// if (IsLogLevelEnabled(LogSeverity::TRACE)) {
// LogMessage(__FILE__, __LINE__, LogSeverity::TRACE, "bt-host", "oops: %d", foo);
// }
//
// or using the bt_log convenience macro:
//
// bt_log(DEBUG, "bt-host", "oops: %d", foo);
//
// DRIVER MODE:
//
// By default, the log messages use <lib/ddk/debug.h> as its backend. In this mode
// the ERROR, WARN, INFO, DEBUG and TRACE severity levels directly correspond to
// the DDK severity levels. Log levels are supplied to the kernel commandline,
// e.g. to disable INFO level and enable TRACE level messages in the bt-host
// driver use the following:
//
// driver.bthost.log=-info,+trace
//
// In driver mode, the "tag" argument to bt_log is informational and gets
// included in the log message.
//
// (refer to https://fuchsia.dev/fuchsia-src/reference/kernel/kernel_cmdline#drivernamelogflags
// for all supported DDK debug log flags).
//
// PRINTF MODE:
//
// When the host stack code is run outside a driver log messages can be routed to stdout via printf
// instead of driver_logf. To enable this mode, call the UsePrintf() function at process start-up:
//
// int main() {
// bt::UsePrintf(bt::LogSeverity::ERROR);
//
// ...do stuff...
//
// return 0;
// }
//
// The |min_severity| parameter determines the smallest severity level that will
// be allowed. For example, passing LogSeverity::INFO will enable INFO, WARN,
// and ERROR severity levels.
//
// The UsePrintf() function is NOT thread-safe. This should be called EARLY
// and ONLY ONCE during initialization. Once the printf mode is enabled it
// cannot be toggled back to driver mode.
//
// CAVEATS:
//
// Since the logging mode is determined at run-time and not compile-time (due
// to build dependency reasons) users of these utilities will need to link a
// symbol for |__zircon_driver_rec__|. While this symbol will remain unused in
// printf-mode it is needed to pass compilation if the target is not a driver.
// Use the BT_DECLARE_FAKE_DRIVER macro for this purpose:
//
// BT_DECLARE_FAKE_DRIVER();
//
// int main() {
// bt::UsePrintf(bt::LogSeverity::TRACE);
// }
namespace bt {
// Log severity levels used by the host library, following the convention of
// <lib/ddk/debug.h>
enum class LogSeverity {
// Indicates unexpected failures.
ERROR = 0,
// Indicates a situation that is not an error but may be indicative of an
// impending problem.
WARN = 1,
// Terse information messages for startup, shutdown, or other infrequent state
// changes.
INFO = 2,
// Verbose messages for transactions and state changes
DEBUG = 3,
// Very verbose messages.
TRACE = 4,
};
constexpr size_t kNumLogSeverities = 5;
bool IsLogLevelEnabled(LogSeverity severity);
[[gnu::format(printf, 5, 6)]] void LogMessage(const char* file, int line, LogSeverity severity,
const char* tag, const char* fmt, ...);
void UsePrintf(LogSeverity min_severity);
struct LogContext {
std::string context;
};
namespace internal {
// Returns the part of a path following the final '/', or the whole path if there is no '/'.
constexpr const char* BaseName(const char* path) {
for (const char* c = path; c && (*c != '\0'); c++) {
if (*c == '/') {
path = c + 1;
}
}
return path;
}
// No-op function used to check consistency between format string and arguments.
[[gnu::format(printf, 1, 2)]] constexpr void CheckFormat([[maybe_unused]] const char* fmt, ...) {}
// LogScope is a helper class that manages the lifetime of a log scope in the global thread-local
// list of log scopes. LogScope should only be created through the bt_log_scope() macro in order to
// guarantee that the the correct scope is removed when a LogScope is destroyed.
class LogScopeGuard final {
public:
// Push log scope.
// The "format" attribute counts arguments starting at 1, including implicit |this|.
[[gnu::format(printf, 2, 3)]] explicit LogScopeGuard(const char* fmt, ...);
// Pop log scope.
~LogScopeGuard();
};
// Like LogScopeGuard, but for LogContext.
class LogContextGuard final {
public:
// Restore log context.
explicit LogContextGuard(LogContext);
// Pop log context.
~LogContextGuard();
private:
bool empty_;
};
// Returns a snapshot of the current registered log scopes that can be restored with
// LogContextGuard::LogContextGuard(LogContext).
LogContext SaveLogContext();
} // namespace internal
} // namespace bt
// This macro should be kept as small as possible to reduce binary size.
// This macro should not wrap its contents in a lambda, as it breaks logs using __FUNCTION__.
// TODO(fxbug.dev/1390): Due to limitations, |tag| is processed by printf-style formatters as a
// format string, so check that |tag| does not specify any additional args.
#define bt_log(flag, tag, fmt...) \
::bt::LogMessage(__FILE__, __LINE__, bt::LogSeverity::flag, tag, fmt); \
::bt::internal::CheckFormat(tag)
#define BT_DECLARE_FAKE_DRIVER() zx_driver_rec_t __zircon_driver_rec__ = {}
#define __BT_CONCAT_(x, y) x##y
// This level of indirection is required for concatenating the results of macros.
#define __BT_CONCAT(x, y) __BT_CONCAT_(x, y)
// bt_log_scope() is a helper macro for defining a uniquely named LogScope variable that will remove
// the log scope at the end of the current scope. capture_log_context() and add_parent_context() can
// be used to save and restore scopes in async callbacks.
//
// Example:
//
// void MyFunction(int value) {
// bt_log_scope("MyFunction value=%d", value);
// bt_log(INFO, "tag", "A");
// auto callback = [ctx = capture_log_context()] {
// add_parent_context(ctx);
// bt_log(INFO, "tag", "B");
// };
// callback();
// async::PostTask(async_get_default_dispatcher(), callback);
// }
//
// Myfunction(5) will log the following:
// [tag:file.cc:line][MyFunction value=5] A
// [tag:file.cc:line]{[MyFunction value=5]}[MyFunction value=5] B
// [tag:file.cc:line]{[MyFunction value=5]} B
#define bt_log_scope(fmt...) bt::internal::LogScopeGuard __BT_CONCAT(log_scope_, __LINE__)(fmt)
// Returns a LogContext containing all of the current scopes and contexts.
#define capture_log_context() bt::internal::SaveLogContext()
// Prepend a saved context to the current context to indicate causality (e.g. in an async callback).
#define add_parent_context(ctx) bt::internal::LogContextGuard __BT_CONCAT(log_ctx_, __LINE__)(ctx)
#endif // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LOG_H_