Skip to content

Commit b6e1ccc

Browse files
i#7556 record switches: Record start ordinal of switches (#7786)
Adds the last timestamp to each switch record. Adds the input's instruction ordinal at the start of the segment to each switch record. This can be reliably used to sort within one input across cores. There is some complexity in providing a uniform value in the face of input readahead; the presented value ends up being inclusive of the start instruction, as documented. This matches replay records. Adds unit tests. Also tested on a large drmemtrace and compare to the replay record for a target tid. Issue: #7556
1 parent 4d35369 commit b6e1ccc

File tree

5 files changed

+125
-53
lines changed

5 files changed

+125
-53
lines changed

clients/drcachesim/tests/core_serial.templatex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,13 @@ Core #1 schedule: [A-I_]*
5858
Core #2 schedule: [A-I_]*
5959
Core #3 schedule: [A-I_]*
6060
Core #0 switch-outs:
61-
0 W0.T[0-9]* instrs= .*
61+
0 W0.T[0-9]* @.*
6262
Core #1 switch-outs:
63-
0 W0.T[0-9]* instrs= .*
63+
0 W0.T[0-9]* @.*
6464
Core #2 switch-outs:
65-
0 W0.T[0-9]* instrs= .*
65+
0 W0.T[0-9]* @.*
6666
Core #3 switch-outs:
67-
0 W0.T[0-9]* instrs= .*
67+
0 W0.T[0-9]* @.*
6868
.*
6969

7070
===========================================================================

clients/drcachesim/tests/schedule_stats_nopreempt.templatex

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,11 @@ Core #1 schedule: [A-Ia-i_]*
102102
Core #2 schedule: [A-Ia-i_]*
103103
Core #3 schedule: [A-Ia-i_]*
104104
Core #0 switch-outs:
105-
0 W0.T[0-9]* instrs= .*
105+
0 W0.T[0-9]* @.*
106106
Core #1 switch-outs:
107-
0 W0.T[0-9]* instrs= .*
107+
0 W0.T[0-9]* @.*
108108
Core #2 switch-outs:
109-
0 W0.T[0-9]* instrs= .*
109+
0 W0.T[0-9]* @.*
110110
Core #3 switch-outs:
111-
0 W0.T[0-9]* instrs= .*
111+
0 W0.T[0-9]* @.*
112112
.*

clients/drcachesim/tests/schedule_stats_test.cpp

Lines changed: 90 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* **********************************************************
2-
* Copyright (c) 2021-2025 Google, LLC All rights reserved.
2+
* Copyright (c) 2021-2026 Google, LLC All rights reserved.
33
* **********************************************************/
44

