You need to enable JavaScript to run this app. 如何排查Haskell程序开启-prof、-xc后无调用栈的hWaitForInput套接字EOF报错来源
优惠活动
大模型
产品
解决方案
定价
更多

如何排查Haskell程序开启-prof、-xc后无调用栈的hWaitForInput套接字EOF报错来源

Socket 输入流EOF 报错相关梳理

报错现象

  • 直接运行编译生成的foo.exe时,终端抛出报错:foo.exe: <socket: 23>: hWaitForInput: end of file
  • 编译时添加-prof参数开启程序分析能力,运行时传入-xc RTS 参数开启异常栈输出后,该类EOF异常不会生成对应的调用栈信息
  • 程序抛出的其他类型异常可以正常输出堆栈跟踪,典型输出格式为*** Exception (reporting due to +RTS -xc): (THUNK), stack trace:

排查初步进展

  • 初步判断报错来源于TCP协议栈或相关依赖模块,但逐行排查依赖代码的工作量较大
  • 根据hWaitForInput的官方文档定义:该操作检测到输入流到达文件末尾时,会抛出isEOFError类型的IO错误,对应错误判定函数的签名为isEOFError :: IOError -> Bool
  • 按照设计逻辑,该类错误属于可捕获、可消解的类型,但实际运行场景中,它仍以未捕获报错的形式直接输出到终端

更新信息

目前收集到一段与该报错疑似关联的堆栈跟踪记录:

*** Exception (reporting due to +RTS -xc): (THUNK), stack trace:
  UnliftIO.Exception.finally.\.\,
  called from UnliftIO.Exception.finally.\,
  called from UnliftIO.Exception.finally,
  called from Control.Concurrent.HierarchyInternal.newChild.\,
  called from Control.Concurrent.HierarchyInternal.newChild,
  called from FDS.Servers.Proto.acceptThreadMapped.safeForkIO,
  called from UnliftIO.Exception.withException.\.\,
  called from UnliftIO.Exception.withException.\,
  called from UnliftIO.Exception.withException,
  called from UnliftIO.Exception.onException,
  called from FDS.Servers.Proto.acceptThreadMapped.\,
  called from FDS.Servers.Proto.acceptThreadMapped,
  called from FDS.Servers.Proto.serveThreadMapped.\,
  called from FDS.Servers.Proto.serveThreadMapped,
  called from FDS.Servers.Proto.serveTBQ.\,
  called from FDS.Servers.Proto.serveTBQ,
  called from FDS.Servers.Proto.protoServe.enQserver,
  called from FDS.Servers.Proto.protoServe,
  called from Control.Monad.Trans.Resource.Internal.>>=.\.(...),
  called from Control.Monad.Trans.Resource.Internal.liftIO,
  called from Control.Monad.Trans.Resource.Internal.>>=.\,
  called from Control.Monad.Trans.Resource.Internal.>>=,
  called from Control.Monad.Trans.Resource.Internal.runResourceT.\.\,
  called from Control.Monad.Trans.Resource.Internal.runResourceT.\,
  called from Control.Monad.Trans.Resource.Internal.runResourceT,
  called from UnliftIO.Exception.bracket.\.\,
  called from UnliftIO.Exception.bracket.\,
  called from UnliftIO.Exception.bracket,
  called from FDS.Data.FarmPCMessage.protoReceiver,
  called from RIO.Prelude.RIO.askUnliftIO.\.\,
  called from RIO.Prelude.RIO.askUnliftIO.\,
  called from RIO.Prelude.RIO.askUnliftIO,
  called from Control.Monad.IO.Unlift.withRunInIO.\,
  called from Control.Monad.IO.Unlift.withRunInIO,
  called from UnliftIO.Exception.catch.\,
  called from UnliftIO.Exception.mask.\.\,
  called from UnliftIO.Exception.mask.\,
  called from Control.Monad.IO.Unlift.withRunInIO,
  called from UnliftIO.Exception.catch,
  called from UnliftIO.Exception.catchAny,
  called from FDS.Config.Core.retryForever,
  called from Main.main
FarmDataServer.exe: <socket: 23>: hWaitForInput: end of file

关联函数实现

当前项目实现的acceptThreadMapped函数代码如下,该函数内部并未直接调用hWaitForInput:

acceptThreadMapped
  :: MonadUnliftIO m
  => ThreadMap -- ^ 用于注册新建线程的线程映射表
  -> NS.Socket -- ^ 已绑定并处于监听状态的套接字
  -> ((NS.Socket, NS.SockAddr) -> IO ())
  -- ^ 接收到新连接后,在独立线程中执行的逻辑,入参为连接套接字与对端地址
  -> m ThreadId
acceptThreadMapped tMap lsock k = liftIO $ mask $ \restore -> do
  (csock, addr) <- restore (NS.accept lsock)
  onException
     (safeForkIO (finally (restore (k (csock, ipv4mapped_to_ipv4 addr)))
                         (TCP.closeSock csock)))
     (TCP.closeSock csock)
  where
    safeForkIO ioAction = newChild tMap (\_ -> ioAction)

内容的提问来源于stack exchange,提问作者bbarker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.11.06 11:54:03