diff --git a/source/lib/uapi/Makefile b/source/lib/uapi/Makefile index b92c5bdc3c5dd5cc0577a892fc990fdfd2f4a455..d267d7a7b02dce65cdaa523a78b12f6af03124d3 100644 --- a/source/lib/uapi/Makefile +++ b/source/lib/uapi/Makefile @@ -1,6 +1,7 @@ SOURCE := $(shell find . -name "*.c") OBJS :=$(patsubst %.c,%.o,$(SOURCE)) STATIC_OBJS := $(addprefix $(OBJPATH)/,$(OBJS)) +LIBS += -L /usr/lib64 -l:libelf.a libsysak: $(OBJ_LIB_PATH)/libsysak.a @@ -10,6 +11,6 @@ $(OBJ_LIB_PATH)/libsysak.a: $(STATIC_OBJS) $(STATIC_OBJS): $(OBJS) $(OBJS): %.o : %.c - gcc -c -o $(OBJPATH)/$@ $< -I$(SRC)/lib/uapi/include + gcc -c -o $(OBJPATH)/$@ $< -I$(SRC)/lib/uapi/include $(LIBS) diff --git a/source/lib/uapi/include/kcore_utils.h b/source/lib/uapi/include/kcore_utils.h new file mode 100644 index 0000000000000000000000000000000000000000..a877ed9c9e7d3b5f347775c560ac53f68475cb2a --- /dev/null +++ b/source/lib/uapi/include/kcore_utils.h @@ -0,0 +1,70 @@ +#ifndef __KCORE_UTILS_H +#define __KCORE_UTLIS_H + +#include +#include +#include + +#define BUFF_MAX 4096 +#define MAX_KCORE_ELF_HEADER_SIZE 32768 + +#ifdef DEBUG +#define LOG_DEBUG(...) fprintf(stderr, __VA_ARGS__) +#else +#define LOG_DEBUG(...) do { } while (0) +#endif /* DEBUG */ + +#define LOG_INFO(...) fprintf(stdout, __VA_ARGS__) +#define LOG_WARN(...) fprintf(stderr, __VA_ARGS__) +#define LOG_ERROR(...) fprintf(stderr, __VA_ARGS__) + +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* struct to record the kcore elf file data*/ +struct proc_kcore_data { + unsigned int flags; + unsigned int segments; + char *elf_header; + size_t header_size; + Elf64_Phdr *load64; + Elf64_Phdr *notes64; + Elf32_Phdr *load32; + Elf32_Phdr *notes32; + void *vmcoreinfo; + unsigned int size_vmcoreinfo; +}; + + +/** + * lookup_kernel_symbol - look up kernel symbol address from /proc/kallsyms + * + * @symbol_name: kernel symbol name to look up. + * @return: the address of the kernel symbol. + * + */ +uintptr_t lookup_kernel_symbol(const char *symbol_name); + +/* prepare_btf_file - check exist btf file, if not exist, download it */ +char *prepare_btf_file(); + +/* open /proc/kcore and read necessary data to interpret kcore */ +int kcore_init(); + +/* close /proc/kcore and do some cleanup */ +void kcore_uninit(); + +/** + * kcore_readmem - read data of certain kernel address from kcore + * + * @kvaddr: kernel address to read. + * @buf: buf for readed data. + * @size: size of the data to read. + * @return: size of the data beeing read if success. + * + * Note: must call after kcore_init() + */ +ssize_t kcore_readmem(unsigned long kvaddr, void *buf, ssize_t size); + + +#endif \ No newline at end of file diff --git a/source/lib/uapi/kcore_utils.c b/source/lib/uapi/kcore_utils.c new file mode 100644 index 0000000000000000000000000000000000000000..e58b45c21151eae4508d706367b3036a5b02ed71 --- /dev/null +++ b/source/lib/uapi/kcore_utils.c @@ -0,0 +1,295 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcore_utils.h" + +#define LEN (128) + +static struct proc_kcore_data proc_kcore_data = { 0 }; +static struct proc_kcore_data *pkd = &proc_kcore_data; + +static int kcore_fd = 0; + +/* + * Routines of kcore, i.e., /proc/kcore + */ +uintptr_t lookup_kernel_symbol(const char *symbol_name) +{ + const char *kallsyms_file = "/proc/kallsyms"; + FILE *fp; + char line[BUFF_MAX]; + char *pos; + uintptr_t addr = -1UL; + + fp = fopen(kallsyms_file, "r"); + if (fp == NULL) { + perror("fopen: /proc/kallsyms"); + return -1; + } + + while (fgets(line, BUFF_MAX, fp)) { + if ((pos = strstr(line, symbol_name)) == NULL) + continue; + + /* Remove trailing newline */ + line[strcspn(line, "\n")] = '\0'; + + /* Exact match */ + if (pos == line || !isspace(*(pos - 1))) + continue; + if (!strcmp(pos, symbol_name)) { + addr = strtoul(line, NULL, 16); + break; + } + } + + if (addr == -1UL) + LOG_ERROR("failed to lookup symbol: %s\n", symbol_name); + + fclose(fp); + return addr; +} + +static int kcore_elf_init() +{ + Elf64_Ehdr *elf64; + Elf64_Phdr *load64; + Elf64_Phdr *notes64; + char eheader[MAX_KCORE_ELF_HEADER_SIZE]; + size_t load_size, notes_size; + + if (read(kcore_fd, eheader, MAX_KCORE_ELF_HEADER_SIZE) != + MAX_KCORE_ELF_HEADER_SIZE) { + perror("read: /proc/kcore ELF header"); + return -1; + } + + elf64 = (Elf64_Ehdr *)&eheader[0]; + notes64 = (Elf64_Phdr *)&eheader[sizeof(Elf64_Ehdr)]; + load64 = (Elf64_Phdr *)&eheader[sizeof(Elf64_Ehdr) + + sizeof(Elf64_Phdr)]; + + pkd->segments = elf64->e_phnum - 1; + + notes_size = load_size = 0; + if (notes64->p_type == PT_NOTE) + notes_size = notes64->p_offset + notes64->p_filesz; + if (notes64->p_type == PT_LOAD) + load_size = (unsigned long)(load64+(elf64->e_phnum)) - + (unsigned long)elf64; + + pkd->header_size = MAX(notes_size, load_size); + if (!pkd->header_size) + pkd->header_size = MAX_KCORE_ELF_HEADER_SIZE; + + if ((pkd->elf_header = (char *)malloc(pkd->header_size)) == NULL) { + perror("malloc: /proc/kcore ELF header"); + return -1; + } + + memcpy(&pkd->elf_header[0], &eheader[0], pkd->header_size); + pkd->notes64 = (Elf64_Phdr *)&pkd->elf_header[sizeof(Elf64_Ehdr)]; + pkd->load64 = (Elf64_Phdr *)&pkd->elf_header[sizeof(Elf64_Ehdr) + + sizeof(Elf64_Phdr)]; + + return 0; +} + +int kcore_init() +{ + if ((kcore_fd = open("/proc/kcore", O_RDONLY)) < 0) { + perror("open: /proc/kcore"); + return -1; + } + + if (kcore_elf_init()) + goto failed; + + return 0; + +failed: + close(kcore_fd); + return -1; +} + +void kcore_uninit(void) +{ + if (pkd->elf_header) + free(pkd->elf_header); + if (kcore_fd > 0) + close(kcore_fd); +} + +/* + * We may accidentally access invalid pfns on some kernels + * like 4.9, due to known bugs. Just skip it. + */ +ssize_t kcore_readmem(unsigned long kvaddr, void *buf, ssize_t size) +{ + Elf64_Phdr *lp64; + unsigned long offset = -1UL; + ssize_t read_size; + unsigned int i; + + for (i = 0; i < pkd->segments; i++) { + lp64 = pkd->load64 + i; + if ((kvaddr >= lp64->p_vaddr) && + (kvaddr < (lp64->p_vaddr + lp64->p_memsz))) { + offset = (off_t)(kvaddr - lp64->p_vaddr) + + (off_t)lp64->p_offset; + break; + } + } + if (i == pkd->segments) { + for (i = 0; i < pkd->segments; i++) { + lp64 = pkd->load64 + i; + LOG_DEBUG("%2d: [0x%lx, 0x%lx)\n", i, lp64->p_vaddr, + lp64->p_vaddr + lp64->p_memsz); + } + //printf("invalid kvaddr 0x%lx\n", kvaddr); + goto failed; + } + + if (lseek(kcore_fd, offset, SEEK_SET) < 0) { + perror("lseek: /proc/kcore"); + goto failed; + } + + read_size = read(kcore_fd, buf, size); + if (read_size < size) { + perror("read: /proc/kcore"); + goto failed; + } + + return read_size; + +failed: + return -1; +} + +static void stripWhiteSpace(char *str) +{ + char tmp_str[strlen(str)]; + int i, j = 0; + + for (i = 0; str[i] != '\0'; i++) { + if (str[i] != ' ' && str[i] != '\t' + && str[i] != '\n') { + tmp_str[j] = str[i]; + j++; + } + } + + tmp_str[j] = '\0'; + strcpy(str, tmp_str); + + return; +} + +static int do_cmd(const char *cmd, char *result, int len) +{ + FILE *res; + char region[LEN] = {0}; + char *str; + + res = popen(cmd, "r"); + if (res == NULL) { + printf("get region id failed\n"); + return -1; + } + + if (feof(res)) { + printf("cmd line end\n"); + return 0; + } + fgets(region, sizeof(region)-1, res); + str = region; + stripWhiteSpace(str); + /* skip \n */ + strncpy(result, str, len - 1); + result[len - 1] = '\0'; + pclose(res); + return 0; +} + +static int download_btf() +{ + char region[LEN] = {0}; + char arch[LEN] = {0}; + char kernel[LEN] = {0}; + char dw[LEN+LEN] = {0}; + char timeout[LEN] = "-internal"; + char sysak_path[LEN] = "/boot"; + char *curl_cmd = "curl -s --connect-timeout 2 http://100.100.100.200/latest/meta-data/region-id 2>&1"; + char *arch_cmd = "uname -m"; + char *kernel_cmd = "uname -r"; + char *tmp; + + do_cmd(curl_cmd, region, LEN); + if (!strstr(region,"cn-")) { + strcpy(region, "cn-hangzhou"); + memset(timeout, 0, sizeof(timeout)); + } + + do_cmd(arch_cmd, arch, LEN); + + do_cmd(kernel_cmd, kernel, LEN); + + if((tmp = getenv("SYSAK_WORK_PATH")) != NULL) + { + memset(sysak_path, 0, sizeof(sysak_path)); + strcpy(sysak_path, tmp); + strcat(sysak_path, "/tools/"); + strcat(sysak_path, kernel); + } + + snprintf(dw, LEN + LEN + LEN, "wget -T 5 -t 2 -q -O %s/vmlinux-%s https://sysom-cn-%s.oss-cn-%s%s.aliyuncs.com/home/hive/btf/%s/vmlinux-%s", sysak_path, kernel, ®ion[3],®ion[3], timeout,arch, kernel); + + do_cmd(dw, kernel, LEN); + return 0; +} + +static int check_btf_file(char *btf) +{ + struct stat fstat; + int ret = 0; + + ret = stat(btf, &fstat); + if (ret) + return -1; + if (fstat.st_size < 10*1024) + return -1; + + return 0; +} + +char *prepare_btf_file() +{ + static char btf[LEN] = {0}; + char ver[LEN] = {0}; + char *cmd = "uname -r"; + + do_cmd(cmd, ver, LEN); + + if (getenv("SYSAK_WORK_PATH") != NULL) + sprintf(btf,"%s/tools/%s/vmlinux-%s", getenv("SYSAK_WORK_PATH"), ver, ver); + else + sprintf(btf,"/boot/vmlinux-%s", ver); + + if (check_btf_file(btf)) { + download_btf(); + }; + + if (check_btf_file(btf)) { + LOG_ERROR("btf file:%s not found \n", btf); + return NULL; + } + + return btf; +} \ No newline at end of file diff --git a/source/tools/detect/mem/memcgoffline/Makefile b/source/tools/detect/mem/memcgoffline/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..de3835b51a9f458b13b6ae9d280a8d44b474eb3f --- /dev/null +++ b/source/tools/detect/mem/memcgoffline/Makefile @@ -0,0 +1,7 @@ +target := memcgoffline +LIBS += -L ${OBJ_LIB_PATH}/lib -l:libcoolbpf.a -l:libsysak.a -lelf -lz +INCLUDES += -I$(SRC)/tools/detect/mem/memcgoffline/include +LDFLAGS += -Wall $(LIBS) +mods := memcg_iter.o memcgoffline.o + +include $(SRC)/mk/csrc.mk \ No newline at end of file diff --git a/source/tools/detect/mem/memcgoffline/include/btfparse.h b/source/tools/detect/mem/memcgoffline/include/btfparse.h new file mode 100644 index 0000000000000000000000000000000000000000..84204f540b000444299df0947cec670c0d896889 --- /dev/null +++ b/source/tools/detect/mem/memcgoffline/include/btfparse.h @@ -0,0 +1,39 @@ + + +#ifndef __BTF_PARSE_H +#define __BTF_PARSE_H + + + +/** + * btf_load: load btf from btf_custom_path + * + * @btf_custom_path: path of btf file + */ +struct btf *btf_load(char *btf_custom_path); +typedef unsigned int uint32_t; + +struct member_attribute +{ + uint32_t size; // size of structure's member + uint32_t real_size; // real_size mean + uint32_t offset; // offset of member in strucutre +}; + +/** + * btf_find_struct_member - find struct btfid by structure's name + * + * @btf: + * @struct_name: name of struct + * @member_name: name of structure's member + * @return: NULL mean error, get error number from errno. + * + * Note: Remember to free pointer of struct member_attribute + */ +struct member_attribute *btf_find_struct_member(struct btf *btf, char *struct_name, char *member_name); + +int btf_get_member_offset(struct btf *btf, char *name, char *member_name); +void btf__free(struct btf *btf); + +#endif + diff --git a/source/tools/detect/mem/memcgoffline/include/memcg_iter.h b/source/tools/detect/mem/memcgoffline/include/memcg_iter.h new file mode 100644 index 0000000000000000000000000000000000000000..300a82b05d1281d70f154e6f407fe2388253acd6 --- /dev/null +++ b/source/tools/detect/mem/memcgoffline/include/memcg_iter.h @@ -0,0 +1,36 @@ +#ifndef __MEMCG_ITER_H_ +#define __MEMCG_ITER_H_ + +#include "btfparse.h" + +#define PATH_MAX (2048) +#define LEN (255) +#define CSS_DYING (1 << 4) /* css is dying */ + +/* iterator function of "for_each_mem_cgroup" */ +unsigned long _mem_cgroup_iter(unsigned long root, unsigned long prev, + struct btf* handle); + +/* find out and set root_mem_cgroup from kallsyms*/ +int memcg_iter_init(); + +/* Iter all memory cgroups, must call after memcg_iter_init() */ +#define for_each_mem_cgroup(iter, start, btf) \ + for (iter = _mem_cgroup_iter(start, (unsigned long)NULL, btf); \ + iter != (unsigned long)NULL; \ + iter = _mem_cgroup_iter(start, iter, btf)) + +/* + * get member offset of certain struct, need to read from btf file, + * (don't call it in loop which may cause huge overhead) + */ +struct member_attribute *get_offset_no_cache(char *struct_name, + char *member_name, struct btf *handle); + +int get_member_offset(char *struct_name, char *member_name, + struct btf *handle); + +void memcg_get_name(unsigned long memcg, char *name, + int len, struct btf *btf_handle); + +#endif \ No newline at end of file diff --git a/source/tools/detect/mem/memcgoffline/memcg_iter.c b/source/tools/detect/mem/memcgoffline/memcg_iter.c new file mode 100644 index 0000000000000000000000000000000000000000..541d0367ffd805d37336b440cc367a4f1f57d582 --- /dev/null +++ b/source/tools/detect/mem/memcgoffline/memcg_iter.c @@ -0,0 +1,291 @@ +#include +#include +#include +#include +#include +#include + +#include "memcg_iter.h" +#include "kcore_utils.h" + +static unsigned long root_mem_cgroup; + +struct member_attribute *get_offset_no_cache(char *struct_name, + char *member_name, struct btf *handle) +{ + struct member_attribute *att; + + att = btf_find_struct_member(handle, struct_name, member_name); + if (!att) { + return NULL; + } + + att->offset = att->offset/8; + + return att; +} + +int get_member_offset(char *struct_name, char *member_name, struct btf *handle) +{ + char prefix[LEN] = "struct "; + + strcat(prefix, struct_name); + + return btf_get_member_offset(handle, prefix, member_name)/8; +} + +static unsigned long _css_next_child(unsigned long pos, unsigned long parent, + struct btf *btf_handle) +{ + struct member_attribute *att, *att2; + unsigned long next; + + att = get_offset_no_cache("cgroup_subsys_state", "sibling", btf_handle); + if (!att) + return 0; + + att2 = get_offset_no_cache("cgroup_subsys_state", "children", btf_handle); + if (!att2) + return 0; + + if(!pos) { + kcore_readmem(parent + att2->offset, &next, sizeof(next)); + next = next - att->offset; + } else { + kcore_readmem(pos + att->offset, &next, sizeof(next)); + next = next - att->offset; + } + + if(next + att->offset != parent + att2->offset) + return next; + + return 0; +} + +unsigned long _mem_cgroup_iter(unsigned long root, unsigned long prev, + struct btf *btf_handle) +{ + struct member_attribute *att, *att2; + unsigned long css, root_css; + unsigned long memcg = 0; + unsigned long pos = 0; + unsigned long next = 0; + unsigned long tmp1, tmp2; + + if(!root) + root = root_mem_cgroup; + if(!prev) + return root; + + //printf("root:%lx, prev:%lx\n", root, prev); + + att = get_offset_no_cache("mem_cgroup", "css", btf_handle); + if (!att) + return 0; + + att2 = get_offset_no_cache("cgroup_subsys_state", "parent", btf_handle); + if (!att2) + return 0; + + pos = prev; + //kcore_readmem(pos + att->offset, &css, sizeof(css)); + css = pos + att->offset; + + //kcore_readmem(root+att->offset, &root_css, sizeof(root_css)); + root_css = root + att->offset; + next = _css_next_child(0, css, btf_handle); + if(!next) + { + tmp1 = css; + while(tmp1 != root_css) + { + kcore_readmem(tmp1 + att2->offset, &tmp2, sizeof(tmp2)); + next = _css_next_child(tmp1, tmp2, btf_handle); + if(next) + break; + tmp1 = tmp2; + } + } + + if(!next) + return 0; + + memcg = next - att->offset; + + return memcg; +} + +int memcg_iter_init() +{ + unsigned long tmp; + size_t size; + + tmp = lookup_kernel_symbol("root_mem_cgroup"); + if (tmp == (unsigned long )-1) { + LOG_ERROR("unable to get root_mem_cgroup\n"); + return -1; + } else { + size = kcore_readmem(tmp, &root_mem_cgroup, 8); + if (size < 8) { + LOG_ERROR("get incorrect address where root_mem_cgroup point to\n"); + return -1; + } + } + + return 0; +} + +static int prepend(char **buffer, int *buflen, const char *str, + int namelen, int off) +{ + *buflen -= namelen + off; + if (*buflen < 0) + return -1; + *buffer -= namelen + off; + if (off) + **buffer = '/'; + memcpy(*buffer + off, str, namelen); + return 0; +} + +static int cgroup_path(unsigned long cgrp, char *buf, + int buflen, struct btf *btf_handle) +{ + int ret = -1; + char *start; + unsigned long cgp; + char tmpname[PATH_MAX]; + struct member_attribute *cg_pa_att, *cg_name_att; + struct member_attribute *cgn_name_attr; + + cg_pa_att = get_offset_no_cache("cgroup", "parent", btf_handle); + if (!cg_pa_att) + return -1; + + cg_name_att = get_offset_no_cache("cgroup", "name", btf_handle); + if (!cg_name_att) + return -1; + + cgn_name_attr = get_offset_no_cache("cgroup_name", "name", btf_handle); + if (!cgn_name_attr) + return -1; + + + kcore_readmem(cgrp + cg_pa_att->offset, &cgp, sizeof(cgp)); + if (!cgp) { + if (strncpy(buf, "/", buflen) == NULL) + return -1; + return 0; + } + + start = buf + buflen - 1; + *start = '\0'; + + do { + int len; + unsigned long name; + + kcore_readmem(cgrp + cg_name_att->offset, &name, sizeof(name)); + + name += cgn_name_attr->offset; + kcore_readmem(name, tmpname,sizeof(tmpname)); + + len = strlen(tmpname); + if ((start -= len) < buf) + goto out; + + memcpy(start, tmpname, len); + + if (--start < buf) + goto out; + + *start = '/'; + cgrp = cgp; + + kcore_readmem(cgp + cg_pa_att->offset, &cgp, sizeof(cgp)); + + } while (cgp); + + ret = 0; + memmove(buf, start, buf + buflen - start); +out: + return ret; +} + +void memcg_get_name(unsigned long memcg, char *name, + int len, struct btf *btf_handle) +{ + char *end; + int pos; + unsigned long cg, knname; + char subname[257]; + struct member_attribute *att; + + memset(subname, 0, sizeof(subname)); + att = get_offset_no_cache("mem_cgroup", "css", btf_handle); + if (!att) + return; + + cg = memcg + att->offset; + + att = get_offset_no_cache("cgroup_subsys_state", "cgroup", btf_handle); + if (!att) + return; + + kcore_readmem(cg + att->offset, &cg, sizeof(cg)); + +#ifdef LINUX_310 + if (!cg) + return; + cgroup_path(cg, name, PATH_MAX); + end = name+strlen("/sys/fs/cgroup/memory/"); + memmove(end, name, strlen(name)+1); + prepend(&end, &len, "/sys/fs/cgroup/memory", strlen("/sys/fs/cgroup/memory"), 0); +#else + unsigned long kn; + unsigned long pkn; + int kn_name_offset, kn_pa_offset; + + att = get_offset_no_cache("cgroup", "kn", btf_handle); + if (!att) + return; + + kcore_readmem(cg + att->offset, &kn, sizeof(kn)); + + if (!cg || !kn) + return; + + end = name + len - 1; + prepend(&end, &len, "\0", 1, 0); + pkn = kn; + + kn_name_offset = get_member_offset("kernfs_node", "name", btf_handle); + if (kn_name_offset < 0) + return; + + kn_pa_offset = get_member_offset("kernfs_node", "parent", btf_handle); + if (kn_pa_offset < 0) + return; + + while (pkn) { + kcore_readmem(pkn + kn_name_offset, &knname, sizeof(knname)); + kcore_readmem(knname, subname, sizeof(subname)); + + pos = prepend(&end, &len, subname, strlen(subname), 0); + if (pos) + break; + + kcore_readmem(pkn + kn_pa_offset, &kn, sizeof(kn)); + if ((pkn == kn) || !kn) + break; + pos = prepend(&end, &len, "/", 1, 0); + if (pos) + break; + pkn = kn; + } + + prepend(&end, &len, "/sys/fs/cgroup/memory", strlen("/sys/fs/cgroup/memory"), 0); + + memmove(name, end, strlen(end) + 1); +#endif +} \ No newline at end of file diff --git a/source/tools/detect/mem/memcgoffline/memcgoffline.c b/source/tools/detect/mem/memcgoffline/memcgoffline.c new file mode 100644 index 0000000000000000000000000000000000000000..16fe17db6a9a1a07ba9605ade1e676fabb2f28fd --- /dev/null +++ b/source/tools/detect/mem/memcgoffline/memcgoffline.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kcore_utils.h" +#include "memcg_iter.h" + +static struct btf *btf_handle = NULL; +int total_memcg_num = 0; + +struct environment { + int print_cg_num; /* unused */ +} env = { + .print_cg_num = 10000, +}; + +static int caculate_offline(unsigned long start_memcg) +{ + int offline_num = 0; + unsigned long css, css_flags, cnt, iter = 0; + long refcnt_value; + unsigned int flags_value; + char fileName[PATH_MAX]; + struct member_attribute *css_attr, *css_flag_attr, *refcnt_attr; + struct member_attribute *cnt_attr; + + css_attr = get_offset_no_cache("mem_cgroup", "css", btf_handle); + if (!css_attr) { + LOG_ERROR("get css offset of mem_cgroup failed!\n"); + return -1; + } + + css_flag_attr = get_offset_no_cache("cgroup_subsys_state", + "flags", btf_handle); + if (!css_flag_attr) { + LOG_ERROR("get flags offset of cgroup_subsys_state failed!\n"); + return -1; + } + + refcnt_attr = get_offset_no_cache("cgroup_subsys_state", + "refcnt", btf_handle); + if (!refcnt_attr) { + LOG_ERROR("get refcnt offset of cgroup_subsys_state failed!\n"); + return -1; + } + + cnt_attr = get_offset_no_cache("percpu_ref", "count", btf_handle); + if (!cnt_attr) { + LOG_ERROR("get cnt offset of percpu_ref failed!\n"); + return -1; + } + + for_each_mem_cgroup(iter, start_memcg, btf_handle) { + css = iter + css_attr->offset; + css_flags = css + css_flag_attr->offset; + + kcore_readmem(css_flags, &flags_value, sizeof(flags_value)); + + if (flags_value & CSS_DYING) { + cnt = css + refcnt_attr->offset + cnt_attr->offset; + + offline_num++; + kcore_readmem(cnt, &refcnt_value, sizeof(refcnt_value)); + + if (env.print_cg_num > 0) { + memcg_get_name(iter, fileName, PATH_MAX, btf_handle); + printf("cgroup path:%s\trefcount=%ld\n", fileName, refcnt_value); + env.print_cg_num--; + } + } + total_memcg_num++; + } + + return offline_num; +} + +static void show_usage(char *prog) +{ + const char *str = + " Usage: %s [OPTIONS]\n" + " Options:\n" + " -n PRINT_MAX_CG_NUM Max offline memcg paths to printf(default 10000)\n" + " -h HELP help\n" + " \n" + + " EXAMPLE:\n " + " memcgoffline # display number of offline memcg and all their paths.\n" + " memcgoffline -n 10 # display number of offline memcg and " + "10 of offline memcg paths.\n" + ; + + fprintf(stderr, str, prog); + exit(EXIT_FAILURE); +} + +static int parse_args(int argc, char **argv, struct environment *env) +{ + int c, option_index; + char *prog_name = "memcgoffline"; + + for (;;) { + c = getopt_long(argc, argv, "n:h", NULL, &option_index); + if (c == -1) + break; + + switch (c) { + case 'n': + env->print_cg_num = (int)strtol(optarg, NULL, 10); + if (!errno) + return -errno; + break; + case 'h': + show_usage(prog_name); /* would exit */ + break; + default: + show_usage(prog_name); + } + } + + return 0; +} + +struct btf *btf_init() +{ + char *btf_path; + + btf_path = prepare_btf_file(); + if (!btf_path) + return NULL; + + return btf_load(btf_path); +} + +void btf_uninit(struct btf *btf) +{ + return btf__free(btf); +} + +int main(int argc, char *argp[]) +{ + int offline_memcg = 0, ret = 0; + + ret = parse_args(argc, argp, &env); + if (ret) { + LOG_ERROR("parse arg error!\n"); + return -1; + } + + btf_handle = btf_init(); + if (!btf_handle) { + LOG_ERROR("btf init failed!\n"); + return -1; + } + + ret = kcore_init(); + if (ret) { + LOG_ERROR("kcore init failed!\n"); + goto uninit_btf; + } + + ret = memcg_iter_init(); + if (ret) { + LOG_ERROR("memcg_iter_init failed!\n"); + goto uninit_kcore; + } + + offline_memcg = caculate_offline((unsigned long)NULL); + if (offline_memcg < 0) { + LOG_ERROR("caculate offline memcg failed!\n"); + ret = offline_memcg; + goto uninit_kcore; + } + printf("Offline memory cgroup num: %d\n", offline_memcg); + printf("Total memory cgroup num: %d\n", total_memcg_num); + +uninit_kcore: + kcore_uninit(); +uninit_btf: + btf_uninit(btf_handle); + + return ret; +}