mbed-os5 only for TYBLE16
Dependents: TYBLE16_simple_data_logger TYBLE16_MP3_Air
platform/source/mbed_error.c@1:9db0e321a9f4, 2019-12-31 (annotated)
- Committer:
- kenjiArai
- Date:
- Tue Dec 31 06:02:27 2019 +0000
- Revision:
- 1:9db0e321a9f4
updated based on mbed-os5.15.0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
kenjiArai | 1:9db0e321a9f4 | 1 | /* mbed Microcontroller Library |
kenjiArai | 1:9db0e321a9f4 | 2 | * Copyright (c) 2006-2019 ARM Limited |
kenjiArai | 1:9db0e321a9f4 | 3 | * SPDX-License-Identifier: Apache-2.0 |
kenjiArai | 1:9db0e321a9f4 | 4 | * |
kenjiArai | 1:9db0e321a9f4 | 5 | * Licensed under the Apache License, Version 2.0 (the "License"); |
kenjiArai | 1:9db0e321a9f4 | 6 | * you may not use this file except in compliance with the License. |
kenjiArai | 1:9db0e321a9f4 | 7 | * You may obtain a copy of the License at |
kenjiArai | 1:9db0e321a9f4 | 8 | * |
kenjiArai | 1:9db0e321a9f4 | 9 | * http://www.apache.org/licenses/LICENSE-2.0 |
kenjiArai | 1:9db0e321a9f4 | 10 | * |
kenjiArai | 1:9db0e321a9f4 | 11 | * Unless required by applicable law or agreed to in writing, software |
kenjiArai | 1:9db0e321a9f4 | 12 | * distributed under the License is distributed on an "AS IS" BASIS, |
kenjiArai | 1:9db0e321a9f4 | 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
kenjiArai | 1:9db0e321a9f4 | 14 | * See the License for the specific language governing permissions and |
kenjiArai | 1:9db0e321a9f4 | 15 | * limitations under the License. |
kenjiArai | 1:9db0e321a9f4 | 16 | */ |
kenjiArai | 1:9db0e321a9f4 | 17 | #include <stdlib.h> |
kenjiArai | 1:9db0e321a9f4 | 18 | #include <stdarg.h> |
kenjiArai | 1:9db0e321a9f4 | 19 | #include <string.h> |
kenjiArai | 1:9db0e321a9f4 | 20 | #include "device.h" |
kenjiArai | 1:9db0e321a9f4 | 21 | #include "platform/source/mbed_crash_data_offsets.h" |
kenjiArai | 1:9db0e321a9f4 | 22 | #include "platform/mbed_atomic.h" |
kenjiArai | 1:9db0e321a9f4 | 23 | #include "platform/mbed_critical.h" |
kenjiArai | 1:9db0e321a9f4 | 24 | #include "platform/mbed_error.h" |
kenjiArai | 1:9db0e321a9f4 | 25 | #include "platform/source/mbed_error_hist.h" |
kenjiArai | 1:9db0e321a9f4 | 26 | #include "platform/mbed_interface.h" |
kenjiArai | 1:9db0e321a9f4 | 27 | #include "platform/mbed_power_mgmt.h" |
kenjiArai | 1:9db0e321a9f4 | 28 | #include "platform/mbed_stats.h" |
kenjiArai | 1:9db0e321a9f4 | 29 | #include "platform/source/TARGET_CORTEX_M/mbed_fault_handler.h" |
kenjiArai | 1:9db0e321a9f4 | 30 | #include "drivers/MbedCRC.h" |
kenjiArai | 1:9db0e321a9f4 | 31 | #include "mbed_rtx.h" |
kenjiArai | 1:9db0e321a9f4 | 32 | #ifdef MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 33 | #include "rtx_os.h" |
kenjiArai | 1:9db0e321a9f4 | 34 | #endif |
kenjiArai | 1:9db0e321a9f4 | 35 | |
kenjiArai | 1:9db0e321a9f4 | 36 | #if DEVICE_STDIO_MESSAGES |
kenjiArai | 1:9db0e321a9f4 | 37 | #include <stdio.h> |
kenjiArai | 1:9db0e321a9f4 | 38 | #endif |
kenjiArai | 1:9db0e321a9f4 | 39 | #ifndef __STDC_FORMAT_MACROS |
kenjiArai | 1:9db0e321a9f4 | 40 | #define __STDC_FORMAT_MACROS |
kenjiArai | 1:9db0e321a9f4 | 41 | #endif |
kenjiArai | 1:9db0e321a9f4 | 42 | #include <inttypes.h> |
kenjiArai | 1:9db0e321a9f4 | 43 | |
kenjiArai | 1:9db0e321a9f4 | 44 | #ifndef MAX |
kenjiArai | 1:9db0e321a9f4 | 45 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) |
kenjiArai | 1:9db0e321a9f4 | 46 | #endif |
kenjiArai | 1:9db0e321a9f4 | 47 | |
kenjiArai | 1:9db0e321a9f4 | 48 | #ifndef NDEBUG |
kenjiArai | 1:9db0e321a9f4 | 49 | #define ERROR_REPORT(ctx, error_msg, error_filename, error_line) print_error_report(ctx, error_msg, error_filename, error_line) |
kenjiArai | 1:9db0e321a9f4 | 50 | static void print_error_report(const mbed_error_ctx *ctx, const char *, const char *error_filename, int error_line); |
kenjiArai | 1:9db0e321a9f4 | 51 | #else |
kenjiArai | 1:9db0e321a9f4 | 52 | #define ERROR_REPORT(ctx, error_msg, error_filename, error_line) ((void) 0) |
kenjiArai | 1:9db0e321a9f4 | 53 | #endif |
kenjiArai | 1:9db0e321a9f4 | 54 | |
kenjiArai | 1:9db0e321a9f4 | 55 | bool mbed_error_in_progress; |
kenjiArai | 1:9db0e321a9f4 | 56 | static core_util_atomic_flag halt_in_progress = CORE_UTIL_ATOMIC_FLAG_INIT; |
kenjiArai | 1:9db0e321a9f4 | 57 | static int error_count = 0; |
kenjiArai | 1:9db0e321a9f4 | 58 | static mbed_error_ctx first_error_ctx = {0}; |
kenjiArai | 1:9db0e321a9f4 | 59 | |
kenjiArai | 1:9db0e321a9f4 | 60 | static mbed_error_ctx last_error_ctx = {0}; |
kenjiArai | 1:9db0e321a9f4 | 61 | static mbed_error_hook_t error_hook = NULL; |
kenjiArai | 1:9db0e321a9f4 | 62 | static mbed_error_status_t handle_error(mbed_error_status_t error_status, unsigned int error_value, const char *filename, int line_number, void *caller); |
kenjiArai | 1:9db0e321a9f4 | 63 | |
kenjiArai | 1:9db0e321a9f4 | 64 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 65 | //Global for populating the context in exception handler |
kenjiArai | 1:9db0e321a9f4 | 66 | static mbed_error_ctx *const report_error_ctx = (mbed_error_ctx *)(ERROR_CONTEXT_LOCATION); |
kenjiArai | 1:9db0e321a9f4 | 67 | static bool is_reboot_error_valid = false; |
kenjiArai | 1:9db0e321a9f4 | 68 | #endif |
kenjiArai | 1:9db0e321a9f4 | 69 | |
kenjiArai | 1:9db0e321a9f4 | 70 | //Helper function to halt the system |
kenjiArai | 1:9db0e321a9f4 | 71 | static MBED_NORETURN void mbed_halt_system(void) |
kenjiArai | 1:9db0e321a9f4 | 72 | { |
kenjiArai | 1:9db0e321a9f4 | 73 | // Prevent recursion if halt is called again during halt attempt - try |
kenjiArai | 1:9db0e321a9f4 | 74 | // something simple instead. |
kenjiArai | 1:9db0e321a9f4 | 75 | if (core_util_atomic_flag_test_and_set(&halt_in_progress)) { |
kenjiArai | 1:9db0e321a9f4 | 76 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 77 | __DSB(); |
kenjiArai | 1:9db0e321a9f4 | 78 | for (;;) { |
kenjiArai | 1:9db0e321a9f4 | 79 | __WFE(); // Not WFI, as don't want to wake for pending interrupts |
kenjiArai | 1:9db0e321a9f4 | 80 | } |
kenjiArai | 1:9db0e321a9f4 | 81 | } |
kenjiArai | 1:9db0e321a9f4 | 82 | |
kenjiArai | 1:9db0e321a9f4 | 83 | //If in ISR context, call mbed_die directly |
kenjiArai | 1:9db0e321a9f4 | 84 | if (core_util_is_isr_active() || !core_util_are_interrupts_enabled()) { |
kenjiArai | 1:9db0e321a9f4 | 85 | mbed_die(); |
kenjiArai | 1:9db0e321a9f4 | 86 | } |
kenjiArai | 1:9db0e321a9f4 | 87 | |
kenjiArai | 1:9db0e321a9f4 | 88 | // In normal context, try orderly exit(1), which eventually calls mbed_die |
kenjiArai | 1:9db0e321a9f4 | 89 | exit(1); |
kenjiArai | 1:9db0e321a9f4 | 90 | } |
kenjiArai | 1:9db0e321a9f4 | 91 | |
kenjiArai | 1:9db0e321a9f4 | 92 | WEAK MBED_NORETURN void error(const char *format, ...) |
kenjiArai | 1:9db0e321a9f4 | 93 | { |
kenjiArai | 1:9db0e321a9f4 | 94 | // Prevent recursion if error is called again during store+print attempt |
kenjiArai | 1:9db0e321a9f4 | 95 | if (!core_util_atomic_exchange_bool(&mbed_error_in_progress, true)) { |
kenjiArai | 1:9db0e321a9f4 | 96 | handle_error(MBED_ERROR_UNKNOWN, 0, NULL, 0, MBED_CALLER_ADDR()); |
kenjiArai | 1:9db0e321a9f4 | 97 | ERROR_REPORT(&last_error_ctx, "Fatal Run-time error", NULL, 0); |
kenjiArai | 1:9db0e321a9f4 | 98 | |
kenjiArai | 1:9db0e321a9f4 | 99 | #ifndef NDEBUG |
kenjiArai | 1:9db0e321a9f4 | 100 | va_list arg; |
kenjiArai | 1:9db0e321a9f4 | 101 | va_start(arg, format); |
kenjiArai | 1:9db0e321a9f4 | 102 | mbed_error_vprintf(format, arg); |
kenjiArai | 1:9db0e321a9f4 | 103 | va_end(arg); |
kenjiArai | 1:9db0e321a9f4 | 104 | // Add a newline to prevent any line buffering |
kenjiArai | 1:9db0e321a9f4 | 105 | mbed_error_puts("\n"); |
kenjiArai | 1:9db0e321a9f4 | 106 | #endif |
kenjiArai | 1:9db0e321a9f4 | 107 | } |
kenjiArai | 1:9db0e321a9f4 | 108 | |
kenjiArai | 1:9db0e321a9f4 | 109 | mbed_halt_system(); |
kenjiArai | 1:9db0e321a9f4 | 110 | } |
kenjiArai | 1:9db0e321a9f4 | 111 | |
kenjiArai | 1:9db0e321a9f4 | 112 | static inline bool mbed_error_is_hw_fault(mbed_error_status_t error_status) |
kenjiArai | 1:9db0e321a9f4 | 113 | { |
kenjiArai | 1:9db0e321a9f4 | 114 | return (error_status == MBED_ERROR_MEMMANAGE_EXCEPTION || |
kenjiArai | 1:9db0e321a9f4 | 115 | error_status == MBED_ERROR_BUSFAULT_EXCEPTION || |
kenjiArai | 1:9db0e321a9f4 | 116 | error_status == MBED_ERROR_USAGEFAULT_EXCEPTION || |
kenjiArai | 1:9db0e321a9f4 | 117 | error_status == MBED_ERROR_HARDFAULT_EXCEPTION); |
kenjiArai | 1:9db0e321a9f4 | 118 | } |
kenjiArai | 1:9db0e321a9f4 | 119 | |
kenjiArai | 1:9db0e321a9f4 | 120 | static bool mbed_error_is_handler(const mbed_error_ctx *ctx) |
kenjiArai | 1:9db0e321a9f4 | 121 | { |
kenjiArai | 1:9db0e321a9f4 | 122 | bool is_handler = false; |
kenjiArai | 1:9db0e321a9f4 | 123 | if (ctx && mbed_error_is_hw_fault(ctx->error_status)) { |
kenjiArai | 1:9db0e321a9f4 | 124 | mbed_fault_context_t *mfc = (mbed_fault_context_t *)ctx->error_value; |
kenjiArai | 1:9db0e321a9f4 | 125 | if (mfc && !(mfc->EXC_RETURN & 0x8)) { |
kenjiArai | 1:9db0e321a9f4 | 126 | is_handler = true; |
kenjiArai | 1:9db0e321a9f4 | 127 | } |
kenjiArai | 1:9db0e321a9f4 | 128 | } |
kenjiArai | 1:9db0e321a9f4 | 129 | return is_handler; |
kenjiArai | 1:9db0e321a9f4 | 130 | } |
kenjiArai | 1:9db0e321a9f4 | 131 | |
kenjiArai | 1:9db0e321a9f4 | 132 | //Set an error status with the error handling system |
kenjiArai | 1:9db0e321a9f4 | 133 | static mbed_error_status_t handle_error(mbed_error_status_t error_status, unsigned int error_value, const char *filename, int line_number, void *caller) |
kenjiArai | 1:9db0e321a9f4 | 134 | { |
kenjiArai | 1:9db0e321a9f4 | 135 | mbed_error_ctx current_error_ctx; |
kenjiArai | 1:9db0e321a9f4 | 136 | |
kenjiArai | 1:9db0e321a9f4 | 137 | //Error status should always be < 0 |
kenjiArai | 1:9db0e321a9f4 | 138 | if (error_status >= 0) { |
kenjiArai | 1:9db0e321a9f4 | 139 | //This is a weird situation, someone called mbed_error with an invalid error code. |
kenjiArai | 1:9db0e321a9f4 | 140 | //We will still handle the situation but change the error code to ERROR_INVALID_ARGUMENT, at least the context will have info on who called it |
kenjiArai | 1:9db0e321a9f4 | 141 | error_status = MBED_ERROR_INVALID_ARGUMENT; |
kenjiArai | 1:9db0e321a9f4 | 142 | } |
kenjiArai | 1:9db0e321a9f4 | 143 | |
kenjiArai | 1:9db0e321a9f4 | 144 | //Clear the context capturing buffer |
kenjiArai | 1:9db0e321a9f4 | 145 | memset(¤t_error_ctx, 0, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 146 | |
kenjiArai | 1:9db0e321a9f4 | 147 | //Capture error information |
kenjiArai | 1:9db0e321a9f4 | 148 | current_error_ctx.error_status = error_status; |
kenjiArai | 1:9db0e321a9f4 | 149 | current_error_ctx.error_value = error_value; |
kenjiArai | 1:9db0e321a9f4 | 150 | mbed_fault_context_t *mfc = NULL; |
kenjiArai | 1:9db0e321a9f4 | 151 | if (mbed_error_is_hw_fault(error_status)) { |
kenjiArai | 1:9db0e321a9f4 | 152 | mfc = (mbed_fault_context_t *)error_value; |
kenjiArai | 1:9db0e321a9f4 | 153 | current_error_ctx.error_address = (uint32_t)mfc->PC_reg; |
kenjiArai | 1:9db0e321a9f4 | 154 | // Note that this SP_reg is the correct SP value of the fault. PSP and MSP are slightly different due to HardFault_Handler. |
kenjiArai | 1:9db0e321a9f4 | 155 | current_error_ctx.thread_current_sp = (uint32_t)mfc->SP_reg; |
kenjiArai | 1:9db0e321a9f4 | 156 | // Note that the RTX thread itself is the same even under this fault exception handler. |
kenjiArai | 1:9db0e321a9f4 | 157 | } else { |
kenjiArai | 1:9db0e321a9f4 | 158 | current_error_ctx.error_address = (uint32_t)caller; |
kenjiArai | 1:9db0e321a9f4 | 159 | current_error_ctx.thread_current_sp = (uint32_t)¤t_error_ctx; // Address local variable to get a stack pointer |
kenjiArai | 1:9db0e321a9f4 | 160 | } |
kenjiArai | 1:9db0e321a9f4 | 161 | |
kenjiArai | 1:9db0e321a9f4 | 162 | #ifdef MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 163 | // Capture thread info in thread mode |
kenjiArai | 1:9db0e321a9f4 | 164 | osRtxThread_t *current_thread = osRtxInfo.thread.run.curr; |
kenjiArai | 1:9db0e321a9f4 | 165 | current_error_ctx.thread_id = (uint32_t)current_thread; |
kenjiArai | 1:9db0e321a9f4 | 166 | current_error_ctx.thread_entry_address = (uint32_t)current_thread->thread_addr; |
kenjiArai | 1:9db0e321a9f4 | 167 | current_error_ctx.thread_stack_size = current_thread->stack_size; |
kenjiArai | 1:9db0e321a9f4 | 168 | current_error_ctx.thread_stack_mem = (uint32_t)current_thread->stack_mem; |
kenjiArai | 1:9db0e321a9f4 | 169 | #endif //MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 170 | |
kenjiArai | 1:9db0e321a9f4 | 171 | #if MBED_CONF_PLATFORM_ERROR_FILENAME_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 172 | //Capture filename/linenumber if provided |
kenjiArai | 1:9db0e321a9f4 | 173 | //Index for tracking error_filename |
kenjiArai | 1:9db0e321a9f4 | 174 | strncpy(current_error_ctx.error_filename, filename, MBED_CONF_PLATFORM_MAX_ERROR_FILENAME_LEN); |
kenjiArai | 1:9db0e321a9f4 | 175 | current_error_ctx.error_line_number = line_number; |
kenjiArai | 1:9db0e321a9f4 | 176 | #endif |
kenjiArai | 1:9db0e321a9f4 | 177 | |
kenjiArai | 1:9db0e321a9f4 | 178 | //Prevent corruption by holding out other callers |
kenjiArai | 1:9db0e321a9f4 | 179 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 180 | |
kenjiArai | 1:9db0e321a9f4 | 181 | //Increment error count |
kenjiArai | 1:9db0e321a9f4 | 182 | error_count++; |
kenjiArai | 1:9db0e321a9f4 | 183 | |
kenjiArai | 1:9db0e321a9f4 | 184 | //Capture the first system error and store it |
kenjiArai | 1:9db0e321a9f4 | 185 | if (error_count == 1) { //first error |
kenjiArai | 1:9db0e321a9f4 | 186 | memcpy(&first_error_ctx, ¤t_error_ctx, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 187 | } |
kenjiArai | 1:9db0e321a9f4 | 188 | |
kenjiArai | 1:9db0e321a9f4 | 189 | //copy this error to last error |
kenjiArai | 1:9db0e321a9f4 | 190 | memcpy(&last_error_ctx, ¤t_error_ctx, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 191 | |
kenjiArai | 1:9db0e321a9f4 | 192 | #if MBED_CONF_PLATFORM_ERROR_HIST_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 193 | //Log the error with error log |
kenjiArai | 1:9db0e321a9f4 | 194 | mbed_error_hist_put(¤t_error_ctx); |
kenjiArai | 1:9db0e321a9f4 | 195 | #endif |
kenjiArai | 1:9db0e321a9f4 | 196 | |
kenjiArai | 1:9db0e321a9f4 | 197 | //Call the error hook if available |
kenjiArai | 1:9db0e321a9f4 | 198 | if (error_hook != NULL) { |
kenjiArai | 1:9db0e321a9f4 | 199 | error_hook(&last_error_ctx); |
kenjiArai | 1:9db0e321a9f4 | 200 | } |
kenjiArai | 1:9db0e321a9f4 | 201 | |
kenjiArai | 1:9db0e321a9f4 | 202 | core_util_critical_section_exit(); |
kenjiArai | 1:9db0e321a9f4 | 203 | |
kenjiArai | 1:9db0e321a9f4 | 204 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 205 | } |
kenjiArai | 1:9db0e321a9f4 | 206 | |
kenjiArai | 1:9db0e321a9f4 | 207 | WEAK void mbed_error_reboot_callback(mbed_error_ctx *error_context) |
kenjiArai | 1:9db0e321a9f4 | 208 | { |
kenjiArai | 1:9db0e321a9f4 | 209 | //Dont do anything here, let application override this if required. |
kenjiArai | 1:9db0e321a9f4 | 210 | } |
kenjiArai | 1:9db0e321a9f4 | 211 | |
kenjiArai | 1:9db0e321a9f4 | 212 | //Initialize Error handling system and report any errors detected on rebooted |
kenjiArai | 1:9db0e321a9f4 | 213 | mbed_error_status_t mbed_error_initialize(void) |
kenjiArai | 1:9db0e321a9f4 | 214 | { |
kenjiArai | 1:9db0e321a9f4 | 215 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 216 | uint32_t crc_val = 0; |
kenjiArai | 1:9db0e321a9f4 | 217 | |
kenjiArai | 1:9db0e321a9f4 | 218 | //Just check if we have valid value for error_status, if error_status is positive(which is not valid), no need to check crc |
kenjiArai | 1:9db0e321a9f4 | 219 | if (report_error_ctx->error_status < 0) { |
kenjiArai | 1:9db0e321a9f4 | 220 | crc_val = mbed_tiny_compute_crc32(report_error_ctx, offsetof(mbed_error_ctx, crc_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 221 | //Read report_error_ctx and check if CRC is correct, and with valid status code |
kenjiArai | 1:9db0e321a9f4 | 222 | if ((report_error_ctx->crc_error_ctx == crc_val) && (report_error_ctx->is_error_processed == 0)) { |
kenjiArai | 1:9db0e321a9f4 | 223 | is_reboot_error_valid = true; |
kenjiArai | 1:9db0e321a9f4 | 224 | |
kenjiArai | 1:9db0e321a9f4 | 225 | //Call the mbed_error_reboot_callback, this enables applications to do some handling before we do the handling |
kenjiArai | 1:9db0e321a9f4 | 226 | mbed_error_reboot_callback(report_error_ctx); |
kenjiArai | 1:9db0e321a9f4 | 227 | |
kenjiArai | 1:9db0e321a9f4 | 228 | //We let the callback reset the error info, so check if its still valid and do the rest only if its still valid. |
kenjiArai | 1:9db0e321a9f4 | 229 | if (report_error_ctx->error_reboot_count > 0) { |
kenjiArai | 1:9db0e321a9f4 | 230 | |
kenjiArai | 1:9db0e321a9f4 | 231 | report_error_ctx->is_error_processed = 1;//Set the flag that we already processed this error |
kenjiArai | 1:9db0e321a9f4 | 232 | crc_val = mbed_tiny_compute_crc32(report_error_ctx, offsetof(mbed_error_ctx, crc_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 233 | report_error_ctx->crc_error_ctx = crc_val; |
kenjiArai | 1:9db0e321a9f4 | 234 | |
kenjiArai | 1:9db0e321a9f4 | 235 | //Enforce max-reboot only if auto reboot is enabled |
kenjiArai | 1:9db0e321a9f4 | 236 | #if MBED_CONF_PLATFORM_FATAL_ERROR_AUTO_REBOOT_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 237 | if (report_error_ctx->error_reboot_count >= MBED_CONF_PLATFORM_ERROR_REBOOT_MAX) { |
kenjiArai | 1:9db0e321a9f4 | 238 | mbed_halt_system(); |
kenjiArai | 1:9db0e321a9f4 | 239 | } |
kenjiArai | 1:9db0e321a9f4 | 240 | #endif |
kenjiArai | 1:9db0e321a9f4 | 241 | } |
kenjiArai | 1:9db0e321a9f4 | 242 | } |
kenjiArai | 1:9db0e321a9f4 | 243 | } |
kenjiArai | 1:9db0e321a9f4 | 244 | #endif |
kenjiArai | 1:9db0e321a9f4 | 245 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 246 | } |
kenjiArai | 1:9db0e321a9f4 | 247 | |
kenjiArai | 1:9db0e321a9f4 | 248 | //Return the first error |
kenjiArai | 1:9db0e321a9f4 | 249 | mbed_error_status_t mbed_get_first_error(void) |
kenjiArai | 1:9db0e321a9f4 | 250 | { |
kenjiArai | 1:9db0e321a9f4 | 251 | //return the first error recorded |
kenjiArai | 1:9db0e321a9f4 | 252 | return first_error_ctx.error_status; |
kenjiArai | 1:9db0e321a9f4 | 253 | } |
kenjiArai | 1:9db0e321a9f4 | 254 | |
kenjiArai | 1:9db0e321a9f4 | 255 | //Return the last error |
kenjiArai | 1:9db0e321a9f4 | 256 | mbed_error_status_t mbed_get_last_error(void) |
kenjiArai | 1:9db0e321a9f4 | 257 | { |
kenjiArai | 1:9db0e321a9f4 | 258 | //return the last error recorded |
kenjiArai | 1:9db0e321a9f4 | 259 | return last_error_ctx.error_status; |
kenjiArai | 1:9db0e321a9f4 | 260 | } |
kenjiArai | 1:9db0e321a9f4 | 261 | |
kenjiArai | 1:9db0e321a9f4 | 262 | //Gets the current error count |
kenjiArai | 1:9db0e321a9f4 | 263 | int mbed_get_error_count(void) |
kenjiArai | 1:9db0e321a9f4 | 264 | { |
kenjiArai | 1:9db0e321a9f4 | 265 | //return the current error count |
kenjiArai | 1:9db0e321a9f4 | 266 | return error_count; |
kenjiArai | 1:9db0e321a9f4 | 267 | } |
kenjiArai | 1:9db0e321a9f4 | 268 | |
kenjiArai | 1:9db0e321a9f4 | 269 | //Reads the fatal error occurred" flag |
kenjiArai | 1:9db0e321a9f4 | 270 | bool mbed_get_error_in_progress(void) |
kenjiArai | 1:9db0e321a9f4 | 271 | { |
kenjiArai | 1:9db0e321a9f4 | 272 | return core_util_atomic_load_bool(&mbed_error_in_progress); |
kenjiArai | 1:9db0e321a9f4 | 273 | } |
kenjiArai | 1:9db0e321a9f4 | 274 | |
kenjiArai | 1:9db0e321a9f4 | 275 | //Sets a non-fatal error |
kenjiArai | 1:9db0e321a9f4 | 276 | mbed_error_status_t mbed_warning(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number) |
kenjiArai | 1:9db0e321a9f4 | 277 | { |
kenjiArai | 1:9db0e321a9f4 | 278 | return handle_error(error_status, error_value, filename, line_number, MBED_CALLER_ADDR()); |
kenjiArai | 1:9db0e321a9f4 | 279 | } |
kenjiArai | 1:9db0e321a9f4 | 280 | |
kenjiArai | 1:9db0e321a9f4 | 281 | //Sets a fatal error, this function is marked WEAK to be able to override this for some tests |
kenjiArai | 1:9db0e321a9f4 | 282 | WEAK MBED_NORETURN mbed_error_status_t mbed_error(mbed_error_status_t error_status, const char *error_msg, unsigned int error_value, const char *filename, int line_number) |
kenjiArai | 1:9db0e321a9f4 | 283 | { |
kenjiArai | 1:9db0e321a9f4 | 284 | // Prevent recursion if error is called again during store+print attempt |
kenjiArai | 1:9db0e321a9f4 | 285 | if (!core_util_atomic_exchange_bool(&mbed_error_in_progress, true)) { |
kenjiArai | 1:9db0e321a9f4 | 286 | //set the error reported |
kenjiArai | 1:9db0e321a9f4 | 287 | (void) handle_error(error_status, error_value, filename, line_number, MBED_CALLER_ADDR()); |
kenjiArai | 1:9db0e321a9f4 | 288 | |
kenjiArai | 1:9db0e321a9f4 | 289 | //On fatal errors print the error context/report |
kenjiArai | 1:9db0e321a9f4 | 290 | ERROR_REPORT(&last_error_ctx, error_msg, filename, line_number); |
kenjiArai | 1:9db0e321a9f4 | 291 | } |
kenjiArai | 1:9db0e321a9f4 | 292 | |
kenjiArai | 1:9db0e321a9f4 | 293 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 294 | uint32_t crc_val = 0; |
kenjiArai | 1:9db0e321a9f4 | 295 | crc_val = mbed_tiny_compute_crc32(report_error_ctx, offsetof(mbed_error_ctx, crc_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 296 | //Read report_error_ctx and check if CRC is correct for report_error_ctx |
kenjiArai | 1:9db0e321a9f4 | 297 | if (report_error_ctx->crc_error_ctx == crc_val) { |
kenjiArai | 1:9db0e321a9f4 | 298 | uint32_t current_reboot_count = report_error_ctx->error_reboot_count; |
kenjiArai | 1:9db0e321a9f4 | 299 | last_error_ctx.error_reboot_count = current_reboot_count + 1; |
kenjiArai | 1:9db0e321a9f4 | 300 | } else { |
kenjiArai | 1:9db0e321a9f4 | 301 | last_error_ctx.error_reboot_count = 1; |
kenjiArai | 1:9db0e321a9f4 | 302 | } |
kenjiArai | 1:9db0e321a9f4 | 303 | last_error_ctx.is_error_processed = 0;//Set the flag that this is a new error |
kenjiArai | 1:9db0e321a9f4 | 304 | //Update the struct with crc |
kenjiArai | 1:9db0e321a9f4 | 305 | last_error_ctx.crc_error_ctx = mbed_tiny_compute_crc32(&last_error_ctx, offsetof(mbed_error_ctx, crc_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 306 | //Protect report_error_ctx while we update it |
kenjiArai | 1:9db0e321a9f4 | 307 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 308 | memcpy(report_error_ctx, &last_error_ctx, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 309 | core_util_critical_section_exit(); |
kenjiArai | 1:9db0e321a9f4 | 310 | //We need not call delete_mbed_crc(crc_obj) here as we are going to reset the system anyway, and calling delete while handling a fatal error may cause nested exception |
kenjiArai | 1:9db0e321a9f4 | 311 | #if MBED_CONF_PLATFORM_FATAL_ERROR_AUTO_REBOOT_ENABLED && (MBED_CONF_PLATFORM_ERROR_REBOOT_MAX > 0) |
kenjiArai | 1:9db0e321a9f4 | 312 | #ifndef NDEBUG |
kenjiArai | 1:9db0e321a9f4 | 313 | mbed_error_printf("\n= System will be rebooted due to a fatal error =\n"); |
kenjiArai | 1:9db0e321a9f4 | 314 | if (report_error_ctx->error_reboot_count >= MBED_CONF_PLATFORM_ERROR_REBOOT_MAX) { |
kenjiArai | 1:9db0e321a9f4 | 315 | //We have rebooted more than enough, hold the system here. |
kenjiArai | 1:9db0e321a9f4 | 316 | mbed_error_printf("= Reboot count(=%" PRIi32") reached maximum, system will halt after rebooting =\n", report_error_ctx->error_reboot_count); |
kenjiArai | 1:9db0e321a9f4 | 317 | } |
kenjiArai | 1:9db0e321a9f4 | 318 | #endif |
kenjiArai | 1:9db0e321a9f4 | 319 | system_reset();//do a system reset to get the system rebooted |
kenjiArai | 1:9db0e321a9f4 | 320 | #endif |
kenjiArai | 1:9db0e321a9f4 | 321 | #endif |
kenjiArai | 1:9db0e321a9f4 | 322 | mbed_halt_system(); |
kenjiArai | 1:9db0e321a9f4 | 323 | } |
kenjiArai | 1:9db0e321a9f4 | 324 | |
kenjiArai | 1:9db0e321a9f4 | 325 | //Register an application defined callback with error handling |
kenjiArai | 1:9db0e321a9f4 | 326 | mbed_error_status_t mbed_set_error_hook(mbed_error_hook_t error_hook_in) |
kenjiArai | 1:9db0e321a9f4 | 327 | { |
kenjiArai | 1:9db0e321a9f4 | 328 | //register the new hook/callback |
kenjiArai | 1:9db0e321a9f4 | 329 | if (error_hook_in != NULL) { |
kenjiArai | 1:9db0e321a9f4 | 330 | error_hook = error_hook_in; |
kenjiArai | 1:9db0e321a9f4 | 331 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 332 | } |
kenjiArai | 1:9db0e321a9f4 | 333 | |
kenjiArai | 1:9db0e321a9f4 | 334 | return MBED_ERROR_INVALID_ARGUMENT; |
kenjiArai | 1:9db0e321a9f4 | 335 | } |
kenjiArai | 1:9db0e321a9f4 | 336 | |
kenjiArai | 1:9db0e321a9f4 | 337 | //Reset the reboot error context |
kenjiArai | 1:9db0e321a9f4 | 338 | mbed_error_status_t mbed_reset_reboot_error_info() |
kenjiArai | 1:9db0e321a9f4 | 339 | { |
kenjiArai | 1:9db0e321a9f4 | 340 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 341 | //Protect for thread safety |
kenjiArai | 1:9db0e321a9f4 | 342 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 343 | memset(report_error_ctx, 0, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 344 | core_util_critical_section_exit(); |
kenjiArai | 1:9db0e321a9f4 | 345 | #endif |
kenjiArai | 1:9db0e321a9f4 | 346 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 347 | } |
kenjiArai | 1:9db0e321a9f4 | 348 | |
kenjiArai | 1:9db0e321a9f4 | 349 | //Reset the reboot error context |
kenjiArai | 1:9db0e321a9f4 | 350 | mbed_error_status_t mbed_reset_reboot_count() |
kenjiArai | 1:9db0e321a9f4 | 351 | { |
kenjiArai | 1:9db0e321a9f4 | 352 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 353 | if (is_reboot_error_valid) { |
kenjiArai | 1:9db0e321a9f4 | 354 | uint32_t crc_val = 0; |
kenjiArai | 1:9db0e321a9f4 | 355 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 356 | report_error_ctx->error_reboot_count = 0;//Set reboot count to 0 |
kenjiArai | 1:9db0e321a9f4 | 357 | //Update CRC |
kenjiArai | 1:9db0e321a9f4 | 358 | crc_val = mbed_tiny_compute_crc32(report_error_ctx, offsetof(mbed_error_ctx, crc_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 359 | report_error_ctx->crc_error_ctx = crc_val; |
kenjiArai | 1:9db0e321a9f4 | 360 | core_util_critical_section_exit(); |
kenjiArai | 1:9db0e321a9f4 | 361 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 362 | } |
kenjiArai | 1:9db0e321a9f4 | 363 | #endif |
kenjiArai | 1:9db0e321a9f4 | 364 | return MBED_ERROR_ITEM_NOT_FOUND; |
kenjiArai | 1:9db0e321a9f4 | 365 | } |
kenjiArai | 1:9db0e321a9f4 | 366 | |
kenjiArai | 1:9db0e321a9f4 | 367 | //Retrieve the reboot error context |
kenjiArai | 1:9db0e321a9f4 | 368 | mbed_error_status_t mbed_get_reboot_error_info(mbed_error_ctx *error_info) |
kenjiArai | 1:9db0e321a9f4 | 369 | { |
kenjiArai | 1:9db0e321a9f4 | 370 | mbed_error_status_t status = MBED_ERROR_ITEM_NOT_FOUND; |
kenjiArai | 1:9db0e321a9f4 | 371 | #if MBED_CONF_PLATFORM_CRASH_CAPTURE_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 372 | if (is_reboot_error_valid) { |
kenjiArai | 1:9db0e321a9f4 | 373 | if (error_info != NULL) { |
kenjiArai | 1:9db0e321a9f4 | 374 | memcpy(error_info, report_error_ctx, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 375 | status = MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 376 | } else { |
kenjiArai | 1:9db0e321a9f4 | 377 | status = MBED_ERROR_INVALID_ARGUMENT; |
kenjiArai | 1:9db0e321a9f4 | 378 | } |
kenjiArai | 1:9db0e321a9f4 | 379 | } |
kenjiArai | 1:9db0e321a9f4 | 380 | #endif |
kenjiArai | 1:9db0e321a9f4 | 381 | return status; |
kenjiArai | 1:9db0e321a9f4 | 382 | } |
kenjiArai | 1:9db0e321a9f4 | 383 | |
kenjiArai | 1:9db0e321a9f4 | 384 | //Retrieve the first error context from error log |
kenjiArai | 1:9db0e321a9f4 | 385 | mbed_error_status_t mbed_get_first_error_info(mbed_error_ctx *error_info) |
kenjiArai | 1:9db0e321a9f4 | 386 | { |
kenjiArai | 1:9db0e321a9f4 | 387 | memcpy(error_info, &first_error_ctx, sizeof(first_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 388 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 389 | } |
kenjiArai | 1:9db0e321a9f4 | 390 | |
kenjiArai | 1:9db0e321a9f4 | 391 | //Retrieve the last error context from error log |
kenjiArai | 1:9db0e321a9f4 | 392 | mbed_error_status_t mbed_get_last_error_info(mbed_error_ctx *error_info) |
kenjiArai | 1:9db0e321a9f4 | 393 | { |
kenjiArai | 1:9db0e321a9f4 | 394 | memcpy(error_info, &last_error_ctx, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 395 | return MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 396 | } |
kenjiArai | 1:9db0e321a9f4 | 397 | |
kenjiArai | 1:9db0e321a9f4 | 398 | //Makes an mbed_error_status_t value |
kenjiArai | 1:9db0e321a9f4 | 399 | mbed_error_status_t mbed_make_error(mbed_error_type_t error_type, mbed_module_type_t entity, mbed_error_code_t error_code) |
kenjiArai | 1:9db0e321a9f4 | 400 | { |
kenjiArai | 1:9db0e321a9f4 | 401 | switch (error_type) { |
kenjiArai | 1:9db0e321a9f4 | 402 | case MBED_ERROR_TYPE_POSIX: |
kenjiArai | 1:9db0e321a9f4 | 403 | if (error_code >= MBED_POSIX_ERROR_BASE && error_code <= MBED_SYSTEM_ERROR_BASE) { |
kenjiArai | 1:9db0e321a9f4 | 404 | return -error_code; |
kenjiArai | 1:9db0e321a9f4 | 405 | } |
kenjiArai | 1:9db0e321a9f4 | 406 | break; |
kenjiArai | 1:9db0e321a9f4 | 407 | |
kenjiArai | 1:9db0e321a9f4 | 408 | case MBED_ERROR_TYPE_SYSTEM: |
kenjiArai | 1:9db0e321a9f4 | 409 | if (error_code >= MBED_SYSTEM_ERROR_BASE && error_code <= MBED_CUSTOM_ERROR_BASE) { |
kenjiArai | 1:9db0e321a9f4 | 410 | return MAKE_MBED_ERROR(MBED_ERROR_TYPE_SYSTEM, entity, error_code); |
kenjiArai | 1:9db0e321a9f4 | 411 | } |
kenjiArai | 1:9db0e321a9f4 | 412 | break; |
kenjiArai | 1:9db0e321a9f4 | 413 | |
kenjiArai | 1:9db0e321a9f4 | 414 | case MBED_ERROR_TYPE_CUSTOM: |
kenjiArai | 1:9db0e321a9f4 | 415 | if (error_code >= MBED_CUSTOM_ERROR_BASE) { |
kenjiArai | 1:9db0e321a9f4 | 416 | return MAKE_MBED_ERROR(MBED_ERROR_TYPE_CUSTOM, entity, error_code); |
kenjiArai | 1:9db0e321a9f4 | 417 | } |
kenjiArai | 1:9db0e321a9f4 | 418 | break; |
kenjiArai | 1:9db0e321a9f4 | 419 | |
kenjiArai | 1:9db0e321a9f4 | 420 | default: |
kenjiArai | 1:9db0e321a9f4 | 421 | break; |
kenjiArai | 1:9db0e321a9f4 | 422 | } |
kenjiArai | 1:9db0e321a9f4 | 423 | |
kenjiArai | 1:9db0e321a9f4 | 424 | //If we are passed incorrect values return a generic system error |
kenjiArai | 1:9db0e321a9f4 | 425 | return MAKE_MBED_ERROR(MBED_ERROR_TYPE_SYSTEM, MBED_MODULE_UNKNOWN, MBED_ERROR_CODE_UNKNOWN); |
kenjiArai | 1:9db0e321a9f4 | 426 | } |
kenjiArai | 1:9db0e321a9f4 | 427 | |
kenjiArai | 1:9db0e321a9f4 | 428 | /** |
kenjiArai | 1:9db0e321a9f4 | 429 | * Clears all the last error, error count and all entries in the error log. |
kenjiArai | 1:9db0e321a9f4 | 430 | * @return 0 or MBED_SUCCESS on success. |
kenjiArai | 1:9db0e321a9f4 | 431 | * |
kenjiArai | 1:9db0e321a9f4 | 432 | */ |
kenjiArai | 1:9db0e321a9f4 | 433 | mbed_error_status_t mbed_clear_all_errors(void) |
kenjiArai | 1:9db0e321a9f4 | 434 | { |
kenjiArai | 1:9db0e321a9f4 | 435 | mbed_error_status_t status = MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 436 | |
kenjiArai | 1:9db0e321a9f4 | 437 | //Make sure we dont multiple clients resetting |
kenjiArai | 1:9db0e321a9f4 | 438 | core_util_critical_section_enter(); |
kenjiArai | 1:9db0e321a9f4 | 439 | //Clear the error and context capturing buffer |
kenjiArai | 1:9db0e321a9f4 | 440 | memset(&last_error_ctx, 0, sizeof(mbed_error_ctx)); |
kenjiArai | 1:9db0e321a9f4 | 441 | //reset error count to 0 |
kenjiArai | 1:9db0e321a9f4 | 442 | error_count = 0; |
kenjiArai | 1:9db0e321a9f4 | 443 | #if MBED_CONF_PLATFORM_ERROR_HIST_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 444 | status = mbed_error_hist_reset(); |
kenjiArai | 1:9db0e321a9f4 | 445 | #endif |
kenjiArai | 1:9db0e321a9f4 | 446 | core_util_critical_section_exit(); |
kenjiArai | 1:9db0e321a9f4 | 447 | |
kenjiArai | 1:9db0e321a9f4 | 448 | return status; |
kenjiArai | 1:9db0e321a9f4 | 449 | } |
kenjiArai | 1:9db0e321a9f4 | 450 | |
kenjiArai | 1:9db0e321a9f4 | 451 | #ifdef MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 452 | static inline const char *name_or_unnamed(const osRtxThread_t *thread) |
kenjiArai | 1:9db0e321a9f4 | 453 | { |
kenjiArai | 1:9db0e321a9f4 | 454 | const char *unnamed = "<unnamed>"; |
kenjiArai | 1:9db0e321a9f4 | 455 | |
kenjiArai | 1:9db0e321a9f4 | 456 | if (!thread) { |
kenjiArai | 1:9db0e321a9f4 | 457 | return unnamed; |
kenjiArai | 1:9db0e321a9f4 | 458 | } |
kenjiArai | 1:9db0e321a9f4 | 459 | |
kenjiArai | 1:9db0e321a9f4 | 460 | const char *name = thread->name; |
kenjiArai | 1:9db0e321a9f4 | 461 | return name ? name : unnamed; |
kenjiArai | 1:9db0e321a9f4 | 462 | } |
kenjiArai | 1:9db0e321a9f4 | 463 | #endif // MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 464 | |
kenjiArai | 1:9db0e321a9f4 | 465 | #if MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 466 | /** Prints stack dump from given stack information. |
kenjiArai | 1:9db0e321a9f4 | 467 | * The arguments should be given in address raw value to check alignment. |
kenjiArai | 1:9db0e321a9f4 | 468 | * @param stack_start The address of stack start. |
kenjiArai | 1:9db0e321a9f4 | 469 | * @param stack_size The size of stack |
kenjiArai | 1:9db0e321a9f4 | 470 | * @param stack_sp The stack pointer currently at. */ |
kenjiArai | 1:9db0e321a9f4 | 471 | static void print_stack_dump_core(uint32_t stack_start, uint32_t stack_size, uint32_t stack_sp, const char *postfix) |
kenjiArai | 1:9db0e321a9f4 | 472 | { |
kenjiArai | 1:9db0e321a9f4 | 473 | #if MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 474 | #define STACK_DUMP_WIDTH 8 |
kenjiArai | 1:9db0e321a9f4 | 475 | #define INT_ALIGN_MASK (~(sizeof(int) - 1)) |
kenjiArai | 1:9db0e321a9f4 | 476 | mbed_error_printf("\nStack Dump: %s", postfix); |
kenjiArai | 1:9db0e321a9f4 | 477 | uint32_t st_end = (stack_start + stack_size) & INT_ALIGN_MASK; |
kenjiArai | 1:9db0e321a9f4 | 478 | uint32_t st = (stack_sp) & INT_ALIGN_MASK; |
kenjiArai | 1:9db0e321a9f4 | 479 | for (; st < st_end; st += sizeof(int) * STACK_DUMP_WIDTH) { |
kenjiArai | 1:9db0e321a9f4 | 480 | mbed_error_printf("\n0x%08" PRIX32 ":", st); |
kenjiArai | 1:9db0e321a9f4 | 481 | for (int i = 0; i < STACK_DUMP_WIDTH; i++) { |
kenjiArai | 1:9db0e321a9f4 | 482 | uint32_t st_cur = st + i * sizeof(int); |
kenjiArai | 1:9db0e321a9f4 | 483 | if (st_cur >= st_end) { |
kenjiArai | 1:9db0e321a9f4 | 484 | break; |
kenjiArai | 1:9db0e321a9f4 | 485 | } |
kenjiArai | 1:9db0e321a9f4 | 486 | uint32_t st_val = *((uint32_t *)st_cur); |
kenjiArai | 1:9db0e321a9f4 | 487 | mbed_error_printf("0x%08" PRIX32 " ", st_val); |
kenjiArai | 1:9db0e321a9f4 | 488 | } |
kenjiArai | 1:9db0e321a9f4 | 489 | } |
kenjiArai | 1:9db0e321a9f4 | 490 | mbed_error_printf("\n"); |
kenjiArai | 1:9db0e321a9f4 | 491 | #endif // MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 492 | } |
kenjiArai | 1:9db0e321a9f4 | 493 | |
kenjiArai | 1:9db0e321a9f4 | 494 | static void print_stack_dump(uint32_t stack_start, uint32_t stack_size, uint32_t stack_sp, const mbed_error_ctx *ctx) |
kenjiArai | 1:9db0e321a9f4 | 495 | { |
kenjiArai | 1:9db0e321a9f4 | 496 | if (ctx && mbed_error_is_handler(ctx)) { |
kenjiArai | 1:9db0e321a9f4 | 497 | // Stack dump extra for handler stack which may have accessed MSP. |
kenjiArai | 1:9db0e321a9f4 | 498 | mbed_fault_context_t *mfc = (mbed_fault_context_t *)ctx->error_value; |
kenjiArai | 1:9db0e321a9f4 | 499 | uint32_t msp_sp = mfc->MSP; |
kenjiArai | 1:9db0e321a9f4 | 500 | uint32_t psp_sp = mfc->PSP; |
kenjiArai | 1:9db0e321a9f4 | 501 | if (mfc && !(mfc->EXC_RETURN & 0x4)) { |
kenjiArai | 1:9db0e321a9f4 | 502 | // MSP mode. Then SP_reg is more correct. |
kenjiArai | 1:9db0e321a9f4 | 503 | msp_sp = mfc->SP_reg; |
kenjiArai | 1:9db0e321a9f4 | 504 | } else { |
kenjiArai | 1:9db0e321a9f4 | 505 | // PSP mode. Then SP_reg is more correct. |
kenjiArai | 1:9db0e321a9f4 | 506 | psp_sp = mfc->SP_reg; |
kenjiArai | 1:9db0e321a9f4 | 507 | } |
kenjiArai | 1:9db0e321a9f4 | 508 | uint32_t msp_size = MAX(0, (int)INITIAL_SP - (int)msp_sp); |
kenjiArai | 1:9db0e321a9f4 | 509 | print_stack_dump_core(msp_sp, msp_size, msp_sp, "MSP"); |
kenjiArai | 1:9db0e321a9f4 | 510 | |
kenjiArai | 1:9db0e321a9f4 | 511 | stack_sp = psp_sp; |
kenjiArai | 1:9db0e321a9f4 | 512 | } |
kenjiArai | 1:9db0e321a9f4 | 513 | |
kenjiArai | 1:9db0e321a9f4 | 514 | print_stack_dump_core(stack_start, stack_size, stack_sp, "PSP"); |
kenjiArai | 1:9db0e321a9f4 | 515 | } |
kenjiArai | 1:9db0e321a9f4 | 516 | #endif // MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 517 | |
kenjiArai | 1:9db0e321a9f4 | 518 | #if MBED_CONF_PLATFORM_ERROR_ALL_THREADS_INFO && defined(MBED_CONF_RTOS_PRESENT) |
kenjiArai | 1:9db0e321a9f4 | 519 | /* Prints info of a thread(using osRtxThread_t struct)*/ |
kenjiArai | 1:9db0e321a9f4 | 520 | static void print_thread(const osRtxThread_t *thread) |
kenjiArai | 1:9db0e321a9f4 | 521 | { |
kenjiArai | 1:9db0e321a9f4 | 522 | uint32_t stack_mem = (uint32_t)thread->stack_mem; |
kenjiArai | 1:9db0e321a9f4 | 523 | mbed_error_printf("\n%s State: 0x%" PRIX8 " Entry: 0x%08" PRIX32 " Stack Size: 0x%08" PRIX32 " Mem: 0x%08" PRIX32 " SP: 0x%08" PRIX32, name_or_unnamed(thread), thread->state, thread->thread_addr, thread->stack_size, stack_mem, thread->sp); |
kenjiArai | 1:9db0e321a9f4 | 524 | |
kenjiArai | 1:9db0e321a9f4 | 525 | #if MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 526 | print_stack_dump(stack_mem, thread->stack_size, thread->sp, NULL); |
kenjiArai | 1:9db0e321a9f4 | 527 | #endif |
kenjiArai | 1:9db0e321a9f4 | 528 | } |
kenjiArai | 1:9db0e321a9f4 | 529 | |
kenjiArai | 1:9db0e321a9f4 | 530 | /* Prints thread info from a list */ |
kenjiArai | 1:9db0e321a9f4 | 531 | static void print_threads_info(const osRtxThread_t *threads) |
kenjiArai | 1:9db0e321a9f4 | 532 | { |
kenjiArai | 1:9db0e321a9f4 | 533 | while (threads != NULL) { |
kenjiArai | 1:9db0e321a9f4 | 534 | print_thread(threads); |
kenjiArai | 1:9db0e321a9f4 | 535 | threads = threads->thread_next; |
kenjiArai | 1:9db0e321a9f4 | 536 | } |
kenjiArai | 1:9db0e321a9f4 | 537 | } |
kenjiArai | 1:9db0e321a9f4 | 538 | #endif |
kenjiArai | 1:9db0e321a9f4 | 539 | |
kenjiArai | 1:9db0e321a9f4 | 540 | #ifndef NDEBUG |
kenjiArai | 1:9db0e321a9f4 | 541 | #define GET_TARGET_NAME_STR(tgt_name) #tgt_name |
kenjiArai | 1:9db0e321a9f4 | 542 | #define GET_TARGET_NAME(tgt_name) GET_TARGET_NAME_STR(tgt_name) |
kenjiArai | 1:9db0e321a9f4 | 543 | static void print_error_report(const mbed_error_ctx *ctx, const char *error_msg, const char *error_filename, int error_line) |
kenjiArai | 1:9db0e321a9f4 | 544 | { |
kenjiArai | 1:9db0e321a9f4 | 545 | int error_code = MBED_GET_ERROR_CODE(ctx->error_status); |
kenjiArai | 1:9db0e321a9f4 | 546 | int error_module = MBED_GET_ERROR_MODULE(ctx->error_status); |
kenjiArai | 1:9db0e321a9f4 | 547 | |
kenjiArai | 1:9db0e321a9f4 | 548 | mbed_error_printf("\n\n++ MbedOS Error Info ++\nError Status: 0x%X Code: %d Module: %d\nError Message: ", ctx->error_status, error_code, error_module); |
kenjiArai | 1:9db0e321a9f4 | 549 | |
kenjiArai | 1:9db0e321a9f4 | 550 | switch (error_code) { |
kenjiArai | 1:9db0e321a9f4 | 551 | //These are errors reported by kernel handled from mbed_rtx_handlers |
kenjiArai | 1:9db0e321a9f4 | 552 | case MBED_ERROR_CODE_RTOS_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 553 | mbed_error_printf("Kernel Error: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 554 | break; |
kenjiArai | 1:9db0e321a9f4 | 555 | |
kenjiArai | 1:9db0e321a9f4 | 556 | case MBED_ERROR_CODE_RTOS_THREAD_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 557 | mbed_error_printf("Thread: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 558 | break; |
kenjiArai | 1:9db0e321a9f4 | 559 | |
kenjiArai | 1:9db0e321a9f4 | 560 | case MBED_ERROR_CODE_RTOS_MUTEX_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 561 | mbed_error_printf("Mutex: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 562 | break; |
kenjiArai | 1:9db0e321a9f4 | 563 | |
kenjiArai | 1:9db0e321a9f4 | 564 | case MBED_ERROR_CODE_RTOS_SEMAPHORE_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 565 | mbed_error_printf("Semaphore: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 566 | break; |
kenjiArai | 1:9db0e321a9f4 | 567 | |
kenjiArai | 1:9db0e321a9f4 | 568 | case MBED_ERROR_CODE_RTOS_MEMORY_POOL_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 569 | mbed_error_printf("MemoryPool: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 570 | break; |
kenjiArai | 1:9db0e321a9f4 | 571 | |
kenjiArai | 1:9db0e321a9f4 | 572 | case MBED_ERROR_CODE_RTOS_EVENT_FLAGS_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 573 | mbed_error_printf("EventFlags: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 574 | break; |
kenjiArai | 1:9db0e321a9f4 | 575 | |
kenjiArai | 1:9db0e321a9f4 | 576 | case MBED_ERROR_CODE_RTOS_TIMER_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 577 | mbed_error_printf("Timer: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 578 | break; |
kenjiArai | 1:9db0e321a9f4 | 579 | |
kenjiArai | 1:9db0e321a9f4 | 580 | case MBED_ERROR_CODE_RTOS_MESSAGE_QUEUE_EVENT: |
kenjiArai | 1:9db0e321a9f4 | 581 | mbed_error_printf("MessageQueue: 0x%" PRIX32 ", ", ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 582 | break; |
kenjiArai | 1:9db0e321a9f4 | 583 | |
kenjiArai | 1:9db0e321a9f4 | 584 | case MBED_ERROR_CODE_ASSERTION_FAILED: |
kenjiArai | 1:9db0e321a9f4 | 585 | mbed_error_printf("Assertion failed: "); |
kenjiArai | 1:9db0e321a9f4 | 586 | break; |
kenjiArai | 1:9db0e321a9f4 | 587 | |
kenjiArai | 1:9db0e321a9f4 | 588 | default: |
kenjiArai | 1:9db0e321a9f4 | 589 | //Nothing to do here, just print the error info down |
kenjiArai | 1:9db0e321a9f4 | 590 | break; |
kenjiArai | 1:9db0e321a9f4 | 591 | } |
kenjiArai | 1:9db0e321a9f4 | 592 | mbed_error_puts(error_msg); |
kenjiArai | 1:9db0e321a9f4 | 593 | mbed_error_printf("\nLocation: 0x%" PRIX32, ctx->error_address); |
kenjiArai | 1:9db0e321a9f4 | 594 | |
kenjiArai | 1:9db0e321a9f4 | 595 | /* We print the filename passed in, not any filename in the context. This |
kenjiArai | 1:9db0e321a9f4 | 596 | * avoids the console print for mbed_error being limited to the presence |
kenjiArai | 1:9db0e321a9f4 | 597 | * and length of the filename storage. Note that although the MBED_ERROR |
kenjiArai | 1:9db0e321a9f4 | 598 | * macro compiles out filenames unless platform.error-filename-capture-enabled |
kenjiArai | 1:9db0e321a9f4 | 599 | * is turned on, MBED_ASSERT always passes filenames, and other direct |
kenjiArai | 1:9db0e321a9f4 | 600 | * users of mbed_error() may also choose to. |
kenjiArai | 1:9db0e321a9f4 | 601 | */ |
kenjiArai | 1:9db0e321a9f4 | 602 | if (error_filename) { |
kenjiArai | 1:9db0e321a9f4 | 603 | mbed_error_puts("\nFile: "); |
kenjiArai | 1:9db0e321a9f4 | 604 | mbed_error_puts(error_filename); |
kenjiArai | 1:9db0e321a9f4 | 605 | mbed_error_printf("+%d", error_line); |
kenjiArai | 1:9db0e321a9f4 | 606 | } |
kenjiArai | 1:9db0e321a9f4 | 607 | |
kenjiArai | 1:9db0e321a9f4 | 608 | mbed_error_printf("\nError Value: 0x%" PRIX32, ctx->error_value); |
kenjiArai | 1:9db0e321a9f4 | 609 | #ifdef MBED_CONF_RTOS_PRESENT |
kenjiArai | 1:9db0e321a9f4 | 610 | bool is_handler = mbed_error_is_handler(ctx); |
kenjiArai | 1:9db0e321a9f4 | 611 | mbed_error_printf("\nCurrent Thread: %s%s Id: 0x%" PRIX32 " Entry: 0x%" PRIX32 " StackSize: 0x%" PRIX32 " StackMem: 0x%" PRIX32 " SP: 0x%" PRIX32 " ", |
kenjiArai | 1:9db0e321a9f4 | 612 | name_or_unnamed((osRtxThread_t *)ctx->thread_id), is_handler ? " <handler>" : "", |
kenjiArai | 1:9db0e321a9f4 | 613 | ctx->thread_id, ctx->thread_entry_address, ctx->thread_stack_size, ctx->thread_stack_mem, ctx->thread_current_sp); |
kenjiArai | 1:9db0e321a9f4 | 614 | #endif |
kenjiArai | 1:9db0e321a9f4 | 615 | |
kenjiArai | 1:9db0e321a9f4 | 616 | #if MBED_STACK_DUMP_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 617 | print_stack_dump(ctx->thread_stack_mem, ctx->thread_stack_size, ctx->thread_current_sp, ctx); |
kenjiArai | 1:9db0e321a9f4 | 618 | #endif |
kenjiArai | 1:9db0e321a9f4 | 619 | |
kenjiArai | 1:9db0e321a9f4 | 620 | #if MBED_CONF_PLATFORM_ERROR_ALL_THREADS_INFO && defined(MBED_CONF_RTOS_PRESENT) |
kenjiArai | 1:9db0e321a9f4 | 621 | mbed_error_printf("\nNext:"); |
kenjiArai | 1:9db0e321a9f4 | 622 | print_thread(osRtxInfo.thread.run.next); |
kenjiArai | 1:9db0e321a9f4 | 623 | |
kenjiArai | 1:9db0e321a9f4 | 624 | mbed_error_printf("\nReady:"); |
kenjiArai | 1:9db0e321a9f4 | 625 | print_threads_info(osRtxInfo.thread.ready.thread_list); |
kenjiArai | 1:9db0e321a9f4 | 626 | |
kenjiArai | 1:9db0e321a9f4 | 627 | mbed_error_printf("\nWait:"); |
kenjiArai | 1:9db0e321a9f4 | 628 | print_threads_info(osRtxInfo.thread.wait_list); |
kenjiArai | 1:9db0e321a9f4 | 629 | |
kenjiArai | 1:9db0e321a9f4 | 630 | mbed_error_printf("\nDelay:"); |
kenjiArai | 1:9db0e321a9f4 | 631 | print_threads_info(osRtxInfo.thread.delay_list); |
kenjiArai | 1:9db0e321a9f4 | 632 | #endif |
kenjiArai | 1:9db0e321a9f4 | 633 | #if !defined(MBED_SYS_STATS_ENABLED) |
kenjiArai | 1:9db0e321a9f4 | 634 | mbed_error_printf("\nFor more info, visit: https://mbed.com/s/error?error=0x%08X&tgt=" GET_TARGET_NAME(TARGET_NAME), ctx->error_status); |
kenjiArai | 1:9db0e321a9f4 | 635 | #else |
kenjiArai | 1:9db0e321a9f4 | 636 | mbed_stats_sys_t sys_stats; |
kenjiArai | 1:9db0e321a9f4 | 637 | mbed_stats_sys_get(&sys_stats); |
kenjiArai | 1:9db0e321a9f4 | 638 | mbed_error_printf("\nFor more info, visit: https://mbed.com/s/error?error=0x%08X&osver=%" PRId32 "&core=0x%08" PRIX32 "&comp=%d&ver=%" PRIu32 "&tgt=" GET_TARGET_NAME(TARGET_NAME), ctx->error_status, sys_stats.os_version, sys_stats.cpu_id, sys_stats.compiler_id, sys_stats.compiler_version); |
kenjiArai | 1:9db0e321a9f4 | 639 | #endif |
kenjiArai | 1:9db0e321a9f4 | 640 | |
kenjiArai | 1:9db0e321a9f4 | 641 | mbed_error_printf("\n-- MbedOS Error Info --\n"); |
kenjiArai | 1:9db0e321a9f4 | 642 | } |
kenjiArai | 1:9db0e321a9f4 | 643 | #endif //ifndef NDEBUG |
kenjiArai | 1:9db0e321a9f4 | 644 | |
kenjiArai | 1:9db0e321a9f4 | 645 | |
kenjiArai | 1:9db0e321a9f4 | 646 | #if MBED_CONF_PLATFORM_ERROR_HIST_ENABLED |
kenjiArai | 1:9db0e321a9f4 | 647 | //Retrieve the error context from error log at the specified index |
kenjiArai | 1:9db0e321a9f4 | 648 | mbed_error_status_t mbed_get_error_hist_info(int index, mbed_error_ctx *error_info) |
kenjiArai | 1:9db0e321a9f4 | 649 | { |
kenjiArai | 1:9db0e321a9f4 | 650 | return mbed_error_hist_get(index, error_info); |
kenjiArai | 1:9db0e321a9f4 | 651 | } |
kenjiArai | 1:9db0e321a9f4 | 652 | |
kenjiArai | 1:9db0e321a9f4 | 653 | //Retrieve the error log count |
kenjiArai | 1:9db0e321a9f4 | 654 | int mbed_get_error_hist_count(void) |
kenjiArai | 1:9db0e321a9f4 | 655 | { |
kenjiArai | 1:9db0e321a9f4 | 656 | return mbed_error_hist_get_count(); |
kenjiArai | 1:9db0e321a9f4 | 657 | } |
kenjiArai | 1:9db0e321a9f4 | 658 | |
kenjiArai | 1:9db0e321a9f4 | 659 | mbed_error_status_t mbed_save_error_hist(const char *path) |
kenjiArai | 1:9db0e321a9f4 | 660 | { |
kenjiArai | 1:9db0e321a9f4 | 661 | mbed_error_status_t ret = MBED_SUCCESS; |
kenjiArai | 1:9db0e321a9f4 | 662 | mbed_error_ctx ctx = {0}; |
kenjiArai | 1:9db0e321a9f4 | 663 | int log_count = mbed_error_hist_get_count(); |
kenjiArai | 1:9db0e321a9f4 | 664 | FILE *error_log_file = NULL; |
kenjiArai | 1:9db0e321a9f4 | 665 | |
kenjiArai | 1:9db0e321a9f4 | 666 | //Ensure path is valid |
kenjiArai | 1:9db0e321a9f4 | 667 | if (path == NULL) { |
kenjiArai | 1:9db0e321a9f4 | 668 | ret = MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_INVALID_ARGUMENT); |
kenjiArai | 1:9db0e321a9f4 | 669 | goto exit; |
kenjiArai | 1:9db0e321a9f4 | 670 | } |
kenjiArai | 1:9db0e321a9f4 | 671 | |
kenjiArai | 1:9db0e321a9f4 | 672 | //Open the file for saving the error log info |
kenjiArai | 1:9db0e321a9f4 | 673 | if ((error_log_file = fopen(path, "w")) == NULL) { |
kenjiArai | 1:9db0e321a9f4 | 674 | ret = MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_OPEN_FAILED); |
kenjiArai | 1:9db0e321a9f4 | 675 | goto exit; |
kenjiArai | 1:9db0e321a9f4 | 676 | } |
kenjiArai | 1:9db0e321a9f4 | 677 | |
kenjiArai | 1:9db0e321a9f4 | 678 | //First store the first and last errors |
kenjiArai | 1:9db0e321a9f4 | 679 | if (fprintf(error_log_file, "\nFirst Error: Status:0x%x ThreadId:0x%x Address:0x%x Value:0x%x\n", |
kenjiArai | 1:9db0e321a9f4 | 680 | (unsigned int)first_error_ctx.error_status, |
kenjiArai | 1:9db0e321a9f4 | 681 | (unsigned int)first_error_ctx.thread_id, |
kenjiArai | 1:9db0e321a9f4 | 682 | (unsigned int)first_error_ctx.error_address, |
kenjiArai | 1:9db0e321a9f4 | 683 | (unsigned int)first_error_ctx.error_value) <= 0) { |
kenjiArai | 1:9db0e321a9f4 | 684 | ret = MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_WRITE_FAILED); |
kenjiArai | 1:9db0e321a9f4 | 685 | goto exit; |
kenjiArai | 1:9db0e321a9f4 | 686 | } |
kenjiArai | 1:9db0e321a9f4 | 687 | |
kenjiArai | 1:9db0e321a9f4 | 688 | if (fprintf(error_log_file, "\nLast Error: Status:0x%x ThreadId:0x%x Address:0x%x Value:0x%x\n", |
kenjiArai | 1:9db0e321a9f4 | 689 | (unsigned int)last_error_ctx.error_status, |
kenjiArai | 1:9db0e321a9f4 | 690 | (unsigned int)last_error_ctx.thread_id, |
kenjiArai | 1:9db0e321a9f4 | 691 | (unsigned int)last_error_ctx.error_address, |
kenjiArai | 1:9db0e321a9f4 | 692 | (unsigned int)last_error_ctx.error_value) <= 0) { |
kenjiArai | 1:9db0e321a9f4 | 693 | ret = MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_WRITE_FAILED); |
kenjiArai | 1:9db0e321a9f4 | 694 | goto exit; |
kenjiArai | 1:9db0e321a9f4 | 695 | } |
kenjiArai | 1:9db0e321a9f4 | 696 | |
kenjiArai | 1:9db0e321a9f4 | 697 | //Update with error log info |
kenjiArai | 1:9db0e321a9f4 | 698 | while (--log_count >= 0) { |
kenjiArai | 1:9db0e321a9f4 | 699 | mbed_error_hist_get(log_count, &ctx); |
kenjiArai | 1:9db0e321a9f4 | 700 | //first line of file will be error log count |
kenjiArai | 1:9db0e321a9f4 | 701 | if (fprintf(error_log_file, "\n%d: Status:0x%x ThreadId:0x%x Address:0x%x Value:0x%x\n", |
kenjiArai | 1:9db0e321a9f4 | 702 | log_count, |
kenjiArai | 1:9db0e321a9f4 | 703 | (unsigned int)ctx.error_status, |
kenjiArai | 1:9db0e321a9f4 | 704 | (unsigned int)ctx.thread_id, |
kenjiArai | 1:9db0e321a9f4 | 705 | (unsigned int)ctx.error_address, |
kenjiArai | 1:9db0e321a9f4 | 706 | (unsigned int)ctx.error_value) <= 0) { |
kenjiArai | 1:9db0e321a9f4 | 707 | ret = MBED_MAKE_ERROR(MBED_MODULE_PLATFORM, MBED_ERROR_CODE_WRITE_FAILED); |
kenjiArai | 1:9db0e321a9f4 | 708 | goto exit; |
kenjiArai | 1:9db0e321a9f4 | 709 | } |
kenjiArai | 1:9db0e321a9f4 | 710 | } |
kenjiArai | 1:9db0e321a9f4 | 711 | |
kenjiArai | 1:9db0e321a9f4 | 712 | exit: |
kenjiArai | 1:9db0e321a9f4 | 713 | fclose(error_log_file); |
kenjiArai | 1:9db0e321a9f4 | 714 | |
kenjiArai | 1:9db0e321a9f4 | 715 | return ret; |
kenjiArai | 1:9db0e321a9f4 | 716 | } |
kenjiArai | 1:9db0e321a9f4 | 717 | #endif |