diagnostics.sh 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/usr/bin/env bash
  2. set -e
  3. all_components=(
  4. "dmi"
  5. "dmesg"
  6. "acpidump"
  7. "acpi"
  8. "platform"
  9. "serial"
  10. "sam"
  11. "kconfig"
  12. "lspci"
  13. )
  14. # commands-line interface
  15. print_help () {
  16. pname="$(basename "${0}")"
  17. echo "${pname} - Linux Surface diagnostics tool"
  18. echo ""
  19. echo "Collects diagnostics information for Microsoft Surface devices running Linux."
  20. echo ""
  21. echo "USAGE:"
  22. echo " ${pname} [FLAGS...] COMPONENTS..."
  23. echo ""
  24. echo "FLAGS:"
  25. echo " -h, --help Prints this help message"
  26. echo " -o, --output FILE The gzip archive to write to [default: diagnostics.tar.gz]"
  27. echo ""
  28. echo "COMPONENTS:"
  29. echo " Space-separated list of components or 'full' for all components."
  30. echo ""
  31. echo " Available components are:"
  32. for c in "${all_components[@]}"; do
  33. echo " ${c}"
  34. done
  35. echo ""
  36. echo "Examples:"
  37. echo " ${pname} --help Print this help message"
  38. echo " ${pname} full Collect full suite of diagnostics"
  39. echo " ${pname} dmi acpidump Collect only DMI information and ACPI dump"
  40. }
  41. components=()
  42. archive="diagnostics.tar.gz"
  43. while [[ ${#} -gt 0 ]]; do
  44. key="${1}"
  45. case "${key}" in
  46. -h|--help)
  47. print_help
  48. exit
  49. ;;
  50. -o|--output)
  51. if [[ ${#} -lt 2 ]]; then
  52. echo "Error: Missing argument for option '${1}'"
  53. echo " Run with '--help' for more information."
  54. exit 1
  55. fi
  56. archive="${2}"
  57. if [[ "${archive}" != *".tar.gz" ]]; then
  58. echo "Error: Invalid file ending for output archive, should be '.tar.gz'."
  59. exit 1
  60. fi
  61. shift 2
  62. ;;
  63. *)
  64. components+=("${1}")
  65. shift
  66. ;;
  67. esac
  68. done
  69. # ensure some component is selected
  70. if [[ ${#components[@]} -eq 0 ]]; then
  71. echo "Error: No component selected"
  72. echo " Run with '--help' for more information."
  73. exit 1
  74. fi
  75. # handle special component names
  76. if [[ " ${components[*]} " =~ " full " ]]; then
  77. components=("${all_components[@]}")
  78. fi
  79. # validate component list
  80. for c in "${components[@]}"; do
  81. if [[ ! " ${all_components[*]} " =~ " ${c} " ]]; then
  82. echo "Error: Unknown component '${c}'"
  83. echo " Run with '--help' for more information."
  84. exit 1
  85. fi
  86. done;
  87. # set up temporary directory
  88. tmpdir=""
  89. on_exit () {
  90. # remove temporary directory if set
  91. if [[ -n "${tmpdir}" ]]; then
  92. rm -rf "${tmpdir}"
  93. fi
  94. }
  95. trap on_exit EXIT
  96. tmpdir="$(mktemp -d -t "surface-diagnostics.XXXXXX")"
  97. # collect information
  98. timestamp=$(date --iso-8601=seconds)
  99. collect_sysfs () {
  100. subsystem="${1}"
  101. if [[ -d "/sys/bus/${subsystem}/" ]]; then
  102. tree -l -L 3 "/sys/bus/${subsystem}/drivers/" > "${tmpdir}/sys-bus-${subsystem}-drivers.txt"
  103. tree -l -L 3 "/sys/bus/${subsystem}/devices/" > "${tmpdir}/sys-bus-${subsystem}-devices.txt"
  104. else
  105. echo "<subsystem not available>" > "${tmpdir}/sys-bus-${subsystem}-drivers.txt"
  106. echo "<subsystem not available>" > "${tmpdir}/sys-bus-${subsystem}-devices.txt"
  107. fi
  108. }
  109. # collect timestamp and uname
  110. echo " ==> collecting information..."
  111. echo "${timestamp}" > "${tmpdir}/timestamp"
  112. echo " - uname"
  113. uname -a > "${tmpdir}/uname"
  114. # collect DMI if specified
  115. if [[ " ${components[*]} " =~ " dmi " ]]; then
  116. echo " - DMI system information"
  117. sudo dmidecode -t system > "${tmpdir}/dmi-system.txt"
  118. fi
  119. # collect dmesg log if specified
  120. if [[ " ${components[*]} " =~ " dmesg " ]]; then
  121. echo " - dmesg log"
  122. sudo dmesg > "${tmpdir}/dmesg.log"
  123. fi
  124. # collect acpidump if specified
  125. if [[ " ${components[*]} " =~ " acpidump " ]]; then
  126. echo " - ACPI dump"
  127. sudo acpidump | perl -pe 'BEGIN{undef $/;} s|MSDM.*?\n\n||sgm' > "${tmpdir}/acpidump.txt"
  128. fi
  129. # collect acpi sysfs entries and device paths if specified
  130. if [[ " ${components[*]} " =~ " acpi " ]]; then
  131. echo " - ACPI device information and paths (sysfs)"
  132. collect_sysfs "acpi"
  133. grep ".*" "/sys/bus/acpi/devices/"*"/path" > "${tmpdir}/sys-bus-acpi-devices.paths.txt"
  134. fi
  135. # collect platform sysfs entries
  136. if [[ " ${components[*]} " =~ " platform " ]]; then
  137. echo " - platform device information (sysfs)"
  138. collect_sysfs "platform"
  139. fi
  140. # collect serial sysfs entries
  141. if [[ " ${components[*]} " =~ " serial " ]]; then
  142. echo " - serial/UART device information (sysfs)"
  143. collect_sysfs "serial"
  144. fi
  145. # collect SAM information
  146. if [[ " ${components[*]} " =~ " sam " ]]; then
  147. echo " - surface aggregator module information (sysfs)"
  148. collect_sysfs "surface_aggregator"
  149. # get SAM firmware version
  150. out="${tmpdir}/sam-firmware-version"
  151. if [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/" ]]; then
  152. echo "<MSHW0084:00 does not exist>" > "${out}"
  153. elif [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/physical_node" ]]; then
  154. echo "<MSHW0084:00 does not have a physical node>" > "${out}"
  155. elif [[ ! -d "/sys/bus/acpi/devices/MSHW0084:00/physical_node/sam" ]]; then
  156. echo "<MSHW0084:00 physical node does not have SAM attributes>" > "${out}"
  157. else
  158. cat "/sys/bus/acpi/devices/MSHW0084:00/physical_node/sam/firmware_version" > "${out}"
  159. fi
  160. fi
  161. # collect kernel config
  162. if [[ " ${components[*]} " =~ " kconfig " ]]; then
  163. echo " - kernel configuration"
  164. # attempt to load module for accessing current kernel config
  165. if modinfo configs > /dev/null 2>&1; then
  166. sudo modprobe configs
  167. fi
  168. # try to get current config from /proc, if available
  169. if [[ -f "/proc/config.gz" ]]; then
  170. zcat "/proc/config.gz" > "${tmpdir}/kernel-$(uname -r).conf"
  171. fi
  172. # try to get current config from /boot, if available
  173. if [[ -f "/boot/config" ]]; then
  174. cp "/boot/config" "${tmpdir}/kernel-$(uname -r).conf"
  175. fi
  176. # try to get any config from /boot
  177. find "/boot" -name 'config-*' -print0 | while IFS= read -r -d '' file; do
  178. name="$(basename "${file}")"
  179. cp "${file}" "${tmpdir}/kernel-${name#"config-"}.conf"
  180. done;
  181. fi
  182. # collect lspci
  183. if [[ " ${components[*]} " =~ " lspci " ]]; then
  184. echo " - kernel configuration"
  185. sudo lspci -nn -vvv > "${tmpdir}/lspci.txt"
  186. sudo lspci -nn -v -t > "${tmpdir}/lspci-tree.txt"
  187. fi
  188. # bundle to archive
  189. echo " ==> generating archive..."
  190. tar -czf "./${archive}" -C "${tmpdir}" .
  191. echo " ==> done"
  192. echo ""
  193. echo "Please post the generated '${archive}' file."