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