diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 119c0c9..8d4d662 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -250,6 +250,10 @@ jobs: okprof=$(find "$PWD/target" -path '*bin*' \( -name okprof -o -name okprof.exe \) -type f | head -1) [ -n "$okprof" ] || { echo "::error::okprof did not build"; exit 1; } echo "OKPROF=$okprof" >> "$GITHUB_ENV" + - name: okprof forwards environment values and the child's status + run: | + python=python3; [ "$RUNNER_OS" = Windows ] && python=python + "$python" tools/tests/record_env.py --okprof "$OKPROF" - name: The same bytes run: | a='${{ matrix.arch }}' diff --git a/README.md b/README.md index 7a71e98..9c136c7 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,13 @@ refuses a later run of the same segment, the loader copies only the refused run. read-only pages remain intact. A copied executable run is published separately; refusal to publish rejects the load. This corrects a startup access violation in profiled images with a short tail. +**Unit memory observation on Linux** (0.4.5): the runner uses openkal-linux 0.24.2, +which avoids opening RSS records for unrelated process groups. Complete group charges, +refused-query fallback, immediate observation and the supervised sampling intervals remain. + +**Profiling environment** (0.4.5): `okprof record` forwards environment values, including +empty values, and sets the requested `OKRUN_PROF` value for the child. + **Read-only confinement on Linux** (0.4.3): the runner is built over openkal-linux 0.24.1. It claims confinement only with Landlock ABI 3 or later, whose policy can prevent file truncation in a read-only grant. Earlier ABIs refuse a confinement request before starting a program. The diff --git a/bind/mcpp.toml b/bind/mcpp.toml index 0ac37eb..9d9009b 100644 --- a/bind/mcpp.toml +++ b/bind/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-bind" -version = "0.4.4" +version = "0.4.5" # `mcpp::xpkg_request', `mcpp::decision' (protocol 15). mcpp = ">=2026.10.1.3" description = "A build plugin: a library published as an ok image (.okso) and a C header is called from a program of any system, through a binding generated from the header and libokrun." diff --git a/ld/mcpp.toml b/ld/mcpp.toml index 8d9f0d8..88de966 100644 --- a/ld/mcpp.toml +++ b/ld/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-ld" -version = "0.4.4" +version = "0.4.5" description = "libokld: the ok format (openkal image format) --- its definition, a reader of bytes, a checker, a signer, and a loader. Freestanding: no C library, no exceptions." license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/run/mcpp.toml b/run/mcpp.toml index 00a52b0..6bd6a70 100644 --- a/run/mcpp.toml +++ b/run/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-run" -version = "0.4.4" +version = "0.4.5" description = "libokrun: the runner's core --- an ok image's names bound to this process's one openkal implementation, in the image's calling convention, and its entry entered." license = "Apache-2.0" authors = ["mcpplibs"] @@ -15,7 +15,7 @@ openkal = "0.23.0" openkal-ld = { path = "../ld" } [target.linux.dependencies] -openkal-linux = "0.24.1" +openkal-linux = "0.24.2" [target.macos.dependencies] openkal-macos = "0.21.3" diff --git a/tools/mcpp.toml b/tools/mcpp.toml index 2bfdbbe..aa0fc5c 100644 --- a/tools/mcpp.toml +++ b/tools/mcpp.toml @@ -1,7 +1,7 @@ [package] namespace = "mcpplibs" name = "openkal-tools" -version = "0.4.4" +version = "0.4.5" description = "okrun, which runs an ok image on this system, and okld, the format's tool on a build machine (check, note, sign, inspect)." license = "Apache-2.0" authors = ["mcpplibs"] diff --git a/tools/src/okprof.cpp b/tools/src/okprof.cpp index fa9e3ae..47ca379 100644 --- a/tools/src/okprof.cpp +++ b/tools/src/okprof.cpp @@ -624,12 +624,15 @@ int record_cmd(std::vector args) { const std::string spec = std::format("OKRUN_PROF={}:{}", runner_what, std::filesystem::absolute(dir, ec).generic_string()); std::vector env; for (kal_uintptr i = 0; i < kal_env_var_count(); ++i) { - char entry[16384]; - const kal_intptr n = kal_env_var_at(i, entry, sizeof entry); - if (n <= 0 || n >= static_cast(sizeof entry)) continue; - std::string e(entry, static_cast(n)); - if (e.starts_with("OKRUN_PROF=")) continue; - env.push_back(std::move(e)); + // var_at enumerates names; var obtains the value, including an empty one. + char name[16384], value[16384]; + const kal_intptr n = kal_env_var_at(i, name, sizeof name); + if (n <= 0 || n >= static_cast(sizeof name)) continue; + const std::string key(name, static_cast(n)); + if (key == "OKRUN_PROF") continue; + const kal_intptr v = kal_env_var(name, static_cast(n), value, sizeof value); + if (v < 0 || v >= static_cast(sizeof value)) continue; + env.push_back(key + "=" + std::string(value, static_cast(v))); } if (!runner_what.empty()) env.push_back(spec); std::vector argv, envp; diff --git a/tools/tests/record_env.py b/tools/tests/record_env.py new file mode 100644 index 0000000..5e3dea4 --- /dev/null +++ b/tools/tests/record_env.py @@ -0,0 +1,33 @@ +"""Exercise the native record wrapper's child environment and exit status.""" +import argparse +import os +from pathlib import Path +import subprocess +import sys +import tempfile + +parser = argparse.ArgumentParser(description=__doc__) +parser.add_argument('--okprof', required=True, type=Path) +args = parser.parse_args() +okprof = args.okprof.resolve().as_posix() +python = Path(sys.executable).resolve().as_posix() +env = os.environ.copy() +env.update(OKPROF_ENV_VALUE='spaces and=equals', OKPROF_ENV_EMPTY='', + OKRUN_PROF='this value must be replaced') +child = ( + 'import os,sys; ' + 'assert os.environ.get("OKPROF_ENV_VALUE") == "spaces and=equals"; ' + 'assert os.environ.get("OKPROF_ENV_EMPTY", "missing") == ""; ' + 'assert os.environ.get("OKRUN_PROF", "").startswith("calls,counters:"); ' + 'print("record passed values, empty entries and its profiling setting"); ' + 'sys.exit(int(sys.argv[1]))' +) +with tempfile.TemporaryDirectory(prefix='okprof-record-') as directory: + for status in (0, 23): + result = subprocess.run( + [okprof, 'record', '-o', Path(directory).as_posix(), '--', + python, '-c', child, str(status)], + env=env, text=True, capture_output=True, timeout=30) + assert result.returncode == status, (result.returncode, result.stdout, result.stderr) + assert 'record passed values, empty entries and its profiling setting' in result.stdout +print('okprof record: inherited values, empty values, profiling override and child status held')