437 строки
13 KiB
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, ©_offset, &length, contents);
|
|
}
|
|
|
|
copy_len = buffer_copy (mem_log->log_buffer, mem_log->state->log_end, ©_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;
|
|
}
|