damn fast and seems to be reliable enough ...

-- 
Jérémy Zurcher
rte de Cully 29
1091 Grandvaux
+41 (0) 79 599 84 27
/*
 * Copyright(c) 2009 by Zurcher Jérémy
 *
 * Hydrogen
 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [[email protected]]
 *
 * http://www.hydrogen-music.org
 *
 * This program 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
 *
 */

// vim :!  g++ % -lpthread -march=i686 -o test && ./test

#define LOG_BUFFER_LEN 60

#include <ostream>

/**
 * cas RingBuffer for n writers and 1 reader
 */
class RingBuffer {
    public:
        ~RingBuffer();
        RingBuffer( unsigned n_buf, unsigned b_len );
        
        unsigned write( const char* msg, const unsigned len );
        std::ostringstream& flush( std::ostringstream &out );
	
    private:
        unsigned __n_buf;
        unsigned __b_len;
        unsigned __read_to;
        unsigned __read_from;
        unsigned __write_to;
        char** __buffer;
        char** __pointers;
};

#include <cstring>
#include <sstream>

RingBuffer::RingBuffer( unsigned n_buf, unsigned b_len ) {
    __n_buf = n_buf;
    __b_len = b_len;
    __buffer = new char*[__n_buf];
    if(__buffer==NULL) { throw 0;  }
    for(int i=0;i<__b_len;i++) {
        __buffer[i] = new char[__b_len];
        if(__buffer[i]==NULL) { throw 0; }
    }
    __pointers = new char*[__n_buf];
    memset( __pointers, 0, __n_buf);
    __write_to = __read_to = __read_from = 0;
}

RingBuffer::~RingBuffer() {
    for(int i=0;i<__b_len;i++) delete [] __buffer[i];
    delete [] __buffer;
    delete [] __pointers;
}

#include <cstdio>
unsigned RingBuffer::write( const char* msg, const unsigned len ) {
    char* dst;
    unsigned idx;
    unsigned l = ( len > __b_len ? __b_len : len );
    /* acquire write buffer and update it
     * this may override an old not outputed message if the ring is too shortly sized
     */
    for(;;){
        idx = __write_to;
        dst = __buffer[idx];
        if( __sync_bool_compare_and_swap( &__write_to, idx, (idx+1)%__n_buf ) ) break;
    }
    memcpy( dst, msg, l );
    /* notify buffer update */
    for(;;){
        idx = __read_to;
        if( __sync_bool_compare_and_swap( &__read_to, idx, (idx+1)%__n_buf ) ) break;
    }
    /* flush may output the previous pointed buffer before this assignment */
    __pointers[idx] = dst;
    return l;
}

std::ostringstream& RingBuffer::flush( std::ostringstream &out ) {
    while( __read_from != __read_to ) {
        if( __pointers[__read_from]) { out << __pointers[__read_from]; }
        __read_from = (__read_from+1)%__n_buf;
    }
    return out;
}

#include <pthread.h>

/**
 * Class for writing logs to the console
 */
class Logger
{
public:
	/* The log level setting is internally a bit-masked integer.
	 * These are the bits.  It is valid for the log level to *not*
	 * be one of these explicitly... however, you won't get proper
	 * syntax highlighting.  E.g. ~0 will log, but won't get any
	 * syntax highlighting.  Same with Error|Warning.
	 *
	 * This also allows for (future) debugging profiles.  For
	 * example, if you only want to turn on log messages in a
	 * specific section of code, you might do Logger::log( 0x80,
	 * ... ), and set the logging level to 0x80 or 0x81 (to
	 * include Error logs) with your debugger.
	 */
	typedef enum _log_level {
		None = 0,
		Error = 1,
		Warning = 2,
		Info = 4,
		Debug = 8,
                AELockTracing = 0x10
	} log_level_t;

        ~Logger();
        Logger( unsigned level, unsigned n_buf, unsigned b_len );
       
        bool should_log( unsigned lvl ) { return (lvl&__log_level); }
        void set_log_level( unsigned lvl ) { __log_level = lvl; }
        unsigned get_log_level() { return __log_level; }
        
        /* returns the number of writen characters */
        int log( log_level_t lvl, const char* cls_name, const char* fct_name, const char* msg, ... );
        
        static unsigned parse_log_level(const char* level);

