1#!/bin/sh
2#-*-sh-*-
3
4#
5# Copyright © 2009 CNRS
6# Copyright © 2009-2021 Inria.  All rights reserved.
7# Copyright © 2009-2012 Université Bordeaux
8# Copyright © 2014 Cisco Systems, Inc.  All rights reserved.
9# See COPYING in top-level directory.
10#
11
12HWLOC_VERSION="@HWLOC_VERSION@"
13HWLOC_top_builddir="@HWLOC_top_builddir@"
14prefix="@prefix@"
15exec_prefix="@exec_prefix@"
16bindir="@bindir@"
17localstatedir="@localstatedir@"
18runstatedir="@HWLOC_runstatedir@"
19# this will be changed into $bindir/lstopo during make install
20lstopo="$HWLOC_top_builddir/utils/lstopo/lstopo-no-graphics"
21hgcpuid="$HWLOC_top_builddir/utils/hwloc/hwloc-gather-cpuid"
22
23# make sure we use default numeric formats
24LANG=C
25LC_ALL=C
26export LANG LC_ALL
27
28# don't let ls append special chars after symlinks etc
29unalias -a ls
30
31gathercpuid=1
32gatherio=0
33gatherdmi=0
34keep=0
35
36if [ ! -x "$lstopo" ]
37then
38    error "Could not find lstopo executable in the install or build dir."
39    exit 1
40fi
41
42error()
43{
44    echo $@ 2>&1
45}
46
47usage()
48{
49   echo `basename $0`" [options] <savepath>"
50   echo "  Saves the Linux topology files (/sys, /proc, ...) under <savepath>.tar.bz2"
51   echo "  and the corresponding lstopo verbose output under <savepath>.output"
52   echo "Options:"
53   echo "  --io        Gather I/O files (takes much longer and generates much larger tarball)"
54   echo "  --dmi       Gather SMBIOS files. Works only when run as root. Requires dmi-sysfs kernel module"
55   echo "  --no-cpuid  Do not gather x86 CPUID using hwloc-gather-cpuid"
56   echo "  --keep      Keep the temporary copy of dumped files"
57   echo "  --version   Report version and exit"
58   echo "  -h --help   Show this usage"
59   echo "Example:"
60   echo "  $0 /tmp/\$(uname -n)"
61}
62
63while [ x`echo "$1" | cut -c1` = x- ] ; do
64  case $1 in
65  --io) gatherio=1;;
66  --dmi) gatherdmi=1;;
67  --no-cpuid) gathercpuid=0;;
68  --keep) keep=1;;
69  --version) echo `basename $0`" $HWLOC_VERSION"; exit 0;;
70  -h|--help) usage; exit 0;;
71  *) echo "Unrecognized option: $1"; usage; exit 1;;
72  esac
73  shift
74done
75
76if test $# -lt 1 -o x$1 = x; then
77  usage
78  exit 1
79fi
80name="$1"
81basename=`basename "$name"`
82dirname=`dirname "$name"`
83
84if [ x$gatherio = x0 ]; then
85  echo "I/O files won't be saved (--io not given)."
86fi
87if [ x$gatherdmi = x0 ]; then
88  echo "DMI files won't be saved (--dmi not given)."
89fi
90
91echo
92echo "*** Note that this tool may be slow on large nodes or when I/O is enabled. ***"
93echo
94
95if ! mkdir -p "$dirname" ; then
96    error "Failed to create directory $dirname."
97    exit 1
98fi
99
100if [ ! -w  "$dirname" ] ; then
101    echo "$dirname is not writable."
102    exit 1
103fi
104
105destdir=`mktemp -d --tmpdir hwloc-gather-topology.XXXXXXXX`
106
107# Use cat so that we properly get proc/sys files even if their
108# file length is wrong
109_savefile() {
110  local dest="$1"
111  local file="$2"
112  if test -r "$file"; then
113    dir=`dirname "$file"`
114    mkdir -p "$dest/$dir" 2>/dev/null
115    cat "$file" > "$dest/$file" 2>/dev/null
116  fi
117}
118
119_savelink() {
120  local dest="$1"
121  local file="$2"
122  dir=`dirname "$file"`
123  mkdir -p "$dest/$dir" 2>/dev/null
124  cp -P "$file" "$dest/$file"
125}
126
127# Get a file
128savefile() {
129  local dest="$1"
130  local file="$2"
131  echo " file $file"
132  _savefile "$dest" "$file"
133}
134
135# Get all files from the given directory
136# Ignore errors since some files may be missing, and some may be
137# restricted to root (but we don't need them).
138savedir() {
139  local dest="$1"
140  local path="$2"
141  echo " directory $path"
142  # gather all directories, including empty ones
143  find "$path" -type d 2>/dev/null | while read -r dir ; do
144    mkdir -p "$dest/$dir" 2>/dev/null
145  done
146  # gather all files now
147  find "$path" -type f 2>/dev/null | while read -r file ; do
148    test -e "$dest/$file" || _savefile "$dest" "$file"
149  done
150  # gather symlinks
151  find "$path" -type l 2>/dev/null | while read -r link ; do
152    test -e "$dest/$link" || _savelink "$dest" "$link"
153  done
154}
155
156# Save a class directory, including symlink targets (except in /devices/pci) and their device symlinks if any
157saveclassdir() {
158  local dest="$1"
159  local classname="$2"
160
161  test -d /sys/class/$classname/ || return
162
163  savedir "$dest" /sys/class/$classname/
164
165  for dev in $(ls /sys/class/$classname/); do
166    target=$(readlink /sys/class/$classname/$dev 2>/dev/null | grep -v /devices/pci)
167    if test "x$target" != x; then
168       savedir "$dest" "/sys/class/$classname/$target"
169    fi
170    device=$(readlink /sys/class/$classname/$target/device 2>/dev/null | grep -v /devices/pci)
171    if test "x$device" != x; then
172       savedir "$dest" "/sys/class/$classname/$target/$device"
173    fi
174  done
175}
176
177# Save a bus devices directory, include symlinks target (except in /devices/pci) with a possible relative path, and their device symlink if any
178savebusdir() {
179  local dest="$1"
180  local busname="$2"
181  local relpath="$3"
182
183  test -d /sys/bus/$busname/devices/ || return
184
185  savedir "$dest" /sys/bus/$busname/devices/
186
187  for dev in $(ls /sys/bus/$busname/devices/); do
188    target=$(readlink /sys/bus/$busname/devices/$dev 2>/dev/null | grep -v /devices/pci)
189    if test "x$target" != x; then
190       savedir "$dest" "/sys/bus/$busname/devices/$target/$relpath"
191    fi
192    device=$(readlink /sys/bus/$busname/devices/$target/$relpath/device 2>/dev/null | grep -v /devices/pci)
193    if test "x$device" != x; then
194       savedir "$dest" "/sys/bus/$busname/devices/$target/$relpath/$device"
195    fi
196  done
197}
198
199# savedir "$destdir/$basename" /sys/bus/dax/devices/
200# readlink /sys/bus/dax/devices/* 2>/dev/null | grep -v /devices/pci | while read -r path ; do savedir "$destdir/$basename" "/sys/bus/dax/devices/$path/.." ; done
201
202# Get an entire mount point, after decoding its path
203# we don't support path with \n since it would break in 'find ... | while read ..." above
204savemntpnt() {
205  local encodedpath=$1
206  if echo "$1" | grep "\\012" ; then
207    echo "Ignoring mount point whose filename contains an end of line."
208    return
209  fi
210  local path=$(echo "$1" | sed -e 's@\\134@\\@g' -e 's@\\040@ @g' -e 's@\\011@	@g')
211  savedir "$destdir/$basename" "${path}/"
212}
213
214#
215# Main stuff
216#
217echo "Gathering main files and directories..."
218
219# Gather the following list of files
220savefile "$destdir/$basename" /proc/cmdline
221savefile "$destdir/$basename" /proc/cpuinfo
222savefile "$destdir/$basename" /proc/meminfo
223savefile "$destdir/$basename" /proc/mounts
224savefile "$destdir/$basename" /proc/stat
225savefile "$destdir/$basename" /proc/version
226savefile "$destdir/$basename" /proc/self/cpuset
227savefile "$destdir/$basename" /proc/self/cgroup
228savefile "$destdir/$basename" /proc/driver/nvidia
229
230# Gather cpu and node information
231# (no need for savebusdir since we already gather the target /sys/devices/system/{cpu,node} explicitly)
232savedir "$destdir/$basename" /sys/devices/system/cpu/
233savedir "$destdir/$basename" /sys/bus/cpu/devices/
234savedir "$destdir/$basename" /sys/devices/system/node/
235savedir "$destdir/$basename" /sys/bus/node/devices/
236
237# Gather DMI IDs
238# (no need for aveclassdir since we only want "id" and it usually points to /sys/devices/virtual/dmi/id/)
239savedir "$destdir/$basename" /sys/class/dmi/id/
240savedir "$destdir/$basename" /sys/devices/virtual/dmi/id/
241
242# Gather hugepage information
243savedir "$destdir/$basename" /sys/kernel/mm/hugepages/
244
245# Gather the default /var/run/hwloc (in case it was created by a system-wide hwloc-dump-hwdata)
246if test -d /var/run/hwloc; then
247  savedir "$destdir/$basename" /var/run/hwloc
248fi
249# Then, gather what the current hwloc installation could have created in a different $runstatedir
250if test "$runstatedir" != "/var/run" -a -d "$runstatedir/hwloc"; then
251  savedir "$destdir/$basename" "$runstatedir/hwloc/"
252  mkdir -p "$destdir/$basename/var/run"
253  ln -sr "$destdir/$basename/$runstatedir/hwloc" "$destdir/$basename/var/run/hwloc.runstatedir"
254fi
255# And gather what custom $HWLOC_DUMPED_HWDATA_DIR
256if test "x$HWLOC_DUMPED_HWDATA_DIR" != x -a -d "$HWLOC_DUMPED_HWDATA_DIR"; then
257  savedir "$destdir/$basename" "$HWLOC_DUMPED_HWDATA_DIR"
258  mkdir -p "$destdir/$basename/var/run"
259  ln -sr "$destdir/$basename/$HWLOC_DUMPED_HWDATA_DIR" "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR"
260fi
261# Now link to /var/run/hwloc (used by default in lstopo -i) to something sane if needed
262if ! test -d "$destdir/$basename/var/run/hwloc"; then
263  if test -e "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR"; then
264    ln -sr "$destdir/$basename/var/run/hwloc.HWLOC_DUMPED_HWDATA_DIR" "$destdir/$basename/var/run/hwloc"
265  else if test -e "$destdir/$basename/$runstatedir/hwloc"; then
266    ln -sr "$destdir/$basename/var/run/hwloc.runstatedir" "$destdir/$basename/var/run/hwloc"
267  fi fi
268fi
269
270# Gather cgroup/cpuset mntpnts
271cat /proc/mounts | while read -r dummy1 mntpath mnttype mntopts dummy2 ; do
272  [ x$mnttype = xcpuset ] && savemntpnt "$mntpath"
273  [ x$mnttype = xcgroup ] && echo $mntopts | grep -w cpuset >/dev/null && savemntpnt "$mntpath"
274  [ x$mnttype = xcgroup2 ] && savemntpnt "$mntpath"
275done
276
277#
278# Optionally gather I/O directories too
279#
280if [ x$gatherio = x1 ]; then
281  echo "Gathering I/O files..."
282  # gather all PCI stuff, lots of links (/sys/bus/pci/devices/* and /sys/class/foo/*) point there
283  savedir "$destdir/$basename" /sys/bus/pci/devices/
284  savedir "$destdir/$basename" /sys/bus/pci/slots/
285  ls -d /sys/devices/pci* 2>/dev/null | while read -r path ; do savedir "$destdir/$basename" "$path" ; done
286  # gather class and bus links, we'll parse that the target path for PCI busids
287  saveclassdir "$destdir/$basename" block
288  saveclassdir "$destdir/$basename" dax
289  savebusdir "$destdir/$basename" dax ..
290  saveclassdir "$destdir/$basename" dma
291  saveclassdir "$destdir/$basename" drm
292  saveclassdir "$destdir/$basename" infiniband
293  saveclassdir "$destdir/$basename" net
294  saveclassdir "$destdir/$basename" ve
295  saveclassdir "$destdir/$basename" mic
296  # udev block data
297  ls -d /run/udev/data/b* 2>/dev/null | while read -r path ; do savefile "$destdir/$basename" "$path" ; done
298fi
299
300#
301# Optionally gather DMI directories too
302#
303if [ x$gatherdmi = x1 ]; then
304  echo "Gathering DMI files..."
305  savedir "$destdir/$basename" /sys/firmware/dmi/
306fi
307
308#
309# Export /proc/hwloc-nofile-info during lstopo (needs HWLOC_DUMP_NOFILE_INFO with HWLOC_THISSYSTEM=1)
310#
311echo "Exporting /proc/hwloc-nofile-info"
312export HWLOC_DUMP_NOFILE_INFO="$destdir/$basename/proc/hwloc-nofile-info"
313"$lstopo" - >/dev/null
314# disable HWLOC_DUMP_NOFILE_INFO for next lstopo invocation
315export HWLOC_DUMP_NOFILE_INFO=
316
317#
318# Export cpuid if available
319#
320if [ x$gathercpuid = x1 -a -e "$hgcpuid" ]; then
321  echo
322  echo "Exporting x86 CPUID using hwloc-gather-cpuid"
323  $hgcpuid --silent "$destdir/$basename/cpuid"
324fi
325
326# Create the archive and optionally keep the tree in /tmp for testing
327echo
328( cd "$destdir/" && tar cfj "$basename.tar.bz2" "$basename" )
329mv "$destdir/$basename.tar.bz2" "$dirname/$basename.tar.bz2"
330if test x$keep = x1; then
331  echo "Topology files gathered in $dirname/$basename.tar.bz2 and kept in $destdir/$basename/"
332else
333  echo "Topology files gathered in $dirname/$basename.tar.bz2"
334  rm -rf "$destdir"
335fi
336
337# Generate the output as well
338# we need "Topology not from this system" in the output so as to make test-topology.sh happy
339export HWLOC_THISSYSTEM=0
340"$lstopo" - -v > "$dirname/$basename.output"
341echo "Expected topology output stored in $dirname/$basename.output"
342"$lstopo" -.xml --whole-io --disallowed > "$dirname/$basename.xml"
343echo "XML topology stored in $dirname/$basename.xml"
344
345echo
346echo "WARNING: Do not post these files on a public list or website unless you"
347echo "WARNING: are sure that no information about this platform is sensitive."
348
349exit 0
350