Ulf Wendel

PHP: Debugging ext/mysqli and mysqlnd

One good news, one bad news – that’s the frank way we blog about mysqlnd. The last posting on significant memory savings (40%) and new tuning options was good news. The bad news: mysqlnd might have bugs. How to report and debug these bugs – using mysqli_debug() – is subject of this posting.

Where to send mysqlnd problem reports, how to contact

Due to a low feedback rate – which is disappointing in a certain way – we are in the comfortable situation that you may report issues or ask questions on pretty much every channel: on the mailing list php@lists.mysql.com, on http://bugs.mysql.com/, on http://bugs.php.net/, by private mail (georg/andrey/uwendel at mysql dot com) or even using a blog comment. Of course, if you are a MySQL customer, you may also use the MySQL Support System to get in touch with us. Whatever way you choose, we should be get your message.

The hard part is to write a good bug report. Ideally, a good report comes at least with a debug trace of ext/mysqli. If you are an expert user, skip the introduction part and read below how to create a client trace with ext/mysqli.

Internal vs. external debugging

There are two basic ways in which can be used for debugging a software: internal debugging and external debugging. Internal debugging refers to everything what you can do within a software. In its most basic form internal debugging is done using printf() to print out status information whenever needed. External debugging means that you run a software in some sort of sandbox to monitor its activity.

Using external debugging tools (GDB, Valgrind, etc.). External debug tools have the advantage that you do not need to to change your software. Also, external debugging tools are very flexible. Different debugging tools can monitor different aspects of your software. The disadvantage is: you need to have an external debug tool installed, the tool might not be available on all platforms and you might need to learn how to use many different external debugging tools.

Internal debugging techniques, for example trace files, solve some of these problems. And in particular trace files can be very handy for client server environments. Have you ever tried to debug Apache under heavy load, especially Apache used with worker MPM? If so, you know how tricky it can become to monitor all child processes and to identify a failing process. As you know, the really tricky issues show some sort of random behavior…

In such a situation, client traces can he of great help. Trace files do not disappear as fast as processes do. Trace files can be send to support staff for analysis and support does not need to be given access to your system. And sometimes trace files are “easy to read” function call traces. If your application is Open Source and you have some basic skills in the underlying programming language, you have a fair chance that you can analyse a problem on your own.

Example of a client trace using libmysql/DBUG Package


