35

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

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

有人评论说这发生在他们身上,因为他们的主键需要是 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 (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 模块运行(服务器 API:Apache 2.0 处理程序)。我认为没有使用任何 PHP 加速器,但可能只是我不知道如何判断。维基百科上列出的没有一个在 phpinfo() 中。

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

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

12 回答 12

19

问题

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

重现问题

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

  • 如果您知道所有 POST/GET 参数,很可能就足够了。
  • 如果您不能仅使用这些进行复制,则需要了解其他请求标头。比如用户代理,accept-encoding,...
  • 如果还是不能复现,那就很困难了:错误可能取决于状态(会话)、当前时间、源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 进入该文件夹并简单地执行 (as root) tcpflow "port 80"。选项(端口 80)是一个 pcap 过滤器表达式。要查看您可以使用它执行的所有操作,请参阅man pcap-filter。这些过滤器表达式可以做很多事情。

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

嘲笑更多的东西

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

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

询问用户

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

如果这些都没有帮助

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

一旦您可以重现问题,请圈出问题

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

告诉我们你发现了什么。我好奇 ;-)。

于 2012-05-24T22:17:31.893 回答
4

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

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

于 2012-05-07T16:17:33.393 回答
3

当您注册异常处理程序时会发生什么,而不是将代码包装在 try/catch 块中?显然,您的 try/catch 块没有捕获异常,从而导致将错误记录到 Apache。通过注册处理程序,您可以确保处理任何未捕获的异常。

此外,如果您在应用程序中使用命名空间,请确保在 catch 块中写入 \Exception(或通过 use 语句包含 Exception 类)。

于 2012-05-18T10:20:24.700 回答
3

我意识到这个问题已经得到解答,但我会添加这个,因为它可能对某人有帮助:

我设法(无意中)在没有堆栈帧的情况下从一个函数产生错误,该函数使用自己的错误处理程序来保持对执行的控制,同时调用一个潜在的“危险”函数,如下所示:

// Assume the function my_error_handler() has been defined to convert any
// PHP Errors, Warnings, or Notices into Exceptions.

function foo() {
    // maintain control if danger() crashes outright:
    set_error_handler('my_error_handler');

    try {
        // Do some stuff.

        $r = danger();
    } catch (Exception $e) {
        $r = 'Bad Stuff, Man!';
    }

    restore error_handler();
    return $r;
}

如果“做一些事情”中的逻辑直接从 foo() 返回,绕过对 restore_error_handler() 的调用,则“无法追踪的失败”将在程序执行结束时发生。我从这次经历中得到的是:

  1. PHP 维护着一堆错误处理程序,每次调用 set_error_handler() 都会变得更深/更高。
  2. 如果您将错误处理程序推入堆栈并且在程序“正常”退出之前不自行清理,则可能会发生坏事。

这是一个难以隔离的错误 - 我基本上将问题缩小到上述功能,然后盯着它直到我的眼睛流血。

那么,如果我知道我现在所知道的,我将如何追踪它呢?由于我不知道直接检查 PHP 错误处理程序“堆栈”的任何方法,我认为使用 Singleton 对象来封装 PHP 错误处理程序的所有设置/恢复操作可能是有意义的。至少可以在正常退出程序之前检查 Singleton 的状态,并且如果检测到“悬空”错误处理程序以在 PHP 崩溃之前生成合理的失败/警告消息。

于 2014-06-03T20:07:28.943 回答
2

这可能有点晚了,但我在将站点从本地服务器移动到远程服务器时发现了一个问题。我正在使用 Concrete5 cms 在本地开发了我的站点(xampp 中的 windows 8),然后上传到运行 Cent 0S 的远程服务器

默认情况下,Windows mysql 不区分大小写,并创建了一个小写数据库。将其上传到远程服务器后,我收到“在第 0 行的未知中抛出没有堆栈帧的异常?”

然后我纠正了数据库表的情况,我的网站又开始工作了。

于 2013-04-12T16:26:54.110 回答
2

对我们来说,这个错误是由于无意中序列化了 SimpleXML 对象。

如果您在 5.3.3 中使用 SimpleXML 对象,请确保在序列化会话中的值时将节点值转换为您需要的任何值(例如字符串)。

之前

  $token = $response->Token->Value;
  /* token saved in session, results in line 0 error */

之后

$token = (string) $response->Token->Value;
  /* token saved in session, no error */
于 2014-05-27T15:53:16.997 回答
1

我有完全相同的错误。一个非常特殊的情况:如果您将未命名的函数(闭包)挂钩连接到对象实例的挂钩点。之后,您尝试序列化此对象。

于 2015-06-24T17:57:08.503 回答
1

在错误地填充了 Illuminate Eloquent 模型的 Fillable 属性后,我遇到了同样的错误。请注意数组的最后 3 个元素,其中一个缺少 coma

protected $fillable = [
    'budget',        
    'routestatus' ,
    'userroutenumber'
    'totalmovingseconds',
    'totalidleseconds'
];
于 2019-06-27T14:29:51.563 回答
0

产生此错误的一种简单方法是使用register_globals = On. 那么你只需要两行代码:

<?php
    $_SESSION["my_var"] = "string";
    $my_var = new MyClass(); //could be any class, i guess
?>

一旦你重新加载这个页面,你就会得到Exception thrown without a stack frame in Unknown on line 0- 错误。似乎类的实例和(会话)变量之间存在冲突。
至少这就是我得到这个很难调试的恼人错误的原因。

于 2015-04-03T08:14:08.070 回答
0

我有同样的错误,它似乎将服务器从 centos 5 升级到 centos 6 并将 PHP 从 5.4 降级到 5.3。实际问题是 PHP apc,未正确配置。检查您的 APC。我使用的是 Symfony2,所以你可能会在Symfony Unable to allocate memory for pool找到一些帮助

于 2013-05-20T13:27:55.130 回答
0

您可能在数据库中有一个损坏/不一致的表。尝试转储数据库。如果你得到一个错误,那就是时候了。修复那张桌子,问题应该会消失。

这就是为什么全新安装有效的原因。干净的安装就是那么干净。

mysqlcheck 应该可以工作,但如果它没有显示并且问题仍然在上面。

于 2017-05-29T23:43:02.940 回答
0

当我在几个 Symfony 包上更改名称空间时,我遇到了这个问题。删除 symfony 缓存目录中的文件解决了这个问题。

于 2015-10-28T13:22:30.767 回答