Personal tools

Sistem de depanare a aplicatiilor in limbajul C-plus-plus

From linux360

Jump to: navigation, search

Acest tutorial de programare este realizat cu ajutorul Bibliotecii Qt versiunea 3.x, dar se poate adapta uşor folosind orice altă bibliotecă.



Când dezvoltăm aplicaţii de proporţii (sau poate mai simple), pot aparea momente în care aplicaţia noastră să nu mai facă ce ne aşteptam noi sa faca şi incepem să ne punem (firesc) întrebarea "Acum ce s-a mai întâmplat?". Pentru a ne ajuta în această privinţă, ne putem construi o mulţime de unelte. Voi enumera o parte dintre ele:

  • depanare prin executia programului folosind breakpoints si/sau linie cu linie;
  • scrierea unor mesaje într-un fişier de log;
  • scrierea unor mesaje într-o consola;
  • aruncarea şi prinderea excepţiilor;

Folosirea unor breakpoints nu face cazul acestui tutorial. Aruncarea şi prinderea excepţiilor este o tehnologie mult prea complex pentru a fi detaliat aici; există suficient material pe internet pentru aceasta.


Mesaje intr-o consola

Sa presupunem ca la un moment dat avem nevoie sa obtinem valorile unor variabile care nu se pot afla prin breakpoints; de exemplu într-o funcţie de reîmprospatare a unei ferestre, care se apelează într-un ciclu infinit. Aceste valori le putem afla prin două modalitaţi: scrierea lor intr-un fişier de log sau afişarea lor intr-o consolă. Fiecare dintre cele două are avantaje şi dezavantaje.


consolă - avantaje (faţă de log):

  • mesajele se pot urmări În timpul rulării programului
  • nu necesită spaţiu suplimentar pe Hard Disk Drive (HDD)


consolă - dezavantaje (faţă de log):

  • consumă resurse suplimentare


  • Crearea consolei

- kdsconsole.h

/***************************************************************************
*   Copyright (C) 2005 Radu Bolovan                                       *
*   radubolovan@dainet.ro                                                 *
*                                                                         *
*   This is free software; you can redistribute it and/or modify it       *
*   under the terms of the GNU General Public License as published by     *
*   the Free Software Foundation; either version 2 of the License, or     *
*   (at your option) any later version.                                   *
*                                                                         *
*   This program is distributed in the hope that it will be useful,       *
*   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
*   GNU General Public License for more details.                          *
*                                                                         *
*   You should have received a copy of the GNU General Public License     *
*   along with this program; if not, write to the                         *
*   Free Software Foundation, Inc.,                                       *
*   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
***************************************************************************/
#ifndef KDSCONSOLE_H
#define KDSCONSOLE_H

#include <qtextedit.h>

class QTimer;

class KDSConsole : public QTextEdit
{
    Q_OBJECT
public:
    /* constructor */
    KDSConsole( QWidget *parent = 0, const char *strLogFileName = 0 );

    /* destructor */
    ~KDSConsole();

    /* activare salvare automata intr-un fisier */
    void enableAutoSave( bool bEnable, int nTime = 2000 );

    /* seteaza fisierul in care sa salveze */
    void setLogFileName( QString strLogFileName );

public slots:
    /* salveaza in fisier */
    void saveToLogFile();

protected:
    /* se apeleaza DOAR cand se apasa butonul drept al mouse-ului
     * ----- vezi kdsconsole.cpp -----
    */
    void contentsMousePressEvent( QMouseEvent *event );

protected:
    /* numele fisierului in care se salveaza */
    char *m_strLogFileName;

    /* timer-ul */
    QTimer *m_pTimer;
};

extern KDSConsole *g_pConsole;

#endif

- kdsconsole.cpp

#include "kdsconsole.h"

#include <qtimer.h>
#include <qaction.h>
#include <qevent.h>
#include <qpopupmenu.h>

KDSConsole *g_pConsole = NULL;

KDSConsole::KDSConsole( QWidget *parent, const char *strLogFileName )
: QTextEdit( parent )
{
    /* nu trebuie sa se editeze */
    setReadOnly( true );

    /* aliniez textul la stanga */
    setAlignment ( Qt::AlignLeft );

    /* daca consola se creaza de sine statatoare
     * (intr-o alta fereastra decat a aplicatiei, sau a unei clase a aplicatiei)
     * setez dimensiunile ferestrei
    */
    if( !parent )
        setMinimumSize( 640, 150 );

    /* setez numele fisierul in care se va scrie
     * strLogFileName este numele fisierului
    */
    if( strLogFileName )
    {
        m_strLogFileName = new char [ strlen( strLogFileName ) + 1 ];
        strcpy( m_strLogFileName, strLogFileName );
    }
    else
        m_strLogFileName = NULL;

    /* setez un ceas pentru optiunea de auto salvare */
    m_pTimer = new QTimer( this );
    connect( m_pTimer, SIGNAL( timeout() ), this, SLOT( saveToLogFile() ) );

    setMouseTracking( true );
}

KDSConsole::~KDSConsole()
{
    /* daca exista un fisier atasat, ii sterg numele din memorie */
    if( m_strLogFileName )
    {
        delete []m_strLogFileName;
        m_strLogFileName = NULL;
    }
}

void KDSConsole::enableAutoSave( bool bEnable, int nTime )
{
    if( bEnable )
        m_pTimer->start( nTime );
    else
        m_pTimer->stop();
}

