I found the answer much later in time. It has to do with the internal debug circuitry. By default, the timer peripherals do not stop when we hit a breakpoint in debug mode but continue counting. This is why we keep getting random measurment intervals between timer interrupt instances using the stop watch. In order to get accurate timing, we need the debug circuity to force the timer peripheral to stop counting once we reach a breakpoint and resume later once we step over it. This can be done by writing this code:
SET_BIT(DBGMCU->APB1FZ, DBGMCU_APB1_FZ_DBG_TIM3_STOP);
Which instructs timer 3 on APB1 bus to stop counting at breakpoints.