困惑:PHP致命错误:在第0行的未知中抛出没有堆栈帧的异常?

2022-08-30 12:57:44

我发现错误的一个常见原因是从异常处理程序中引发异常。我很确定这不会发生在我试图调试的应用程序中...但是我已经把所有的初始化处理行放在索引的顶部.php尝试/捕获。

显然,这种情况也可能发生,因为某些内容无法序列化以存储在会话中。这个应用程序最多将数组存储到会话中(相当多),但我相信它不会在其中存储任何不寻常的东西。

有人评论说,这发生在他们身上,因为他们的主键需要是CHAR(32)而不是INT(11)。。此应用程序中的PK都是INT。

其他建议是,这可能是 PHP 5.3.3 在 5.3.6 中修复的问题,即已满磁盘,并且需要对 SimpleXML 值进行类型转换。我们确实碰巧运行PHP 5.3.3,但在这种情况下,升级必须是最后的手段。它并不总是这样做。

更新/注意:我实际上无法自己重现错误,只能在日志中看到它发生,请参阅下面的段落,了解我认为错误发生的位置...

*从错误日志中,似乎至少有一个地方正在发生它是索引.php。我推断这一点只是因为它在某些条目中由引用URL指示。try/catch 代码目前仅位于脚本的“top”初始化部分附近,下面主要是 HTML 输出。输出中有一些PHP代码(虽然非常简单),所以我可能需要测试它。下面是 catch 部分,它不会在日志中生成任何输出:

} catch (Exception $e) {
    error_log(get_class($e)." thrown. Message: ".$e->getMessage(). "  in " . $e->getFile() . " on line ".$e->getLine());
    error_log('Exception trace stack: ' . print_r($e->getTrace(),1));
}

真的非常感谢任何提示!

编辑:PHP作为Apache模块运行(Server API:Apache 2.0 Handler)。我不认为有任何PHP加速器在使用中,但可能只是我不知道该怎么说。维基百科上列出的没有一个在phpinfo()中。

据我所知,MPM是prefork。这是我第一次研究MPM:

# ./httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c

答案 1

问题

简而言之,您在某个地方抛出了一个异常,您不知道在哪里,直到现在您都无法重现错误:它只发生在某些人身上,但不发生在您身上。您知道这种情况发生在其他人身上,因为您在错误日志中看到了这一点。

重现问题

由于您已经消除了常见原因,因此需要重现错误。如果您知道哪个参数会导致错误,则应该很容易找到错误。

  • 如果您知道所有POST / GET参数,很可能就足够了。
  • 如果仅使用这些内容无法重现,则需要知道其他请求标头。如用户代理、接受编码,...
  • 如果您仍然无法重现,那么它将变得非常困难:错误可能取决于状态(会话),当前时间,源IP地址等。

自定义日志方法

让我们从简单开始:要获取所有参数,您可以在受影响的php文件的开头写入如下内容:

file_put_contents("/path/to/some/custom_error_log", date()."\n".print_r(get_defined_vars(), true), FILE_APPEND | LOCK_EX);

不要忘记,custom_error_log文件必须对您的php应用程序是可写的。然后,当错误日志中发生错误时,请在custom_error_log文件中找到相应的行。希望每秒的请求不多,以便您仍然可以识别请求。也许错误日志中的一些附加参数(如源 ip)可以帮助您识别请求(如果您的错误日志显示)。根据该数据,使用相同的 POST/GET 参数重建请求。

tcpdump 方法

下一个选项也非常简单,但要求您在目标计算机上具有root访问权限,那就是安装tcpflow。然后创建一个文件夹,cd到该文件夹中,然后简单地执行(以root身份)。该选项(端口 80)是 pcap 筛选器表达式。要查看您可以执行的所有操作,请参阅。这些筛选器表达式可以执行很多操作。tcpflow "port 80"man pcap-filter

现在,tcpflow将记录端口80上的所有tcp连接,通过组合属于一个连接的包来重建完整的数据交换,并将此数据转储到一个文件中,为每个连接创建两个新文件,一个用于传入数据,另一个用于传出数据。现在,再次根据错误日志中的时间戳和文件的上次修改时间戳查找导致错误的连接的文件。然后,您将获得完整的 http 请求标头。您现在可以完全重建HTTP请求,包括设置相同的接受编码,用户代理等。您甚至可以将请求直接通过管道传输到 netcat 中,重播确切的请求。但要注意,像会话ID这样的一些参数可能会妨碍你。如果php发现会话已过期,您可能只会获得指向登录名或其他意外内容的重定向。您可能需要交换会话 ID 之类的内容。

嘲笑更多事物

如果这些都无济于事,并且您无法在计算机上重现错误,那么您可以尝试模拟所有难以模拟的内容。例如,源 IP 地址。这可能会使一些特技变得必要,但这是可能的:您可以使用带有“-w”选项的ssh连接到服务器,从而创建隧道接口。然后将有问题的IP地址分配给您自己的计算机,并设置路由(路由添加主机)规则以使用特定IP的隧道。如果您可以将两台计算机直接连接在一起,那么您甚至可以在没有隧道的情况下做到这一点。

不要嘲笑应该最精彩的会议。您可以使用带有print_r(get_defined_vars()))的方法读取所有会话变量。然后,您需要创建一个具有完全相同变量的会话。

询问用户

另一种选择是实际上询问用户他在做什么。也许你可以按照和他一样的步骤,可以繁殖。

如果这些都无济于事

如果这些都无济于事...井。。。然后它变得非常困难。IP的事情已经极不可能了。它可能是一个GEO-IP库,导致来自特定区域的IP出错,但这些都是不太可能的事情。如果上述方法都不能帮助您重现问题,那么您可能只是在custom_log_file-call / tcpflow生成的所有数据中找不到正确的请求。尝试通过获取更准确的时间戳来增加机会。你可以在 php 中使用 microtime() 作为 date() 的替代品。检查您的Web服务器,是否可以在错误日志中获得比秒更准确的内容。编写您自己的“tail”实现,这将为您提供更准确的时间戳,...减少系统上的负载,这样您就不必从那么多数据中进行选择(尝试一天中的另一个时间,将用户加载到不同的服务器,...)

一旦可以重现,就可以圈出问题

现在,一旦你可以重现,它应该是在公园里散步,找到真正的原因。您可以通过反复试验或将其与导致错误的其他请求进行比较来查找导致错误的参数,以查找相似之处。然后你可以看到这个参数做什么,哪些库访问它,等等。您可以逐个禁用使用该参数的每个组件,直到您无法再重现为止。然后你得到了你的组件,可以更深入地研究这个问题。

告诉我们您的发现。我很好奇;-)。


答案 2

我也有这样的错误。发现我在会话类中返回了一个 sql 对象(该对象由session_handler使用),而不是不返回任何内容或至少不返回 sql 对象。首先看看你的_write和_read方法,如果你也返回一些不正确的东西。

通知:。。。第 0 行未知 - 如何找到正确的行,它不是“行 0”


推荐