void KDSConsole::setLogFileName( QString strLogFileName )
{
    unsigned int nLength = strLogFileName.length() + 1;

    /* daca exista deja un fisier atasat si daca numele lui este mai mic decat cel curent,
     * el (numele) trebuie realocat dinamic
    */
    if( m_strLogFileName )
    {
        if( strlen( m_strLogFileName ) < nLength )
        {
            delete []m_strLogFileName;
            m_strLogFileName = new char[ nLength ];
            }
    }
    else
        m_strLogFileName = new char[ nLength ];

    unsigned int nIndex;

    /* copiez numele fisierului curent "in" cel al clasei */
    for( nIndex = 0; nIndex < nLength - 1; nIndex++ )
        m_strLogFileName[ nIndex ] = strLogFileName.at( nIndex ).latin1();
    m_strLogFileName[ nIndex ] = '\0';
}

void KDSConsole::saveToLogFile()
{
    /* daca nu exista un fisier atasat
     * atunci afisez un mesaj de eroare in consola
     * si ies din functie
     */
    if( !m_strLogFileName )
    {
        append( "I cannot find an attached file" );
        return;
    }

    FILE *file = fopen( m_strLogFileName, "w" );

    /* daca nu se poate crea fisierul cu numele dat,
     * afisez un mesaj in consola si ies din functie
    */
    if( !file )
    {
        QString strTmp = "I cannot create file " + (QString)m_strLogFileName;
        append( strTmp );
        return;
    }

    /* obtin textul din consola si il convertesc QString la char* */
    QString strTmp = text();
    int nLength = strTmp.length() + 1;
    char *strText = new char[ nLength ];
    int nIndex;
    for( nIndex = 0; nIndex < nLength - 1; nIndex++ )
        strText[ nIndex ] = strTmp.at( nIndex ).latin1();
    strText[ nIndex ] = '\0';

    /* scriu in fisier textul din consola */
    fprintf( file, "%s", strText );

    /* daca nu se poate inchide fisierul, se afiseaza un mesaj in consola */
    if( fclose( file ) == 0 )
         append( "Saved to log" );
    else
         append( "I couldn't close the file" );

    delete []strText;
}

void KDSConsole::contentsMousePressEvent( QMouseEvent *event )
{
    /* in cazul in care se apasa butonul drept al mouse-ului
     * se creaza un meniu
    */
    if( event->button() == Qt::RightButton )
    {
        QPopupMenu *menu = new QPopupMenu( this );
        menu->insertItem( tr( "Save to log file" ), this, SLOT( saveToLogFile() ) );
        menu->setMouseTracking( true );
        menu->exec( mapToGlobal( event->pos() ) );
    }
}

Dupa cum se vede am declarat şi definit o variabilă globală de tipul KDSConsole, externă - pentru a o putea apela din orice fişier. Pentru folosirea unui astfel de obiect (KDSConsole) voi mai adăuga incă două metode, faţă de cel obişnuit: g_pConsole = new KDSConsole();

g_pConsole->setCaption( "KDSConsole" );
g_pConsole->show();

//ceva cod

g_pConsole->append( "Test in consola" ); //functia append() apartine clasei QTextEdit

//alt cod

g_pConsole->hide();
delete g_pConsole;
g_pConsole = NULL;

1) Funcţii statice ale unei clase:

- kdsutils.h

#ifndef KDSUTILS_H
#define KDSUTILS_H

#include "kdsconsole.h"

#include <qstring.h>

class KDSUtils
{
    public:
    KDSUtils(){;}
    ~KDSUtils(){;}

    static void initConsole(){ g_pConsole = new KDSConsole(); g_pConsole->setCaption( "KDSConsole" ); g_pConsole->Show(); }
    static void appendToConsole( QString str ){ if( g_pConsole ) g_pConsole->append( str ); }
    static void setConsoleFileName( QString str ){ if( g_pConsole ) g_pConsole->setLogFileName( str ); }
    static void setConsoleAutoSave( bool bEnable, int nTime ){ if( g_pConsole ) g_pConsole->enableAutoSave( bEnable, nTime ); }
    static void saveConsoleToFile() { if( g_pConsole ) g_pConsole->saveToLogFile(); }
    static void destroyConsole(){ if( g_pConsole ){ g_pConsole->hide(); delete g_pConsole; g_pConsole = NULL; } }
};
 
#endif

Acestea se pot apela (după cum se cunoaşte) prin: KDSUtils::initConsole(); //numai la initializarea aplicatiei KDSUtils::setConsoleFileName( "/home/Radu/kdsconsole.txt" ); KDSUtils::setConsoleAutoSave( true, 60000 ); // salveaza o data pe minut - 1000 milisecunde = o secunda

//ceva cod

KDSUtils::ippendToConsole( "Un text in consola" );

//alt cod

KDSUtils::destroyConsole(); //cand nu mai avem nevoie de consola

2) Macro-uri:

- kdsdefs.h

#ifndef KDSUTILS_H
#define KDSUTILS_H

#include "kdsconsole.h"

#define INIT_CONSOLE() KDSUtils::initConsole()
#define SET_CONSOLE_FILE_NAME( fileName ) KDSUtils::setConsoleFileName( fileName )
#define SET_CONSOLE_AUTOSAVE( enable, time ) KDSUtils::setConsoleAutoSave( enable, time )
#define SAVE_CONSOLE_TO_FILE() KDSUtils::saveConsoleToFile()
#define KDS_TRACE( string ) KDSUtils::appendToConsole( string )
#define DESTROY_CONSOLE() KDSUtils::destroyConsole()

#endif

Observatii:

1) Nu folosiţi mai mult de una dintre cele trei metode!!!

2) Acelaşi efect se poate obţine punând

printf( "Un text catre consola" );

oriunde in cod si rularea programului din consolă (KConsole)

3) Funcţia de adăugare a unui text în consolă se mai poate implementa astfel încat sa primească un numar nelimitat de parametrii şi să funcţioneze analog funcţiei sprintf().

--Radu Bolovan 01:32, 21 December 2005 (EET)