JAT - Just A Testing library https://pagure.io/shellfu-bash-jat

jat_dump.sh.skel 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #!/bin/bash
  2. shellfu import jat
  3. shellfu import pretty
  4. #
  5. # Helpers for dumping files to log
  6. #
  7. # This module provides several functions to enable showing
  8. # contents of (supposedly small) files or command outputs directly
  9. # inside JAT log.
  10. #
  11. # This can be very helpful, especially if you know you are dealing
  12. # with relatively small files or to provide generic diagnostic
  13. # data.
  14. #
  15. # For example:
  16. #
  17. # some_command >out 2>err
  18. # jat__eval "grep foo out"
  19. # jat__eval "grep bar out" 1
  20. # jat__eval "test -s err" 1
  21. # jat_dump__file out err
  22. #
  23. # Here, if your asserts fail, it will be very useful to see the content
  24. # right in the log.
  25. #
  26. # Another example (from real life):
  27. #
  28. # rlPhaseStartTest infodump
  29. # ps -C cimserver -o euser,ruser,suser,fuser,f,comm,label \
  30. # | jat_dump__pipe PS_SECINFO
  31. # ps -C cimserver -f \
  32. # | jat_dump__pipe PS_F
  33. # ps -C cimserver -Lf \
  34. # | jat_dump__pipe PS_THREADS
  35. # ps -ef \
  36. # | jat_dump__pipe PS_EF
  37. # jat_dump__file /etc/passwd /etc/group
  38. # rlPhaseEnd
  39. #
  40. # Here, in one phase -- without getting in the way of other code
  41. # we can get reasonable profile of the cimserver process and
  42. # basic system setup.
  43. #
  44. #
  45. # Pipe via hexdump before printing?
  46. #
  47. # `always` to enforce all files through hexdump, `never` to disable
  48. # hexdump, `auto` to have the filetype detected.
  49. #
  50. JAT_DUMP__HEXMODE=${JAT_DUMP__HEXMODE:-auto}
  51. #
  52. # Command to use for hex dumping
  53. #
  54. JAT_DUMP__HEXCMD=${JAT_DUMP__HEXCMD:-hexdump -C}
  55. #
  56. # Limit in lines before jat__submit() is used instead of printing
  57. #
  58. JAT_DUMP__LIMIT_L=${JAT_DUMP__LIMIT_L:-100}
  59. #
  60. # Limit in bytes before jat__submit() is used instead of printing
  61. #
  62. JAT_DUMP__LIMIT_B=${JAT_DUMP__LIMIT_B:-10000}
  63. #
  64. # Function to use for logging
  65. #
  66. JAT_DUMP__DMPFUN=${JAT_DUMP__DMPFUN:-jat__log_info}
  67. jat_dump__file() {
  68. #
  69. # Dump contents of given file(s) using jat__log_info
  70. #
  71. # Usage:
  72. #
  73. # jat_dump__file [options] FILE...
  74. #
  75. # Description:
  76. #
  77. # Print contents of given file(s) to the main TESTOUT.log
  78. # using jat_log* functions and adding BEGIN/END delimiters.
  79. # If a file is too big, use jat__submit() instead. If a file
  80. # is binary, pipe it through `hexdump -C`.
  81. #
  82. # Options:
  83. #
  84. # * -h, --hex
  85. # * -H, --no-hex
  86. # enforce or ban hexdump for all files
  87. # * -b BYTES, --max-bytes BYTES
  88. # * -l LINES, --max-lines LINES
  89. # set limit to lines or bytes; if file is bigger,
  90. # use jat__submit() instead
  91. # * -I, --info
  92. # * -E, --error
  93. # * -D, --debug
  94. # use different level of jat_log* (Info by default)
  95. # * -s, --skip-empty
  96. # do not do anything if file is empty
  97. # * -S, --skip-missing
  98. # do not do anything if file is missing
  99. #
  100. local hexmode=$JAT_DUMP__HEXMODE
  101. local limit_b=$JAT_DUMP__LIMIT_B
  102. local limit_l=$JAT_DUMP__LIMIT_L
  103. local dmpfun="$JAT_DUMP__DMPFUN"
  104. local skip_empty=false
  105. local skip_missing=false
  106. while true; do case "$1" in
  107. -h|--hex) hexmode=always; shift 1 ;;
  108. -H|--no-hex) hexmode=never; shift 1 ;;
  109. -b|--max-bytes) limit_b=$2; shift 2 ;;
  110. -l|--max-lines) limit_l=$2; shift 2 ;;
  111. -I|--info) dmpfun=jat__log_info; shift 1 ;;
  112. -E|--error) dmpfun=jat__log_error; shift 1 ;;
  113. -D|--debug) dmpfun=debug; shift 1 ;;
  114. -s|--skip-empty) skip_empty=true; shift 1 ;;
  115. -S|--skip-missing) skip_missing=true; shift 1 ;;
  116. *) break ;;
  117. esac done
  118. local fpath # path to original file
  119. local hexdump # path to hexdumped file, if needed
  120. for fpath in "$@";
  121. do
  122. # skipping logic
  123. ! test -e "$fpath" && $skip_missing && continue
  124. test -e "$fpath" || { jat__log_error "no such file: $fpath"; continue; }
  125. test -f "$fpath" || { jat__log_error "not a file: $fpath"; continue; }
  126. ! test -s "$fpath" && $skip_empty && continue
  127. if __jat_dump__use_hex "$hexmode" "$fpath";
  128. then
  129. hexdump=$(__jat_dump__mkhexdump < "$fpath")
  130. __jat_dump__logobj HEXDUMP "$hexdump" "$fpath" "$fpath"
  131. rm "$hexdump"
  132. else
  133. __jat_dump__logobj FILE "$fpath" "$fpath"
  134. fi
  135. done
  136. }
  137. jat_dump__mkphase() {
  138. #
  139. # Make a dump phase incl. JAT formalities
  140. #
  141. # Usage:
  142. # jat_dump__mkphase [-n NAME] FILE...
  143. #
  144. # Create a separate diag phase called NAME ('dump' by
  145. # default) and dump all listed files there.
  146. #
  147. local name="dump" # phase name, should you not like dump
  148. while true; do case "$1" in
  149. -n|--name) name=$2; shift 2 ;;
  150. *) break ;;
  151. esac done
  152. jat__pstartd "$name"
  153. jat_dump__file "$@"
  154. jat__pend
  155. }
  156. jat_dump__pipe() {
  157. #
  158. # Dump contents passed to stdin using jat__log_info
  159. #
  160. # Usage:
  161. #
  162. # some_command | jat_dump__pipe [options] [NAME]
  163. #
  164. # Description:
  165. #
  166. # Cache contents of stream given on STDIN and print them to
  167. # the main TESTOUT.log using jat__log* functions and adding
  168. # BEGIN/END delimiters. If the content is too big, use
  169. # jat__submit() instead. If the content is binary, pipe it
  170. # through `hexdump -C`.
  171. #
  172. # NAME will appear along with pipe content delimiters, and
  173. # if a limit is reached, will be used as name for file to
  174. # store using jat__submit(). NAME will be generated if
  175. # omitted.
  176. #
  177. # Options:
  178. #
  179. # * -h, --hex
  180. # * -H, --no-hex
  181. # enforce or ban hexdump for all files
  182. # * -b BYTES, --max-bytes BYTES
  183. # * -l LINES, --max-lines LINES
  184. # set limit to lines or bytes; if file is bigger,
  185. # use jat__submit() instead
  186. # * -I, --info
  187. # * -E, --error
  188. # * -D, --debug
  189. # use different level of jat__log* (Info by default)
  190. #
  191. local hexmode=$JAT_DUMP__HEXMODE
  192. local limit_b=$JAT_DUMP__LIMIT_B
  193. local limit_l=$JAT_DUMP__LIMIT_L
  194. local dmpfun="$JAT_DUMP__DMPFUN"
  195. local cache
  196. while true; do case "$1" in
  197. -h|--hex) hexmode=always; shift 1 ;;
  198. -H|--no-hex) hexmode=never; shift 1 ;;
  199. -b|--max-bytes) limit_b=$2; shift 2 ;;
  200. -l|--max-lines) limit_l=$2; shift 2 ;;
  201. -I|--info) dmpfun=jat__log_info; shift 1 ;;
  202. -E|--error) dmpfun=jat__log_error; shift 1 ;;
  203. -D|--debug) dmpfun=debug; shift 1 ;;
  204. *) break ;;
  205. esac done
  206. local name="$1"
  207. # cache the stream
  208. cache=$(mktemp -t jat_dump-cache.XXXXXXXXXX)
  209. cat > "$cache"
  210. # make up name if not given (re-use XXXXXXXXXX from $cache)
  211. test -n "$name" || name="ANONYMOUS.$$.${cache##*.}"
  212. if __jat_dump__use_hex "$hexmode" "$cache";
  213. then
  214. hexdump=$(__jat_dump__mkhexdump < "$cache")
  215. __jat_dump__logobj HEXDUMP "$hexdump" "$name" "$cache"
  216. rm "$hexdump"
  217. else
  218. __jat_dump__logobj PIPE "$cache" "$name"
  219. fi
  220. rm "$cache"
  221. }
  222. ## ## starts behind this strip ##
  223. ## museum of INTERNAL ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
  224. ## '''''''' ## no admission; you can steal but not touch anything ##
  225. __jat_dump__count() {
  226. #
  227. # Count sizes, compare to limits and print problem report if any
  228. #
  229. #inherits: limit_l limit_b
  230. local file="$1"
  231. local sizes size_b size_l
  232. local over_b over_l many
  233. sizes="$(wc -lc <"$file")" # [newline] [bytes]
  234. sizes="$(eval "echo $sizes")" # squash + trim whites
  235. size_b="${sizes##* }"
  236. size_l="${sizes%% *}"
  237. test "$size_b" -gt "$limit_b"; over_b=$?
  238. test "$size_l" -gt "$limit_l"; over_l=$?
  239. case "$over_b:$over_l" in
  240. 1:1) many="" ;;
  241. 1:0) many="lines ($size_l)" ;;
  242. 0:1) many="bytes ($size_b)" ;;
  243. 0:0) many="lines ($size_l) and bytes ($size_b)" ;;
  244. *) jat__log_error "panic: ${FUNCNAME[1]}" ;;
  245. esac
  246. echo "$many"
  247. test -z "$many"
  248. }
  249. __jat_dump__is_binary() {
  250. #
  251. # Check if file is binary
  252. #
  253. local path="$1"
  254. test -s "$path" || return 1
  255. grep -qI . "$path" && return 1
  256. return 0
  257. }
  258. __jat_dump__logobj() {
  259. #
  260. # Dump the object type $1 from $2, or save it under name $3
  261. #
  262. # Type can be FILE, HEXDUMP or PIPE. If object is too big to dump
  263. # to log, it will be submitted using jat__submit(). in that case
  264. # $3 may be specified as alternative name.
  265. #
  266. # In case of HEXDUMP, path to original file must be specified
  267. # as $4, since in case file is too big, we want to submitt *both*
  268. # the original and the hexdump (suffixed with .hex).
  269. #
  270. # FIXME: no way of dumping HEXDUMP without supplying alternate name
  271. #
  272. # In case of PIPE that needs to be submitted, suffiix ".pipe" is added
  273. # to the filename.
  274. #
  275. # FIXME: Account for PIPE that gets HEXDUMPed (now be treated as file)
  276. #
  277. local otype=$1 # PIPE|HEXDUMP|FILE
  278. local opath=$2 # printable data
  279. local oname=${3:-$opath} # chosen name
  280. local oorig=$4 # original file in case of HEXDUMP
  281. local bloat="" # human description of limit excess; empty
  282. # .. means OK to dump to log; else use jat__submit()
  283. local llines=() # logged lines
  284. oname="$(tr / - <<<"$oname")"
  285. bloat="$(__jat_dump__count "$opath")"
  286. debug "${FUNCNAME[1]}:otype='$otype'"
  287. debug "${FUNCNAME[1]}:opath='$opath'"
  288. debug "${FUNCNAME[1]}:oname='$oname'"
  289. debug "${FUNCNAME[1]}:oorig='$oorig'"
  290. debug "${FUNCNAME[1]}:bloat='$bloat'"
  291. # start outputting
  292. if test -z "$bloat";
  293. then
  294. test -s "$opath" || {
  295. case $dmpfun in
  296. debug) debug "=====EMPTY $otype $oname=====" ;;
  297. *) jat__log_info "=====EMPTY $otype $oname=====" ;;
  298. esac
  299. return
  300. }
  301. {
  302. echo "=====BEGIN $otype $oname====="
  303. cat "$opath"
  304. test -n "$(tail -c 1 "$opath")" \
  305. && echo "=====NO_NEWLINE_AT_EOF====="
  306. echo "=====END $otype $oname====="
  307. } \
  308. | {
  309. readarray -t llines
  310. $dmpfun "${llines[@]}"
  311. }
  312. else
  313. case $otype in
  314. FILE)
  315. jat__log_info "not dumping, file has too many $bloat: $opath"
  316. jat__submit "$opath" "$oname"
  317. ;;
  318. PIPE)
  319. jat__log_info "not dumping, pipe has too many $bloat: $oname"
  320. jat__submit "$opath" "$oname.pipe"
  321. ;;
  322. HEXDUMP)
  323. jat__log_info "not dumping, hexdump is too long for: $oorig"
  324. jat__submit "$opath" "$oname.hex"
  325. jat__submit "$oorig" "$oname"
  326. ;;
  327. esac
  328. fi
  329. }
  330. __jat_dump__mkhexdump() {
  331. #
  332. # Create hexdump (as tempfile) from stdin and echo path to it
  333. #
  334. local hdtmp
  335. hdtmp=$(mktemp -t jat_dump-hex.XXXXXXXXXX)
  336. $JAT_DUMP__HEXCMD > "$hdtmp"
  337. echo "$hdtmp"
  338. }
  339. __jat_dump__use_hex() {
  340. #
  341. # True if we need to use hexdump
  342. #
  343. local hexmode="$1"
  344. local fpath="$2"
  345. case "$hexmode" in
  346. always) return 0 ;;
  347. never) return 1 ;;
  348. auto) __jat_dump__is_binary "$fpath"; return $? ;;
  349. *) jat__log_error "bad value of JAT_DUMP__HEXMODE: $hexmode" ;;
  350. esac
  351. }
  352. #shellfu module-version=__MKIT_PROJ_VERSION__