xref: /linux-tools/perf/uprobe/uprobe.cpp (revision 95e178a3db4ca122777571bcb7115acecd55b53d)
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  */
parse_elf64(FILE * fp,STORE_T & store)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 
load_symbol_from_file(const char * path)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-------------------------
perf_event_open(struct perf_event_attr * perf_event,pid_t pid,int cpu,int group_fd,unsigned long flags)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 
int_exit(int _)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  */
process_event(char * base,unsigned long long size,unsigned long long offset)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 };
main(int argc,char * argv[])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