feat: add dump_stats support

This commit is contained in:
Paul Pan 2024-03-13 16:35:47 +08:00
parent a6ec85c1a0
commit 25ba8d7076
5 changed files with 56 additions and 1 deletions

View File

@ -271,10 +271,37 @@ bool initNsFromParent(nsjconf_t *nsjconf, pid_t pid) {
return initNsFromParentCpu(nsjconf, pid); return initNsFromParentCpu(nsjconf, pid);
} }
void dumpCgroupStats(nsjconf_t *nsjconf, const std::string &cgroup_path) {
LOG_D("Dump stats to '%s'", nsjconf->dump_stats_file.c_str());
std::ofstream dump_file(nsjconf->dump_stats_file);
if (!dump_file.is_open()) {
PLOG_W("Failed to open dump stats file '%s'", nsjconf->dump_stats_file.c_str());
return;
}
const auto resources = {"cpu.stat", "io.stat", "memory.stat"};
for (const auto &resource : resources) {
std::ifstream resource_file(cgroup_path + "/" + resource);
if (!resource_file.is_open()) {
PLOG_W("Failed to open cgroup resource file '%s'",
(cgroup_path + "/" + resource).c_str());
continue;
}
dump_file << resource_file.rdbuf();
dump_file << std::endl;
resource_file.close();
}
dump_file.close();
}
void finishFromParent(nsjconf_t *nsjconf, pid_t pid) { void finishFromParent(nsjconf_t *nsjconf, pid_t pid) {
if (nsjconf->cgroup_mem_max != (size_t)0 || nsjconf->cgroup_pids_max != 0U || if (nsjconf->cgroup_mem_max != (size_t)0 || nsjconf->cgroup_pids_max != 0U ||
nsjconf->cgroup_cpu_ms_per_sec != 0U) { nsjconf->cgroup_cpu_ms_per_sec != 0U) {
removeCgroup(getCgroupPath(nsjconf, pid)); auto cgroup_path = getCgroupPath(nsjconf, pid);
dumpCgroupStats(nsjconf, cgroup_path);
removeCgroup(cgroup_path);
} }
} }

View File

@ -170,6 +170,8 @@ static const struct custom_option custom_opts[] = {
{ { "macvlan_vs_mo", required_argument, nullptr, 0x706 }, "Mode of the 'vs' interface. Can be either 'private', 'vepa', 'bridge' or 'passthru' (default: 'private')" }, { { "macvlan_vs_mo", required_argument, nullptr, 0x706 }, "Mode of the 'vs' interface. Can be either 'private', 'vepa', 'bridge' or 'passthru' (default: 'private')" },
{ { "disable_tsc", no_argument, nullptr, 0x707 }, "Disable rdtsc and rdtscp instructions. WARNING: To make it effective, you also need to forbid `prctl(PR_SET_TSC, PR_TSC_ENABLE, ...)` in seccomp rules! (x86 and x86_64 only). Dynamic binaries produced by GCC seem to rely on RDTSC, but static ones should work." }, { { "disable_tsc", no_argument, nullptr, 0x707 }, "Disable rdtsc and rdtscp instructions. WARNING: To make it effective, you also need to forbid `prctl(PR_SET_TSC, PR_TSC_ENABLE, ...)` in seccomp rules! (x86 and x86_64 only). Dynamic binaries produced by GCC seem to rely on RDTSC, but static ones should work." },
{ { "forward_signals", no_argument, nullptr, 0x708 }, "Forward fatal signals to the child process instead of always using SIGKILL." }, { { "forward_signals", no_argument, nullptr, 0x708 }, "Forward fatal signals to the child process instead of always using SIGKILL." },
{ { "dump_stats", no_argument, nullptr, 0x1001 }, "Dump cgroups stat when process terminated. (Only work with MODE_STANDALONE_ONCE and cgroupv2)" },
{ { "dump_stats_file", required_argument, nullptr, 0x1002 }, "Location of dump_stats file." },
}; };
// clang-format on // clang-format on
@ -540,6 +542,8 @@ std::unique_ptr<nsjconf_t> parseArgs(int argc, char *argv[]) {
nsjconf->seccomp_fprog.len = 0; nsjconf->seccomp_fprog.len = 0;
nsjconf->seccomp_log = false; nsjconf->seccomp_log = false;
nsjconf->nice_level = 19; nsjconf->nice_level = 19;
nsjconf->dump_stats = false;
nsjconf->dump_stats_file = "/tmp/nsjail.log";
nsjconf->openfds.push_back(STDIN_FILENO); nsjconf->openfds.push_back(STDIN_FILENO);
nsjconf->openfds.push_back(STDOUT_FILENO); nsjconf->openfds.push_back(STDOUT_FILENO);
@ -985,6 +989,12 @@ std::unique_ptr<nsjconf_t> parseArgs(int argc, char *argv[]) {
case 0x903: case 0x903:
nsjconf->nice_level = (int)strtol(optarg, NULL, 0); nsjconf->nice_level = (int)strtol(optarg, NULL, 0);
break; break;
case 0x1001:
nsjconf->dump_stats = true;
break;
case 0x1002:
nsjconf->dump_stats_file = optarg;
break;
default: default:
cmdlineUsage(argv[0]); cmdlineUsage(argv[0]);
return nullptr; return nullptr;
@ -1008,6 +1018,15 @@ std::unique_ptr<nsjconf_t> parseArgs(int argc, char *argv[]) {
LOG_F("cannot set both cgroup_mem_memsw_max and cgroup_mem_swap_max"); LOG_F("cannot set both cgroup_mem_memsw_max and cgroup_mem_swap_max");
} }
if (nsjconf->dump_stats) {
if (nsjconf->mode != MODE_STANDALONE_ONCE) {
LOG_F("dump_stats can only be used with MODE_STANDALONE_ONCE");
}
if (!nsjconf->use_cgroupv2) {
LOG_E("dump_stats can only be used with cgroup v2");
}
}
return nsjconf; return nsjconf;
} }

View File

@ -299,6 +299,9 @@ static bool parseInternal(nsjconf_t* nsjconf, const nsjail::NsJailConfig& njc) {
nsjconf->use_execveat = njc.exec_bin().exec_fd(); nsjconf->use_execveat = njc.exec_bin().exec_fd();
} }
nsjconf->dump_stats = njc.dump_stats();
nsjconf->dump_stats_file = njc.dump_stats_file();
return true; return true;
} }

View File

@ -278,4 +278,8 @@ message NsJailConfig {
/* Check whether cgroupv2 is available, and use it if available. */ /* Check whether cgroupv2 is available, and use it if available. */
optional bool detect_cgroupv2 = 95 [default = false]; optional bool detect_cgroupv2 = 95 [default = false];
/* Dump statistics to a file */
optional bool dump_stats = 96 [default = false];
optional string dump_stats_file = 97 [default = "/tmp/nsjail.log"];
} }

View File

@ -180,6 +180,8 @@ struct nsjconf_t {
std::vector<int> caps; std::vector<int> caps;
std::vector<std::string> ifaces; std::vector<std::string> ifaces;
std::vector<pipemap_t> pipes; std::vector<pipemap_t> pipes;
bool dump_stats;
std::string dump_stats_file;
}; };
#endif /* _NSJAIL_H */ #endif /* _NSJAIL_H */