Files
MeowOS-For-R3P2/patch_v2/meow_svc_perf/meow_svc_perf.c
T
MeowOS 23727cf400 feat(system): 系统框架一期 — 专项服务(性能/电源)+IPC+电源CLI+电菜单改经服务(agent-155)
- meow_svc_power: 单一二进制(svcd空参IPC常驻 /usr/data/meow_power.sock + root CLI免鉴权), 12命令(shutdown/reboot强制=0/upgrade/set-default-hiby/reboot--hiby + set-default-meow/status/screen/dac(status仅)/battery/reboot-reason/upgrade-status); 动作同源sys_do_reboot/set_boot_default_hiby/mtd5=ota:kernel2; dac on/off无控制点不支持
- meow_svc_perf: IPC(/usr/data/meow_perf.sock MSG_PERF_*) + CLI, 聚合/proc/df/thermal/version/uname JSON(与MEOWPANEL_PROTOCOL对齐)
- power_menu.c 改经 meow_power_action(launcher先转储再IPC), 删sys_do_reboot/set_boot_default_hiby; 4动作+8s强关+confirm_reboot_player+3处长按强关全经服务; launcher内 sys_do_reboot/bootmode.sh/set_boot_default_hiby 0调用
- meow_ipc.h 仅加 MSG_PERF_QUERY/RESULT(0x50/51)+MSG_POWER_ACTION/STATUS(0x60/61)+POWER_*(12)+m_perf/power_t; 未动既有
- S89meow_svcd 预置 meow_svc_perf/power.conf(trigger=boot); 各独立socket
- IPC鉴权一期=SO_PEERCRED uid==0基础(完整os.meow.*二期); 通知转储=launcher先转储再IPC(二期补launcher→服务通道); 失败安全(服务不可达→按取消,不静默不直连)
- meow_svc_power=32d6be92/meow_svc_perf=7b0f3b64/launcher=d90f09e9/svcd=fb2d608b(未动)/meow_ui.a=92e61fb8(未改); 全0warning; 备份power_menu.c.pre-svc.bak回退
2026-08-30 23:16:12 +08:00

