]> git.ipfire.org Git - thirdparty/libcgroup.git/commitdiff
Here is the v2 of the patch which introduces APIs to read controller
authorBharata B Rao <bharata@linux.vnet.ibm.com>
Wed, 1 Apr 2009 05:59:19 +0000 (11:29 +0530)
committerBalbir Singh <balbir@linux.vnet.ibm.com>
Wed, 1 Apr 2009 05:59:19 +0000 (11:29 +0530)
specific stats. Added Makefile.am updates which I had missed in the
previous post.

New APIs to read controller specific statistics.

This patch introduces 3 new APIs which can be used to read controller
statistics iteratively. (Eg. stats from memory.stat etc)

Reading of stats is initiated by cgroup_read_stats_begin() API, which
returns the first stat of the requested controller in addition to returing
a handle that should be used in subsequent reads.

cgroup_read_stats_next() API can be used to read the remaining stats
one by one. This needs the handle returned by cgroup_read_stats_begin().

cgroup_read_stats_end() API will terminate the stats reading iteration
initiated by cgroup_read_stats_begin().

Changelog:

v2
- Update tests/Makefile.am so that it generates appropriate rules
  for tests/read_stats.c in the Makefile. This is in addition to
  the manual updates done to the generated file tests/Makefile.in.

v1
- cgroup_read_stats apis now work with relative cgroup path names instead
  of absolute path names.

v0
- Initial post.

Signed-off-by: Bharata B Rao <bharata@linux.vnet.ibm.com>
Signed-off-by: Balbir Singh <balbir@linux.vnet.ibm.com>
include/libcgroup.h
src/api.c
src/libcgroup.map
tests/Makefile.am
tests/Makefile.in
tests/read_stats.c [new file with mode: 0644]

index 750e36e306d3658af9609680f5c894b6f21f95c6..efa852b99e6ec685c8bcd4e2c1e56c6b930e47ee 100644 (file)
@@ -129,6 +129,11 @@ struct cgroup_file_info {
  */
 #define CG_HIER_MAX  CG_CONTROLLER_MAX
 
+struct cgroup_stat {
+       char name[FILENAME_MAX];
+       char value[CG_VALUE_MAX];
+};
+
 /* Functions and structures that can be used by the application*/
 struct cgroup;
 struct cgroup_controller;
@@ -250,6 +255,25 @@ int cgroup_walk_tree_next(const int depth, void **handle,
                                struct cgroup_file_info *info, int base_level);
 int cgroup_walk_tree_end(void **handle);
 
+/**
+ * Read the statistics values for the specified controller
+ * @controller: Name of the controller for which stats are requested.
+ * @path: cgroup path.
+ * @handle: Handle to be used during iteration.
+ * @stat: Stats values will be filled and returned here.
+ */
+int cgroup_read_stats_begin(char *controller, char *path, void **handle,
+                               struct cgroup_stat *stat);
+
+/**
+ * Read the next stat value.
+ * @handle: Handle to be used during iteration.
+ * @stat: Stats values will be filled and returned here.
+ */
+int cgroup_read_stats_next(void **handle, struct cgroup_stat *stat);
+
+int cgroup_read_stats_end(void **handle);
+
 /* The wrappers for filling libcg structures */
 
 struct cgroup *cgroup_new_cgroup(const char *name);
index 2d57351fcba38286c89ec51b7c43c9924e159769..a45802e529c0f7d6e67134df596be9208f38ce82 100644 (file)
--- a/src/api.c
+++ b/src/api.c
@@ -20,6 +20,9 @@
  *
  * Code initiated and designed by Dhaval Giani. All faults are most likely
  * his mistake.
+ *
+ * Bharata B Rao <bharata@linux.vnet.ibm.com> is willing is take blame
+ * for mistakes in APIs for reading statistics.
  */
 
 #include <dirent.h>
@@ -2321,3 +2324,102 @@ int cgroup_walk_tree_begin(char *controller, char *base_path, const int depth,
        *handle = fts;
        return ret;
 }
+
+/*
+ * This parses a stat line which is in the form of (name value) pair
+ * separated by a space.
+ */
+int cg_read_stat(FILE *fp, struct cgroup_stat *stat)
+{
+       int ret = 0;
+       char *line = NULL;
+       size_t len = 0;
+       ssize_t read;
+       char *token, *saveptr;
+
+       read = getline(&line, &len, fp);
+       if (read == -1)
+               return ECGEOF;
+
+       token = strtok_r(line, " ", &saveptr);
+       if (!token) {
+               ret = ECGINVAL;
+               goto out_free;
+       }
+       strncpy(stat->name, token, FILENAME_MAX);
+
+       token = strtok_r(NULL, " ", &saveptr);
+       if (!token) {
+               ret = ECGINVAL;
+               goto out_free;
+       }
+       strncpy(stat->value, token, CG_VALUE_MAX);
+
+out_free:
+       free(line);
+       return 0;
+}
+
+int cgroup_read_stats_end(void **handle)
+{
+       FILE *fp;
+
+       if (!cgroup_initialized)
+               return ECGROUPNOTINITIALIZED;
+
+       if (!handle)
+               return ECGINVAL;
+
+       fp = (FILE *)*handle;
+       fclose(fp);
+       return 0;
+}
+
+int cgroup_read_stats_next(void **handle, struct cgroup_stat *stat)
+{
+       int ret = 0;
+       FILE *fp;
+
+       if (!cgroup_initialized)
+               return ECGROUPNOTINITIALIZED;
+
+       if (!handle || !stat)
+               return ECGINVAL;
+
+       fp = (FILE *)*handle;
+       ret = cg_read_stat(fp, stat);
+       *handle = fp;
+       return ret;
+}
+
+/*
+ * TODO: Need to decide a better place to put this function.
+ */
+int cgroup_read_stats_begin(char *controller, char *path, void **handle,
+                               struct cgroup_stat *stat)
+{
+       int ret = 0;
+       char stat_file[FILENAME_MAX];
+       FILE *fp;
+
+       if (!cgroup_initialized)
+               return ECGROUPNOTINITIALIZED;
+
+       if (!stat || !handle)
+               return ECGINVAL;
+
+       if (!cg_build_path(path, stat_file, controller))
+               return ECGOTHER;
+
+       sprintf(stat_file, "%s/%s.stat", stat_file, controller);
+
+       fp = fopen(stat_file, "r");
+       if (!fp) {
+               cgroup_dbg("fopen failed\n");
+               return ECGINVAL;
+       }
+
+       ret = cg_read_stat(fp, stat);
+       *handle = fp;
+       return ret;
+}
index 1989f901cd129da6032c93dcb302ad73ee1f99cf..dd44fd7cdca3097e11870ee908cd284f3007d31b 100644 (file)
@@ -52,5 +52,8 @@ global:
        cgroup_walk_tree_begin;
        cgroup_walk_tree_next;
        cgroup_walk_tree_end;
+       cgroup_read_stats_begin;
+       cgroup_read_stats_next;
+       cgroup_read_stats_end;
 } CGROUP_0.32.1;
 
