/* * 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 #include #include #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); }