1
/******************************************************************************
2
 * Copyright (C) 2003-2005 Max Howell <max.howell@methylblue.com>             *
3
 *           (C) 2007-2009 Mark Kretschmann <kretschmann@kde.org>             *
4
 *                                                                            *
5
 * This program is free software; you can redistribute it and/or              *
6
 * modify it under the terms of the GNU General Public License as             *
7
 * published by the Free Software Foundation; either version 2 of             *
8
 * the License, or (at your option) any later version.                        *
9
 *                                                                            *
10
 * This program is distributed in the hope that it will be useful,            *
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              *
13
 * GNU General Public License for more details.                               *
14
 *                                                                            *
15
 * You should have received a copy of the GNU General Public License          *
16
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.      *
17
 ******************************************************************************/
18
19
#ifndef AMAROK_DEBUG_H
20
#define AMAROK_DEBUG_H
21
22
// We always want debug output available at runtime
23
#undef QT_NO_DEBUG_OUTPUT
24
#undef KDE_NO_DEBUG_OUTPUT
25
26
#include <KGlobal>
27
#include <KCmdLineArgs>
28
#include <KConfig>
29
#include <KConfigGroup>
30
#include <kdebug.h>
31
#include <QApplication>
32
#include <QMutex>
33
#include <QObject>
34
35
#include <sys/time.h>
36
#include <unistd.h>
37
#include <iostream>
38
#include <cerrno>
39
40
#include "amarok_export.h"
41
42
#ifdef _WIN32
43
#define __PRETTY_FUNCTION__ __FUNCTION__
44
#endif
45
46
/**
47
 * @namespace Debug
48
 * @short kdebug with indentation functionality and convenience macros
49
 * @author Max Howell <max.howell@methylblue.com>
50
 *
51
 * Usage:
52
 *
53
 *     #define DEBUG_PREFIX "Blah"
54
 *     #include "debug.h"
55
 *
56
 *     void function()
57
 *     {
58
 *        Debug::Block myBlock( __PRETTY_FUNCTION__ );
59
 *
60
 *        debug() << "output1" << endl;
61
 *        debug() << "output2" << endl;
62
 *     }
63
 *
64
 * Will output:
65
 *
66
 * app: BEGIN: void function()
67
 * app:   [Blah] output1
68
 * app:   [Blah] output2
69
 * app: END: void function(): Took 0.1s
70
 *
71
 * @see Block
72
 * @see CrashHelper
73
 * @see ListStream
74
 */
