[RFC] lscpu - CPU architecture information helper

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

 



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,

$ /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
1,3,15,3,3,3,15,15
2,3,15,3,3,3,15,15
4,12,15,12,12,12,15,15
8,12,15,12,12,12,15,15
16,48,240,48,48,48,240,240
32,48,240,48,48,48,240,240
64,192,240,192,192,192,240,240
128,192,240,192,192,192,240,240

Therefore, cache arrangement is like the following,
 _______________________________________________________________
|      |       |            |     |     |      |       |        |
| cpu0 |       |            |     |     |      |       |        |
|______| core0 |            | L1d | L1i | L2d  |       |        |
|      |       |            | 16K | 16K | 256K |       |        |
| cpu1 |       |            |     |     |      |       |        |
|______|_______| processor0 |_____|_____|______| L2i   | L3     |
|      |       |            |     |     |      | 1024K | 12288K |
| cpu2 |       |            |     |     |      |       |        |
|______| core1 |            | L1d | L1i | L2d  |       |        |
|      |       |            | 16K | 16K | 256K |       |        |
| cpu3 |       |            |     |     |      |       |        |
|______|_______|____________|_____|_____|______|_______|________|
|      |       |            |     |     |      |       |        |
| cpu4 |       |            |     |     |      |       |        |
|______| core2 |            | L1d | L1i | L2d  |       |        |
|      |       |            | 16K | 16K | 256K |       |        |
| cpu5 |       |            |     |     |      |       |        |
|______|_______| processor1 |_____|_____|______| L2i   | L3     |                    
|      |       |            |     |     |      | 1024K | 12288K |
| cpu6 |       |            |     |     |      |       |        |
|______| core3 |            | L1d | L1i | L2d  |       |        |
|      |       |            | 16K | 16K | 256K |       |        |
| cpu7 |       |            |     |     |      |       |        |
|______|_______|____________|_____|_____|______|_______|________|

The program has been tested successfully on around 40 different i386,
x86_64 and ia64 machines. You could find the program in attachment.

Thanks,
CaiQian
#!/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}")
    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}")
    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
        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

<<< Multipart/Mixed; boundary="--BOUNDARY1(Fri_Jul__4_11_23_10_2008_773)--": Unrecognized >>>

[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