Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

i#7216: noise generator basic structure #7283

Open
wants to merge 43 commits into
base: master
Choose a base branch
from

Conversation

edeiana
Copy link
Contributor

@edeiana edeiana commented Feb 18, 2025

Scaffolding for the noise generator.
Adds noise_generator_t as a subclass of the iterator reader_t.
Currently noise_generator_t only produces TRACE_TYPE_READ records
with address 0xdeadbeef (easier to spot in tools' output). These records are
preceded by TRACE_TYPE_THREAD, TRACE_TYPE_PID,
TRACE_MARKER_TYPE_TIMESTAMP and followed by TRACE_TYPE_THREAD_EXIT
as this is the sequence of records the scheduler expects.
As of now, the user can add a single-process, single-thread noise generator workload to
the scheduler's input workloads through the -noise_generator_enable analyzer option
in the drmemtrace framework. Adding a noise_generator_t to the scheduler's input
workloads is done by the factory class noise_generator_factory_t through its
create_noise_generator() method, which returns a noise generator as an
input_reader_t that can then be added to the scheduler's input_workload_t
vector. Currently, there are three knobs the user can control in the noise generator:
PID, TID, and number of noise records to generate. These knobs are encapsulated
in the noise_generator_info_t struct. For now they are pre-set with default values:
PID = 1, TID = 1, number of noise records to generate = 1000.

Adds a unit test in scheduler_unit_tests.cpp and an end-to-end test leveraging
the schedule_stats tool.

Issue: #7216

@edeiana edeiana changed the title i#7216 noise generator basic structure i#7216: noise generator basic structure Feb 21, 2025
@edeiana edeiana marked this pull request as ready for review February 21, 2025 11:38
@edeiana edeiana requested a review from derekbruening March 6, 2025 11:42
- Added #dynamorio::drmemtrace::noise_generator_t scaffolding as a
#dynamorio::drmemtrace::reader_t to produce synthetic trace records in the drmemtrace
framework.
- Added #dynamorio::drmemtrace::noise_generator_info_t, which contains the metadata
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Make these all a nested list under a top level item on adding noise generator support?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Add noise generator to workload_inputs.
if (noise_generator_factory_.is_enabled()) {
// TODO i#7216: here can be a good place to analyze the workloads in order to
// tweak noise_generator_info_t parameters. We'd also need a method to replace
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Double space

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed, thanks!

std::string noise_generator_factory_error_string =
noise_generator_factory_.get_error_string();
if (!noise_generator_factory_error_string.empty()) {
error_string_ = noise_generator_factory_error_string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just assign this instead of using the local var? If we don't want to clobber a pre-existing error: this is already clobbering it; could append.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done (with append).

noise_generator_factory_t<trace_entry_t, record_reader_t>::create_noise_generator(
addr_t pid, addr_t tid, uint64_t num_records)
{
error_string_ = "Noise generator is not suppported for record_reader_t";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need this to create core-sharded traces via record_filter. Include a TODO or XXX so we know we plan to implement this. Or implement it now.

Copy link
Contributor Author

@edeiana edeiana Mar 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comment. I prefer to break it down, this PR is already on the large side.

get_error_string();

void
add_noise_generator_to_workloads(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems an inflexible approach: it doesn't allow adding a noise thread within a an existing process, or a set of noise processes each with different thread counts, or a set of noise threads each with different noise characteristics (load/store mix, etc.). This interface does not seem like it will work for future uses.

I'm going to stick with my original suggestion at https://github.com/DynamoRIO/dynamorio/pull/7283/files#r1973978208 of a factory that takes in a noise_generator_info_t and returns a single input_reader_t. That gives the most flexibility.

Another layer or maybe another method in the same factory could provide convenience support for creating a set of N noise generator threads/processes, but the user can do that through iteration and has full control over the config of each. I guess non-colliding tids/pids is a factor: but this PR doesn't solve that either if the factory is used twice. Maybe the factory for a single one increments an atomic integer to use for the id.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a factory that takes in a noise_generator_info_t and returns a single input_reader_t. That gives the most flexibility.

Agreed. Done.
noise_generator_info_t now controls a single noise_generator_t for maximum flexibility.
create_noise_generator() in noise_generator_factory_t takes a noise_generator_info_t and returns a single-process, single-thread noise_generator_t as an input_reader_t.

Another layer or maybe another method in the same factory could provide convenience support for creating a set of N noise generator threads/processes, but the user can do that through iteration and has full control over the config of each.

Since we don't have a clear use for that right now, I removed the add_noise_generator_to_workloads() convenience method. The user is currently responsible for non-colliding PIDs (I think we can have colliding TIDs, as long as they belong to different processes).

init();

bool
is_enabled();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This "enabled" seems odd: it's not used to gate the add_ function; it is in fact ignored by the internal code of this class. It doesn't seem like it belongs inside the class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, moved to the analyzer instead.

@edeiana edeiana requested a review from derekbruening March 9, 2025 09:41
typename sched_type_t::input_reader_t noise_generator_reader =
noise_generator_factory_.create_noise_generator(noise_generator_info);
// Check for errors.
error_string_ = error_string_ + noise_generator_factory_.get_error_string();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+= should work

if (noise_generator_enabled_) {
// TODO i#7216: here can be a good place to analyze the workloads in order to
// tweak noise_generator_info_t parameters. For now we use noise_generator_info_t
// default values.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also comment that for now we create a single noise thread but we might want more in the future?

op_noise_generator_enable(DROPTION_SCOPE_FRONTEND, "noise_generator_enable", false,
"Enables noise generation.",
"Enables the scheduler to interleave trace records with "
"synthetic records produced by a noise generator.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This description is not clear: it could be interpreted as not actually adding any noise but just enabling someone else to add noise. Make it clear that it adds actual noise inputs. Maybe say right now it adds a single input but that may change in the future, to give us room to auto-add more threads/processes or add other CLI options as we decide in the future.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe even change the name to add_noise_generator instead of "enable"? That seems clearer.

addr_t pid = 1;
addr_t tid = 1;
uint64_t num_records_to_generate = 1000;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a TODO or XXX about adding parameters controlling the mix of noise: load/store mix, address patterns, etc., so it's clear we don't consider this to be the final set of option fields.

* Contains metadata information to drive the noise generation.
*/
struct noise_generator_info_t {
noise_generator_info_t() {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use = default;

std::unique_ptr<mock_noise_generator_t>(new mock_noise_generator_t(
noise_generator_info, noise_generator_addr_to_generate)),
std::unique_ptr<mock_noise_generator_t>(new mock_noise_generator_t()),
INVALID_THREAD_ID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this match the info tid? Needs a named constant?

std::vector<scheduler_t::input_workload_t> sched_inputs;
sched_inputs.emplace_back(std::move(readers));
if (scheduler.init(sched_inputs, 1,
scheduler_t::make_scheduler_serial_options(/*verbosity=*/4)) !=
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Serial shouldn't be supported as the max timestamps will put the noise at the very end.
You could do the same as serial but pass DEPENDENCY_IGNORE (hmm would have to review the scheduler to see what order it would do then).

std::unique_ptr<mock_noise_generator_t>(new mock_noise_generator_t(
noise_generator_info, noise_generator_addr_to_generate)),
std::unique_ptr<mock_noise_generator_t>(new mock_noise_generator_t()),
INVALID_THREAD_ID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test of using the factory. Seems ok to change this test to use the factory instead?

// There is just one output so we expect to always see 0 as the ordinal.
assert(stream->get_input_workload_ordinal() == 0);
// All TRACE_TYPE_READ records are generated by the noise generator and their
// addr is always the constant noise_generator_addr_to_generate.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to do this arrangement with the only reads in the noise: you have the tid of the noise input, and you also have the ordinal of the noise input. You can use one of those, and then put reads in the other inputs, which seems a stronger check.

Schedule stats tool results:
Total counts:
*[1-9][0-9]* cores
2 threads:.*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The small_trace_file IIRC is asm with a fixed known count of record types, and other tests rely on those? So should we check that the result has small_trace_file read count plus 100? Is 100 hidden from us?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants