• Skip to secondary menu
  • Skip to main content
  • Skip to primary sidebar
  • Home
  • Projects
  • Products
  • Themes
  • Tools
  • Request for Quote

Vengala Vinay

Having 12+ Years of Experience in Software Development

  • Home
  • WordPress
  • PHP
    • Codeigniter
  • Django
  • Magento
  • Selenium
  • Server
Home » Step-by-Step: Diagnosing Segmentation Fault (core dumped) in multi-threaded C/C++ daemons on Google Cloud Servers

Step-by-Step: Diagnosing Segmentation Fault (core dumped) in multi-threaded C/C++ daemons on Google Cloud Servers

Initial Triage: Identifying the Segmentation Fault

A segmentation fault (often indicated by a “core dumped” message) in a multi-threaded C/C++ daemon on Google Cloud Platform (GCP) is a critical issue that points to a memory access violation. This typically means your program attempted to access memory it doesn’t have permission to access, or it tried to write to read-only memory. On GCP, especially with managed services or containerized environments, the initial steps involve understanding the context of the crash.

The first indicator is usually a log message from the system or your application. For systemd services, you might see:

journalctl -u your-daemon.service -n 50 --since "1 hour ago"

Look for lines containing “Segmentation fault” or “core dumped”. If your daemon is running in a Docker container managed by GKE or Compute Engine, you’ll need to check the container logs:

# For GKE (using kubectl)
kubectl logs  -c  --tail=50

# For Compute Engine (if running directly or via systemd)
# Check journalctl as above, or application-specific log files.

Crucially, you need to ensure that core dumps are enabled on the host machine where the daemon is running. By default, they are often disabled or limited. You can check the current limits with:

ulimit -c

If this outputs ‘0’, core dumps are disabled. To enable them for the current session (and potentially for a service if configured correctly), you can run:

ulimit -c unlimited

For persistent changes, especially for services managed by systemd, you’ll need to modify the service unit file. Find the unit file (e.g., `/etc/systemd/system/your-daemon.service`) and add or modify the `LimitCore` directive:

[Service]
# ... other directives
LimitCore=infinity
# ...

After modifying the unit file, reload systemd and restart your service:

sudo systemctl daemon-reload
sudo systemctl restart your-daemon.service

You also need to configure where core dumps are saved. The `kernel.core_pattern` sysctl parameter controls this. A common setup is to pipe the core dump to a handler that compresses and saves it, often with process information.

# Check current pattern
sysctl kernel.core_pattern

# Example: Save to /var/crash/core.%e.%p.%t
# This might require installing a package like 'systemd-coredump' or 'abrt'
# For manual setup, you might pipe to a script:
sudo sysctl -w kernel.core_pattern="|/usr/bin/gzip -c > /var/crash/core.%e.%p.%t.gz"

Ensure the directory specified (e.g., `/var/crash/`) exists and has appropriate write permissions for the user running your daemon.

Analyzing the Core Dump with GDB

Once a core dump file is generated (e.g., `core.your-daemon.12345.gz`), the next step is to analyze it using the GNU Debugger (GDB). If the core dump is compressed, you’ll need to decompress it first or use GDB’s ability to handle compressed files if available (though explicit decompression is often more reliable).

# If compressed
gunzip core.your-daemon.12345.gz
mv core.your-daemon.12345 core.your-daemon.12345

# Load the core dump with the executable
gdb /path/to/your-daemon /path/to/core.your-daemon.12345

Inside GDB, the first command to run is `bt` (backtrace) to see the call stack at the moment of the crash. For multi-threaded applications, you’ll want to see the backtrace for all threads:

(gdb) thread apply all bt

This will show you the execution path for every thread. Look for the thread that triggered the segmentation fault. It’s often indicated by a specific signal (e.g., `SIGSEGV`). The output will pinpoint the exact function and line number where the crash occurred. For example:

#0  0x00007f1234567890 in some_function (arg1=0x0) at /path/to/source/file.c:123
#1  0x00007f1234567abc in another_function (arg2=...) at /path/to/source/file.c:234
#2  0x00007f1234567def in <signal handler called>

In this example, the crash happened in `some_function` at line 123 of `file.c`. The `0x0` argument often indicates a null pointer dereference, a very common cause of segfaults.

