Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MySQLi abstract layer dont works with hhvm, PHP without hhvm its works #4505

Closed
leoelcoder opened this issue Dec 20, 2014 · 6 comments
Closed

Comments

@leoelcoder
Copy link

This mysqli abstract layer work very well with php-fpm and nignx. But when Im trying to implement it with HHVM + nginx there isn't way to got working correctly.

I think the problem is around the Reflection class to pass the arguments to be bind (bind params, bind results) as arrays.

connect_errno) { print("Página no disponible. Intente de nuevo más tarde."); exit(); } } protected static function prepare() { self::$stmt = self::$conn->prepare(self::$sql); self::$reflection = new ReflectionClass('mysqli_stmt'); } protected static function setParams() { $method = self::$reflection->getMethod('bind_param'); $method->invokeArgs(self::$stmt, self::$data); } protected static function getData($fields) { self::$results = array(); $method = self::$reflection->getMethod('bind_result'); $method->invokeArgs(self::$stmt, $fields); while(self::$stmt->fetch()) { self::$results[] = unserialize(serialize($fields)); } } protected static function close() { self::$stmt->close(); self::$conn->close(); } public static function execute($sql, $data, $fields=False) { self::$sql = strtolower($sql); self::$data = $data; self::open(); self::$conn->set_charset("utf8"); self::prepare(); self::setParams(); self::$stmt->execute(); if($fields) { self::getData($fields); return self::$results; } else { if(strpos(self::$sql, strtolower('INSERT')) === 0) { return self::$conn->insert_id; } } self::close(); } ``` } ?>

example :

$sql = "SELECT user_id, name FROM user WHERE user_id > ?";
$data = array("i",0);
$fields = array('ID'=>'', 'NAME=>'');
$fields = DBModel::execute($sql, $data, $fields);
var_dump($fields);

The error log display:
\nWarning: Argument 1 not passed as reference
\nNotice: Array to string conversion in /var/www/core/DBModel.php on line 49
\nNotice: Array to string conversion in /var/www/core/DBModel.php on line 49
Core dumped: Aborted
Stack trace in /tmp/stacktrace.11929.log

And then HHVM crashes!

I'm not the author of this layer. more infor about http://pastebin.com/SHwDkQzL

@bauerj
Copy link
Contributor

bauerj commented Dec 20, 2014

Line 42 is a blank line in the code you supplied. Could you please post the real code you use?

Also, proper formatting would be nice 😜

@Majkl578
Copy link
Contributor

This code makes me cry. Every time you write a class misusing static like this, God kills a dozen of kittens...

Anyway, you say HHVM crashes, can you provide a stack trace? Install hhvm-dbg or hhvm-nightly-dbg and make it crash once again. The stack trace should then be in /tmp.

@leoelcoder
Copy link
Author

I've update de orginal issue, and the error is at line 49.

with hhvm-dbg, the stack trace display this:

Host: maquina
ProcessID: 11931
ThreadID: 7f58fc7ff700
ThreadPID: 11936
Name: unknown program
Type: Aborted
Runtime: hhvm
Version: tags/HHVM-3.4.2-0-g40d85d7386b3342ab848fc45c6892433c89ef5b2
DebuggerCount: 0

ThreadType: Web Request
Server_SERVER_NAME: hhvm.dev
Server: hhvm.dev
URL: /

0 bt_handler at /tmp/tmp.JlCliioODY/hphp/runtime/base/crash-reporter.cpp:71
1 gsignal at /lib/x86_64-linux-gnu/libc.so.6:0
2 abort at /lib/x86_64-linux-gnu/libc.so.6:0
3 uselocale at /lib/x86_64-linux-gnu/libc.so.6:0
4 assert_fail at /lib/x86_64-linux-gnu/libc.so.6:0
5 HPHP::Variant::toInt64Helper(int) const at /tmp/tmp.JlCliioODY/hphp/runtime/base/type-variant.cpp:435
6 HPHP::Variant::toInt64() const at /tmp/tmp.JlCliioODY/hphp/runtime/base/type-variant.h:621
7 HPHP::MySQLStmtVariables::bind_params(st_mysql_stmt
) at /tmp/tmp.JlCliioODY/hphp/runtime/ext/mysql/mysql_common.cpp:925
8 HPHP::MySQLStmt::execute() at /tmp/tmp.JlCliioODY/hphp/runtime/ext/mysql/mysql_common.cpp:1083
9 c_mysqli_stmt_ni_execute at /tmp/tmp.JlCliioODY/hphp/runtime/ext/mysql/ext_mysqli.cpp:540
10 HPHP::Native::callFuncInt64Impl(HPHP::TypedValue
()(HPHP::ActRec), long_, int, double_, int) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/native-func-caller.h:788
11 void HPHP::Native::callFunc<false, false>(HPHP::Func const_, void_, HPHP::TypedValue_, HPHP::TypedValue&) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/native.cpp:176
12 HPHP::TypedValue_ HPHP::Native::methodWrapper<false, false>(HPHP::ActRec_) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/native.cpp:407
13 HPHP::ExecutionContext::iopNativeImpl(unsigned char const_&) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:6815
14 void HPHP::ExecutionContext::dispatchImpl() at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:7450
15 HPHP::ExecutionContext::dispatch() at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:7457
16 HPHP::ExecutionContext::enterVMAtFunc(HPHP::ActRec_, HPHP::ExecutionContext::StackArgsState) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:1892
17 HPHP::ExecutionContext::enterVM(HPHP::ActRec_, HPHP::ExecutionContext::StackArgsState, HPHP::Resumable_, HPHP::ObjectData_) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:1946
18 HPHP::ExecutionContext::invokeFunc(HPHP::TypedValue_, HPHP::Func const_, HPHP::Variant const&, HPHP::ObjectData_, HPHP::Class_, HPHP::VarEnv_, HPHP::StringData_, HPHP::ExecutionContext::InvokeFlags) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:2113
19 HPHP::ExecutionContext::invokeUnit(HPHP::TypedValue_, HPHP::Unit const_) at /tmp/tmp.JlCliioODY/hphp/runtime/vm/bytecode.cpp:2294
20 invoke_file_impl at /tmp/tmp.JlCliioODY/hphp/runtime/base/builtin-functions.cpp:723
21 invoke_file at /tmp/tmp.JlCliioODY/hphp/runtime/base/builtin-functions.cpp:734
22 HPHP::include_impl_invoke(HPHP::String const&, bool, char const_) at /tmp/tmp.JlCliioODY/hphp/runtime/base/builtin-functions.cpp:745
23 HPHP::hphp_invoke(HPHP::ExecutionContext_, std::basic_string<char, std::char_traits, std::allocator > const&, bool, HPHP::Array const&, HPHP::VRefParamValue const&, std::basic_string<char, std::char_traits, std::allocator > const&, std::basic_string<char, std::char_traits, std::allocator > const&, bool&, std::basic_string<char, std::char_traits, std::allocator >&, bool, bool, bool) at /tmp/tmp.JlCliioODY/hphp/runtime/base/program-functions.cpp:1808
24 HPHP::HttpRequestHandler::executePHPRequest(HPHP::Transport_, HPHP::RequestURI&, HPHP::SourceRootInfo&, bool) at /tmp/tmp.JlCliioODY/hphp/runtime/server/http-request-handler.cpp:400
25 HPHP::HttpRequestHandler::handleRequest(HPHP::Transport_) at /tmp/tmp.JlCliioODY/hphp/runtime/server/http-request-handler.cpp:311
26 HPHP::ServerWorkerstd::shared_ptr<HPHP::FastCGIJob, HPHP::FastCGITransportTraits>::doJobImpl(std::shared_ptrHPHP::FastCGIJob, bool) at /tmp/tmp.JlCliioODY/hphp/runtime/server/server-worker.h:108
27 HPHP::ServerWorkerstd::shared_ptr<HPHP::FastCGIJob, HPHP::FastCGITransportTraits>::doJob(std::shared_ptrHPHP::FastCGIJob) at /tmp/tmp.JlCliioODY/hphp/runtime/server/server-worker.h:57
28 HPHP::JobQueueWorkerstd::shared_ptr<HPHP::FastCGIJob, HPHP::Server_, true, false, HPHP::JobQueueDropVMStack>::start() at /tmp/tmp.JlCliioODY/hphp/util/job-queue.h:463
29 HPHP::AsyncFuncHPHP::ServerWorker<std::shared_ptr<HPHP::FastCGIJob, HPHP::FastCGITransportTraits> >::run_(void_) at /tmp/tmp.JlCliioODY/hphp/util/async-func.h:214
30 HPHP::AsyncFuncImpl::threadFuncImpl() at /tmp/tmp.JlCliioODY/hphp/util/async-func.cpp:140
31 HPHP::AsyncFuncImpl::ThreadFunc(void_) at /tmp/tmp.JlCliioODY/hphp/util/async-func.cpp:52
32 HPHP::start_routine_wrapper(void_) at /tmp/tmp.JlCliioODY/hphp/runtime/base/thread-hooks.cpp:113
33 start_thread at /lib/x86_64-linux-gnu/libpthread.so.0:0
34 clone at /lib/x86_64-linux-gnu/libc.so.6:0

PHP Stacktrace:
0 mysqli_stmt->execute() called at [/var/www/core/DBModel.php:49]
1 DBModel::execute(SELECT user_id, name FROM user WHERE user_id > ?, Array, Array) called at [/var/www/AppEngine.php:8]

@leoelcoder
Copy link
Author

When I execute directly in a terminal
AppEngine import the mysqli layer
In AppEngine:
require_once 'core/DBModel.php';
$sql = "SELECT user_id, name FROM user WHERE user_id > ?";
$data = array("i",0);
$fields = array('ID'=>'', 'NAME=>'');
$fields = DBModel::execute($sql, $data, $fields);
var_dump($fields);

in terminal

hhvm /var/www/AppEngine.php

Display this:
Warning: Argument 1 not passed as reference
hhvm: /tmp/tmp.WTUkCgzncO/hphp/runtime/base/type-variant.cpp:458: int64_t HPHP::Variant::toInt64Helper(int) const: Assertion `false' failed.
Segment Violation