75
76
namespace Debug
77
{
78
    extern AMAROK_EXPORT QMutex mutex; // defined in App.cpp
79
80
    // we can't use a statically instantiated QString for the indent, because
81
    // static namespaces are unique to each dlopened library. So we piggy back
82
    // the QString on the KApplication instance
83
84
    #define qOApp reinterpret_cast<QObject*>(qApp)
85
    class Indent : QObject
86
    {
87
        friend QString &modifieableIndent();
88
        Indent() : QObject( qOApp ) { setObjectName( "DEBUG_indent" ); }
89
        QString m_string;
90
    };
91
92
    inline QString &modifieableIndent()
93
    {
94
        QObject* o = qOApp ? qOApp->findChild<QObject*>( "DEBUG_indent" ) : 0;
95
        QString &ret = (o ? static_cast<Indent*>( o ) : new Indent)->m_string;
96
        return ret;
97
    }
98
99
    inline QString indent()
100
    {
101
        return modifieableIndent();
102
    }
103
104
    inline bool debugEnabled()
105
    {
106
        KConfigGroup config = KGlobal::config()->group( "General" );
107
        const bool debug = config.readEntry( "Debug Enabled", false );
108
        
109
        return debug; 
110
    }
111
112
    inline kdbgstream dbgstream()
113
    {
114
        return debugEnabled() ? kdbgstream( QtDebugMsg ) : kDebugDevNull();
115
    }
116
 
117
    #undef qOApp
118
119
    #ifndef DEBUG_PREFIX
120
    #define AMK_PREFIX ""
121
    #else
122
    #define AMK_PREFIX "[" DEBUG_PREFIX "]"
123
    #endif
124
125
    //from kdebug.h
126
    enum DebugLevels {
127
        KDEBUG_INFO  = 0,
128
        KDEBUG_WARN  = 1,
129
        KDEBUG_ERROR = 2,
130
        KDEBUG_FATAL = 3
131
    };
132
133
        
134
    static inline kdbgstream debug()   { mutex.lock(); QString ind = indent(); mutex.unlock(); return dbgstream() << qPrintable( "amarok: " + ind + AMK_PREFIX ); }
135
    static inline kdbgstream warning() { mutex.lock(); QString ind = indent(); mutex.unlock(); return dbgstream() << qPrintable( "amarok: " + ind + AMK_PREFIX + " [WARNING!]" ); }
136
    static inline kdbgstream error()   { mutex.lock(); QString ind = indent(); mutex.unlock(); return dbgstream() << qPrintable( "amarok: " + ind + AMK_PREFIX + " [ERROR!]" ); }
137
    static inline kdbgstream fatal()   { mutex.lock(); QString ind = indent(); mutex.unlock(); return dbgstream() << qPrintable( "amarok: " + ind + AMK_PREFIX ); }
138
139
    #undef AMK_PREFIX
140
141
    static inline void perfLog( const QString &message, const QString &func )
142
    {
143
#ifdef Q_OS_UNIX
144
        if( !debugEnabled() )
145
        {
146
            return;
147
        }
148
        QString str = QString( "MARK: %1: %2 %3" ).arg( KCmdLineArgs::appName(), func, message );
149
        access( str.toLocal8Bit().data(), F_OK );
150
#endif
151
    }
152
}
153
154
using Debug::debug;
155
using Debug::warning;
156
using Debug::error;
157
using Debug::fatal;
158
159
/// Standard function announcer
160
#define DEBUG_FUNC_INFO { Debug::mutex.lock(); kDebug() << Debug::indent() ; Debug::mutex.unlock(); }
161
162
/// Announce a line
163
#define DEBUG_LINE_INFO { Debug::mutex.lock(); kDebug() << Debug::indent() << "Line: " << __LINE__; Debug::mutex.unlock(); }
164
165
#ifdef __SUNPRO_CC
166
#define __PRETTY_FUNCTION__ __FILE__
167
#endif
168
169
/// Convenience macro for making a standard Debug::Block
170
#define DEBUG_BLOCK Debug::Block uniquelyNamedStackAllocatedStandardBlock( __PRETTY_FUNCTION__ );
171
172
/// Use this to remind yourself to finish the implementation of a function
173
#define AMAROK_NOTIMPLEMENTED warning() << "NOT-IMPLEMENTED: " << __PRETTY_FUNCTION__ << endl;
174
175
/// Use this to alert other developers to stop using a function
176
#define AMAROK_DEPRECATED warning() << "DEPRECATED: " << __PRETTY_FUNCTION__ << endl;
177
178
/// Performance logging
179
#define PERF_LOG( msg ) { Debug::perfLog( msg, __PRETTY_FUNCTION__ ); }
180
181
namespace Debug
182
{
183
    /**
184
     * @class Debug::Block
185
     * @short Use this to label sections of your code
186
     *
187
     * Usage:
188
     *
189
     *     void function()
190
     *     {
191
     *         Debug::Block myBlock( "section" );
192
     *
193
     *         debug() << "output1" << endl;
194
     *         debug() << "output2" << endl;
195
     *     }
196
     *
197
     * Will output:
198
     *
199
     *     app: BEGIN: section
200
     *     app:  [prefix] output1
201
     *     app:  [prefix] output2
202
     *     app: END: section - Took 0.1s
203
     *
204
     */
205
206
    class Block
207
    {
208
        timeval     m_start;
209
        const char *m_label;
210
211
    public:
212
        Block( const char *label )
213
                : m_label( label )
214
        {
215
            if ( gettimeofday( &m_start, 0 ) == -1 )
216
                dbgstream() << "amarok: Block - gettimeofday failed with "
217
                            << strerror(errno);
218
219
            if( !debugEnabled() ) return;
220
221
            mutex.lock();
222
223
            dbgstream() << qPrintable( "amarok: " + indent() + "BEGIN: " + label );
224
            Debug::modifieableIndent() += "  ";
225
            mutex.unlock();
226
        }
227
228
        ~Block()
229
        {
230
            if( !debugEnabled() ) return;
231
232
            mutex.lock();
233
            timeval end;
234
            int result = gettimeofday( &end, 0 );
235
            if( result == -1 )
236
            {
237
                mutex.unlock();
238
                return;
239
            }
240
241
            end.tv_sec -= m_start.tv_sec;
242
            if( end.tv_usec < m_start.tv_usec) {
243
                // Manually carry a one from the seconds field.
244
                end.tv_usec += 1000000;
245
                end.tv_sec--;
246
            }
247
            end.tv_usec -= m_start.tv_usec;
248
249
            double duration = double(end.tv_sec) + (double(end.tv_usec) / 1000000.0);
250
251
            Debug::modifieableIndent().truncate( Debug::indent().length() - 2 );
252
            dbgstream() << qPrintable( "amarok: " + indent() + "END__: " + m_label + " - Took " + QString::number( duration, 'g', 2 ) + "s" );
253
            mutex.unlock();
254
        }
255
    };
256
257
258
    /**
259
     * @name Debug::stamp()
260
     * @short To facilitate crash/freeze bugs, by making it easy to mark code that has been processed
261
     *
262
     * Usage:
263
     *
264
     *     {
265
     *         Debug::stamp();
266
     *         function1();
267
     *         Debug::stamp();
268
     *         function2();
269
     *         Debug::stamp();
270
     *     }
271
     *
272
     * Will output (assuming the crash occurs in function2()
273
     *
274
     *     app: Stamp: 1
275
     *     app: Stamp: 2
276
     *
277
     */
278
279
    inline void stamp()
280
    {
281
        static int n = 0;
282
        debug() << "| Stamp: " << ++n << endl;
283
    }
284
}
285
286
287
#include <QVariant>
288
289
namespace Debug
290
{
291
    /**
292
     * @class Debug::List
293
     * @short You can pass anything to this and it will output it as a list
294
     *
295
     *     debug() << (Debug::List() << anInt << aString << aQStringList << aDouble) << endl;
296
     */
297
298
    typedef QList<QVariant> List;
299
}
300
301
#endif