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