index 3927621b73189a2c87e706da422e7f91e4066f47..599938945c54bc7f91254b781a66257c01a9233a 100644 (file)
@@ -2,13 +2,14 @@ INCLUDES = -I$(top_srcdir)/include
 LDADD = $(top_srcdir)/src/.libs/libcgroup.la
 
 # compile the tests, but do not install them
-noinst_PROGRAMS = libcgrouptest01 libcg_ba setuid pathtest walk_test
+noinst_PROGRAMS = libcgrouptest01 libcg_ba setuid pathtest walk_test read_stats
 
 libcgrouptest01_SOURCES=libcgrouptest01.c test_functions.c libcgrouptest.h
 libcg_ba_SOURCES=libcg_ba.cpp
 setuid_SOURCES=setuid.c
 pathtest_SOURCES=pathtest.c
 walk_test_SOURCES=walk_test.c
+read_stats_SOURCES=read_stats.c
 
 EXTRA_DIST = pathtest.sh runlibcgrouptest.sh
 
index dae7a12618e03941d862915cd59a592a0604426c..704e96fb6a99c2140e1be0168a7efb183eab7273 100644 (file)
@@ -33,7 +33,8 @@ POST_UNINSTALL = :
 build_triplet = @build@
 host_triplet = @host@
 noinst_PROGRAMS = libcgrouptest01$(EXEEXT) libcg_ba$(EXEEXT) \
-       setuid$(EXEEXT) pathtest$(EXEEXT) walk_test$(EXEEXT)
+       setuid$(EXEEXT) pathtest$(EXEEXT) walk_test$(EXEEXT) \
+       read_stats$(EXEEXT)
 subdir = tests
 DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
 ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
@@ -65,6 +66,10 @@ am_walk_test_OBJECTS = walk_test.$(OBJEXT)
 walk_test_OBJECTS = $(am_walk_test_OBJECTS)
 walk_test_LDADD = $(LDADD)
 walk_test_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la
+am_read_stats_OBJECTS = read_stats.$(OBJEXT)
+read_stats_OBJECTS = $(am_read_stats_OBJECTS)
+read_stats_LDADD = $(LDADD)
+read_stats_DEPENDENCIES = $(top_srcdir)/src/.libs/libcgroup.la
 DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
