Support per-ss cgroups
[util-vserver.git] / src / vserver-stat.c
index 101b916..e81c38d 100644 (file)
@@ -190,6 +190,12 @@ getUptime()
   return secs*1000 + msecs;
 }
 
+static inline uint64_t
+toMsec(uint64_t v)
+{
+  return v*1000llu/hertz;
+}
+
 static int
 cmpData(void const *xid_v, void const *map_v)
 {
@@ -302,12 +308,17 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
     int                                cpu;
     char                       vhi_name[65],
                                filename[128],
-                               cgroup[65],
+                               cgroup[129],
+                               name[129],
                                buf[30];
     int                                fd;
-    ssize_t                    cgroup_len;
-    unsigned long long         rss;
+    ssize_t                    cgroup_len, name_len;
+    unsigned long long         rss = 0;
     char                       *endptr;
+    size_t                     len;
+    uint64_t                   stime_total, utime_total;
+    int                                per_ss = 0;
+
 
     if (vc_virt_stat(xid, &vstat) == -1) {
       perror("vc_virt_stat()");
@@ -332,45 +343,129 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
         perror("read(cgroup/mnt)");
         return;
       }
+      if (cgroup_len > 0) {
+       close(fd);
+       while (cgroup[cgroup_len - 1] == '\n' || cgroup[cgroup_len - 1] == '\r')
+         cgroup_len--;
+       cgroup[cgroup_len] = '/';
+       cgroup_len += 1;
+       cgroup[cgroup_len] = 0;
+      }
+    }
+
+    if ((fd = open(DEFAULTCONFDIR "/cgroup/base", O_RDONLY)) != -1) {
+      len = read(fd, cgroup + cgroup_len, sizeof(cgroup) - cgroup_len);
+      if (len == -1) {
+        perror("read(cgroup/base)");
+        return;
+      }
       close(fd);
-      cgroup[cgroup_len] = '/';
-      cgroup_len += 1;
-      cgroup[cgroup_len] = 0;
+      if (len > 0) {
+       while (cgroup[cgroup_len + len - 1] == '\n' || cgroup[cgroup_len + len - 1] == '\r')
+         len--;
+       cgroup_len += len;
+       if (cgroup[cgroup_len - 1] != '/') {
+         cgroup[cgroup_len] = '/';
+         cgroup_len += 1;
+       }
+       cgroup[cgroup_len] = 0;
+      }
+    }
+
+    if (access(DEFAULTCONFDIR "/cgroup/per-ss", F_OK) == 0)
+      per_ss = 1;
+
+    len = strlen(vhi_name);
+    if ((len + sizeof("/cgroup/name")) >= sizeof(filename)) {
+      WRITE_MSG(2, "too long context name: ");
+      WRITE_STR(2, vhi_name);
+      WRITE_MSG(2, "\n");
+      return;
     }
+    strcpy(filename, vhi_name);
+    strcpy(filename + len, "/cgroup/name");
 
