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

gro*_*tar 33 php error-handling exception

我发现错误的一个常见原因是异常处理程序中抛出异常.我很确定在我正在尝试调试的应用程序中不会发生这种情况......但我已将所有初始化处理行放在try.cat中的index.php顶部.*

它显然也可能发生,因为有些东西无法序列化 存储在会话中.这个应用程序最多将数组存储到会话中(相当多),但我确信它不会存储任何与众不同的数据.

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

其他建议是,它可能是在5.3.6中修复的PHP 5.3.3 ,完整磁盘以及需要对SimpleXML值进行类型转换的问题.我们碰巧运行PHP 5.3.3,但在这种情况下升级必须是最后的手段.它并不总是如此.

更新/注意:我实际上无法自己重现错误,只能在日志中看到它,请参阅下面的段落,我相信错误发生在哪里...

*从错误日志中,似乎至少发生了一个地方是index.php.我推断这只是因为它在某些条目中通过引用URL表示.try/catch代码目前仅围绕脚本的"顶部"初始化部分,下面主要是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));
}
Run Code Online (Sandbox Code Playgroud)

非常感谢任何关于此的提示!

编辑:PHP作为Apache模块运行(服务器API:Apache 2.0处理程序).我不认为有任何PHP加速器在使用,但它可能只是我不知道如何分辨.的那些没有在维基百科上列出的是phpinfo()函数.

据我所知,MPM是prefork.这是我第一次看到MPM:

# ./httpd -l
Compiled in modules:
  core.c
  prefork.c
  http_core.c
  mod_so.c
Run Code Online (Sandbox Code Playgroud)

yan*_*kee 18

问题

总之,你有一个异常抛出某个地方,你不知道到现在为止你无法重现错误:它只发生在一些人身上,但不适合你.你知道它发生在其他人身上,因为你在错误日志中看到了这一点.

重现问题

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

  • 如果您知道所有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);
Run Code Online (Sandbox Code Playgroud)

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

tcpdump方法

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

现在,tcpflow将记录端口80上的所有tcp连接,通过组合属于一个连接的包并将此数据转储到文件来重建完整数据交换,每个连接创建两个新文件,一个用于传入数据,一个用于传出数据.现在,再次根据错误日志中的时间戳和文件的上次修改时间戳,找到导致错误的连接的文件.然后,您将获得完整的http请求标头.您现在可以完全重建HTTP请求,包括设置相同的accept-encoding,user-agent等.您甚至可以将请求直接传递到netcat,重放确切的请求.请注意,像sessionid这样的参数可能会妨碍你.如果php发现会话过期,您可能只是重定向到登录或其他意外的事情.您可能需要交换会话ID等内容.

嘲笑更多的东西

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

不要嘲笑应该是最神圣的会话.您可以使用print_r(get_defined_vars())方法读取所有会话变量.然后,您需要创建具有完全相同变量的会话.

询问用户

另一种选择实际上是询问用户他在做什么.也许你可以按照与他相同的步骤进行复制.

如果这些都没有帮助

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

一旦你可以重现,循环问题

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

告诉我们你找到了什么.我好奇 ;-).