Skip to content
VLSI Mentor

Linux & Bash · Part II · Module 34 · Defensive Automation Around EDA Tools

Truncated, Missing and Partial Output

A check written as 'no error line was found' answers the same way for a clean run, an empty file, half a file and no file at all. This chapter measures all four, finds that a truncated 'TEST PASSED' still matches a loose pattern because it was cut mid-word, and rebuilds the check around positive evidence that a complete run leaves behind.

Advanced25 min readLinuxPart IIEDALogsDefensive

Part II · Module 34 · Chapter 34.2 · Truncated, Missing and Partial Output

Chapter 34.1 moved the verdict from the exit status into the log. This chapter attacks the assumption that move rests on: that there is a log, and that it is all there.

1. Four Inputs, One Answer

The check every flow contains somewhere:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
if [ "$(grep -c 'ERROR' "$log")" -eq 0 ]; then verdict=PASS; else verdict=FAIL; fi

Four files — a real passing run, an empty file, the first 60 bytes of the real one, and a path that does not exist:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  FILE           grep rc   count   verdict   is that right?
  good.log       1         0       PASS      yes -- it really passed
  empty.log      1         0       PASS      NO -- the file is empty
  truncated.log  1         0       PASS      NO -- half a log
  absent.log     2         (none)  PASS      NO -- there is no file

Three of four are wrong, and all three are wrong in the direction that ships.

2. Truncation Cuts Mid-Word

The 60-byte file is the interesting one. Here it is in full:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
TB  test=smoke seed=1 cycles=200 verbosity=LOW
TB  TEST PASS

The run really did pass, and the log was cut in the middle of the word PASSED. Now match it:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  grep -c 'TEST PASSED'    -> 0
  grep -c 'TEST PASS'      -> 1  <<< MATCHES A TRUNCATED VERDICT
  grep -c 'PASS'           -> 1  <<< MATCHES A TRUNCATED VERDICT
  grep -c 'TEST PASSED$'   -> 0

A truncated verdict matches a loose pattern. grep 'PASS' is what people write, and here it reports a pass from a file that was cut off before the verdict was finished — which means it would report a pass from a run killed at that moment for any reason at all, including the reason the verdict was going to be FAILED.

Two defences, and both are needed:

Anchor the pattern. 'TEST PASSED$' returns 0 here, and '^TB TEST PASSED ' is better still — it requires the line's beginning and a trailing space, so no prefix of a longer word can satisfy it.

And check that the file ends properly, because anchoring alone cannot tell a complete log from one truncated after the verdict.

Top row: a good log, an empty file, a truncated file and an absent file all report PASS under an absence-based check. Bottom row: the same four under a presence-based check give pass, empty, incomplete and no log respectively.good logPASS — correctempty filePASS — wronghalf a logPASS — wrongno filePASS — wrongPASSverdict and end markeremptyexit 4incompleteexit 2no logexit 312
Figure 1 — the same check over four inputs. Asking whether an error line is absent gives the same answer for a clean run and for three kinds of nothing. Asking what a finished run leaves behind separates all four, because nothing-happened fails every presence test.

3. Positive Evidence

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
#!/bin/bash
# logcheck LOG -- decide a run from POSITIVE evidence in a log that must exist.
#   0 a complete, consistent pass        3 no such log
#   1 an explicit failure                4 the log exists but is empty
#   2 the log is present but incomplete  5 it contradicts itself
set -u
log=${1:-}
[ -n "$log" ] || { echo "logcheck: need a log path" >&2; exit 64; }
[ -e "$log" ] || { echo "logcheck: no log at '$log' -- the run produced nothing" >&2; exit 3; }
[ -s "$log" ] || { echo "logcheck: '$log' is empty -- the run produced a file and no output" >&2; exit 4; }

# A tool that finished writes a terminator. Without one the file is a fragment,
# whatever it appears to say -- a truncated "TEST PASSED" ends as "TEST PASS",
# which a loose pattern happily matches.
tail -c 1 "$log" | od -An -c | /usr/bin/grep -q '\\n' \
  || { echo "logcheck: '$log' does not end in a newline -- truncated mid-line" >&2; exit 2; }
/usr/bin/grep -q '\$finish called' "$log" \
  || { echo "logcheck: '$log' has no end-of-run marker -- the run did not finish" >&2; exit 2; }

p=$(/usr/bin/grep -c '^TB  TEST PASSED ' "$log")
f=$(/usr/bin/grep -c '^TB  TEST FAILED ' "$log")
[ $((p + f)) -gt 0 ] || { echo "logcheck: '$log' states no verdict" >&2; exit 2; }
[ $((p * f)) -eq 0 ] || { echo "logcheck: '$log' states both verdicts" >&2; exit 5; }
[ "$f" -eq 0 ] || { echo "logcheck: explicit failure" >&2; exit 1; }
e=$(/usr/bin/grep -c '[[:space:]]ERROR' "$log")
[ "$e" -eq 0 ] || { echo "logcheck: PASSED with $e error line(s)" >&2; exit 5; }
exit 0

