Added Thomas Kurschel's fast_log module.

git-svn-id: file:///srv/svn/repos/haiku/trunk/current@7764 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-06-06 23:06:25 +00:00
parent 8a9c2f0e60
commit 5e3662534f
6 changed files with 1187 additions and 0 deletions

View File

@ -0,0 +1,10 @@
SubDir OBOS_TOP src add-ons kernel generic fast_log ;
UsePrivateHeaders kernel ;
KernelAddon fast_log : kernel generic :
clients.c
device.c
long_buffers.c
;

View File

@ -0,0 +1,599 @@
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Fast logging facilities.
Client interface.
Every client opens a connection, passing a list of event
ids together with their description. All logging calls
write the encoded event into a buffer, using the event id
to save space. Once the logging data is to be read, the
buffer is decoded and written to the long buffers. The
same happens when a client closes a connection, as the
association between event ids and descriptions would be
lost.
The buffer is split into a lower and an upper half, each
of size fast_log_buffer_size. The currently active half
is determined by bit 31 of fast_log_buffer_offset_alloc:
zero means lower, one means upper half.
During write, fast_log_buffer_offset_alloc is first
incremented by the requested number of bytes atomically.
If there is enough space, the even data is copied into
the buffer and fast_log_buffer_offset_filled is
atomically incremented as well. This allows concurrent
filling by different threads without using any locking.
If the buffer is too short, logging data is discarded.
For read out, fast_log_buffer_offset_alloc
is atomically set to the beginning of the opposite buffer,
making sure that no further events are appended to the
previously active buffer. Then, we wait for
fast_log_buffer_offset having reached the value of
fast_log_buffer_alloc when it was reset, i.e. until
all active writes are completed. Now, the buffer can
be read out savely.
To detect buffer overflow, buffer halves are set to
zero before they are activated. If a write fails because
lack of space, nothing is writting to the buffer, though
fast_log_buffer_offset and fast_log_buffer_alloc are
incremented. This leads to an unwritten entry in the log,
which content remains zero. The easier way is to detect
whether fast_log_alloc is byond buffer size (which is
done as well), but because entries have different sizes,
we don't know which was the lastly written entry.
*/
#include "fast_log_int.h"
#include "dl_list.h"
#include <KernelExport.h>
#include <stdio.h>
#include <string.h>
// one client connection
typedef struct fast_log_connection {
struct fast_log_connection *prev, *next;
// prefix in log
const char *prefix;
// list of event types
fast_log_event_type *types;
} fast_log_connection;
// one entry in logging buffer
typedef struct fast_log_entry {
// client handle.
// this must be first as it is checked against NULL
// by reader to detect overflown entries; moving it
// to a later offset would make reading of it dangerous
// as it could be located byond end of buffer
// (perhaps this could be handled as well, but it
// would make things more complicated)
fast_log_connection *handle;
// event type
int16 event;
// number of entries in "params"
int16 num_params;
// timestamp
bigtime_t time;
// parameters
uint32 params[1];
} fast_log_entry;
// list of clients
static fast_log_connection *fast_log_handles = NULL;
// to lock during access of fast_log_handles
sem_id fast_log_clients_sem;
// to lock during conversion of encoded buffer to long buffer
sem_id fast_log_convert_sem;
// logging buffer.
// it consists of 2*fast_log_buffer_size, see header
char *fast_log_buffer;
// offset in logging buffer for next write.
// bit 31 is set for upper buffer half and is rest for lower buffer half
uint32 fast_log_buffer_offset_alloc;
// offset of end of last write
uint32 fast_log_buffer_offset_filled;
// size of one half of log buffer
int fast_log_buffer_size = /*512*/ 32*1024;
// ToDo: cut down because of current allocator...
device_manager_info *pnp;
static int32
atomic_exchange(vint32 *value, int32 newValue)
{
int32 oldValue;
__asm__ __volatile__ (
"xchg %0,%1"
: "=r" (oldValue), "=m" (*value)
: "0" (newValue), "1" (*value)
);
return oldValue;
}
/** convert event code to text
* text_buffer - buffer used if text must is composed manually
* (must be at least 24 characters long)
*/
static const char *
fast_log_event2text(fast_log_connection *handle, int16 event, char *text_buffer)
{
fast_log_event_type *type;
for (type = handle->types; type->event != 0; ++type) {
if (type->event == event)
return type->description;
}
// longest message is 11 characters + 11 integer characters + zero byte
sprintf(text_buffer, "unknown (%d)", event);
return text_buffer;
}
/** decode one entry and write it to long buffer */
static void
fast_log_decode_entry(fast_log_entry *entry)
{
fast_log_connection *handle = entry->handle;
char buffer[100];
uint64 time;
uint32 min, sec, mill, mic;
char text_buffer[30];
int i;
time = entry->time; //entry->tsc / (sysinfo.cpu_clock_speed / 1000000);
mic = time % 1000;
time /= 1000;
mill = time % 1000;
time /= 1000;
sec = time % 60;
time /= 60;
min = time;
sprintf(buffer, "%03ld:%02ld:%03ld.%03ld ", min, sec, mill, mic);
fast_log_write_long_log(buffer);
fast_log_write_long_log(handle->prefix);
fast_log_write_long_log(": ");
fast_log_write_long_log(fast_log_event2text(handle, entry->event, text_buffer));
fast_log_write_long_log(" ");
for (i = 0; i < entry->num_params; ++i) {
if (i > 0)
fast_log_write_long_log(", ");
sprintf(buffer, "%08x", (uint)entry->params[i]);
fast_log_write_long_log(buffer);
}
fast_log_write_long_log("\n");
}
/** flush all compressed logging entries to long log */
void
fast_log_flush_buffer(void)
{
uint32 offset;
uint32 start, new_start, new_offset_alloc, size;
int i;
acquire_sem(fast_log_convert_sem);
// flip between lower and upper half of buffer
if ((fast_log_buffer_offset_alloc & (1L << 31)) == 0) {
start = 0;
new_start = fast_log_buffer_size;
new_offset_alloc = 1L << 31;
} else {
start = fast_log_buffer_size;
new_start = 0;
new_offset_alloc = 0;
}
// reset new buffer to zero to detect uninitialized, overflown entries
memset(fast_log_buffer + new_start, 0, fast_log_buffer_size);
// swap buffers
size = atomic_exchange(&fast_log_buffer_offset_alloc, new_offset_alloc);
// get rid of bit 31 flag
size &= ~(1 << 31);
// decrement offset of filled data by number of bytes allocated when
// the buffer was swapped; if all writers are done, the offset is
// >= 0 now, if some writers aren't finished, it is < 0
atomic_add(&fast_log_buffer_offset_filled, -size);
// wait until all writers are finished
for (i = 1000000; i >= 0; --i) {
if ((int32)fast_log_buffer_offset_filled >= 0)
break;
spin(1);
// ToDo: spin?? If that should be a "yield", it should be snooze(1).
}
// perhaps we are in a spinlock or something nasty like that,
// so the writer is blocked by us
if (i < 0) {
fast_log_write_long_log("<TIME-OUT> during waiting for log writers; some "
"events got lost; perhaps a client disconnected with a spinlock hold\n");
}
// dprintf( "start=%d, size=%d\n", (int)start, (int)size );
// uncompress log
for (offset = 0; offset < size; ) {
fast_log_entry *entry;
// check whether there was an overflow when the buffer
// had zero bytes left
if (offset > (uint32)fast_log_buffer_size) {
fast_log_write_long_log("<TRUNCATED1>\n");
break;
}
entry = (fast_log_entry *)&fast_log_buffer[start + offset];
// check for overflow when the buffer had some bytes left;
// in this case, the entry is uninitialized and thus contains
// all zeros
if (entry->handle == NULL) {
fast_log_write_long_log( "<TRUNCATED2>\n" );
// the entry may be partially byond end of buffer, so
// we must not access any other element of the entry
break;
} else {
// normal entry, so it's time to decode it
fast_log_decode_entry(entry);
}
offset += sizeof(fast_log_entry)
+ (entry->num_params - 1) * sizeof(entry->params[0]);
}
release_sem(fast_log_convert_sem);
}
/** start logging of one client */
static fast_log_handle
fast_log_start_log(const char *prefix, fast_log_event_type types[])
{
fast_log_connection *handle;
handle = malloc(sizeof(*handle));
if (handle == NULL)
return NULL;
handle->prefix = prefix;
handle->types = types;
acquire_sem(fast_log_clients_sem);
ADD_DL_LIST_HEAD(handle, fast_log_handles, );
release_sem(fast_log_clients_sem);
return handle;
}
/** close connection of one client */
static void
fast_log_stop_log(fast_log_handle handle)
{
if (handle == NULL)
return;
fast_log_flush_buffer();
acquire_sem(fast_log_clients_sem);
REMOVE_DL_LIST(handle, fast_log_handles, );
release_sem(fast_log_clients_sem);
free(handle);
}
#define ENTRY_SIZE(num_params) (sizeof(fast_log_entry) + sizeof(uint32) * ((num_params)-1))
/** allocate space in log */
static fast_log_entry *
fast_log_alloc_entry(fast_log_handle handle, int num_params)
{
uint32 offset, real_offset, chunk_offset;
fast_log_entry *entry;
if (handle == NULL)
return NULL;
// alloc space in buffer
offset = atomic_add( &fast_log_buffer_offset_alloc, ENTRY_SIZE( num_params ));
real_offset = offset & ~(1L << 31);
chunk_offset = ((offset & (1L << 31)) == 0 ? 0 : fast_log_buffer_size);
// take care of bit 31 trick to get the address of the new entry
entry = (fast_log_entry *)&fast_log_buffer[chunk_offset + real_offset];
if (real_offset + ENTRY_SIZE( num_params ) < (uint32)fast_log_buffer_size) {
// there is enough space in buffer, so we can fill it
entry->time = system_time();
entry->handle = handle;
entry->num_params = num_params;
return entry;
} else {
// buffer overflow, return NULL to notify caller
atomic_add( &fast_log_buffer_offset_filled, ENTRY_SIZE( num_params ));
return NULL;
}
}
/** log event without parameters */
static void
fast_log_log_0(fast_log_handle handle, int event)
{
fast_log_entry *entry;
entry = fast_log_alloc_entry(handle, 0);
if (entry != NULL) {
entry->event = event;
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(0));
}
}
/** log event with one parameter */
static void
fast_log_log_1(fast_log_handle handle, int event, uint32 param1)
{
fast_log_entry *entry;
entry = fast_log_alloc_entry(handle, 1);
if (entry != NULL) {
entry->event = event;
entry->params[0] = param1;
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(1));
}
}
/** log event with two parameters */
static void
fast_log_log_2(fast_log_handle handle, int event, uint32 param1, uint32 param2)
{
fast_log_entry *entry;
entry = fast_log_alloc_entry(handle, 2);
if (entry != NULL) {
entry->event = event;
entry->params[0] = param1;
entry->params[1] = param2;
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(2));
}
}
/** log event with three parameters */
static void
fast_log_log_3(fast_log_handle handle, int event,
uint32 param1, uint32 param2, uint32 param3)
{
fast_log_entry *entry;
entry = fast_log_alloc_entry(handle, 3);
if (entry != NULL) {
entry->event = event;
entry->params[0] = param1;
entry->params[1] = param2;
entry->params[2] = param3;
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(3));
}
}
/** log event with four parameters */
static void
fast_log_log_4(fast_log_handle handle, int event, uint32 param1,
uint32 param2, uint32 param3, uint32 param4)
{
fast_log_entry *entry;
entry = fast_log_alloc_entry(handle, 4);
if (entry != NULL) {
entry->event = event;
entry->params[0] = param1;
entry->params[1] = param2;
entry->params[2] = param3;
entry->params[3] = param4;
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(4));
}
}
/** log event with n parameters */
static void
fast_log_log_n(fast_log_handle handle, int event, int num_params, ...)
{
fast_log_entry *entry;
va_list vl;
entry = fast_log_alloc_entry(handle, num_params);
if (entry != NULL) {
int i;
va_start(vl, num_params);
entry->event = event;
for (i = 0; i < num_params; ++i) {
entry->params[i] = va_arg(vl, int);
}
va_end(vl);
atomic_add(&fast_log_buffer_offset_filled, ENTRY_SIZE(num_params));
}
}
/** clean up client structures */
static void
fast_log_uninit_clients()
{
free(fast_log_buffer);
delete_sem(fast_log_clients_sem);
delete_sem(fast_log_convert_sem);
}
/** init client structures.
* you must call fast_log_uninit_clients on fail
*/
static status_t
fast_log_init_clients()
{
fast_log_buffer = NULL;
fast_log_clients_sem = -1;
fast_log_convert_sem = -1;
fast_log_clients_sem = create_sem(1, "fast_log_clients_sem");
if (fast_log_clients_sem < B_OK)
return fast_log_clients_sem;
fast_log_convert_sem = create_sem(1, "fast_log_convert_sem");
if (fast_log_convert_sem < B_OK)
return fast_log_convert_sem;
fast_log_buffer = malloc(2 * fast_log_buffer_size);
if (fast_log_buffer == NULL)
return B_NO_MEMORY;
memset(fast_log_buffer, 0, 2 * fast_log_buffer_size);
fast_log_buffer_offset_filled = fast_log_buffer_offset_alloc = 0;
return B_OK;
}
/** uninitialize module */
static status_t
fast_log_uninit(void)
{
fast_log_uninit_dev();
fast_log_uninit_long_buffer();
fast_log_uninit_clients();
return B_OK;
}
/** initialize module */
static status_t
fast_log_init(void)
{
status_t res;
if ((res = fast_log_init_long_buffer()) < B_OK)
goto err3;
if ((res = fast_log_init_clients()) < B_OK)
goto err2;
if ((res = fast_log_init_dev()) < B_OK)
// yes: don't call uninit for device.c
goto err2;
return B_OK;
err2:
fast_log_uninit_clients();
err3:
fast_log_uninit_long_buffer();
return res;
}
static status_t
fast_log_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
return fast_log_init();
case B_MODULE_UNINIT:
return fast_log_uninit();
default:
return B_ERROR;
}
}
module_dependency module_dependencies[] = {
{ DEVICE_MANAGER_MODULE_NAME, (module_info **)&pnp },
{}
};
// client interface
fast_log_info fast_log_module = {
{
FAST_LOG_MODULE_NAME,
0,
fast_log_std_ops
},
fast_log_start_log,
fast_log_stop_log,
fast_log_log_0,
fast_log_log_1,
fast_log_log_2,
fast_log_log_3,
fast_log_log_4,
fast_log_log_n
};
// embedded modules
module_info *modules[] = {
(module_info *)&fast_log_module,
(module_info *)&fast_log_devfs_module,
NULL
};

