feat(crash): meow_crash 系统级崩溃捕获 (agent-187) — svcd 服务异常退出上报(AF_UNIX best-effort,不崩svcd)+dmesg增量扫segfault/oops; 落盘 TF MeowOS/logs/crash_<bootts>.log 无卡回退/tmp+迁移; CLI dump/tail; S89 预置 crash.conf(主会话补)
meow_crash 62781084; meow_svcd da6ebc66→605646c4(仅+68行上报挂点); 宿主5场景全过(上报链路/dmesg增量/无卡迁移/铁律验证/CLI)
This commit is contained in:
1 parent
243148e440
commit
cf86cbd387
10 files changed
+885
-1
No files matched your search
@@ -94,6 +94,22 @@ EOF
|
||||
chmod 644 /usr/data/meow/services.d/meow_svc_power.conf
|
||||
echo -n "provisioned power.conf "
|
||||
fi
|
||||
if [ -f /usr/data/meow/services.d/meow_crash.conf ]; then
|
||||
echo -n "crash.conf exists "
|
||||
else
|
||||
cat > /usr/data/meow/services.d/meow_crash.conf <<'EOF'
|
||||
exec=/usr/bin/meow_crash
|
||||
args=
|
||||
enabled=1
|
||||
restart=on-fail
|
||||
backoff_ms=2000
|
||||
max_restart=5
|
||||
trigger=boot
|
||||
cap=
|
||||
EOF
|
||||
chmod 644 /usr/data/meow/services.d/meow_crash.conf
|
||||
echo -n "provisioned crash.conf "
|
||||
fi
|
||||
else
|
||||
echo -n "skip svc confs (/usr/data/meow not writable) "
|
||||
fi
|
||||
|
||||
Binary file not shown.
@@ -97,6 +97,8 @@
|
||||
#define SVCD_SOCK_PATH_MAX 108
|
||||
#define SVCD_SIGCHLD_CHAR 'C' /* 自管道字节(拉起 poll) */
|
||||
#define SVCD_SIGTERM_CHAR 'T'
|
||||
#define SVCD_CRASH_SOCK "/tmp/meow_crash.sock" /* meow_crash 崩溃上报 socket(best-effort) */
|
||||
#define SVCD_CRASH_POLL_MS 150 /* 上报 connect 的排空等待上限(绝不重试) */
|
||||
|
||||
/* ---- ANSI 终端配色(status 回显用;仅客户端 stdout 为 TTY 时保留,非 TTY 去色) ---- */
|
||||
#define AN_RESET "\033[0m"
|
||||
@@ -532,6 +534,65 @@ static void sig_term(int sig)
|
||||
(void)w;
|
||||
}
|
||||
|
||||
/* /proc/uptime 首字段 → 秒(整数)。失败返回 0。 */
|
||||
static long svcd_uptime(void)
|
||||
{
|
||||
int fd = open("/proc/uptime", O_RDONLY);
|
||||
if (fd < 0) return 0;
|
||||
char b[64];
|
||||
ssize_t n = read(fd, b, sizeof b - 1);
|
||||
close(fd);
|
||||
if (n <= 0) return 0;
|
||||
b[n] = '\0';
|
||||
return (long)atof(b);
|
||||
}
|
||||
|
||||
/* 服务异常退出 → 上报 meow_crash(best-effort,绝不阻塞/重试 svcd)。
|
||||
*
|
||||
* 铁律:服务崩不崩 svcd。上报全程非阻塞:
|
||||
* - 客户端 socket 置 O_NONBLOCK;
|
||||
* - connect 失败即放弃;若 EINPROGRESS/EWOULDBLOCK,短 poll(POLLOUT) ≤150ms 排空
|
||||
* 本地 listen backlog,仍不可写即丢弃;
|
||||
* - 单行小消息一次 write(失败即弃),绝不重试。
|
||||
* meow_crash 不在线(connect ECONNREFUSED 等)→ 静默丢,不影响 svcd 任何状态。
|
||||
*/
|
||||
static void notify_crash(const char *name, int status)
|
||||
{
|
||||
const char *kind;
|
||||
int num;
|
||||
if (WIFSIGNALED(status)) { kind = "sig"; num = WTERMSIG(status); }
|
||||
else { kind = "exit"; num = WIFEXITED(status) ? WEXITSTATUS(status) : 1; }
|
||||
|
||||
char msg[256];
|
||||
snprintf(msg, sizeof msg, "SVC %s %s %d %ld %ld\n",
|
||||
name, kind, num, (long)time(NULL), svcd_uptime());
|
||||
|
||||
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
if (fd < 0) return;
|
||||
int fl = fcntl(fd, F_GETFL, 0);
|
||||
if (fl >= 0) fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||
|
||||
struct sockaddr_un a;
|
||||
memset(&a, 0, sizeof a);
|
||||
a.sun_family = AF_UNIX;
|
||||
safe_strcpy(a.sun_path, sizeof a.sun_path, SVCD_CRASH_SOCK);
|
||||
|
||||
if (connect(fd, (struct sockaddr *)&a, sizeof a) != 0) {
|
||||
if (errno == EINPROGRESS || errno == EWOULDBLOCK) {
|
||||
struct pollfd pf;
|
||||
pf.fd = fd; pf.events = POLLOUT; pf.revents = 0;
|
||||
int r = poll(&pf, 1, SVCD_CRASH_POLL_MS);
|
||||
if (r <= 0 || !(pf.revents & POLLOUT)) { close(fd); return; }
|
||||
} else {
|
||||
close(fd);
|
||||
return;
|
||||
}
|
||||
}
|
||||
ssize_t w = write(fd, msg, strlen(msg));
|
||||
(void)w;
|
||||
close(fd);
|
||||
}
|
||||
|
||||
/* 排空 SIGCHLD 自管道,waitpid 匹配所有已退出子进程。 */
|
||||
static void reap_children(void)
|
||||
{
|
||||
@@ -545,7 +606,13 @@ static void reap_children(void)
|
||||
for (int i = 0; i < g_nsvc; i++)
|
||||
if (g_svcs[i].pid == pid) { idx = i; break; }
|
||||
if (idx < 0) { svcd_log("reap unknown pid=%d", (int)pid); continue; }
|
||||
apply_exit(&g_svcs[idx], status);
|
||||
svc_t *s = &g_svcs[idx];
|
||||
/* 服务异常退出(非 0 / 被信号杀)且非受控 stop/restart → 上报 meow_crash。
|
||||
* 必须在 apply_exit 前判(apply_exit 会清 stop_requested/restart_pending)。 */
|
||||
int failed = !(WIFEXITED(status) && WEXITSTATUS(status) == 0);
|
||||
if (failed && !s->stop_requested && !s->restart_pending)
|
||||
notify_crash(s->name, status);
|
||||
apply_exit(s, status);
|
||||
}
|
||||
build_status_file();
|
||||
}
|
||||
|
||||
Binary file not shown.
Reference in new issue
Block a user