>_mymalloc
| enter: Size: 964
| exit: ptr: 0x8413c48
<_mymalloc
>mysql_real_connect
| enter: host: localhost  db: phptest  user: root
| info: Using UNIX sock '/tmp/mysql.sock'
| >vio_new
| | enter: sd: 3
| | >_mymalloc
| | | enter: Size: 156
| | | exit: ptr: 0x8413778
| | <_mymalloc
| | >vio_init
| | | enter: type: 2  sd: 3  flags: 3
| | | >_mymalloc
| | | | enter: Size: 16384
| | | | exit: ptr: 0x8414030
| | | <_mymalloc
| | <vio_init
| <vio_new
| >my_net_init
| | >my_net_set_read_timeout
| | | enter: timeout: 31536000
| | | >vio_timeout
| | | <vio_timeout
| | <my_net_set_read_timeout
| | >my_net_set_write_timeout
| | | enter: timeout: 31536000
| | | >vio_timeout
| | | <vio_timeout
| | <my_net_set_write_timeout
| | >_mymalloc
| | | enter: Size: 8199
| | | exit: ptr: 0x8418050
| | <_mymalloc
| | >vio_fastsend
| | | warning: Couldn't set socket option for fast send
| | | exit: -1
| | <vio_fastsend
| <my_net_init
| >vio_keepalive
| | enter: sd: 3  set_keep_alive: 1
| <vio_keepalive
| >vio_is_blocking
| | exit: 1
| <vio_is_blocking
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 4
| | >vio_read
| | | enter: sd: 3  buf: 0x8414030  size: 16384
| | | exit: 71
| | <vio_read
| <vio_read_buff
| packet_header: Memory: 0x8418050  Bytes: (4)
43 00 00 00 
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 67
| <vio_read_buff
| packet: Memory: 0x8418050  Bytes: (10)
0A 35 2E 31 2E 32 32 2D 62 65 
| info: mysql protocol version 10, server=10
| >_mymalloc
| | enter: Size: 7
| | exit: ptr: 0x8413858
| <_mymalloc
| >get_charset_by_csname
| | enter: name: 'latin1'
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | >get_charsets_dir
| | | >convert_dirname
| | | <convert_dirname
| | | info: charsets dir: '/usr/local/mysql/share/mysql/charsets/'
| | <get_charsets_dir
| | >my_stat
| | | my: path: '/usr/local/mysql/share/mysql/charsets/Index.xml'  stat_area: 0xbfffb7f0  MyFlags: 0
| | <my_stat
| | >_mymalloc
| | | enter: Size: 18261
| | | exit: ptr: 0x8423250
| | <_mymalloc
| | >my_open
| | | my: Name: '/usr/local/mysql/share/mysql/charsets/Index.xml'  Flags: 0  MyFlags: 0
| | <my_open
| | >_mymalloc
| | | enter: Size: 48
| | | exit: ptr: 0x8413880
| | <_mymalloc
| | exit: fd: 4
| | >my_read
| | | my: Fd: 4  Buffer: 0x8423250  Count: 18261  MyFlags: 0
| | <my_read
| | >my_close
| | | my: fd: 4  MyFlags: 0
| | | >_myfree
| | | | enter: ptr: 0x8413880
| | | <_myfree
| | <my_close
| | test: my_once_malloc 4060 byte malloced
| | test: my_once_malloc 4060 byte malloced
| | >_myfree
| | | enter: ptr: 0x8423250
| | <_myfree
| <get_charset_by_csname
| >my_multi_malloc
| | >_mymalloc
| | | enter: Size: 120
| | | exit: ptr: 0x8423250
| | <_mymalloc
| <my_multi_malloc
| >_mymalloc
| | enter: Size: 5
| | exit: ptr: 0x84232e8
| <_mymalloc
| >_mymalloc
| | enter: Size: 1
| | exit: ptr: 0x8423310
| <_mymalloc
| info: Server version = '5.1.22-beta-debug-log'  capabilites: 41516  status: 2  client_flag: 172685
| info: user: root
| >_mymalloc
| | enter: Size: 8
| | exit: ptr: 0x8423338
| <_mymalloc
| packet_header: Memory: 0xbfffbf60  Bytes: (4)
2E 00 00 01 
| >net_flush
| | >vio_is_blocking
| | | exit: 1
| | <vio_is_blocking
| | >net_real_write
| | | >vio_write
| | | | enter: sd: 3  buf: 0x8418050  size: 50
| | | | exit: 50
| | | <vio_write
| | <net_real_write
| <net_flush
| >vio_is_blocking
| | exit: 1
| <vio_is_blocking
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 4
| | >vio_read
| | | enter: sd: 3  buf: 0x8414030  size: 16384
| | | exit: 11
| | <vio_read
| <vio_read_buff
| packet_header: Memory: 0x8418050  Bytes: (4)
07 00 00 02 
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 7
| <vio_read_buff
| exit: Mysql handler: 0x8413c48
<mysql_real_connect
>mysql_option
| enter: option: 8
<mysql_option
>mysql_real_query
| enter: handle: 0x8413c48
| query: Query = 'DROP TABLE IF EXISTS test'
| >mysql_send_query
| | enter: rpl_parse: 0  rpl_pivot: 1
| <mysql_send_query
| >cli_advanced_command
| | >net_clear
| | <net_clear
| | >net_write_command
| | | enter: length: 25
| | <net_write_command
| | >net_flush
| | | >vio_is_blocking
| | | | exit: 1
| | | <vio_is_blocking
| | | >net_real_write
| | | | >vio_write
| | | | | enter: sd: 3  buf: 0x8418050  size: 30
| | | | | exit: 30
| | | | <vio_write
| | | <net_real_write
| | <net_flush
| | exit: result: 0
| <cli_advanced_command
<mysql_real_query
>cli_read_query_result
| >vio_is_blocking
| | exit: 1
| <vio_is_blocking
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 4
| | >vio_read
| | | enter: sd: 3  buf: 0x8414030  size: 16384
| | | exit: 11
| | <vio_read
| <vio_read_buff
| packet_header: Memory: 0x8418050  Bytes: (4)
07 00 00 01 
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 7
| <vio_read_buff
| >free_old_query
| | >init_alloc_root
| | | enter: root: 0x8413ee0
| | <init_alloc_root
| <free_old_query
| info: affected_rows: 0  insert_id: 0
| info: status: 2  warning_count: 0
<cli_read_query_result
>mysql_real_query
| enter: handle: 0x8413c48
| query: Query = 'CREATE TABLE test(id INT)'
| >mysql_send_query
| | enter: rpl_parse: 0  rpl_pivot: 1
| <mysql_send_query
| >cli_advanced_command
| | >net_clear
| | <net_clear
| | >net_write_command
| | | enter: length: 25
| | <net_write_command
| | >net_flush
| | | >vio_is_blocking
| | | | exit: 1
| | | <vio_is_blocking
| | | >net_real_write
| | | | >vio_write
| | | | | enter: sd: 3  buf: 0x8418050  size: 30
| | | | | exit: 30
| | | | <vio_write
| | | <net_real_write
| | <net_flush
| | exit: result: 0
| <cli_advanced_command
<mysql_real_query
>cli_read_query_result
| >vio_is_blocking
| | exit: 1
| <vio_is_blocking
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 4
| | >vio_read
| | | enter: sd: 3  buf: 0x8414030  size: 16384
| | | exit: 11
| | <vio_read
| <vio_read_buff
| packet_header: Memory: 0x8418050  Bytes: (4)
07 00 00 01 
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 7
| <vio_read_buff
| >free_old_query
| | >init_alloc_root
| | | enter: root: 0x8413ee0
| | <init_alloc_root
| <free_old_query
| info: affected_rows: 0  insert_id: 0
| info: status: 2  warning_count: 0
<cli_read_query_result
>mysql_real_query
| enter: handle: 0x8413c48
| query: Query = 'INSERT INTO test(id) VALUES (1)'
| >mysql_send_query
| | enter: rpl_parse: 0  rpl_pivot: 1
| <mysql_send_query
| >cli_advanced_command
| | >net_clear
| | <net_clear
| | >net_write_command
| | | enter: length: 31
| | <net_write_command
| | >net_flush
| | | >vio_is_blocking
| | | | exit: 1
| | | <vio_is_blocking
| | | >net_real_write
| | | | >vio_write
| | | | | enter: sd: 3  buf: 0x8418050  size: 36
| | | | | exit: 36
| | | | <vio_write
| | | <net_real_write
| | <net_flush
| | exit: result: 0
| <cli_advanced_command
<mysql_real_query
>cli_read_query_result
| >vio_is_blocking
| | exit: 1
| <vio_is_blocking
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 4
| | >vio_read
| | | enter: sd: 3  buf: 0x8414030  size: 16384
| | | exit: 11
| | <vio_read
| <vio_read_buff
| packet_header: Memory: 0x8418050  Bytes: (4)
07 00 00 01 
| >vio_read_buff
| | enter: sd: 3  buf: 0x8418050  size: 7
| <vio_read_buff
| >free_old_query
| | >init_alloc_root
| | | enter: root: 0x8413ee0
| | <init_alloc_root
| <free_old_query
| info: affected_rows: 1  insert_id: 0
| info: status: 2  warning_count: 0
<cli_read_query_result
>mysql_close
| >free_old_query
| | >init_alloc_root
| | | enter: root: 0x8413ee0
| | <init_alloc_root
| <free_old_query
| >cli_advanced_command
| | >net_clear
| | <net_clear
| | >net_write_command
| | | enter: length: 0
| | <net_write_command
| | >net_flush
| | | >vio_is_blocking
| | | | exit: 1
| | | <vio_is_blocking
| | | >net_real_write
| | | | >vio_write
| | | | | enter: sd: 3  buf: 0x8418050  size: 5
| | | | | exit: 5
| | | | <vio_write
| | | <net_real_write
| | <net_flush
| | exit: result: 0
| <cli_advanced_command
| >end_server
| | info: Net: socket (3)
| | >vio_close
| | <vio_close
| | >_myfree
| | | enter: ptr: 0x8414030
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x8413778
| | <_myfree
| | >net_end
| | | >_myfree
| | | | enter: ptr: 0x8418050
| | | <_myfree
| | <net_end
| | >free_old_query
| | | >init_alloc_root
| | | | enter: root: 0x8413ee0
| | | <init_alloc_root
| | <free_old_query
| <end_server
| >mysql_close_free_options
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x8413858
| | <_myfree
| | >_myfree
| | | enter: ptr: 0x0
| | <_myfree
| | >mysql_ssl_free
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | | >_myfree
| | | | enter: ptr: 0x0
| | | <_myfree
| | <mysql_ssl_free
| <mysql_close_free_options
| >_myfree
| | enter: ptr: 0x8423250
| <_myfree
| >_myfree
| | enter: ptr: 0x84232e8
| <_myfree
| >_myfree
| | enter: ptr: 0x8423310
| <_myfree
| >_myfree
| | enter: ptr: 0x8423338
| <_myfree
| >_myfree
| | enter: ptr: 0x0
| <_myfree
| >mysql_detach_stmt_list
| <mysql_detach_stmt_list
| >_myfree
| | enter: ptr: 0x8413c48
| <_myfree
<mysql_close