    private:
        bool __running;
        unsigned __log_level;   // A bitmask of log_level_t
        RingBuffer *buffer;
        pthread_t loggerThread;
        static const char* __prefixes[];
        static const char* __suffix;
        friend void* loggerThread_func(void* param);
};


#include <cstring>
#include <cstdio>
#include <cstdarg>

void* loggerThread_func(void* param);

#define LOG_PREFIX_LEN 9
const char* Logger::__prefixes[] = { "\033[0m     ", "\033[31m(E) ", "\033[36m(W) ", "\033[32m(I) ", "\033[35m(D) ", "\033[0m     " };
#define LOG_SUFFIX_LEN 5
const char* Logger::__suffix = "\033[0m\n";

Logger::Logger( unsigned level, unsigned n_buf, unsigned b_len ) : __running(true), __log_level(level) {
    buffer = new RingBuffer( n_buf, b_len );
    pthread_attr_t attr;
    pthread_attr_init( &attr );
    pthread_create( &loggerThread, &attr, loggerThread_func, this );
}

Logger::~Logger(){
    __running = false;
    pthread_join( loggerThread, NULL );
}

int Logger::log( log_level_t lvl, const char* cls_name, const char* fct_name, const char* msg, ... ) {
    char tmp[LOG_BUFFER_LEN];
    /* prefix */
    int i;
    switch(lvl) {
        case Logger::None:      i = 0; break;
        case Logger::Error:     i = 1; break;
        case Logger::Warning:   i = 2; break;
        case Logger::Info:      i = 3; break;
        case Logger::Debug:     i = 4; break;
        default:                i = 0; break;
    }
    memcpy( tmp, __prefixes[i], LOG_PREFIX_LEN );
    i = LOG_PREFIX_LEN;
    int l;
    // TODO JEYZU check length !!!
    /* cls_name */
    if( cls_name ) {
        l = strlen(cls_name);
        memcpy( &tmp[i], cls_name, l );
        i+=l;
        tmp[i++]=':';
        tmp[i++]=':';
    }
    /* fct_name */
    if( fct_name ) {
        l = strlen(fct_name);
        memcpy( &tmp[i], fct_name, l );
        i+=l;
        tmp[i++]=' ';
    }
    /* msg */
    va_list args;
    va_start( args, msg );
    i += vsnprintf( &tmp[i], LOG_BUFFER_LEN-i, msg, args );
    va_end(args);
    /* suffix */
    if(LOG_BUFFER_LEN-i<(LOG_SUFFIX_LEN+1)) i = LOG_BUFFER_LEN-(LOG_SUFFIX_LEN+1);
    i += snprintf(&tmp[i],LOG_SUFFIX_LEN+1,"%s\n",__suffix);
    /* log */
    return buffer->write( tmp, i );
}

unsigned Logger::parse_log_level(const char* level) {
    const char none[] = "None";
    const char error[] = "Error";
    const char warning[] = "Warning";
    const char info[] = "Info";
    const char debug[] = "Debug";
    unsigned log_level;

    // insert hex-detecting code here.  :-)

    if( 0 == strncasecmp( level, none, sizeof(none) ) ) {
        log_level = Logger::None;
    } else if ( 0 == strncasecmp( level, error, sizeof(error) ) ) {
        log_level = Logger::Error;
    } else if ( 0 == strncasecmp( level, warning, sizeof(warning) ) ) {
        log_level = Logger::Error | Logger::Warning;
    } else if ( 0 == strncasecmp( level, info, sizeof(info) ) ) {
        log_level = Logger::Error | Logger::Warning | Logger::Info;
    } else if ( 0 == strncasecmp( level, debug, sizeof(debug) ) ) {
        log_level = Logger::Error | Logger::Warning | Logger::Info | Logger::Debug;
    } else {
        int val = 0; //H2Core::hextoi(level, -1);
        if( val == 0 ) {
            // Probably means hex was invalid.  Use -VNone instead.
            log_level = Logger::Error;
        } else {
            log_level = val;
        }
    }
    return log_level;
}

#include <iostream>
#include <sstream>

