Skip to content

Introduce dtoctl, a CLI wrapper for running programs with DTO preloaded - #33

Closed
honggyukim wants to merge 3 commits into
intel:mainfrom
skhynix:feat/dtoctl-tool-v2
Closed

honggyukim wants to merge 3 commits into
intel:mainfrom
skhynix:feat/dtoctl-tool-v2

Conversation

@honggyukim

Copy link
Copy Markdown
Contributor

Using DTO currently requires understanding two orthogonal mechanisms before a single
program can be offloaded to DSA. The library has to be injected into the target
through LD_PRELOAD, and its behavior is steered entirely through many DTO_*
environment variables that have to be discovered in the README and then exported
by hand. That is easy to get wrong: the preload path and the variable names are
stringly typed, an exported variable silently leaks into every later command in
the same shell, and there is no validation or discoverability at the point of
use.

This adds dtoctl, a small wrapper that turns the setup into a single
self-describing command line. It parses options, translates them into the
environment the library expects, prepends the resolved libdto.so to
LD_PRELOAD, and then execvp()s the target program, so nothing lingers in the
caller's shell.

So the previous usage

export LD_PRELOAD=/usr/local/lib/libdto.so
export DTO_WAIT_METHOD=busypoll
export DTO_CPU_SIZE_FRACTION=0.33
./prog

collapses to

dtoctl -w busypoll -c 0.33 ./prog

Using the DTO library today requires the user to understand two orthogonal
mechanisms before a single program can be offloaded to DSA.  The library has
to be injected into the target through LD_PRELOAD, and its behavior is steered
entirely through a set of roughly twenty DTO_* environment variables that must
be discovered by reading the manual and then exported by hand.  This is easy to
get wrong: the LD_PRELOAD path and the variable names are stringly typed, an
exported variable silently leaks into every later command in the same shell,
and there is no validation or discoverability at the point of use.

Provide a small wrapper, dtoctl, that turns this into a single self-describing
command line.  It follows the same model as hmctl from the hmsdk project:
parse options, translate them into the environment the library expects, and
then exec the target program so no state lingers in the caller's shell.  On top
of that model dtoctl also takes care of the injection itself by prepending the
resolved libdto.so to LD_PRELOAD, which is possible because DTO works by
transparently interposing on the standard memory APIs.

Every DTO_* variable is exposed as an option so the mapping stays obvious and
greppable.  Only options that are actually given are written to the child
environment; anything left unset keeps whatever the caller already exported or
the library's own default, and an explicit option wins over an inherited
variable.  The library path is resolved from -l/--library, then $DTO_LIBRARY,
then a compiled-in default, and execvp() is used so a bare command name on
PATH works as well as an explicit path.

Build and install dtoctl with both Make and CMake.  Set its compiled-in
library path to match the installation directory, since CMake's default
/usr/local/lib differs from the Makefile's /usr/lib64.  Allow that path to be
overridden with DTOCTL_LIBPATH.

With this in place the previous incantation

  $ export LD_PRELOAD=/usr/lib64/libdto.so
  $ export DTO_WAIT_METHOD=busypoll
  $ export DTO_CPU_SIZE_FRACTION=0.33
  $ ./prog

collapses to

  $ dtoctl -w busypoll -c 0.33 ./prog

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
The dtoctl options mirror the DTO_* environment variables, but a user coming to
the tool for the first time still needs a single authoritative place that
explains what each option does, what values it accepts, and how the library
path and LD_PRELOAD are resolved.  Scattering that information across the source
help text alone makes it hard to consult without running the tool.

Add a manual page in the same troff-friendly markdown form the hmsdk project
uses for hmctl, and render it as a section 1 man page for the user command.
Besides the per-option reference it documents the library resolution order and
the override semantics, and it gathers the common tuning goals - latency, power,
and cache-pollution avoidance - as ready-to-run examples such as

  $ dtoctl -w umwait -c 0.0 --no-auto-adjust ./prog

so users can map an intent directly onto a command instead of reverse
engineering it from the individual variables.

Keep the markdown as the source form and generate dtoctl.1 with pandoc during
the Makefile and CMake builds.  Both build systems install the generated page
under man1, so it can be read with

  $ man 1 dtoctl

Pandoc is optional: when it is unavailable, print a message recommending its
installation and skip manual generation.  The Makefile can still install an
already generated page.  Ignore its generated doc/dtoctl.1 and remove it on
make clean; CMake keeps its generated page in the build directory.

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
The README is where users first learn how to configure and run DTO, and until
now it only described the manual LD_PRELOAD and DTO_* export workflow.  A reader
following it has no reason to know that dtoctl exists or that it removes most of
that ceremony, so the easier path stays hidden precisely where people look for
guidance.

Introduce dtoctl after the environment variable reference and the common usage
models that accompany it, so that discussion stays in one piece and the new
section can point back at those usage models.  Contrast the old export sequence
with the equivalent

  $ dtoctl -w busypoll -c 0.33 ./prog

and spell out the resolution order for libdto.so along with the rule that only
the options actually passed are applied.

