Bash parsing print affinity
I have the following function:
affinity ()
{
local tid TASKSET CORE;
for tid in $(ps -efL | awk '/foo.*d/ {print $4}');
do
TASKSET=($( taskset -pc $tid | awk -F"[' ]" '{print $2,$NF}' ));
CORE[${TASKSET[1]}]="${CORE["${TASKSET[1]}"]} ${TASKSET[0]}";
done;
for i in "${!CORE[@]}";
do
printf "Core %-2s:%2s\n" "$i" "${CORE[$i]}";
done
}
It usually prints something like this:
$ affinity
Core 5 : 2545
Core 9 : 14621 14640
Core 10: 1056
Core 11: 1081
I noticed that it works well until I ran into a server with more than 1 cpu returned taskset
per thread, which breaks this script /
$ taskset -pc 14471
pid 14471 current affinity list: 0-3
$ taskset -pc 14621
pid 14621 current affinity list: 4,5
$ affinity
-bash: CORE: bad array subscript
-bash: CORE[${TASKSET[1]}]: bad array subscript
How would you advise me to improve this script to handle such a case?
More details:
$ taskset
taskset (util-linux-ng 2.17.2)
usage: taskset [options] [mask | cpu-list] [pid | cmd [args...]]
set or get the affinity of a process
-p, --pid operate on existing given pid
-c, --cpu-list display and specify cpus in list format
-h, --help display this help
-V, --version output version information
The default behavior is to run a new command:
taskset 03 sshd -b 1024
You can retrieve the mask of an existing task:
taskset -p 700
Or set it:
taskset -p 03 700
List format uses a comma-separated list instead of a mask:
taskset -pc 0,3,7-11 700
Ranges in list format can take a stride argument:
e.g. 0-31:2 is equivalent to mask 0x55555555
As for linux type, this is a custom dist:
$ uname -a
Linux MySrv 2.6.32-358.23.2.61.foo.x86_64 #1 SMP PREEMPT Wed Jun 18 06:59:23 CDT 2014 x86_64 x86_64 x86_64 GNU/Linux
thank
Solution: After combining all the great hints and answers below, this is what I ended up with: This also handles the output 0,1,4-6 which the original question was skipped.
affinity ()
{
local ii tid CORE CORES_STR CORES_ARR;
for tid in $(ps -Leo tid,command | awk '/[f]oo.*d/ {print $1}');
do
CORES_STR=($(taskset -pc $tid | awk '{gsub(/,/," ",$NF);print $NF}'));
CORES_ARR=();
for ii in ${CORES_STR[@]};
do
[[ $ii =~ - ]] && CORES_ARR+=($(eval echo {${ii/-/..}})) || CORES_ARR+=($ii);
done;
for ii in ${CORES_ARR[@]};
do
CORE[${ii}]="${CORE[${ii}]} $tid";
done;
done;
for ii in "${!CORE[@]}";
do
printf "Core %-2d:%2s\n" "$ii" "${CORE[$ii]}";
done
}
source to share
Include a range analyzer in your function to identify all kernels in the range. Here's an example of getting all cores in an array for a range. Then let's move on to this to add the process to all cores.
#!/bin/bash
range=0,3,7-11
cores=()
while read part; do
if [[ $part =~ - ]]; then
cores+=($(seq ${part/-/ }))
else
cores+=($part)
fi
done < <( echo $range | tr ',' '\n' )
for core in ${cores[*]}; do
echo $core
done
Taking a cue from @bishop this can be somewhat simplified with the eval and bash extension, for example:
range=0,3,7-11
cores=()
IFS=',' read -ra parts <<<$range
for part in "${parts[@]}"; do
[[ $part =~ - ]] && cores+=($(eval echo {${part/-/..}})) || cores+=($part)
done
source to share
$ tasklist -pc 2420 14370 17654
pid 2420 current affinity list: 0-1
pid 14370 current affinity list: 0,2-4
pid 17654 current affinity list: 2-5,9,11,13-15
$ affinity_for_tid 2420 14370 17654
Core 0 : 2420 14370
Core 1 : 2420
Core 2 : 17654 14370
Core 3 : 17654 14370
Core 4 : 17654 14370
Core 5 : 17654
Core 9 : 17654
Core 11: 17654
Core 13: 17654
Core 14: 17654
Core 15: 17654
function affinity_for_tid() {
declare -A map
# give me every line from tasklist
while read -r line; do
# break it up into an array
# array[0] === the tid, as a number; array[1] === the stripe
IFS=' '
local array=( $(awk '{print strtonum($2) " " $6}' <<<"$line") )
# convert the stripe of form like "0,2-4" to "{0,{2..4}}"
# so that shell brace expansion can deal with it
local parts=(), stripe=()
IFS=',' read -ra parts <<<"${array[1]}"
for part in "${parts[@]}"; do
if [[ $part == *-* ]]; then
stripe+=("{${part//-/..}}")
else
stripe+=("$part")
fi
done
# expand
local expansion=$(IFS=,; echo "${stripe[*]}")
if [[ $expansion == *,* ]]; then
expansion="{$expansion}"
fi
local expanded=$(eval echo "${expansion}")
# put the expansion into the map
for cpu in $expanded; do
map[$cpu]="${map[$cpu]} ${array[0]}"
done
done < <(tasklist -pc "@")
# output as desired
for cpu in "${!map[@]}"; do
printf 'Core %-2d:%s\n' "$cpu" "${map[$cpu]}"
done | sort -t ' ' -k 2n
}
At the center, this converts the striped output taskset
to a bash extension. Thus, "0.2-4" becomes "{0, {2..4}}". The rest is just glue for moving data around, which could probably be simplified.
source to share
I was able to extend my code with a range parser. (before seeing the + zerodiff answer :))
affinity ()
{
CORE=()
for tid in $(ps -efL | awk '/[f]oo.*d/ {print $4}');
do
CPU=$(taskset -pc $tid | awk -F"[' ]" '{print $NF}' );
if [[ $CPU =~ "-" ]]; then
for ii in $(seq ${CPU/-/ });
do
CORE[${ii}]="${CORE[${ii}]} $tid";
done;
else
if [[ $CPU =~ "," ]]; then
for ii in ${CPU/,/ };
do
CORE[${ii}]="${CORE[${ii}]} $tid";
done;
else
CORE[${CPU}]="${CORE[${CPU}]} $tid";
fi;
fi;
done;
for i in "${!CORE[@]}";
do
printf "Core %-2s:%2s\n" "$i" "${CORE[$i]}";
done
}
+ zerodiff code looks cleaner. I'll try to enable it tomorrow :)
source to share