From rahul.x.yadav at oracle.com Thu Mar 1 06:58:19 2018 From: rahul.x.yadav at oracle.com (Rahul Yadav) Date: Wed, 28 Feb 2018 22:58:19 -0800 Subject: [libresource-devel] [PATCH] library of interfaces to fetch system resource information Message-ID: <1519887499-12171-1-git-send-email-rahul.x.yadav@oracle.com> 1: System resource Information related to memory, CPU, stat, networking, security etc. 2: Currently most of such information is read from /proc and /sys. 3: Add basic infrastructure and code for some of memory and network related information. 4: Update README file with the use cases and design details. 5: Added example codes in README. Signed-off-by: Rahul Yadav --- Makefile | 18 ++++ README.md | 160 ++++++++++++++++++++++++++-- resmem.c | 218 ++++++++++++++++++++++++++++++++++++++ resmem.h | 16 +++ resnet.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ resnet.h | 14 +++ resource.c | 211 +++++++++++++++++++++++++++++++++++++ resource.h | 147 ++++++++++++++++++++++++++ resource_impl.h | 47 +++++++++ 9 files changed, 1137 insertions(+), 10 deletions(-) create mode 100644 Makefile create mode 100644 resmem.c create mode 100644 resmem.h create mode 100644 resnet.c create mode 100644 resnet.h create mode 100644 resource.c create mode 100644 resource.h create mode 100644 resource_impl.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2c71328 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +CC = gcc +CFLAGS = -g -Wall -Werror -fPIC +DEPS = resource.h res_impl.h resmem.h resnet.h +OBJ = resource.o resmem.o resnet.o +LIB = libresource.so +TEST = test +RM = rm -rf +CP = cp + +%.o: %.c $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + +all: $(OBJ) + $(CC) -shared -o $(LIB) $^ $(CFLAGS) + +.PHONY : clean +clean: + $(RM) $(LIB) $(OBJ) $(TEST) diff --git a/README.md b/README.md index 960468d..97b37df 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,154 @@ # libresource +library of interfaces through which we can get system resource information +like memory, CPU, stat, networking, device etc. +Currently most of such information is read from /proc and /sys. -libresource is a community effort to generate a new library which tools -like ps, top, etc can use to get information typically garnered from -/proc. libresource however will attempt to virtualize the data according -to the calling task's cgroup information. +Use case: +1: Ease of use - +Currently applications and tools need to read this info mostly from +/proc and /sys file-systems. In most of the cases complex string +parsing is involved which is needed to be done in application +code. With the library interfaces application can get the information +directly and all the string parsing, if any, will be done by library. +2: Stability - +If the format in which the information is provided in /proc or /sys +file-system is changed then the application code is changed to align +with those changes. Also if a better way to get information comes in +future, like through a syscall or a sysconf then again application code +needs to be changed to get the benefit of it. Library will take care of +such changes. +3: Virtualization - +In cases where DB is running in a virtualized environment using +cgroup or namespaces, reading from /proc and /sys file-systems +might not give correct information as these are not cgroup +aware. Library API will take care of this e.g. if a process +is running in a cgroup then library should provide information +which is local to that cgroup. -# contributing +Design: +Each resource is identified by a resource id. User land application should +provide the id while calling the interfaces to fetch the information. +Following are resource IDs which are already implemented. -We will interact through pull requests and the issue tracker. I think we -should start with pull requests editing the CONTRIBUTORS so we can have a -feel whether enough people are able to contribute or interested in following -for the project to be successful. We can also start pull requests against -the specs/api.md file to discuss what that should look like. +RES_MEM_ACTIVE +RES_MEM_INACTIVE +RES_MEM_AVAILABLE +RES_MEM_FREE +RES_MEM_TOTAL +RES_MEM_PAGESIZE +RES_MEM_SWAPFREE +RES_MEM_SWAPTOTAL +RES_KERN_COMPILE_TIME +RES_KERN_RELEASE +RES_NET_ALLIFSTAT +RES_NET_IFSTAT +RES_MEM_INFOALL + +Interfaces: +1: +int res_read(int res_id, void *out, void *hint, int pid, int flags); + +This is to read a resource information. A valid resource id should be provided +in res_id, out should be properly allocated on the basis of size of resource +information, hint should be given where needed. Currently pid and flags are +not used, they are for future extensions. + +2: + +If an application wants to read multiple resource information in one call, it +can call res_*_blk APIs to do so. + +2.1 structs +Following struct holds information about one resource in such case. +typedef struct res_unit { + int status; + int res_id; + void *hint; + union r_data data; +} res_unit_t; + +An array of these form res_blk_t structure as below. +typedef struct res_blk { + int res_count; + res_unit_t *res_unit[0]; +} res_blk_t; + +res_blk_t strcut is used in all res_*_blk interfaces. + +2.2 functions +res_blk_t *res_build_blk(int *res_ids, int res_count); + +This allocates memory for resources and initiates them properly. res_ids +holds and array of valid resource ids and res_count holds number of +resource ids. It also initiates struct fields properly. + +int res_read_blk(res_blk_t *resblk, int pid, int flags); +Reading bulk resource information. Memory must be properly allocated and +all fields should be properly filled to return error free resource +information. res_build_blk call is suggested to allocate build res_blk_t +structure. + +/* Free allocated memory from res_build_blk */ +extern void res_destroy_blk(res_blk_t *resblk); + +Example +1: +Reading total memory +size_t stemp = 0; +res_read(RES_MEM_TOTAL,&stemp,NULL, 0, 0); +printf("MEMTOTAL is: %zu\n", stemp); + + +2: +reading network interface related statistics for interface named "lo" +res_net_ifstat_t ifstat; +res_read(RES_NET_IFSTAT,&ifstat, (void *)"lo",0, 0); +printf("status for %s: %llu %llu\n", ifstat.ifname, + ifstat.rx_bytes, + ifstat.rx_packets + ); +3: +Reading multiple resouce information in one call. + + res_blk_t *b = NULL; + int a[NUM] = {RES_MEM_PAGESIZE, + RES_MEM_TOTAL, + RES_MEM_AVAILABLE, + RES_MEM_INFOALL, + RES_KERN_RELEASE, + RES_NET_IFSTAT, + RES_NET_ALLIFSTAT, + RES_KERN_COMPILE_TIME + }; + b = res_build_blk(a, NUM); + b->res_unit[5]->hint = (void *)"lo"; + + res_read_blk(b, 0, 0); + + printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable %ld kb,\n" + " mefree %ld kb,\n release %s,\n compile time %s\n", + b->res_unit[0]->data.sz, + b->res_unit[1]->data.sz, + b->res_unit[2]->data.sz, + ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree, + b->res_unit[4]->data.str, + b->res_unit[7]->data.str + ); + + res_net_ifstat_t *ip = (res_net_ifstat_t *)b->res_unit[5]->data.ptr; + printf("stat for interface %s: %llu %llu\n", ip->ifname, + ip->rx_bytes, + ip->rx_packets + ); + + int k = (int)(long long)b->res_unit[6]->hint; + res_net_ifstat_t *ipp = (res_net_ifstat_t *)b->res_unit[6]->data.ptr; + for (int j=0; j< k; j++) { + printf("stat for interface %s: %llu %llu\n", ipp[j].ifname, + ipp[j].rx_bytes, + ipp[j].rx_packets + ); + } + + free(ipp); + res_destroy_blk(b); diff --git a/resmem.c b/resmem.c new file mode 100644 index 0000000..df7d261 --- /dev/null +++ b/resmem.c @@ -0,0 +1,218 @@ +#ifndef _RESOURCE_H +#include "resource.h" +#endif +#include +#include +#include +#include +#include +#include "resmem.h" +#include "resource_impl.h" +#include + +int meminfo_fd = -1; + +/* read a specific information from file on the basis of a string. + * String should tell what information is being read. + */ +static inline int get_info_infile(char *fname, char *res, void *out) +{ + const char *loc; + char buf[MEMBUF_2048]; + + if (file_to_buf(fname, buf) == -1) + return -1; + + loc = strstr(buf, res); + if (loc == NULL) { + eprintf("%s is not found in buffer of file %s", res, fname); + errno = ENODATA; + return -1; + } + + sscanf(loc, "%*s%zu", (size_t *)out); + return 0; +} + +/* Read resource information corresponding to res_id */ +int getmeminfo(int res_id, void *out, void *hint, int pid, int flags) +{ + char buf[MEMBUF_128]; + FILE *fp; + int err = 0; + + switch (res_id) { + + case RES_MEM_FREE: + return get_info_infile(MEMINFO_FILE, "MemFree:", out); + + case RES_MEM_AVAILABLE: + return get_info_infile(MEMINFO_FILE, "MemAvailable:", out); + + case RES_MEM_TOTAL: + return get_info_infile(MEMINFO_FILE, "MemTotal:", out); + + case RES_MEM_ACTIVE: + return get_info_infile(MEMINFO_FILE, "Active:", out); + + case RES_MEM_INACTIVE: + return get_info_infile(MEMINFO_FILE, "Inactive:", out); + + case RES_MEM_SWAPTOTAL: + return get_info_infile(MEMINFO_FILE, "SwapTotal:", out); + + case RES_MEM_SWAPFREE: + return get_info_infile(MEMINFO_FILE, "SwapFree:", out); + + case RES_MEM_INFOALL: + fp = fopen(MEMINFO_FILE, "r"); + if (fp == NULL) { + err = errno; + eprintf("while opening File %s with errno: %d", + MEMINFO_FILE, errno); + errno = err; + return -1; + } + /* Read through file and populate all information which + * is required. + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (strncmp("MemTotal:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->memtotal); + + else if (strncmp("MemFree:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->memfree); + + else if (strncmp("MemAvailable:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->memavailable); + + else if (strncmp("Active:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->active); + + else if (strncmp("Inactive:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->inactive); + + else if (strncmp("SwapTotal:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->swaptotal); + + else if (strncmp("SwapFree:", buf, MEMBUF_8) == 0) + sscanf(buf, "%*s%zu", + &((res_mem_infoall_t *)out)->swapfree); + } + fclose(fp); + break; + + case RES_MEM_PAGESIZE: + *(size_t *)out = sysconf(_SC_PAGESIZE); + break; + + default: + eprintf("Resource Id is invalid"); + errno = EINVAL; + return -1; + } + + return 0; +} + +int populate_meminfo(res_blk_t *res, int pid, int flags) +{ + size_t temp; + const char *loc; + char buf[MEMBUF_2048]; + + if (file_to_buf(MEMINFO_FILE, buf) == -1) { + for (int i = 0 ; i < res->res_count; i++) + res->res_unit[i]->status = errno; + return -1; + } + +/* Macro to read memory related information corresponding to a string + * from buffer. + */ +#define SCANMEMSTR(str) do {\ + loc = strstr(buf, str);\ + if (loc == NULL) {\ + eprintf("%s is not found in file %s", str, MEMINFO_FILE);\ + res->res_unit[i]->status = ENODATA;\ + } else {\ + sscanf(loc, "%*s%zu", &temp);\ + (res->res_unit[i]->data).sz = temp;\ + res->res_unit[i]->status = RES_STATUS_FILLED;\ + } \ +} while (0)\ + + + for (int i = 0; i < res->res_count; i++) { + loc = NULL; + switch (res->res_unit[i]->res_id) { + case RES_MEM_FREE: + SCANMEMSTR("MemFree:"); + break; + + case RES_MEM_AVAILABLE: + SCANMEMSTR("MemAvailable:"); + break; + + case RES_MEM_TOTAL: + SCANMEMSTR("MemTotal:"); + break; + + case RES_MEM_ACTIVE: + SCANMEMSTR("Active:"); + break; + + case RES_MEM_INACTIVE: + SCANMEMSTR("Inactive:"); + break; + + case RES_MEM_SWAPTOTAL: + SCANMEMSTR("SwapTotal:"); + break; + + case RES_MEM_SWAPFREE: + SCANMEMSTR("SwapFree:"); + break; + + case RES_MEM_PAGESIZE: + (res->res_unit[i]->data).sz = sysconf(_SC_PAGESIZE); + break; + + case RES_MEM_INFOALL: + loc = strstr(buf, "MemTotal:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->memtotal); + loc = strstr(buf, "MemFree:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->memfree); + loc = strstr(buf, "MemAvailable:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->memavailable); + loc = strstr(buf, "Active:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->active); + loc = strstr(buf, "Inactive:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->inactive); + loc = strstr(buf, "SwapTotal:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->swaptotal); + loc = strstr(buf, "SwapFree:"); + sscanf(loc, "%*s%zu", &((res_mem_infoall_t *) + (res->res_unit[i]->data).ptr)->swapfree); + + res->res_unit[i]->status = RES_STATUS_FILLED; + break; + default: + res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED; + } + } + return 0; +} + diff --git a/resmem.h b/resmem.h new file mode 100644 index 0000000..817b9ec --- /dev/null +++ b/resmem.h @@ -0,0 +1,16 @@ +#ifndef _RESMEM_H +#define _RESMEM_H + +#define MEMINFO_FILE "/proc/meminfo" +extern int meminfo_fd; + +#define VMINFO_FILE "/proc/vmstat" + +#define MEMBUF_8 8 +#define MEMBUF_128 128 +#define MEMBUF_2048 2048 + +extern int populate_meminfo(struct res_blk *res, int pid, int flags); +extern int getmeminfo(int res_id, void *out, void *hint, int pid, int flags); + +#endif /* _RESMEM_H */ diff --git a/resnet.c b/resnet.c new file mode 100644 index 0000000..56269bf --- /dev/null +++ b/resnet.c @@ -0,0 +1,316 @@ +#ifndef _RESOURCE_H +#include "resource.h" +#endif +#include +#include +#include +#include +#include +#include +#include "resnet.h" +#include "resource_impl.h" + +int netdev_fd = -1; + +static inline int procnetdev_ver(char *buf) +{ + if (strstr(buf, "compressed")) + return 3; + if (strstr(buf, "bytes")) + return 2; + return 1; +} + +static inline char *get_ifname(char *name, char *p) +{ + char *d; + char *dname; + + while (isspace(*p)) + p++; + + while (*p) { + if (isspace(*p)) + break; + if (*p == ':') { + d = p, dname = name; + *name++ = *p++; + while (isdigit(*p)) + *name++ = *p++; + if (*p != ':') { + p = d; + name = dname; + } + if (*p == '\0') + return NULL; + p++; + break; + } + *name++ = *p++; + } + *name++ = '\0'; + return p; +} + +static inline int scan_net_stat(char *buf, res_net_ifstat_t *i, int ver) +{ + switch (ver) { + case 3: + sscanf(buf, + "%llu %llu %lu %lu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu %lu", + &i->rx_bytes, + &i->rx_packets, + &i->rx_errors, + &i->rx_dropped, + &i->rx_fifo_err, + &i->rx_frame_err, + &i->rx_compressed, + &i->rx_multicast, + + &i->tx_bytes, + &i->tx_packets, + &i->tx_errors, + &i->tx_dropped, + &i->tx_fifo_err, + &i->tx_collisions, + &i->tx_carrier_err, + &i->tx_compressed); + break; + case 2: + sscanf(buf, + "%llu %llu %lu %lu %lu %lu %llu %llu %lu %lu %lu %lu %lu", + &i->rx_bytes, + &i->rx_packets, + &i->rx_errors, + &i->rx_dropped, + &i->rx_fifo_err, + &i->rx_frame_err, + + &i->tx_bytes, + &i->tx_packets, + &i->tx_errors, + &i->tx_dropped, + &i->tx_fifo_err, + &i->tx_collisions, + &i->tx_carrier_err); + i->rx_multicast = 0; + break; + case 1: + sscanf(buf, + "%llu %lu %lu %lu %lu %llu %lu %lu %lu %lu %lu", + &i->rx_packets, + &i->rx_errors, + &i->rx_dropped, + &i->rx_fifo_err, + &i->rx_frame_err, + + &i->tx_packets, + &i->tx_errors, + &i->tx_dropped, + &i->tx_fifo_err, + &i->tx_collisions, + &i->tx_carrier_err); + i->rx_bytes = 0; + i->tx_bytes = 0; + i->rx_multicast = 0; + break; + } + return 0; +} + +static inline int getallnetinfo(void *out, void *hint) +{ char buf[NETBUF_1024]; + FILE *fp; + char ifname[IFNAMSIZ]; + int ver = 0; + char *p; + int inum = 0; + int i; + res_net_ifstat_t *lst; + size_t msz = 0; + int err; + + /* Hint should not be null */ + if (hint == NULL) { + eprintf( + "Hint should be provided for RES_NET_ALLIFSTAT"); + errno = EINVAL; + return -1; + } + + fp = fopen(NETDEV_FILE, "r"); + if (fp == NULL) { + err = errno; + eprintf("while opening File %s with errno: %d", + NETDEV_FILE, errno); + errno = err; + return -1; + } + + fgets(buf, sizeof(buf), fp); + fgets(buf, sizeof(buf), fp); + ver = procnetdev_ver(buf); + inum = *(int *)hint; + + /* Hint should hold number of interfaces for which memory is + * allocated in out. If number of interfaces is 0 then allocated + * memory for all interfaces and fill it with interface stat. + */ + if (inum == 0) { + i = 0; + + /* Memory is allocated for NET_ALLIFSTAT_SZ interfaces + * initially and then reallocated in chunks of + * NET_ALLIFSTAT_SZ till all information is read. + */ + msz = sizeof(res_net_ifstat_t) * NET_ALLIFSTAT_SZ; + lst = (res_net_ifstat_t *)malloc(msz); + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (i == NET_ALLIFSTAT_SZ) { + msz = (sizeof(res_net_ifstat_t) * inum) + + NET_ALLIFSTAT_SZ; + lst = (res_net_ifstat_t *)realloc(lst, msz); + i = 0; + } + p = get_ifname(ifname, buf); + strcpy(lst[inum].ifname, ifname); + scan_net_stat(p, &lst[inum], ver); + inum++; + i++; + } + *((res_net_ifstat_t **)out) = lst; + *(int *)hint = inum; + } else { + /* If number of interfaces is not 0 then read interfaces + * stat sequentially and put it in memory allocated + * by user in out. Memory might be allocated for more interfaces + * than there are on system. So read sequentially till either + * all interfaces are read or memory allocated is totally + * filled. + */ + if (out == NULL) { + errno = EINVAL; + return -1; + } + + for (i = 0; + fgets(buf, sizeof(buf), fp) != NULL && i < inum; + i++) { + p = get_ifname(ifname, buf); + strcpy(((res_net_ifstat_t *)out)[i].ifname, ifname); + scan_net_stat(p, &(((res_net_ifstat_t *)out)[i]), ver); + } + + /* Write how many interfaces are read */ + *(int *)hint = i; + } + fclose(fp); + return 0; +} + +/* Get resource information related to network */ +int getnetinfo(int res_id, void *out, void *hint, int pid, int flags) +{ + char buf[NETBUF_1024]; + FILE *fp; + int err = 0; + char ifname[IFNAMSIZ]; + int ver = 0; + char *p; + + switch (res_id) { + case RES_NET_IFSTAT: + /* Interface name should be provided */ + if (hint == NULL) { + eprintf( + "Interface name is not provided for RES_NET_IFSTAT"); + errno = EINVAL; + return -1; + } + + /* Interface name is provided. Open /proc/net/dev and read + * through it. + */ + fp = fopen(NETDEV_FILE, "r"); + if (fp == NULL) { + err = errno; + eprintf("while opening File %s with errno: %d", + NETDEV_FILE, errno); + errno = err; + return -1; + } + + /* Skip first line */ + fgets(buf, sizeof(buf), fp); + + fgets(buf, sizeof(buf), fp); + /* Get what information /proc/net/dev holds so that we can + * read it in correct format. + */ + ver = procnetdev_ver(buf); + + /* Read each interface till we find the interface which we + * are looking for. + */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* get interface name in the line */ + p = get_ifname(ifname, buf); + + if ((strncasecmp((char *)hint, ifname, + sizeof(*(char *)hint))) == 0) { + /* Interface found, scan information */ + strcpy(((res_net_ifstat_t *)out)->ifname, + ifname); + scan_net_stat(p, (res_net_ifstat_t *)out, ver); + break; + } + } + + fclose(fp); + break; + + /* Information for all interfaces is asked for */ + case RES_NET_ALLIFSTAT: + return getallnetinfo(out, hint); + + default: + eprintf("Resource Id is invalid"); + errno = EINVAL; + return -1; + } + + return 0; +} + +int populate_netinfo(res_blk_t *res, int pid, int flags) +{ + void *p = NULL; + + for (int i = 0; i < res->res_count; i++) { + switch (res->res_unit[i]->res_id) { + case RES_NET_IFSTAT: + if (getnetinfo(RES_NET_IFSTAT, + res->res_unit[i]->data.ptr, + res->res_unit[i]->hint, 0, 0) == -1) { + res->res_unit[i]->status = errno; + } else + res->res_unit[i]->status = RES_STATUS_FILLED; + break; + case RES_NET_ALLIFSTAT: + res->res_unit[i]->hint = (int)0; + + if (getnetinfo(RES_NET_ALLIFSTAT, &p, + &(res->res_unit[i]->hint), 0, 0) == -1) { + res->res_unit[i]->status = errno; + } else { + res->res_unit[i]->status = RES_STATUS_FILLED; + res->res_unit[i]->data.ptr = p; + } + break; + default: + res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED; + } + } + return 0; +} diff --git a/resnet.h b/resnet.h new file mode 100644 index 0000000..164ce16 --- /dev/null +++ b/resnet.h @@ -0,0 +1,14 @@ +#ifndef _RESNET_H +#define _RESNET_H + +#define NETDEV_FILE "/proc/net/dev" + +#define NET_ALLIFSTAT_SZ 4 +#define NETBUF_1024 1024 + +extern int netdev_fd; + +extern int populate_netinfo(struct res_blk *res, int pid, int flags); +extern int getnetinfo(int res_id, void *out, void *hint, int pid, int flags); + +#endif /* _RESNET_H */ diff --git a/resource.c b/resource.c new file mode 100644 index 0000000..e0c4e42 --- /dev/null +++ b/resource.c @@ -0,0 +1,211 @@ +#include "resource.h" +#include +#include +#include +#include + +#include "resource_impl.h" +#include "resmem.h" +#include "resnet.h" + +/* Allocate memory for bulk resource information and initiate it + * properly. + */ +res_blk_t *res_build_blk(int *res_ids, int res_count) +{ + res_unit_t *temp; + res_blk_t *res = NULL; + + if (!res_ids || res_count <= 0) { + errno = EINVAL; + return NULL; + } + + /* Allocate memory to hold addresses of individual resource + * information. + */ + res = (res_blk_t *) + malloc(sizeof(res_blk_t) + (sizeof(res_unit_t *)) * res_count); + res->res_count = res_count; + + /* Allocate and set each resource information properly */ + for (int i = 0; i < res_count; i++) { + /* Allocate memory for resource info */ + temp = (res_unit_t *)malloc(sizeof(res_unit_t)); + memset(temp, 0, sizeof(res_unit_t)); + temp->status = RES_STATUS_EMPTY; + + /* Some resource information are big and need extra allocation. + * In these cases an address is returned which hold actual + * resource information. + */ + switch (res_ids[i]) { + case RES_MEM_ACTIVE: + case RES_MEM_INACTIVE: + case RES_MEM_AVAILABLE: + case RES_MEM_FREE: + case RES_MEM_TOTAL: + case RES_MEM_PAGESIZE: + case RES_MEM_SWAPFREE: + case RES_MEM_SWAPTOTAL: + case RES_KERN_COMPILE_TIME: + case RES_KERN_RELEASE: + case RES_NET_ALLIFSTAT: + break; + + case RES_NET_IFSTAT: + temp->data.ptr = (res_net_ifstat_t *) + malloc(sizeof(res_net_ifstat_t)); + break; + + case RES_MEM_INFOALL: + temp->data.ptr = (res_mem_infoall_t *) + malloc(sizeof(res_mem_infoall_t)); + break; + + default: + eprintf("Invalid resource ID: %d", res_ids[i]); + while (--i >= 0) + free(res->res_unit[i]); + free(temp); + free(res); + errno = EINVAL; + return NULL; + } + + temp->res_id = res_ids[i]; + res->res_unit[i] = temp; + } + + return res; +} + +/* Free resources allocated in res_build */ +void res_destroy_blk(res_blk_t *res) +{ + for (int i = 0; i < res->res_count; i++) { + /* Some resource information are big and needed extra + * Free memory allocated for that. + */ + switch (res->res_unit[i]->res_id) { + case RES_NET_IFSTAT: + case RES_MEM_INFOALL: + free((res->res_unit[i])->data.ptr); + break; + } + + /* Free memory tho hold this resource info */ + free(res->res_unit[i]); + } + free(res); +} + +/* read resource information corresponding to res_id, out should have been + * properly allocated by caller if required. + */ +int res_read(int res_id, void *out, void *hint, int pid, int flags) +{ + struct utsname t; + + if (out == NULL) { + switch (res_id) { + /* In case of RES_NET_ALLIFSTAT memory is allocated on the + * basis of number of interfaces available on system. + * So out can be NULL in that case. + */ + case RES_NET_ALLIFSTAT: + break; + + default: + eprintf("out argument cannot be NULL"); + errno = EINVAL; + return -1; + } + } + + /* Check if memory proc file is needed to open */ + if (res_id >= MEM_MIN && res_id < MEM_MAX) + return getmeminfo(res_id, out, hint, pid, flags); + + /* Check if net proc file is needed to open */ + if (res_id >= NET_MIN && res_id < NET_MAX) + return getnetinfo(res_id, out, hint, pid, flags); + + switch (res_id) { + case RES_KERN_RELEASE: + uname(&t); + strcpy(out, t.release); + break; + + case RES_KERN_COMPILE_TIME: + uname(&t); + sscanf(t.version, "%*s%*s%*s%[^\t\n]", (char *) out); + break; + + default: + eprintf("Resource Id is invalid"); + errno = EINVAL; + return -1; + } + return 0; +} + +/* Read bulk resource information */ +int res_read_blk(res_blk_t *res, int pid, int flags) +{ + int ismeminforeq = 0; + int isnetdevreq = 0; + struct utsname t; + + /* Loop through all resource information. If it can be filled through + * a syscall or such method then fill it. Else set flags which tell + * what files might have the information. + */ + for (int i = 0; i < res->res_count; i++) { + switch (res->res_unit[i]->res_id) { + case RES_MEM_TOTAL: + case RES_MEM_FREE: + case RES_MEM_AVAILABLE: + case RES_MEM_ACTIVE: + case RES_MEM_INACTIVE: + case RES_MEM_SWAPFREE: + case RES_MEM_SWAPTOTAL: + case RES_MEM_INFOALL: + ismeminforeq = 1; + break; + + case RES_MEM_PAGESIZE: + (res->res_unit[i]->data).sz = sysconf(_SC_PAGESIZE); + res->res_unit[i]->status = RES_STATUS_FILLED; + break; + + case RES_KERN_RELEASE: + uname(&t); + strcpy((res->res_unit[i]->data).str, t.release); + res->res_unit[i]->status = RES_STATUS_FILLED; + break; + + case RES_KERN_COMPILE_TIME: + uname(&t); + sscanf(t.version, "%*s%*s%*s%[^\t\n]", + (res->res_unit[i]->data).str); + break; + + case RES_NET_IFSTAT: + case RES_NET_ALLIFSTAT: + isnetdevreq = 1; + break; + + default: + res->res_unit[i]->status = RES_STATUS_NOTSUPPORTED; + } + } + + if (ismeminforeq) + populate_meminfo(res, pid, flags); + + if (isnetdevreq) + populate_netinfo(res, pid, flags); + + return 0; +} diff --git a/resource.h b/resource.h new file mode 100644 index 0000000..963877d --- /dev/null +++ b/resource.h @@ -0,0 +1,147 @@ +#ifndef _RESOURCE_H +#define _RESOURCE_H + +#include +#include + +/* libresource version */ +#define LIBRESOURCE_API_VERSION 1 + +/* Possible status for libresource information returned */ +/* libresource information was fetched correctly */ +#define RES_STATUS_FILLED 0 +/* There was some error in fetching libresource information. In most cases + * errno will be set. + */ +#define RES_STATUS_EMPTY -1 +/* Resource information is not supported yet, or Invalid resource + * information. + */ +#define RES_STATUS_NOTSUPPORTED -2 +/* If partial information was read for a libresource information. For example + * a string was read partially. + */ +#define RES_STATUS_TRUNCATED -3 + + +/* Maximum size of a resource information data type which can be returned + * without explicitly allocating memory for it. If resource information + * size is larger than this this a pointer to allocated memory will be + * returned. + */ +#define RES_UNIT_OUT_SIZE 256 + +/* This union is used to return resource information of various types */ +union r_data { + int i; + size_t sz; + long l; + char str[RES_UNIT_OUT_SIZE]; + void *ptr; +}; + +/* In case of res_read_blk, each resource information will be represented by + * following structure. + */ +typedef struct res_unit { + int status; + int res_id; + void *hint; + union r_data data; +} res_unit_t; + +/* In case of bulk read (res_read_blk), this structure will hold all required + * information needed to do so. + */ +typedef struct res_blk { + int res_count; + res_unit_t *res_unit[0]; +} res_blk_t; + +/* Resource information is divided in broad categories and each + * category is assigned a number range for its resource information + * Memory related (RES_MEM_*) 1024- + * Network related (RES_NET_*) 2048- + * General kernel related (RES_KERN_*) 3072- + * This is done to facilitate any future optimization which can be made + * on the basis of resource information (hashing etc ?) + */ +#define MEM_MIN 1024 +#define RES_MEM_HUGEPAGEALL 1025 +#define RES_MEM_HUGEPAGESIZE 1026 +#define RES_MEM_INACTIVE 1027 +#define RES_MEM_INFOALL 1028 +#define RES_MEM_AVAILABLE 1029 +#define RES_MEM_FREE 1030 +#define RES_MEM_TOTAL 1031 +#define RES_MEM_PAGESIZE 1032 +#define RES_MEM_SHMALL 1033 +#define RES_MEM_SHMINFOALL 1034 +#define RES_MEM_SHMMAX 1035 +#define RES_MEM_SHMMNI 1036 +#define RES_MEM_SWAPFREE 1037 +#define RES_MEM_SWAPTOTAL 1038 +#define RES_MEM_ACTIVE 1039 +#define MEM_MAX 1040 + +#define NET_MIN 2048 +#define RES_NET_IFSTAT 2049 +#define RES_NET_ALLIFSTAT 2050 +#define NET_MAX 2051 + +#define KERN_MIN 3072 +#define RES_KERN_COMPILE_TIME 3073 +#define RES_KERN_RELEASE 3074 +#define KERN_MAX 3075 + +/* Structure to return RES_MEM_INFOALL resource information */ +typedef struct res_mem_infoall { + size_t memfree; + size_t memtotal; + size_t memavailable; + size_t active; + size_t inactive; + size_t swaptotal; + size_t swapfree; +} res_mem_infoall_t; + +/* Structure to return RES_MEM_ALLIFSTAT resource information */ +typedef struct res_net_ifstat { + char ifname[IFNAMSIZ]; + unsigned long long rx_bytes; + unsigned long long rx_packets; + unsigned long rx_errors; + unsigned long rx_dropped; + unsigned long rx_fifo_err; + unsigned long rx_frame_err; + unsigned long rx_compressed; + unsigned long rx_multicast; + unsigned long long tx_bytes; + unsigned long long tx_packets; + unsigned long tx_errors; + unsigned long tx_dropped; + unsigned long tx_fifo_err; + unsigned long tx_collisions; + unsigned long tx_carrier_err; + unsigned long tx_compressed; +} res_net_ifstat_t; + +/* Allocating memory and building a res_blk structure to return bulk + * resource information. + */ +extern res_blk_t *res_build_blk(int *res_ids, int res_count); + +/* Reading bulk resource information. Memory must be properly allocated and + * all fields should be properly filled to return error free resource + * information. res_build_blk call is suggested to allocate build res_blk_t + * structure. + */ +extern int res_read_blk(res_blk_t *resblk, int pid, int flags); + +/* Free allocated memory from res_build_blk */ +extern void res_destroy_blk(res_blk_t *resblk); + +/* Read a resource information. Memory for out should be properly allocated */ +extern int res_read(int res_id, void *out, void *hint, int pid, int flags); + +#endif /* RESOURCE_H */ diff --git a/resource_impl.h b/resource_impl.h new file mode 100644 index 0000000..e40e1d7 --- /dev/null +++ b/resource_impl.h @@ -0,0 +1,47 @@ +#ifndef _RESOURCE_IMPL_H +#define _RESOURCE_IMPL_H + +#include +#include +#define SIZE_OF_TEMP_BUF 2048 +#define eprintf(msg, ...) fprintf(stderr,\ + "Err at line %d in file %s: "msg"\n",\ + __LINE__, __FILE__, ##__VA_ARGS__)\ + + +static inline int file_to_buf(char *fname, char *buf) +{ + int fd = 0; + size_t rdsz = 0; + int err = 0; + + fd = open(fname, O_RDONLY); + if (fd == -1) { + err = errno; + eprintf("in opening File %s with errno: %d", fname, errno); + errno = err; + return -1; + } + + if (lseek(fd, 0L, SEEK_SET) == -1) { + err = errno; + eprintf("in lseek for File %s with errno: %d", fname, errno); + close(fd); + errno = err; + return -1; + } + + rdsz = read(fd, buf, SIZE_OF_TEMP_BUF - 1); + if (rdsz < 0) { + err = errno; + eprintf("in read from File %s with errno: %d", fname, errno); + close(fd); + errno = err; + return -1; + } + buf[rdsz] = '\0'; + close(fd); + return rdsz; +} + +#endif /* _RESOURCE_IMPL_H */ -- 2.7.4 From jonathan.helman at oracle.com Thu Mar 1 19:55:30 2018 From: jonathan.helman at oracle.com (Jonathan Helman) Date: Thu, 1 Mar 2018 11:55:30 -0800 Subject: [libresource-devel] [PATCH] library of interfaces to fetch system resource information In-Reply-To: <1519887499-12171-1-git-send-email-rahul.x.yadav@oracle.com> References: <1519887499-12171-1-git-send-email-rahul.x.yadav@oracle.com> Message-ID: <5690c180-c4ca-8d5b-9a6f-be4b8c04b973@oracle.com> Hi Rahul, I have a few questions and comments below on the resource.h API and README. Did not check out the implementation yet. Jon On 02/28/2018 10:58 PM, Rahul Yadav wrote: > 1: System resource Information related to memory, CPU, stat, networking, > security etc. > 2: Currently most of such information is read from /proc and /sys. > 3: Add basic infrastructure and code for some of memory and network related > information. > 4: Update README file with the use cases and design details. > 5: Added example codes in README. > > Signed-off-by: Rahul Yadav > --- > Makefile | 18 ++++ > README.md | 160 ++++++++++++++++++++++++++-- > resmem.c | 218 ++++++++++++++++++++++++++++++++++++++ > resmem.h | 16 +++ > resnet.c | 316 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ > resnet.h | 14 +++ > resource.c | 211 +++++++++++++++++++++++++++++++++++++ > resource.h | 147 ++++++++++++++++++++++++++ > resource_impl.h | 47 +++++++++ > 9 files changed, 1137 insertions(+), 10 deletions(-) > create mode 100644 Makefile > create mode 100644 resmem.c > create mode 100644 resmem.h > create mode 100644 resnet.c > create mode 100644 resnet.h > create mode 100644 resource.c > create mode 100644 resource.h > create mode 100644 resource_impl.h > > diff --git a/Makefile b/Makefile > new file mode 100644 > index 0000000..2c71328 > --- /dev/null > +++ b/Makefile > @@ -0,0 +1,18 @@ > +CC = gcc > +CFLAGS = -g -Wall -Werror -fPIC > +DEPS = resource.h res_impl.h resmem.h resnet.h > +OBJ = resource.o resmem.o resnet.o > +LIB = libresource.so > +TEST = test > +RM = rm -rf > +CP = cp > + > +%.o: %.c $(DEPS) > + $(CC) -c -o $@ $< $(CFLAGS) > + > +all: $(OBJ) > + $(CC) -shared -o $(LIB) $^ $(CFLAGS) > + > +.PHONY : clean > +clean: > + $(RM) $(LIB) $(OBJ) $(TEST) > diff --git a/README.md b/README.md > index 960468d..97b37df 100644 > --- a/README.md > +++ b/README.md > @@ -1,14 +1,154 @@ > # libresource > +library of interfaces through which we can get system resource information > +like memory, CPU, stat, networking, device etc. > +Currently most of such information is read from /proc and /sys. > > -libresource is a community effort to generate a new library which tools > -like ps, top, etc can use to get information typically garnered from > -/proc. libresource however will attempt to virtualize the data according > -to the calling task's cgroup information. > +Use case: > +1: Ease of use - > +Currently applications and tools need to read this info mostly from > +/proc and /sys file-systems. In most of the cases complex string > +parsing is involved which is needed to be done in application > +code. With the library interfaces application can get the information > +directly and all the string parsing, if any, will be done by library. > +2: Stability - > +If the format in which the information is provided in /proc or /sys > +file-system is changed then the application code is changed to align > +with those changes. What is the method by which you do this? Is there a chance the library breaks when /proc or /sys is updated, until libresource is updated? Also if a better way to get information comes in > +future, like through a syscall or a sysconf then again application code > +needs to be changed to get the benefit of it. Library will take care of > +such changes. > +3: Virtualization - > +In cases where DB is running in a virtualized environment using > +cgroup or namespaces, reading from /proc and /sys file-systems > +might not give correct information as these are not cgroup > +aware. Library API will take care of this e.g. if a process > +is running in a cgroup then library should provide information > +which is local to that cgroup. > > -# contributing > +Design: > +Each resource is identified by a resource id. User land application should > +provide the id while calling the interfaces to fetch the information. > +Following are resource IDs which are already implemented. > > -We will interact through pull requests and the issue tracker. I think we > -should start with pull requests editing the CONTRIBUTORS so we can have a > -feel whether enough people are able to contribute or interested in following > -for the project to be successful. We can also start pull requests against > -the specs/api.md file to discuss what that should look like. > +RES_MEM_ACTIVE > +RES_MEM_INACTIVE > +RES_MEM_AVAILABLE > +RES_MEM_FREE > +RES_MEM_TOTAL > +RES_MEM_PAGESIZE > +RES_MEM_SWAPFREE > +RES_MEM_SWAPTOTAL > +RES_KERN_COMPILE_TIME > +RES_KERN_RELEASE > +RES_NET_ALLIFSTAT > +RES_NET_IFSTAT > +RES_MEM_INFOALL > + > +Interfaces: > +1: > +int res_read(int res_id, void *out, void *hint, int pid, int flags); > + > +This is to read a resource information. A valid resource id should be provided > +in res_id, out should be properly allocated on the basis of size of resource > +information, hint should be given where needed. Currently pid and flags are > +not used, they are for future extensions. > + > +2: > + > +If an application wants to read multiple resource information in one call, it > +can call res_*_blk APIs to do so. > + > +2.1 structs > +Following struct holds information about one resource in such case. > +typedef struct res_unit { > + int status; > + int res_id; > + void *hint; > + union r_data data; > +} res_unit_t; > + > +An array of these form res_blk_t structure as below. > +typedef struct res_blk { > + int res_count; > + res_unit_t *res_unit[0]; > +} res_blk_t; > + > +res_blk_t strcut is used in all res_*_blk interfaces. s/strcut/struct > + > +2.2 functions > +res_blk_t *res_build_blk(int *res_ids, int res_count); > + > +This allocates memory for resources and initiates them properly. res_ids > +holds and array of valid resource ids and res_count holds number of > +resource ids. It also initiates struct fields properly. > + > +int res_read_blk(res_blk_t *resblk, int pid, int flags); > +Reading bulk resource information. Memory must be properly allocated and > +all fields should be properly filled to return error free resource > +information. res_build_blk call is suggested to allocate build res_blk_t > +structure. It seems that flags, since for future extensions, would be per res_unit (like the hint). Could you explain why flags is per res_blk? > + > +/* Free allocated memory from res_build_blk */ > +extern void res_destroy_blk(res_blk_t *resblk); > + > +Example > +1: > +Reading total memory > +size_t stemp = 0; > +res_read(RES_MEM_TOTAL,&stemp,NULL, 0, 0); > +printf("MEMTOTAL is: %zu\n", stemp); > + > + > +2: > +reading network interface related statistics for interface named "lo" > +res_net_ifstat_t ifstat; > +res_read(RES_NET_IFSTAT,&ifstat, (void *)"lo",0, 0); > +printf("status for %s: %llu %llu\n", ifstat.ifname, > + ifstat.rx_bytes, > + ifstat.rx_packets > + ); > +3: > +Reading multiple resouce information in one call. s/resouce/resource > + > + res_blk_t *b = NULL; > + int a[NUM] = {RES_MEM_PAGESIZE, > + RES_MEM_TOTAL, > + RES_MEM_AVAILABLE, > + RES_MEM_INFOALL, > + RES_KERN_RELEASE, > + RES_NET_IFSTAT, > + RES_NET_ALLIFSTAT, > + RES_KERN_COMPILE_TIME > + }; > + b = res_build_blk(a, NUM); > + b->res_unit[5]->hint = (void *)"lo"; > + > + res_read_blk(b, 0, 0); > + > + printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable %ld kb,\n" > + " mefree %ld kb,\n release %s,\n compile time %s\n", s/mefree/memfree > + b->res_unit[0]->data.sz, > + b->res_unit[1]->data.sz, > + b->res_unit[2]->data.sz, > + ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree, > + b->res_unit[4]->data.str, > + b->res_unit[7]->data.str > + ); > + > + res_net_ifstat_t *ip = (res_net_ifstat_t *)b->res_unit[5]->data.ptr; > + printf("stat for interface %s: %llu %llu\n", ip->ifname, > + ip->rx_bytes, > + ip->rx_packets > + ); > + > + int k = (int)(long long)b->res_unit[6]->hint; > + res_net_ifstat_t *ipp = (res_net_ifstat_t *)b->res_unit[6]->data.ptr; > + for (int j=0; j< k; j++) { > + printf("stat for interface %s: %llu %llu\n", ipp[j].ifname, > + ipp[j].rx_bytes, > + ipp[j].rx_packets > + ); > + } > + > + free(ipp); What is ipp? > + res_destroy_blk(b); > > diff --git a/resource.h b/resource.h > new file mode 100644 > index 0000000..963877d > --- /dev/null > +++ b/resource.h > @@ -0,0 +1,147 @@ > +#ifndef _RESOURCE_H > +#define _RESOURCE_H > + > +#include What is this file included for here? > +#include > + > +/* libresource version */ > +#define LIBRESOURCE_API_VERSION 1 > + > +/* Possible status for libresource information returned */ > +/* libresource information was fetched correctly */ > +#define RES_STATUS_FILLED 0 > +/* There was some error in fetching libresource information. In most cases > + * errno will be set. > + */ > +#define RES_STATUS_EMPTY -1 > +/* Resource information is not supported yet, or Invalid resource > + * information. Seems that notsupported should be differentiated from invalid resource info. > + */ > +#define RES_STATUS_NOTSUPPORTED -2 > +/* If partial information was read for a libresource information. For example > + * a string was read partially. > + */ > +#define RES_STATUS_TRUNCATED -3 How do you know which information is valid? Is this if you want multiple units in a block and only some of the units are filled? > + > + > +/* Maximum size of a resource information data type which can be returned > + * without explicitly allocating memory for it. If resource information > + * size is larger than this this a pointer to allocated memory will be > + * returned. > + */ > +#define RES_UNIT_OUT_SIZE 256 > + > +/* This union is used to return resource information of various types */ > +union r_data { > + int i; > + size_t sz; > + long l; > + char str[RES_UNIT_OUT_SIZE]; > + void *ptr; > +}; Hmm, this is a confusing idea, IMO. Maybe everything is in void *ptr? And then this union could be collapsed. > + > +/* In case of res_read_blk, each resource information will be represented by > + * following structure. > + */ > +typedef struct res_unit { > + int status; > + int res_id; > + void *hint; > + union r_data data; > +} res_unit_t; > + > +/* In case of bulk read (res_read_blk), this structure will hold all required > + * information needed to do so. > + */ > +typedef struct res_blk { > + int res_count; > + res_unit_t *res_unit[0]; > +} res_blk_t; > + > +/* Resource information is divided in broad categories and each > + * category is assigned a number range for its resource information > + * Memory related (RES_MEM_*) 1024- > + * Network related (RES_NET_*) 2048- > + * General kernel related (RES_KERN_*) 3072- > + * This is done to facilitate any future optimization which can be made > + * on the basis of resource information (hashing etc ?) Should these be u64 instead of int then? > + */ > +#define MEM_MIN 1024 > +#define RES_MEM_HUGEPAGEALL 1025 > +#define RES_MEM_HUGEPAGESIZE 1026 > +#define RES_MEM_INACTIVE 1027 > +#define RES_MEM_INFOALL 1028 > +#define RES_MEM_AVAILABLE 1029 > +#define RES_MEM_FREE 1030 > +#define RES_MEM_TOTAL 1031 > +#define RES_MEM_PAGESIZE 1032 > +#define RES_MEM_SHMALL 1033 > +#define RES_MEM_SHMINFOALL 1034 > +#define RES_MEM_SHMMAX 1035 > +#define RES_MEM_SHMMNI 1036 What is MNI? > +#define RES_MEM_SWAPFREE 1037 > +#define RES_MEM_SWAPTOTAL 1038 > +#define RES_MEM_ACTIVE 1039 > +#define MEM_MAX 1040 > + > +#define NET_MIN 2048 > +#define RES_NET_IFSTAT 2049 > +#define RES_NET_ALLIFSTAT 2050 > +#define NET_MAX 2051 > + > +#define KERN_MIN 3072 > +#define RES_KERN_COMPILE_TIME 3073 > +#define RES_KERN_RELEASE 3074 > +#define KERN_MAX 3075 > + > +/* Structure to return RES_MEM_INFOALL resource information */ > +typedef struct res_mem_infoall { > + size_t memfree; > + size_t memtotal; > + size_t memavailable; > + size_t active; > + size_t inactive; > + size_t swaptotal; > + size_t swapfree; > +} res_mem_infoall_t; > + > +/* Structure to return RES_MEM_ALLIFSTAT resource information */ > +typedef struct res_net_ifstat { > + char ifname[IFNAMSIZ]; > + unsigned long long rx_bytes; > + unsigned long long rx_packets; > + unsigned long rx_errors; > + unsigned long rx_dropped; > + unsigned long rx_fifo_err; > + unsigned long rx_frame_err; > + unsigned long rx_compressed; > + unsigned long rx_multicast; > + unsigned long long tx_bytes; > + unsigned long long tx_packets; > + unsigned long tx_errors; > + unsigned long tx_dropped; > + unsigned long tx_fifo_err; > + unsigned long tx_collisions; > + unsigned long tx_carrier_err; > + unsigned long tx_compressed; > +} res_net_ifstat_t; Maybe this struct name should have the word all in it to be consistent with struct res_mem_infoall? > + > +/* Allocating memory and building a res_blk structure to return bulk > + * resource information. > + */ > +extern res_blk_t *res_build_blk(int *res_ids, int res_count); > + > +/* Reading bulk resource information. Memory must be properly allocated and > + * all fields should be properly filled to return error free resource > + * information. res_build_blk call is suggested to allocate build res_blk_t s/allocate build/allocate and build > + * structure. > + */ > +extern int res_read_blk(res_blk_t *resblk, int pid, int flags); > + > +/* Free allocated memory from res_build_blk */ > +extern void res_destroy_blk(res_blk_t *resblk); > + > +/* Read a resource information. Memory for out should be properly allocated */ > +extern int res_read(int res_id, void *out, void *hint, int pid, int flags); > + > +#endif /* RESOURCE_H */ > From rahul.x.yadav at oracle.com Thu Mar 1 20:48:09 2018 From: rahul.x.yadav at oracle.com (Rahul Yadav) Date: Thu, 1 Mar 2018 12:48:09 -0800 Subject: [libresource-devel] [PATCH] library of interfaces to fetch system resource information In-Reply-To: <5690c180-c4ca-8d5b-9a6f-be4b8c04b973@oracle.com> References: <1519887499-12171-1-git-send-email-rahul.x.yadav@oracle.com> <5690c180-c4ca-8d5b-9a6f-be4b8c04b973@oracle.com> Message-ID: <4c9497f7-08a7-4c5a-979e-e279c6085f6c@oracle.com> Hi Jon, Thanks for initial review, I have provided comments below. I will make changes in code and README etc as mentioned at few places in my reply. Thanks Rahul On 3/1/2018 11:55 AM, Jonathan Helman wrote: > Hi Rahul, > > I have a few questions and comments below on the resource.h API and > README. Did not check out the implementation yet. > > Jon > > On 02/28/2018 10:58 PM, Rahul Yadav wrote: >> 1: System resource Information related to memory, CPU, stat, networking, >> security etc. >> 2: Currently most of such information is read from /proc and /sys. >> 3: Add basic infrastructure and code for some of memory and network >> related >> information. >> 4: Update README file with the use cases and design details. >> 5: Added example codes in README. >> >> Signed-off-by: Rahul Yadav >> --- >>   Makefile        |  18 ++++ >>   README.md       | 160 ++++++++++++++++++++++++++-- >>   resmem.c        | 218 ++++++++++++++++++++++++++++++++++++++ >>   resmem.h        |  16 +++ >>   resnet.c        | 316 >> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ >>   resnet.h        |  14 +++ >>   resource.c      | 211 +++++++++++++++++++++++++++++++++++++ >>   resource.h      | 147 ++++++++++++++++++++++++++ >>   resource_impl.h |  47 +++++++++ >>   9 files changed, 1137 insertions(+), 10 deletions(-) >>   create mode 100644 Makefile >>   create mode 100644 resmem.c >>   create mode 100644 resmem.h >>   create mode 100644 resnet.c >>   create mode 100644 resnet.h >>   create mode 100644 resource.c >>   create mode 100644 resource.h >>   create mode 100644 resource_impl.h >> >> diff --git a/Makefile b/Makefile >> new file mode 100644 >> index 0000000..2c71328 >> --- /dev/null >> +++ b/Makefile >> @@ -0,0 +1,18 @@ >> +CC = gcc >> +CFLAGS = -g -Wall -Werror -fPIC >> +DEPS = resource.h res_impl.h resmem.h resnet.h >> +OBJ = resource.o resmem.o resnet.o >> +LIB = libresource.so >> +TEST = test >> +RM = rm -rf >> +CP = cp >> + >> +%.o: %.c $(DEPS) >> +    $(CC) -c -o $@ $< $(CFLAGS) >> + >> +all: $(OBJ) >> +    $(CC) -shared -o $(LIB) $^ $(CFLAGS) >> + >> +.PHONY : clean >> +clean: >> +    $(RM) $(LIB) $(OBJ) $(TEST) >> diff --git a/README.md b/README.md >> index 960468d..97b37df 100644 >> --- a/README.md >> +++ b/README.md >> @@ -1,14 +1,154 @@ >>   # libresource >> +library of interfaces through which we can get system resource >> information >> +like memory, CPU, stat, networking, device etc. >> +Currently most of such information is read from /proc and /sys. >>   -libresource is a community effort to generate a new library which >> tools >> -like ps, top, etc can use to get information typically garnered from >> -/proc.  libresource however will attempt to virtualize the data >> according >> -to the calling task's cgroup information. >> +Use case: >> +1: Ease of use - >> +Currently applications and tools need to read this info mostly from >> +/proc and /sys file-systems. In most of the cases complex string >> +parsing is involved which is needed to be done in application >> +code. With the library interfaces application can get the information >> +directly and all the string parsing, if any, will be done by library. >> +2: Stability - >> +If the format in which the information is provided in /proc or /sys >> +file-system is changed then the application code is changed to align >> +with those changes. > > What is the method by which you do this? Is there a chance the library > breaks when /proc or /sys is updated, until libresource is updated? If something changes in /proc which changes the format, then libresource code must need to change to pick those changes, else libresource interfaces will give ENODATA or such error till libresource is update. If the changes are done taking care of existing format, for example appending new info at the end of proc file, then libresource will keep working, Basically the idea is that application will not need to make any code changes, administrator just need to update the library and it should work. And by making code changes to library, all applications using the library will start using updated information. However I understand that, libresource should pick such changes as soon as it is available in /proc (or any such file) for it to work. Also there is a library version which can be checked by applications to make decisions. With that, if there are any significant changes to a particular resource then resource ID will be suffixed with V1, V2 etc. Old version will also work in that case for backward compatibility. > >  Also if a better way to get information comes in >> +future, like through a syscall or a sysconf then again application code >> +needs to be changed to get the benefit of it. Library will take care of >> +such changes. >> +3: Virtualization - >> +In cases where DB is running in a virtualized environment using >> +cgroup or namespaces, reading from /proc and /sys file-systems >> +might not give correct information as these are not cgroup >> +aware. Library API will take care of this e.g. if a process >> +is running in a cgroup then library should provide information >> +which is local to that cgroup. >>   -# contributing >> +Design: >> +Each resource is identified by a resource id. User land application >> should >> +provide the id while calling the interfaces to fetch the information. >> +Following are resource IDs which are already implemented. >>   -We will interact through pull requests and the issue tracker.  I >> think we >> -should start with pull requests editing the CONTRIBUTORS so we can >> have a >> -feel whether enough people are able to contribute or interested in >> following >> -for the project to be successful.  We can also start pull requests >> against >> -the specs/api.md file to discuss what that should look like. >> +RES_MEM_ACTIVE >> +RES_MEM_INACTIVE >> +RES_MEM_AVAILABLE >> +RES_MEM_FREE >> +RES_MEM_TOTAL >> +RES_MEM_PAGESIZE >> +RES_MEM_SWAPFREE >> +RES_MEM_SWAPTOTAL >> +RES_KERN_COMPILE_TIME >> +RES_KERN_RELEASE >> +RES_NET_ALLIFSTAT >> +RES_NET_IFSTAT >> +RES_MEM_INFOALL >> + >> +Interfaces: >> +1: >> +int res_read(int res_id, void *out, void *hint, int pid, int flags); >> > + >> +This is to read a resource information. A valid resource id should >> be provided >> +in res_id, out should be properly allocated on the basis of size of >> resource >> +information, hint should be given where needed. Currently pid and >> flags are >> +not used, they are for future extensions. >> + >> +2: >> + >> +If an application wants to read multiple resource information in one >> call, it >> +can call res_*_blk APIs to do so. >> + >> +2.1 structs >> +Following struct holds information about one resource in such case. >> +typedef struct res_unit { >> +        int status; >> +        int res_id; >> +        void *hint; >> +        union r_data data; >> +} res_unit_t; >> + >> +An array of these form res_blk_t structure as below. >> +typedef struct res_blk { >> +        int res_count; >> +        res_unit_t *res_unit[0]; >> +} res_blk_t; >> + >> +res_blk_t strcut is used in all res_*_blk interfaces. > > s/strcut/struct Will spell check again on whole PATCH. > >> + >> +2.2 functions >> +res_blk_t *res_build_blk(int *res_ids, int res_count); >> + >> +This allocates memory for resources and initiates them properly. >> res_ids >> +holds and array of valid resource ids and res_count holds number of >> +resource ids. It also initiates struct fields properly. >> + >> +int res_read_blk(res_blk_t *resblk, int pid, int flags); >> +Reading bulk resource information. Memory must be properly allocated >> and >> +all fields should be properly filled to return error free resource >> +information. res_build_blk call is suggested to allocate build >> res_blk_t >> +structure. > > It seems that flags, since for future extensions, would be per > res_unit (like the hint). Could you explain why flags is per res_blk? I was thinking flags as clues for overall libresource interfaces instead of individual resource ids. For example we can put a "consistent single snapshot"bit in the flag, which will mean that all information fetched should show one snapshot of system resource i.e. for example memfree, memtoal, and memavailble should be read at once instead of one by one where values might change in middle of reads. For that reason they are one per interface. > >> + >> +/* Free allocated memory from res_build_blk */ >> +extern void res_destroy_blk(res_blk_t *resblk); >> + >> +Example >> +1: >> +Reading total memory >> +size_t stemp = 0; >> +res_read(RES_MEM_TOTAL,&stemp,NULL, 0, 0); >> +printf("MEMTOTAL is: %zu\n", stemp); >> + >> + >> +2: >> +reading network interface related statistics for interface named "lo" >> +res_net_ifstat_t ifstat; >> +res_read(RES_NET_IFSTAT,&ifstat, (void *)"lo",0, 0); >> +printf("status for %s: %llu %llru\n", ifstat.ifname, >> +    ifstat.rx_bytes, >> +    ifstat.rx_packets >> +    ); >> +3: >> +Reading multiple resouce information in one call. > > s/resouce/resource > >> + >> +    res_blk_t *b = NULL; >> +    int a[NUM] = {RES_MEM_PAGESIZE, >> +                RES_MEM_TOTAL, >> +                RES_MEM_AVAILABLE, >> +                RES_MEM_INFOALL, >> +                RES_KERN_RELEASE, >> +                RES_NET_IFSTAT, >> +                RES_NET_ALLIFSTAT, >> +                RES_KERN_COMPILE_TIME >> +                }; >> +    b = res_build_blk(a, NUM); >> +    b->res_unit[5]->hint = (void *)"lo"; >> + >> +    res_read_blk(b, 0, 0); >> + >> +    printf("pagesize %ld bytes,\n memtotal %ld kb,\n memavailable >> %ld kb,\n" >> +            " mefree %ld kb,\n release %s,\n compile time %s\n", > > s/mefree/memfree > >> + b->res_unit[0]->data.sz, >> +            b->res_unit[1]->data.sz, >> +            b->res_unit[2]->data.sz, >> +            ((res_mem_infoall_t *)(b->res_unit[3]->data.ptr))->memfree, >> +            b->res_unit[4]->data.str, >> +            b->res_unit[7]->data.str >> +    ); >> + >> +    res_net_ifstat_t *ip = (res_net_ifstat_t >> *)b->res_unit[5]->data.ptr; >> +    printf("stat for interface %s: %llu %llu\n", ip->ifname, >> +        ip->rx_bytes, >> +        ip->rx_packets >> +        ); >> + >> +    int k = (int)(long long)b->res_unit[6]->hint; >> +    res_net_ifstat_t *ipp = (res_net_ifstat_t >> *)b->res_unit[6]->data.ptr; >> +    for (int j=0; j< k; j++) { >> +        printf("stat for interface %s: %llu %llu\n", ipp[j].ifname, >> +            ipp[j].rx_bytes, >> +            ipp[j].rx_packets >> +        ); >> +    } >> + >> +    free(ipp); > > What is ipp? For some resources (very few) memory might be allocated at the time of read, for such resources it needs to be freed in application code. I shall provide such info in man page, I am trying to figure out how to add man page for libraries. For now I will update README with such info. > >> +    res_destroy_blk(b); > > >> > > >> diff --git a/resource.h b/resource.h >> new file mode 100644 >> index 0000000..963877d >> --- /dev/null >> +++ b/resource.h >> @@ -0,0 +1,147 @@ >> +#ifndef    _RESOURCE_H >> +#define    _RESOURCE_H >> + >> +#include > > What is this file included for here? It is not needed, will remove it. > >> +#include >> + >> +/* libresource version */ >> +#define LIBRESOURCE_API_VERSION 1 >> + >> +/* Possible status for libresource information returned */ >> +/* libresource information was fetched correctly */ >> +#define RES_STATUS_FILLED 0 >> +/* There was some error in fetching libresource information. In most >> cases >> + * errno will be set. >> + */ >> +#define RES_STATUS_EMPTY -1 >> +/* Resource information is not supported yet, or Invalid resource >> + * information. > > Seems that notsupported should be differentiated from invalid resource > info. For library any invalid resource_id number is same as not supported resource_id numbers, right ? or I missed something here. > >> + */ >> +#define RES_STATUS_NOTSUPPORTED    -2 >> +/* If partial information was read for a libresource information. >> For example >> + * a string was read partially. >> + */ >> +#define RES_STATUS_TRUNCATED -3 > > How do you know which information is valid? If status is FILLED that means information is valid. > Is this if you want multiple units in a block and only some of the > units are filled? Yes this status should be used in such cases . > >> + >> + >> +/* Maximum size of a resource information data type which can be >> returned >> + * without explicitly allocating memory for it. If resource information >> + * size is larger than this this a pointer to allocated memory will be >> + * returned. >> + */ >> +#define RES_UNIT_OUT_SIZE    256 >> + >> +/* This union is used to return resource information of various >> types */ >> +union r_data { >> +    int i; >> +    size_t sz; >> +    long l; >> +    char str[RES_UNIT_OUT_SIZE]; >> +    void *ptr; >> +}; > > Hmm, this is a confusing idea, IMO. Maybe everything is in void *ptr? > And then this union could be collapsed. Even if it is a pointer application will need to know what is the return type in order to cast it. With pointer there will be one extra memory dereference to get the data, so i thought this is faster. > >> + >> +/* In case of res_read_blk, each resource information will be >> represented by >> + * following structure. >> + */ >> +typedef struct res_unit { >> +    int status; >> +    int res_id; >> +    void *hint; >> +    union r_data data; >> +} res_unit_t; >> + >> +/* In case of bulk read (res_read_blk), this structure will hold all >> required >> + * information needed to do so. >> + */ >> +typedef struct res_blk { >> +    int res_count; >> +    res_unit_t *res_unit[0]; >> +} res_blk_t; >> + >> +/* Resource information is divided in broad categories and each >> + * category is assigned a number range for its resource information >> + * Memory related            (RES_MEM_*)        1024- >> + * Network related            (RES_NET_*)        2048- >> + * General kernel related        (RES_KERN_*)        3072- >> + * This is done to facilitate any future optimization which can be made >> + * on the basis of resource information (hashing etc ?) > > Should these be u64 instead of int then? you are right, we can use unsigned in instead. Will do that. > >> + */ >> +#define MEM_MIN                1024 >> +#define RES_MEM_HUGEPAGEALL        1025 >> +#define RES_MEM_HUGEPAGESIZE        1026 >> +#define RES_MEM_INACTIVE        1027 >> +#define RES_MEM_INFOALL            1028 >> +#define RES_MEM_AVAILABLE        1029 >> +#define RES_MEM_FREE            1030 >> +#define RES_MEM_TOTAL            1031 >> +#define RES_MEM_PAGESIZE        1032 >> +#define RES_MEM_SHMALL            1033 >> +#define RES_MEM_SHMINFOALL        1034 >> +#define RES_MEM_SHMMAX            1035 >> +#define RES_MEM_SHMMNI            1036 > > What is MNI? My bad. It should be MIN, implementation for this is not in this patch yet, I will remove such #defines which are not implemented. > >> +#define RES_MEM_SWAPFREE        1037 >> +#define RES_MEM_SWAPTOTAL        1038 >> +#define RES_MEM_ACTIVE            1039 >> +#define MEM_MAX                1040 >> + >> +#define NET_MIN                2048 >> +#define RES_NET_IFSTAT            2049 >> +#define RES_NET_ALLIFSTAT        2050 >> +#define NET_MAX                2051 >> + >> +#define KERN_MIN            3072 >> +#define RES_KERN_COMPILE_TIME        3073 >> +#define RES_KERN_RELEASE        3074 >> +#define KERN_MAX            3075 >> + >> +/* Structure to return RES_MEM_INFOALL resource information */ >> +typedef struct res_mem_infoall { >> +    size_t memfree; >> +    size_t memtotal; >> +    size_t memavailable; >> +    size_t active; >> +    size_t inactive; >> +    size_t swaptotal; >> +    size_t swapfree; >> +} res_mem_infoall_t; >> + >> +/* Structure to return RES_MEM_ALLIFSTAT resource information */ >> +typedef struct res_net_ifstat { >> +    char ifname[IFNAMSIZ]; >> +    unsigned long long rx_bytes; >> +    unsigned long long rx_packets; >> +    unsigned long rx_errors; >> +    unsigned long rx_dropped; >> +    unsigned long rx_fifo_err; >> +    unsigned long rx_frame_err; >> +    unsigned long rx_compressed; >> +    unsigned long rx_multicast; >> +    unsigned long long tx_bytes; >> +    unsigned long long tx_packets; >> +    unsigned long tx_errors; >> +    unsigned long tx_dropped; >> +    unsigned long tx_fifo_err; >> +    unsigned long tx_collisions; >> +    unsigned long tx_carrier_err; >> +    unsigned long tx_compressed; >> +} res_net_ifstat_t; > > Maybe this struct name should have the word all in it to be consistent > with struct res_mem_infoall? For interfaces ifstat provides stat for one interface, allifstat might suggest that it is stat for all interfaces. So I did not choose that name. > >> + >> +/* Allocating memory and building a res_blk structure to return bulk >> + * resource information. >> + */ >> +extern res_blk_t *res_build_blk(int *res_ids, int res_count); >> + >> +/* Reading bulk resource information. Memory must be properly >> allocated and >> + * all fields should be properly filled to return error free resource >> + * information. res_build_blk call is suggested to allocate build >> res_blk_t > > s/allocate build/allocate and build > >> + * structure. >> + */ >> +extern int res_read_blk(res_blk_t *resblk, int pid, int flags); >> + >> +/* Free allocated memory from res_build_blk */ >> +extern void res_destroy_blk(res_blk_t *resblk); >> + >> +/* Read a resource information. Memory for out should be properly >> allocated */ >> +extern int res_read(int res_id, void *out, void *hint, int pid, int >> flags); >> + >> +#endif /* RESOURCE_H */ >> -------------- next part -------------- An HTML attachment was scrubbed... URL: