/****************************************************************************** 版权所有: 文件名称: 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) 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 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); } /*------------------------------- 文件结束 -------------------------------*/