Keep the per-option reference in dtoctl(1) instead of repeating it here.  The
option list already exists in the --help output and in the manual, and a third
copy in the README would only be one more place to forget when an option
changes.

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
Copilot AI lite review requested due to automatic review settings September 6, 2026 15:35
@honggyukim

Copy link
Copy Markdown
Contributor Author

Hi @byrnedj, this is the work that I showed you before. Please have a look. Thanks!

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

There are a few correctness/UX issues (notably --stats-file not enabling stats) plus build cleanup gaps and missing test coverage for the new CLI behavior.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR introduces dtoctl, a small CLI wrapper that configures DTO via command-line options, sets the corresponding DTO_* environment variables, prepends libdto.so to LD_PRELOAD, and then execs the target program to avoid leaking configuration into the caller’s shell.

Changes:

  • Add dtoctl executable that translates CLI options into DTO_* env vars and composes LD_PRELOAD.
  • Add user documentation for dtoctl (README section + pandoc-generated dtoctl(1) manpage source).
  • Update Makefile/CMake build and install flows to build/install dtoctl and optionally generate/install the man page.
File summaries
File Description
README.md Documents dtoctl usage and library path resolution behavior.
Makefile Builds/installs dtoctl and optionally generates/installs dtoctl(1) via pandoc.
dtoctl.c Implements the dtoctl CLI wrapper (arg parsing, env var mapping, LD_PRELOAD composition, exec).
doc/dtoctl.md Adds the dtoctl(1) manual source (pandoc markdown).
CMakeLists.txt Adds dtoctl build/install rules and optional manpage generation/install.
.gitignore Ignores the dtoctl binary and generated doc/dtoctl.1.
Review details
  • Files reviewed: 5/6 changed files
  • Comments generated: 4
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread dtoctl.c
Comment on lines +244 to +246
case OPT_STATS_FILE:
opts->stats_file = arg;
break;
Comment thread Makefile
Comment on lines 57 to +59
clean:
rm -rf *.o *.so dto-test
rm -rf *.o *.so dto-test dtoctl
rm -f doc/dtoctl.1
Comment thread dtoctl.c
free(preload);
}

int main(int argc, char *argv[])
Comment thread dtoctl.c
execvp(opts.exename, argv);
perror(opts.exename);

return -1;
byrnedj added a commit that referenced this pull request Oct 27, 2026
…cks up #33) (#38)

* Add dtoctl wrapper to drive DTO without manual LD_PRELOAD setup

Using the DTO library today requires the user to understand two orthogonal
mechanisms before a single program can be offloaded to DSA.  The library has
to be injected into the target through LD_PRELOAD, and its behavior is steered
entirely through a set of roughly twenty DTO_* environment variables that must
be discovered by reading the manual and then exported by hand.  This is easy to
get wrong: the LD_PRELOAD path and the variable names are stringly typed, an
exported variable silently leaks into every later command in the same shell,
and there is no validation or discoverability at the point of use.

Provide a small wrapper, dtoctl, that turns this into a single self-describing
command line.  It follows the same model as hmctl from the hmsdk project:
parse options, translate them into the environment the library expects, and
then exec the target program so no state lingers in the caller's shell.  On top
of that model dtoctl also takes care of the injection itself by prepending the
resolved libdto.so to LD_PRELOAD, which is possible because DTO works by
transparently interposing on the standard memory APIs.

Every DTO_* variable is exposed as an option so the mapping stays obvious and
greppable.  Only options that are actually given are written to the child
environment; anything left unset keeps whatever the caller already exported or
the library's own default, and an explicit option wins over an inherited
variable.  The library path is resolved from -l/--library, then $DTO_LIBRARY,
then a compiled-in default, and execvp() is used so a bare command name on
PATH works as well as an explicit path.

Build and install dtoctl with both Make and CMake.  Set its compiled-in
library path to match the installation directory, since CMake's default
/usr/local/lib differs from the Makefile's /usr/lib64.  Allow that path to be
overridden with DTOCTL_LIBPATH.

With this in place the previous incantation

  $ export LD_PRELOAD=/usr/lib64/libdto.so
  $ export DTO_WAIT_METHOD=busypoll
  $ export DTO_CPU_SIZE_FRACTION=0.33
  $ ./prog

collapses to

  $ dtoctl -w busypoll -c 0.33 ./prog

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>

* Add a dtoctl man page

The dtoctl options mirror the DTO_* environment variables, but a user coming to
the tool for the first time still needs a single authoritative place that
explains what each option does, what values it accepts, and how the library
path and LD_PRELOAD are resolved.  Scattering that information across the source
help text alone makes it hard to consult without running the tool.

Add a manual page in the same troff-friendly markdown form the hmsdk project
uses for hmctl, and render it as a section 1 man page for the user command.
Besides the per-option reference it documents the library resolution order and
the override semantics, and it gathers the common tuning goals - latency, power,
and cache-pollution avoidance - as ready-to-run examples such as

  $ dtoctl -w umwait -c 0.0 --no-auto-adjust ./prog

so users can map an intent directly onto a command instead of reverse
engineering it from the individual variables.