-    snprintf(filename, sizeof(filename), "%s/cgroup/name", vhi_name);
     if ((fd = open(filename, O_RDONLY)) == -1) {
       char *dir = strrchr(vhi_name, '/');
       if (dir == NULL) {
-        fprintf(stderr, "invalid context name: %s\n", dir);
+        WRITE_MSG(2, "invalid context name: ");
+        WRITE_STR(2, dir);
+        WRITE_MSG(2, "\n");
+        return;
+      }
+      name_len = strlen(dir);
+      if (name_len >= sizeof(name)) {
+        WRITE_MSG(2, "cgroup name too long: ");
+        WRITE_STR(2, dir);
+        WRITE_MSG(2, "\n");
         return;
       }
-      snprintf(cgroup + cgroup_len, sizeof(cgroup) - cgroup_len, "%s", dir);
+      strcpy(name, dir);
     }
     else {
-      ssize_t ret;
-      ret = read(fd, cgroup + cgroup_len, sizeof(cgroup) - cgroup_len);
-      if (ret == -1) {
+      name_len = read(fd, name, sizeof(name));
+      if (name_len == -1) {
         perror("read(cgroup/name)");
         return;
       }
+      if (name_len > 0) {
+       while (name[name_len - 1] == '\n' || name[name_len - 1] == '\r')
+         name_len--;
+       name[name_len] = '\0';
+      }
       close(fd);
     }
 
-    snprintf(filename, sizeof(filename), "%s/memory.usage_in_bytes", cgroup);
-    if ((fd = open(filename, O_RDONLY)) == -1) {
-      perror("open(memory.usage_in_bytes)");
+    if ((cgroup_len + name_len + sizeof("/memory/memory.usage_in_bytes")) > sizeof(filename)) {
+      WRITE_MSG(2, "cgroup name too long: ");
+      WRITE_STR(2, cgroup);
+      WRITE_MSG(2, "\n");
       return;
     }
-    if (read(fd, buf, sizeof(buf)) == -1) {
-      perror("read(memory.usage_in_bytes)");
-      return;
+    snprintf(filename, sizeof(filename), "%s%s%s/memory.usage_in_bytes", cgroup, (per_ss ? "/memory" : ""), name);
+
+    if ((fd = open(filename, O_RDONLY)) == -1)
+      perror("open(memory.usage_in_bytes)");
+    else {
+      if (read(fd, buf, sizeof(buf)) == -1) {
+       perror("read(memory.usage_in_bytes)");
+       return;
+      }
+      close(fd);
+      if ((rss = strtoull(buf, &endptr, 0)) == ULLONG_MAX ||
+         (*endptr != '\n' && *endptr != '\0')) {
+       perror("strtoull(memory.usage_in_bytes)");
+       return;
+      }
     }
-    close(fd);
-    if ((rss = strtoull(buf, &endptr, 0)) == ULLONG_MAX ||
-        (*endptr != '\n' && *endptr != '\0')) {
-      perror("strtoull(memory.usage_in_bytes)");
-      return;
+
+    snprintf(filename, sizeof(filename), "%s%s%s/cpuacct.stat", cgroup, (per_ss ? "/cpuacct" : ""), name);
+
+    if ((fd = open(filename, O_RDONLY)) == -1) {
+      utime_total      = 0;
+      stime_total      = 0;
+      // XXX: arbitrary CPU limit.
+      for (cpu = 0; cpu < 1024; cpu++) {
+       sched.cpu_id = cpu;
+       sched.bucket_id = 0;
+       if (vc_sched_info(xid, &sched) == -1)
+         break;
+
+       utime_total     += sched.user_msec;
+       stime_total     += sched.sys_msec;
+      }
+    }
+    else {
+      if (read(fd, buf, sizeof(buf)) == -1) {
+       perror("read(cpuacct.stat)");
+       return;
+      }
+      close(fd);
+
+      if (sscanf(buf, "user %llu\nsystem %llu\n", &utime_total, &stime_total) != 2) {
+       perror("sscanf(cpuacct.stat)");
+       return;
+      }
     }
 
     res                        = Vector_insert(vec, &xid, cmpData);
@@ -380,29 +475,13 @@ registerXidCgroups(struct Vector *vec, struct process_info *process)
     res->VmRSS_total   = rss / 4096;
     res->start_time_oldest= getUptime() - vstat.uptime/1000000;
 
-    res->utime_total   = 0;
-    res->stime_total   = 0;
-    // XXX: arbitrary CPU limit.
-    for (cpu = 0; cpu < 1024; cpu++) {
-      sched.cpu_id = cpu;
-      sched.bucket_id = 0;
-      if (vc_sched_info(xid, &sched) == -1)
-        break;
-
-      res->utime_total += sched.user_msec;
-      res->stime_total += sched.sys_msec;
-    }
+    res->utime_total   = toMsec(utime_total);
+    res->stime_total   = toMsec(stime_total);
   }
   
   res->VmSize_total    += process->VmSize;
 }
 
-static inline uint64_t
-toMsec(uint64_t v)
-{
-  return v*1000llu/hertz;
-}
-
 
 // shamelessly stolen from procps...
 static unsigned long
@@ -733,7 +812,7 @@ int main(int argc, char **argv)
   
   Vector_init(&xid_data, sizeof(struct XidData));
 
-  if (vc_isSupported(vcFEATURE_VSTAT) && !vc_isSupported(vcFEATURE_MEMCG)) {
+  if (vc_isSupported(vcFEATURE_VSTAT)) {
     unsigned long xid;
     Echdir(PROC_VIRT_DIR_NAME);
     proc_dir = Eopendir(".");