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
}

      

+3


source to share


3 answers


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

      

+2


source


$ 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.

+1


source


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 :)

0


source







All Articles