View File

@ -0,0 +1,179 @@
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Fast logging facilities.
Devfs interface.
Publishes a file where logging data can be read from
directly via read. Once read, the logging data is freed.
Obviously, you cannot seek in the file.
Concurrent reads are permitted, but you will have fun
merging read data.
*/
#include "fast_log_int.h"
#include <pnp_devfs.h>
#include <device_manager.h>
// Devfs node
pnp_node_handle fast_log_devfs_node;
static status_t
fast_log_devfs_open(void *device_cookie, uint32 flags, void **handle_cookie)
{
handle_cookie = NULL;
return B_OK;
}
static status_t
fast_log_devfs_close(void *cookie)
{
return B_OK;
}
static status_t
fast_log_devfs_free(void *cookie)
{
return B_OK;
}
static status_t
fast_log_devfs_control(void *cookie, uint32 op, void *data, size_t len)
{
return B_ERROR;
}
static status_t
fast_log_devfs_read(void *cookie, off_t position, void *data, size_t *num_bytes)
{
*num_bytes = fast_log_read_long(data, *num_bytes);
return B_OK;
}
static status_t
fast_log_devfs_write(void *cookie, off_t position, const void *data, size_t *num_bytes)
{
*num_bytes = 0;
return B_ERROR;
}
// #pragma mark -
/** create devfs entry */
static status_t
fast_log_create_devfs_entry(void)
{
status_t res;
pnp_node_attr attrs[] = {
{ PNP_DRIVER_DRIVER, B_STRING_TYPE, { string: FAST_LOG_DEVFS_MODULE_NAME }},
{ PNP_DRIVER_TYPE, B_STRING_TYPE, { string: PNP_DEVFS_TYPE_NAME }},
{ PNP_DRIVER_FIXED_CONSUMER, B_STRING_TYPE, { string: PNP_DEVFS_MODULE_NAME }},
{ PNP_DRIVER_CONNECTION, B_STRING_TYPE, { string: "fast_log" }},
{ PNP_DEVFS_FILENAME, B_STRING_TYPE, { string: FAST_LOG_DEVFS_NAME }},
{ NULL }
};
res = pnp->register_device(NULL, attrs, NULL, &fast_log_devfs_node);
if (res < 0)
fast_log_devfs_node = NULL;
return res;
}
static status_t
fast_log_devfs_init_device(pnp_node_handle node, void *user_cookie, void **cookie)
{
*cookie = NULL;
return B_OK;
}
static status_t
fast_log_devfs_uninit_device(void *cookie)
{
return B_OK;
}
static status_t
fast_log_devfs_std_ops(int32 op, ...)
{
switch (op) {
case B_MODULE_INIT:
case B_MODULE_UNINIT:
return B_OK;
default:
return B_ERROR;
}
}
// devfs interface
pnp_devfs_driver_info fast_log_devfs_module = {
{
{
FAST_LOG_DEVFS_MODULE_NAME,
0,
fast_log_devfs_std_ops
},
fast_log_devfs_init_device,
fast_log_devfs_uninit_device,
NULL,
NULL,
NULL,
},
fast_log_devfs_open,
fast_log_devfs_close,
fast_log_devfs_free,
fast_log_devfs_control,
fast_log_devfs_read,
fast_log_devfs_write
};
/** cleanup devfs interface */
void
fast_log_uninit_dev(void)
{
if (fast_log_devfs_node != NULL)
pnp->unregister_device(fast_log_devfs_node);
}
/** init devfs interface */
status_t
fast_log_init_dev(void)
{
status_t res;
fast_log_devfs_node = NULL;
if ((res = fast_log_create_devfs_entry()) < 0)
return res;
return B_OK;
}