@@ -87,9 +92,11 @@ CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
        --mode=link $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) \
        $(LDFLAGS) -o $@
 SOURCES = $(libcg_ba_SOURCES) $(libcgrouptest01_SOURCES) \
-       $(pathtest_SOURCES) $(setuid_SOURCES) $(walk_test_SOURCES)
+       $(pathtest_SOURCES) $(setuid_SOURCES) $(walk_test_SOURCES) \
+       $(read_stats_SOURCES)
 DIST_SOURCES = $(libcg_ba_SOURCES) $(libcgrouptest01_SOURCES) \
-       $(pathtest_SOURCES) $(setuid_SOURCES) $(walk_test_SOURCES)
+       $(pathtest_SOURCES) $(setuid_SOURCES) $(walk_test_SOURCES) \
+       $(read_test_SOURCES)
 ETAGS = etags
 CTAGS = ctags
 DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -214,6 +221,7 @@ libcg_ba_SOURCES = libcg_ba.cpp
 setuid_SOURCES = setuid.c
 pathtest_SOURCES = pathtest.c
 walk_test_SOURCES = walk_test.c
+read_stats_SOURCES = read_stats.c
 EXTRA_DIST = pathtest.sh runlibcgrouptest.sh
 TESTS = runlibcgrouptest.sh
 all: all-am
@@ -271,6 +279,9 @@ setuid$(EXEEXT): $(setuid_OBJECTS) $(setuid_DEPENDENCIES)
 walk_test$(EXEEXT): $(walk_test_OBJECTS) $(walk_test_DEPENDENCIES) 
        @rm -f walk_test$(EXEEXT)
        $(LINK) $(walk_test_OBJECTS) $(walk_test_LDADD) $(LIBS)
+read_stats$(EXEEXT): $(read_stats_OBJECTS) $(read_stats_DEPENDENCIES)
+       @rm -f read_stats$(EXEEXT)
+       $(LINK) $(read_stats_OBJECTS) $(read_stats_LDADD) $(LIBS)
 
 mostlyclean-compile:
        -rm -f *.$(OBJEXT)
@@ -284,6 +295,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/setuid.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_functions.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/walk_test.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/read_stats.Po@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
diff --git a/tests/read_stats.c b/tests/read_stats.c
new file mode 100644 (file)
index 0000000..6e61b97
--- /dev/null
@@ -0,0 +1,80 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <string.h>
+#include <libcgroup.h>
+
+int read_stats(char *path, char *controller)
+{
+       int ret;
+       void *handle;
+       struct cgroup_stat stat;
+
+       ret = cgroup_read_stats_begin(controller, path,  &handle, &stat);
+
+       if (ret != 0) {
+               fprintf(stderr, "stats read failed\n");
+               return -1;
+       }
+
+       printf("Stats for %s:\n", path);
+       printf("%s: %s", stat.name, stat.value);
+
+       while ((ret = cgroup_read_stats_next(&handle, &stat)) !=
+                       ECGEOF) {
+               printf("%s: %s", stat.name, stat.value);
+       }
+
+       cgroup_read_stats_end(&handle);
+       printf("\n");
+       return 0;
+}
+
+int main(int argc, char *argv[])
+{
+       int ret;
+       char *controller;
+       void *handle;
+       struct cgroup_file_info info;
+       int lvl;
+       char cgroup_path[FILENAME_MAX];
+       int root_len;
+
+       if (argc < 2) {
+               fprintf(stderr, "Usage %s: <controller name>\n",
+                       argv[0]);
+               exit(EXIT_FAILURE);
+       }
+
+       controller = argv[1];
+
+       cgroup_init();
+
+       ret = cgroup_walk_tree_begin(controller, "/", 0, &handle, &info, &lvl);
+
+       if (ret != 0) {
+               fprintf(stderr, "Walk failed\n");
+               exit(EXIT_FAILURE);
+       }
+
+       root_len = strlen(info.full_path) - 1;
+       strncpy(cgroup_path, info.path, FILENAME_MAX);
+       ret = read_stats(cgroup_path, controller);
+       if (ret < 0)
+               exit(EXIT_FAILURE);
+
+       while ((ret = cgroup_walk_tree_next(0, &handle, &info, lvl)) !=
+                       ECGEOF) {
+               if (info.type != CGROUP_FILE_TYPE_DIR)
+                       continue;
+               strncpy(cgroup_path, info.full_path + root_len, FILENAME_MAX);
+               strcat(cgroup_path, "/");
+               ret = read_stats(cgroup_path, controller);
+               if (ret < 0)
+                       exit(EXIT_FAILURE);
+       }
+       cgroup_walk_tree_end(&handle);
+
+       return EXIT_SUCCESS;
+}