strawberry-audio-player-win.../ext/libstrawberry-common/core/logging.cpp

404 lines
12 KiB
C++
Raw Normal View History

2018-02-27 18:06:05 +01:00
/* This file is part of Strawberry.
Copyright 2011, David Sansome <me@davidsansome.com>
2021-04-10 07:32:38 +02:00
Copyright 2018-2021, Jonas Kvinge <jonas@jkvinge.net>
2018-02-27 18:06:05 +01:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <QtGlobal>
2020-03-08 18:40:39 +01:00
#include "config.h"
2020-06-14 23:54:18 +02:00
#include <cstdio>
#include <cstdlib>
#include <cstring>
2020-03-08 18:40:39 +01:00
#include <iostream>
2020-03-08 19:13:13 +01:00
#include <memory>
2021-08-24 17:52:08 +02:00
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wold-style-cast"
#endif
2020-03-08 18:40:39 +01:00
#include <glib.h>
#ifdef __clang__
# pragma clang diagnostic pop
#endif
2020-03-08 18:40:39 +01:00
#ifdef HAVE_BACKTRACE
# include <execinfo.h>
#endif
#include <QByteArray>
#include <QList>
#include <QMap>
#include <QString>
#include <QStringList>
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QDateTime>
#include <QIODevice>
2020-03-08 18:40:39 +01:00
#include <QBuffer>
#include <QtMessageHandler>
#include <QMessageLogContext>
2020-03-08 18:40:39 +01:00
#include <QDebug>
2018-02-27 18:06:05 +01:00
#include "logging.h"
namespace logging {
static Level sDefaultLevel = Level_Debug;
2021-10-31 13:14:17 +01:00
static QMap<QString, Level> *sClassLevels = nullptr;
2018-02-27 18:06:05 +01:00
static QIODevice *sNullDevice = nullptr;
2021-10-31 13:14:17 +01:00
const char *kDefaultLogLevels = "*:3";
2018-02-27 18:06:05 +01:00
static const char *kMessageHandlerMagic = "__logging_message__";
2021-06-20 19:04:08 +02:00
static const size_t kMessageHandlerMagicLength = strlen(kMessageHandlerMagic);
2018-02-27 18:06:05 +01:00
static QtMessageHandler sOriginalMessageHandler = nullptr;
2022-03-22 21:09:05 +01:00
template<class T>
2021-10-31 13:14:17 +01:00
static T CreateLogger(Level level, const QString &class_name, int line, const char *category);
2018-02-27 18:06:05 +01:00
2020-03-08 18:40:39 +01:00
void GLog(const char *domain, int level, const char *message, void*) {
2019-09-15 20:27:32 +02:00
2018-02-27 18:06:05 +01:00
switch (level) {
case G_LOG_FLAG_RECURSION:
case G_LOG_FLAG_FATAL:
case G_LOG_LEVEL_ERROR:
2020-03-08 18:40:39 +01:00
case G_LOG_LEVEL_CRITICAL:
qLogCat(Error, domain) << message;
break;
case G_LOG_LEVEL_WARNING:
qLogCat(Warning, domain) << message;
break;
2018-02-27 18:06:05 +01:00
case G_LOG_LEVEL_MESSAGE:
2020-03-08 18:40:39 +01:00
case G_LOG_LEVEL_INFO:
qLogCat(Info, domain) << message;
break;
2018-02-27 18:06:05 +01:00
case G_LOG_LEVEL_DEBUG:
2020-03-08 18:40:39 +01:00
default:
qLogCat(Debug, domain) << message;
break;
2018-02-27 18:06:05 +01:00
}
2022-03-22 21:19:59 +01:00
2018-02-27 18:06:05 +01:00
}
2022-03-22 21:09:05 +01:00
template<class T>
2020-03-08 18:40:39 +01:00
class DebugBase : public QDebug {
public:
DebugBase() : QDebug(sNullDevice) {}
2020-06-26 22:41:38 +02:00
explicit DebugBase(QtMsgType t) : QDebug(t) {}
2021-10-31 13:14:17 +01:00
T &space() { return static_cast<T&>(QDebug::space()); }
T &nospace() { return static_cast<T&>(QDebug::nospace()); }
2020-03-08 18:40:39 +01:00
};
// Debug message will be stored in a buffer.
class BufferedDebug : public DebugBase<BufferedDebug> {
public:
2021-06-21 15:38:58 +02:00
BufferedDebug() = default;
2020-06-26 22:41:38 +02:00
explicit BufferedDebug(QtMsgType) : buf_(new QBuffer, later_deleter) {
2020-03-08 18:40:39 +01:00
buf_->open(QIODevice::WriteOnly);
// QDebug doesn't have a method to set a new io device, but swap() allows the devices to be swapped between two instances.
QDebug other(buf_.get());
swap(other);
}
2018-02-27 18:06:05 +01:00
2020-03-08 18:40:39 +01:00
// Delete function for the buffer. Since a base class is holding a reference to the raw pointer,
// it shouldn't be deleted until after the deletion of this object is complete.
2021-10-31 13:14:17 +01:00
static void later_deleter(QBuffer *b) { b->deleteLater(); }
2020-03-08 18:40:39 +01:00
std::shared_ptr<QBuffer> buf_;
};
// Debug message will be logged immediately.
class LoggedDebug : public DebugBase<LoggedDebug> {
public:
2021-06-21 15:38:58 +02:00
LoggedDebug() = default;
2020-06-26 22:41:38 +02:00
explicit LoggedDebug(QtMsgType t) : DebugBase(t) { nospace() << kMessageHandlerMagic; }
2020-03-08 18:40:39 +01:00
};
static void MessageHandler(QtMsgType type, const QMessageLogContext&, const QString &message) {
2019-09-15 20:27:32 +02:00
if (message.startsWith(kMessageHandlerMagic)) {
2022-09-12 22:39:08 +02:00
QByteArray message_data = message.toUtf8();
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", message_data.constData() + kMessageHandlerMagicLength);
fflush(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout);
2018-02-27 18:06:05 +01:00
return;
}
Level level = Level_Debug;
switch (type) {
case QtFatalMsg:
2020-03-08 18:40:39 +01:00
case QtCriticalMsg:
level = Level_Error;
break;
case QtWarningMsg:
level = Level_Warning;
break;
2018-02-27 18:06:05 +01:00
case QtDebugMsg:
2020-03-08 18:40:39 +01:00
default:
level = Level_Debug;
break;
2018-02-27 18:06:05 +01:00
}
2021-10-31 13:14:17 +01:00
for (const QString &line : message.split('\n')) {
2020-03-08 18:40:39 +01:00
BufferedDebug d = CreateLogger<BufferedDebug>(level, "unknown", -1, nullptr);
d << line.toLocal8Bit().constData();
if (d.buf_) {
d.buf_->close();
fprintf(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout, "%s\n", d.buf_->buffer().data());
fflush(type == QtCriticalMsg || type == QtFatalMsg ? stderr : stdout);
2020-03-08 18:40:39 +01:00
}
2018-02-27 18:06:05 +01:00
}
if (type == QtFatalMsg) {
abort();
}
2019-09-15 20:27:32 +02:00
2018-02-27 18:06:05 +01:00
}
void Init() {
2018-10-02 00:38:52 +02:00
2018-02-27 18:06:05 +01:00
delete sClassLevels;
delete sNullDevice;
sClassLevels = new QMap<QString, Level>();
sNullDevice = new NullDevice;
sNullDevice->open(QIODevice::ReadWrite);
// Catch other messages from Qt
if (!sOriginalMessageHandler) {
sOriginalMessageHandler = qInstallMessageHandler(MessageHandler);
}
2021-10-31 13:14:17 +01:00
2018-02-27 18:06:05 +01:00
}
void SetLevels(const QString &levels) {
if (!sClassLevels) return;
2021-07-11 09:49:38 +02:00
for (const QString &item : levels.split(',')) {
2018-02-27 18:06:05 +01:00
const QStringList class_level = item.split(':');
QString class_name;
bool ok = false;
int level = Level_Error;
if (class_level.count() == 1) {
level = class_level.last().toInt(&ok);
}
else if (class_level.count() == 2) {
class_name = class_level.first();
level = class_level.last().toInt(&ok);
}
if (!ok || level < Level_Error || level > Level_Debug) {
continue;
}
if (class_name.isEmpty() || class_name == "*") {
2020-06-15 17:59:02 +02:00
sDefaultLevel = static_cast<Level>(level);
2018-02-27 18:06:05 +01:00
}
else {
2020-06-15 17:59:02 +02:00
sClassLevels->insert(class_name, static_cast<Level>(level));
2018-02-27 18:06:05 +01:00
}
}
}
2020-03-08 18:40:39 +01:00
static QString ParsePrettyFunction(const char *pretty_function) {
2018-02-27 18:06:05 +01:00
// Get the class name out of the function name.
QString class_name = pretty_function;
2021-10-30 02:21:29 +02:00
const qint64 paren = class_name.indexOf('(');
2018-02-27 18:06:05 +01:00
if (paren != -1) {
2021-10-30 02:21:29 +02:00
const qint64 colons = class_name.lastIndexOf("::", paren);
2018-02-27 18:06:05 +01:00
if (colons != -1) {
class_name = class_name.left(colons);
}
else {
class_name = class_name.left(paren);
}
}
2021-10-30 02:21:29 +02:00
const qint64 space = class_name.lastIndexOf(' ');
2018-02-27 18:06:05 +01:00
if (space != -1) {
2021-07-11 09:49:38 +02:00
class_name = class_name.mid(space + 1);
2018-02-27 18:06:05 +01:00
}
return class_name;
2021-10-31 13:14:17 +01:00
2018-02-27 18:06:05 +01:00
}
2020-03-08 18:40:39 +01:00
template <class T>
2021-10-31 13:14:17 +01:00
static T CreateLogger(Level level, const QString &class_name, int line, const char *category) {
2018-02-27 18:06:05 +01:00
// Map the level to a string
const char *level_name = nullptr;
switch (level) {
case Level_Debug: level_name = " DEBUG "; break;
case Level_Info: level_name = " INFO "; break;
case Level_Warning: level_name = " WARN "; break;
case Level_Error: level_name = " ERROR "; break;
case Level_Fatal: level_name = " FATAL "; break;
}
2020-03-08 18:40:39 +01:00
QString filter_category = (category != nullptr) ? category : class_name;
2018-02-27 18:06:05 +01:00
// Check the settings to see if we're meant to show or hide this message.
Level threshold_level = sDefaultLevel;
2020-03-08 18:40:39 +01:00
if (sClassLevels && sClassLevels->contains(filter_category)) {
threshold_level = sClassLevels->value(filter_category);
2018-02-27 18:06:05 +01:00
}
if (level > threshold_level) {
2020-03-08 18:40:39 +01:00
return T();
2018-02-27 18:06:05 +01:00
}
QString function_line = class_name;
if (line != -1) {
function_line += ":" + QString::number(line);
}
2020-03-08 18:40:39 +01:00
if (category) {
function_line += "(" + QString(category) + ")";
}
2018-02-27 18:06:05 +01:00
QtMsgType type = QtDebugMsg;
if (level == Level_Fatal) {
type = QtFatalMsg;
}
2020-03-08 18:40:39 +01:00
T ret(type);
2021-10-31 13:14:17 +01:00
ret.nospace() << QDateTime::currentDateTime().toString("hh:mm:ss.zzz").toLatin1().constData() << level_name << function_line.leftJustified(32).toLatin1().constData();
2018-02-27 18:06:05 +01:00
return ret.space();
2021-10-31 13:14:17 +01:00
2018-02-27 18:06:05 +01:00
}
2021-08-24 17:52:08 +02:00
#ifdef Q_OS_UNIX
2020-04-23 21:08:28 +02:00
QString CXXDemangle(const QString &mangled_function);
2018-02-27 18:06:05 +01:00
QString CXXDemangle(const QString &mangled_function) {
2021-03-26 21:30:13 +01:00
int status = 0;
2021-06-20 19:04:08 +02:00
char *demangled_function = abi::__cxa_demangle(mangled_function.toLatin1().constData(), nullptr, nullptr, &status);
2018-02-27 18:06:05 +01:00
if (status == 0) {
QString ret = QString::fromLatin1(demangled_function);
free(demangled_function);
return ret;
}
return mangled_function; // Probably not a C++ function.
}
2021-08-24 17:52:08 +02:00
#endif // Q_OS_UNIX
2018-02-27 18:06:05 +01:00
2021-08-24 17:52:08 +02:00
#ifdef Q_OS_LINUX
2020-04-23 21:08:28 +02:00
QString LinuxDemangle(const QString &symbol);
2018-02-27 18:06:05 +01:00
QString LinuxDemangle(const QString &symbol) {
QRegularExpression regex("\\(([^+]+)");
QRegularExpressionMatch match = regex.match(symbol);
if (!match.hasMatch()) {
2018-02-27 18:06:05 +01:00
return symbol;
}
QString mangled_function = match.captured(1);
2018-02-27 18:06:05 +01:00
return CXXDemangle(mangled_function);
2021-10-31 13:14:17 +01:00
2021-08-24 17:52:08 +02:00
}
#endif // Q_OS_LINUX
#ifdef Q_OS_MACOS
QString DarwinDemangle(const QString &symbol);
QString DarwinDemangle(const QString &symbol) {
2022-03-22 21:09:05 +01:00
# if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0)
2021-08-24 17:52:08 +02:00
QStringList split = symbol.split(' ', Qt::SkipEmptyParts);
2022-03-22 21:09:05 +01:00
# else
2021-08-24 17:52:08 +02:00
QStringList split = symbol.split(' ', QString::SkipEmptyParts);
2022-03-22 21:09:05 +01:00
# endif
2021-08-24 17:52:08 +02:00
QString mangled_function = split[3];
return CXXDemangle(mangled_function);
2018-02-27 18:06:05 +01:00
}
2021-08-24 17:52:08 +02:00
#endif // Q_OS_MACOS
2018-02-27 18:06:05 +01:00
2020-04-23 21:08:28 +02:00
QString DemangleSymbol(const QString &symbol);
2018-02-27 18:06:05 +01:00
QString DemangleSymbol(const QString &symbol) {
2021-10-31 13:14:17 +01:00
2018-07-01 22:26:46 +02:00
#ifdef Q_OS_MACOS
2018-02-27 18:06:05 +01:00
return DarwinDemangle(symbol);
#elif defined(Q_OS_LINUX)
return LinuxDemangle(symbol);
#else
return symbol;
#endif
2021-10-31 13:14:17 +01:00
2018-02-27 18:06:05 +01:00
}
void DumpStackTrace() {
2021-10-31 13:14:17 +01:00
#ifdef HAVE_BACKTRACE
2021-07-11 09:49:38 +02:00
void *callstack[128];
2018-02-27 18:06:05 +01:00
int callstack_size = backtrace(reinterpret_cast<void**>(&callstack), sizeof(callstack));
2021-07-11 09:49:38 +02:00
char **symbols = backtrace_symbols(reinterpret_cast<void**>(&callstack), callstack_size);
2018-02-27 18:06:05 +01:00
// Start from 1 to skip ourself.
for (int i = 1; i < callstack_size; ++i) {
std::cerr << DemangleSymbol(QString::fromLatin1(symbols[i])).toStdString() << std::endl;
}
free(symbols);
#else
qLog(Debug) << "FIXME: Implement printing stack traces on this platform";
#endif
2021-10-31 13:14:17 +01:00
2018-02-27 18:06:05 +01:00
}
2021-10-31 13:14:17 +01:00
// These are the functions that create loggers for the rest of Strawberry.
// It's okay that the LoggedDebug instance is copied to a QDebug in these. It doesn't override any behavior that should be needed after return.
2020-03-08 18:40:39 +01:00
#define qCreateLogger(line, pretty_function, category, level) logging::CreateLogger<LoggedDebug>(logging::Level_##level, logging::ParsePrettyFunction(pretty_function), line, category)
2021-07-11 09:49:38 +02:00
QDebug CreateLoggerInfo(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Info); }
QDebug CreateLoggerFatal(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Fatal); }
QDebug CreateLoggerError(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Error); }
2018-02-27 18:06:05 +01:00
#ifdef QT_NO_WARNING_OUTPUT
2020-03-08 18:40:39 +01:00
QNoDebug CreateLoggerWarning(int, const char*, const char*) { return QNoDebug(); }
2018-02-27 18:06:05 +01:00
#else
2021-10-31 13:14:17 +01:00
QDebug CreateLoggerWarning(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Warning); }
2018-02-27 18:06:05 +01:00
#endif // QT_NO_WARNING_OUTPUT
#ifdef QT_NO_DEBUG_OUTPUT
2020-03-08 18:40:39 +01:00
QNoDebug CreateLoggerDebug(int, const char*, const char*) { return QNoDebug(); }
2018-02-27 18:06:05 +01:00
#else
2021-10-31 13:14:17 +01:00
QDebug CreateLoggerDebug(int line, const char *pretty_function, const char *category) { return qCreateLogger(line, pretty_function, category, Debug); }
2018-02-27 18:06:05 +01:00
#endif // QT_NO_DEBUG_OUTPUT
} // namespace logging
namespace {
2022-03-22 21:09:05 +01:00
template<typename T>
2021-07-11 09:49:38 +02:00
QString print_duration(T duration, const std::string &unit) {
2018-02-27 18:06:05 +01:00
return QString("%1%2").arg(duration.count()).arg(unit.c_str());
}
} // namespace
QDebug operator<<(QDebug dbg, std::chrono::seconds secs) {
dbg.nospace() << print_duration(secs, "s");
return dbg.space();
}