524 lines
19 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#define _GNU_SOURCE /* gethostname 等 GNU/musl 扩展 */
/* meow_svc_perf.c — MeowOS 性能分析专项子服务(SYSTEM_FRAMEWORK_SERVICES §1.2/§2.2)。
*
* 单一二进制双形态:
* - svcd 以**空参**拉起(§1.3 conf `args=` 空)→ IPC 服务常驻:bind `/usr/data/meow_perf.sock`
* (`--sock`/`MEOW_IPC_PERF_SOCK` 可覆盖),收 MSG_HELLO→MSG_ACK、MSG_PERF_QUERY→MSG_PERF_RESULT。
* - root shell `meow_svc_perf [query]` → 一次性 CLI(免鉴权)打印聚合 JSON(不常驻)。
*
* 聚合的数据源与 launcher `about.c`/`util.c`/bootmenu**同源**(/proc/stat、/proc/meminfo、
* /proc/uptime、/proc/loadavg、/sys/class/thermal 各 zone、df/mount、/proc/cpuinfo、/etc/meowos_version、
* gethostname/uname),聚合成与 MEOWPANEL_PROTOCOL §3.1 `/api/status`/.data 对齐的 JSON——
* 单一权威源,MeowPanel 后端/launcher 不再各自爬 /proc。
*
* MSG_PERF_QUERY.subset 位掩码(m_perf_query_t):0=全部;PERF_SUBSET_PERF / PERF_SUBSET_SYSTEM。
* 鉴权(一期):性能查询**只读安全**,本批不强制 os.meow.*;完整读级鉴权见 §5.3(二期)。
*
* 编译(mipsel 静态):build_svc_perf.sh;宿主编译 build_svc_perf.sh host。
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <signal.h>
#include <poll.h>
#include <time.h>
#include <ctype.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/utsname.h>
#include "meow_ipc.h" /* 帧格式/消息号 + m_perf_query_t/m_perf_result_t(与 launcher 同源) */
#define VERSION_FILE "/etc/meowos_version"
#define TF_MOUNT "/usr/data/mnt/sd_0"
#define MEOW_PIPE_MAX 4096
#define MEOW_TIMEOUT_MS 1500
static const char *g_sock = "/usr/data/meow_perf.sock";
static void svc_log(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "[meow_svc_perf] ");
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
/* ---------------- 有界 JSON 追加 ---------------- */
typedef struct { char *b; size_t n; size_t cap; } sbuf_t;
static void sA(sbuf_t *s, const char *fmt, ...)
{
if (s->n >= s->cap - 1) return;
va_list ap;
va_start(ap, fmt);
int w = vsnprintf(s->b + s->n, s->cap - s->n, fmt, ap);
va_end(ap);
if (w < 0) return;
if ((size_t)w >= s->cap - s->n) s->n = s->cap - 1;
else s->n += (size_t)w;
}
/* JSON 字符串转义(version/hostname 等;ASCII 常见字符直通,引号/反斜杠转义) */
static void sA_str(sbuf_t *s, const char *v)
{
if (!v) v = "";
sA(s, "\"");
for (const char *p = v; *p; p++) {
unsigned char c = (unsigned char)*p;
if (c == '"' || c == '\\') { sA(s, "\\%c", (int)c); }
else if (c < 0x20) { sA(s, "\\u%04x", c); }
else sA(s, "%c", (int)c); /* buf 0x80.. 按字节透传(utf-8) */
}
sA(s, "\"");
}
/* ---------------- 限时读整文件 ---------------- */
static int read_file_txt(const char *path, char *buf, int cap)
{
int fd = open(path, O_RDONLY);
if (fd < 0) return -1;
int n = 0;
while (n < cap - 1) {
ssize_t r = read(fd, buf + n, (size_t)(cap - 1 - n));
if (r <= 0) break;
n += (int)r;
}
close(fd);
buf[n] = 0;
while (n > 0 && (buf[n - 1] == '\n' || buf[n - 1] == '\r')) buf[--n] = 0;
return n;
}
/* ---------------- popen 限时读(kill 子进程防挂) ---------------- */
static char *popen_read_tmo(int tmo_ms, const char *cmd)
{
int p[2];
if (pipe(p) < 0) return NULL;
pid_t pid = fork();
if (pid < 0) { close(p[0]); close(p[1]); return NULL; }
if (pid == 0) { dup2(p[1], 1); close(p[0]); close(p[1]); execl("/bin/sh", "sh", "-c", cmd, (char *)0); _exit(127); }
close(p[1]);
fcntl(p[0], F_SETFL, fcntl(p[0], F_GETFL, 0) | O_NONBLOCK);
char *buf = malloc(MEOW_PIPE_MAX);
if (!buf) { close(p[0]); waitpid(pid, NULL, 0); return NULL; }
size_t off = 0;
int remain = tmo_ms;
while (remain > 0 && off < MEOW_PIPE_MAX - 1) {
struct pollfd pf = { p[0], POLLIN, 0 };
int r = poll(&pf, 1, remain);
if (r < 0) { if (errno == EINTR) continue; break; }
if (r == 0) break;
ssize_t rd = read(p[0], buf + off, MEOW_PIPE_MAX - 1 - off);
if (rd == 0) break;
if (rd < 0) { if (errno == EAGAIN || errno == EWOULDBLOCK) continue; break; }
off += (size_t)rd;
}
close(p[0]);
waitpid(pid, NULL, 0);
buf[off] = 0;
return buf;
}
/* ---------------- CPU ---------------- */
/* /proc/stat 首行 "cpu user nice system idle iowait irq softirq steal ..."。 */
static int g_cpu_prev[10];
static int g_cpu_prev_init = 0;
static double cpu_usage_pct(void)
{
FILE *f = fopen("/proc/stat", "r");
char line[256];
if (!f || !fgets(line, sizeof line, f)) { if (f) fclose(f); return -1; }
fclose(f);
unsigned long long vals[10];
char *p = line + 3; /* skip "cpu" */
int n = 0;
while (n < 10) {
while (*p == ' ') p++;
if (!*p || !isdigit((unsigned char)*p)) break;
vals[n++] = strtoull(p, &p, 10);
}
if (n < 4) return -1;
unsigned long long total = 0, idle = 0;
for (int i = 0; i < n; i++) total += vals[i];
idle = vals[3] + (n > 4 ? vals[4] : 0);
if (!g_cpu_prev_init) {
for (int i = 0; i < 10; i++) g_cpu_prev[i] = (int)vals[i];
g_cpu_prev_init = 1;
return 0;
}
unsigned long long dtotal = total, didle = idle;
for (int i = 0; i < n; i++) if (i < 10) dtotal -= (unsigned long long)g_cpu_prev[i];
if (n > 4) didle -= (unsigned long long)g_cpu_prev[3] + (unsigned long long)g_cpu_prev[4];
else didle -= (unsigned long long)g_cpu_prev[3];
for (int i = 0; i < 10; i++) g_cpu_prev[i] = (int)vals[i];
if (!dtotal) return 0;
double u = 100.0 * (double)(dtotal - didle) / (double)dtotal;
if (u < 0) u = 0;
return u;
}
static int core_count(void)
{
FILE *f = fopen("/proc/stat", "r");
char line[256];
int n = 0;
if (!f) return 1;
while (fgets(line, sizeof line, f)) {
if (line[0] == 'c' && line[1] == 'p' && line[2] == 'u' && isdigit((unsigned char)line[3]))
n++;
if (line[0] != 'c') break;
}
fclose(f);
return n > 0 ? n : 1;
}
static void loadavg(double *l1, double *l5, double *l15)
{
FILE *f = fopen("/proc/loadavg", "r");
*l1 = *l5 = *l15 = 0;
if (f) { if (fscanf(f, "%lf %lf %lf", l1, l5, l15) != 3) { *l1 = *l5 = *l15 = 0; } fclose(f); }
}
/* ---------------- 内存 ---------------- */
static void meminfo(unsigned long *total, unsigned long *used, unsigned long *free_kb,
unsigned long *avail, unsigned long *stotal, unsigned long *sused)
{
*total = *used = *free_kb = *avail = *stotal = *sused = 0;
FILE *f = fopen("/proc/meminfo", "r");
if (!f) return;
char k[64], line[256];
unsigned long mtot = 0, mfree = 0, mavail = 0, swapT = 0, swapF = 0;
while (fgets(line, sizeof line, f)) {
unsigned long v;
if (sscanf(line, "%63s %lu", k, &v) != 2) continue;
if (!strcmp(k, "MemTotal:")) mtot = v;
else if (!strcmp(k, "MemFree:")) mfree = v;
else if (!strcmp(k, "MemAvailable:")) mavail = v;
else if (!strcmp(k, "SwapTotal:")) swapT = v;
else if (!strcmp(k, "SwapFree:")) swapF = v;
}
fclose(f);
*total = mtot;
*free_kb = mfree;
if (mavail > 0) { *avail = mavail; *used = (mtot >= mavail) ? mtot - mavail : 0; }
else { *avail = mfree; *used = (mtot >= mfree) ? mtot - mfree : 0; }
*stotal = swapT;
*sused = (swapT >= swapF) ? swapT - swapF : 0;
}
static double uptime_sec(void)
{
FILE *f = fopen("/proc/uptime", "r");
double up = 0;
if (f) { if (fscanf(f, "%lf", &up) != 1) up = 0; fclose(f); }
return up;
}
/* ---------------- 温度(/sys/class/thermal/thermal_zone*;type 匹配 cpu→cpu,其余→board) ---------------- */
static void temp_json(sbuf_t *s, int *any)
{
DIR *d = opendir("/sys/class/thermal");
*any = 0;
if (!d) { sA(s, "{}"); return; }
double cpu = -1, board = -1;
struct dirent *e;
while ((e = readdir(d))) {
if (strncmp(e->d_name, "thermal_zone", 12) != 0) continue;
char path[300], typ[64] = "", tmp[32] = "";
snprintf(path, sizeof path, "/sys/class/thermal/%s/type", e->d_name);
read_file_txt(path, typ, sizeof typ);
snprintf(path, sizeof path, "/sys/class/thermal/%s/temp", e->d_name);
read_file_txt(path, tmp, sizeof tmp);
long millideg = atol(tmp);
if (millideg <= 0) continue;
double c = (double)millideg / 1000.0;
if (strstr(typ, "cpu") || strstr(typ, "CPU")) { if (cpu < 0) cpu = c; }
else if (board < 0) board = c;
}
closedir(d);
if (cpu < 0 && board < 0) { sA(s, "{}"); return; }
sA(s, "{");
int first = 1;
if (cpu >= 0) { sA(s, "\"cpu\":%.1f", cpu); first = 0; }
if (board >= 0) { if (!first) sA(s, ","); sA(s, "\"board\":%.1f", board); }
sA(s, "}");
*any = 1;
}
/* ---------------- 存储(df -k <path> 第 2 行:右 5 字段 = fs,total,used,avail,use%,mount) ---------------- */
static int df_parse(const char *path, unsigned long *total, unsigned long *used, unsigned long *avail)
{
*total = *used = *avail = 0;
char cmd[160];
snprintf(cmd, sizeof cmd, "PATH=/bin:/sbin:/usr/bin:/usr/sbin df -k %s 2>/dev/null", path);
char *o = popen_read_tmo(MEOW_TIMEOUT_MS, cmd);
if (!o) return -1;
char *ln = strchr(o, '\n');
if (ln) ln++;
char *save = NULL;
char *toks[8]; int nt = 0;
for (char *tk = strtok_r(ln ? ln : o, " \t", &save); tk && nt < 8; tk = strtok_r(NULL, " \t", &save))
toks[nt++] = tk;
if (nt >= 5) { *total = strtoul(toks[nt - 5], NULL, 10); *used = strtoul(toks[nt - 4], NULL, 10); *avail = strtoul(toks[nt - 3], NULL, 10); }
free(o);
return (*total || *avail) ? 0 : -1;
}
static int tf_present(void)
{
FILE *f = fopen("/proc/mounts", "r");
if (!f) return 0;
char line[512];
int present = 0;
while (fgets(line, sizeof line, f)) {
if (strstr(line, TF_MOUNT)) { present = 1; break; }
}
fclose(f);
return present;
}
/* ---------------- 系统静态(version/hostname/kernel/arch) ---------------- */
static void sys_arch(char *out, int cap)
{
struct utsname u;
const char *m = "mipsel";
if (uname(&u) == 0) m = u.machine;
if (strstr(m, "mips")) m = "mipsel"; /* mipsel-linux-muslsf 目标 */
snprintf(out, cap, "%.31s", m);
}
static void sys_kernel(char *out, int cap)
{
struct utsname u;
size_t rl = 0;
if (uname(&u) != 0) { snprintf(out, cap, "-"); return; }
rl = strnlen(u.release, 64);
if (rl > (size_t)(cap - 1)) rl = (size_t)(cap - 1);
memcpy(out, u.release, rl);
out[rl] = 0;
}
/* ---------------- 聚合 JSON(subset 位掩码;与 MEOWPANEL_PROTOCOL §3.1 .data 对齐) ---------------- */
static int agg_json(int subset, char *out, int cap)
{
int ip = (subset == 0) || (subset & PERF_SUBSET_PERF);
int isys = (subset == 0) || (subset & PERF_SUBSET_SYSTEM);
sbuf_t s = { out, 0, (size_t)cap };
sA(&s, "{");
int first = 1;
#define SEP() do { if (!first) sA(&s, ","); first = 0; } while (0)
if (ip) {
SEP(); sA(&s, "\"ts\":%ld", (long)time(NULL));
SEP(); sA(&s, "\"uptime_sec\":%.0f", uptime_sec());
double u = cpu_usage_pct(); int cores = core_count();
double l1, l5, l15; loadavg(&l1, &l5, &l15);
SEP(); sA(&s, "\"cpu\":{\"usage\":%.1f,\"cores\":%d,\"load\":[%.2f,%.2f,%.2f]}", u, cores, l1, l5, l15);
unsigned long mtot, mused, mfree, mavail, swapT, swapU;
meminfo(&mtot, &mused, &mfree, &mavail, &swapT, &swapU);
SEP(); sA(&s, "\"memory\":{\"total\":%lu,\"used\":%lu,\"free\":%lu,\"available\":%lu,\"swap_total\":%lu,\"swap_used\":%lu}",
mtot, mused, mfree, mavail, swapT, swapU);
unsigned long st, su, sa;
if (df_parse("/", &st, &su, &sa) == 0) {
SEP();
sA(&s, "\"storage\":{\"path\":\"/\",\"total\":%lu,\"used\":%lu,\"avail\":%lu}", st, su, sa);
} else {
SEP();
sA(&s, "\"storage\":{\"path\":\"/\",\"total\":0,\"used\":0,\"avail\":0}");
}
if (tf_present()) {
unsigned long tt, tu, ta;
if (df_parse(TF_MOUNT, &tt, &tu, &ta) == 0) {
SEP();
sA(&s, "\"tfcard\":{\"present\":true,\"path\":\"" TF_MOUNT "\",\"total\":%lu,\"used\":%lu,\"avail\":%lu,\"filesystem\":\"exfat\"}", tt, tu, ta);
} else {
SEP();
sA(&s, "\"tfcard\":{\"present\":true,\"path\":\"" TF_MOUNT "\",\"total\":0,\"used\":0,\"avail\":0,\"filesystem\":\"exfat\"}");
}
} else {
SEP();
sA(&s, "\"tfcard\":{\"present\":false,\"path\":\"" TF_MOUNT "\",\"total\":0,\"used\":0,\"avail\":0,\"filesystem\":null}");
}
int any;
SEP(); sA(&s, "\"temp\":"); temp_json(&s, &any);
}
if (isys) {
char ver[64] = "-", host[64] = "-", ker[64] = "-", arch[32] = "-";
if (read_file_txt(VERSION_FILE, ver, sizeof ver) <= 0 || !ver[0]) snprintf(ver, sizeof ver, "-");
if (gethostname(host, sizeof host) != 0 || !host[0]) snprintf(host, sizeof host, "-");
sys_kernel(ker, sizeof ker);
sys_arch(arch, sizeof arch);
SEP(); sA(&s, "\"system\":{\"version\":");
sA_str(&s, ver);
sA(&s, ",\"hostname\":");
sA_str(&s, host);
sA(&s, ",\"kernel\":"); sA_str(&s, ker);
sA(&s, ",\"arch\":"); sA_str(&s, arch);
sA(&s, "}");
}
sA(&s, "}"); /* 外层闭合 */
#undef SEP
if (s.n >= (size_t)cap) s.n = (size_t)cap - 1;
s.b[s.n] = 0;
return 0;
}
/* ---------------- IPC 帧读写 ---------------- */
static int x_read_full(int fd, uint8_t *buf, size_t n)
{
size_t off = 0;
while (off < n) { ssize_t r = read(fd, buf + off, n - off); if (r <= 0) return -1; off += (size_t)r; }
return 0;
}
static int x_write_full(int fd, const uint8_t *buf, size_t n)
{
size_t off = 0;
while (off < n) { ssize_t w = write(fd, buf + off, n - off); if (w <= 0) return -1; off += (size_t)w; }
return 0;
}
static int send_frame(int fd, uint16_t type, uint32_t req_id, const void *payload, uint32_t len)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC, rlen = len;
uint16_t ver = MEOW_IPC_VERSION, t = type;
memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2);
memcpy(hdr + 8, &req_id, 4); memcpy(hdr + 12, &rlen, 4);
if (x_write_full(fd, hdr, MEOW_IPC_HDR_LEN) < 0) return -1;
if (len && payload && x_write_full(fd, payload, len) < 0) return -1;
return 0;
}
static int send_ack(int fd, uint32_t req_id, uint32_t code, uint32_t status)
{
m_ack_t a; memset(&a, 0, sizeof a); a.code = code; a.status = status;
return send_frame(fd, MSG_ACK, req_id, &a, sizeof a);
}
static int send_err(int fd, uint32_t req_id, uint32_t code, const char *msg)
{
m_err_t e; memset(&e, 0, sizeof e);
uint16_t ml = msg ? (uint16_t)strlen(msg) : 0;
if (ml > 63) ml = 63;
e.code = code; e.msg_len = ml;
if (ml) memcpy(e.msg, msg, ml);
return send_frame(fd, MSG_ERR, req_id, &e, sizeof e);
}
static int send_perf_result(int fd, uint32_t req_id, int subset, const char *json)
{
m_perf_result_t r;
memset(&r, 0, sizeof r);
r.code = MEOW_OK;
r.subset = (uint32_t)subset;
uint16_t jl = (uint16_t)strlen(json);
if (jl > sizeof r.json - 1) jl = (uint16_t)(sizeof r.json - 1);
memcpy(r.json, json, jl);
r.json[jl] = 0;
r.json_len = jl;
return send_frame(fd, MSG_PERF_RESULT, req_id, &r, sizeof r);
}
/* MSG_PERF_QUERY:读取 subset 后聚合。0=全部。 */
static void handle_query(int c, uint32_t req_id, const uint8_t *payload, uint32_t rlen)
{
m_perf_query_t q;
memset(&q, 0, sizeof q);
if (rlen > sizeof q) rlen = sizeof q;
memcpy(&q, payload, rlen);
int subset = (q.subset == 0) ? 0 : (int)q.subset;
char json[MEOW_IPC_MAX - 12];
agg_json(subset, json, sizeof json);
send_perf_result(c, req_id, subset, json);
}
static void handle_conn(int c)
{
for (;;) {
uint8_t hdr[MEOW_IPC_HDR_LEN];
if (x_read_full(c, hdr, sizeof hdr) < 0) break;
uint32_t magic, rlen, req_id; uint16_t ver, type;
memcpy(&magic, hdr, 4); memcpy(&ver, hdr + 4, 2); memcpy(&type, hdr + 6, 2);
memcpy(&req_id, hdr + 8, 4); memcpy(&rlen, hdr + 12, 4);
if (magic != MEOW_IPC_MAGIC || ver != MEOW_IPC_VERSION) { send_err(c, req_id, MEOW_ERR_INVAL, "bad magic/ver"); break; }
if (rlen > MEOW_IPC_MAX) { send_err(c, req_id, MEOW_ERR_INVAL, "payload too big"); break; }
uint8_t payload[MEOW_IPC_MAX];
if (rlen && x_read_full(c, payload, rlen) < 0) break;
if (type == MSG_HELLO) { send_ack(c, req_id, MEOW_OK, 0); continue; }
if (type == MSG_PERF_QUERY) { handle_query(c, req_id, payload, rlen); continue; }
send_err(c, req_id, MEOW_ERR_INVAL, "unknown type");
break;
}
}
static int run_daemon(const char *sock)
{
signal(SIGPIPE, SIG_IGN);
cpu_usage_pct(); /* 预热:设 /proc/stat 基线,首次查询即有值 */
int lfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (lfd < 0) { svc_log("socket fail: %s", strerror(errno)); return 1; }
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, sock, sizeof a.sun_path - 1);
unlink(sock);
if (bind(lfd, (struct sockaddr *)&a, sizeof a) < 0) { svc_log("bind %s fail: %s", sock, strerror(errno)); close(lfd); return 1; }
chmod(sock, 0666);
if (listen(lfd, 8) < 0) { svc_log("listen fail: %s", strerror(errno)); close(lfd); return 1; }
svc_log("meow_svc_perf listening on %s", sock);
for (;;) {
int c = accept(lfd, NULL, NULL);
if (c < 0) { if (errno == EINTR) continue; continue; }
handle_conn(c);
close(c);
}
return 0;
}
/* ---------------- CLI ---------------- */
static int cli_run(int n, char **v)
{
int subset = 0;
if (n >= 1) {
if (!strcmp(v[0], "perf")) subset = PERF_SUBSET_PERF;
else if (!strcmp(v[0], "system")) subset = PERF_SUBSET_SYSTEM;
else if (!strcmp(v[0], "all") || !strcmp(v[0], "-")) subset = 0;
else { fprintf(stderr, "usage: meow_svc_perf [all|perf|system]\n"); return 1; }
}
cpu_usage_pct(); /* 预热基线 */
usleep(100000); /* 采样间隔,取 delta */
char json[4096];
agg_json(subset, json, sizeof json);
puts(json);
return 0;
}
static const char *cli_usage(void)
{
return "meow_svc_perf [all|perf|system] (CLI 免鉴权;无参=全部)";
}
int main(int argc, char **argv)
{
const char *env = getenv("MEOW_IPC_PERF_SOCK");
if (env && env[0]) g_sock = env;
int i = 1;
for (; i < argc; i++) {
if (!strcmp(argv[i], "--sock") && i + 1 < argc) g_sock = argv[++i];
else if (!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")) { puts(cli_usage()); return 0; }
else break;
}
if (i >= argc) return run_daemon(g_sock); /* svcd 空参 → IPC 常驻 */
return cli_run(argc - i, argv + i);
}