xref: /linux-tools/perf/execommand/perf_comm.cpp (revision 342f926fd8205d4a7cd416c2ea8b61a513b3828a)
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 
14 
15 #include <map>
16 #include <unordered_set>
17 #include <unordered_map>
18 #include <queue>
19 using namespace std;
20 
21 
22 #define error(msg) do { perror(msg); exit(1); } while(0)
23 #define MAXN  128
24 
25 static long perf_event_open(struct perf_event_attr *perf_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
26     return syscall(__NR_perf_event_open, perf_event, pid, cpu, group_fd, flags);
27 }
28 static void *addr = NULL;
29 static long long psize;
30 map<int, pair<void*, long long>> res;
31 static unsigned long long cx_count=0, g_stime=0, g_total=0, g_rtotal=0;
32 static int alive=1;
33 void int_exit(int s) {
34     for (auto x: res) {
35         auto y = x.second;
36         void* addr = y.first;
37         munmap(addr, (1+MAXN)*psize);
38         close(x.first);
39     }
40     res.clear();
41     alive=0;
42 }
43 
44 unordered_map<int, string> hostnames;
45 unordered_map<int, string> commands;
46 queue<int> pids;
47 char bb[256];
48 int get_hostname(int pid) {
49     sprintf(bb, "/proc/%d/root/etc/hostname", pid);
50     FILE* fp = fopen(bb, "r");
51     int i=0;
52     if (fp) {
53         fgets(bb, sizeof(bb), fp);
54         while(i<sizeof(bb)-1&&bb[i]&&bb[i]!='\r'&&bb[i]!='\n') i++; bb[i]=0;
55         fclose(fp);
56     }
57     return i;
58 }
59 
60 int process_event(char *base, unsigned long long size, unsigned long long offset) {
61     struct perf_event_header* p = NULL;
62     int pid, i, j, ppid;
63     offset%=size;
64     // assuming the header would fit within size
65     p = (struct perf_event_header*) (base+offset);
66     offset+=sizeof(*p); if (offset>=size) offset-=size;
67     if (p->type == PERF_RECORD_FORK) {
68         pid = *(int*)(base+offset);
69         ppid = *(int*)(base+offset+4);
70         if (hostnames.count(pid)==0&&get_hostname(ppid)) hostnames[pid] =string(bb);
71     } else if (p->type == PERF_RECORD_COMM) {
72         pid = *(int*)(base+offset);
73         offset+=8; if (offset>=size) offset-=size;
74         i=offset;
75         for (j=0; j<sizeof(bb)-1; j++) {
76             if (base[i]==0) break;
77             bb[j]=base[i];
78             i++; if (i>=size) i-=size;
79         } bb[j]=0;
80         commands[pid] = string(bb);
81         if (hostnames.count(pid)==0&&get_hostname(pid)) hostnames[pid] = string(bb);
82         pids.push(pid);
83     } else if (p->type == PERF_RECORD_EXIT) {
84         pid = *(int*)(base+offset);
85         ppid = *(int*)(base+offset+4);
86         if (hostnames.count(pid)==0&&get_hostname(ppid)) hostnames[pid] =string(bb);
87         pids.push(-pid);
88     }
89     return p->size;
90 }
91 
92 void process_queue() {
93     int pid;
94     while(!pids.empty()) {
95         pid=pids.front(); pids.pop();
96         if (pid>0) {
97             if (commands.count(pid)==0) continue;
98             if (hostnames.count(pid)==0&&get_hostname(pid)) hostnames[pid]=string(bb);
99             if (hostnames.count(pid)==0) {
100                 printf("<--?--> start command [%s](%d)\n", commands[pid].c_str(), pid);
101             } else {
102                 printf("<%s> start command [%s](%d)\n", hostnames[pid].c_str(), commands[pid].c_str(), pid);
103             }
104         } else {
105             pid=-pid;
106             if (commands.count(pid)) {
107                 if (hostnames.count(pid)==0) {
108                     printf("<--?--> stop  command [%s](%d)\n", commands[pid].c_str(), pid);
109                 } else {
110                     printf("<%s> stop  command [%s](%d)\n", hostnames[pid].c_str(), commands[pid].c_str(), pid);
111                 }
112                 commands.erase(pid);
113             }
114             if (hostnames.count(pid)) hostnames.erase(pid);
115         }
116     }
117     fflush(stdout);
118 }
119 
120 #define MAXCPU 1024
121 struct pollfd polls[MAXCPU];
122 int main(int argc, char *argv[]) {
123     int i, k;
124     // start perf event
125     psize = sysconf(_SC_PAGE_SIZE); // getpagesize();
126     int cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
127 	struct perf_event_attr attr;
128     memset(&attr, 0, sizeof(attr));
129     attr.type = PERF_TYPE_SOFTWARE;
130     attr.size = sizeof(attr);
131     attr.config = PERF_COUNT_SW_DUMMY;
132     // attr.sample_period = 1;
133     // attr.wakeup_events = 1;
134     attr.comm = 1;
135     // attr.comm_exec = 1;
136     attr.sample_id_all = 1;
137     attr.task = 1;
138     int fd, cgroup_fd;
139     for (i=0, k=0; i<cpu_num&&i<MAXCPU; i++) {
140         printf("attaching cpu %d\n", i);
141         fd = perf_event_open(&attr, -1, i, -1, PERF_FLAG_FD_CLOEXEC);
142         if (fd<0) { perror("fail to open perf event"); continue; }
143         addr = mmap(NULL, (1+MAXN)*psize, PROT_READ, MAP_SHARED, fd, 0);
144         if (addr == MAP_FAILED) { perror("mmap failed"); close(fd); continue; }
145         res[fd] = make_pair(addr, 0);
146         k++;
147     }
148     if (k==0) { printf("no cpu event attached at all\n"); return 1; }
149 
150 	signal(SIGINT, int_exit);
151 	signal(SIGTERM, int_exit);
152 
153     unsigned long long head;
154     struct perf_event_mmap_page *mp;
155     while(alive) {
156         process_queue();
157         sleep(1);
158         for (auto x: res) {
159             addr = x.second.first;
160             head = x.second.second;
161             mp = (struct perf_event_mmap_page *)addr;
162             fd = x.first;
163             if (head >= mp->data_head) continue;
164             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 1);
165             head = mp->data_head-((mp->data_head-head)%mp->data_size);
166             while(head<mp->data_head) head+=process_event((char*)addr+mp->data_offset, mp->data_size, head);
167             res[fd].second = mp->data_head;
168             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 0);
169         }
170     }
171     int_exit(0);
172     return 0;
173 }
174