diagnostics.sh 6.6 KB

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