diff --git a/source/tools/detect/memleak/Makefile b/source/tools/detect/memleak/Makefile index 80731fd72878ae8ce021b8592377536c438b60fc..a76e3460f6aec0f1cc76db9afc36cb0bdbd81a0e 100644 --- a/source/tools/detect/memleak/Makefile +++ b/source/tools/detect/memleak/Makefile @@ -1,7 +1,7 @@ target := memleak -mods := main.o slab.o page.o vmalloc.o +mods := main.o slab.o page.o vmalloc.o check.o LDFLAGS += -lpthread DEPEND := "prev{default modin};post{default modun}" diff --git a/source/tools/detect/memleak/check.c b/source/tools/detect/memleak/check.c new file mode 100644 index 0000000000000000000000000000000000000000..b5e161853fe95a6ff6b2dff722e2b9cd275a28ca --- /dev/null +++ b/source/tools/detect/memleak/check.c @@ -0,0 +1,112 @@ +#include +#include +#include + +#include "user_api.h" + +#define MEMINFO "/proc/meminfo" +#define LEN_128 (128) +#define LEN_4096 (4096) + + +#define hugepath "/sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages" +static int read_hugepage1G(void) +{ + FILE *fp; + char line[LEN_128]; + unsigned long nr =0; + + fp = fopen(hugepath, "r"); + if (fp == NULL) + return 0; + + if (fgets(line, 128,fp) == NULL) { + fclose(fp); + return 0; + } + + sscanf(line, "%lu", &nr); + fclose(fp); + return nr; +} + + +int read_meminfo(struct meminfo *mem) +{ + FILE *fp; + char line[LEN_128]; + char buf[LEN_4096]; + struct meminfo st_mem; + unsigned long anonPage = 0; + unsigned long hugeSize = 0; + unsigned long hugeTotal = 0; + + memset(buf, 0, LEN_4096); + memset(&st_mem, 0, sizeof(st_mem)); + if ((fp = fopen(MEMINFO, "r")) == NULL) { + return 0; + } + + while (fgets(line, 128, fp) != NULL) { + + if (!strncmp(line, "MemTotal:", 9)) { + /* Read the total amount of memory in kB */ + sscanf(line + 9, "%lu", &st_mem.tlmkb); + } + else if (!strncmp(line, "MemFree:", 8)) { + /* Read the amount of free memory in kB */ + sscanf(line + 8, "%lu", &st_mem.frmkb); + } + else if (!strncmp(line, "Buffers:", 8)) { + /* Read the amount of buffered memory in kB */ + sscanf(line + 8, "%lu", &st_mem.bufkb); + } + else if (!strncmp(line, "SUnreclaim:", 11)) { + /* Read the amount of slab memory in kB */ + sscanf(line + 11, "%lu", &st_mem.uslabkb); + } + else if (!strncmp(line, "Active:", 7)) { + /* Read the amount of Active memory in kB */ + sscanf(line + 7, "%lu", &st_mem.acmkb); + } + else if (!strncmp(line, "Inactive:", 9)) { + /* Read the amount of Inactive memory in kB */ + sscanf(line + 9, "%lu", &st_mem.iamkb); + } + else if (!strncmp(line, "Slab:", 5)) { + /* Read the amount of Slab memory in kB */ + sscanf(line + 5, "%lu", &st_mem.slmkb); + } + else if (!strncmp(line, "HugePages_Total:", 16)) { + /* Read the amount of commited memory in kB */ + sscanf(line + 16, "%lu", &hugeTotal); + } + else if (!strncmp(line, "Hugepagesize:", 13)) { + /* Read the amount of commited memory in kB */ + sscanf(line + 13, "%lu", &hugeSize); + } + else if (!strncmp(line, "Mlocked:", 8)) { + /* Read the amount of commited memory in kB */ + sscanf(line + 8, "%lu", &st_mem.lock); + } + } + st_mem.huge1G = read_hugepage1G()*1024*1024; + if (hugeSize == 2048) + st_mem.huge2M = hugeTotal * hugeSize; + + st_mem.kernel = st_mem.tlmkb - st_mem.frmkb - st_mem.acmkb - st_mem.iamkb - st_mem.lock -\ + st_mem.bufkb - st_mem.slmkb - st_mem.huge2M - st_mem.huge1G; + + if (st_mem.kernel < 0) + st_mem.kernel = (1 << 10); + *mem = st_mem; + return 0; +} + +int test_main(void) +{ + struct meminfo mem; + read_meminfo(&mem); + printf(" kernel used %dM\n", mem.kernel/1024); + return 0; +} diff --git a/source/tools/detect/memleak/main.c b/source/tools/detect/memleak/main.c index f88c7d42386be41eaadf9f21376e6522625bc5bc..9be3a939b9e0b3bae6313de29291afc4961284e7 100644 --- a/source/tools/detect/memleak/main.c +++ b/source/tools/detect/memleak/main.c @@ -14,11 +14,13 @@ #include "memleak.h" #include "user_api.h" +extern int read_meminfo(struct meminfo *mem); extern int slab_main(struct memleak_settings *set); extern int vmalloc_main(int argc, char **argv); extern int page_main(struct memleak_settings *set); static int error = 0; static int off = 0; +static struct meminfo mem; static void show_usage(void) { @@ -29,15 +31,41 @@ static void show_usage(void) printf("-i: trace internal,default 300s \n"); printf("-s: stacktrace for memory alloc \n"); printf("-d: memleak off \n"); + printf("-c: only check memleak,don't diagnose \n"); printf("-n: trace slab name, defualt select the max size or objects \n"); } +static int memleak_check_only(struct meminfo *mi) +{ + int ret = 0; + int vmalloc = 0; + + read_meminfo(mi); + vmalloc = vmalloc_main(0, NULL); + printf("allocPages:%dM, uslab:%dM vmalloc:%dM\n", (mi->kernel)/1024, mi->uslabkb/1024, vmalloc/1024); + if (mi->kernel < vmalloc) + mi->kernel = vmalloc + 1; + + if ((mi->kernel - vmalloc) > 1024*1024*1.5) { + printf("alloc page memleak\n"); + return MEMLEAK_TYPE_PAGE; + } else if (mi->uslabkb > 5*1024*1024 || + mi->uslabkb > mi->tlmkb*0.15) { + printf("slab memleak\n"); + return MEMLEAK_TYPE_SLAB; + } else if (vmalloc > 2*1024 * 1024) { + printf("vmalloc memleak\n"); + return MEMLEAK_TYPE_VMALLOC; + } + return 0; +} + int get_arg(struct memleak_settings *set, int argc, char * argv[]) { int ch; - while ((ch = getopt(argc, argv, "dshi:r:n:t:")) != -1) + while ((ch = getopt(argc, argv, "dshci:r:n:t:")) != -1) { switch (ch) { @@ -54,6 +82,10 @@ int get_arg(struct memleak_settings *set, int argc, char * argv[]) break; case 'r': set->rate = atoi(optarg); + break; + case 'c': + memleak_check_only(&mem); + error = 1; break; case 'n': strncpy(set->name, optarg, NAME_LEN - 1); @@ -91,6 +123,8 @@ static int memleak_off(void) return 0; } + + int main(int argc, char **argv) { struct memleak_settings set; diff --git a/source/tools/detect/memleak/user_api.h b/source/tools/detect/memleak/user_api.h index b7ab77fb0111a8cf50ec77bde64a91fbc16f0cbe..c2ce97a77e45dd2fa370eec0ca4595936de2bbcc 100644 --- a/source/tools/detect/memleak/user_api.h +++ b/source/tools/detect/memleak/user_api.h @@ -6,6 +6,26 @@ #include "common.h" +struct meminfo { + unsigned long frmkb; + unsigned long bufkb; + unsigned long camkb; + unsigned long tlmkb; + unsigned long acmkb; + unsigned long iamkb; + unsigned long slmkb; + unsigned long frskb; + unsigned long tlskb; + unsigned long caskb; + unsigned long comkb; + unsigned long uslabkb; + unsigned long slabkb; + unsigned long kernel; + unsigned long huge2M; + unsigned long huge1G; + unsigned long lock; +}; + typedef enum _memleak_type { MEMLEAK_TYPE_SLAB = 1, MEMLEAK_TYPE_PAGE, diff --git a/source/tools/detect/memleak/vmalloc.c b/source/tools/detect/memleak/vmalloc.c index 8067c944c1f4ee3fd7a901047106e47bf2239dd4..1469f572cc01c79cce43d19c0f3282b9efff5601 100644 --- a/source/tools/detect/memleak/vmalloc.c +++ b/source/tools/detect/memleak/vmalloc.c @@ -75,6 +75,7 @@ int vmalloc_main(int argc, char **argv) int size = 0; int ret = 0; + unsigned int total = 0; fd = fopen("/proc/vmallocinfo", "r"); if (!fd) { @@ -91,7 +92,8 @@ int vmalloc_main(int argc, char **argv) if (!fgets(lines, sizeof(lines), fd)) break; - + if (!strstr(lines,"vmalloc")) + continue; if (sscanf(lines, "%s %d %s", addrs, &size, funcs) != 3) continue; @@ -103,14 +105,18 @@ int vmalloc_main(int argc, char **argv) qsort(call, call_index, sizeof(struct call_site), cmp); - printf("VMALLOC 未释放函数汇总:\n"); - printf("次数 总大小 函数\n"); + if (argc) { + printf("VMALLOC 未释放函数汇总:\n"); + printf("次数 总大小 函数\n"); + } for (ret = 0; ret < call_index; ret++) { - if (call[ret].size >> 20) + total += (call[ret].size >> 10); + if (argc && call[ret].size >> 20) printf("%d %2dMB %s \n", call[ret].nr, call[ret].size >> 20, call[ret].funcs); } fclose(fd); + return total; } diff --git a/source/tools/detect/skcheck.py/Makefile b/source/tools/detect/skcheck.py/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..fa19a290b1515db138ce81fb819c37b008dc6af1 --- /dev/null +++ b/source/tools/detect/skcheck.py/Makefile @@ -0,0 +1,4 @@ +target := skcheck +mods := skcheck +include $(SRC)/mk/py.mk + diff --git a/source/tools/detect/skcheck.py/skcheck.py b/source/tools/detect/skcheck.py/skcheck.py new file mode 100644 index 0000000000000000000000000000000000000000..ebc636e6ad55ac713abbf48b91ae8fef8f6ffa20 --- /dev/null +++ b/source/tools/detect/skcheck.py/skcheck.py @@ -0,0 +1,279 @@ +#!/bin/python + +import os +import sys +import getopt + +memThres = 102400 +socketCheck = False +socketThres = 2000 +socketLeak = 500 + +def os_cmd(cmd): + ret = os.popen(cmd).read().split("\n") + return ret + +def get_tcp_mem(): + ret = os_cmd(" cat /proc/net/sockstat") + for line in ret: + if line.find("TCP:") == -1: + continue + tcp_mem = line.strip().split(" ") + return int(tcp_mem[-1])*4 + +def get_local_ip(line): + if line.find(".") == -1: + return "unknow" + ip = line.split(" ") + for tmp in ip: + if tmp.find(".") != -1: + return tmp.strip() + return "unkonw" + +def get_task(line): + if line.find("users") == -1: + return get_local_ip(line) + start = line.find("(") + if start == -1: + return "unknow" + end = line.find(")") + if end == -1: + return "unknow" + task = line[start+3:end].strip() + if len(task) < 2: + return "unknow" + task = task.split(",") + comm = task[0][:-1] + pid = task[1].split("=")[1] + return comm + ":" + pid + +def tcp_mem_check(): + ret = os_cmd("ss -tnapm") + tcp_mem = get_tcp_mem() + memTask = {} + tx_mem = 0 + rx_mem = 0 + idx = 0 + for idx in range(len(ret)): + line = ret[idx] + #print("line start") + #print(line) + #print("line end") + if line.find("skmem") == -1: + continue + prev_line = ret[idx -1] + task = get_task(prev_line) + skmem = line.strip().split("(")[1] + skmem = skmem[:-1].split(",") + rx = int(skmem[0][1:]) + tx = int(skmem[2][1:]) + rx_mem += rx + tx_mem += tx + if rx + tx < 1024: + continue + if task not in memTask.keys(): + memTask[task] = 0 + memTask[task] += (rx + tx) + + total = (rx_mem + tx_mem)/1024 + print("tx_queue {}K rx_queue {}K queue_total {}K tcp_mem {}K".format(tx_mem/1024, rx_mem/1024, total, tcp_mem)) + if tcp_mem > memThres and tcp_mem > total*1.5: + print("tcp memleak tcp_mem:{}K tx_rx queue:{}K".format(tcp_mem, total)) + print("\n") + print("task hold memory:") + for task, value in memTask.items(): + print("task {} mem {}K".format(task, value/1024)) + print("\n") + return total,tcp_mem + +def _socket_inode_x(inodes,protocol,idx): + cmd = "cat /proc/net/" + protocol + " " + ret = os_cmd(cmd) + skip = 0 + + for line in ret: + tmp = idx + if skip == 0: + skip = 1 + continue + line = line.strip() + inode = line.split(" ") + if len(inode) < abs(idx) + 1: + continue + #print("line : {}".format(line)) + #print("list : {}".format(inode)) + + """ fix idx for unix socket """ + if (idx == -2) and (line.find("/") == -1): + tmp = -1 + + #print("inode = {} idx {} ".format(inode[tmp], idx)) + if inode[tmp]: + inodes.append(inode[tmp]) + #print("\n") + return inodes + +def socket_inode_1(inodes): + _socket_inode_x(inodes, "netlink", -1) + _socket_inode_x(inodes, "packet", -1) + +def socket_inode_2(inodes): + return _socket_inode_x(inodes, "unix", -2) + +def socket_inode_4(inodes): + _socket_inode_x(inodes, "udp", -4) + _socket_inode_x(inodes, "udp6", -4) + _socket_inode_x(inodes, "udplite", -4) + _socket_inode_x(inodes, "udplite6", -4) + _socket_inode_x(inodes, "raw", -4) + _socket_inode_x(inodes, "raw6", -4) + +def socket_inode_8(inodes): + _socket_inode_x(inodes, "tcp", -8) + _socket_inode_x(inodes, "tcp6", -8) + +def socket_inode_get(inodes): + socket_inode_1(inodes) + socket_inode_2(inodes) + socket_inode_4(inodes) + socket_inode_8(inodes) + + +def is_number(s): + try: + float(s) + return True + except ValueError: + pass + try: + import unicodedata + unicodedata.numeric(s) + return True + except (TypeError, ValueError): + pass + return False + +def get_comm(proc): + cmd = "cat " +proc+"/comm" + ret = os.popen(cmd).read().strip() + return ret + +def scan_all_proc(inodes): + root = "/proc/" + allProcInode = [] + global socketThres + global socketLeak + try: + for proc in os.listdir(root): + if not os.path.exists(root + proc): + continue + if not is_number(proc): + continue + procName = root + proc + "/fd/" + taskInfo = {} + taskInfo["task"] = "" + taskInfo["inode"] = [] + inodeNum = 0 + inodeLeakNum = 0 + try: + for fd in os.listdir(procName): + inodeInfo = {} + if not os.path.exists(procName+fd): + continue + link = os.readlink(procName+fd) + if link.find("socket") == -1: + continue + inodeNum += 1 + inode = link.strip().split("[") + inode = inode[1][:-1].strip() + #print("fd {} link {} inode {}".format(procName+fd, link, inode)) + if inode not in inodes: + inodeInfo["fd"] = procName+fd + inodeInfo["link"] = link + inodeInfo["inode"] = inode + taskInfo["inode"].append(inodeInfo) + inodeLeakNum += 1 + if inodeNum >= socketThres or inodeLeakNum > socketLeak: + taskInfo["task"] = get_comm(root+proc) + taskInfo["pid"] = proc + taskInfo["num"] = inodeNum + taskInfo["numleak"] = inodeLeakNum + allProcInode.append(taskInfo) + except Exception: + import traceback + traceback.print_exc() + pass + except Exception : + import traceback + traceback.print_exc() + pass + #print("inode leak ={}".format(allProcInode)) + return allProcInode + +def socket_leak_check(): + inodes = [] + newLeak = [] + global socketCheck + + if socketCheck == False: + return newLeak + socket_inode_get(inodes) + taskLeak = scan_all_proc(inodes) + """ Try again""" + inodes = [] + socket_inode_get(inodes) + newLeak = [] + for taskInfo in taskLeak: + if taskInfo["num"] > socketThres: + newLeak.append(taskInfo) + continue + inodeNum = 0 + for inodeInfo in taskInfo["inode"]: + if not os.path.exists(inodeInfo["fd"]): + continue + link = os.readlink(inodeInfo["fd"]) + if link != inodeInfo["link"]: + continue + if inodeInfo["inode"] not in inodes: + inodeNum += 1 + if inodeNum > socketLeak: + newLeak.append(taskInfo) + return newLeak + +def get_args(argv): + global memThres + global socketCheck + global socketThres + global socketLeak + + try: + opts, args = getopt.getopt(argv,"hm:t:sl:") + except getopt.GetoptError: + print 'tcp memory and socket leak check' + sys.exit(2) + for opt, arg in opts: + if opt == '-h': + print("tcp memory and socket leak check") + print("default enable for tcp memmory check") + print("-s:enable socket leak check") + print("-t:threshold value for open socket ,default is 2000") + print("-l:leak threshold for shutdown socket ,default is 500") + sys.exit() + elif opt in ("-m"): + memThres = int(arg) * 1024 + elif opt in ("-s"): + socketCheck = True + elif opt in ("-t"): + socketThres = int(arg) + elif opt in ("-l"): + socketLeak = int(arg) + else: + print("error args options") + +if __name__ == "__main__": + inodes = [] + get_args(sys.argv[1:]) + tcp_mem_check() + leak = socket_leak_check() + for taskInfo in leak: + print("{}:{} socketNum {} socketLeakNum {}".format(taskInfo["task"], taskInfo["pid"], taskInfo["num"], taskInfo["numleak"]))