void* loggerThread_func(void* param) {
    if ( param == NULL ) {
        std::cerr << "Logger instance missing" << std::endl;
        return NULL;
    }
    Logger *logger = ( Logger* )param;
    RingBuffer *buffer = logger->buffer;
    while ( logger->__running ) {
        usleep( 1000 );
        std::ostringstream o;
        std::cerr << buffer->flush( o ).str();
        std::cerr.flush();
    }
}

#include <map>
#include <iostream>

/**
 * Base class.
 */
class Object {
    public:
        typedef std::map<const char*, int> object_count_t;
        typedef std::map<const Object*, const char*> object_ref_t;

        Object( const char* class_name ) { if(__debug) add_object( this, class_name ); }
        Object( const Object& obj ) { if(__debug) add_object( this, obj.class_name() ); }
        ~Object() { if(__debug) del_object( this ); }

        virtual const char* class_name() const = 0;

	static bool debug_active() { return __debug; };
	static void set_debug( bool status ) { __debug=status; };
        static int objects_number() { return __objects_refs.size(); };

        static void write_objects_map_to(std::ostream &out );
        static void write_objects_map_to_cerr() { Object::write_objects_map_to( std::cerr ); }
       
        /* called from Logger::Logger(...); */
        static void bootstrap( Logger *logger, bool debug=false ) {
            __logger = logger;
            __debug = debug;
            pthread_mutex_init( &__mutex, NULL );
        }
        static Logger* logger() { return __logger; }

    private:
        static void del_object( const Object* obj );
        static void add_object( const Object* obj, const char* class_name );
        
        static bool __debug;
        static object_count_t __objects_count;
        static object_ref_t __objects_refs;
	static pthread_mutex_t __mutex;

    protected:
        static Logger* __logger;
};

#define __LOG_METHOD( lvl, ... ) \
    if( __logger->should_log( (lvl) ) ) { __logger->log( (lvl), class_name(), __FUNCTION__, __VA_ARGS__ ); }

#define __LOG_CLASS( lvl, ... ) \
    if( logger()->should_log( (lvl) ) ) { logger()->log( (lvl), st_class_name(), __FUNCTION__, __VA_ARGS__ ); }

#define __LOG_THREAD( lvl, ... ) \
    if( __object->logger()->should_log( (lvl) ) ) { __object->logger()->log( (lvl), 0, __PRETTY_FUNCTION__, __VA_ARGS__ ); }
/*
#define __LOG( lvl, ... ) \
    if( __logger->should_log( (lvl) ) ) { __logger->log( (lvl), 0, 0, __VA_ARGS__ ); }
*/

/* Object instance method logging macros */
#define LOG_DEBUG( ... )    __LOG_METHOD( Logger::Debug,    __VA_ARGS__ )
#define LOG_INFO( ... )     __LOG_METHOD( Logger::Info,     __VA_ARGS__ )
#define LOG_WARN( ... )     __LOG_METHOD( Logger::Warning,  __VA_ARGS__ )
#define LOG_ERROR( ... )    __LOG_METHOD( Logger::Error,    __VA_ARGS__ )

/* Object instance method logging macros */
#define LOG_DEBUG_ST( ... ) __LOG_CLASS( Logger::Debug,   __VA_ARGS__ )
#define LOG_INFO_ST( ... )  __LOG_CLASS( Logger::Info,    __VA_ARGS__ )
#define LOG_WARN_ST( ... )  __LOG_CLASS( Logger::Warning, __VA_ARGS__ )
#define LOG_ERROR_ST( ... ) __LOG_CLASS( Logger::Error,   __VA_ARGS__ )

/* thread logging macros : Object* __object = ( Object* )param; */
#define LOG_DEBUG_TH( ... ) __LOG_THREAD( Logger::Debug,   __VA_ARGS__ )
#define LOG_INFO_TH( ... )  __LOG_THREAD( Logger::Info,    __VA_ARGS__ )
#define LOG_WARN_TH( ... )  __LOG_THREAD( Logger::Warning, __VA_ARGS__ )
#define LOG_ERROR_TH( ... ) __LOG_THREAD( Logger::Error,   __VA_ARGS__ )

/* convert QtCore/QString into char* */
#define LOG_QS( qs ) ( (qs).toLatin1().data() )

/* Object inherited class declaration macro */
#define H2_OBJECT                                                               \
    private: virtual const char* class_name() const { return __class_name; }     \
    private: static const char* st_class_name() { return __class_name; }         \
    private: static const char* __class_name;                                   \