To inspect the state of variables at the crash site, switch to the crashing thread (if not already selected) and examine variables:

(gdb) thread   # e.g., thread 1
(gdb) frame  # e.g., frame 0
(gdb) info locals
(gdb) print variable_name
(gdb) x/xg &variable_name # Examine memory at address

Pay close attention to pointers. If a pointer is `0x0` (NULL) or points to an invalid memory address (e.g., `0xcccccccc` or `0xdddddddd` in uninitialized memory, or addresses outside your process’s allocated space), that’s a strong clue.

Common Causes and Debugging Strategies

Segmentation faults in multi-threaded C/C++ applications often stem from specific patterns:

  • Null Pointer Dereference: Accessing memory through a pointer that is `NULL`. This is frequently seen when a function returns `NULL` on error, and the caller doesn’t check the return value before using it.
  • Dangling Pointers: Using a pointer after the memory it points to has been freed or has gone out of scope. This is particularly tricky in multi-threaded environments where one thread might free memory that another thread is still using.
  • Buffer Overflows/Underflows: Writing past the allocated bounds of an array or buffer. This can corrupt adjacent memory, including control structures or other data, leading to a crash later.
  • Stack Overflow: Excessive recursion or very large local variables can exhaust the stack space allocated to a thread.
  • Data Races: While not directly causing a segfault, data races can lead to corrupted data structures. If a corrupted pointer is later dereferenced, it can result in a segfault.
  • Use-After-Free: Similar to dangling pointers, but specifically when memory is deallocated and then accessed again.
  • Uninitialized Memory: Using memory that hasn’t been initialized can lead to unpredictable behavior, including using garbage pointer values.

To combat these, consider these strategies:

  • Compile with Debug Symbols and Sanitizers: Always compile your daemon with `-g` for debug symbols. More importantly, use AddressSanitizer (ASan) and ThreadSanitizer (TSan) during development and testing. These tools can detect memory errors and data races at runtime with relatively low overhead.
# Example GCC/Clang compilation flags
g++ -g -fsanitize=address -fno-omit-frame-pointer -o your-daemon your-daemon.cpp -pthread
g++ -g -fsanitize=thread -fno-omit-frame-pointer -o your-daemon your-daemon.cpp -pthread

When ASan or TSan detect an error, they provide detailed reports directly to stderr, often including stack traces and memory access information, which are invaluable for pinpointing the issue without needing a core dump.

  • Static Analysis Tools: Tools like `cppcheck`, `clang-tidy`, or commercial options can identify potential issues like uninitialized variables, null pointer dereferences, and memory leaks before runtime.
  • Code Reviews: Thorough code reviews, especially focusing on pointer usage, memory management, and thread synchronization, are critical.
  • Defensive Programming: Always check return values from functions that can fail (e.g., `malloc`, `fopen`, network calls). Validate pointers before dereferencing them.
  • Memory Allocator Debugging: Tools like Valgrind (though potentially slow for daemons) or the built-in ASan can help detect memory corruption.
  • Logging: Implement robust logging within your application. Log critical events, pointer values before dereferencing, and state changes. This can help reconstruct the sequence of events leading to the crash.

GCP Specific Considerations

When running on GCP, several factors can influence debugging:

  • Managed Services (e.g., GKE, Cloud Run): In these environments, you often don’t have direct access to the host OS to configure `ulimit` or `kernel.core_pattern`. You’ll rely more heavily on application-level logging and the sanitizers mentioned earlier. For GKE, you might need to configure the container runtime or Kubernetes itself to enable core dumps within the container, which can be complex. Often, it’s easier to capture logs and use sanitizers.
  • Compute Engine Instances: On GCE, you have full control over the VM. Ensure your instance has sufficient disk space for core dumps. Consider using Google Cloud Logging and Cloud Monitoring to collect logs and metrics, which can help correlate crashes with resource utilization or other system events.
  • Ephemeral Environments: If your daemon runs in short-lived instances or containers, ensure that core dumps are uploaded to a persistent storage location (e.g., a Cloud Storage bucket) before the instance is terminated. A startup script or a systemd service that runs `coredumpctl` or a custom handler can achieve this.
  • Resource Limits: High CPU or memory usage can sometimes trigger unexpected behavior or race conditions. Monitor your daemon’s performance using Cloud Monitoring.

