66#include " Scopes.hxx"
77#include " CgroupAccounting.hxx"
88#include " LAccounting.hxx"
9+ #include " time/ISO8601.hxx"
10+ #include " time/StatxCast.hxx"
11+ #include " util/StringBuffer.hxx"
912#include " util/StringCompare.hxx"
1013
1114#include < fmt/format.h>
1417#include < stdio.h>
1518#include < unistd.h>
1619#include < errno.h>
20+ #include < sys/stat.h>
1721
1822using std::string_view_literals::operator " " sv;
1923
@@ -31,10 +35,14 @@ GetManagedSuffix(const char *path)
3135
3236static void
3337CollectCgroupStats (const char *suffix,
38+ const std::chrono::system_clock::time_point btime,
3439 const CgroupResourceUsage &u)
3540{
3641 char buffer[4096 ], *p = buffer;
3742
43+ if (btime != std::chrono::system_clock::time_point{})
44+ p = fmt::format_to (p, " since={}" sv, FormatISO8601 (btime).c_str ());
45+
3846 if (u.cpu .user .count () >= 0 || u.cpu .system .count () >= 0 ) {
3947 const auto user = std::max (u.cpu .user .count (), 0 .);
4048 const auto system = std::max (u.cpu .system .count (), 0 .);
@@ -106,12 +114,23 @@ Instance::OnCgroupEmpty(const char *path) noexcept
106114 UniqueFileDescriptor cgroup_fd;
107115 (void )cgroup_fd.Open (root_cgroup, path + 1 , O_DIRECTORY |O_RDONLY );
108116
117+ std::chrono::system_clock::time_point btime;
118+
119+ if (cgroup_fd.IsDefined ()) {
120+ struct statx stx;
121+ if (statx (cgroup_fd.Get (), " " , AT_EMPTY_PATH |AT_STATX_FORCE_SYNC ,
122+ STATX_BTIME , &stx) == 0 ) {
123+ if (stx.stx_mask & STATX_BTIME )
124+ btime = ToSystemTimePoint (stx.stx_btime );
125+ }
126+ }
127+
109128 // TODO read resource usage right before the cgroup actually gets deleted
110129 const auto u = cgroup_fd.IsDefined ()
111130 ? ReadCgroupResourceUsage (cgroup_fd)
112131 : CgroupResourceUsage{};
113132
114- CollectCgroupStats (suffix, u);
133+ CollectCgroupStats (suffix, btime, u);
115134
116135 if (lua_accounting)
117136 lua_accounting->InvokeCgroupReleased (std::move (cgroup_fd), path, u);
0 commit comments