CertGrid CertGrid
Hands-on Lab·LPIC-2

Finding what is consuming the machine

Topic 200 asks you to measure resource usage and predict future needs. This guide puts a real CPU load on the machine and names the process responsible, replaces it with a real I/O load and shows every number changing to a different set, then reads back the record sysstat has been keeping for days without anyone installing an agent.

201: Capacity Planning Guide 27 of 29 Intermediate

One host. Nothing is installed - sysstat, pidstat and iostat are already present, and both loads are disposable scripts removed at the end.
Server NameIP AddressOSRolesCPURAMHDD
LPIC2-A01192.168.0.78Ubuntu 26.04 LTSPrimary service host - BIND, Apache, Samba, Postfix. Topic 204 RAID runs on loop devices; this machine has no spare disk2 Core4 GB50 GB

This guide includes

Use this when a machine is slow and you need to say what is causing it, or when someone asks how long the current disk will last. This matters because the numbers that say a machine is busy do not say what is making it busy, and because a prediction needs history that has to have been collected before you were asked.

Before you start

  1. A quiet machine, described in numbers

    The baseline, and two loops that will spoil it.

    bash Example session
    nproc; echo "CPUs"; uptime; vmstat 1 3 | tail -22CPUs 10:06:47 up 1 day, 23:38,  1 user,  load average: 0.32, 0.47, 0.26 0  0      0 1970692 145816 1065396    0    0     0     0  160   74  0  0 100  0  0  0 0  0      0 1970692 145816 1065396    0    0     0     0  147   90  0  0 100  0  0  0sudo tee /tmp/xk0cpu.sh > /dev/null <<'SH'#!/bin/shecho $$ > /tmp/xk0cpu$1.pidwhile :; do :; doneSHsudo chmod +x /tmp/xk0cpu.sh; sudo setsid /tmp/xk0cpu.sh 1 > /dev/null 2>&1 & sudo setsid /tmp/xk0cpu.sh 2 > /dev/null 2>&1 & sleep 3; cat /tmp/xk0cpu1.pid /tmp/xk0cpu2.pid; echo "--- two busy loops, one per CPU"6273262734--- two busy loops, one per CPU

    Expected result2 CPUs, load average: 0.12, 0.11, 0.09, and vmstat reporting id 100 - then two PIDs, one busy loop per CPU.

    Success conditionYou have a recorded baseline to compare everything else against.

  2. Busy is not a diagnosis

    The load is real. Now say what is causing it.

    bash Example session
    sleep 60; uptime; vmstat 1 3 | tail -2; echo "--- us user, sy system, id idle, wa waiting on I/O" 10:07:53 up 1 day, 23:39,  2 users,  load average: 1.55, 0.79, 0.38 2  0      0 1990188 145824 1065424    0    0     0     0 2064   72 100  0  0  0  0  0 2  0      0 1990188 145824 1065424    0    0     0     0 2066   67 100  0  0  0  0  0--- us user, sy system, id idle, wa waiting on I/Opidstat -u 1 2 | tail -4Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  CommandAverage:        0     61153    0.00    0.50    0.00    0.00    0.50     -  kworker/0:1-eventsAverage:        0     62732  100.00    0.00    0.00    0.50  100.00     -  xk0cpu.shAverage:        0     62734  100.00    0.00    0.00    0.50  100.00     -  xk0cpu.shps -eo pid,ni,comm,%cpu,etimes --sort=-%cpu | head -4; echo "--- ni is the nice value, and it is why iostat reports this as %nice and not %user"    PID  NI COMMAND         %CPU ELAPSED  62734   5 xk0cpu.sh       99.7      68  62732   5 xk0cpu.sh       99.6      68  62544   0 systemd          0.0      70--- ni is the nice value, and it is why iostat reports this as %nice and not %user

    Expected resultload average: 1.55 and vmstat showing us 100, id 0, wa 0 - pidstat naming two xk0cpu.sh at 100.00 %usr each - and ps showing both at NI 5.

    Success conditionYou can attribute a load to named processes with a number beside each.

  3. A CPU-bound machine has an idle disk

    The measurement that rules something out.

    bash Example session
    iostat -xz 1 2 | tail -4; echo "--- -z omits idle devices, so an empty table IS the measurement" Device            r/s     rkB/s   rrqm/s  %rrqm r_await rareq-sz     w/s     wkB/s   wrqm/s  %wrqm w_await wareq-sz     d/s     dkB/s   drqm/s  %drqm d_await dareq-sz     f/s f_await  aqu-sz  %util  --- -z omits idle devices, so an empty table IS the measurement

    Expected resulta device header with no rows underneath it at all.

    Success conditionYou can rule the disk out with one command and no ambiguity.

  4. The other kind of slow

    Kill the first load, prove it is gone, and start a completely different one.

    bash Example session
    sudo kill $(cat /tmp/xk0cpu1.pid) $(cat /tmp/xk0cpu2.pid); sleep 3; ps -eo comm --sort=-%cpu | head -2; echo "--- the CPU load is gone before the next one starts"COMMANDsystemd--- the CPU load is gone before the next one startssudo tee /tmp/xk0io.sh > /dev/null <<'SH'#!/bin/shecho $$ > /tmp/xk0io.pidwhile :; do dd if=/dev/zero of=/var/tmp/xk0io.dat bs=1M count=800 oflag=direct status=none; doneSHsudo chmod +x /tmp/xk0io.sh; sudo setsid /tmp/xk0io.sh > /dev/null 2>&1 & sleep 8; sudo iostat -xz 1 2 | tail -4dm-0             0.00      0.00     0.00   0.00    0.00     0.00 10368.00 5308416.00     0.00   0.00    0.13   512.00    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    1.32  74.20sda              0.00      0.00     0.00   0.00    0.00     0.00 10367.00 5308415.50     0.00   0.00    0.13   512.05    0.00      0.00     0.00   0.00    0.00     0.00    0.00    0.00    1.37  74.20vmstat 1 3 | tail -2; echo "--- wa is time the CPU spent with nothing to do but wait for the disk" 1  0      0 1990164 146008 1065444    0    0     0 6983828 12346 14433  1 23 45 31  0  0 0  1      0 1990216 146008 1065444    0    0     0 8508416 14395 17510  0 27 45 27  0  0--- wa is time the CPU spent with nothing to do but wait for the disksudo pidstat -d 1 2 | tail -3; echo "--- named by process, and it is not a busy loop this time"Average:      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  CommandAverage:        0     63121      0.00 6963200.00      0.00       0  xk0io.shAverage:        0     63559      0.00 217600.00      0.00       0  dd--- named by process, and it is not a busy loop this time

    Expected resultthe CPU load gone - then %util 74.20 with over 10,000 writes per second - vmstat reporting wa 31, us 1, sy 23 - and pidstat -d naming xk0io.sh and dd.

    Success conditionYou can recognise an I/O-bound machine and name the process writing.

  5. The record that was being kept the whole time

    No agent, no dashboard, and days of history already on disk.

    bash Example session
    ls /var/log/sysstat/ | tail -4; grep -E "^ENABLED" /etc/default/sysstat; systemctl list-timers 'sysstat*' --no-pager | head -3sa30sa31sar29sar30ENABLED="true"NEXT                            LEFT LAST                          PASSED UNIT                  ACTIVATESMon 2026-08-31 10:10:00 UTC 1min 42s Mon 2026-08-31 10:00:17 UTC 7min ago sysstat-collect.timer sysstat-collect.serviceTue 2026-09-01 00:00:00 UTC      13h Mon 2026-08-31 00:00:17 UTC  10h ago sysstat-rotate.timer  sysstat-rotate.servicesar -u | tail -309:50:17 AM     all      0.00      0.00      0.02      0.00      0.00     99.9810:00:17 AM     all      0.00      0.00      0.01      0.01      0.00     99.98Average:        all      0.07      0.00      0.06      0.01      0.00     99.86sar -r | tail -2; echo "--- memory, from the same collection, with no agent installed"10:00:17 AM   2009908   2970708    198132      5.69    145672    991928    309168      4.13    387540    804108       100Average:      2007491   2966489    202667      5.82    144846    991000    310393      4.15    386531    803445       101--- memory, from the same collection, with no agent installed

    Expected resultdaily files sa30, sa31 and text reports sar29, sar30 - ENABLED="true" - sysstat-collect.timer firing every ten minutes and sysstat-rotate.timer daily - then per-interval CPU lines with a daily Average, and the same for memory.

    Success conditionYou can produce yesterday's figures on a machine nobody instrumented.

  6. Turning a measurement into a prediction

    Three days of averages, and the arithmetic that makes them a date.

    bash Example session
    for f in $(ls /var/log/sysstat/sa[0-9]* | tail -3); do echo -n "$(basename $f)  "; sar -u -f $f 2>/dev/null | tail -1; donesa29  Average:        all      0.01      0.00      0.03      0.01      0.00     99.94sa30  Average:        all      0.04      0.01      0.05      0.03      0.00     99.87sa31  Average:        all      0.07      0.00      0.06      0.01      0.00     99.86for f in $(ls /var/log/sysstat/sa[0-9]* | tail -3); do echo -n "$(basename $f)  "; sar -r -f $f 2>/dev/null | tail -1 | awk '{print "memused%", $5}'; donesa29  memused% 5.34sa30  memused% 5.77sa31  memused% 5.82df -h /var | tail -1; sudo du -sh /var/tmp/xk0io.dat 2>/dev/null; echo "--- a size and a rate are what turn a measurement into a date"/dev/mapper/ubuntu--vg-ubuntu--lv   48G  7.6G   38G  17% /673M	/var/tmp/xk0io.dat--- a size and a rate are what turn a measurement into a date

    Expected resultdaily CPU averages rising 0.01 → 0.04 → 0.07, memused 5.34 → 5.77 → 5.82 - and the filesystem at 17% with a single 673M file in it.

    Success conditionYou can state a trend from evidence instead of an impression.

  7. Putting the machine back

    Both loads gone, the file gone, the load average falling.

    zsh Example session
    sudo kill -- -$(cat /tmp/xk0io.pid) 2>/dev/null; sleep 2; sudo pkill -x dd 2>/dev/null; sleep 2; pgrep -c -x dd; echo "dd processes left"; sudo rm -f /var/tmp/xk0io.dat /tmp/xk0cpu*.pid /tmp/xk0io.pid /tmp/xk0cpu.sh /tmp/xk0io.sh0dd processes leftsleep 45; uptime; df -h /var | tail -1; ls /tmp/xk0* 2>&1 | tail -1; ps -eo comm --sort=-%cpu | head -2 10:09:07 up 1 day, 23:40,  1 user,  load average: 0.67, 0.72, 0.39/dev/mapper/ubuntu--vg-ubuntu--lv   48G  7.0G   38G  16% /zsh:2: no matches found: /tmp/xk0*COMMANDsystemd

    Expected result0 dd processes left, the filesystem back to 16%, load average: 0.67 and falling, no matches found: /tmp/xk0*, and nothing but systemd at the top of the CPU list.

    Success conditionThe host carries no trace of either load.

Troubleshooting

Official sources