Files
pebble/src/fw/kernel/logging.c
2025-01-27 11:38:16 -08:00

198 lines
5.7 KiB
C

/*
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pulse_logging.h"
#include "pebble_tasks.h"
#include "logging_private.h"
#include "util/stack_info.h"
#include "console/console_internal.h"
#include "console/prompt.h"
#include "console/serial_console.h"
#include "debug/advanced_logging.h"
#include "drivers/rtc.h"
#include "system/logging.h"
#include "mcu/interrupts.h"
#include "mcu/privilege.h"
#include "util/math.h"
#include "util/net.h"
#include "util/string.h"
#include "FreeRTOS.h"
#include "task.h"
#include <ctype.h>
#include <stdio.h>
#include <time.h>
#ifndef PBL_LOG_LEVEL
#define PBL_LOG_LEVEL LOG_LEVEL_DEBUG
#endif
int g_pbl_log_level = PBL_LOG_LEVEL;
bool g_pbl_log_enabled = true;
static bool prv_check_serial_log_enabled(int level) {
return (g_pbl_log_enabled) &&
(level == LOG_LEVEL_ALWAYS ||
(level <= g_pbl_log_level));
}
#if !PULSE_EVERYWHERE
#define TIMESTAMP_BUFFER_SIZE 14
static void prv_log_timestamp(void) {
// Enough stack space to use sprintfs?
uint32_t stack_space = stack_free_bytes();
if (stack_space < LOGGING_MIN_STACK_FOR_SPRINTF) {
serial_console_write_log_message(LOGGING_STACK_FULL_MSG);
serial_console_write_log_message(" ");
return;
}
char buffer[TIMESTAMP_BUFFER_SIZE];
time_t time_seconds;
uint16_t time_ms;
rtc_get_time_ms(&time_seconds, &time_ms);
struct tm time_seconds_calendar;
gmtime_r(&time_seconds, &time_seconds_calendar);
sniprintf(buffer, TIMESTAMP_BUFFER_SIZE, "%02u:%02u:%02u.%03u ",
time_seconds_calendar.tm_hour, time_seconds_calendar.tm_min, time_seconds_calendar.tm_sec, time_ms);
serial_console_write_log_message(buffer);
}
static void prv_log_serial(
uint8_t log_level, const char* src_filename, int src_line_number, const char* message) {
if (!serial_console_is_logging_enabled() && log_level != LOG_LEVEL_ALWAYS) {
return;
}
// Log the log level and the current task+privilege level
{
unsigned char task_char = pebble_task_get_char(pebble_task_get_current());
if (mcu_state_is_privileged()) {
task_char = toupper(task_char);
}
char buffer[] = { pbl_log_get_level_char(log_level), ' ', task_char, ' ', 0 };
serial_console_write_log_message(buffer);
}
// Start out with the timestamp
prv_log_timestamp();
// Write out the filename
src_filename = GET_FILE_NAME(src_filename);
serial_console_write_log_message(src_filename);
// Write out the line number
{
char line_number_buffer[12];
itoa_int(src_line_number, line_number_buffer, 10);
serial_console_write_log_message(":");
serial_console_write_log_message(line_number_buffer);
serial_console_write_log_message("> ");
}
// Write the actual log message.
serial_console_write_log_message(message);
// Append our newlines and our trailing null
serial_console_write_log_message("\r\n");
}
#endif // PULSE_EVERYWHERE
void kernel_pbl_log_serial(LogBinaryMessage *log_message, bool async) {
if (!prv_check_serial_log_enabled(log_message->log_level)) {
return;
}
#if PULSE_EVERYWHERE
if (async) {
pulse_logging_log(log_message->log_level, log_message->filename,
htons(log_message->line_number), log_message->message);
} else {
pulse_logging_log_sync(
log_message->log_level, log_message->filename,
htons(log_message->line_number), log_message->message);
}
#else
prv_log_serial(log_message->log_level, log_message->filename,
htons(log_message->line_number), log_message->message);
#endif
}
void kernel_pbl_log_flash(LogBinaryMessage *log_message, bool async) {
int length = sizeof(*log_message) + log_message->message_length;
static const uint8_t FLASH_LOG_LEVEL = LOG_LEVEL_INFO;
if (g_pbl_log_enabled &&
(log_message->log_level == LOG_LEVEL_ALWAYS ||
(log_message->log_level <= FLASH_LOG_LEVEL))) {
pbl_log_advanced((const char*) log_message, length, async);
}
}
void kernel_pbl_log(LogBinaryMessage* log_message, bool async) {
kernel_pbl_log_serial(log_message, async);
if (!portIN_CRITICAL() && !mcu_state_is_isr() &&
xTaskGetSchedulerState() != taskSCHEDULER_SUSPENDED) {
kernel_pbl_log_flash(log_message, async);
}
}
void kernel_pbl_log_from_fault_handler(
const char *src_filename, uint16_t src_line_number, const char *message) {
#if PULSE_EVERYWHERE
pulse_logging_log_sync(LOG_LEVEL_ALWAYS, src_filename,
src_line_number, message);
#else
serial_console_write_log_message(message);
serial_console_write_log_message("\r\n");
#endif
}
void kernel_pbl_log_from_fault_handler_fmt(
const char *src_filename, uint16_t src_line_number, char *buffer,
unsigned int buffer_size, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
vsniprintf(buffer, buffer_size, fmt, ap);
va_end(ap);
kernel_pbl_log_from_fault_handler(src_filename, src_line_number, buffer);
}
// Serial Commands
///////////////////////////////////////////////////////////
void command_log_level_set(const char* level) {
char buffer[32];
g_pbl_log_level = atoi(level);
prompt_send_response_fmt(buffer, 32, "Log level set to: %i", g_pbl_log_level);
}
void command_log_level_get(void) {
char buffer[32];
prompt_send_response_fmt(buffer, 32, "Log level: %i", g_pbl_log_level);
}