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 //
perf_event_open(struct perf_event_attr * perf_event,pid_t pid,int cpu,int group_fd,unsigned long flags)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;
int_exit(int s)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;
process_event(char * base,unsigned long long size,unsigned long long offset)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];
main(int argc,char * argv[])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