Every branch, exercised:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  LOG              rc    MESSAGE
  smoke.log        0
  prio0.log        1     explicit failure
  inconsistent.log 5     PASSED with 1 error line(s)
  silent.log       2     states no verdict
  empty.log        4     is empty -- the run produced a file and no output
  truncated.log    2     does not end in a newline -- truncated mid-line
  noend.log        2     has no end-of-run marker -- the run did not finish
  absent.log       3     no log -- the run produced nothing

Four ideas in that script, and the first is the one the chapter exists for.

Ask what a complete run leaves, not what a broken one leaves

Every condition is of the form this must be present:

  • the file must exist
  • it must be non-empty
  • it must end in a newline
  • it must contain $finish called
  • it must state exactly one verdict
  • a PASSED must not sit beside an error line

A check built from absences answers the same way for "nothing went wrong" and "nothing happened". A check built from presences cannot: nothing happened fails every one of them. This is the fifth time this track has met the shape, and the previous four were Chapter 31.2's empty test list, Chapter 31.7's disk scan, Chapter 32.5's zero-row results file, and Chapter 32.7's arithmetic over an empty set.

Two different kinds of incomplete

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
tail -c 1 "$log" | od -An -c | /usr/bin/grep -q '\\n' || { … truncated mid-line …; exit 2; }
/usr/bin/grep -q '\$finish called' "$log" || { … no end-of-run marker …; exit 2; }

The first catches a file cut in the middle of a line — the 60-byte case. The second catches a file that happens to end on a line boundary but never reached the end of the run, which is what noend.log is: 100 bytes plus a newline, perfectly well-formed, and missing everything after it.

Neither check subsumes the other, and a writer who stops exactly at a newline is not unusual — a 4,096-byte buffer boundary falls on a newline roughly once in every few dozen lines.

tail -c 1 | od -An -c rather than reading the byte directly, because command substitution strips trailing newlines, so [ "$(tail -c1 "$log")" = "" ] is true for both a file ending in a newline and a file ending in nothing. That is Chapter 33.5's defect 5 arriving where it does real damage.

Exactly one verdict

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
[ $((p + f)) -gt 0 ] || { … states no verdict …; exit 2; }
[ $((p * f)) -eq 0 ] || { … states both verdicts …; exit 5; }

The sum catches none and the product catches both — two conditions in two cheap expressions. A log containing both is a multi-phase testbench whose phases disagreed, which is a different problem from either verdict alone.

And distinct statuses

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  0 a complete, consistent pass        3 no such log
  1 an explicit failure                4 the log exists but is empty
  2 the log is present but incomplete  5 it contradicts itself

3 and 4 are separate because they are different failures of the flow: no file means the tool never wrote one; an empty file means it created one and wrote nothing, which usually means it died between open and the first flush. Chapter 32.3's classifier would merge them; here, where the subject is the artifact rather than the verdict, the distinction is the point.

4. The Artifact That Looks Fine

Logs are text and truncation is visible. A binary artifact is worse, because the parts a cheap check looks at are at the front.

A waveform written under a file-size cap, against a complete one:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  full   : rc=0   log=229B dump=18678B
  capped : rc=153 log=132B dump=8192B
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  full     header=1  $enddefinitions=1  last line: 1"
  capped   header=1  $enddefinitions=1  last line: #

The truncated dump has a valid $version header and a $enddefinitions marker, because both live in the first few hundred bytes. Every structural check a flow is likely to make passes:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  full     dump present -> usable
  capped   dump present -> usable

The only difference is at the end, where capped finishes with a bare # — a timestamp marker with no value changes after it, written halfway.

So for an artifact the rules are different from a log:

Check the end, not the beginning. A format's header is written before any work happens and proves nothing about completion.

Prefer the producer's own status. rc=153 is 128 + 25, SIGXFSZ, which Chapter 33.8 measured — and for an artifact, unlike a verdict, the tool's status is the right signal, because a truncated write is exactly the kind of failure the tool does report.

And check the log and the artifact together. capped's log is 132 bytes against the full run's 229 — also truncated, by the same cap, at the same moment. Two artifacts cut off together is a stronger signal than either alone, and a flow that validates only the log would call this run incomplete without knowing the waveform was too.

5. Where These Checks Go

Immediately after the tool, inside the wrapper that ran it, before anything downstream reads the artifact:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
( cd "$d" && exec vvp "$root/logs/sim.vvp" … ) > "$d/run.log" 2>&1
rc=$?
printf '%s\n' "$rc" > "$d/tool.rc"
"$root/bin/logcheck" "$d/run.log"; lc=$?
printf '%s\n' "$lc" > "$d/logcheck.rc"

Three properties.

