File indexing completed on 2024-05-19 04:49:32

0001 /*
0002     Copyright (c) 2003-2005 Max Howell <max.howell@methylblue.com>
0003     Copyright (c) 2007-2009 Mark Kretschmann <kretschmann@kde.org>
0004     Copyright (c) 2010-2011 Kevin Funk <krf@electrostorm.net>
0005 
0006     This library is free software; you can redistribute it and/or
0007     modify it under the terms of the GNU Lesser General Public
0008     License as published by the Free Software Foundation; either
0009     version 2.1 of the License, or (at your option) any later version.
0010 
0011     This library is distributed in the hope that it will be useful,
0012     but WITHOUT ANY WARRANTY; without even the implied warranty of
0013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014     Lesser General Public License for more details.
0015 
0016     You should have received a copy of the GNU Lesser General Public
0017     License along with this library.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "amarok_export.h"
0021 #include "core/support/Debug.h"
0022 #include "core/support/Debug_p.h"
0023 
0024 #include <KConfigCore/KConfigGroup>
0025 
0026 #include <QApplication>
0027 #include <QMutex>
0028 #include <QObject>
0029 
0030 #include <iostream>
0031 #include <unistd.h>
0032 
0033 // Define Application wide prefix
0034 #ifndef APP_PREFIX
0035 #define APP_PREFIX QLatin1String( "amarok:" )
0036 #endif
0037 
0038 #define DEBUG_INDENT_OBJECTNAME QLatin1String("Debug_Indent_object")
0039 
0040 AMAROKCORE_EXPORT QMutex Debug::mutex( QMutex::Recursive );
0041 
0042 using namespace Debug;
0043 
0044 static bool s_debugEnabled = false;
0045 static bool s_debugColorsEnabled = false;
0046 
0047 Q_GLOBAL_STATIC( NoDebugStream, s_noDebugStream )
0048 
0049 IndentPrivate::IndentPrivate(QObject* parent)
0050     : QObject(parent)
0051 {
0052     setObjectName( DEBUG_INDENT_OBJECTNAME );
0053 }
0054 
0055 /**
0056  * We can't use a statically instantiated QString for the indent, because
0057  * static namespaces are unique to each dlopened library. So we piggy back
0058  * the QString on the QApplication instance
0059  */
0060 IndentPrivate* IndentPrivate::instance()
0061 {
0062     QObject* qOApp = reinterpret_cast<QObject*>(qApp);
0063     QObject* obj = qOApp ? qOApp->findChild<QObject*>( DEBUG_INDENT_OBJECTNAME ) : nullptr;
0064     return (obj ? static_cast<IndentPrivate*>( obj ) : new IndentPrivate( qApp ));
0065 }
0066 
0067 /*
0068   Text color codes (use last digit here)
0069   30=black 31=red 32=green 33=yellow 34=blue 35=magenta 36=cyan 37=white
0070 */
0071 static int s_colors[] = { 1, 2, 4, 5, 6 }; // no yellow and white for sanity
0072 static int s_colorIndex = 0;
0073 
0074 static QString toString( DebugLevel level )
0075 {
0076     switch( level )
0077     {
0078         case KDEBUG_WARN:
0079             return QStringLiteral("[WARNING]");
0080         case KDEBUG_ERROR:
0081             return QStringLiteral("[ERROR__]");
0082         case KDEBUG_FATAL:
0083             return QStringLiteral("[FATAL__]");
0084         default:
0085             return QString();
0086     }
0087 }
0088 
0089 static int toColor( DebugLevel level )
0090 {
0091     switch( level ) {
0092         case KDEBUG_WARN:
0093             return 3; // red
0094         case KDEBUG_ERROR:
0095         case KDEBUG_FATAL:
0096             return 1; // yellow
0097         default:
0098             return 0; // default: black
0099     }
0100 }
0101 
0102 static QString colorize( const QString &text, int color = s_colorIndex )
0103 {
0104     if( !debugColorEnabled() )
0105         return text;
0106 
0107     return QStringLiteral( "\x1b[00;3%1m%2\x1b[00;39m" ).arg( QString::number(s_colors[color]), text );
0108 }
0109 
0110 static QString reverseColorize( const QString &text, int color )
0111 {
0112     if( !debugColorEnabled() )
0113         return text;
0114 
0115     return QStringLiteral( "\x1b[07;3%1m%2\x1b[00;39m" ).arg( QString::number(color), text );
0116 }
0117 
0118 QString Debug::indent()
0119 {
0120     return IndentPrivate::instance()->m_string;
0121 }
0122 
0123 bool Debug::debugEnabled()
0124 {
0125     return s_debugEnabled;
0126 }
0127 
0128 bool Debug::debugColorEnabled()
0129 {
0130     return s_debugColorsEnabled;
0131 }
0132 
0133 void Debug::setDebugEnabled( bool enable )
0134 {
0135     s_debugEnabled = enable;
0136 }
0137 
0138 void Debug::setColoredDebug( bool enable )
0139 {
0140     s_debugColorsEnabled = enable;
0141 }
0142 
0143 QDebug Debug::dbgstream( DebugLevel level )
0144 {
0145     if( !debugEnabled() )
0146         return QDebug( s_noDebugStream );
0147 
0148     mutex.lock();
0149     const QString currentIndent = indent();
0150     mutex.unlock();
0151 
0152     QString text = QStringLiteral("%1%2").arg( APP_PREFIX ).arg( currentIndent );
0153     if ( level > KDEBUG_INFO )
0154         text.append( ' ' + reverseColorize( toString(level), toColor( level ) ) );
0155 
0156     return QDebug( QtDebugMsg ) << qPrintable( text );
0157 }
0158 
0159 void Debug::perfLog( const QString &message, const QString &func )
0160 {
0161 #ifdef Q_OS_UNIX
0162     if( !debugEnabled() )
0163         return;
0164 
0165     QString str = QStringLiteral( "MARK: %1: %2 %3" ).arg( qApp->applicationName(), func, message );
0166     access( str.toLocal8Bit().data(), F_OK );
0167 #endif
0168 }
0169 
0170 Block::Block( const char *label )
0171     : m_label( label )
0172     , m_color( s_colorIndex )
0173 {
0174     if( !debugEnabled() )
0175         return;
0176 
0177     m_startTime.start();
0178 
0179     mutex.lock();
0180     s_colorIndex = (s_colorIndex + 1) % 5;
0181     dbgstream()
0182         << qPrintable( colorize( QLatin1String( "BEGIN:" ), m_color ) )
0183         << m_label;
0184     IndentPrivate::instance()->m_string += QLatin1String("  ");
0185     mutex.unlock();
0186 }
0187 
0188 Block::~Block()
0189 {
0190     if( !debugEnabled() )
0191         return;
0192 
0193     double duration = m_startTime.elapsed() / 1000.0;
0194 
0195     mutex.lock();
0196     IndentPrivate::instance()->m_string.truncate( Debug::indent().length() - 2 );
0197     mutex.unlock();
0198 
0199 #ifdef DEBUG_OVERRIDE_ELAPSED_TIME
0200     duration = DEBUG_OVERRIDE_ELAPSED_TIME;
0201 #endif
0202     // Print timing information, and a special message (DELAY) if the method took longer than 5s
0203     if( duration < 5.0 )
0204     {
0205         dbgstream()
0206             << qPrintable( colorize( QLatin1String( "END__:" ), m_color ) )
0207             << m_label
0208             << qPrintable( colorize( QString( "[Took: %3s]")
0209                                      .arg( QString::number(duration, 'g', 2) ), m_color ) );
0210     }
0211     else
0212     {
0213         dbgstream()
0214             << qPrintable( colorize( QString( "END__:" ), m_color ) )
0215             << m_label
0216             << qPrintable( reverseColorize( QString( "[DELAY Took (quite long) %3s]")
0217                                             .arg( QString::number(duration, 'g', 2) ), toColor( KDEBUG_WARN ) ) );
0218     }
0219 }
0220 
0221 void Debug::stamp()
0222 {
0223     static int n = 0;
0224     debug() << "| Stamp: " << ++n << Qt::endl;
0225 }