fix(scheduler): break jobid= filters accept every jobid form the REPL prints (bug-1926)
repl_u64_str_eq() compared the packed jobid's raw decimal string with the filter text, so a breakpoint on the dashed jobid the REPL prints never fired. The filter is now parsed once when the breakpoint is set (repl_parse_jobsel_arg, moved to repl_fmt.c) and matched with repl_jobsel_matches: dashed ids, X[k], X[a-b] and raw decimal ids all work, a bare array-aligned id also matches its elements, and an unparseable value is rejected instead of creating a dead breakpoint. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
d2a94ebc3e
commit
7aef6b85c9
6 changed files with 501 additions and 69 deletions
|
|
@ -906,6 +906,296 @@ static void test_decision_breakpoint_matches_type_filter(void)
|
|||
printf("test_decision_breakpoint_matches_type_filter: PASS\n");
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------- */
|
||||
/* bug-1926: a `jobid=` filter takes the jobid forms the REPL prints and */
|
||||
/* `info job`/`info trace`/`trace on job` accept (the dashed id, X[k], */
|
||||
/* X[a-b]) as well as the raw packed decimal id, and an unparseable value */
|
||||
/* is rejected when the breakpoint is set. */
|
||||
/* ---------------------------------------------------------------------- */
|
||||
|
||||
/* The array 0-0-0-32768 (sequence 1 << 15, so array-aligned) and its
|
||||
* element k, the packed ids parse_jobsel()'s `X[k]` names. */
|
||||
#define B1926_ARRAY_SEQ (1U << N_ARRAY_IDX_BITS)
|
||||
static uint64_t b1926_elem(uint32_t seq, uint32_t k)
|
||||
{
|
||||
return MAKE_JOBID(0, 0, 0, TAG_JOB_ARRAY, seq | k);
|
||||
}
|
||||
|
||||
static void b1926_sim_init(xce_sched_sim_driver_t* sim)
|
||||
{
|
||||
memset(sim, 0, sizeof(*sim));
|
||||
/* makeString() needs xce_sched_core_init()'s xstring table */
|
||||
TEST_ASSERT_EQUAL_INT(0, xce_sched_core_init(&(sim->core)));
|
||||
sim->repl_state = repl_state_ensure(sim);
|
||||
TEST_ASSERT_NOT_NULL(sim->repl_state);
|
||||
}
|
||||
|
||||
/* true iff a PEND decision for `jobid` hits a breakpoint */
|
||||
static bool b1926_pend_hits(xce_sched_sim_driver_t* sim, uint64_t jobid)
|
||||
{
|
||||
xce_pendreason_t reason;
|
||||
xce_decision_t d;
|
||||
|
||||
memset(&reason, 0, sizeof(reason));
|
||||
reason.jobid = jobid;
|
||||
memset(&d, 0, sizeof(d));
|
||||
d.op = XCE_DECISION_PEND;
|
||||
d.u.reason = &reason;
|
||||
|
||||
return repl_find_decision_breakpoint(&(sim->core), sim->repl_state, &d)
|
||||
!= NULL;
|
||||
}
|
||||
|
||||
static void test_decision_breakpoint_jobid_dashed_form_matches(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
char printed[64];
|
||||
char line[128];
|
||||
|
||||
b1926_sim_init(&sim);
|
||||
/* the form the hit line prints: "decision: PEND jobid=0-0-0-104" */
|
||||
fmt_jobid(printed, sizeof(printed), 104ULL);
|
||||
TEST_ASSERT_EQUAL_STRING("0-0-0-104", printed);
|
||||
snprintf(line, sizeof(line), "break DECISION jobid=%s", printed);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, line));
|
||||
TEST_ASSERT_EQUAL_INT(1, sim.repl_state->nbreakpoints);
|
||||
|
||||
TEST_ASSERT_TRUE_MESSAGE(b1926_pend_hits(&sim, 104ULL),
|
||||
"jobid=0-0-0-104 must match job 104");
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim, 105ULL),
|
||||
"jobid=0-0-0-104 must NOT match job 105");
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim, 103ULL),
|
||||
"jobid=0-0-0-104 must NOT match job 103");
|
||||
|
||||
printf("test_decision_breakpoint_jobid_dashed_form_matches: PASS\n");
|
||||
}
|
||||
|
||||
static void test_decision_breakpoint_jobid_element_and_range_match(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
char printed[64];
|
||||
char line[128];
|
||||
|
||||
/* X[k], copied from the REPL's own rendering of element 2 */
|
||||
b1926_sim_init(&sim);
|
||||
fmt_jobid(printed, sizeof(printed), b1926_elem(B1926_ARRAY_SEQ, 2));
|
||||
TEST_ASSERT_EQUAL_STRING("0-0-0-32768[2]", printed);
|
||||
snprintf(line, sizeof(line), "break DECISION jobid=%s", printed);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, line));
|
||||
TEST_ASSERT_EQUAL_INT(1, sim.repl_state->nbreakpoints);
|
||||
TEST_ASSERT_TRUE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 2)),
|
||||
"X[2] must match element 2");
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 1)),
|
||||
"X[2] must NOT match element 1");
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 3)),
|
||||
"X[2] must NOT match element 3");
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim, B1926_ARRAY_SEQ + 2),
|
||||
"X[2] must NOT match the plain job whose "
|
||||
"sequence equals element 2's");
|
||||
TEST_ASSERT_FALSE_MESSAGE(
|
||||
b1926_pend_hits(&sim, b1926_elem(2 * B1926_ARRAY_SEQ, 2)),
|
||||
"X[2] must NOT match element 2 of another array");
|
||||
|
||||
/* X[a-b] */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, "break DECISION jobid=0-0-0-32768[1-3]"));
|
||||
TEST_ASSERT_EQUAL_INT(1, sim.repl_state->nbreakpoints);
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 0)));
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 1)));
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 2)));
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 3)));
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 4)));
|
||||
|
||||
printf("test_decision_breakpoint_jobid_element_and_range_match: PASS\n");
|
||||
}
|
||||
|
||||
/* A bare array-aligned dashed id selects what `trace on job` selects: the
|
||||
* plain job with that id and every element of the array with that id. A
|
||||
* raw packed decimal id keeps its pre-bug-1926 meaning: that exact id. */
|
||||
static void test_decision_breakpoint_jobid_bare_array_id_and_raw(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
char line[128];
|
||||
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, "break DECISION jobid=0-0-0-32768"));
|
||||
TEST_ASSERT_TRUE_MESSAGE(b1926_pend_hits(&sim, B1926_ARRAY_SEQ),
|
||||
"a bare id matches the plain job");
|
||||
TEST_ASSERT_TRUE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 0)),
|
||||
"a bare array id matches element 0");
|
||||
TEST_ASSERT_TRUE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 7)),
|
||||
"a bare array id matches element 7");
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, B1926_ARRAY_SEQ + 1));
|
||||
TEST_ASSERT_FALSE(
|
||||
b1926_pend_hits(&sim, b1926_elem(2 * B1926_ARRAY_SEQ, 0)));
|
||||
|
||||
/* an unaligned bare id is only a plain job (an element's packed id
|
||||
* carries the array tag, so it never equals a plain id) */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, "break DECISION jobid=0-0-0-104"));
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, 104ULL));
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 0)));
|
||||
|
||||
/* raw decimal: exactly that packed id, as before */
|
||||
b1926_sim_init(&sim);
|
||||
snprintf(line, sizeof(line), "break DECISION jobid=%llu",
|
||||
(unsigned long long)B1926_ARRAY_SEQ);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, line));
|
||||
TEST_ASSERT_EQUAL_INT(1, sim.repl_state->nbreakpoints);
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, B1926_ARRAY_SEQ));
|
||||
TEST_ASSERT_FALSE_MESSAGE(b1926_pend_hits(&sim,
|
||||
b1926_elem(B1926_ARRAY_SEQ, 0)),
|
||||
"a raw packed id names one exact job");
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, B1926_ARRAY_SEQ + 1));
|
||||
|
||||
/* raw decimal of an element's packed id: that element */
|
||||
b1926_sim_init(&sim);
|
||||
snprintf(line, sizeof(line), "break DECISION jobid=%llu",
|
||||
(unsigned long long)b1926_elem(B1926_ARRAY_SEQ, 5));
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, line));
|
||||
TEST_ASSERT_TRUE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 5)));
|
||||
TEST_ASSERT_FALSE(b1926_pend_hits(&sim, b1926_elem(B1926_ARRAY_SEQ, 4)));
|
||||
|
||||
printf("test_decision_breakpoint_jobid_bare_array_id_and_raw: PASS\n");
|
||||
}
|
||||
|
||||
static void test_break_invalid_jobid_rejected(void)
|
||||
{
|
||||
static const char* bad[] = {
|
||||
"bogus", "0-0-0", "0-0-0-104[", "0-0-0-104x", "-5",
|
||||
"0-0-0-32768[3-1]", /* reversed range */
|
||||
"0-0-0-104[1]", /* element of an unaligned id */
|
||||
"99999999999999999999999", /* wider than 64 bits */
|
||||
"", /* jobid= with no value */
|
||||
};
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(bad) / sizeof(bad[0]); i++) {
|
||||
xce_sched_sim_driver_t sim;
|
||||
char line[128];
|
||||
char want[96];
|
||||
char* captured;
|
||||
|
||||
b1926_sim_init(&sim);
|
||||
snprintf(line, sizeof(line), "break DECISION type=PEND jobid=%s",
|
||||
bad[i]);
|
||||
captured = capture_dispatch(&sim, line);
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(0, sim.repl_state->nbreakpoints, line);
|
||||
snprintf(want, sizeof(want), "invalid jobid '%s'", bad[i]);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, want), captured);
|
||||
TEST_ASSERT_NULL_MESSAGE(strstr(captured, "breakpoint 1 set"),
|
||||
captured);
|
||||
free(captured);
|
||||
}
|
||||
|
||||
/* event kinds validate the value the same way */
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
char* captured;
|
||||
|
||||
b1926_sim_init(&sim);
|
||||
captured = capture_dispatch(&sim, "break JOB_STATE_CHANGE jobid=abc");
|
||||
TEST_ASSERT_EQUAL_INT(0, sim.repl_state->nbreakpoints);
|
||||
TEST_ASSERT_NOT_NULL(strstr(captured, "invalid jobid 'abc'"));
|
||||
free(captured);
|
||||
}
|
||||
|
||||
printf("test_break_invalid_jobid_rejected: PASS\n");
|
||||
}
|
||||
|
||||
static void test_event_breakpoint_jobid_dashed_form_matches(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
xce_core_event_t ev;
|
||||
xce_job_t job_a;
|
||||
xce_job_t job_b;
|
||||
xce_job_t* jobs[2];
|
||||
|
||||
/* JOB_STATE_CHANGE */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(
|
||||
repl_dispatch(&sim, "break JOB_STATE_CHANGE jobid=0-0-0-42"));
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_JOB_STATE_CHANGE;
|
||||
ev.u.job_state_change.jobid = 42;
|
||||
ev.u.job_state_change.new_state = JOB_STAT_RUN;
|
||||
TEST_ASSERT_NOT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
ev.u.job_state_change.jobid = 43;
|
||||
TEST_ASSERT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
|
||||
/* JOB_UPDATE, an element */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(
|
||||
repl_dispatch(&sim, "break JOB_UPDATE jobid=0-0-0-32768[4]"));
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_JOB_UPDATE;
|
||||
ev.u.job_update.jobid = b1926_elem(B1926_ARRAY_SEQ, 4);
|
||||
TEST_ASSERT_NOT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
ev.u.job_update.jobid = b1926_elem(B1926_ARRAY_SEQ, 5);
|
||||
TEST_ASSERT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
|
||||
/* JOB_RUSAGE, a range */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(
|
||||
repl_dispatch(&sim, "break JOB_RUSAGE jobid=0-0-0-32768[0-1]"));
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_JOB_RUSAGE;
|
||||
ev.u.job_rusage.jobid = b1926_elem(B1926_ARRAY_SEQ, 1);
|
||||
TEST_ASSERT_NOT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
ev.u.job_rusage.jobid = b1926_elem(B1926_ARRAY_SEQ, 2);
|
||||
TEST_ASSERT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
|
||||
/* JOB_PUSH, any pushed job */
|
||||
b1926_sim_init(&sim);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, "break JOB_PUSH jobid=0-0-0-4242"));
|
||||
memset(&job_a, 0, sizeof(job_a));
|
||||
memset(&job_b, 0, sizeof(job_b));
|
||||
job_a.jobid = 7;
|
||||
job_b.jobid = 4242;
|
||||
jobs[0] = &job_a;
|
||||
jobs[1] = &job_b;
|
||||
memset(&ev, 0, sizeof(ev));
|
||||
ev.type = XCE_CORE_EVENT_JOBS_PUSH;
|
||||
ev.u.jobs_push.jobs = jobs;
|
||||
ev.u.jobs_push.njobs = 2;
|
||||
TEST_ASSERT_NOT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
job_b.jobid = 4243;
|
||||
TEST_ASSERT_NULL(repl_find_event_breakpoint(sim.repl_state, &ev));
|
||||
|
||||
printf("test_event_breakpoint_jobid_dashed_form_matches: PASS\n");
|
||||
}
|
||||
|
||||
/* `info break` and the "set" line echo the filter as typed, not a
|
||||
* re-rendering of the parsed selector. */
|
||||
static void test_info_break_echoes_jobid_filter_as_typed(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
char* captured;
|
||||
|
||||
b1926_sim_init(&sim);
|
||||
captured = capture_dispatch(&sim,
|
||||
"break DECISION jobid=0-0-0-32768[1-3]");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(
|
||||
strstr(captured,
|
||||
"[sim] breakpoint 1 set: DECISION jobid=0-0-0-32768[1-3]\n"),
|
||||
captured);
|
||||
free(captured);
|
||||
TEST_ASSERT_FALSE(repl_dispatch(&sim, "break DECISION jobid=104"));
|
||||
|
||||
captured = capture_dispatch(&sim, "info break");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, "jobid=0-0-0-32768[1-3]"),
|
||||
captured);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, "jobid=104"), captured);
|
||||
free(captured);
|
||||
|
||||
printf("test_info_break_echoes_jobid_filter_as_typed: PASS\n");
|
||||
}
|
||||
|
||||
static void test_begin_round_breakpoint_bare_matches_filtered_does_not(void)
|
||||
{
|
||||
xce_sched_sim_driver_t sim;
|
||||
|
|
@ -2306,6 +2596,53 @@ static void test_decision_breakpoint_pauses_after_decision_output(void)
|
|||
printf("test_decision_breakpoint_pauses_after_decision_output: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1926: a decision breakpoint set with the jobid exactly as the REPL
|
||||
* prints it (fmt_jobid(), the dashed form) fires in a real
|
||||
* sched_sim_driver_run(); before the fix only the raw decimal id matched.
|
||||
* Same fixture and `step 1` script shape as the test above.
|
||||
*/
|
||||
static void test_decision_breakpoint_dashed_jobid_pauses_run(void)
|
||||
{
|
||||
char replay_path[PATH_MAX];
|
||||
char aux_dir[PATH_MAX];
|
||||
char script_path[PATH_MAX];
|
||||
char capture_path[PATH_MAX];
|
||||
char script[160];
|
||||
char bp_marker[112];
|
||||
char jobid_buf[64];
|
||||
FILE* f;
|
||||
char* captured;
|
||||
|
||||
f = write_header_and_snapshot(replay_path, sizeof(replay_path),
|
||||
"_decdash");
|
||||
TEST_ASSERT_NOT_NULL(f);
|
||||
|
||||
append_queue_update_event(f, 1000ULL, "normal");
|
||||
append_jobs_push_event(f, 1500ULL, 555ULL, "normal");
|
||||
append_replay_session(f, 2000ULL);
|
||||
append_end_of_stream_marker(f);
|
||||
fclose(f);
|
||||
|
||||
make_tmp_dir(aux_dir, sizeof(aux_dir), "_decdashaux");
|
||||
snprintf(script_path, sizeof(script_path), "%s/script.txt", aux_dir);
|
||||
snprintf(capture_path, sizeof(capture_path), "%s/capture.out", aux_dir);
|
||||
fmt_jobid(jobid_buf, sizeof(jobid_buf), 555ULL);
|
||||
snprintf(script, sizeof(script), "break DECISION jobid=%s\nstep 1\nquit\n",
|
||||
jobid_buf);
|
||||
write_text_file(script_path, script);
|
||||
|
||||
captured = run_group_d_and_capture(replay_path, script_path, capture_path);
|
||||
|
||||
snprintf(bp_marker, sizeof(bp_marker),
|
||||
"hit breakpoint 1 @ decision: PEND jobid=%s", jobid_buf);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(strstr(captured, bp_marker), captured);
|
||||
|
||||
free(captured);
|
||||
|
||||
printf("test_decision_breakpoint_dashed_jobid_pauses_run: PASS\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* bug-1595 (task 12.5): `step`/`continue` must echo each input event, in
|
||||
* the order it is decoded, BEFORE the session's own resulting decision --
|
||||
|
|
@ -2465,6 +2802,12 @@ int main(void)
|
|||
RUN_TEST(test_event_breakpoint_matches_state_filter);
|
||||
RUN_TEST(test_decision_breakpoint_matches_type_filter);
|
||||
RUN_TEST(test_begin_round_breakpoint_bare_matches_filtered_does_not);
|
||||
RUN_TEST(test_decision_breakpoint_jobid_dashed_form_matches);
|
||||
RUN_TEST(test_decision_breakpoint_jobid_element_and_range_match);
|
||||
RUN_TEST(test_decision_breakpoint_jobid_bare_array_id_and_raw);
|
||||
RUN_TEST(test_break_invalid_jobid_rejected);
|
||||
RUN_TEST(test_event_breakpoint_jobid_dashed_form_matches);
|
||||
RUN_TEST(test_info_break_echoes_jobid_filter_as_typed);
|
||||
|
||||
RUN_TEST(test_info_job_reports_state_queue_priority);
|
||||
RUN_TEST(test_info_job_unknown_reports_not_found);
|
||||
|
|
@ -2486,6 +2829,7 @@ int main(void)
|
|||
|
||||
RUN_TEST(test_event_breakpoint_pauses_repl_at_matching_event);
|
||||
RUN_TEST(test_decision_breakpoint_pauses_after_decision_output);
|
||||
RUN_TEST(test_decision_breakpoint_dashed_jobid_pauses_run);
|
||||
RUN_TEST(test_step_echoes_events_before_session_decisions);
|
||||
RUN_TEST(test_replay_load_reports_total_session_count);
|
||||
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@
|
|||
* (0-0-0-32768[1] and [2]) and a host-selection mismatch (203 selects
|
||||
* mem>20000, which only w02 has).
|
||||
*
|
||||
* Breakpoint filters compare the raw decimal jobid (repl_break.c
|
||||
* repl_u64_str_eq), so `break DECISION jobid=105` names 0-0-0-105.
|
||||
* `break DECISION jobid=105` (a raw packed decimal id) names 0-0-0-105;
|
||||
* since bug-1926 the dashed form 0-0-0-105 names it too.
|
||||
*
|
||||
* Scenarios:
|
||||
* 1. step mode, all jobs, level full, file copy (baseline replay, five
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue