From 48532a1617ed59b5aaef247eace7a5cb67a1c69c Mon Sep 17 00:00:00 2001 From: Bharata B Rao Date: Wed, 1 Apr 2009 11:29:19 +0530 Subject: 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 Signed-off-by: Balbir Singh --- 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(-) create mode 100644 tests/read_stats.c diff --git a/include/libcgroup.h b/include/libcgroup.h index 750e36e..efa852b 100644 --- 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 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); diff --git a/src/api.c b/src/api.c index 2d57351..a45802e 100644 --- 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 is willing is take blame + * for mistakes in APIs for reading statistics. */ #include @@ -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; +} diff --git a/src/libcgroup.map b/src/libcgroup.map index 1989f90..dd44fd7 100644 --- 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; diff --git a/tests/Makefile.am b/tests/Makefile.am index 3927621..5999389 100644 --- 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 diff --git a/tests/Makefile.in b/tests/Makefile.in index dae7a12..704e96f 100644 --- 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.$(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 index 0000000..6e61b97 --- /dev/null +++ b/tests/read_stats.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include +#include +#include + +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: \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; +} -- cgit