mirror of
https://github.com/google/pebble
synced 2026-10-14 07:44:10 +02:00
208 lines
7.0 KiB
C
208 lines
7.0 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 "debug.h"
|
|
#include "advanced_logging.h"
|
|
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
|
|
#include "comm/ble/gatt_service_changed.h"
|
|
#include "drivers/pmic.h"
|
|
#include "kernel/core_dump.h"
|
|
#include "kernel/event_loop.h"
|
|
#include "kernel/pbl_malloc.h"
|
|
#include "popups/crashed_ui.h"
|
|
#include "services/common/analytics/analytics.h"
|
|
#include "system/logging.h"
|
|
#include "system/reboot_reason.h"
|
|
|
|
static void log_reboot_reason_cb(void *reason) {
|
|
AnalyticsEventCrash *crash_report = (AnalyticsEventCrash *)reason;
|
|
analytics_event_crash(crash_report->crash_code, crash_report->link_register);
|
|
kernel_free(crash_report);
|
|
}
|
|
|
|
static RebootReasonCode s_last_reboot_reason_code = RebootReasonCode_Unknown;
|
|
RebootReasonCode reboot_reason_get_last_reboot_reason(void) {
|
|
return s_last_reboot_reason_code;
|
|
}
|
|
|
|
void debug_reboot_reason_print(McuRebootReason mcu_reboot_reason) {
|
|
RebootReason reason;
|
|
reboot_reason_get(&reason);
|
|
bool show_reset_alert = !reason.restarted_safely;
|
|
s_last_reboot_reason_code = reason.code;
|
|
|
|
// We're out of flash space, scrape a few bytes back!
|
|
static const char* rebooted_due_to = " rebooted due to ";
|
|
|
|
const char* restarted_safely_string = "Safely";
|
|
if (!reason.restarted_safely) {
|
|
restarted_safely_string = "Dangerously";
|
|
}
|
|
|
|
// Keep hourly logging to keep track of hours without crashes.
|
|
analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_CODE,
|
|
0xDEAD0000 | reason.code, AnalyticsClient_System);
|
|
uint32_t lr = reason.extra;
|
|
|
|
// Leave this NULL to do your own printing.
|
|
const char *reason_string = NULL;
|
|
switch (reason.code) {
|
|
// Normal stuff
|
|
case RebootReasonCode_Unknown:
|
|
reason_string = "We don't know why we %s rebooted.";
|
|
lr = mcu_reboot_reason.reset_mask;
|
|
break;
|
|
case RebootReasonCode_LowBattery:
|
|
reason_string = "%s%sLowBattery";
|
|
break;
|
|
case RebootReasonCode_SoftwareUpdate:
|
|
gatt_service_changed_server_handle_fw_update();
|
|
reason_string = "%s%sSoftwareUpdate";
|
|
break;
|
|
case RebootReasonCode_ResetButtonsHeld:
|
|
// Since we forced the reset, it isn't unexpected
|
|
show_reset_alert = false;
|
|
reason_string = "%s%sResetButtonsHeld";
|
|
break;
|
|
case RebootReasonCode_ShutdownMenuItem:
|
|
reason_string = "%s%sLowBattery";
|
|
break;
|
|
case RebootReasonCode_FactoryResetReset:
|
|
reason_string = "%s%sFactoryResetReset";
|
|
break;
|
|
case RebootReasonCode_FactoryResetShutdown:
|
|
reason_string = "%s%sFactoryResetShutdown";
|
|
break;
|
|
case RebootReasonCode_MfgShutdown:
|
|
reason_string = "%s%sMfgShutdown";
|
|
break;
|
|
case RebootReasonCode_Serial:
|
|
reason_string = "%s%sSerial";
|
|
break;
|
|
case RebootReasonCode_RemoteReset:
|
|
reason_string = "%s%sa Remote Reset";
|
|
break;
|
|
case RebootReasonCode_ForcedCoreDump:
|
|
reason_string = "%s%sa Forced Coredump";
|
|
break;
|
|
case RebootReasonCode_PrfIdle:
|
|
reason_string = "%s%sIdle PRF";
|
|
break;
|
|
// Error occurred
|
|
case RebootReasonCode_Assert:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sAssert: LR %#"PRIxPTR;
|
|
break;
|
|
case RebootReasonCode_HardFault:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sHardFault: LR %#"PRIxPTR;
|
|
break;
|
|
case RebootReasonCode_LauncherPanic:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sLauncherPanic: code 0x%"PRIx32;
|
|
break;
|
|
case RebootReasonCode_ClockFailure:
|
|
reason_string = "%s%sClock Failure";
|
|
break;
|
|
case RebootReasonCode_WorkerHardFault:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sWorker HardFault";
|
|
break;
|
|
case RebootReasonCode_OutOfMemory:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sOOM";
|
|
break;
|
|
case RebootReasonCode_BtCoredump:
|
|
show_reset_alert = true;
|
|
reason_string = "%s%sBT Coredump";
|
|
break;
|
|
default:
|
|
reason_string = "%s%sUnrecognized Reason";
|
|
break;
|
|
// Error occurred
|
|
case RebootReasonCode_Watchdog:
|
|
show_reset_alert = true;
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "%s%sWatchdog: Bits 0x%" PRIx8 ", Mask 0x%" PRIx8,
|
|
restarted_safely_string, rebooted_due_to, reason.data8[0], reason.data8[1]);
|
|
|
|
if (reason.watchdog.stuck_task_pc != 0) {
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "Stuck task PC: 0x%" PRIx32 ", LR: 0x%" PRIx32,
|
|
reason.watchdog.stuck_task_pc, reason.watchdog.stuck_task_lr);
|
|
|
|
if (reason.watchdog.stuck_task_callback) {
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "Stuck callback: 0x%" PRIx32,
|
|
reason.watchdog.stuck_task_callback);
|
|
}
|
|
}
|
|
break;
|
|
case RebootReasonCode_StackOverflow:
|
|
show_reset_alert = true;
|
|
PebbleTask task = (PebbleTask) reason.data8[0];
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "%s%sStackOverflow: Task #%d (%s)", restarted_safely_string,
|
|
rebooted_due_to, task, pebble_task_get_name(task));
|
|
break;
|
|
case RebootReasonCode_EventQueueFull:
|
|
show_reset_alert = true;
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "%s%sEvent Queue Full", restarted_safely_string, rebooted_due_to);
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "Task: <%s> LR: 0x%"PRIx32" Current: 0x%"PRIx32" Dropped: 0x%"PRIx32,
|
|
pebble_task_get_name(reason.event_queue.destination_task),
|
|
reason.event_queue.push_lr,
|
|
reason.event_queue.current_event,
|
|
reason.event_queue.dropped_event);
|
|
break;
|
|
}
|
|
// Generic reason string
|
|
if (reason_string) {
|
|
DEBUG_LOG(LOG_LEVEL_INFO, reason_string, restarted_safely_string, rebooted_due_to,
|
|
reason.extra);
|
|
}
|
|
|
|
analytics_set(ANALYTICS_DEVICE_METRIC_SYSTEM_CRASH_LR, lr, AnalyticsClient_System);
|
|
|
|
// We need to wait for the logging service to initialize.
|
|
AnalyticsEventCrash *crash_report = kernel_malloc_check(sizeof(AnalyticsEventCrash));
|
|
*crash_report = (AnalyticsEventCrash) {
|
|
.crash_code = reason.code,
|
|
.link_register = lr
|
|
};
|
|
launcher_task_add_callback(log_reboot_reason_cb, crash_report);
|
|
|
|
if (is_unread_coredump_available()) {
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "Unread coredump file is present!");
|
|
}
|
|
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "MCU reset reason mask: 0x%x", (int)mcu_reboot_reason.reset_mask);
|
|
#if CAPABILITY_HAS_PMIC
|
|
uint32_t pmic_reset_reason = pmic_get_last_reset_reason();
|
|
if (pmic_reset_reason != 0) {
|
|
DEBUG_LOG(LOG_LEVEL_INFO, "PMIC reset reason mask: 0x%x", (int)pmic_reset_reason);
|
|
}
|
|
#endif
|
|
|
|
#ifdef SHOW_PEBBLE_JUST_RESET_ALERT
|
|
// Trigger an alert display so that the user knows the watch rebooted due to a crash. This event
|
|
// will be caught and handled by the launcher.c event loop.
|
|
if (show_reset_alert) {
|
|
crashed_ui_show_pebble_reset();
|
|
}
|
|
#endif
|
|
|
|
reboot_reason_clear();
|
|
}
|