# Machine-Level Debugging Logic

Computer Science I · Introduction to Computers · https://tryals.app/learn/computer-science-i/machine-level-debugging-logic

## Finding the Fault

A running program is **state**: register values, memory contents, and the program counter. A bug is where that state diverges from correct execution.

Debugging makes state **observable** using a **debugger** with three primary tools:

| Tool | Action |
| --- | --- |
| **Breakpoint** | Pause execution at an instruction |
| **Inspect** | View actual registers and memory |
| **Single-step** | Execute one instruction at a time |

The strategy is a **binary search in time**: verify state at an early point, verify it is wrong later, and narrow the gap to find the first incorrect instruction.

**Pitfall:** Debugging by guessing. Never change code on intuition; inspect actual values at breakpoints. Reason strictly from evidence.

## Bug Families & Evidence

Machine-level bugs cluster into five recognizable families that speed up the debugging hunt:

| Bug Family | Description |
| --- | --- |
| **Off-by-one** | Error in loop bound calculations |
| **Integer overflow** | Value wraps around unexpectedly |
| **Wrong address** | Reading or writing incorrect memory |
| **Uninitialized** | Using leftover state in cells/registers |
| **Signed/unsigned** | Misinterpreting number representations |

The overriding principle is to **reason from evidence, not assumption**. Observe what the state actually is at each step, letting the discrepancy point directly to the fault.

## Practice questions

5 of this lesson's 12 practice questions, with answers. The full set is in the app.

### 1. Order the steps of narrowing down a machine-level bug by binary search in time.

**Answer:**

1. Reproduce the failure so the wrong value appears on every run
2. Set a breakpoint early and confirm the state is still correct there
3. Set a breakpoint late and confirm the state is already wrong there
4. Halve the gap and inspect the state at the midpoint
5. Repeat the halving until a single instruction is left, and inspect it

**Why:** A binary search needs bracketing before bisecting: a point known good and a point known bad, with the fault somewhere between. Halving before establishing both ends leaves you unable to interpret the result at the midpoint, and none of it is repeatable without a reliable reproduction first.

Page: https://tryals.app/practice/computer-science-i/machine-level-debugging-logic/order-the-steps-of-narrowing-down-a-machine-level-bug-by-binary

### 2. Sort each debugging move by whether it reasons from evidence or merely guesses.

**Answer:**

- Reasoning from evidence: Set a breakpoint and read the actual register values, Single-step and watch which instruction first writes the bad value
- Debugging by guessing: Change a loop bound because it looks wrong, then re-run, Insert a delay because the fault feels like a timing problem

**Why:** Breakpoints, single-stepping and memory dumps all make hidden state observable; changing bounds, adding delays and rewriting produce no evidence at all. The danger of the guessing moves is that they can make a symptom disappear while leaving the real fault in place.

Page: https://tryals.app/practice/computer-science-i/machine-level-debugging-logic/sort-each-debugging-move-by-whether-it-reasons-from-evidence-or

### 3. A program is made to run on eight cores instead of one and gets four times faster, not eight. What sets the ceiling?

A. Memory bandwidth, which is the only real constraint on parallel speedup
B. The sequential fraction, which bounds the speedup however many cores you add
C. Communication overhead between cores, which always halves the gain
D. The operating system reserving cores for its own use

**Answer:** B. The sequential fraction, which bounds the speedup however many cores you add

**Why:** The sequential part does not shrink, so it becomes the whole runtime as cores multiply: 20% sequential caps you at five times with infinite hardware. It is why parallelising is a design problem rather than a procurement one, and why the interesting question is always which fraction is genuinely serial.

Page: https://tryals.app/practice/computer-science-i/machine-level-debugging-logic/a-program-is-made-to-run-on-eight-cores-instead-of-one-and-gets-four

### 4. Moore's law described transistor counts doubling roughly every two years, and single-core clock speeds stopped rising around 2005 while transistor counts kept climbing. Why did the two part company?

A. Because power density became the limit, so transistors went into more cores.
B. Because Moore's law was never about clock speed, which has always been constant.
C. Because software could not use faster clocks, so manufacturers stopped providing them.
D. Because transistors stopped getting smaller, ending the improvements that clock speed depended on.

**Answer:** A. Because power density became the limit, so transistors went into more cores.

**Why:** Power scales roughly with frequency times voltage squared, and higher clocks needed higher voltages, so heat rose far faster than performance and the wall arrived while transistor budgets kept growing. The extra transistors went sideways into cores, which made it a software event: a sequential program stopped getting faster by waiting, and Amdahl's law sets the ceiling.

Page: https://tryals.app/practice/computer-science-i/machine-level-debugging-logic/moores-law-described-transistor-counts-doubling-roughly-every-two

### 5. An 8-bit register holds the pattern 11111111. Select every statement that is true.

A. Read as two's-complement signed, its value is -1
B. Read as unsigned it is 255, and read as signed it is 127
C. The unsigned and signed readings differ by 256
D. Adding 1 to it as an 8-bit unsigned value gives 0
E. Read as unsigned, its value is 255

**Answer:** A. Read as two's-complement signed, its value is -1; C. The unsigned and signed readings differ by 256; D. Adding 1 to it as an 8-bit unsigned value gives 0; E. Read as unsigned, its value is 255

**Why:** 11111111 is 255 unsigned; inverting gives 00000000, plus 1 is 1, so signed it is -1, and 255 minus -1 is 256, the size of the range. Adding 1 unsigned overflows to 0. Option 3 is the trap: 127 is the largest positive 8-bit signed value, not the reading of this pattern.

Page: https://tryals.app/practice/computer-science-i/machine-level-debugging-logic/an-8-bit-register-holds-the-pattern-11111111-select-every-statement
