/* * This file is part of the Distributed Network Block Device 3 * * Copyright(c) 2011-2012 Simon Rettberg * * This file may be licensed under the terms of of the * GNU General Public License Version 2 (the ``GPL''). * * Software distributed under the License is distributed * on an ``AS IS'' basis, WITHOUT WARRANTY OF ANY KIND, either * express or implied. See the GPL for the specific language * governing rights and limitations. * * You should have received a copy of the GPL along with this * program. If not, go to http://www.gnu.org/licenses/gpl.html * or write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include "locks.h" #define MAX(a,b) (a > b ? a : b) static pthread_spinlock_t logLock; #define LINE_LEN 500 #define LINE_COUNT 50 typedef struct { uint16_t len; char text[LINE_LEN]; } LogLine; // This will be used as a ring buffer static volatile LogLine *logBuffer = NULL; // bufferPos counts up, use modulo LINE_COUNT to get array index static volatile int bufferPos = 0; void initmemlog() { // Use main spinlock to make sure we really init only once if ( logBuffer ) return; spin_init( &logLock, PTHREAD_PROCESS_PRIVATE ); logBuffer = (LogLine *)calloc( LINE_COUNT, sizeof(LogLine) ); } void memlogf(const char *fmt, ...) { if ( logBuffer == NULL ) { va_list ap; va_start( ap, fmt ); vprintf( fmt, ap ); va_end( ap ); printf( "\n" ); return; // Not initialized yet } va_list ap; int ret; time_t rawtime; struct tm *timeinfo; time( &rawtime ); timeinfo = localtime( &rawtime ); spin_lock( &logLock ); LogLine * const line = (LogLine *)&(logBuffer[bufferPos % LINE_COUNT]); const size_t offset = strftime( line->text, LINE_LEN, "[%d.%m. %H:%M:%S] ", timeinfo ); if ( offset == 0 ) *line->text = '\0'; va_start( ap, fmt ); ret = vsnprintf( line->text + offset, LINE_LEN - offset, fmt, ap ); va_end( ap ); char *end = line->text + strlen( line->text ); while ( end > line->text && *--end == '\n' ) *end = '\0'; // remove trailing \n // glibc 2.0 would return -1 if the buffer was too small // glibc 2.1 would return the number of bytes required if the buffer was too small // so to be safe either way, let strlen do the job line->len = strlen( line->text ); if ( ret > 0 || line->len > 0 ) ++bufferPos; spin_unlock( &logLock ); puts( line->text ); } char *fetchlog(int maxlines) { if ( !logBuffer ) return NULL ; if ( maxlines <= 0 || maxlines > LINE_COUNT ) maxlines = LINE_COUNT; const int start = MAX(0, bufferPos - maxlines); int len = 1, i; //printf("Outputting log from %d to %d\n", start, bufferPos); spin_lock( &logLock ); // Determine required buffer space for all log lines for (i = start; i < bufferPos; ++i) { if ( logBuffer[i % LINE_COUNT].len > 0 ) { len += logBuffer[i % LINE_COUNT].len + 1; } } //printf("Have to allocate %d bytes\n", len); // Allocate buffer. If this is a bottleneck because of malloc, consider passing a buffer to the function that the caller allocates on the stack char *retval = (char *)calloc( len, sizeof(char) ); if ( retval == NULL ) goto endFunction; // Concatenate all log lines, delimit using '\n' char *pos = retval; for (i = start; i < bufferPos; ++i) { LogLine * const line = (LogLine *)&(logBuffer[i % LINE_COUNT]); if ( line->len > 0 ) { memcpy( pos, (char *)line->text, line->len ); pos += line->len; *pos++ = '\n'; } } *pos = '\0'; endFunction: spin_unlock( &logLock ); return retval; }