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
[Site Home] [Netdev] [Ethernet Bridging] [Linux Wireless] [Kernel Newbies] [Memory] [Security] [Linux for Hams] [Netfilter] [Bugtraq] [Rubini] [Photo] [Yosemite] [Yosemite News] [MIPS Linux] [ARM Linux] [Linux RAID] [Linux Admin] [Samba] [Video 4 Linux] [Linux Resources]