1 #include <stdlib.h> 2 #include <stdio.h> 3 #include <unistd.h> 4 #include <string.h> 5 #include <sys/ioctl.h> 6 #include <linux/perf_event.h> 7 #include <asm/unistd.h> 8 #include <sys/mman.h> 9 #include <sys/stat.h> 10 #include <poll.h> 11 #include <signal.h> 12 #include <fcntl.h> 13 #include <elf.h> 14 #include <string.h> 15 #include <asm/perf_regs.h> 16 17 #include <vector> 18 #include <string> 19 #include <map> 20 #include <unordered_map> 21 #include <algorithm> 22 using namespace std; 23 24 25 #define MAXN 1024 26 #define MAXCPU 1024 27 #define error(msg) do { perror(msg); exit(1); } while(0) 28 //--------------------------------symbols------------------------------------------- 29 typedef struct { unsigned long long addr, size, baddr, boffset; } FNode; 30 using STORE_T = unordered_map<string, FNode>; 31 /* 32 * load FUNC symbols refering to the section indicated by the offset, relocate the virtual address 33 */ 34 void parse_elf64(FILE *fp, STORE_T& store) { 35 // printf("read elf with offset 0x%llx, addr 0x%llx\n", v_offset, v_addr); 36 Elf64_Ehdr ehdr; 37 FNode func; 38 int rc = fread(&ehdr, sizeof(ehdr), 1, fp); 39 if (rc != 1) return; 40 int n, s, i; 41 unsigned long long offset; 42 43 // load program headers 44 n = ehdr.e_phnum; 45 s = ehdr.e_phentsize; 46 offset = ehdr.e_phoff; 47 Elf64_Phdr phdr; 48 for (i=0; i<n; i++) { 49 rc = fseek(fp, offset, SEEK_SET); 50 if (rc<0) { perror("fail to seek"); return; } 51 rc = fread(&phdr, sizeof(phdr), 1, fp); 52 if (rc != 1) { perror("fail to read program header"); return; } 53 if (phdr.p_flags&PF_X) { 54 func.baddr=phdr.p_vaddr; 55 func.boffset=phdr.p_offset; 56 } 57 offset+=s; 58 } 59 // load section headers 60 n = ehdr.e_shnum; 61 s = ehdr.e_shentsize; 62 offset = ehdr.e_shoff; 63 Elf64_Shdr shdr; 64 vector<Elf64_Shdr> headers; 65 for (int i=0; i<n; i++) { 66 rc = fseek(fp, offset, SEEK_SET); 67 if (rc<0) { perror("fail to seek"); return; } 68 rc = fread(&shdr, sizeof(shdr), 1, fp); 69 if (rc != 1) { perror("fail to read sec header"); return; } 70 headers.push_back(shdr); 71 offset+=s; 72 } 73 Elf64_Sym symb; 74 // TODO: remove symbols which need relocation 75 Elf64_Rel rel; 76 Elf64_Rela rela; 77 unsigned long long faddr, fsize; 78 unsigned long long size, item_size; 79 int link, ix, flink, k; 80 char fname[128]; 81 for (int i=0; i<n; i++) { 82 offset = headers[i].sh_offset; 83 size = headers[i].sh_size; 84 item_size = headers[i].sh_entsize; 85 link = headers[i].sh_link; 86 switch(headers[i].sh_type) { 87 case SHT_DYNSYM: 88 case SHT_SYMTAB: 89 if (link<=0) break; 90 for (k=0; k+item_size<=size; k+=item_size) { 91 rc = fseek(fp, offset+k, SEEK_SET); if (rc<0) continue; 92 rc = fread(&symb, sizeof(symb), 1, fp); if (rc != 1) continue; 93 if (ELF64_ST_TYPE(symb.st_info) != STT_FUNC ) continue; 94 flink = symb.st_shndx; if (flink==0) continue; 95 fsize = symb.st_size; 96 faddr = symb.st_value; 97 ix = symb.st_name; if (ix==0) continue; 98 rc = fseek(fp, headers[link].sh_offset+ix, SEEK_SET); if (rc<0) continue; 99 if (fgets(fname, sizeof(fname), fp)==NULL) continue; 100 func.addr=faddr; func.size=fsize; 101 store[string(fname)] = func; 102 } 103 break; 104 default: 105 break; 106 } 107 } 108 } 109 110 STORE_T* load_symbol_from_file(const char *path) { 111 // printf("loading symble from %s\n", path); 112 STORE_T *store = NULL; 113 FILE *fp = fopen(path, "rb"); 114 if (fp==NULL) { perror("fail to open file"); return NULL; } 115 char ident[EI_NIDENT], c; 116 int err=0; 117 int rc = fread(ident, sizeof(ident), 1, fp); 118 if (rc != 1) { perror("fail to read ident"); err=-1; goto end; } 119 if (ident[0]!=0x7f) { printf("not a elf file\n"); err=-1; goto end; } 120 c=ident[4]; 121 rc = fseek(fp, 0, SEEK_SET); if (rc<0) { perror("fail to rewind"); goto end; } 122 if (c == ELFCLASS32) { 123 printf("32bit elf not supported yet\n"); err=-2; goto end; 124 } else if (c == ELFCLASS64) { 125 store = new STORE_T(); 126 parse_elf64(fp, *store); 127 } 128 129 end: 130 fclose(fp); 131 return store; 132 } 133 134 135 //------------------------------perf profiler------------------------- 136 static long perf_event_open(struct perf_event_attr *perf_event, pid_t pid, int cpu, int group_fd, unsigned long flags) { 137 return syscall(__NR_perf_event_open, perf_event, pid, cpu, group_fd, flags); 138 } 139 140 struct pollfd polls[MAXCPU]; 141 // res for cleanup 142 static long long psize; 143 map<int, pair<void*, long long>> res; 144 145 void int_exit(int _) { 146 for (auto x: res) { 147 auto y = x.second; 148 void* addr = y.first; 149 munmap(addr, (1+MAXN)*psize); 150 close(x.first); 151 } 152 exit(0); 153 } 154 /* 155 perf call chain process 156 For now, if a address would not be located to some function, the address would be skipped. 157 */ 158 int process_event(char *base, unsigned long long size, unsigned long long offset) { 159 struct perf_event_header* p = NULL; 160 int pid, xpid; 161 unsigned long long rax, arg1, arg2, arg3, arg4; 162 offset%=size; 163 // assuming the header would fit within size 164 p = (struct perf_event_header*) (base+offset); 165 offset+=sizeof(*p); if (offset>=size) offset-=size; 166 if (p->type != PERF_RECORD_SAMPLE) return p->size; 167 // pid, tip; 168 pid = *((int *)(base+offset)); offset+=8; if (offset>=size) offset-=size; 169 unsigned long long abi = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; 170 if (abi == PERF_SAMPLE_REGS_ABI_64) { 171 rax = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rax 172 arg3 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdx 173 arg2 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rsi 174 arg1 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdi 175 arg4 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //r10 176 printf("uprobe event: rax[0x%llx], f(%lld,...)\n", rax, arg1); 177 } 178 return p->size; 179 } 180 181 struct RNode { 182 string func_name, file_name; 183 unsigned long long vaddr, raddr; 184 }; 185 int main(int argc, char *argv[]) { 186 char bb[256], *p, *func; 187 if (argc<3) { printf("usage: %s <pid> <func_name> \n", argv[0]); return 1; } 188 int pid = atoi(argv[1]); 189 func = argv[2]; 190 if (pid<=0) { printf("invalid pid %d\n", pid); return 1; } 191 sprintf(bb, "/proc/%d/maps", pid); 192 FILE *fp = fopen(bb, "r"); 193 if (fp==NULL) { printf("Fail to read proc for pid %d\n", pid); return 1; } 194 unsigned long long start, end, offset, inode; 195 char fname[128], mod[16], idx[32]; 196 char ff[256]; 197 char xx[64], xxx[32]; 198 int i, valid, k; 199 int type = 0; 200 vector<RNode> cans; 201 RNode rr; 202 while(1) { 203 p = fgets(bb, sizeof(bb), fp); if (p==NULL) break; 204 if (sscanf(p, "%s %s %s %s %lld %s", xx, mod, xxx, idx, &inode, fname)!=6) continue; 205 for (i=0; i<4; i++) if (mod[i]=='x') break; if (i>=4) continue; 206 if (fname[0]!='/') continue; 207 valid=1; 208 start=0; for (i=0; xx[i]&&xx[i]!='-'; i++) { 209 if (xx[i]>='0'&&xx[i]<='9') start=start*16+xx[i]-'0'; 210 else if (xx[i]>='A'&&xx[i]<='F') start=start*16+xx[i]-'A'+10; 211 else if (xx[i]>='a'&&xx[i]<='f') start=start*16+xx[i]-'a'+10; 212 else { valid=0; break; } 213 } 214 if (valid==0||start==0) continue; 215 end=0; for (i++; xx[i]; i++) { 216 if (xx[i]>='0'&&xx[i]<='9') end=end*16+xx[i]-'0'; 217 else if (xx[i]>='A'&&xx[i]<='F') end=end*16+xx[i]-'A'+10; 218 else if (xx[i]>='a'&&xx[i]<='f') end=end*16+xx[i]-'a'+10; 219 else { valid=0; break; } 220 } 221 if (valid==0||start==0) continue; 222 offset=0; for (i=0; xxx[i]; i++) { 223 if (xxx[i]>='0'&&xxx[i]<='9') offset=offset*16+xxx[i]-'0'; 224 else if (xxx[i]>='A'&&xxx[i]<='F') offset=offset*16+xxx[i]-'A'+10; 225 else if (xxx[i]>='a'&&xxx[i]<='f') offset=offset*16+xxx[i]-'a'+10; 226 else { valid=0; break; } 227 } 228 if (valid==0) break; 229 sprintf(ff, "/proc/%d/root%s", pid, fname); 230 STORE_T* s = load_symbol_from_file(ff); 231 if (s) { 232 for (auto x=s->begin(); x!=s->end(); x++) { 233 auto v = (*x).second; 234 if (strstr((*x).first.c_str(), func)) { 235 rr.func_name = (*x).first; 236 rr.file_name = string(ff); 237 rr.vaddr = v.addr-v.baddr+start; 238 rr.raddr = v.addr-v.baddr+v.boffset; 239 cans.push_back(rr); 240 } 241 } 242 delete s; 243 } 244 } 245 fclose(fp); 246 247 if (cans.size()==0) { printf("no func found\n"); return 0; } 248 k=0; if (cans.size()>1) { 249 printf("%d candidates found, please make a selection: [0, %d]\n", cans.size(), cans.size()-1); 250 for (i=0; i<cans.size(); i++) { 251 printf("[%d]: %s<%s>\n", i, cans[i].func_name.c_str(), cans[i].file_name.c_str()); 252 } 253 scanf("%d", &k); 254 if (k<0||k>=cans.size()) { printf("invalid selection, abort\n"); return 1; } 255 } 256 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); 257 fp = fopen("/sys/bus/event_source/devices/uprobe/type", "r"); 258 if (fp == NULL) { printf("fail to find type for kprobe\n"); return 1; } 259 fscanf(fp, "%d", &type); 260 fclose(fp); 261 if (type <= 0) { printf("unexpected type %d\n", type); return 1; } 262 // start perf event 263 psize = sysconf(_SC_PAGE_SIZE); // getpagesize(); 264 int cpu_num = sysconf(_SC_NPROCESSORS_ONLN), fd; 265 struct perf_event_attr attr; 266 void *addr; 267 memset(&attr, 0, sizeof(attr)); 268 attr.type = type; 269 attr.size = sizeof(attr); 270 attr.config = 0; 271 attr.sample_period = 1; 272 attr.wakeup_events = 1; 273 attr.sample_type = PERF_SAMPLE_TID|PERF_SAMPLE_REGS_USER; //PERF_SAMPLE_REGS_INTR; 274 attr.uprobe_path = (__u64)cans[k].file_name.c_str(); 275 attr.probe_offset = cans[k].raddr; 276 attr.sample_regs_user = (1<<PERF_REG_X86_AX)|(1<<PERF_REG_X86_DI)|(1<<PERF_REG_X86_SI)|(1<<PERF_REG_X86_DX)|(1<<PERF_REG_X86_R10)| 277 (1<<PERF_REG_X86_R8)|(1<<PERF_REG_X86_R9); 278 for (i=0, k=0; i<cpu_num&&i<MAXCPU; i++) { 279 printf("attaching cpu %d\n", i); 280 fd = perf_event_open(&attr, pid, i, -1, PERF_FLAG_FD_CLOEXEC); 281 if (fd<0) { perror("fail to open perf event for pid"); continue; } 282 addr = mmap(NULL, (1+MAXN)*psize, PROT_READ, MAP_SHARED, fd, 0); 283 if (addr == MAP_FAILED) { perror("mmap failed"); close(fd); continue; } 284 res[fd] = make_pair(addr, 0); 285 polls[k].fd = fd; 286 polls[k].events = POLLIN; 287 polls[k].revents = 0; 288 k++; 289 } 290 if (k==0) { printf("no cpu event attached at all\n"); return 1; } 291 292 signal(SIGINT, int_exit); 293 signal(SIGTERM, int_exit); 294 295 unsigned long long head; 296 struct perf_event_mmap_page *mp; 297 while (poll(polls, k, -1)>0) { 298 // printf("wake\n"); 299 for (i=0; i<k; i++) { 300 if ((polls[i].revents&POLLIN)==0) continue; 301 fd = polls[i].fd; 302 addr = res[fd].first; 303 mp = (struct perf_event_mmap_page *)addr; 304 head = res[fd].second; 305 if (head==mp->data_head) continue; 306 ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 1); 307 head = mp->data_head-((mp->data_head-head)%mp->data_size); 308 while(head<mp->data_head) head+=process_event((char*)addr+mp->data_offset, mp->data_size, head); 309 ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 0); 310 res[fd].second = mp->data_head; 311 } 312 } 313 int_exit(0); 314 return 0; 315 } 316