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:
parent
8a9c2f0e60
commit
5e3662534f
10
src/add-ons/kernel/generic/fast_log/Jamfile
Normal file
10
src/add-ons/kernel/generic/fast_log/Jamfile
Normal 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
|
||||
;
|
||||
|
599
src/add-ons/kernel/generic/fast_log/clients.c
Normal file
599
src/add-ons/kernel/generic/fast_log/clients.c
Normal 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
|
||||
};
|
179
src/add-ons/kernel/generic/fast_log/device.c
Normal file
179
src/add-ons/kernel/generic/fast_log/device.c
Normal 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;
|
||||
}
|
88
src/add-ons/kernel/generic/fast_log/dl_list.h
Normal file
88
src/add-ons/kernel/generic/fast_log/dl_list.h
Normal 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
|
45
src/add-ons/kernel/generic/fast_log/fast_log_int.h
Normal file
45
src/add-ons/kernel/generic/fast_log/fast_log_int.h
Normal 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;
|
266
src/add-ons/kernel/generic/fast_log/long_buffers.c
Normal file
266
src/add-ons/kernel/generic/fast_log/long_buffers.c
Normal 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;
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user