Keep the markdown as the source form and generate dtoctl.1 with pandoc during
the Makefile and CMake builds.  Both build systems install the generated page
under man1, so it can be read with

  $ man 1 dtoctl

Pandoc is optional: when it is unavailable, print a message recommending its
installation and skip manual generation.  The Makefile can still install an
already generated page.  Ignore its generated doc/dtoctl.1 and remove it on
make clean; CMake keeps its generated page in the build directory.

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>

* Document dtoctl in the README

The README is where users first learn how to configure and run DTO, and until
now it only described the manual LD_PRELOAD and DTO_* export workflow.  A reader
following it has no reason to know that dtoctl exists or that it removes most of
that ceremony, so the easier path stays hidden precisely where people look for
guidance.

Introduce dtoctl after the environment variable reference and the common usage
models that accompany it, so that discussion stays in one piece and the new
section can point back at those usage models.  Contrast the old export sequence
with the equivalent

  $ dtoctl -w busypoll -c 0.33 ./prog

and spell out the resolution order for libdto.so along with the rule that only
the options actually passed are applied.

Keep the per-option reference in dtoctl(1) instead of repeating it here.  The
option list already exists in the --help output and in the manual, and a third
copy in the README would only be one more place to forget when an option
changes.

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>

* dto: honor DTO_WAIT_METHOD=yield

The yield wait method is implemented in dsa_wait() and documented in
the README, but dsa_init() never matched "yield" when parsing
DTO_WAIT_METHOD, so DTO_WAIT_METHOD=yield silently ran with busypoll.


* dtoctl: validate options, resolve the library up front, add --dry-run

Make the wrapper catch the mistakes it exists to prevent instead of
passing them on to libdto, which silently falls back to a default:

- Validate every option value: wait method names, integer ranges for
  --numa-aware and --log-level, a cpu fraction in [0, 1), a positive
  umwait delay, and non-empty paths and WQ list.  --min-bytes takes an
  optional K/M/G suffix and is passed on as plain bytes; libdto would
  read "64K" as 64 and "abc" as 0.
- Resolve libdto.so to an absolute path and fail with exit status 125
  if it cannot be read.  ld.so only warns about a missing preload and
  runs the program without DTO, and a relative path stopped working as
  soon as the program changed directory.  When built with
  LIBDTO_REL_PATH, prefer the library installed next to dtoctl so a
  relocated install finds its own copy.
- Drop other libdto.so entries from an inherited LD_PRELOAD, so one
  process never loads two copies of DTO.
- --stats-file implies --stats; libdto ignores DTO_STATS_FILE otherwise.
- Add -i/--ignore-env to unset inherited DTO_* variables.  The --no-*
  flags can only turn features off, so this is the way to undo an
  exported DTO_USESTDC_CALLS=1 or DTO_DSA_MEMCPY=0.
- Add --dry-run to print the resolved library, LD_PRELOAD, each DTO_*
  variable with whether it came from an option or the environment, and
  the command, without running it.
- Add --version, and exit with 126/127 when the program cannot be run
  or is not found, as env(1) and timeout(1) do.
- Make the options local to main() and drop dead argument handling.


* dtoctl: follow the install prefix, report the version, add tests

CMake cached the default library path on the first configure, so a
later CMAKE_INSTALL_PREFIX change was ignored, and "cmake --install
--prefix" produced a dtoctl that preloaded /usr/local/lib/libdto.so.
Compute the default on every configure, pass dtoctl the library's path
relative to its own bindir so a relocated install finds its own copy,
and keep DTOCTL_LIBPATH as an explicit override.

Pass the project version to both builds for --version, and make the
Makefile's install target build dtoctl first.

Add tests/test_dtoctl.sh as the "dtoctl" CTest (label functional).  It
needs no DSA: it checks the option to variable mapping, validation,
library resolution, LD_PRELOAD handling and exit statuses through
--dry-run and the program's environment, and that libdto applies the
settings through its own configuration trace.


* docs: document dtoctl's validation, library resolution and --dry-run

Update the dtoctl man page and README section for the new behavior:
option validation, -i/--ignore-env, --dry-run, --version, size
suffixes, --stats-file implying --stats, how the library is resolved,
and the exit statuses.  Add a note on sudo, which resets the
environment and strips LD_PRELOAD when it sits between dtoctl and the
program, and on settings being inherited by child processes.

Fix stale defaults in the README's variable list: DTO_WAIT_METHOD
defaults to busypoll and DTO_MIN_BYTES to 65536, tpause is a valid
wait method, and DTO_LOG_FILE gets .<program>.<pid> appended.  Describe
the dtoctl test in TESTING.md.


* gitignore: ignore the libdto_explicit build output

---------

Signed-off-by: Honggyu Kim <honggyu.kim@sk.com>
Co-authored-by: Honggyu Kim <honggyu.kim@sk.com>
@byrnedj

byrnedj commented Oct 27, 2026

Copy link
Copy Markdown
Contributor

Thanks, we actually merged this here: 5dfa009

@byrnedj byrnedj closed this Oct 27, 2026
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.

3 participants