#include <cassert>
#include <sstream>
#include <cstdlib>

/* Object class instance */
bool Object::__debug = false;
pthread_mutex_t Object::__mutex;
Logger* Object::__logger = 0;
Object::object_count_t Object::__objects_count;
Object::object_ref_t Object::__objects_refs;


void Object::add_object( const Object* obj, const char* class_name ) {
    pthread_mutex_lock( &__mutex );
    if( __objects_count.size()==0) atexit( Object::write_objects_map_to_cerr );
    __objects_count[ class_name ]++;
    __objects_refs[obj ] = class_name;
    pthread_mutex_unlock( &__mutex );
}

void Object::del_object( const Object* obj ) {
    object_ref_t::iterator it_ref = __objects_refs.find( obj );
    if ( it_ref==__objects_refs.end() ) {
        std::cerr << "delete an unreferenced object " << obj << std::endl;
        return;
    }
    object_count_t::iterator it_count = __objects_count.find( (*it_ref).second );
    if ( it_count==__objects_count.end() ) {
        std::cerr << "delete a referenced but not counted object " << obj << " " << (*it_ref).second << std::endl;
        return;
    }
    pthread_mutex_lock( &__mutex );
    assert(__objects_count[ (*it_count).first ]>0);
    __objects_refs.erase( it_ref );
    __objects_count[ (*it_count).first ]--;
    pthread_mutex_unlock( &__mutex );
}

void Object::write_objects_map_to(std::ostream &out ) {
    if(!__debug) {
        out << "object tracing not active" << std::endl;
        return;
    }
    int n = 0;
    std::ostringstream o;
    pthread_mutex_lock( &__mutex );
    object_count_t::iterator it = __objects_count.begin();
    while ( it != __objects_count.end() ) {
        o << "\t[ " << (*it).first << " ]\t" << (*it).second << std::endl;
        n+=(*it).second;
        it++;
    }
    pthread_mutex_unlock( &__mutex );
    out << std::endl << "Objects map : " << std::endl << o.str() << "Total : " << n << " objects." << std::endl << std::endl;
    return;
}

/****************************************************************************************************************/
class Dev1 : public Object {
    H2_OBJECT
    public:
        Dev1() :Object(__class_name) {
            LOG_DEBUG( msg_create, class_name() )
        }
        ~Dev1() {
            LOG_WARN( msg_destroy, class_name() )
        }
        static void st_hello( char* name ) {
            LOG_INFO_ST( "Hello %s - %.3f",name, 123.456789 )
        }
        void say_hello( char* name ) {
            LOG_INFO( "Hello %s - %.2f",name, 123.456789 )
        }

        static const char *msg_create;
        static const char *msg_destroy;
};
const char* Dev1::__class_name="Dev1";
const char* Dev1::msg_create = "%s Object Creation";
const char* Dev1::msg_destroy = "%s Object Destruction";

/****************************************************************************************************************/
class Dev2 : public Object {
    H2_OBJECT
    public:
        Dev2()  :Object(__class_name){ }
        void Destroy() { }
};
const char* Dev2::__class_name="Dev2";

/****************************************************************************************************************/
int run() {
    Dev1 a = Dev1(); Dev1 b = Dev1(); Dev2 c = Dev2(); Dev2 d = Dev2(); Dev2 e = Dev2( d );
    a.say_hello( (char*)"Jeremy" );
    Object* o = &a;
    Dev1::st_hello( (char*)"static" );
    a.say_hello( (char*)"Corina" );
    Object::write_objects_map_to_cerr();
    a.say_hello( (char*)"Roisin" );
}
int main () {
    Logger *l = new Logger( Logger::Warning|Logger::Debug|Logger::Info, 10, LOG_BUFFER_LEN );
    Object::bootstrap( l, true );
    run();
    usleep(5000);
}
------------------------------------------------------------------------------
This SF.Net email is sponsored by the Verizon Developer Community
Take advantage of Verizon's best-in-class app development support
A streamlined, 14 day to market process makes app distribution fast and easy
Join now and get one step closer to millions of Verizon customers
http://p.sf.net/sfu/verizon-dev2dev 
_______________________________________________
Hydrogen-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/hydrogen-devel

Reply via email to