For instance, if your daemon is crashing under heavy load, it might be a sign of a data race exacerbated by increased thread contention. TSan would be your best bet here.

Advanced GDB Techniques for Multi-threaded Crashes

When `bt` isn’t enough, consider these GDB commands:

  • `info threads`: Lists all threads and indicates the current thread.
  • `thread `: Switches the context to a specific thread.
  • `thread apply all bt`: As mentioned, crucial for seeing all call stacks.
  • `set print thread-events off`: Suppresses messages about threads starting/stopping, which can clutter output.
  • `set follow-fork-mode child`: If your daemon forks, this tells GDB to follow the child process.
  • `catch signal SIGSEGV`: Tells GDB to stop execution when a SIGSEGV signal is received, even if it’s not the one causing the crash directly.
  • `set max-string-length `: Useful for printing string variables without truncation.
  • `set max-array-length `: Useful for printing array contents.

If the crash occurs within a library (e.g., glibc, OpenSSL), ensure you have the debug symbols for that library installed on the debugging host. On Debian/Ubuntu systems, this often involves installing packages like `libc6-dbg`.

Finally, remember that analyzing core dumps requires the exact executable that generated the dump. If your application is deployed via containers or automated build pipelines, ensure you have access to the precise binary version that crashed.

Primary Sidebar

A little about the Author

Having 12+ Years of Experience in Software Development, Vinay is a principal software architect, senior systems engineer, and elite technical consultant. He specializes in bespoke PHP/WordPress development, high-performance Magento 2 & Shopify architectures, custom plugin/theme development from scratch, and legacy code modernization (including VB6, VB.NET, PyQt, and Crystal Reports). Known for solving complex database bottlenecks, speed optimization (Core Web Vitals), and advanced security code auditing, Vinay engineers production-ready systems designed to scale under heavy concurrent load conditions.



Chat on WhatsApp

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability
  • Scala Pekko vs. Go Goroutines: Actor Model vs. CSP for Event-Driven Reactive Systems
  • Java Loom Virtual Threads vs. Go Goroutines: Under-the-Hood Scheduler and Thread Overhead Comparison

Categories

  • apache (1)
  • Business & Monetization (390)
  • Centos (4)
  • Comparisons & Decision Making (55)
  • Debian (2)
  • Debugging & Troubleshooting (584)
  • Desktop Applications (14)
  • DevOps (7)
  • DevOps & Cloud Scaling (962)
  • Django (1)
  • Laravel (4)
  • Migration & Architecture (192)
  • Mobile Applications (24)
  • MySQL (1)
  • Performance & Optimization (806)
  • PHP (5)
  • PHP Development (21)
  • Plugins & Themes (244)
  • Programming Languages (9)
  • Python (19)
  • Ruby on Rails (1)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Server (23)
  • Ubuntu (9)
  • VB6 & VB.NET (8)
  • Web Applications & Frontend (19)
  • Web Assembly (Wasm) (2)
  • WordPress (22)
  • WordPress Plugin Development (7)
  • WordPress Theme Development (357)

Recent Posts

  • Go Goroutines vs. Node.js Event Loop: Scaling I/O-Bound Microservices Under High Load
  • Elixir Phoenix vs. Go Gin: Concurrency Models and Fault Tolerance Under Peak Request Volume
  • Python Celery vs. Go Channels: Distributed Task Queue Overhead and Memory Reliability

Top Categories

  • DevOps & Cloud Scaling (962)
  • Performance & Optimization (806)
  • Debugging & Troubleshooting (584)
  • Security & Compliance (543)
  • SEO & Growth (491)
  • Business & Monetization (390)

Our Products

  • ERP & LMS Systems (4)
  • Directories & Marketplaces (4)
  • Healthcare Portals (3)
  • Point of Sale (POS) (2)
  • E-Commerce Engines (2)

Our Services

  • E-Commerce Development (10)
  • WordPress Development (8)
  • Python & Desktop GUI (7)
  • General Consulting (7)
  • Legacy Modernization (5)
  • Mobile App Development (4)

Copyright © 2026 · Vinay Vengala