diff --git a/src/add-ons/kernel/generic/fast_log/Jamfile b/src/add-ons/kernel/generic/fast_log/Jamfile new file mode 100644 index 0000000000..b15d05e63c --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/Jamfile @@ -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 + ; + diff --git a/src/add-ons/kernel/generic/fast_log/clients.c b/src/add-ons/kernel/generic/fast_log/clients.c new file mode 100644 index 0000000000..83eae6a240 --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/clients.c @@ -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 + +#include +#include + + +// 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(" 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("\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( "\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 +}; diff --git a/src/add-ons/kernel/generic/fast_log/device.c b/src/add-ons/kernel/generic/fast_log/device.c new file mode 100644 index 0000000000..8b9885a2ea --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/device.c @@ -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 +#include + + +// 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; +} diff --git a/src/add-ons/kernel/generic/fast_log/dl_list.h b/src/add-ons/kernel/generic/fast_log/dl_list.h new file mode 100644 index 0000000000..36a0d22da9 --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/dl_list.h @@ -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 diff --git a/src/add-ons/kernel/generic/fast_log/fast_log_int.h b/src/add-ons/kernel/generic/fast_log/fast_log_int.h new file mode 100644 index 0000000000..0a573e9073 --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/fast_log_int.h @@ -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 +#include +#include +#include + +// 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; diff --git a/src/add-ons/kernel/generic/fast_log/long_buffers.c b/src/add-ons/kernel/generic/fast_log/long_buffers.c new file mode 100644 index 0000000000..8681cb7a71 --- /dev/null +++ b/src/add-ons/kernel/generic/fast_log/long_buffers.c @@ -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 + +#include + + +// 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("\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; +} +