TL;DR: This script collects hardware and OS info for reporting bugs in Linux games because, on Linux, the functionality of DxDiag is split up across several different tools.
The Humble Indie Bundle 6 just came out and, wanting Torchlight, I bought it. Now, like any early adopter, I did run into a bug (it’s already been fixed). This isn’t about that, though.
When I read the README.linux file, it asked me to gather a bunch of diagnostic information and, being the distractible geek I am, I ended up writing a script to automate it.
…I went above and beyond the call and expanded it into something more like a console version of the Windows DxDiag utility. (Which will also open a terminal for you if you double-click it) So, if you ever need to submit a bug report for a Linux game, run this script. It’ll gather all your hardware and system configuration in one easy-to-read, easy-to-attach file.
Note: I’m still working on figuring out what kind of data I should collect for debugging audio and input issues. If you have any suggestions, please leave a comment.
In case you don’t feel like scrolling past the embed or you’re not used to working with embeds from GitHub Gists, here’s the direct download link.
#!/bin/sh
# dash-Compatible Linux Configuration Dumper for Troubleshooting Games
# (Sort of like DxDiag but for Linux)
#
# Get and contribute updates to this script at: https://gist.github.com/3759156
#
# NOTE: Please test changes on as many of the following shells as possible:
# dash, bash, pdksh, busybox, ash, zsh
#
# TODO:
# - Confirm that the presence or absence of S3 Texture Compression support is
# included in existing read-outs. If feasible, list it separately for easy
# recognition by users should the developers ask them to check.
# - Linking and crash logs:
# - Provide support for taking the relative path to the game binary (both
# hard-coded and as an argument)
# - If the game binary is provided, include the binary format (eg. ELF64) and
# data about the system's supported binary formats in the readout.
# - If the game binary is provided, include the ldd output in the log
# - Support a --log-crash flag which causes it to also run the game under gdb
# and automatically collect the output of "bt full".
# - Compositing and window management:
# - Find a way to detect whether a compositor like Compiz is running
# 1. log into KDE 4
# 2. (xrandr -q -v ; xdpyinfo -queryExtensions -ext all; glxinfo -t -v -l) 2>&1 1>log.txt`
# 3. toggle compositing, log them again, and diff the logs to see if
# anything reliable shows up.
# - Find a generic mechanism for detecting the active WM (eg. How does
# fusion-icon do it?)
# - Design tests for whether Compiz and Kwin are set to suspend compositing
# for fullscreen windows.
# - Audio:
# - A list of audio devices (input and output, waveform, midi, and mixers)
# - Which audio devices are default?
# - Are the PulseAudio, OSS4, JACK, etc. daemons installed?
# - Are the PulseAudio, JACK, CUSE ossproxy, etc. daemons running?
# - A list of installed libpulse/libjack/etc. to clarify ld and dlopen behavior
# (locate libpulse | fgrep .so | xargs ls -l)
# - Use lsof or equivalent to check what's hanging on the soundcard endpoints
# - Is pasuspender installed?
# - Is another application using pasuspender to block the sound card?
# - What else can cause audio issues?
# - Input Devices:
# - List of connected Joystics
# - List of XInput devices
# - Find a way to detect off-calibration joysticks which could cause in-game
# menus to cycle wildly. (Known problem with some USB-PS2 controller bridges
# and at least the official Windows drivers for 3DConnexion Space Navigators)
# - Look into whether any other kind of input debugging data would be
# worth gathering
# - Decide what network-related data would be useful.
# - Compare my output to Steam's output as shown at
# http://phoronix.com/forums/showthread.php?p=369297#post369297
#
# Known Bugs:
# - busybox's free is ignorant of memory used for opportunistic disk caching.
# - The version of busybox included with the OpenPandora misunderstands
# the command `df -h /`
#
# Copyright (c) 2012 Stephan Sokolow
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to
# deal in the Software without restriction, including without limitation the
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
# Edit these as appropriate to your specific debugging needs
DUMP_FILE=~/game_troubleshooting_dump.txt
REQUIRE_CMDS="glxinfo"
# ---=== Respawn self early if necessary ===---
# Since we don't read from stdin anyway, it's reasonable to assume that, if
# it's not a TTY, the user must need a terminal window to see our output.
# (As opposed to being intentionally piped or redirected by the user)
if [ ! -t 0 ]; then
for CMD in x-terminal-emulator urxvt uxterm rxvt xterm; do
if type "$CMD" 1>/dev/null 2>&1; then
exec "$CMD" -e "$0" --wait
fi
done
fi
# Redirect stdout to the dump file to avoid repetitive output redirects
exec > "$DUMP_FILE"
# ---=== Function Declarations ===---
posix_check() {
if ! type "$1" 1>/dev/null 2>&1; then
echo "ERROR: Your system is either broken or not POSIX-compatible." 1>&2
echo " Please run this in a shell that provides the $1 command." 1>&2
exit 1
fi
}
prefer_cmd() {
if ! type "$1" 1>/dev/null 2>&1; then
printf "$3 $1 missing.\n"
printf "WARNING: %s is not installed. $3\n" "$1" 1>&2
printf " (It is often found in a package with a name like '%s')\n\n" "$2" 1>&2
local REQUIRE_CMDS=" $REQUIRE_CMDS "
case $REQUIRE_CMDS in *" $1 "*)
printf "ERROR: The data gathered by "$1" is required. Please install it and try again.\n\n" 1>&2
exit 2
;;
esac
return 1
fi
}
#FIXME: This breaks on the version of BusyBox provided by the OpenPandora
# (because they misunderstand `df -h /` to match only dev nodes)
# Implement some kind of line-counting and condense into one awk expr.
# http://www.catonmat.net/blog/awk-one-liners-explained-part-one/
# http://mywiki.wooledge.org/BashFAQ/094
show_free() {
if [ -z "$2" ]; then
name="$1"
else
name="$2"
fi
# The "grep %" strips off the long device paths that busybox or bad luck
# can use to word-wrap the rows.
# The first awk ensures the second awk will count fields properly if
# word-wrap occurred.
fspace="`LC_ALL=C df -h \"$1\" | grep % | awk '{ getline; printf \"x%s\", $0 }' | awk '{ print $4 }'`"
# We need the "grep %" so long device paths like those in busybox won't
# break things when they trigger word-wrap
printf "%-20s: %s\n" "$name" "$fspace"
}
# Check for basic system sanity to be ridiculously thorough
posix_check type
posix_check printf
posix_check cat
posix_check grep
posix_check sed
posix_check awk
# Get the OS name and platform architecture strings since they're not included
# in the kernel version
if prefer_cmd uname coreutils "Cannot read platform string. This system may be very broken or not Linux."; then
printf "Platform:\n %s %s\n\n" "`uname -o`" "`uname -m`"
fi
# Get the kernel version (an all-around useful thing to know)
printf "Kernel Version:\n"
cat /proc/version | sed 's@^@ @'
printf "\n=== distro release version ===\n\n"
if type lsb_release 1>/dev/null 2>&1; then
lsb_release -a 2>/dev/null
elif [ -e /etc/lsb-release ]; then
cat /etc/lsb-release
else
printf "WARNING: No Linux Standards Base release metadata found.\n" 1>&2
printf " Verify the distro name and version before submitting.\n\n" 1>&2
grep '' /etc/issue /etc/*_release /etc/*-release /etc/*_version /etc/*-version 2>|/dev/null
fi
printf "\n=== desktop environment ===\n\n"
printf "DESKTOP_SESSION : %s\n" "$DESKTOP_SESSION"
printf "XDG_CURRENT_DESKTOP : %s\n" "$XDG_CURRENT_DESKTOP"
printf "\n=== SDL environment ===\n\n"
set | grep -a SDL_
# Get the CPU model in case it's incompatible with the build options
printf "\n=== cpu information (per core) ===\n\n"
if [ -e /proc/cpuinfo ]; then
cpuinfo=`cat /proc/cpuinfo | grep -E '^(processor|model name|flags|cpu MHz|$)'`
if [ -z "$cpuinfo" ]; then
cat /proc/cpuinfo
printf "\n"
else
printf "%s\n\n" "$cpuinfo"
fi
else
printf "ERROR: /proc/cpuinfo not found! (Non-Linux platform?)\n\n"
printf "WARNING: /proc/cpuinfo not found! This script only supports Linux.\n" 1>&2
fi
if [ -e /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies ]; then (
printf 'CPU Frequency Scaling:\n'
cd /sys/devices/system/cpu
for CPU in cpu*; do
(
# Skip folders not corresponding to individual CPUs
# (Can't use continue here in pdksh)
if [ -e "$CPU/cpufreq" ]; then
cd "$CPU/cpufreq"
printf " $CPU:\n"
grep '' scaling_min_freq scaling_max_freq scaling_governor | sed 's@^@ @'
fi
)
done )
printf '\n'
else
printf "Unable to query CPU frequency scaling settings\n\n"
fi
# Get the memory stats in case the user is experiencing an OOM-related crash
# TODO: Find some way to detect busybox and warn about the +/- buffers issue
if prefer_cmd free procps "This system may be very broken or not Linux."; then
printf "=== memory information (in megabytes) ===\n\n"
free -m
elif [ -e /proc/meminfo ]; then
printf "=== memory information (fallback) ===\n\n"
cat /proc/meminfo | grep -E '^(MemTotal|MemFree|Buffers|Cached|SwapTotal|SwapFree)'
else
printf "=== memory information ===\n\n"
printf "Unable to retrieve memory statistics (Non-Linux platform?)\n\n"
printf "ERROR: Neither /usr/bin/free nor /proc/meminfo exist!\n" 1>&2
fi
# Record the free disk space in places known to cause death by failure to write
printf "\n=== free disk space ===\n\n"
show_free /
show_free /tmp
show_free /var/tmp
show_free /var/log
show_free ~ "User Home Directory"
show_free "$0" "Game Directory"
printf "\n"
# Try to look up the video driver used by X.org
if [ -e /var/log/Xorg.0.log ]; then
printf "=== X.org Drivers Used ===\n"
# Source: http://askubuntu.com/a/28037
x_drivers=`egrep -i " connected|card detect|primary dev|rgb weight|default visual is" /var/log/Xorg.0.log | sed 's@^@ @'`
if [ -z "$x_drivers" ]; then
continue
fi
printf "%s" "$x_drivers"
printf '\n\n'
elif type glxinfo 1>/dev/null 2>&1; then
printf "=== X.org Drivers Used (glxinfo) ===\n"
glxinfo | grep -i vendor
printf '\n'
printf "WARNING: Detection of in-use video drivers is vague. /var/log/Xorg.0.log is missing!\n" 1>&2
else
printf "Unable to detect in-use video drivers. /var/log/Xorg.0.log does not exist.\n\n"
printf "WARNING: Unable to detect in-use video drivers. glxinfo and /var/log/Xorg.0.log are missing!\n" 1>&2
fi
if prefer_cmd lspci pciutils "Unable to identify video card model and kernel driver."; then
printf "=== video card details ===\n\n"
for X in `lspci | grep VGA | cut -d' ' -f1`; do
lspci -v -s "$X" | grep -E '(VGA|Subsystem|Kernel|^$)'
done
fi
if prefer_cmd xrandr x11-server-utils "Unable to detect available monitor resolutions."; then
printf "=== available monitor resolutions ===\n\n"
xrandr 2>/dev/null
fi
if prefer_cmd xdpyinfo x11-utils "Unable to check for Xinerama (multi-monitor) data."; then
printf "\nXinerama Info:\n"
xdpyinfo -ext XINERAMA 2>/dev/null | grep head | sed 's@^@ @'
fi
if prefer_cmd glxinfo mesa-utils "Unable to dump OpenGL data for debugging 2D/3D acceleration."; then
printf "\n=== glxinfo output ===\n\n"
glxinfo
fi
# Weird, misconfigured locale settings have been known to cause problems
printf "\n=== language and localization ===\n\n"
if type locale 1>/dev/null 2>&1; then
locale
else
set | grep -a LANG
set | grep -a LC_
fi
printf "\n=== diagnostic script info ===\n\n"
printf "Novice-safe shell script for gathering data to report bugs in Linux games\n\n"
printf "Source:\n https://gist.github.com/3759156\n"
# XXX: If you know another robust way to pin down this script's version, I'm all ears.
if type sha1sum 1>/dev/null 2>&1; then
printf "Version Hash (SHA1):\n %s\n" "`sha1sum \"$0\" | cut -d' ' -f1`"
elif type md5sum 1>/dev/null 2>&1; then
printf "Version Hash (MD5):\n %s\n" "`md5sum \"$0\" | cut -d' ' -f1`"
else
printf " Unable to identify script version\n"
printf " WARNING: You are advised to re-run this script with sha1sum or md5sum installed so it can note its own version.\n" 1>&2
fi
printf '\nThis Report Generated: %s' "`date -R`"
printf "Data gathered. Please attach this file to your bug report:\n %s\n" "$DUMP_FILE" 1>&2
# In case we exec'd ourself into a terminal
if [ "$1" = "--wait" ]; then
printf '\nPress Enter to close this window... ' 1>&2
read throwaway
fi
# vim: set noexpandtab sw=4 sts=4
…sort of like DxDiag for Linux by Stephan Sokolow is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License .
By submitting a comment here you grant this site a perpetual license to reproduce your words and name/web site in attribution under the same terms as the associated post.
All comments are moderated. If your comment is generic enough to apply to any post, it will be assumed to be spam. Borderline comments will have their URL field erased before being approved.