Generating client traces with ext/mysqli

For those reasons ext/mysqli has a function mysqli_debug() to write a client trace. The client trace is available both with libmysql and mysqlnd. If you use ext/mysqli with libmysql, the tracing functionality is based on the DBUG Package used by the MySQL Server. If you use ext/mysqli with mysqlnd, the DBUG Package is not available, but Andrey has started to implement a similar functionality. It is not as powerful as the DBUG Package, but it covers the main functionality of it.

Creating a trace file is easy, it is just one function call you need to add to your PHP script: mysqli_debug("d:t:O,/tmp/client.trace"). Prerequisite with libmysql: you are running a debug version of libmysql and a debug version of PHP. Prerequisite with mysqlnd: you running a debug version of PHP, that is PHP compiled with the flag --enable-debug.


nixnutz@linux-eu6p:~/php5_mysqlnd> sapi/cli/php -i | grep debug
Configure Command =>  './configure'  '--with-mysql=mysqlnd' '--with-mysqli=mysqlnd' '--enable-maintainer-zts' '--enable-debug'
[...]

Control string

mysqli_debug() takes a control string which determines what gets recorded in the client trace. The control string has the format: option1[,parameter_option1][:option2[,parameter_option2]]. The following options are supported by both mysqlnd and libmysql. libmysql/DBUG know of a few more options documented in the MySQL Internal Manual. However, as those options are not meaningful for mysqlnd, I’ll list only those options that work both with libmysql and mysqlnd.

