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