@wjywbs
Copy link
Contributor

wjywbs commented Jan 6, 2015

Part 1

The function definitions of bind-param() and bind-result() in http://php.net/manual/en/mysqli-stmt.bind-param.php and http://php.net/manual/en/mysqli-stmt.bind-result.php expect variable references (mixed &$var1). You are passing number 0, which is not a reference. So you can try:

$num = 0;
$data = array("i", &$num);

$id = '';
$name = '';
$fields = array(&$id, &$name);
$fields = DBModel::execute($sql, $data, $fields);

Both hhvm 3.3 and 3.4 work.

Part 2

If I only correct $data but not $fields, for example $fields = array($id, $name), I get "\nWarning: Argument 1 not passed as reference in \xe8\x95E-m\x7f on line 0". I get different file names and corrupted results from var_dump() in different runs. After this, all other php files will have corruption and return various errors. When using $fields = array($id, &$name), hhvm crashes in serialize().

However, hhvm shouldn't crash. Adding some checks about if the variable has the correct type in bytecode.cpp to stop executing the function from reflection or in ext_mysqli.cpp (line 129) can prevent from toInt64Helper() crashing and corruption.

Part 3

If I change the lines $method = self::$reflection->getMethod('bind_param'); $method->invokeArgs(self::$stmt, self::$data); to $num = 0; self::$stmt->bind_param("i", $num); to remove the reflection and use the normal way to call bind_param(), hhvm 3.4 will not show any results. hhvm 3.3 is fine. I did a bisecting and found that b104d5e caused the problem. I didn't see any leak with extraArgs without this commit.

My understanding is that bind_param_helper() in ext_mysqli.cpp stores all the arguments in a vector and calls arReturn() at the end. The reference variables are freed in arReturn() due to this commit, so update_result() in mysql_common.cpp can't set the result later (*m_arr[i]->getRefData() = v;).

Part 4: Memory leak

Since that commit was about fixing memory leak, I reverted that commit and did a stress test. I found that the memory leaked so fast that somewhere else must be leaking. jemalloc profiling dumps showed MySQLStmt::bind_result() leaked. It turned out that ~MySQLStmt() and ~MySQLStmtVariables() were never called. The buffer will also leak if the result has strings.

Every time hhvm runs your scripts above, it will be gradually leaking memory (about 1gb/5000 requests in my testing).

@paulbiss
Copy link
Contributor

paulbiss commented Jan 7, 2015

@wjywbs: nice debugging, thanks!

https://reviews.facebook.net/D31095

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants