0% found this document useful (0 votes)
9 views1 page

Using pstack for GDB Backtraces

This shell script is designed to obtain a backtrace of a specified process by its process ID. It checks if the process exists and determines whether to use a simple backtrace or a threaded backtrace based on the process's characteristics. The script then invokes GDB to execute the appropriate backtrace command and formats the output to remove unnecessary information.

Uploaded by

wz1151897402
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views1 page

Using pstack for GDB Backtraces

This shell script is designed to obtain a backtrace of a specified process by its process ID. It checks if the process exists and determines whether to use a simple backtrace or a threaded backtrace based on the process's characteristics. The script then invokes GDB to execute the appropriate backtrace command and formats the output to remove unnecessary information.

Uploaded by

wz1151897402
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd

#!

/bin/sh

if test $# -ne 1; then


echo "Usage: `basename $0 .sh` <process-id>" 1>&2
exit 1
fi

if test ! -r /proc/$1; then


echo "Process $1 not found." 1>&2
exit 1
fi

# GDB doesn't allow "thread apply all bt" when the process isn't
# threaded; need to peek at the process to determine if that or the
# simpler "bt" should be used.

backtrace="bt"
if test -d /proc/$1/task ; then
# Newer kernel; has a task/ directory.
if test `/bin/ls /proc/$1/task | /usr/bin/wc -l` -gt 1 2>/dev/null ; then
backtrace="thread apply all bt"
fi
elif test -f /proc/$1/maps ; then
# Older kernel; go by it loading libpthread.
if /bin/grep -e libpthread /proc/$1/maps > /dev/null 2>&1 ; then
backtrace="thread apply all bt"
fi
fi

GDB=${GDB:-/usr/bin/gdb}

if $GDB -nx --quiet --batch --readnever > /dev/null 2>&1; then


readnever=--readnever
else
readnever=
fi

# Run GDB, strip out unwanted noise.


$GDB --quiet $readnever -nx /proc/$1/exe $1 <<EOF 2>&1 |
set width 0
set height 0
set pagination no
$backtrace
EOF
/bin/sed -n \
-e 's/^\((gdb) \)*//' \
-e '/^#/p' \
-e '/^Thread/p'

Common questions

Powered by AI

Using 'echo' and redirection enhances user experience by providing clear, immediate feedback in cases where script conditions fail. The use of 'echo' with descriptive error messages such as 'Usage: `basename $0 .sh` <process-id>' or 'Process $1 not found.' informs the user about the correct usage or specific issues encountered. Redirecting these messages to standard error (1>&2) ensures they are distinguished from regular output, aiding prompt debugging and proper usage .

Setting the GDB environment variable allows the script to flexibly use a specific GDB binary, potentially different from the default '/usr/bin/gdb'. The script uses `${GDB:-/usr/bin/gdb}` to allow overwriting the GDB binary path for testing or execution in different environments without modifying the script directly. This flexibility can be essential in scenarios where specific GDB versions are required .

The script checks the kernel version or process properties by probing the /proc filesystem. For newer kernels with threading, indicated by the presence of a task directory with more than one entry, it chooses 'thread apply all bt'. This is checked using the command `/bin/ls /proc/$1/task | /usr/bin/wc -l`, which checks if the count is greater than one. For older kernels, it checks if the process has loaded 'libpthread' in its address space by searching in '/proc/$1/maps'; a match here also indicates threading, choosing the command 'thread apply all bt' .

The script specifies the executable and process ID for GDB by using the command `$GDB --quiet $readnever -nx /proc/$1/exe $1 ...`, where `/proc/$1/exe` points to the executable associated with the process ID, $1. This ensures that GDB attaches to the correct process using the provided information from the proc filesystem, making it contextually accurate .

The '--readnever' option is used to run GDB without reading in symbol information unless instructed explicitly. This can improve performance by avoiding unnecessary data retrieval and is particularly useful if the intention is only to acquire a simple backtrace without processing additional detailed debugging information .

The condition to exit if the number of arguments is not equal to one ensures that the script is provided exactly one process ID as an argument. This validation is crucial as the script's logic depends on having this specific input to locate and interact with the intended process within the /proc directory. It prevents execution without proper context, which could lead to errors or undefined behavior .

The script checks for the readability of the /proc/<pid> directory to ensure that the process with the given ID exists and is accessible. This serves as a precondition to prevent errors when attempting to interact with a non-existent process, leading to an early exit with a relevant error message if the check fails .

'sed' is used to filter and clean the GDB output by stripping out unwanted noise and retaining only the backtrace lines. It achieves this by using specific patterns: the expression '-e 's/^ (gdb) //*'' removes command line prompts, '/^#/p' retains lines that start with '#' which typically signify stack frames, and '/^Thread/p' captures thread information. This makes the output cleaner and more relevant .

The script redirects its output when probing the /proc/<pid>/task and /proc/<pid>/maps paths to suppress error messages from the `ls` and `grep` commands. By redirecting with 2>/dev/null and >/dev/null 2>&1, it ensures that any errors (such as file or directory not found) do not confuse the user with irrelevant or noisy output .

The script accommodates changes in kernel versions by detecting the presence of a '/proc/<pid>/task' directory or inspecting '/proc/<pid>/maps' for threading indicators. If the task directory exists and contains multiple entries, it confirms threading support in newer kernels. Alternatively, it checks for the 'libpthread' library in the mappings for older kernels. By using these methods, the script determines the appropriate GDB command to handle threaded processes, thus maintaining functionality across kernel versions .

You might also like