Re: [RFC] lscpu - CPU architecture information helper

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi,

2008/7/4 Cai Qian <qcai@xxxxxxxxxx>:
> Hi,
>
> I propose a new tool lscpu to util-linux-ng, This program gathers CPU
> architecture information like number of CPUs, cores, sockets, NUMA
> nodes, information about CPU caches, SMT, CPU family, model and stepping
> from sysfs and /proc/cpuinfo, and prints it in human-readable
> format. Alternatively, it can print out in parsable format including how
> different caches are shared by different CPUs, which can also be fed to
> other programs. The output is like the following,
Very nice idea. But:

>
> $ /usr/bin/lscpu
> Processor(s):    2
> CPU core(s):     2
> CPU(s):          8
> Vendor ID:       GenuineIntel
> CPU family:      Itanium 2
> Model:           0
> CPU MHz:         1598.000005
> L1d cache:       16K
> L1i cache:       16K
> L2d cache:       256K
> L2i cache:       1024K
> L3 cache:        12288K
> SMT:             Yes
> NUMA node(s):    1
>
> $ /usr/bin/lscpu -p
> #The following is the parsable format, which can be fed to other
> #programs. The decimal numbers in the first column are CPU IDs
> #calculated from the bitmaps for each CPUs. Each CPU has one bit set, so
> #the first CPU's ID is 2^0, the second's is 2^1, and so on. The rest of
> #numbers are the sum of CPU IDs to indicate CPU and cache arrangement.
> #For example, if Core has both the first and the second CPU bit set (2^0
> #+ 2^1), it means those two CPUs reside in the same CPU Core, i.e.
> #simultaneous multithreading (SMT). The same for caches, if one cache
> #has several CPUs' bits set, it means they share the same cache.
> #
> #CPU,Core,Socket,L1d,L1i,L2d,L2i,L3
What about NUMA nodes here? You get the mapping from cpu to node from
the ${syspath}/node/node*/cpu* symbolic links or from the
${syspath}/node/node*/nodemap file.

> #!/bin/bash
>
> # lscpu - CPU architecture information helper
> # Copyright (C) 2008 Cai Qian <qcai@xxxxxxxxxx>
> #
> # This program is free software: you can redistribute it and/or modify
> # it under the terms of the GNU General Public License as published by
> # the Free Software Foundation, either version 3 of the License, or
> # (at your option) any later version.
> #
> #  This program is distributed in the hope that it will be useful,
> #  but WITHOUT ANY WARRANTY; without even the implied warranty of
> #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> #  GNU General Public License for more details.
> #
> #  You should have received a copy of the GNU General Public License
> #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
>
>
> log()
> {
>    local x=$1 n=2 l=-1
>
>    if [ "$2" != "" ]; then
>        n=${x}
>        x=$2
>    fi
>
>    while((x)); do
>        let l+=1 x/=n
>    done
>    echo ${l}
> }
>
> # Calculate on number of silbings from a hexadecimal mapping.
> sibling()
> {
>    local x=$1 l=0 n=0
>
>    while [ "${x}" -ne 0 ]; do
>        l=$((l + 1))
>        n=$(log 2 "${x}")
>        x=$((x - 2**n))
>    done
>    echo ${l}
> }
>
> # Calculate on decimal number (2^0 + 2^1 + 2^2 ...) from number of
> # silbings.
> decimal()
> {
>    local i=$1 j=$2
>
>    m=${j}
>    k=$((i / j + 1))
>    sum=0
>    while [ "${j}" -ne 0 ]; do
>        sum=$((sum + base**(m * k - j)))
>        j=$((j - 1))
>    done
>
>    echo "${sum}"
> }
>
>
> usage()
> {
>    cat <<EOF
> Usage: $0 [OPTION]
> CPU architecture information helper
>
>  -p, --parse      print out in parsable instead of printable format.
>  -h, --help       usage information
> EOF
> }
>
> #=============
> # Main program
> #=============
> # Command-line parsing
> while [ $# -gt 0 ]; do
>    case "$1" in
>    -p | --parse)
>        parse=1
>        ;;
>    -h | --help)
>        usage
>        exit 0
>        ;;
>    *)
>        echo "Error: unknown option: $1" >&2
>        usage
>        exit 1
>        ;;
>    esac
>    shift
> done
>
> arch=$(uname -m)
> # Only i686, X86_64 and IA64 are supported at the moment.
> if [ "${arch}" != "x86_64" ] && [ "${arch}" != "i686" ] \
>   && [ "${arch}" != "ia64" ]; then
>    echo "Error: ${arch} is not supported." >&2
>    exit 1
> fi
>
> # Dom0 Kernel gives wrong information.
> if grep "control_d" "/proc/xen/capabilities" >/dev/null 2>&1; then
>    echo "Error: Dom0 Kernel is not supported." >&2
>    exit 1
> fi
>
> # Read through sysfs.
> syspath="/sys/devices/system"
>
> if [ ! -d "${syspath}" ]; then
>    echo "Error: sysfs is not accessable." >&2
>    exit 1
> fi
>
> if [ -d "${syspath}/cpu/cpu0/topology" ]; then
>    have_topology=1
> fi
>
> if [ -d "${syspath}/cpu/cpu0/cache" ]; then
>    have_cache=1
> fi
>
> # Number of CPUs
> cpu=$(find "${syspath}/cpu" -name 'cpu[0-9]*' -prune | wc -l)
>
> if [ "${have_topology}" ]; then
>    # Number of threads
>    hex=$(sed 's/^[0,]*//' \
>      "${syspath}/cpu/cpu0/topology/thread_siblings")
>    map=$(printf "%d\n" "0x${hex}")
This wont work for machines with more than 32 logical CPUs.

