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;
6464using ::dynamorio::drmemtrace::TRACE_MARKER_TYPE_SYSCALL_TRACE_END;
6565using ::dynamorio::drmemtrace::TRACE_MARKER_TYPE_SYSCALL_TRACE_START;
6666using 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
6869static constexpr bool VOLUNTARY = true ;
70+ static constexpr bool INVOLUNTARY = false ;
6971static 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+
102125class mock_stream_t : public default_memtrace_stream_t {
103126public:
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);
0 commit comments