#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define MAXN 128 #define MAXCPU 1024 #define error(msg) do { perror(msg); exit(1); } while(0) //--------------------------------Tree for call chain and report------------------------------- // struct TNode { int c=0; unordered_map s; struct TNode *add(string n) { c++; if (s[n]==nullptr) s[n] = new TNode(); return s[n]; } int printit(FILE *fp, int k) { if (s.size()) { using tt = tuple; vector xx; for (auto x: s) xx.push_back(make_tuple(x.second->c, x.first, x.second)); sort(begin(xx), end(xx), greater()); for (auto x: xx) { auto count = get<0>(x); if (100.0*count/c<1) continue; auto name = get<1>(x); auto nx = get<2>(x); fprintf(fp, "
  • \n"); fprintf(fp, "\n", k); fprintf(fp, "\n", k, name.c_str(), 100.0*count/c, count, c); fprintf(fp, "
      \n"); // printf("%s(%.3f%% %d/%d)\n", name.c_str(), 100.0*count/c, count, c); k = nx->printit(fp, k+1); fprintf(fp, "
    \n"); fprintf(fp, "
  • \n"); } } return k; } }; //--------------------------------symbols------------------------------------------- using STORE_T = map>; using K_STORE_T = map; /* * load FUNC symbols refering to the section indicated by the offset, relocate the virtual address */ void parse_elf64(FILE *fp, unsigned long long v_addr, unsigned long long v_size, unsigned long long v_offset, STORE_T& store) { // printf("read elf with offset 0x%llx, addr 0x%llx\n", v_offset, v_addr); Elf64_Ehdr ehdr; int rc = fread(&ehdr, sizeof(ehdr), 1, fp); if (rc != 1) return; int n, s, i; unsigned long long offset; // load program headers unsigned long long p_vaddr, p_size; n = ehdr.e_phnum; s = ehdr.e_phentsize; offset = ehdr.e_phoff; Elf64_Phdr phdr; for (i=0; i=n) { printf("No program header match offset found, fail to load\n"); return; } // load section headers n = ehdr.e_shnum; s = ehdr.e_shentsize; offset = ehdr.e_shoff; Elf64_Shdr shdr; vector headers; for (int i=0; ip_vaddr+p_size) continue; ix = symb.st_name; if (ix==0) continue; rc = fseek(fp, headers[link].sh_offset+ix, SEEK_SET); if (rc<0) continue; if (fgets(fname, sizeof(fname), fp)==NULL) continue; faddr = faddr-p_vaddr+v_addr; // printf("0x%llx +%lld > %s\n", faddr, fsize, fname); store[faddr] = make_pair(string(fname), fsize); } break; default: break; } } } int load_symbol_from_file(const char *path, unsigned long long addr, unsigned long long size, unsigned long long offset, STORE_T& store) { printf("loading symble from %s\n", path); FILE *fp = fopen(path, "rb"); if (fp==NULL) { perror("fail to open file"); return -1; } char ident[EI_NIDENT], c; int err=0; int rc = fread(ident, sizeof(ident), 1, fp); if (rc != 1) { perror("fail to read ident"); err=-1; goto end; } if (ident[0]!=0x7f) { printf("not a elf file\n"); err=-1; goto end; } c=ident[4]; rc = fseek(fp, 0, SEEK_SET); if (rc<0) { perror("fail to rewind"); goto end; } if (c == ELFCLASS32) { printf("32bit elf not supported yet\n"); err=-2; goto end; } else if (c == ELFCLASS64) { parse_elf64(fp, addr, size, offset, store); } else { printf("unknown elf type %d\n", c); } end: fclose(fp); return err; } static unsigned long long parse_hex(char *p, int *n) { unsigned long long r=0; int i=0; *n = 0; while(p[i]==' '||p[i]=='\t') i++; if (p[i]==0) return 0; if (p[i+1]=='x') i+=2; int v; while(p[i]) { if (p[i]>='0'&&p[i]<='9') v=p[i]-'0'; else if (p[i]>='a'&&p[i]<='f') v=10+p[i]-'a'; else if (p[i]>='A'&&p[i]<='F') v=10+p[i]-'A'; else break; r=(r<<4)+v; i++; } *n = i; return r; } STORE_T* load_symbol_pid(int pid, STORE_T* in) { printf("loading symbols for %d\n", pid); char bb[128]; sprintf(bb, "/proc/%d/maps", pid); FILE* fp = fopen(bb, "r"); if (fp==NULL) return NULL; STORE_T *store = in; unsigned long long start, end, offset=0; if (store==NULL) store=new STORE_T(); char *p; int i, c, j; while(1) { p=fgets(bb, sizeof(bb), fp); if (p==NULL) break; i=0; c=0; start = parse_hex(p, &c); if (start==0) continue; i+=c; if (p[i]!='-') continue; i++; end = parse_hex(p+i, &c); if (end==0) continue; i+=c; // parse type for (j=0; j<8; j++) { if (p[i]=='x') break; i++; } if (j>=8) continue; while(p[i]!=' '&&p[i]!='\t'&&p[i]!=0) i++; if (p[i]==0) continue; offset = parse_hex(p+i, &c); if (c==0) continue; // remaining should contains '/' indicating this mmap is refering to a file while(p[i]&&p[i]!='/') i++; if (p[i]==0) continue; sprintf(bb, "/proc/%d/map_files/%llx-%llx", pid, start, end); load_symbol_from_file(bb, start, end-start, offset, *store); } fclose(fp); return store; } /* parse kernel func symbols from /proc/kallsyms */ K_STORE_T* load_kernel() { FILE* fp = fopen("/proc/kallsyms", "r"); if (fp == NULL) return NULL; char *p; unsigned long long addr; int c; K_STORE_T* store = new K_STORE_T(); char bb[128], adr[128], type[8], name[128]; while(1) { p = fgets(bb, sizeof(bb), fp); if (p==NULL) break; if (sscanf(p, "%s %s %s", adr, type, name)!=3) continue;; if (type[0]!='t'&&type[0]!='T') continue; addr=parse_hex(adr, &c); if (c==0) continue; (*store)[addr] = string(name); } return store; fclose(fp); } //------------------------------perf profiler------------------------- static long perf_event_open(struct perf_event_attr *perf_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { return syscall(__NR_perf_event_open, perf_event, pid, cpu, group_fd, flags); } unordered_map pid_symbols; unordered_map> pid_infos; K_STORE_T* kernel_symbols = NULL; struct pollfd polls[MAXCPU]; // res for cleanup static long long psize; map> res; TNode* gnode = NULL; int exiting = 0; void int_exit(int x) { exiting = 1; for (auto x: res) { auto y = x.second; void* addr = y.first; munmap(addr, (1+MAXN)*psize); close(x.first); } res.clear(); if (gnode!=NULL) { FILE* fp = fopen("./report.html", "w"); if (fp) { fprintf(fp, " \n"); fprintf(fp, "
      \n"); gnode->printit(fp, 0); fprintf(fp, "
    \n"); fclose(fp); printf("report done\n"); } gnode = NULL; } exit(x); } /* perf call chain process For now, if a address would not be located to some function, the address would be skipped. */ int process_event(char *base, unsigned long long size, unsigned long long offset) { struct perf_event_header* p = NULL; int pid, xpid; unsigned long long time; offset%=size; // assuming the header would fit within size p = (struct perf_event_header*) (base+offset); offset+=sizeof(*p); if (offset>=size) offset-=size; if (p->type != PERF_RECORD_SAMPLE) return p->size; // pid, tip; pid = *((int *)(base+offset)); offset+=8; if (offset>=size) offset-=size; unsigned long long nr = *((unsigned long long*)(base+offset)); offset+=8; if (offset>=size) offset-=size; unsigned long long addr, o, addr0; if (nr) { if (gnode==NULL) gnode=new TNode(); char bb[64]; TNode* r = gnode; if (pid_symbols.count(pid)==0) { pid_symbols[pid] = load_symbol_pid(pid, NULL); // load command char b[128]; char comm[128]="NA"; char host[128]="NA"; sprintf(b, "/proc/%d/comm", pid); FILE* fp = fopen(b, "r"); if (fp) { fscanf(fp, "%s", comm); fclose(fp); } sprintf(b, "/proc/%d/root/etc/hostname", pid); fp = fopen(b, "r"); if (fp) { fscanf(fp, "%s", host); fclose(fp); } pid_infos[pid]=make_pair(string(comm), string(host)); } STORE_T* px = pid_symbols[pid]; addr0 = *((unsigned long long *)(base+offset)); char user_mark = 0; char need_reload= 0; auto pinfo = pid_infos[pid]; for (int i=nr-1; i>=0; i--) { o = i*8+offset; if (o>=size) o-=size; addr = *((unsigned long long*)(base+o)); if ((addr>>56)==(addr0>>56) && (p->misc&PERF_RECORD_MISC_KERNEL)) { // skip the cross line command, no idear how to correctly resolve it now. if (user_mark) { user_mark=0; continue; } // check in kernel if (kernel_symbols&&!kernel_symbols->empty()) { auto x = kernel_symbols->upper_bound(addr); if (x==kernel_symbols->begin()) { // sprintf(bb, "0x%llx", addr); r = r->add(string(bb)); } else { x--; r = r->add((*x).second); } } else { // sprintf(bb, "0x%llx", addr); r = r->add(string(bb)); } } else { if (px) { auto x = px->upper_bound(addr); if (x==px->begin()) { // sprintf(bb, "0x%llx", addr); r = r->add(string(bb)); r = r->add(string("unknwon[")+pinfo.first+'@'+pinfo.second+"]"); } else { x--; auto y = (*x).second; if (addr>(*x).first+y.second) { // r = r->add(y.first); // sprintf(bb, "0x%llx", addr); r = r->add(string(bb)); r = r->add(string("unknwon[")+pinfo.first+'@'+pinfo.second+"]"); } else { r = r->add(y.first+"["+pinfo.first+'@'+pinfo.second+"]"); } } } else { // sprintf(bb, "0x%llx", addr); r = r->add(string(bb)); } user_mark=1; } } } return p->size; } int main(int argc, char *argv[]) { kernel_symbols = load_kernel(); if (argc<2) { printf("Need kprobe function name, e.g. %s \n", argv[0]); return 1; } int type; char *func = argv[1]; FILE *fp = fopen("/sys/bus/event_source/devices/kprobe/type", "r"); if (fp == NULL) { printf("fail to find type for kprobe\n"); return 1; } type = 0; fscanf(fp, "%d", &type); fclose(fp); if (type <= 0) { printf("unexpected type %d\n", type); return 1; } // start perf event psize = sysconf(_SC_PAGE_SIZE); // getpagesize(); int cpu_num = sysconf(_SC_NPROCESSORS_ONLN); struct perf_event_attr attr; memset(&attr, 0, sizeof(attr)); attr.type = type; attr.size = sizeof(attr); attr.config = 0; // (1<<0) for kreprobe attr.sample_period = 2; attr.wakeup_events = 2; attr.sample_type = PERF_SAMPLE_TID|PERF_SAMPLE_CALLCHAIN; attr.kprobe_func = (__u64)func; // "do_sys_open"; // "bprm_execve"; attr.probe_offset = 0; int fd, i, k; void* addr; for (i=0, k=0; i0) { for (i=0; idata_head) continue; ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 1); head = mp->data_head-((mp->data_head-head)%mp->data_size); while(headdata_head) head+=process_event((char*)addr+mp->data_offset, mp->data_size, head); ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 0); res[fd].second = mp->data_head; } } int_exit(0); return 0; }