A[,file]
Append trace output to specified file. Ensure that data gets written after each write. Typically this is done by closing and reopening the trace file (slow!). It helps to get a complete log file in case of crashes.
a[,file]
Append trace output to specified file.
f[,functions]
Limit debugger actions to the specified list of functions. An empty list of functions implies that all functions are selected.
F
Mark each debugger output line with the name of the source file containing the macro causing the output.
i
Mark each debugger output line with the PID of the current process.
L
Mark each debugger output line with the name of the source file line number of the macro causing the output.
n
Mark each debugger output line with the current function nesting depth
o[,file]
Like a[,file] but overwrite old file, do not append.
O[,file]
Like A[,file] but overwrite old file, do not append.
t[,N]
Enable function control flow tracing. The maximum nesting depth is specified by N, and defaults to 200.

Here is the PHP script that has written the above libmysql/DBUG trace and the below mysqlnd trace.


<?php
mysqli_debug("d:t:O,/tmp/client.trace");
$link = mysqli_connect("localhost", "root", "root", "phptest");
mysqli_query($link, "DROP TABLE IF EXISTS test");
mysqli_query($link, "CREATE TABLE test(id INT)");
mysqli_query($link, "INSERT INTO test(id) VALUES (1)");
mysqli_close($link);
?>


>mysqlnd_init
| info : persistent=0
<mysqlnd_init
>mysqlnd_connect
| info : host=localhost user=root db=phptest port=0 flags=131072 persistent=0 state=0
| info : unix:///tmp/mysql.sock
| info : stream=0xb7c79ab0
| >php_mysqlnd_greet_read
| <php_mysqlnd_greet_read
| >php_mysqlnd_auth_write
| <php_mysqlnd_auth_write
| >mysqlnd_stream_write_w_header
| | info : conn=69 count=66
| <mysqlnd_stream_write_w_header
| >php_mysqlnd_ok_read
| | info : OK packet: aff_rows=0 last_ins_id=0 server_status=0 warnings=2
| <php_mysqlnd_ok_read
| >mysqlnd_conn::set_client_option
| | info : conn=69 option=203
| <mysqlnd_conn::set_client_option
| >mysqlnd_conn::set_client_option
| | info : conn=69 option=202
| <mysqlnd_conn::set_client_option
| info : connection_id=69
| >mysqlnd_conn::set_client_option
| | info : conn=69 option=200
| <mysqlnd_conn::set_client_option
| info : unicode set
<mysqlnd_connect
>mysqlnd_conn::set_client_option
| info : conn=69 option=8
<mysqlnd_conn::set_client_option
>mysqlnd_conn::set_server_option
| >mysqlnd_simple_command
| | info : command=SET_OPTION ok_packet=3 silent=0
| | >php_mysqlnd_cmd_write
| | | >php_mysqlnd_consume_uneaten_data
| | | <php_mysqlnd_consume_uneaten_data
| | | >mysqlnd_stream_write_w_header
| | | | info : conn=69 count=3
| | | <mysqlnd_stream_write_w_header
| | <php_mysqlnd_cmd_write
| | >mysqlnd_simple_command_handle_response
| | | info : silent=0 packet=3 command=SET_OPTION
| | | >php_mysqlnd_eof_read
| | | | info : EOF packet: status=2 warnings=0
| | | <php_mysqlnd_eof_read
| | | info : OK from server
| | | info : PASS
| | <mysqlnd_simple_command_handle_response
| | info : PASS
| <mysqlnd_simple_command
<mysqlnd_conn::set_server_option
>mysqlnd_conn::query
| info : conn=69 query=DROP TABLE IF EXISTS test
| >mysqlnd_simple_command
| | info : command=QUERY ok_packet=11 silent=0
| | >php_mysqlnd_cmd_write
| | | >php_mysqlnd_consume_uneaten_data
| | | <php_mysqlnd_consume_uneaten_data
| | | >mysqlnd_stream_write_w_header
| | | | info : conn=69 count=26
| | | <mysqlnd_stream_write_w_header
| | <php_mysqlnd_cmd_write
| | info : PASS
| <mysqlnd_simple_command
| >mysqlnd_query_read_result_set_header
| | info : stmt=0
| | >php_mysqlnd_rset_header_read
| | <php_mysqlnd_rset_header_read
| | info : UPSERT
| | info : PASS
| <mysqlnd_query_read_result_set_header
<mysqlnd_conn::query
>mysqlnd_conn::query
| info : conn=69 query=CREATE TABLE test(id INT)
| >mysqlnd_simple_command
| | info : command=QUERY ok_packet=11 silent=0
| | >php_mysqlnd_cmd_write
| | | >php_mysqlnd_consume_uneaten_data
| | | <php_mysqlnd_consume_uneaten_data
| | | >mysqlnd_stream_write_w_header
| | | | info : conn=69 count=26
| | | <mysqlnd_stream_write_w_header
| | <php_mysqlnd_cmd_write
| | info : PASS
| <mysqlnd_simple_command
| >mysqlnd_query_read_result_set_header
| | info : stmt=0
| | >php_mysqlnd_rset_header_read
| | <php_mysqlnd_rset_header_read
| | info : UPSERT
| | info : PASS
| <mysqlnd_query_read_result_set_header
<mysqlnd_conn::query
>mysqlnd_conn::query
| info : conn=69 query=INSERT INTO test(id) VALUES (1)
| >mysqlnd_simple_command
| | info : command=QUERY ok_packet=11 silent=0
| | >php_mysqlnd_cmd_write
| | | >php_mysqlnd_consume_uneaten_data
| | | <php_mysqlnd_consume_uneaten_data
| | | >mysqlnd_stream_write_w_header
| | | | info : conn=69 count=32
| | | <mysqlnd_stream_write_w_header
| | <php_mysqlnd_cmd_write
| | info : PASS
| <mysqlnd_simple_command
| >mysqlnd_query_read_result_set_header
| | info : stmt=0
| | >php_mysqlnd_rset_header_read
| | <php_mysqlnd_rset_header_read
| | info : UPSERT
| | info : PASS
| <mysqlnd_query_read_result_set_header
<mysqlnd_conn::query
>mysqlnd_conn::close
| info : conn=69
| >mysqlnd_send_close
| | info : conn=69 conn->net.stream->abstract=0xb7c79a6c
| | info : Connection clean, sending COM_QUIT
| | >mysqlnd_simple_command
| | | info : command=QUIT ok_packet=11 silent=1
| | | >php_mysqlnd_cmd_write
| | | | >php_mysqlnd_consume_uneaten_data
| | | | <php_mysqlnd_consume_uneaten_data
| | | | >mysqlnd_stream_write_w_header
| | | | | info : conn=69 count=1
| | | | <mysqlnd_stream_write_w_header
| | | <php_mysqlnd_cmd_write
| | | info : PASS
| | <mysqlnd_simple_command
| <mysqlnd_send_close
| >mysqlnd_conn::free_reference
| | info : conn=69 conn->refcount=1
| | >mysqlnd_send_close
| | | info : conn=69 conn->net.stream->abstract=0xb7c79a6c
| | <mysqlnd_send_close
| | >mysqlnd_conn::dtor
| | | info : conn=69
| | | >mysqlnd_conn::free_contents
| | | | info : Freeing stream. abstract=0xb7c79a6c
| | | | info : Freeing memory of members
| | | | info : Freeing user
| | | | info : Freeing passwd
| | | | info : Freeing unix_socket
| | | | info : Freeing scheme
| | | | info : Freeing server_version
| | | | info : Freeing host_info
| | | | info : Freeing scramble
| | | | info : Freeing zval cache reference
| | | | info : Freeing cmd buffer
| | | <mysqlnd_conn::free_contents
| | <mysqlnd_conn::dtor
| <mysqlnd_conn::free_reference
<mysqlnd_conn::close
>RSHUTDOWN