55
/*
@@ -64,9 +64,12 @@ using ::dynamorio::drmemtrace::TRACE_MARKER_TYPE_SYSCALL;
6464
using ::dynamorio::drmemtrace::TRACE_MARKER_TYPE_SYSCALL_TRACE_END;
6565
using ::dynamorio::drmemtrace::TRACE_MARKER_TYPE_SYSCALL_TRACE_START;
6666
using schedule_record_t = ::dynamorio::drmemtrace::schedule_stats_t::schedule_record_t;
67+
using workload_tid_t = ::dynamorio::drmemtrace::schedule_stats_t::workload_tid_t;
6768

6869
static constexpr bool VOLUNTARY = true;
70+
static constexpr bool INVOLUNTARY = false;
6971
static constexpr bool DIRECT = true;
72+
static constexpr bool DEFAULT = false;
7073

7174
// Create a class for testing that has reliable timing.
7275
// It assumes it is only used with one thread and parallel operation
@@ -99,6 +102,26 @@ class mock_schedule_stats_t : public schedule_stats_t {
99102
uint64_t global_time_ = 1;
100103
};
101104

105+
class mock_input_stream_t : public default_memtrace_stream_t {
106+
public:
107+
uint64_t
108+
get_instruction_ordinal() const override
109+
{
110+
// We add 1 to emulate the real scheduler waiting until the next instruction
111+
// boundary before doing any context switch, resulting in the input interface
112+
// ordinal always being 1 higher.
113+
return count + 1;
114+
}
115+
void
116+
add_instruction()
117+
{
118+
++count;
119+
}
120+
121+
private:
122+
int64_t count;
123+
};
124+
102125
class mock_stream_t : public default_memtrace_stream_t {
103126
public:
104127
uint64_t
@@ -110,8 +133,17 @@ class mock_stream_t : public default_memtrace_stream_t {
110133
memtrace_stream_t *
111134
get_input_interface() const override
112135
{
113-
return const_cast<mock_stream_t *>(this);
136+
return cur_input;
114137
}
138+
139+
void
140+
set_input_interface(memtrace_stream_t *stream)
141+
{
142+
cur_input = stream;
143+
}
144+
145+
private:
146+
memtrace_stream_t *cur_input = nullptr;
115147
};
116148

117149
// Bypasses the analyzer and scheduler for a controlled test sequence.
@@ -136,6 +168,9 @@ run_schedule_stats(
136168
per_core[cpu].shard_data = tool.parallel_shard_init_stream(
137169
cpu, per_core[cpu].worker_data, &per_core[cpu].stream);
138170
}
171+
std::unordered_map<workload_tid_t, mock_input_stream_t,
172+
schedule_stats_t::workload_tid_hash_t>
173+
input2stream;
139174
// Walk in lockstep until all are empty.
140175
int num_finished = 0;
141176
while (num_finished < static_cast<int>(memrefs.size())) {
@@ -146,6 +181,11 @@ run_schedule_stats(
146181
per_core[cpu].stream.set_tid(memref.instr.tid);
147182
per_core[cpu].stream.set_workload_id(memref.instr.pid);
148183
per_core[cpu].stream.set_output_cpuid(cpu);
184+
185+
mock_input_stream_t &input =
186+
input2stream[workload_tid_t(memref.instr.pid, memref.instr.tid)];
187+
per_core[cpu].stream.set_input_interface(&input);
188+
149189
if (memref.marker.type == TRACE_TYPE_MARKER) {
150190
switch (memref.marker.marker_type) {
151191
case TRACE_MARKER_TYPE_SYSCALL_TRACE_START:
@@ -158,6 +198,7 @@ run_schedule_stats(
158198
break;
159199
case TRACE_MARKER_TYPE_TIMESTAMP:
160200
per_core[cpu].stream.set_last_timestamp(memref.marker.marker_value);
201+
input.set_last_timestamp(memref.marker.marker_value);
161202
break;
162203
default:;
163204
}
@@ -169,6 +210,10 @@ run_schedule_stats(
169210
per_core[cpu].finished = true;
170211
++num_finished;
171212
}
213+
// Since our mock input stream adds 1 to emulate switching on the next
214+
// instr boundary, we increment its count *after* invoking the tool.
215+
if (type_is_instr(memref.instr.type))
216+
input.add_instruction();
172217
}
173218
}
174219
std::vector<std::vector<schedule_record_t>> record(memrefs.size());
@@ -182,6 +227,9 @@ run_schedule_stats(
182227
for (size_t i = 0; i < record.size(); ++i) {
183228
assert(record[i].size() == (*expected_record)[i].size());
184229
for (size_t j = 0; j < record[i].size(); ++j) {
230+
if (!(record[i][j] == (*expected_record)[i][j])) {
231+
std::cerr << "Record " << i << ", " << j << " mismatch\n";
232+
}
185233
assert(record[i][j] == (*expected_record)[i][j]);
186234
}
187235
}
@@ -252,15 +300,15 @@ test_basic_stats()
252300
};
253301
const std::vector<std::vector<schedule_record_t>> expected_record = {
254302
{
255-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
256-
{ 0, TID_B, /*ins=*/1, /*sys#=*/0, /*lat=*/500, VOLUNTARY },
257-
{ 0, TID_A, /*ins=*/3, /*sys#=*/0, /*lat=*/200, VOLUNTARY, DIRECT },
258-
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
303+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
304+
{ 0, TID_B, /*ins=*/1, /*sys#=*/0, /*lat=*/500, VOLUNTARY, DEFAULT, 2 },
305+
{ 0, TID_A, /*ins=*/3, /*sys#=*/0, /*lat=*/200, VOLUNTARY, DIRECT, 3 },
306+
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 4 },
259307
},
260308
{
261-
{ 0, TID_B, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
262-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
263-
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1 },
309+
{ 0, TID_B, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
310+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 2 },
311+
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
264312
},
265313
};
266314
auto result = run_schedule_stats(memrefs, &expected_record);
@@ -381,15 +429,15 @@ test_basic_stats_with_syscall_trace()
381429
};
382430
const std::vector<std::vector<schedule_record_t>> expected_record = {
383431
{
384-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
385-
{ 0, TID_B, /*ins=*/2, /*sys#=*/0, /*lat=*/500, VOLUNTARY },
386-
{ 0, TID_A, /*ins=*/4, /*sys#=*/0, /*lat=*/200, VOLUNTARY, DIRECT },
387-
{ 0, TID_C, /*ins=*/6, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
432+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
433+
{ 0, TID_B, /*ins=*/2, /*sys#=*/0, /*lat=*/500, VOLUNTARY, DEFAULT, 2 },
434+
{ 0, TID_A, /*ins=*/4, /*sys#=*/0, /*lat=*/200, VOLUNTARY, DIRECT, 3 },
435+
{ 0, TID_C, /*ins=*/6, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 4 },
388436
},
389437
{
390-
{ 0, TID_B, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
391-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
392-
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1 },
438+
{ 0, TID_B, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
439+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 2 },
440+
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
393441
},
394442
};
395443
auto result = run_schedule_stats(memrefs, &expected_record);
@@ -462,14 +510,14 @@ test_idle()
462510
};
463511
const std::vector<std::vector<schedule_record_t>> expected_record = {
464512
{
465-
{ 0, TID_B, /*ins=*/2, /*sys#=*/-1, /*lat=*/-1 },
513+
{ 0, TID_B, /*ins=*/2, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
466514
},
467515
{
468-
{ 0, TID_C, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
469-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
470-
{ 0, TID_C, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
471-
{ 0, TID_C, /*ins=*/2, /*sys#=*/-1, /*lat=*/-1 },
472-
{ 0, TID_A, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
516+
{ 0, TID_C, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
517+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
518+
{ 0, TID_C, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 2 },
519+
{ 0, TID_C, /*ins=*/2, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 3 },
520+
{ 0, TID_A, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 2 },
473521
},
474522
};
475523
auto result = run_schedule_stats(memrefs, &expected_record);
@@ -622,20 +670,20 @@ test_syscall_latencies()
622670
};
623671
const std::vector<std::vector<schedule_record_t>> expected_record = {
624672
{
625-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
626-
{ 0, TID_B, /*ins=*/1, /*sys#=*/12, /*lat=*/500, VOLUNTARY },
627-
{ 0, TID_A, /*ins=*/3, /*sys#=*/167, /*lat=*/200, VOLUNTARY, DIRECT },
628-
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
673+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
674+
{ 0, TID_B, /*ins=*/1, /*sys#=*/12, /*lat=*/500, VOLUNTARY, DEFAULT, 1 },
675+
{ 0, TID_A, /*ins=*/3, /*sys#=*/167, /*lat=*/200, VOLUNTARY, DIRECT, 2 },
676+
{ 0, TID_C, /*ins=*/3, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 1 },
629677
},
630678
{
631-
{ 0, TID_C, /*ins=*/0, /*sys#=*/12, /*lat=*/1000, VOLUNTARY },
632-
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
633-
{ 0, TID_D, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
634-
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
679+
{ 0, TID_C, /*ins=*/0, /*sys#=*/12, /*lat=*/1000, VOLUNTARY, DEFAULT, 1 },
680+
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 1 },
681+
{ 0, TID_D, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
682+
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 2 },
635683
},
636684
{
637-
{ 0, TID_D, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1 },
638-
{ 0, TID_E, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
685+
{ 0, TID_D, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
686+
{ 0, TID_E, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
639687
},
640688
};
641689
auto result = run_schedule_stats(memrefs, &expected_record);
@@ -764,20 +812,20 @@ test_syscall_latencies_with_kernel_trace()
764812
};
765813
const std::vector<std::vector<schedule_record_t>> expected_record = {
766814
{
767-
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
768-
{ 0, TID_B, /*ins=*/2, /*sys#=*/12, /*lat=*/500, VOLUNTARY },
769-
{ 0, TID_A, /*ins=*/4, /*sys#=*/167, /*lat=*/200, VOLUNTARY, DIRECT },
770-
{ 0, TID_C, /*ins=*/6, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
815+
{ 0, TID_A, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
816+
{ 0, TID_B, /*ins=*/2, /*sys#=*/12, /*lat=*/500, VOLUNTARY, DEFAULT, 1 },
817+
{ 0, TID_A, /*ins=*/4, /*sys#=*/167, /*lat=*/200, VOLUNTARY, DIRECT, 2 },
818+
{ 0, TID_C, /*ins=*/6, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 2 },
771819
},
772820
{
773-
{ 0, TID_C, /*ins=*/1, /*sys#=*/12, /*lat=*/1000, VOLUNTARY },
774-
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
775-
{ 0, TID_D, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
776-
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY },
821+
{ 0, TID_C, /*ins=*/1, /*sys#=*/12, /*lat=*/1000, VOLUNTARY, DEFAULT, 1 },
822+
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 2 },
823+
{ 0, TID_D, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
824+
{ 0, TID_E, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, VOLUNTARY, DEFAULT, 2 },
777825
},
778826
{
779-
{ 0, TID_D, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1 },
780-
{ 0, TID_E, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1 },
827+
{ 0, TID_D, /*ins=*/0, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
828+
{ 0, TID_E, /*ins=*/1, /*sys#=*/-1, /*lat=*/-1, INVOLUNTARY, DEFAULT, 1 },
781829
},
782830
};
783831
auto result = run_schedule_stats(memrefs, &expected_record);

clients/drcachesim/tools/schedule_stats.cpp

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ schedule_stats_t::record_context_switch(per_shard_t *shard, int64_t prev_workloa
219219
!(prev_workload_id == INVALID_WORKLOAD_ID && prev_tid == IDLE_THREAD_ID &&
220220
workload_id != INVALID_WORKLOAD_ID && tid != INVALID_THREAD_ID);
221221
uint64_t instr_delta = shard->counters.instrs - shard->switch_start_instrs;
222+
memtrace_stream_t *istream = shard->stream->get_input_interface();
222223

223224
if (shard->thread_sequence.empty()) {
224225
std::lock_guard<std::mutex> lock(prev_core_mutex_);
@@ -230,6 +231,7 @@ schedule_stats_t::record_context_switch(per_shard_t *shard, int64_t prev_workloa
230231
record.workload = prev_workload_id;
231232
record.tid = prev_tid;
232233
record.instructions = instr_delta;
234+
record.input_instr_start_ordinal = shard->switch_start_input_instr_ordinal;
233235
if (shard->saw_syscall || shard->saw_exit) {
234236
++shard->counters.voluntary_switches;
235237
record.voluntary = true;
@@ -274,6 +276,12 @@ schedule_stats_t::record_context_switch(per_shard_t *shard, int64_t prev_workloa
274276
shard->counters.tid2instrs_per_switch,
275277
workload_tid_t(prev_workload_id, prev_tid), kSwitchBinSize);
276278
hist_ptr->add(instr_delta);
279+
} else {
280+
// Update the start ordinal on a swap-in after idle.
281+
if (shard->switch_start_input_instr_ordinal == 0 && istream != nullptr)
282+
shard->switch_start_input_instr_ordinal = std::max(
283+
// Normalize to 1 regardless of whether the input was pre-read or not.
284+
istream->get_instruction_ordinal(), static_cast<uint64_t>(1));
277285
}
278286
if (knob_verbose_ >= 2) {
279287
std::cerr << "Core #" << std::setw(2) << shard->core
@@ -293,6 +301,12 @@ schedule_stats_t::record_context_switch(per_shard_t *shard, int64_t prev_workloa
293301
prev_core_[workload_tid] = shard->core;
294302
}
295303
shard->switch_start_instrs = shard->counters.instrs;
304+
// Set the start ordinal if we're switching to an input; if idle or wait
305+
// will be in between we'll set on a the next input in the !add_to_counts above.
306+
shard->switch_start_input_instr_ordinal = (istream == nullptr)
307+
? 0
308+
// Normalize to 1 regardless of whether the input was pre-read or not.
309+
: std::max(istream->get_instruction_ordinal(), static_cast<uint64_t>(1));
296310
}
297311
// The idle and wait strings are handled by the caller.
298312
if (tid != INVALID_THREAD_ID && tid != IDLE_THREAD_ID) {
@@ -303,7 +317,6 @@ schedule_stats_t::record_context_switch(per_shard_t *shard, int64_t prev_workloa
303317
shard->cur_segment_instrs = 0;
304318
}
305319
if (knob_verbose_ >= 2) {
306-
memtrace_stream_t *istream = shard->stream->get_input_interface();
307320
std::ostringstream line;
308321
line << "Core #" << std::setw(2) << shard->core << " @" << std::setw(9)
309322
<< shard->stream->get_record_ordinal() << " refs, " << std::setw(9)
@@ -730,7 +743,8 @@ schedule_stats_t::print_results()
730743
++i) {
731744
const schedule_record_t &record = shard.second->switch_record[i];
732745
std::cerr << std::setw(5) << i << " W" << record.workload << ".T" << std::left
733-
<< std::setw(6) << record.tid << std::right
746+
<< std::setw(6) << record.tid << std::right << " @ "
747+
<< std::setw(11) << record.input_instr_start_ordinal
734748
<< " instrs=" << std::setw(9) << record.instructions
735749
<< " sys#=" << std::setw(4) << record.syscall_number
736750
<< " latency=" << std::setw(7) << record.syscall_latency

clients/drcachesim/tools/schedule_stats.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ class schedule_stats_t : public analysis_tool_t {
316316
instructions == rhs.instructions &&
317317
syscall_number == rhs.syscall_number &&
318318
syscall_latency == rhs.syscall_latency && voluntary == rhs.voluntary &&
319-
direct == rhs.direct;
319+
direct == rhs.direct &&
320+
input_instr_start_ordinal == rhs.input_instr_start_ordinal;
320321
}
321322
int64_t workload = INVALID_WORKLOAD_ID;
322323
memref_tid_t tid = INVALID_THREAD_ID;
@@ -333,6 +334,14 @@ class schedule_stats_t : public analysis_tool_t {
333334
bool voluntary = false;
334335
// Whether a direct switch request.
335336
bool direct = false;
337+
// The input's absolute ordinal at the start of this segment.
338+
// This is inclusive of the segment's first instruction: so it starts at 1,
339+
// and this value minus 1 should be used when skipping to this point.
340+
// (This aligns with the values in schedule_record_t.)
341+
// (Switches are currently always at instruction boundaries.)
342+
// Providing this enables viewing by input across cores.
343+
// (The timestamp does not help as it is not synchronized across cores.)
344+
uint64_t input_instr_start_ordinal = 1;
336345
};
337346

338347
counters_t
@@ -388,6 +397,7 @@ class schedule_stats_t : public analysis_tool_t {
388397
uint64_t segment_start_microseconds = 0;
389398
intptr_t filetype = 0;
390399
uint64_t switch_start_instrs = 0;
400+
uint64_t switch_start_input_instr_ordinal = 1; // Inclusive, so start at 1.
391401
bool in_syscall_trace = false;
392402
// A complete record of the switches.
393403
std::vector<schedule_record_t> switch_record;

0 commit comments

Comments
 (0)