Here is the v2 of the patch which introduces APIs to read controller
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 <[email protected]>
---
 include/libcgroup.h |   24 ++++++++++++
 src/api.c           |  102 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/libcgroup.map   |    3 +
 tests/Makefile.am   |    3 +
 tests/Makefile.in   |   18 +++++++--
 tests/read_stats.c  |   80 ++++++++++++++++++++++++++++++++++++++++
 6 files changed, 226 insertions(+), 4 deletions(-)

--- a/include/libcgroup.h
+++ b/include/libcgroup.h
@@ -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 dept
                                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);
--- 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 <[email protected]> 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 *control
        *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;
+}
--- a/src/libcgroup.map
+++ b/src/libcgroup.map
@@ -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;
 
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -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
 
--- a/tests/Makefile.in
+++ b/tests/Makefile.in
@@ -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.$(OBJEX
 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 = -...@am__isrc@ -I$(top_builddir)
 depcomp = $(SHELL) $(top_srcdir)/depcomp
 am__depfiles_maybe = depfiles
@@ -87,9 +92,11 @@ CXXLINK = $(LIBTOOL) --tag=CXX $(AM_LIBT
        --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) $(set
 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__qu...@./$(DEPDIR)/setuid...@am__quote@
 @AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/test_functions...@am__quote@
 @AMDEP_TRUE@@am__include@ @am__qu...@./$(DEPDIR)/walk_test...@am__quote@
+...@amdep_true@@am__include@ @am__qu...@./$(DEPDIR)/read_stats...@am__quote@
 
 .c.o:
 @am__fastdepCC_TRUE@   $(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ 
$<
--- /dev/null
+++ b/tests/read_stats.c
@@ -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;
+}

------------------------------------------------------------------------------
_______________________________________________
Libcg-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/libcg-devel

Reply via email to