>    thread=$(sibling "${map}")
>
>    if [ ${thread} -ne 1 ]; then
>        smt="Yes"
>    else
>        smt="No"
>    fi
>
>    # Number of cores
>    hex=$(sed 's/^[0,]*//' \
>      "${syspath}/cpu/cpu0/topology/core_siblings")
>    map=$(printf "%d\n" "0x${hex}")
dito

>    core=$(( $(sibling "${map}") / thread))
>
>    # Number of CPU sockets
>    socket=$((cpu / core / thread))
> fi
>
> # Read through cpuinfo.
> vendor=$(grep -m 1 "vendor" /proc/cpuinfo | sed 's/.*: \(.*\)/\1/')
> family=$(grep -m 1 "family" /proc/cpuinfo | sed 's/.*: \(.*\)/\1/')
> model=$(grep -m 1 "model.[^n]" /proc/cpuinfo | sed 's/.*: \(.*\)/\1/')
> stepping=$(grep -m 1 "stepping" /proc/cpuinfo | sed 's/.*: \(.*\)/\1/')
> mhz=$(grep -m 1 "cpu MHz" /proc/cpuinfo | sed 's/.*: \(.*\)/\1/')
>
> # Cache information
> if [ "${have_cache}" ]; then
>    ncache=0
>    for i in $(ls "${syspath}/cpu/cpu0/cache"); do
>        ncache=$((ncache + 1))
>        level=$(cat "${syspath}/cpu/cpu0/cache/${i}/level")
>        type=$(cat "${syspath}/cpu/cpu0/cache/${i}/type")
>
>        if [ "${type}" = "Data" ]; then
>            caname[${ncache}]="L${level}d"
>        elif [ "${type}" = "Instruction" ]; then
>            caname[${ncache}]="L${level}i"
>        else
>            caname[${ncache}]="L${level}"
>        fi
>
>        camap[${ncache}]=$(sed 's/^[0,]*//' \
>          "${syspath}/cpu/cpu0/cache/${i}/shared_cpu_map")
>
>        casize[${ncache}]=$(cat "${syspath}/cpu/cpu0/cache/${i}/size")
>    done
> fi
>
> # Number of NUMA node
> if [ -d "${syspath}/node" ]; then
>    numa=$(find "${syspath}/node" -name 'node[0-9]*' -prune | wc -l)
> else
>    numa=1
> fi
>
> # Show time.
> if [ "${parse}" ]; then
>    # Parsable format
>    if [ ! "${have_topology}" ]; then
>        echo "Error: CPU topology information is unavailable." >&2
>        exit 1
>    fi
>
>    cat <<EOF
> #The following is the parsable format, which can be fed to other
> #programs. The decimal numbers in the first column are CPU IDs
> #calculated from the bitmaps for each CPUs. Each CPU has one bit set, so
> #the first CPU's ID is 2^0, the second's is 2^1, and so on. The rest of
> #numbers are the sum of CPU IDs to indicate CPU and cache arrangement.
> #For example, if Core has both the first and the second CPU bit set (2^0
> #+ 2^1), it means those two CPUs reside in the same CPU Core, i.e.
> #simultaneous multithreading (SMT). The same for caches, if one cache
> #has several CPUs' bits set, it means they share the same cache.
> EOF
>
>    # Show comments.
>    echo "#"
>    echo -n "#CPU,Core,Socket"
>    if [ "${have_cache}" ]; then
>        m=0
>        while [ "${m}" -ne "${ncache}" ]; do
>            m=$((m + 1))
>            echo -n ",${caname[${m}]}"
>        done
>    fi
>    echo
>
>    base=2
>    for i in $(seq 0 "$((cpu - 1))"); do
Whats with offlined cpus, i.e. there could be holes in the cpu/cpu* range?

