When troubleshooting a MySQL crash, having only the error log is rarely enough to pinpoint the exact root cause. To truly understand what happened, we need to go deeper—into the memory state of the process at the moment it crashed. That’s where GDB, the GNU Debugger, comes in. GDB lets us inspect a core dump (a memory snapshot taken at crash time) and navigate through the function calls, variables, and internal MySQL structures as they existed right before failure.
In this post, we’ll walk through how to set up the right debug environment using Percona Server for MySQL with symbols, load a core dump, and extract the exact query, database, and table that caused the crash—step by step.
When MySQL crashes due to an internal error, such as a failed assertion, corruption, or invalid memory access, it writes detailed information to the error log. Among that output is a key value:
|
1 |
BuildID[sha1]=004605e2ba4743ab1c52b6baf7b5bb3d07854be5<br>Server Version: 8.0.32-24 Percona Server (GPL), Release 24 |
The Build ID uniquely identifies the exact binary that was running at the time of the crash. It is used to:
If the binary and the core dump are even slightly mismatched (e.g., a minor patch difference or rebuild), your analysis may fail or point to the wrong location.
We also need to confirm the operating system version on which MySQL was running. This helps us choose the right base image and debug packages.
In our case, the OS information was collected using pt-summary from the Percona Toolkit:
|
1 |
# Percona Toolkit System Summary Report ######################<br> Date | 2025-05-07 15:57:35 UTC (local TZ: UTC +0000)<br> Hostname | XXXXX<br> Uptime | 340 days, 20:24, 2 users, load average: 0.11, 0.28, 0.34<br> Platform | Linux<br> Release | AlmaLinux release 9.5 (Teal Serval)<br> Kernel | 5.14.0-427.18.1.el9_4.x86_64 |
From this, we confirmed:
With this information, we could recreate a similar environment using Red Hat UBI9 + Percona RPMs.
To safely inspect the core dump and reproduce the runtime environment, we use a Docker container based on Red Hat’s official UBI9 (Universal Base Image).
|
1 |
docker run -it --name mysql-debug-rhel9 <br> -d -v /path/to/core-dump:/mnt/core_dump <br> redhat/ubi9:latest |
This mounts your local crash directory into the container and runs the base image in detached mode.
Then, connect to it:
|
1 |
docker exec -it mysql-debug-rhel9 /bin/bash |
Now you’re inside a clean RHEL 9 container where you can install exactly the same version of MySQL + debug symbols.
Inside the container, install Percona’s debug builds and modern GDB tools:
|
1 |
yes | yum update<br>yes | yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm<br>percona-release setup ps80 |
Then install the packages:
|
1 |
yes | yum install -y <br> percona-server-server-8.0.32-24 <br> percona-server-debuginfo-8.0.32-24 <br> centos-release-scl-rh <br> devtoolset-11-gdb <br> sudo less vim which file |
This setup mirrors the environment of the production server, but safely and with tooling.
Now, verify that the binary you just installed is identical to the one that produced the core file. Below is an example of the expected output:
|
1 |
$ eu-unstrip -n --core core.mysqld | grep mysqld<br>0x5560d78b8000+0x4409000 0071b58535b12886aad88842353efa60058e9de1@0x5560d78b82bc - - /usr/sbin/mysqld<br><br>$ file mysqld<br>mysqld: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0071b58535b12886aad88842353efa60058e9de1, stripped |
If they don’t match, GDB won’t show function names or source code. If they do match, you’ll get full symbolized backtraces.
Now we’re ready to inspect the core:
|
1 |
gdb -ex "set debuginfod enabled on" <br> -ex "set print thread-events off" <br> -ex "set print pretty on" <br> -ex "set auto-load safe-path /" <br> -ex "file /usr/sbin/mysqld" <br> -ex "core /mnt/core_dump/core.31251" |
Once inside GDB, you’ll have access to a fully symbolized debug session—with real function names, line numbers, and source info.
Find out what query was running and in which schema when the crash occurred.
|
1 |
(gdb) bt |
This prints all function calls that led to the crash. This is the full stack trace:
|
1 |
(gdb) bt<br>#0 0x00007f659d68b52c in __pthread_kill_implementation (threadid=..., signo=..., no_tid=6) at pthread_kill.c:43 <br>#1 0x0000000000ddb2a1 in my_write_core (sig=6) at /usr/src/debug/percona-server-8.0.32-24/.../mysys/stacktrace.cc:322 <br>#2 handle_fatal_signal (sig=6) at /usr/src/debug/percona-server-8.0.32-24/.../sql/signal_handler.cc:252 <br>#3 handle_fatal_signal (sig=6) at /usr/src/debug/percona-server-8.0.32-24/.../sql/signal_handler.cc:224 <br>#4 <signal handler called> <br>#5 0x00007f659d68b52c in __pthread_kill_implementation (...) at pthread_kill.c:43 <br>#6 0x00007f659d68b5a3 in __pthread_kill_internal (...) at pthread_kill.c:78 <br>#7 0x00007f659d63e686 in __GI_raise (sig=6) at ../sysdeps/posix/raise.c:26 <br>#8 0x00007f659d628833 in __GI_abort () at abort.c:79 <br>#9 0x00000000008adc15 in my_server_abort () at /usr/src/debug/percona-server-8.0.32-24/.../sql/signal_handler.cc:311 <br>#10 0x000000000161ad1e in my_abort () at /usr/src/debug/percona-server-8.0.32-24/.../mysys/my_init.cc:264 <br>#11 0x00000000017d1433 in ut_dbg_assertion_failed (expr=..., file=..., line=7547) <br> at /usr/src/debug/percona-server-8.0.32-24/.../storage/innobase/ut/ut0dbg.cc:99 <br>#12 0x0000000001e6ef78 in fil_report_invalid_page_access_low(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../fil/fil0fil.cc:7547 <br>#13 0x00000000018fa9cb in Fil_shard::do_io(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf0types.h:268 <br>#14 0x00000000018faaa3 in _fil_io(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../fil/fil0fil.cc:8063 <br>#15 0x000000000184a7dc in buf_read_page_low(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../page0size.h:123 <br>#16 0x000000000184b8ad in buf_read_page(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf/buf0rea.cc:292 <br>#17 0x000000000184bf48 in Buf_fetch<Buf_fetch_normal>::read_page(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf/buf0buf.cc:4086 <br>#18 0x000000000184c4d6 in Buf_fetch_normal::get(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf/buf0buf.cc:3709 <br>#19 0x000000000184e559 in Buf_fetch<Buf_fetch_normal>::single_page(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf/buf0buf.cc:4281 <br>#20 buf_page_get_gen(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../buf/buf0buf.cc:4478 <br>#21 0x00000000016b1390 in buf_page_get(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../include/buf0buf.h:430 <br>#22 fut_get_ptr(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../include/fut0fut.ic:56 <br>#23 0x000000000224cda5 in lob::first_page_t::addr2ptr_s_cache(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../include/lob0first.h:239 <br>#24 0x000000000197b843 in lob::read(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../lob/lob0impl.cc:1163 <br>#25 0x00000000016cdda8 in lob::btr_copy_externally_stored_field_func(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../lob/lob0lob.cc:943 <br>#26 0x00000000016cdf73 in lob::btr_rec_copy_externally_stored_field_func(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../lob/lob0lob.cc:680 <br>#27 0x0000000001e797f9 in lob::btr_rec_copy_externally_stored_field(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../include/lob0lob.h:608 <br>#28 row_sel_store_mysql_field(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../row/row0sel.cc:2785 <br>#29 0x00000000017738bc in row_sel_store_mysql_rec(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../row/row0sel.cc:3021 <br>#30 0x000000000178d52f in row_search_mvcc(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../row/row0sel.cc:5801 <br>#31 0x0000000001654feb in ha_innobase::general_fetch(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../handler/ha_innodb.cc:11162 <br>#32 0x00000000009b45dc in handler::ha_rnd_next(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/handler.cc:3108 <br>#33 0x0000000000af2613 in TableScanIterator::Read(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../row_iterator.h:245 <br>#34 0x0000000000ee2e18 in FilterIterator::Read(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../composite_iterators.cc:76 <br>#35 0x0000000000ee2f4f in NestedLoopIterator::Read(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../composite_iterators.cc:465 <br>#36 0x0000000000c1f67a in DeleteRowsIterator::Read(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_delete.cc:1249 <br>#37 0x0000000000d5c5f3 in Query_expression::ExecuteIteratorQuery(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_union.cc:1771 <br>#38 0x0000000000cdb8d0 in Query_expression::execute(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_union.cc:1824 <br>#39 Sql_cmd_dml::execute_inner(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_select.cc:778 <br>#40 0x0000000000cdad12 in Sql_cmd_dml::execute(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_select.cc:578 <br>#41 0x0000000000c8b319 in mysql_execute_command(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_parse.cc:4944 <br>#42 0x0000000000c8f163 in dispatch_sql_command(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_parse.cc:5586 <br>#43 0x0000000000c9124f in dispatch_command(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_parse.cc:2129 <br>#44 0x0000000000c91d8b in do_command(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../sql/sql_parse.cc:1501 <br>#45 0x0000000000dcb388 in handle_connection(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../connection_handler_per_thread.cc:308 <br>#46 0x00000000019b67b6 in pfs_spawn_thread(...) <br> at /usr/src/debug/percona-server-8.0.32-24/.../pfs.cc:2987 <br>#47 0x00007f659d6897e2 in start_thread (...) at pthread_create.c:595 <br>#48 0x00007f659d70e800 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:47 <br>#49 0x0000000000000000 in ?? () |
The stack trace is read from bottom up. This means the thread started in frame 49, and it went all the way up until the crash. We can see that Percona Server raised the signal to the OS in frame 4. In our case, since we want the details about the table structure we will look into the 41:
|
1 |
#41 mysql_execute_command (thd=...) |
Then we will switch to that frame:
|
1 |
(gdb) f 41 |
This command jumps to frame 41. This specific frame corresponds to the mysql_execute_command() function, which is responsible for executing SQL commands (e.g., SELECT, INSERT, DELETE). The output confirms the source location:
|
1 |
#41 0x0000000000c8b319 in mysql_execute_command (thd=thd@entry=0x7f582c0e3ce0, first_level=first_level@entry=true)<br> at /usr/src/debug/percona-server-8.0.32-24.1.el9.x86_64/percona-server-8.0.32-24/sql/sql_parse.cc:4944 |
|
1 |
(gdb) print *thd |
This is where the magic happens. thd is a rich structure that includes:
And a lot more information.
From this, you can extract the exact SQL query that triggered the crash:
|
1 |
m_query_string = {<br> str = 0x7f582c1967c0 "DELETE table_x, table_y FROM table_x LEFT JOIN ...",<br> length = 40138<br>}<br>m_db = {<br> str = 0x7f582c4ac240 "database_x"<br>} |
This shows that a DELETE statement using a LEFT JOIN was involved, and the active database was database_x.
With this analysis, we now know:
This step is often enough to correlate with logs, find the responsible application code, or try to reproduce the issue and report a bug if necessary.
This workflow helped us diagnose a production crash caused by a query. Using GDB, we extracted the full query and schema, even though some information was unavailable in the logs.
This method is essential for:
This blog post aims to highlight some of what GDB can do for core dump analysis. GDB is a powerful tool that enables deep-level debugging, helping to identify and reproduce complex issues.
If you ever run into crashes, Percona Support can help you investigate and resolve them.
Resources
RELATED POSTS