Guest User

Memtest Parser

a guest
May 23rd, 2020
241
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 9.27 KB | None | 0 0
  1. #!/bin/bash
  2.  
  3. # Written 2020 by Axel Norstedt
  4.  
  5. ################################################################################
  6. # Memtest Parser (early test version)                                          #
  7. ################################################################################
  8. # Use in conjunction with a memtest enabled kernel and the memtest=N kernel    #
  9. #  cmdline. Typical usage should be memtest=17 since it's a very trivial test  #
  10. #  otherwise and would only catch flat out dead memory cells.                  #
  11. # Depending on your distribution dmesg might require root access to access the #
  12. #  kernel boot log. In such case you can either invoke the script with sudo or #
  13. #  with the path to a user readable kernel log file. If you go the file route, #
  14. #  be careful not to include more than one memtest run as that will lead to    #
  15. #  undefined behavior.                                                         #
  16. ################################################################################
  17. # Exit status map:                                                             #
  18. # 0 Test completed, no errors found                                            #
  19. # 1 Test output could not be found                                             #
  20. # 2 Test completed but encountered errors                                      #
  21. # 3 Specified file containing kernel log not found                             #
  22. # 4 Dependency error
  23. ################################################################################
  24.  
  25. # Check dependencies
  26. if [[ ! -x $(command -v dmesg) && -z $1 ]]; then
  27.     echo "Fatal: This script requires dmesg unless a log file is specified"
  28.     exit 4
  29. fi
  30.  
  31. if [[ ! -x $(command -v sed) ]]; then
  32.     echo "Fatal: This script requires sed"
  33.     exit 4
  34. fi
  35.  
  36. #TODO
  37. # Investigate posibility of a graphical representation (SVG or bitmap?)
  38. #  ASCII-art?
  39. #   Neat trick found on "https://stackoverflow.com/questions/263890/
  40. #    how-do-i-find-the-width-height-of-a-terminal-window" to get the terminal size
  41. #    without having to use external tools like tput
  42. #     The terminal size will be available through $LINES and $COLUMNS after this
  43. #      shopt -s checkwinsize; (:); echo $LINES $COLUMNS
  44.  
  45. # Get the kernel log
  46. if [[ -z $1 ]]; then
  47.     BOOTLOG="$(dmesg)"
  48. elif [[ -f $1 ]]; then
  49.     BOOTLOG="$(cat $1)"
  50. else
  51.     echo "Fatal: File \"$1\" not found"
  52.     exit 3
  53. fi
  54.  
  55. ################################################################################
  56. # What this inline sed script does                                             #
  57. ################################################################################
  58. # Match from the line containing "early_memtest" to the next line not          #
  59. #  containing "]   ", at which point the test is over (end of indented block)  #
  60. # Remove leading timestamp string "[    0.000000] " (or similar)               #
  61. # Print the pattern space (the current line after processing)                  #
  62. ################################################################################
  63.  
  64. MT_BLOCK="$(sed -n -e '/early_memtest/,/\] [^ ]/ {
  65.                    s/\[ *[0-9]*\.[0-9]*\] // p
  66.                }' <<< "$BOOTLOG")"
  67.  
  68. if [[ -z $MT_BLOCK ]]; then
  69.     echo "No memtest output found in kernel log, wrong boot option?"
  70.     exit 1
  71. fi
  72.  
  73. #echo "$MT_BLOCK"
  74.  
  75. ################################################################################
  76. # Extract number of test patterns used for testing                             #
  77. ################################################################################
  78. # The line looks like this:                                                    #
  79. #  "early_memtest: # of tests: 17"                                             #
  80. # We want the "17"                                                             #
  81. ################################################################################
  82.  
  83. MT_TESTS="$(sed '1 s/.*early_memtest: # of tests: \([0-9]*\).*/\1/ ; q' \
  84.                <<< "$MT_BLOCK")"
  85.  
  86. #echo "$MT_TESTS"
  87.  
  88. ################################################################################
  89. # Extract test patterns used for testing                                       #
  90. ################################################################################
  91. # Each line looks like this:                                                   #
  92. #  "  0x0000000000200000 - 0x0000000002080000 pattern 4c494e5558726c7a"        #
  93. # We want the "4c494e5558726c7a"                                               #
  94. ################################################################################
  95.  
  96. MT_PATTERNS="$(sed -n 's/..* pattern \([0-9a-f]*\).*/\1/p' \
  97.                    <<< "$MT_BLOCK" | uniq)"
  98.  
  99. #echo "$MT_PATTERNS"
  100.  
  101. ################################################################################
  102. # Extract test range                                                           #
  103. ################################################################################
  104. # Each line looks like this:                                                   #
  105. #  "  0x0000000000200000 - 0x0000000002080000 pattern 4c494e5558726c7a"        #
  106. # Then a few lines further down:                                               #
  107. #  "  0x000000003ffff000 - 0x0000000040000000 pattern 4c494e5558726c7a"        #
  108. # We want the "0x0000000000200000" and the "0x0000000040000000"                #
  109. ################################################################################
  110.  
  111. # Extract the first pattern from the list
  112. MT_PATTERN="$(sed q <<< "$MT_PATTERNS")"
  113.  
  114. # Expression to match the start
  115. #  (first hexadecimal on a line containing the word "pattern")
  116. SED_EXP_S='.*0x\([0-9a-f]*\) ..* pattern ..*'
  117.  
  118. # Expression to match the end
  119. #  (last hexadecimal before the word "pattern", if it exists)
  120. SED_EXP_E='..* 0x\([0-9a-f]*\) pattern ..*'
  121.  
  122. # Initialize variables
  123. MT_START="0"
  124. MT_END="0"
  125. MT_CHUNKS=""
  126.  
  127. # Loop through the tests and extract both start and end adresses
  128. while read -r MT_TEST
  129. do
  130.     # Basic sanity check to make sure the line looks like we expecit it to
  131.     if [[ "$MT_TEST" == *" pattern "* ]]; then
  132.         LOOP_S="$(sed -n 's/'"$SED_EXP_S"'/\1/p' <<< "$MT_TEST")"
  133.         LOOP_E="$(sed -n 's/'"$SED_EXP_E"'/\1/p' <<< "$MT_TEST")"
  134.  
  135.         # Check if this is the lowest adress so far
  136.         if [[ "$LOOP_S" < "$MT_START" || "$MT_START" == "0" ]]; then
  137.             MT_START="$LOOP_S"
  138.         fi
  139.  
  140.         # Check if this is the highest adress so far
  141.         if [[ "$LOOP_E" > "$MT_END" || "$MT_END" == "0" ]]; then
  142.             MT_END="$LOOP_E"
  143.         fi
  144.  
  145.         # Finally, add the memory chunk to the list for later use
  146.         MT_CHUNKS="$MT_CHUNKS"$'\n'"$LOOP_S $LOOP_E"
  147.     fi
  148.  
  149. # Only read through one pattern, all tests should encompass the same range
  150. done <<< $(sed -n "/$MT_PATTERN/p" <<< "$MT_BLOCK")
  151.  
  152. # How many bytes of memory the test pattern was written to (HEX to DEC)
  153. MT_SIZE="$((16#$MT_END - 16#$MT_START))"
  154.  
  155. #echo "$MT_START"
  156. #echo "$MT_END"
  157. #echo "$MT_SIZE"
  158.  
  159. ################################################################################
  160. # Extract any error mesages                                                    #
  161. ################################################################################
  162. # Each line looks like this:                                                   #
  163. #  "  4c494e5558726c7a bad mem addr 0x0000000000200000 - 0x0000000002080000    #
  164. # We want the "0x0000000000200000" and the "0x0000000002080000"                #
  165. ################################################################################
  166.  
  167. MT_ERRORS=$(sed -n 's/.*bad mem addr 0x\([0-9a-f]*\).*0x\([0-9a-f]*\)/\1 \2/p' \
  168.                 <<< "$MT_BLOCK")
  169.  
  170. #echo "$MT_ERRORS"
  171.  
  172. # Initialize variables
  173. MT_ASCIIMAP=""
  174.  
  175. while read -r MT_CHUNK
  176. do
  177.     # Reset for each chunk, for interoperability reasons this is a string
  178.     MT_CHUNK_BAD="0"
  179.  
  180.     # First line of MT_CHUNK is always empty
  181.     if [[ ! -z "$MT_CHUNK" ]]; then
  182.  
  183.         # Loop through each error for further investigation
  184.         while read -r MT_ERROR
  185.         do
  186.             # Check if the error location inside the chunk
  187.             if [[ "16#${MT_ERROR%% *}" -ge "16#${MT_CHUNK%% *}" && \
  188.                     "16#${MT_ERROR##* }" -le "16#${MT_CHUNK##* }" ]]; then
  189.                 MT_CHUNK_BAD="1"
  190.             fi
  191.         done <<< "$MT_ERRORS"
  192.  
  193.         # This is where we update the error map
  194.         MT_ASCIIMAP="$MT_ASCIIMAP"$'\n'"0x${MT_CHUNK%% *} - 0x${MT_CHUNK##* } "
  195.         if [[ "$MT_CHUNK_BAD" == "1" ]]; then
  196.             MT_ASCIIMAP="$MT_ASCIIMAP BAD"
  197.         else
  198.             MT_ASCIIMAP="$MT_ASCIIMAP GOOD"
  199.         fi
  200.     fi
  201. done <<< "$MT_CHUNKS"
  202.  
  203. ################################################################################
  204. # Print out the collected statistics                                           #
  205. ################################################################################
  206.  
  207. echo "$MT_TESTS tests completed with these patterns:"
  208. echo ""
  209. echo "$MT_PATTERNS"
  210. echo ""
  211. echo "Tested memory region 0x$MT_START to 0x$MT_END" \
  212.         "($(($MT_SIZE / 1048576))MB)"
  213.  
  214. # No empty echo here because the ASCII map already contains a leading newline
  215. echo "$MT_ASCIIMAP"
  216. echo ""
  217.  
  218. # Print conclusion and exit
  219. if [[ ! -z "$MT_ERRORS" ]]; then
  220.     echo "Error(s) found"
  221.     exit 2
  222. else
  223.     echo "No errors found"
  224.     exit 0
  225. fi
Add Comment
Please, Sign In to add comment