The check runs where the context is. The wrapper knows which run this is, where its log should be and what the tool returned. A central checker running later over a results directory has to reconstruct all of that, and Chapter 31.7 measured what a directory scan cannot know.

Its result is recorded, not acted on. logcheck.rc goes into the run directory beside tool.rc, and Chapter 32.4's collector picks both up as columns. The wrapper does not decide the regression's fate; it records evidence, which is Chapter 32.2's separation of dispatch from interpretation.

And it does not repair anything. A truncated log is not deleted, not re-run, not patched. It is the evidence that something truncated it, and Chapter 33.1 made the general argument: a tool that repairs its input destroys the reason it was needed.

Practice

  1. Write the "no ERROR line" check and run it on a good log.
  2. Run it on an empty file, on half a file, and on a path that does not exist.
  3. Count how many of the four it gets wrong, and in which direction.
  4. Look at grep's exit status for each of the four and say what it distinguishes.
  5. Write the check as [ "$n" -eq 0 ] with n empty and see what happens.
  6. Truncate a log mid-word and match it against four patterns of decreasing looseness.
  7. Find the loosest pattern that still matches.
  8. Anchor the pattern at both ends and re-run.
  9. Write a checker made only of "must be present" conditions.
  10. Give it a file that ends on a line boundary but stops early.
  11. Explain why the trailing-newline check does not subsume the end-marker check.
  12. Try [ "$(tail -c1 f)" = "" ] and work out why it cannot distinguish the two cases.
  13. Detect "both verdicts" with a product and "no verdict" with a sum.
  14. Give no-file and empty-file different statuses and justify it.
  15. Produce a waveform under a file-size cap.
  16. Check its header and its $enddefinitions marker.
  17. Compare its last line against a complete one's.
  18. Check the log and the artifact together and say what that adds.
  19. Add a minimum-size threshold and name its two limitations.
  20. Replace it with -s and say what you lose and gain.
  21. Put the check in the wrapper and record its result rather than acting on it.
  22. Resist repairing a truncated log, and say why.

Check Your Understanding

  1. What assumption does Chapter 34.1's advice rest on?
  2. What did the "no ERROR line" check say about the four files?
  3. Which three were wrong, and in which direction?
  4. What are grep's three exit statuses and what does each mean?
  5. How was that information discarded?
  6. Give the three different wrong behaviours of the -eq idiom.
  7. What does the truncated log actually contain?
  8. Which patterns matched it, and which did not?
  9. Why is matching a truncated verdict especially dangerous?
  10. Give the two defences, and why both are needed.
  11. List the six "must be present" conditions.
  12. Why can a presence-based check not confuse "nothing wrong" with "nothing happened"?
  13. Name the four earlier chapters that met this shape.
  14. What does the trailing-newline check catch that the end-marker check does not, and vice versa?
  15. Why is a file ending exactly on a line boundary not unusual?
  16. Why tail -c 1 | od rather than comparing a command substitution?
  17. How do the sum and product catch the two verdict problems?
  18. Why are statuses 3 and 4 separate?
  19. What does a truncated waveform still have, and why?
  20. What does a "dump present" check conclude about both?
  21. What is the only observable difference, and where?
  22. Give the three rules for validating a binary artifact.
  23. Why is the tool's status the right signal for an artifact but not for a verdict?
  24. What does checking the log and the artifact together add?
  25. Give the two limitations of a size threshold.
  26. What is -s good for?
  27. Why does the check belong in the wrapper?
  28. Why record its result rather than act on it?
  29. Why must it not repair anything?

6. Remember

  • "No error line was found" answers the same way for a clean run, an empty file, half a file and no file. Three of four were wrong, all in the shipping direction.
  • grep returns 0, 1 and 2 — matched, no match, could not read — and $(grep -c …) throws the status away.
  • A truncated TEST PASSED ends as TEST PASS, which grep 'PASS' happily matches.
  • Anchor the pattern and check the file ends properly. Neither alone is enough.
  • Build checks from presences. Nothing-happened fails every presence check and satisfies every absence check.
  • Two kinds of incomplete: cut mid-line, and ending cleanly without reaching the end of the run. Neither check subsumes the other.
  • tail -c 1 | od, not a command substitution — substitution strips the newline you are testing for.
  • Sum catches "no verdict", product catches "both".
  • A truncated waveform keeps its header and its $enddefinitions. Check the end, trust the producer's status, and validate the log and the artifact together.
  • A size threshold is a guess that ages and cannot catch truncation above it. -s is the version with no guess.
  • Run the check in the wrapper, record its result as a column, and repair nothing.

7. What Is Next

Both chapters so far have treated a failure as a fact to be reported accurately. Chapter 34.3 takes on the class where that is not enough — failures worth retrying — and measures what happens when a retry is applied to a failure that is not.

Where this fits

Part of the Linux & Bash curriculum.