xref: /linux-tools/perf/kprobe/perf_kprobe.cpp (revision de922be4e919572d28577568db563e691d5e7702)
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 <asm/perf_regs.h>
14 
15 
16 #include <map>
17 #include <unordered_set>
18 #include <unordered_map>
19 #include <queue>
20 using namespace std;
21 
22 
23 #define error(msg) do { perror(msg); exit(1); } while(0)
24 #define MAXN  128
25 
26 // refer to https://lkml.kernel.org/netdev/D0757E13-27E8-4392-972A-399D6E132111@fb.com/t/
27 //
28 //
29 //
30 static long perf_event_open(struct perf_event_attr *perf_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
31     return syscall(__NR_perf_event_open, perf_event, pid, cpu, group_fd, flags);
32 }
33 static void *addr = NULL;
34 static long long psize;
35 map<int, pair<void*, long long>> res;
36 static int alive=1;
37 void int_exit(int s) {
38     for (auto x: res) {
39         auto y = x.second;
40         void* addr = y.first;
41         munmap(addr, (1+MAXN)*psize);
42         close(x.first);
43     }
44     res.clear();
45     alive=0;
46     exit(0);
47 }
48 
49 char *func = NULL;
50 using xtt =  tuple<time_t, string, string>;
51 static map<int, xtt> pids;
52 int process_event(char *base, unsigned long long size, unsigned long long offset) {
53     struct perf_event_header* p = NULL;
54     int pid, i;
55     unsigned long long abi, addr, arg1, arg2, arg3, arg4;
56     offset%=size;
57     // assuming the header would fit within size
58     p = (struct perf_event_header*) (base+offset);
59     offset+=sizeof(*p); if (offset>=size) offset-=size;
60     if (p->type == PERF_RECORD_SAMPLE) {
61         pid = *(int*)(base+offset);  offset+=8; if (offset>=size) offset-=size;
62         time_t ctime = time(NULL);
63         if (pids.count(pid)==0 || ctime-get<0>(pids[pid]) > 1000) {
64             char b[128];
65             char comm[128];
66             char host[128];
67             sprintf(b, "/proc/%d/comm", pid);
68             FILE* fp;
69             size_t i=0;
70             fp = fopen(b, "r"); if (fp) {
71                 comm[0]=0; fgets(comm, sizeof(comm), fp);
72                 while(i<sizeof(comm)&&comm[i]!=0&&comm[i]!='\n'&&comm[i]!='\r') i++;
73                 comm[i]=0;
74                 fclose(fp);
75             }
76             if (i<1) strcpy(comm, "unknown-command");
77             sprintf(b, "/proc/%d/root/etc/hostname", pid);
78             i=0; fp = fopen(b, "r"); if (fp) {
79                 host[0]=0; fgets(host, sizeof(host), fp);
80                 while(i<sizeof(host)&&host[i]!=0&&host[i]!='\n'&&host[i]!='\r') i++;
81                 host[i]=0;
82                 fclose(fp);
83             }
84             if (i<1) strcpy(host, "unknown-host");
85             pids[pid] = make_tuple(ctime, string(comm), string(host));
86         }
87         abi = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size;
88         if (abi == PERF_SAMPLE_REGS_ABI_64) {
89             addr = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rax
90             arg3 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdx
91             arg2 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rsi
92             arg1 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //rdi
93             arg4 = *(unsigned long long*)(base+offset); offset+=8; if (offset>=size) offset-=size; //r10
94             printf("%s@%s[%d] %s(0x%llx,0x%llx,0x%llx,0x%llx)\n", get<1>(pids[pid]).c_str(), get<2>(pids[pid]).c_str(), pid, func, arg1, arg2, arg3, arg4);
95         }
96     }
97     return p->size;
98 }
99 
100 
101 #define MAXCPU 1024
102 struct pollfd polls[MAXCPU];
103 int main(int argc, char *argv[]) {
104     int i, k, type;
105     // start perf event
106     if (argc<2) { printf("Need kprobe function name, e.g. %s do_sys_open\n", argv[0]); return 1; }
107     func = argv[1];
108     psize = sysconf(_SC_PAGE_SIZE); // getpagesize();
109     int cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
110 	struct perf_event_attr attr;
111     memset(&attr, 0, sizeof(attr));
112     // /sys/bus/event_source/devices/kprobe/type
113     FILE *fp = fopen("/sys/bus/event_source/devices/kprobe/type", "r");
114     if (fp == NULL) { printf("fail to find type for kprobe\n"); return 1; }
115     type = 0;
116     fscanf(fp, "%d", &type);
117     fclose(fp);
118     if (type <= 0) { printf("unexpected type %d\n", type); return 1; }
119     attr.type = type;
120     attr.size = sizeof(attr);
121     attr.config = 0; // (1<<0) for kreprobe
122     attr.sample_period = 1;
123     attr.wakeup_events = 1;
124     attr.sample_type = PERF_SAMPLE_TID|PERF_SAMPLE_REGS_INTR;
125     // ffffffff92ea9240 t bprm_execve
126     attr.kprobe_func = (__u64)func; // "do_sys_open"; // "bprm_execve";
127     attr.probe_offset = 0;
128     attr.sample_regs_intr = (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)|
129         (1<<PERF_REG_X86_R8)|(1<<PERF_REG_X86_R9);
130     int fd, cgroup_fd;
131     for (i=0, k=0; i<cpu_num&&i<MAXCPU; i++) {
132         printf("attaching cpu %d\n", i);
133         fd = perf_event_open(&attr, -1, i, -1, PERF_FLAG_FD_CLOEXEC);
134         if (fd<0) { perror("fail to open perf event"); continue; }
135         addr = mmap(NULL, (1+MAXN)*psize, PROT_READ, MAP_SHARED, fd, 0);
136         if (addr == MAP_FAILED) { perror("mmap failed"); close(fd); continue; }
137         res[fd] = make_pair(addr, 0);
138         polls[k].fd = fd;
139         polls[k].events = POLLIN;
140         polls[k].revents = 0;
141         k++;
142     }
143     if (k==0) { printf("no cpu event attached at all\n"); return 1; }
144 
145 	signal(SIGINT, int_exit);
146 	signal(SIGTERM, int_exit);
147 
148     unsigned long long head;
149     struct perf_event_mmap_page *mp;
150     while (poll(polls, k, -1)>0) {
151         for (i=0; i<k; i++) {
152             if (!alive) break;
153             if ((polls[i].revents&POLLIN)==0) continue;
154             fd = polls[i].fd;
155             addr = res[fd].first;
156             mp = (struct perf_event_mmap_page *)addr;
157             head = res[fd].second;
158             if (head==mp->data_head) continue;
159             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 1);
160             head = mp->data_head-((mp->data_head-head)%mp->data_size);
161             while(head<mp->data_head) head+=process_event((char*)addr+mp->data_offset, mp->data_size, head);
162             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 0);
163             res[fd].second = mp->data_head;
164         }
165     }
166     int_exit(0);
167     return 0;
168 }
169