12f4d9adbSJeremy L Thompson#!/bin/bash 22f4d9adbSJeremy L Thompson 32f4d9adbSJeremy L Thompson# Copyright (c) 2017-2018, Lawrence Livermore National Security, LLC. 42f4d9adbSJeremy L Thompson# Produced at the Lawrence Livermore National Laboratory. LLNL-CODE-734707. 52f4d9adbSJeremy L Thompson# All Rights reserved. See files LICENSE and NOTICE for details. 62f4d9adbSJeremy L Thompson# 72f4d9adbSJeremy L Thompson# This file is part of CEED, a collection of benchmarks, miniapps, software 82f4d9adbSJeremy L Thompson# libraries and APIs for efficient high-order finite element and spectral 92f4d9adbSJeremy L Thompson# element discretizations for exascale applications. For more information and 102f4d9adbSJeremy L Thompson# source code availability see http://github.com/ceed. 112f4d9adbSJeremy L Thompson# 122f4d9adbSJeremy L Thompson# The CEED research is supported by the Exascale Computing Project 17-SC-20-SC, 132f4d9adbSJeremy L Thompson# a collaborative effort of two U.S. Department of Energy organizations (Office 142f4d9adbSJeremy L Thompson# of Science and the National Nuclear Security Administration) responsible for 152f4d9adbSJeremy L Thompson# the planning and preparation of a capable exascale ecosystem, including 162f4d9adbSJeremy L Thompson# software, applications, hardware, advanced system engineering and early 172f4d9adbSJeremy L Thompson# testbed platforms, in support of the nation's exascale computing imperative. 182f4d9adbSJeremy L Thompson 192f4d9adbSJeremy L Thompsonthis_file="${BASH_SOURCE[0]}" 202f4d9adbSJeremy L Thompsonif [[ "${#BASH_ARGV[@]}" -ne "$#" ]]; then 212f4d9adbSJeremy L Thompson script_is_sourced="yes" 222f4d9adbSJeremy L Thompson exit_cmd=return 232f4d9adbSJeremy L Thompsonelse 242f4d9adbSJeremy L Thompson script_is_sourced="" 252f4d9adbSJeremy L Thompson exit_cmd=exit 262f4d9adbSJeremy L Thompsonfi 272f4d9adbSJeremy L Thompsontest_file="" 282f4d9adbSJeremy L Thompsonbackend_list="/cpu/self" 290c59ef15SJeremy L Thompsonbp_list="bp1 bp3" 302f4d9adbSJeremy L Thompsonrun="" 312f4d9adbSJeremy L Thompsonnum_proc_run=${num_proc_run:-""} 322f4d9adbSJeremy L Thompsonnum_proc_node=${num_proc_node:-""} 332f4d9adbSJeremy L Thompsondry_run="" # empty string = NO 342f4d9adbSJeremy L Thompsonstart_shell="" 352f4d9adbSJeremy L Thompsonverbose="" 362f4d9adbSJeremy L Thompsoncur_dir="$PWD" 372f4d9adbSJeremy L Thompson 382f4d9adbSJeremy L Thompsonmpiexec="mpirun" 392f4d9adbSJeremy L Thompsonmpiexec_np="-np" 402f4d9adbSJeremy L Thompsonmpiexec_opts="" 412f4d9adbSJeremy L Thompsonmpiexec_post_opts="" 422f4d9adbSJeremy L Thompsonprofiler="" 432f4d9adbSJeremy L Thompson 442f4d9adbSJeremy L Thompsonfunction abspath() 452f4d9adbSJeremy L Thompson{ 462f4d9adbSJeremy L Thompson local outvar="$1" path="$2" cur_dir="$PWD" 472f4d9adbSJeremy L Thompson cd "$path" && path="$PWD" && cd "$cur_dir" && eval "$outvar=\"$path\"" 482f4d9adbSJeremy L Thompson} 492f4d9adbSJeremy L Thompson 502f4d9adbSJeremy L Thompsonabspath root_dir ".." || $exit_cmd 1 512f4d9adbSJeremy L Thompsonbuild_root="$root_dir/build" 522f4d9adbSJeremy L Thompson 532f4d9adbSJeremy L Thompsonif [[ -t 1 ]]; then 542f4d9adbSJeremy L Thompson # ANSI color codes 552f4d9adbSJeremy L Thompson none=$'\E[0m' 562f4d9adbSJeremy L Thompson red=$'\E[0;31m' 572f4d9adbSJeremy L Thompson green=$'\E[0;32m' 582f4d9adbSJeremy L Thompson yellow=$'\E[0;33m' 592f4d9adbSJeremy L Thompson blue=$'\E[0;34m' 602f4d9adbSJeremy L Thompson bblue=$'\E[1;34m' 612f4d9adbSJeremy L Thompson magenta=$'\E[0;35m' 622f4d9adbSJeremy L Thompson cyan=$'\E[0;36m' 632f4d9adbSJeremy L Thompson clear="$(tput sgr0)" 642f4d9adbSJeremy L Thompsonfi 652f4d9adbSJeremy L Thompson 662f4d9adbSJeremy L Thompsonhelp_msg=" 672f4d9adbSJeremy L Thompson$this_file [options] 682f4d9adbSJeremy L Thompson 692f4d9adbSJeremy L ThompsonOptions: 702f4d9adbSJeremy L Thompson -h|--help print this usage information and exit 710c59ef15SJeremy L Thompson -b|--bp \"list\" choose the benchmark problems to run 722f4d9adbSJeremy L Thompson -c|--ceed \"list\" choose the libCEED backends to benchmark 732f4d9adbSJeremy L Thompson -r|--run <name> run the tests in the script <name> 742f4d9adbSJeremy L Thompson -n|--num-proc \"list\" total number of MPI tasks to use in the tests 752f4d9adbSJeremy L Thompson -p|--proc-node \"list\" number of MPI tasks per node to use in the tests 762f4d9adbSJeremy L Thompson -d|--dry-run show (but do not run) the commands for the tests 772f4d9adbSJeremy L Thompson -s|--shell execute bash shell commands before running the test 782f4d9adbSJeremy L Thompson -v|--verbose print additional messages 792f4d9adbSJeremy L Thompson -x enable script tracing with 'set -x' 802f4d9adbSJeremy L Thompson var=value define shell variables; evaluated with 'eval' 812f4d9adbSJeremy L Thompson 822f4d9adbSJeremy L ThompsonThis script builds and runs a set of benchmarks for a list of specified 832f4d9adbSJeremy L Thompsonbackends. 842f4d9adbSJeremy L Thompson 852f4d9adbSJeremy L ThompsonExample usage: 86*cb32e2e7SValeria Barra $this_file --run petsc-bpsraw.sh 872f4d9adbSJeremy L Thompson" 882f4d9adbSJeremy L Thompson 892f4d9adbSJeremy L Thompson 902f4d9adbSJeremy L Thompsonfunction build_examples() 912f4d9adbSJeremy L Thompson{ 922f4d9adbSJeremy L Thompson for example; do 932f4d9adbSJeremy L Thompson # We require the examples to be already built because we do not know what 942f4d9adbSJeremy L Thompson # options to use when building the library + examples. 952f4d9adbSJeremy L Thompson if [ ! -e $build_root/$example ]; then 962f4d9adbSJeremy L Thompson echo "Error: example is not built: $example" 972f4d9adbSJeremy L Thompson return 1 982f4d9adbSJeremy L Thompson fi 992f4d9adbSJeremy L Thompson done 1002f4d9adbSJeremy L Thompson} 1012f4d9adbSJeremy L Thompson 1022f4d9adbSJeremy L Thompson 1032f4d9adbSJeremy L Thompsonfunction compose_mpi_run_command() 1042f4d9adbSJeremy L Thompson{ 1052f4d9adbSJeremy L Thompson mpi_run="${mpiexec:-mpirun} ${mpiexec_opts}" 1062f4d9adbSJeremy L Thompson mpi_run+=" ${mpiexec_np:--np} ${num_proc_run} ${mpiexec_post_opts}" 1072f4d9adbSJeremy L Thompson if [[ -n "$profiler" ]]; then 1082f4d9adbSJeremy L Thompson mpi_run+=" $profiler" 1092f4d9adbSJeremy L Thompson fi 1102f4d9adbSJeremy L Thompson} 1112f4d9adbSJeremy L Thompson 1122f4d9adbSJeremy L Thompson 1132f4d9adbSJeremy L Thompsonfunction quoted_echo() 1142f4d9adbSJeremy L Thompson{ 1152f4d9adbSJeremy L Thompson local arg= string= 1162f4d9adbSJeremy L Thompson for arg; do 1172f4d9adbSJeremy L Thompson if [[ -z "${arg##* *}" ]]; then 1182f4d9adbSJeremy L Thompson string+=" \"${arg//\"/\\\"}\"" 1192f4d9adbSJeremy L Thompson else 1202f4d9adbSJeremy L Thompson string+=" $arg" 1212f4d9adbSJeremy L Thompson fi 1222f4d9adbSJeremy L Thompson done 1232f4d9adbSJeremy L Thompson printf "%s\n" "${string# }" 1242f4d9adbSJeremy L Thompson} 1252f4d9adbSJeremy L Thompson 1262f4d9adbSJeremy L Thompson 1272f4d9adbSJeremy L Thompsonfunction set_num_nodes() 1282f4d9adbSJeremy L Thompson{ 1292f4d9adbSJeremy L Thompson if [[ -n "$num_proc_node" ]]; then 1302f4d9adbSJeremy L Thompson ((num_proc_run % num_proc_node != 0)) && { 1312f4d9adbSJeremy L Thompson echo "The total number of tasks ($num_proc_run) must be a multiple of" 1322f4d9adbSJeremy L Thompson echo "the number of tasks per node ($num_proc_node). Stop." 1332f4d9adbSJeremy L Thompson return 1 1342f4d9adbSJeremy L Thompson } 1352f4d9adbSJeremy L Thompson ((num_nodes = num_proc_run / num_proc_node)) 1362f4d9adbSJeremy L Thompson else 1372f4d9adbSJeremy L Thompson num_proc_node="unknown number of" 1382f4d9adbSJeremy L Thompson num_nodes="" 1392f4d9adbSJeremy L Thompson fi 1402f4d9adbSJeremy L Thompson echo "Running the tests using a total of $num_proc_run MPI tasks ..." | tee -a $output_file 1412f4d9adbSJeremy L Thompson echo "... with $num_proc_node tasks per node ..." | tee -a $output_file 1422f4d9adbSJeremy L Thompson echo | tee -a $output_file 1432f4d9adbSJeremy L Thompson} 1442f4d9adbSJeremy L Thompson 1452f4d9adbSJeremy L Thompson 1462f4d9adbSJeremy L Thompson### Process command line parameters 1472f4d9adbSJeremy L Thompson 1482f4d9adbSJeremy L Thompsonwhile [ $# -gt 0 ]; do 1492f4d9adbSJeremy L Thompson 1502f4d9adbSJeremy L Thompsoncase "$1" in 1512f4d9adbSJeremy L Thompson -h|--help) 1522f4d9adbSJeremy L Thompson # Echo usage information 1532f4d9adbSJeremy L Thompson echo "$help_msg" 1542f4d9adbSJeremy L Thompson $exit_cmd 1552f4d9adbSJeremy L Thompson ;; 1560c59ef15SJeremy L Thompson -b|--bp) 1570c59ef15SJeremy L Thompson shift 1580c59ef15SJeremy L Thompson [ $# -gt 0 ] || { 1590c59ef15SJeremy L Thompson echo "Missing \"list\" in --bp \"list\""; $exit_cmd 1; } 1600c59ef15SJeremy L Thompson bp_list="$1" 1610c59ef15SJeremy L Thompson ;; 1622f4d9adbSJeremy L Thompson -c|--ceed) 1632f4d9adbSJeremy L Thompson shift 1642f4d9adbSJeremy L Thompson [ $# -gt 0 ] || { 1652f4d9adbSJeremy L Thompson echo "Missing \"list\" in --ceed \"list\""; $exit_cmd 1; } 1662f4d9adbSJeremy L Thompson backend_list="$1" 1672f4d9adbSJeremy L Thompson ;; 1682f4d9adbSJeremy L Thompson -r|--run) 1692f4d9adbSJeremy L Thompson run=on 1702f4d9adbSJeremy L Thompson shift 1712f4d9adbSJeremy L Thompson [ $# -gt 0 ] || { echo "Missing <name> in --run <name>"; $exit_cmd 1; } 1722f4d9adbSJeremy L Thompson test_file="$1" 1732f4d9adbSJeremy L Thompson [[ -r "$test_file" ]] || { 1742f4d9adbSJeremy L Thompson echo "Test script not found: '$1'"; $exit_cmd 1 1752f4d9adbSJeremy L Thompson } 1762f4d9adbSJeremy L Thompson ;; 1772f4d9adbSJeremy L Thompson -n|--num-proc) 1782f4d9adbSJeremy L Thompson shift 1792f4d9adbSJeremy L Thompson [ $# -gt 0 ] || { 1802f4d9adbSJeremy L Thompson echo "Missing \"list\" in --num-proc \"list\""; $exit_cmd 1; } 1812f4d9adbSJeremy L Thompson num_proc_run="$1" 1822f4d9adbSJeremy L Thompson ;; 1832f4d9adbSJeremy L Thompson -p|--proc-node) 1842f4d9adbSJeremy L Thompson shift 1852f4d9adbSJeremy L Thompson [ $# -gt 0 ] || { 1862f4d9adbSJeremy L Thompson echo "Missing \"list\" in --proc-node \"list\""; $exit_cmd 1; } 1872f4d9adbSJeremy L Thompson num_proc_node="$1" 1882f4d9adbSJeremy L Thompson ;; 1892f4d9adbSJeremy L Thompson -d|--dry-run) 1902f4d9adbSJeremy L Thompson dry_run="quoted_echo" 1912f4d9adbSJeremy L Thompson ;; 1922f4d9adbSJeremy L Thompson -s|--shell) 1932f4d9adbSJeremy L Thompson start_shell="yes" 1942f4d9adbSJeremy L Thompson ;; 1952f4d9adbSJeremy L Thompson -v|--verbose) 1962f4d9adbSJeremy L Thompson verbose="yes" 1972f4d9adbSJeremy L Thompson ;; 1982f4d9adbSJeremy L Thompson -x) 1992f4d9adbSJeremy L Thompson set -x 2002f4d9adbSJeremy L Thompson ;; 2012f4d9adbSJeremy L Thompson *=*) 2022f4d9adbSJeremy L Thompson eval "$1" || { echo "Error evaluating argument: $1"; $exit_cmd 1; } 2032f4d9adbSJeremy L Thompson ;; 2042f4d9adbSJeremy L Thompson *) 2052f4d9adbSJeremy L Thompson echo "Unknown option: '$1'" 2062f4d9adbSJeremy L Thompson $exit_cmd 1 2072f4d9adbSJeremy L Thompson ;; 2082f4d9adbSJeremy L Thompsonesac 2092f4d9adbSJeremy L Thompson 2102f4d9adbSJeremy L Thompsonshift 2112f4d9adbSJeremy L Thompsondone # while ... 2122f4d9adbSJeremy L Thompson# Done processing command line parameters 2132f4d9adbSJeremy L Thompson 2142f4d9adbSJeremy L Thompson 2152f4d9adbSJeremy L Thompsonnum_proc_list=(${num_proc_run:-4}) 2162f4d9adbSJeremy L Thompsonnum_proc_list_size=${#num_proc_list[@]} 2172f4d9adbSJeremy L Thompsonnum_proc_node_list=(${num_proc_node:-4}) 2182f4d9adbSJeremy L Thompsonnum_proc_node_list_size=${#num_proc_node_list[@]} 2192f4d9adbSJeremy L Thompson(( num_proc_list_size != num_proc_node_list_size )) && { 2202f4d9adbSJeremy L Thompson echo " 2212f4d9adbSJeremy L ThompsonThe size of the number-of-processors list (option --num-proc) must be the same 2222f4d9adbSJeremy L Thompsonas the size of the number-of-processors-per-node list (option --proc-node)." 2232f4d9adbSJeremy L Thompson echo 2242f4d9adbSJeremy L Thompson $exit_cmd 1 2252f4d9adbSJeremy L Thompson} 2262f4d9adbSJeremy L Thompson 2270c59ef15SJeremy L Thompson### Loop over BPs 2280c59ef15SJeremy L Thompson 2290c59ef15SJeremy L Thompsonfor bp in $bp_list; do 2302f4d9adbSJeremy L Thompson 2312f4d9adbSJeremy L Thompson### Loop over backends 2322f4d9adbSJeremy L Thompson 2332f4d9adbSJeremy L Thompsonfor backend in $backend_list; do 2342f4d9adbSJeremy L Thompson( ## Run each backend in its own environment 2352f4d9adbSJeremy L Thompson 2362f4d9adbSJeremy L Thompson### Setup output 2372f4d9adbSJeremy L Thompson### Test name 2382f4d9adbSJeremy L Thompsoncd "$cur_dir" 2392f4d9adbSJeremy L Thompsonabspath test_dir "$(dirname "$test_file")" || $exit_cmd 1 2402f4d9adbSJeremy L Thompsontest_basename="$(basename "$test_file")" 2412f4d9adbSJeremy L Thompsontest_file="${test_dir}/${test_basename}" 2422f4d9adbSJeremy L Thompson### Backend name 2432f4d9adbSJeremy L Thompsonshort_backend=${backend//[\/]} 2442f4d9adbSJeremy L Thompson### Output file 2450c59ef15SJeremy L Thompsonoutput_file="${test_file%%.*}-$bp-$short_backend-output.txt" 2462f4d9adbSJeremy L Thompsonrm -rf output_file 2472f4d9adbSJeremy L Thompson 2482f4d9adbSJeremy L Thompson### Setup the environment based on $backend 2492f4d9adbSJeremy L Thompson 2502f4d9adbSJeremy L Thompsonecho 2512f4d9adbSJeremy L Thompsonecho "Using backend $backend ..." | tee $output_file 2522f4d9adbSJeremy L Thompson 2532f4d9adbSJeremy L Thompson### Run the tests (building and running $test_file) 2542f4d9adbSJeremy L Thompson 2552f4d9adbSJeremy L Thompson[ -n "$run" ] && { 2562f4d9adbSJeremy L Thompson 2572f4d9adbSJeremy L Thompson[[ "$verbose" = "yes" ]] && { 2582f4d9adbSJeremy L Thompson echo "Test problem file, $test_basename:" | tee -a $output_file 2592f4d9adbSJeremy L Thompson echo "------------------------------------------------" | tee -a $output_file 2602f4d9adbSJeremy L Thompson cat $test_file | tee -a $output_file 2612f4d9adbSJeremy L Thompson echo "------------------------------------------------" | tee -a $output_file 2622f4d9adbSJeremy L Thompson echo | tee -a $output_file 2632f4d9adbSJeremy L Thompson} 2642f4d9adbSJeremy L Thompson 2652f4d9adbSJeremy L Thompsontest_exe_dir="$build_root" 2662f4d9adbSJeremy L Thompson 2672f4d9adbSJeremy L Thompsontrap 'printf "\nScript interrupted.\n"; '$exit_cmd' 33' INT 2682f4d9adbSJeremy L Thompson 2692f4d9adbSJeremy L Thompson## Source the test script file. 2702f4d9adbSJeremy L Thompsonecho "Reading test file: $test_file" | tee -a $output_file 2712f4d9adbSJeremy L Thompsonecho | tee -a $output_file 2722f4d9adbSJeremy L Thompsontest_required_examples="" 2732f4d9adbSJeremy L Thompson. "$test_file" || $exit_cmd 1 2742f4d9adbSJeremy L Thompson 2752f4d9adbSJeremy L Thompson## Build files required by the test 2762f4d9adbSJeremy L Thompsonecho "Example(s) required by the test: $test_required_examples" | tee -a $output_file 2772f4d9adbSJeremy L Thompsonbuild_examples $test_required_examples || $exit_cmd 1 2782f4d9adbSJeremy L Thompsonecho | tee -a $output_file 2792f4d9adbSJeremy L Thompson 2802f4d9adbSJeremy L Thompson## Loop over the number-of-processors list. 2812f4d9adbSJeremy L Thompsonfor (( num_proc_idx = 0; num_proc_idx < num_proc_list_size; num_proc_idx++ )) 2822f4d9adbSJeremy L Thompsondo 2832f4d9adbSJeremy L Thompson 2842f4d9adbSJeremy L Thompsonnum_proc_run="${num_proc_list[$num_proc_idx]}" 2852f4d9adbSJeremy L Thompsonnum_proc_node="${num_proc_node_list[$num_proc_idx]}" 2862f4d9adbSJeremy L Thompson 2872f4d9adbSJeremy L Thompsonset_num_nodes || $exit_cmd 1 2882f4d9adbSJeremy L Thompsoncompose_mpi_run_command 2892f4d9adbSJeremy L Thompson 2902f4d9adbSJeremy L Thompsonif [[ "$start_shell" = "yes" ]]; then 2912f4d9adbSJeremy L Thompson if [[ ! -t 1 ]]; then 2922f4d9adbSJeremy L Thompson echo "Standard output is not a terminal. Stop." | tee -a $output_file 2932f4d9adbSJeremy L Thompson $exit_cmd 1 2942f4d9adbSJeremy L Thompson fi 2952f4d9adbSJeremy L Thompson echo "Reading shell commands, type 'c' to continue, 'exit' to stop ..." | tee -a $output_file 2962f4d9adbSJeremy L Thompson echo | tee -a $output_file 2972f4d9adbSJeremy L Thompson cd "$cur_dir" 2982f4d9adbSJeremy L Thompson set -o emacs 2992f4d9adbSJeremy L Thompson PS1='$ ' 3002f4d9adbSJeremy L Thompson [[ -r $HOME/.bashrc ]] && source $HOME/.bashrc 3012f4d9adbSJeremy L Thompson HISTFILE="$root_dir/.bash_history" 3022f4d9adbSJeremy L Thompson history -c 3032f4d9adbSJeremy L Thompson history -r 3042f4d9adbSJeremy L Thompson # bind '"\\C-i": menu-complete' 3052f4d9adbSJeremy L Thompson alias c='break' 3062f4d9adbSJeremy L Thompson while cwd="$PWD/" cwd="${cwd#${root_dir}/}" cwd="${cwd%/}" \ 3072f4d9adbSJeremy L Thompson prompt="[${cyan}benchmarks$none:$blue$cwd$clear]\$ " && \ 3082f4d9adbSJeremy L Thompson read -p "$prompt" -e line; do 3092f4d9adbSJeremy L Thompson history -s "$line" 3102f4d9adbSJeremy L Thompson history -w 3112f4d9adbSJeremy L Thompson shopt -q -s expand_aliases 3122f4d9adbSJeremy L Thompson eval "$line" 3132f4d9adbSJeremy L Thompson shopt -q -u expand_aliases 3142f4d9adbSJeremy L Thompson done 3152f4d9adbSJeremy L Thompson [[ "${#line}" -eq 0 ]] && { echo; $exit_cmd 0; } 3162f4d9adbSJeremy L Thompson shopt -q -u expand_aliases 3172f4d9adbSJeremy L Thompson echo "Continuing ..." | tee -a $output_file 3182f4d9adbSJeremy L Thompsonfi 3192f4d9adbSJeremy L Thompson 3202f4d9adbSJeremy L Thompson# Call the function run_tests defined inside the $test_file 3212f4d9adbSJeremy L Thompsonceed=$backend 3222f4d9adbSJeremy L Thompsonif [ -z "$dry_run" ]; then 3232f4d9adbSJeremy L Thompson run_tests >> $output_file 3242f4d9adbSJeremy L Thompsonelse 3252f4d9adbSJeremy L Thompson run_tests 3262f4d9adbSJeremy L Thompsonfi 3272f4d9adbSJeremy L Thompsonecho 3282f4d9adbSJeremy L Thompson 3292f4d9adbSJeremy L Thompsondone ## End of loop over processor numbers 3302f4d9adbSJeremy L Thompson 3312f4d9adbSJeremy L Thompsontrap - INT 3322f4d9adbSJeremy L Thompson 3332f4d9adbSJeremy L Thompson} ## run is on 3342f4d9adbSJeremy L Thompson 3352f4d9adbSJeremy L Thompson$exit_cmd 0 3362f4d9adbSJeremy L Thompson 3372f4d9adbSJeremy L Thompson) || { 3382f4d9adbSJeremy L Thompson echo "Sub-shell for backend '$backend' returned error code $?. Stop." 3392f4d9adbSJeremy L Thompson $exit_cmd 1 3402f4d9adbSJeremy L Thompson} 3412f4d9adbSJeremy L Thompsondone ## Loop over $backend_list 3422f4d9adbSJeremy L Thompson 3430c59ef15SJeremy L Thompsondone ## Loop over $bp_list 3440c59ef15SJeremy L Thompson 3452f4d9adbSJeremy L Thompson 3462f4d9adbSJeremy L Thompson$exit_cmd 0 3472f4d9adbSJeremy L Thompson 348