/****************************************************************************** 版权所有: 文件名称: watchdog.c 文件版本: 01.00 创建作者: sunxi 创建日期: 2022-05-19 功能说明: watchdog 的使用说明: 1.本看门狗只侦察本进程中的各个线程的运行情况; 2.如何使用,请参考watchdog_test(); 3. 在阻塞型的线程中,不能加入watchdog; 4. 本狗为应用狗; 其它说明: 修改记录: */ /*------------------------------- 头文件 -------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include "../include/bspconfig.h" #include "rt.h" #include "watchdog.h" #include "shm_comm_packet.h" #include "shm.h" // #define WD_PATH "/tmp/wdt_record" #define WD_PATH "/app/data/wdt_record" // noted by sunxi: 保存在flash中,重启后,不会掉失 #define WDT_MAX_ITEMS 64 #define wd_info_str "" // 心跳结构体 typedef struct HB_T { uint16_t A; uint16_t B; uint16_t heartbeat_A; // 每秒加1 uint16_t heartbeat_B; // 与heartbeat_A相同 uint16_t crc; } HB_T; // 裸核程序空间写完标志结构体 typedef struct SHM_E907_PRO_W_F_T { uint16_t A; uint16_t B; uint16_t finish_flag; // 裸核程序空间写完时,0x55写入finish_flag. uint32_t len; // 裸核程序的实际长度 uint16_t crc; } SHM_E907_PRO_W_F_T; typedef struct _wdt_item { const char *name; uint8_t feed; uint32_t runing; // 计时器 uint32_t period; // 秒 } wdt_item_t; struct ap_watchdog_t { int (*add_item)(const char *name, uint32_t *id, uint32_t period); int (*remove_item)(uint32_t id); int (*feed)(uint32_t id); int (*set_period)(uint32_t id, uint32_t seconds); uint32_t (*get_period)(uint32_t id); int (*start)(void); int (*stop)(void); int (*is_start)(void); int (*record)(const char *name); void (*set_debug)(int set); char appName[128]; int _debug; wdt_item_t items[WDT_MAX_ITEMS]; uint8_t runing_flag; pthread_mutex_t mutex; }; static pthread_t pthread_tid = 0; int watchdog_feed_flag = 0; // 放到心跳线程(与db进行心跳握手),以确保watchdog_thread线程运行正常 struct ap_watchdog_t ap_watchdog; extern uint32_t wdt_id_mainloop; // 检查文件夹是否存在 // 不存在,创建 int CreateNewFolder(char *path) { DIR *dir; if (path == NULL) { return -1; } // 检查文件夹是否存在 dir = opendir(path); if (NULL == dir) { // 不存在,创建 mkdir(path, 0775); } else { closedir(dir); } return 0; } int watchdog_init(void) { int i; CreateNewFolder(WD_PATH); memset(ap_watchdog.items, 0, sizeof(ap_watchdog.items)); for (i = 0; i < WDT_MAX_ITEMS; i++) ap_watchdog.items[i].feed = 1; ap_watchdog.runing_flag = 0; ap_watchdog._debug = 0; strcpy(ap_watchdog.appName, "dtu_t536"); pthread_mutex_init(&ap_watchdog.mutex, NULL); ap_watchdog.start(); return 0; } int watchdog_exit(void) { ap_watchdog.stop(); pthread_join(pthread_tid, NULL); return 0; } // period:周期,单位为秒 static int add_item(const char *name, uint32_t *id, uint32_t period) { int i; if (name == NULL) return -1; int ret = -1; // try to get mutex struct timespec tout; clock_gettime(CLOCK_REALTIME, &tout); tout.tv_sec += 5; // 5s default if (pthread_mutex_timedlock(&ap_watchdog.mutex, &tout) != 0) return -1; for (i = 0; i < WDT_MAX_ITEMS; i++) { if (ap_watchdog.items[i].name == NULL) { ap_watchdog.items[i].feed = 1; ap_watchdog.items[i].runing = 0; ap_watchdog.items[i].period = period; *id = i; ap_watchdog.items[i].name = name; rt_printf("WDT add_item i = %d,name = %s \r\n", i, ap_watchdog.items[i].name); ret = 0; break; } } pthread_mutex_unlock(&ap_watchdog.mutex); return ret; } static int remove_item(uint32_t id) { if (id > WDT_MAX_ITEMS) return -1; // try to get mutex struct timespec tout; clock_gettime(CLOCK_REALTIME, &tout); tout.tv_sec += 5; // 5s default if (pthread_mutex_timedlock(&ap_watchdog.mutex, &tout) != 0) return -1; ap_watchdog.items[id].name = NULL; ap_watchdog.items[id].feed = 1; ap_watchdog.items[id].runing = 0; pthread_mutex_unlock(&ap_watchdog.mutex); return 0; } static int feed(uint32_t id) { if (id > WDT_MAX_ITEMS) return -1; ap_watchdog.items[id].feed = 1; return 0; } static int set_period(uint32_t id, uint32_t seconds) { if (id > WDT_MAX_ITEMS) return -1; ap_watchdog.items[id].period = seconds; return 0; } static uint32_t get_period(uint32_t id) { if (id > WDT_MAX_ITEMS) return -1; return ap_watchdog.items[id].period; } // 设置心跳 static int write_freq_hb(uint8_t *data) { int ret = 0; int cnt = 0; if (data == NULL) return -1; while (1) { // ret = shm_comm_packet_write(SHM_ADDR_D_HB, data, sizeof(HB_T)); if (ret > 0) { break; } if (++cnt > 3) break; usleep(30); } return ret; } static int get_e907_PRO_W_F(void) { SHM_E907_PRO_W_F_T flag; int ret = 0; int cnt = 0; while (1) { ret = shm_comm_packet_read(SHM_ADDR_U_PRO_W_F, sizeof(flag), (uint8_t *)&flag, sizeof(flag)); if (ret > 0) { if (flag.finish_flag == 0x55) return flag.len; else return -1; } if (++cnt > 3) break; usleep(30); } return ret; } static void *watchdog_thread(void *arg) { int i = 0; static HB_T hb; int ret = 0; static int read_flag = 0; struct file *pfile; loff_t pos; memset(&hb, 0, sizeof(hb)); pthread_detach(pthread_self()); prctl(PR_SET_NAME, "watchdog_thread"); while (ap_watchdog.is_start()) { watchdog_feed_flag = 1; sleep(1); // 把裸核程序读出来,并写到tmp文件夹中。 if (read_flag == 0) { ret = get_e907_PRO_W_F(); if (ret > 100) // 返回裸核程序的实际长度,所以一定会大于100才有意义 { // finish read_flag = 1; // 从shm中读取裸核程序 // 创建数据文件 pfile = rt_file_open("/tmp/amp_rv0.bin", O_CREAT | O_RDWR, 0); if (!IS_ERR(pfile)) { pos = 0; rt_file_write(pfile, SHM_BASE_R + SHM_ADDR_U_E907_PRO, ret, &pos); rt_file_close(pfile, 0); } } } hb.heartbeat_A++; hb.heartbeat_B = hb.heartbeat_A; write_freq_hb((uint8_t *)&hb); // 给裸核的心跳 for (i = 0; i < WDT_MAX_ITEMS; i++) { if (ap_watchdog.items[i].name != NULL) { if (++ap_watchdog.items[i].runing > ap_watchdog.items[i].period) { if (ap_watchdog.items[i].feed != 1) { ap_watchdog.record(ap_watchdog.items[i].name); rt_printf("WDT-------%s is timeout!!!\r\n", ap_watchdog.items[i].name); system("reboot -f"); // exit(EXIT_FAILURE);//TODO. noted by sunxi: 这里需要确认是,是重启应用程序,还是系统reboot!!! } else { if (ap_watchdog._debug) { rt_printf("WDT-------%s is Runing.\r\n", ap_watchdog.items[i].name); } } ap_watchdog.items[i].feed = 0; ap_watchdog.items[i].runing = 0; } } } } return NULL; } static int start(void) { int ret; if (ap_watchdog.runing_flag == 1) { return 0; } ap_watchdog._debug = 0; ap_watchdog.runing_flag = 1; ret = pthread_create(&pthread_tid, NULL, watchdog_thread, NULL); if (ret != 0) { rt_printf("can not create thread any more: %d\r\n", errno); ap_watchdog.runing_flag = 0; return -1; } return 0; } static int stop(void) { ap_watchdog.runing_flag = 0; return 0; } static int is_start(void) { return ap_watchdog.runing_flag; } static int record(const char *name) { time_t tt; char buf[512]; FILE *f = NULL; char rec[256]; char tmpBuf[128]; int size; if (name == NULL) return -1; tt = time(NULL); memset(buf, 0, sizeof(buf)); sprintf(buf, "%s/wdt_record_%s", WD_PATH, ap_watchdog.appName); f = fopen(buf, "a"); if (f == NULL) { perror("fopen"); rt_printf("fopen is error!!!\r\n"); return -1; } memset(tmpBuf, 0, sizeof(tmpBuf)); strftime(tmpBuf, 128, "%Y-%m-%d %H:%M:%S", localtime(&tt)); memset(rec, 0, sizeof(rec)); size = snprintf(rec, sizeof(rec), "%s %s fuck you up; \r\n", tmpBuf, name); fwrite(rec, size, 1, f); fclose(f); return 0; } static void set_debug(int set) { ap_watchdog._debug = set; } struct ap_watchdog_t ap_watchdog = { .add_item = add_item, .remove_item = remove_item, .feed = feed, .set_period = set_period, .get_period = get_period, .start = start, .stop = stop, .is_start = is_start, .record = record, .set_debug = set_debug, }; /* period: 看门狗的喂狗周期 因为可能存在某些线程,其运行过程很长,运行的中间,没有合里加sleep等休息函数, 造成占用时间较长。 于是,这里的period,可以尽量设置长一点的时间。 */ int watchdog_add_item(const char *name, uint32_t *id, uint32_t period) { int ret = 0; ret = ap_watchdog.add_item(name, id, period); return ret; } int watchdog_feed(uint32_t id) { int ret = 0; ret = ap_watchdog.feed(id); return ret; } int watchdog_remove_item(uint32_t id) { int ret = 0; ret = ap_watchdog.remove_item(id); return ret; } //=================================================================================================================== // test void *watchdog_test_pthread(void *arg) { pthread_detach(pthread_self()); uint32_t wdt_id; int rc = watchdog_add_item("watchdog_test_pthread", &wdt_id, 10); if (rc != 0) { printf("can not add watchdog_test_pthread task to wdt\r\n"); return NULL; } while (1) { // 把喂狗操作放在首行,确保每个循环都有喂狗 watchdog_feed(wdt_id); // 去掉本行喂狗,就可以产生记录 sleep(1); } watchdog_remove_item(wdt_id); return NULL; } int watchdog_test(void) { int ret; watchdog_init(); pthread_t ConnectCheck_id; ret = pthread_create(&ConnectCheck_id, NULL, watchdog_test_pthread, NULL); if (ret != 0) { printf("can not create watchdog_test_pthread\r\n"); return -1; } return 0; } void watchdog_feed_mainloop(void) { watchdog_feed(wdt_id_mainloop); } void watchdog_feed_protect(void) { } void watchdog_reset_cpu(int i) { printf("reboot code: %d .\n", i); system("reboot -f"); } void watchdog_feed_mainloop_50s(void) { watchdog_feed(wdt_id_mainloop); } /*------------------------------- 文件结束 -------------------------------*/