Project-Cerberus/core/logging/logging_memory.c

437 строки
13 KiB
C

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "logging_memory.h"
#include "common/buffer_util.h"
#include "common/unused.h"
int logging_memory_create_entry (const struct logging *logging, uint8_t *entry, size_t length)
{
const struct logging_memory *mem_log = (const struct logging_memory*) logging;
struct logging_entry_header header;
if ((mem_log == NULL) || (entry == NULL)) {
return LOGGING_INVALID_ARGUMENT;
}
if (length != (mem_log->entry_size - sizeof (struct logging_entry_header))) {
return LOGGING_BAD_ENTRY_LENGTH;
}
platform_mutex_lock (&mem_log->state->lock);
header.log_magic = LOGGING_MAGIC_START;
header.length = sizeof (header) + length;
header.entry_id = mem_log->state->next_entry_id++;
memcpy (&mem_log->log_buffer[mem_log->state->log_end], (uint8_t*) &header, sizeof (header));
mem_log->state->log_end += sizeof (header);
memcpy (&mem_log->log_buffer[mem_log->state->log_end], entry, length);
mem_log->state->log_end += length;
if (mem_log->state->log_end == mem_log->log_size) {
mem_log->state->log_end = 0;
mem_log->state->is_full = true;
}
if (mem_log->state->is_full) {
mem_log->state->log_start = mem_log->state->log_end;
}
platform_mutex_unlock (&mem_log->state->lock);
return 0;
}
#ifndef LOGGING_DISABLE_FLUSH
int logging_memory_flush (const struct logging *logging)
{
UNUSED (logging);
return 0;
}
#endif
int logging_memory_clear (const struct logging *logging)
{
const struct logging_memory *mem_log = (const struct logging_memory*) logging;
if (mem_log == NULL) {
return LOGGING_INVALID_ARGUMENT;
}
platform_mutex_lock (&mem_log->state->lock);
mem_log->state->log_start = 0;
mem_log->state->log_end = 0;
mem_log->state->is_full = false;
memset (mem_log->log_buffer, 0, mem_log->log_size);
platform_mutex_unlock (&mem_log->state->lock);
return 0;
}
int logging_memory_get_size (const struct logging *logging)
{
const struct logging_memory *mem_log = (const struct logging_memory*) logging;
int log_size = 0;
if (mem_log == NULL) {
return LOGGING_INVALID_ARGUMENT;
}
platform_mutex_lock (&mem_log->state->lock);
if ((mem_log->state->log_end != mem_log->state->log_start) || mem_log->state->is_full) {
if (mem_log->state->log_end == mem_log->state->log_start) {
log_size = mem_log->log_size;
}
else {
log_size = mem_log->state->log_end;
}
}
platform_mutex_unlock (&mem_log->state->lock);
return log_size;
}
int logging_memory_read_contents (const struct logging *logging, uint32_t offset, uint8_t *contents,
size_t length)
{
const struct logging_memory *mem_log = (const struct logging_memory*) logging;
size_t first_copy = 0;
int bytes_read = 0;
size_t copy_len;
size_t copy_offset = offset;
platform_mutex_lock (&mem_log->state->lock);
if ((mem_log->state->log_end != mem_log->state->log_start) || mem_log->state->is_full) {
if (mem_log->state->log_end == mem_log->state->log_start) {
first_copy = buffer_copy (&mem_log->log_buffer[mem_log->state->log_start],
mem_log->log_size - mem_log->state->log_start, &copy_offset, &length, contents);
}
copy_len = buffer_copy (mem_log->log_buffer, mem_log->state->log_end, &copy_offset, &length,
&contents[first_copy]);
bytes_read = first_copy + copy_len;
}
platform_mutex_unlock (&mem_log->state->lock);
return bytes_read;
}
/**
* Initialize a log that stores contents in volatile memory. The memory for the log will by
* dynamically allocated to the necessary size.
*
* @param logging The log to initialize.
* @param state Variable context for the log. This must be uninitialized.
* @param entry_count The maximum number of entries the log should be able to hold.
* @param entry_length The length of a single log entry. This does not include the length of
* standard logging overhead.
*
* @return 0 if the log was successfully initialized or an error code.
*/
int logging_memory_init (struct logging_memory *logging, struct logging_memory_state *state,
size_t entry_count, size_t entry_length)
{
size_t entry_size = entry_length + sizeof (struct logging_entry_header);
size_t log_size = entry_size * entry_count;
uint8_t *log_buffer;
int status;
if ((logging == NULL) || (state == NULL) || (entry_count == 0) || (entry_length == 0)) {
return LOGGING_INVALID_ARGUMENT;
}
log_buffer = platform_malloc (log_size);
if (log_buffer == NULL) {
return LOGGING_NO_MEMORY;
}
status = logging_memory_init_from_buffer (logging, state, log_buffer, log_size, entry_length);
if (status == 0) {
logging->alloc_buffer = true;
}
else {
platform_free (log_buffer);
}
return status;
}
/**
* Initialize a log that stores contents in volatile memory. The memory for the log will be
* preallocated by the caller and not managed by the log instance.
*
* If the provided buffer is not aligned to the size of the entry, including the logging header,
* the usable buffer will be truncated to generate this alignment.
*
* @param logging The log to initialize.
* @param state Variable context for the log. This must be uninitialized.
* @param log_buffer The buffer to use for log entries.
* @param log_size Length of the provided log buffer.
* @param entry_length The length of a single log entry. This does not include the length of
* standard logging overhead.
*
* @return 0 if the log was successfully initialized or an error code.
*/
int logging_memory_init_from_buffer (struct logging_memory *logging,
struct logging_memory_state *state, uint8_t *log_buffer, size_t log_size, size_t entry_length)
{
size_t entry_size = entry_length + sizeof (struct logging_entry_header);
if ((logging == NULL) || (state == NULL) || (log_buffer == NULL) | (entry_length == 0)) {
return LOGGING_INVALID_ARGUMENT;
}
memset (logging, 0, sizeof (struct logging_memory));
/* Make sure the buffer is entry aligned. */
logging->log_size = log_size - (log_size % entry_size);
logging->log_buffer = log_buffer;
logging->entry_size = entry_size;
logging->base.create_entry = logging_memory_create_entry;
#ifndef LOGGING_DISABLE_FLUSH
logging->base.flush = logging_memory_flush;
#endif
logging->base.clear = logging_memory_clear;
logging->base.get_size = logging_memory_get_size;
logging->base.read_contents = logging_memory_read_contents;
logging->state = state;
return logging_memory_init_state (logging);
}
/**
* Find the location in the buffer that contains the last entry and update the log state.
*
* @param logging The log to scan for entries.
*/
static void logging_memory_find_last_entry (const struct logging_memory *logging)
{
struct logging_entry_header *header = (struct logging_entry_header*) logging->log_buffer;
struct logging_entry_header *prev = NULL;
while (!logging->state->is_full && (logging->state->log_end != logging->log_size) &&
LOGGING_IS_ENTRY_START (header->log_magic)) {
if (prev && (header->entry_id != logging->state->next_entry_id)) {
logging->state->is_full = true;
logging->state->log_start = logging->state->log_end;
}
else {
prev = header;
logging->state->next_entry_id = header->entry_id + 1;
logging->state->log_end += logging->entry_size;
header = (struct logging_entry_header*) &logging->log_buffer[logging->state->log_end];
}
}
if (logging->state->log_end == logging->log_size) {
logging->state->is_full = true;
logging->state->log_end = 0;
}
}
/**
* Initialize a log that stores contents in volatile memory. The memory for the log will already
* exist and could contain entries. New log entries will be appended to any existing entries.
*
* If the provided buffer is not aligned to the size of the entry, including the logging header,
* the usable buffer will be truncated to generate this alignment.
*
* The buffer is scanned for the first entry location that does not contain a valid entry or that
* has a discontinuity in entry IDs. This will mark the current end of the log, and new entries
* will be added starting at this location. If this location contains a valid entry, it is assumed
* that the log is full and the rest of the buffer also contains valid entries. If this is not
* guaranteed by the caller, reading the log could result in corrupt log entries.
*
* @param logging The log to initialize.
* @param state Variable context for the log. This must be uninitialized.
* @param log_buffer The buffer to use for log entries.
* @param log_size Length of the provided log buffer.
* @param entry_length The length of a single log entry. This does not include the length of
* standard logging overhead.
*
* @return 0 if the log was successfully initialized or an error code.
*/
int logging_memory_init_append_existing (struct logging_memory *logging,
struct logging_memory_state *state, uint8_t *log_buffer, size_t log_size, size_t entry_length)
{
int status;
status = logging_memory_init_from_buffer (logging, state, log_buffer, log_size, entry_length);
if (status != 0) {
return status;
}
logging_memory_find_last_entry (logging);
return 0;
}
/**
* Initialize the variable state for log in memory and allocate the log buffer. The rest of the log
* instance is assumed to have already been initialized.
*
* This would generally be used with a statically initialized instance. But it cannot be used with
* a constant instance.
*
* The log will be initialized in the same way as logging_memory_init.
*
* @param logging The log instance that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int logging_memory_init_dynamic_state (struct logging_memory *logging)
{
int status;
if ((logging == NULL) || (logging->state == NULL)) {
return LOGGING_INVALID_ARGUMENT;
}
logging->log_buffer = platform_malloc (logging->log_size);
if (logging->log_buffer == NULL) {
return LOGGING_NO_MEMORY;
}
status = logging_memory_init_state (logging);
if (status == 0) {
logging->alloc_buffer = true;
}
else {
platform_free (logging->log_buffer);
logging->log_buffer = NULL;
}
return status;
}
/**
* Initialize only the variable state for log in memory. The rest of the log instance is assumed to
* have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* The log will be initialized in the same way as logging_memory_init_from_buffer.
*
* @param logging The log instance that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int logging_memory_init_state (const struct logging_memory *logging)
{
if ((logging == NULL) || (logging->state == NULL) || (logging->log_buffer == NULL)) {
return LOGGING_INVALID_ARGUMENT;
}
if (logging->log_size < logging->entry_size) {
return LOGGING_INSUFFICIENT_STORAGE;
}
memset (logging->state, 0, sizeof (struct logging_memory_state));
return platform_mutex_init (&logging->state->lock);
}
/**
* Initialize only the variable state for log in memory. The rest of the log instance is assumed to
* have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* The log will be initialized in the same way as logging_memory_init_append_existing.
*
* @param logging The log instance that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int logging_memory_init_state_append_existing (const struct logging_memory *logging)
{
int status;
status = logging_memory_init_state (logging);
if (status != 0) {
return status;
}
logging_memory_find_last_entry (logging);
return 0;
}
/**
* Release the resources used by a log in memory.
*
* @param logging The log to release.
*/
void logging_memory_release (struct logging_memory *logging)
{
if (logging) {
platform_mutex_free (&logging->state->lock);
if (logging->alloc_buffer) {
platform_free (logging->log_buffer);
}
}
}
/**
* Copy all entries in the log to another log instance. The entries will remain unchanged in the
* source log.
*
* @param logging The source log to copy entries from.
* @param dest The destination log for the entries. This cannot be the same as the source.
*
* @return 0 if all entries were copied successfully or an error code.
*/
int logging_memory_copy_entries (const struct logging_memory *logging, const struct logging *dest)
{
struct logging_entry_header *header;
uint8_t *pos;
uint8_t *end;
int status = 0;
if ((logging == NULL) || (dest == NULL)) {
return LOGGING_INVALID_ARGUMENT;
}
platform_mutex_lock (&logging->state->lock);
pos = &logging->log_buffer[logging->state->log_start];
end = &logging->log_buffer[logging->state->log_end];
if ((pos != end) || logging->state->is_full) {
do {
header = (struct logging_entry_header*) pos;
status = dest->create_entry (dest, &pos[sizeof (*header)],
header->length - sizeof (*header));
if (status != 0) {
goto exit;
}
pos += header->length;
if (pos >= &logging->log_buffer[logging->log_size]) {
pos = logging->log_buffer;
}
} while (pos != end);
}
exit:
platform_mutex_unlock (&logging->state->lock);
return status;
}