Differences with mysqlnd

The different trace content and limited control string format support is not the only difference between mysqlnd and libmysql. mysqlnd allows you to use mysqli_debug() and the mysqlnd.debug ini setting to control the trace. Instead of passing the control string to mysqli_debug() you can also set it in the php.ini (but not change it in your script with ini_set(), however, mysqli_debug() can overrule it).

Like with other ini settings you may set mysqlnd.debug also on the CLI using the option -d of the PHP CLI binary.

nixnutz@linux-eu6p:~/php5_mysqlnd> sapi/cli/php -d mysqlnd.debug="d:t:O,/tmp/mysqlnd_client.trace" myscript.php

And the cool thing is… – it works for ext/mysql and ext/mysqli. As it is basically a mysqlnd library trace what you get, you can get it for both extension as both extensions can be compiled with mysqlnd.

Limitations

The mysqlnd client trace is not available on Windows. And we still have not merged the latest code from the MySQL SVN repository into PHP Head. Its been me who wanted to finish this blog and test the new debugging feature a little bit before we push it into PHP Head (PHP 6). However, expect that to happen during the next week. If you want to try out the client trace right now, you need to use a SVN copy.

Summary: what to send with you bug report?

To sum up on reporting mysqlnd issues: If you have any chance, send us the PHP code. If needed send your SQL definitions. Send a description of your environment, and for tricky issues send us a client trace. Read above how to create a client trace ;-) .

PS: Tracing is slow because of the disk operations involved. In particular A and O (close and reconnect after write) are slow.

Comments are closed.