View File

@ -0,0 +1,88 @@
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Macros for double linked lists
*/
#ifndef _DL_LIST_H
#define _DL_LIST_H
#define REMOVE_DL_LIST( item, head, prefix ) \
do { \
if( item->prefix##prev ) \
item->prefix##prev->prefix##next = item->prefix##next; \
else \
head = item->prefix##next; \
\
if( item->prefix##next ) \
item->prefix##next->prefix##prev = item->prefix##prev; \
} while( 0 )
#define ADD_DL_LIST_HEAD( item, head, prefix ) \
do { \
item->prefix##next = head; \
item->prefix##prev = NULL; \
\
if( (head) ) \
(head)->prefix##prev = item; \
\
(head) = item; \
} while( 0 )
#define REMOVE_CDL_LIST( item, head, prefix ) \
do { \
item->prefix##next->prefix##prev = item->prefix##prev; \
item->prefix##prev->prefix##next = item->prefix##next; \
\
if( item == (head) ) { \
if( item->prefix##next != item ) \
(head) = item->prefix##next; \
else \
(head) = NULL; \
} \
} while( 0 )
#define ADD_CDL_LIST_TAIL( item, type, head, prefix ) \
do { \
type *old_head = head; \
\
if( old_head ) { \
type *first, *last; \
\
first = old_head; \
last = first->prefix##prev; \
\
item->prefix##next = first; \
item->prefix##prev = last; \
first->prefix##prev = item; \
last->prefix##next = item; \
} else { \
head = item; \
item->prefix##next = item->prefix##prev = item; \
} \
} while( 0 )
#define ADD_CDL_LIST_HEAD( item, type, head, prefix ) \
do { \
type *old_head = head; \
\
head = item; \
if( old_head ) { \
type *first, *last; \
\
first = old_head; \
last = first->prefix##prev; \
\
item->prefix##next = first; \
item->prefix##prev = last; \
first->prefix##prev = item; \
last->prefix##next = item; \
} else { \
item->prefix##next = item->prefix##prev = item; \
} \
} while( 0 )
#endif

View File

@ -0,0 +1,45 @@
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Fast logging facilities.
Internal header.
*/
#include <drivers/fast_log.h>
#include <malloc.h>
#include <pnp_devfs.h>
#include <device_manager.h>
// maximum number of long buffer
#define FAST_LOG_MAX_LONG_BUFFERS 256;
// size of one long buffer
#define FAST_LOG_LONG_BUFFER_SIZE (16*1024)
// name of dev interface module
#define FAST_LOG_DEVFS_MODULE_NAME "generic/fast_log/devfs/v1"
extern device_manager_info *pnp;
// long_buffers.c
void fast_log_write_long_log(const char *str);
void fast_log_flush_buffer(void);
size_t fast_log_read_long(char *buffer, size_t len);
status_t fast_log_init_long_buffer(void);
void fast_log_uninit_long_buffer(void);
// device.c
status_t fast_log_init_dev(void);
void fast_log_uninit_dev(void);
extern pnp_devfs_driver_info fast_log_devfs_module;

View File

@ -0,0 +1,266 @@
/*
** Copyright 2002/03, Thomas Kurschel. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
/*
Fast logging facilities.
Long buffer.
The internal log buffer uses encoded events and
cannot be decoded on demand as the event codes are
passed by the client and can be changed each time
the client connects. So, we store the decoded events
in long buffers which can be read from directly.
The long buffer is split up into several smaller
buffers that are allocated and freed on demand
to save space.
*/
#include "fast_log_int.h"
#include "dl_list.h"
#include <lock.h>
#include <string.h>
// one long buffer
typedef struct tagfast_log_long_buffer {
struct tagfast_log_long_buffer *next, *prev;
// content
char *data;
// offset for writing
size_t write_offset;
// offset for reading
size_t read_offset;
// size of buffer
size_t size;
} fast_log_long_buffer;
static fast_log_long_buffer
// list of long buffers
*fast_log_long_buffers,
// current buffer to write to
// (NULL if all long buffers are full)
*fast_log_cur_long_buffer;
static uint
// current number of long buffers
fast_log_num_long_buffers,
// maximum number of long buffers
fast_log_max_long_buffers;
// size of one long buffer
static size_t fast_log_long_buffer_size;
// true, if all long buffers are full
// (used to insert a proper message in logging)
static bool fast_log_long_overflow = false;
// grab this when you want to access a long buffer.
// you must make sure that no swapping occurs when holding this lock
static benaphore fast_log_long_buffer_ben;
// grab this when you read out long buffers.
// you are allowed to swap when you hold this lock;
// must be acquired before fast_log_long_buffer_ben
static benaphore fast_log_long_read_ben;
/** flush current long log buffer and allocate new one */
static status_t
fast_log_inc_long_buffer(void)
{
fast_log_long_buffer *buffer;
fast_log_cur_long_buffer = NULL;
// allocate new buffer if allowed and possible
if (fast_log_num_long_buffers >= fast_log_max_long_buffers)
return B_ERROR;
buffer = malloc(sizeof(*buffer) + fast_log_long_buffer_size);
if (buffer == NULL)
return B_NO_MEMORY;
memset(buffer, 0, sizeof(*buffer));
buffer->write_offset = 0;
buffer->read_offset = 0;
buffer->size = fast_log_long_buffer_size;
buffer->data = (char *)(buffer + 1);
ADD_CDL_LIST_TAIL(buffer, fast_log_long_buffer, fast_log_long_buffers, );
fast_log_cur_long_buffer = buffer;
return B_OK;
}
/** store string in long log, internal */
static status_t
fast_log_write_long_log_intern(const char *str)
{
size_t len;
len = strlen(str);
while (len > 0) {
fast_log_long_buffer *buffer;
size_t cur_len;
// get buffer, alloc a new one if there isn't one
if (fast_log_cur_long_buffer == NULL) {
if (fast_log_inc_long_buffer() != B_OK)
return B_ERROR;
}
buffer = fast_log_cur_long_buffer;
cur_len = min(len, buffer->size - buffer->write_offset);
memcpy(buffer->data + buffer->write_offset, str, cur_len);
buffer->write_offset += cur_len;
len -= cur_len;
str += cur_len;
if (buffer->write_offset == buffer->size) {
// next iteration will get a new one
fast_log_cur_long_buffer = NULL;
}
}
return B_OK;
}
/** store string in long log */
void
fast_log_write_long_log(const char *str)
{
benaphore_lock(&fast_log_long_buffer_ben);
// write pending "overflow" message
if (fast_log_long_overflow) {
if (fast_log_write_long_log_intern("<BUFFER OVERFLOW, increase max_long_buffers>\n") != B_OK)
goto err;
fast_log_long_overflow = false;
}
if (fast_log_write_long_log_intern(str) != B_OK)
fast_log_long_overflow = true;
err:
benaphore_unlock(&fast_log_long_buffer_ben);
}
/** free (first) long buffer */
static void
fast_log_free_long_buffer(void)
{
fast_log_long_buffer *buffer = fast_log_long_buffers;
REMOVE_CDL_LIST(buffer, fast_log_long_buffers, );
free(buffer);
}
/** read data from long buffers */
size_t
fast_log_read_long(char *data, size_t len)
{
size_t left_bytes = len;
// fill long buffer
fast_log_flush_buffer();
// make sure noone reads log concurrently
benaphore_lock(&fast_log_long_read_ben);
benaphore_lock(&fast_log_long_buffer_ben);
while (left_bytes > 0 && fast_log_long_buffers != NULL) {
size_t cur_len;
fast_log_long_buffer *buffer;
buffer = fast_log_long_buffers;
cur_len = min(left_bytes, buffer->write_offset - buffer->read_offset);
if (cur_len == 0)
break;
// "data" may be pagable, so release lock during memcpy
benaphore_unlock(&fast_log_long_buffer_ben);
memcpy(data, buffer->data + buffer->read_offset, cur_len);
benaphore_lock(&fast_log_long_buffer_ben);
buffer->read_offset += cur_len;
data += cur_len;
left_bytes -= cur_len;
// once a buffer is completely read, free it
if (buffer->read_offset == buffer->size) {
fast_log_free_long_buffer();
buffer = fast_log_long_buffers;
}
}
benaphore_unlock(&fast_log_long_buffer_ben);
benaphore_unlock(&fast_log_long_read_ben);
return len - left_bytes;
}
/** cleanup long buffers */
void
fast_log_uninit_long_buffer(void)
{
while (fast_log_long_buffers != NULL)
fast_log_free_long_buffer();
benaphore_destroy(&fast_log_long_buffer_ben);
benaphore_destroy(&fast_log_long_read_ben);
}
/** initialize long buffers.
* you must call "fast_log_uninit_long_buffer" on fail
*/
status_t
fast_log_init_long_buffer(void)
{
status_t res;
fast_log_long_buffer_ben.sem = -1;
fast_log_long_read_ben.sem = -1;
fast_log_long_buffers = fast_log_cur_long_buffer = NULL;
fast_log_num_long_buffers = 0;
fast_log_max_long_buffers = 256;
fast_log_long_buffer_size = 16*1024;
fast_log_long_overflow = false;
if ((res = benaphore_init(&fast_log_long_buffer_ben, "fast_log_long_buffer_ben")) < 0)
return res;
if ((res = benaphore_init( &fast_log_long_read_ben, "fast_log_long_read_ben")) < 0)
return res;
return res;
}