>        pcpu[${i}]=$((base**i))
>        pcore[${i}]=$(decimal "${i}" "${thread}")
>        psock[${i}]=$(decimal "${i}" "$((core * thread))")
>
>        printf "%d,%d,%d" "${pcpu[${i}]}" "${pcore[${i}]}" \
>          "${psock[${i}]}"
>
>        # Cache information
>        if [ "${have_cache}" ]; then
>            m=0
>            while [ "${m}" -ne "${ncache}" ]; do
>                m=$((m + 1))
>                # If shared_cpu_map is 0, all CPUs share the same cache.
>                if [ "${camap[${m}]}" ]; then
>                    map=$(printf "%d\n" "0x${camap[${m}]}")
>                    j=$(sibling "${map}")
>                else
>                    j=$((core * thread))
>                fi
>
>                pcache=$(decimal "${i}" "${j}")
>                printf ",%d" "${pcache}"
>            done
>        fi
>        echo
>    done
> else
>    # Printable format
>    pad=17
>    if [ "${have_topology}" ]; then
>        printf "%-${pad}s%s\n" "Processor(s):" "${socket}"
>        printf "%-${pad}s%s\n" "CPU core(s):" "${core}"
>    fi
>
>    printf "%-${pad}s%s\n" "CPU(s):" "${cpu}"
>    printf "%-${pad}s%s\n" "Vendor ID:" "${vendor}"
>    printf "%-${pad}s%s\n" "CPU family:" "${family}"
>
>    if [ "${model}" ]; then
>        printf "%-${pad}s%s\n" "Model:" "${model}"
>    fi
>
>    if [ "${stepping}" ]; then
>        printf "%-${pad}s%s\n" "Stepping:" "${stepping}"
>    fi
>
>    printf "%-${pad}s%s\n" "CPU MHz:" "${mhz}"
>
>    if [ "${have_cache}" ]; then
>        i=0
>        while [ "${i}" -ne "${ncache}" ]; do
>            i=$((i + 1))
>            printf "%-${pad}s%s\n" "${caname[${i}]} cache:" "${casize[${i}]}"
>        done
>    fi
>
>    if [ "${have_topology}" ]; then
>        printf "%-${pad}s%s\n" "SMT:" "${smt}"
>    fi
>
>    printf "%-${pad}s%s\n" "NUMA node(s):" "${numa}"
> fi
>
> exit 0
>
>

Overall, still a very nice idea. But to handle >32 cpus it is probaly
better to rewrite it in C.

Regards
Bert
--
To unsubscribe from this list: send the line "unsubscribe util-linux-ng" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Netdev]     [Ethernet Bridging]     [Linux Wireless]     [Kernel Newbies]     [Security]     [Linux for Hams]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux RAID]     [Linux Admin]     [Samba]

  Powered by Linux