#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define MAXN 1024 #define MAXCPU 1024 #define error(msg) do { perror(msg); exit(1); } while(0) //--------------------------------symbols------------------------------------------- typedef struct { unsigned long long addr, size, baddr, boffset; } FNode; using STORE_T = unordered_map; /* * load FUNC symbols refering to the section indicated by the offset, relocate the virtual address */ void parse_elf64(FILE *fp, STORE_T& store) { // printf("read elf with offset 0x%llx, addr 0x%llx\n", v_offset, v_addr); Elf64_Ehdr ehdr; FNode func; int rc = fread(&ehdr, sizeof(ehdr), 1, fp); if (rc != 1) return; int n, s, i; unsigned long long offset; // load program headers n = ehdr.e_phnum; s = ehdr.e_phentsize; offset = ehdr.e_phoff; Elf64_Phdr phdr; for (i=0; i headers; for (int i=0; i> res; void int_exit(int _) { for (auto x: res) { auto y = x.second; void* addr = y.first; munmap(addr, (1+MAXN)*psize); close(x.first); } exit(0); } /* 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 rax, arg1, arg2, arg3, arg4; 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 abi = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; if (abi == PERF_SAMPLE_REGS_ABI_64) { rax = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rax arg3 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdx arg2 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rsi arg1 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdi arg4 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //r10 printf("uprobe event: rax[0x%llx], f(%lld,...)\n", rax, arg1); } return p->size; } struct RNode { string func_name, file_name; unsigned long long vaddr, raddr; }; int main(int argc, char *argv[]) { char bb[256], *p, *func; if (argc<3) { printf("usage: %s \n", argv[0]); return 1; } int pid = atoi(argv[1]); func = argv[2]; if (pid<=0) { printf("invalid pid %d\n", pid); return 1; } sprintf(bb, "/proc/%d/maps", pid); FILE *fp = fopen(bb, "r"); if (fp==NULL) { printf("Fail to read proc for pid %d\n", pid); return 1; } unsigned long long start, end, offset, inode; char fname[128], mod[16], idx[32]; char ff[256]; char xx[64], xxx[32]; int i, valid, k; int type = 0; vector cans; RNode rr; while(1) { p = fgets(bb, sizeof(bb), fp); if (p==NULL) break; if (sscanf(p, "%s %s %s %s %lld %s", xx, mod, xxx, idx, &inode, fname)!=6) continue; for (i=0; i<4; i++) if (mod[i]=='x') break; if (i>=4) continue; if (fname[0]!='/') continue; valid=1; start=0; for (i=0; xx[i]&&xx[i]!='-'; i++) { if (xx[i]>='0'&&xx[i]<='9') start=start*16+xx[i]-'0'; else if (xx[i]>='A'&&xx[i]<='F') start=start*16+xx[i]-'A'+10; else if (xx[i]>='a'&&xx[i]<='f') start=start*16+xx[i]-'a'+10; else { valid=0; break; } } if (valid==0||start==0) continue; end=0; for (i++; xx[i]; i++) { if (xx[i]>='0'&&xx[i]<='9') end=end*16+xx[i]-'0'; else if (xx[i]>='A'&&xx[i]<='F') end=end*16+xx[i]-'A'+10; else if (xx[i]>='a'&&xx[i]<='f') end=end*16+xx[i]-'a'+10; else { valid=0; break; } } if (valid==0||start==0) continue; offset=0; for (i=0; xxx[i]; i++) { if (xxx[i]>='0'&&xxx[i]<='9') offset=offset*16+xxx[i]-'0'; else if (xxx[i]>='A'&&xxx[i]<='F') offset=offset*16+xxx[i]-'A'+10; else if (xxx[i]>='a'&&xxx[i]<='f') offset=offset*16+xxx[i]-'a'+10; else { valid=0; break; } } if (valid==0) break; sprintf(ff, "/proc/%d/root%s", pid, fname); STORE_T* s = load_symbol_from_file(ff); if (s) { for (auto x=s->begin(); x!=s->end(); x++) { auto v = (*x).second; if (strstr((*x).first.c_str(), func)) { rr.func_name = (*x).first; rr.file_name = string(ff); rr.vaddr = v.addr-v.baddr+start; rr.raddr = v.addr-v.baddr+v.boffset; cans.push_back(rr); } } delete s; } } fclose(fp); if (cans.size()==0) { printf("no func found\n"); return 0; } k=0; if (cans.size()>1) { printf("%d candidates found, please make a selection: [0, %d]\n", cans.size(), cans.size()-1); for (i=0; i\n", i, cans[i].func_name.c_str(), cans[i].file_name.c_str()); } scanf("%d", &k); if (k<0||k>=cans.size()) { printf("invalid selection, abort\n"); return 1; } } printf("uprobing %s[%s] (vaddr 0x%llx, relative addr 0x%llx)\n", cans[k].func_name.c_str(), cans[k].file_name.c_str(), cans[k].vaddr, cans[k].raddr); fp = fopen("/sys/bus/event_source/devices/uprobe/type", "r"); if (fp == NULL) { printf("fail to find type for kprobe\n"); return 1; } 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), fd; struct perf_event_attr attr; void *addr; memset(&attr, 0, sizeof(attr)); attr.type = type; attr.size = sizeof(attr); attr.config = 0; attr.sample_period = 1; attr.wakeup_events = 1; attr.sample_type = PERF_SAMPLE_TID|PERF_SAMPLE_REGS_USER; //PERF_SAMPLE_REGS_INTR; attr.uprobe_path = (__u64)cans[k].file_name.c_str(); attr.probe_offset = cans[k].raddr; attr.sample_regs_user = (1<0) { // printf("wake\n"); 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; }