当前位置: 代码网 > it编程>App开发>苹果IOS > SIGPIPE(Signal 13, Code 0) 异常排查及处理

SIGPIPE(Signal 13, Code 0) 异常排查及处理

2024年05月18日 苹果IOS 我要评论
问题现象最近一个版本 app 更新之后,sentry 大量异常数据上报,影响用户的数量非常夸张 10w +,具体报错如下排查过程首先查看 sigpipe 的报错原因, 在官网搜索到了相关信息大意是 s

问题现象

最近一个版本 app 更新之后,sentry 大量异常数据上报,影响用户的数量非常夸张 10w +,具体报错如下

排查过程

首先查看 sigpipe 的报错原因, 在官网搜索到了相关信息

大意是 socket 连接关闭后,如果客户端仍在发送数据,这个时候就会产生 sigpipe 信号,如果信号没有被处理就会产生崩溃,这里截取了部分关键信息。

文档上说可以使用 signal(sigpipe, sig_ign) 全局忽略,确认客户端添加了该逻辑,但是异常还是上报到了 sentrysignal 这个函数是给信号关联一个 handler,收到这个信号的时候去执行。 sig_ign 是系统提供的忽略信号的处理方式,定义如下:

#define sig_ign         (void (*)(int))1

尝试手动触发 sigpipe, 运行后可以正常输出。

void signalhandler(int signal) {
    printf("bingo");
}
int main(int argc, char * argv[]) {
    signal(sigpipe, signalhandler);
    kill(getpid(), sigpipe);
}

多次添加 handler 继续尝试, 控制台输出 333, 也就是说只有最后添加的 handler 会执行到,比较容易理解一个信号只能关联一个 handler

void signalhandler(int signal) {
    printf("111");
}
void signalhandler2(int signal) {
    printf("222");
}
void signalhandler3(int signal) {
    printf("333");
}
int main(int argc, char * argv[]) {
    signal(sigpipe, signalhandler);
    signal(sigpipe, signalhandler2);
    signal(sigpipe, signalhandler3);
    kill(getpid(), sigpipe);
}

现状是 sentry 可以捕获并处理这个异常,所以此时怀疑是 sentry 把客户端的处理给覆盖了。

查看 sentry 里面的逻辑,sentry 使用了 sigaction 函数关联 handler,这个函数与 signal 函数一样,可以设置与信号 sig 关联的动作,而 oact 如果不是空指针的话,就用它来保存原先对该信号的动作的位置,act 则用于设置指定信号的动作。sentry 关联了自己的处理 handlesignal 并且会把之前的handler 存储到数组 g_previoussignalhandlers 里面。

int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
// sentry 关联的 action 为 handlesignal
sigaction(fatalsignals[i], &action, &g_previoussignalhandlers[i])

sentryhandlesignal 里面上报异常并且执行了了 sentrycrashcm_handleexception,然后使用 raise 重新抛出这个信号。

static void handlesignal(int signum, siginfo_t *signalinfo, void *usercontext)
{
    sentrycrashlog_debug("trapped signal %d", signum);
    if (g_isenabled) {
        // 这里省略上报逻辑
        sentrycrashcm_handleexception();
    }
    sentrycrashlog_debug("re-raising signal for regular handlers to catch.");
    // this is technically not allowed, but it works in osx and ios.
    raise(signum);
}

查看 handleexception 简化后的调用栈:

void sentrycrashcm_handleexception(**struct** sentrycrash_monitorcontext *context)
{
    sentrycrashcm_setactivemonitors(sentrycrashmonitortypenone);
}
void sentrycrashcm_setactivemonitors(sentrycrashmonitortype monitortypes)
{
    // isenabled = false
    setmonitorenabled(monitor, isenabled);
}
static inline void setmonitorenabled(monitor *monitor, bool isenabled) {
    uninstallsignalhandler();
}
static void uninstallsignalhandler(void) {
    sigaction(fatalsignals[i], &g_previoussignalhandlers[i], **null**);
}

可以看到 handleexception 这个函数最终会重新关联保存在 g_previoussignalhandlers里面的 handler,也就是客户端设置的 sig_ign 默认忽略。sentry 关联的函数 handlesignal 会在处理完会重新抛出信号,这个信号会触发 sig_ign,所以这里并不存在覆盖关系,sentry 不会影响到客户端默认忽略的逻辑。

综上客户端设置的 sig_ign 是会生效的,sentry 只是上报了异常,并没有崩溃产生。在 app 里面手动触发 sigpipecharles 抓包可以看到 sentry 上报,app 未出现崩溃。

原因与处理

和多个业务方确认这个版本并没有 socket 相关的改动,那为什么在这个版本之后突然有大量异常上报呢?

后面 diff 代码发现是改动了 sentry 的初始时机造成的。之前的逻辑是 sentry 初始化,客户端调用 signal 关联 sig_ign,这个时候 sig_ign 覆盖了 sentrysignalhandler,并且没有保存和恢复之前 handler 的逻辑,sentry 捕获不到信号不会上报,当前版本的改动使这个顺序颠倒了,导致了大量异常数据上报。后续尝试去定位具体的 socket 无果,重新修改了顺序 sig_ignsentry 初始化之后关联,之后的版本不再有异常数据上报。

以上就是sigpipe(signal 13, code 0) 异常排查及处理的详细内容,更多关于sigpipe异常排查的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com