xref: /linux-tools/perf/profiler/profiler.cpp (revision abe8ce508af08f1ec5ce27b33832a6722241342b)
178c68143SDavid Wang #include <stdlib.h>
278c68143SDavid Wang #include <stdio.h>
378c68143SDavid Wang #include <unistd.h>
478c68143SDavid Wang #include <string.h>
578c68143SDavid Wang #include <sys/ioctl.h>
678c68143SDavid Wang #include <linux/perf_event.h>
778c68143SDavid Wang #include <asm/unistd.h>
878c68143SDavid Wang #include <sys/mman.h>
978c68143SDavid Wang #include <sys/stat.h>
1078c68143SDavid Wang #include <poll.h>
1178c68143SDavid Wang #include <signal.h>
1278c68143SDavid Wang #include <fcntl.h>
1378c68143SDavid Wang #include <elf.h>
1478c68143SDavid Wang #include <string.h>
1578c68143SDavid Wang 
1678c68143SDavid Wang #include <vector>
1778c68143SDavid Wang #include <string>
1878c68143SDavid Wang #include <map>
1978c68143SDavid Wang #include <unordered_map>
2093979a5eSDavid Wang #include <unordered_set>
2178c68143SDavid Wang #include <algorithm>
2278c68143SDavid Wang using namespace std;
2378c68143SDavid Wang 
2478c68143SDavid Wang 
2593979a5eSDavid Wang #define MAXN  512
2693979a5eSDavid Wang #define MAXCPU 128
2778c68143SDavid Wang #define error(msg) do { perror(msg); exit(1); } while(0)
28abbb67abSDavid Wang 
29abbb67abSDavid Wang static char gflag_kernel_only = 0;
30abbb67abSDavid Wang 
3178c68143SDavid Wang //--------------------------------Tree for call chain and report-------------------------------
3278c68143SDavid Wang //
3378c68143SDavid Wang struct TNode {
3478c68143SDavid Wang     int c=0;
3578c68143SDavid Wang     unordered_map<string, TNode*> s;
addTNode3678c68143SDavid Wang     struct TNode *add(string n) {
3778c68143SDavid Wang         c++;
3878c68143SDavid Wang         if (s[n]==nullptr) s[n] = new TNode();
3978c68143SDavid Wang         return s[n];
4078c68143SDavid Wang     }
printitTNode4178c68143SDavid Wang     int printit(FILE *fp, int k) {
4278c68143SDavid Wang         if (s.size()) {
4378c68143SDavid Wang             using tt = tuple<int, string, TNode*>;
4478c68143SDavid Wang             vector<tt> xx;
4578c68143SDavid Wang             for (auto x: s) xx.push_back(make_tuple(x.second->c, x.first, x.second));
4678c68143SDavid Wang             sort(begin(xx), end(xx), greater<tt>());
4778c68143SDavid Wang             for (auto x: xx) {
4878c68143SDavid Wang                 auto count = get<0>(x);
4978c68143SDavid Wang                 if (100.0*count/c<1) continue;
5078c68143SDavid Wang                 auto name = get<1>(x);
5178c68143SDavid Wang                 auto nx = get<2>(x);
5278c68143SDavid Wang                 fprintf(fp, "<li>\n");
5378c68143SDavid Wang                 fprintf(fp, "<input type=\"checkbox\" id=\"c%d\" />\n", k);
5478c68143SDavid Wang                 fprintf(fp, "<label class=\"tree_label\" for=\"c%d\">%s(%.3f%% %d/%d)</label>\n", k, name.c_str(), 100.0*count/c, count, c);
5578c68143SDavid Wang                 fprintf(fp, "<ul>\n");
5678c68143SDavid Wang                 // printf("%s(%.3f%% %d/%d)\n", name.c_str(), 100.0*count/c, count, c);
5778c68143SDavid Wang                 k = nx->printit(fp, k+1);
5878c68143SDavid Wang                 fprintf(fp, "</ul>\n");
5978c68143SDavid Wang                 fprintf(fp, "</li>\n");
6078c68143SDavid Wang             }
6178c68143SDavid Wang         }
6278c68143SDavid Wang         return k;
6378c68143SDavid Wang     }
6478c68143SDavid Wang };
6578c68143SDavid Wang 
6678c68143SDavid Wang //--------------------------------symbols-------------------------------------------
6778c68143SDavid Wang using STORE_T = map<unsigned long long, pair<string, unsigned long long>>;
6878c68143SDavid Wang using K_STORE_T = map<unsigned long long, string>;
6978c68143SDavid Wang 
7078c68143SDavid Wang /*
7178c68143SDavid Wang  * load FUNC symbols refering to the section indicated by the offset, relocate the virtual address
7278c68143SDavid Wang  */
parse_elf64(FILE * fp,unsigned long long v_addr,unsigned long long v_size,unsigned long long v_offset,STORE_T & store)7378c68143SDavid Wang void parse_elf64(FILE *fp, unsigned long long v_addr, unsigned long long v_size, unsigned long long v_offset, STORE_T& store) {
7478c68143SDavid Wang     Elf64_Ehdr ehdr;
7578c68143SDavid Wang     int rc = fread(&ehdr, sizeof(ehdr), 1, fp);
7678c68143SDavid Wang     if (rc != 1) return;
7778c68143SDavid Wang     int n, s, i;
7878c68143SDavid Wang     unsigned long long offset;
7978c68143SDavid Wang 
8078c68143SDavid Wang     // load program headers
8178c68143SDavid Wang     unsigned long long p_vaddr, p_size;
8278c68143SDavid Wang     n = ehdr.e_phnum;
8378c68143SDavid Wang     s = ehdr.e_phentsize;
8478c68143SDavid Wang     offset = ehdr.e_phoff;
8578c68143SDavid Wang     Elf64_Phdr phdr;
8678c68143SDavid Wang     for (i=0; i<n; i++) {
8778c68143SDavid Wang         rc = fseek(fp, offset, SEEK_SET);
8878c68143SDavid Wang         if (rc<0) { perror("fail to seek"); return; }
8978c68143SDavid Wang         rc = fread(&phdr, sizeof(phdr), 1, fp);
9078c68143SDavid Wang         if (rc != 1) { perror("fail to read program header"); return; }
9178c68143SDavid Wang         if (phdr.p_flags&PF_X) {
9278c68143SDavid Wang             if (phdr.p_offset == v_offset) {
9378c68143SDavid Wang                 p_vaddr = phdr.p_vaddr;
9478c68143SDavid Wang                 p_size = phdr.p_memsz; if (p_size==0) p_size = 0xffffffff;
9578c68143SDavid Wang                 break;
9678c68143SDavid Wang             }
9778c68143SDavid Wang         }
9878c68143SDavid Wang         offset+=s;
9978c68143SDavid Wang     }
10078c68143SDavid Wang     if (i>=n) { printf("No program header match offset found, fail to load\n"); return; }
10178c68143SDavid Wang 
10278c68143SDavid Wang     // load section headers
10378c68143SDavid Wang     n = ehdr.e_shnum;
10478c68143SDavid Wang     s = ehdr.e_shentsize;
10578c68143SDavid Wang     offset = ehdr.e_shoff;
10678c68143SDavid Wang     Elf64_Shdr shdr;
10778c68143SDavid Wang     vector<Elf64_Shdr> headers;
10878c68143SDavid Wang     for (int i=0; i<n; i++) {
10978c68143SDavid Wang         rc = fseek(fp, offset, SEEK_SET);
11078c68143SDavid Wang         if (rc<0) { perror("fail to seek"); return; }
11178c68143SDavid Wang         rc = fread(&shdr, sizeof(shdr), 1, fp);
11278c68143SDavid Wang         if (rc != 1) { perror("fail to read sec header"); return; }
11378c68143SDavid Wang         headers.push_back(shdr);
11478c68143SDavid Wang         offset+=s;
11578c68143SDavid Wang     }
11678c68143SDavid Wang     Elf64_Sym symb;
11778c68143SDavid Wang     unsigned long long faddr, fsize;
11878c68143SDavid Wang     unsigned long long size, item_size;
11978c68143SDavid Wang     int link, ix, flink, k;
12078c68143SDavid Wang     char fname[128];
12178c68143SDavid Wang     for (int i=0; i<n; i++) {
12278c68143SDavid Wang         switch(headers[i].sh_type) {
12378c68143SDavid Wang             case SHT_SYMTAB:
12478c68143SDavid Wang             case SHT_DYNSYM:
12578c68143SDavid Wang                 offset = headers[i].sh_offset;
12678c68143SDavid Wang                 size = headers[i].sh_size;
12778c68143SDavid Wang                 item_size = headers[i].sh_entsize;
12878c68143SDavid Wang                 link = headers[i].sh_link;
12978c68143SDavid Wang                 if (link<=0) break;
13078c68143SDavid Wang                 for (k=0; k+item_size<=size; k+=item_size) {
13178c68143SDavid Wang                     rc = fseek(fp, offset+k, SEEK_SET); if (rc<0) continue;
13278c68143SDavid Wang                     rc = fread(&symb, sizeof(symb), 1, fp); if (rc != 1) continue;
13378c68143SDavid Wang                     if (ELF64_ST_TYPE(symb.st_info) != STT_FUNC ) continue;
13478c68143SDavid Wang                     flink = symb.st_shndx; if (flink==0) continue;
13593979a5eSDavid Wang                     fsize = symb.st_size; // if (fsize==0) continue;
13678c68143SDavid Wang                     faddr = symb.st_value; if (faddr>p_vaddr+p_size) continue;
13778c68143SDavid Wang                     ix = symb.st_name; if (ix==0) continue;
13878c68143SDavid Wang                     rc = fseek(fp, headers[link].sh_offset+ix, SEEK_SET); if (rc<0) continue;
13978c68143SDavid Wang                     if (fgets(fname, sizeof(fname), fp)==NULL) continue;
14078c68143SDavid Wang                     faddr = faddr-p_vaddr+v_addr;
14193979a5eSDavid Wang                     if (store.count(faddr)) {
14293979a5eSDavid Wang                         if (store[faddr].second<fsize) store[faddr] = make_pair(string(fname), fsize);
14393979a5eSDavid Wang                     } else store[faddr] = make_pair(string(fname), fsize);
14478c68143SDavid Wang                 }
14578c68143SDavid Wang                 break;
14678c68143SDavid Wang             default:
14778c68143SDavid Wang                 break;
14878c68143SDavid Wang         }
14978c68143SDavid Wang     }
15078c68143SDavid Wang }
15178c68143SDavid Wang 
load_symbol_from_file(const char * path,unsigned long long addr,unsigned long long size,unsigned long long offset,STORE_T & store)15278c68143SDavid Wang int load_symbol_from_file(const char *path, unsigned long long addr, unsigned long long size, unsigned long long offset, STORE_T& store) {
15378c68143SDavid Wang     printf("loading symble from %s\n", path);
15478c68143SDavid Wang     FILE *fp = fopen(path, "rb");
15578c68143SDavid Wang     if (fp==NULL) { perror("fail to open file"); return -1; }
15678c68143SDavid Wang     char ident[EI_NIDENT], c;
15778c68143SDavid Wang     int err=0;
15878c68143SDavid Wang     int rc = fread(ident, sizeof(ident), 1, fp);
15978c68143SDavid Wang     if (rc != 1) { perror("fail to read ident"); err=-1; goto end; }
16078c68143SDavid Wang     if (ident[0]!=0x7f) { printf("not a elf file\n"); err=-1; goto  end; }
16178c68143SDavid Wang     c=ident[4];
16278c68143SDavid Wang     rc = fseek(fp, 0, SEEK_SET); if (rc<0) { perror("fail to rewind"); goto end; }
16378c68143SDavid Wang     if (c == ELFCLASS32) {
16478c68143SDavid Wang         printf("32bit elf not supported yet\n"); err=-2; goto end;
16578c68143SDavid Wang     } else if (c == ELFCLASS64) {
16678c68143SDavid Wang         parse_elf64(fp, addr, size, offset, store);
16778c68143SDavid Wang     }
16878c68143SDavid Wang 
16978c68143SDavid Wang end:
17078c68143SDavid Wang     fclose(fp);
17178c68143SDavid Wang     return err;
17278c68143SDavid Wang }
17378c68143SDavid Wang 
parse_hex(char * p,int * n)17478c68143SDavid Wang static unsigned long long parse_hex(char *p, int *n) {
17578c68143SDavid Wang     unsigned long long r=0;
17678c68143SDavid Wang     int i=0;
17778c68143SDavid Wang     *n = 0;
17878c68143SDavid Wang     while(p[i]==' '||p[i]=='\t') i++;
17978c68143SDavid Wang     if (p[i]==0) return 0;
18078c68143SDavid Wang     if (p[i+1]=='x') i+=2;
18178c68143SDavid Wang     int v;
18278c68143SDavid Wang     while(p[i]) {
18378c68143SDavid Wang         if (p[i]>='0'&&p[i]<='9') v=p[i]-'0';
18478c68143SDavid Wang         else if (p[i]>='a'&&p[i]<='f') v=10+p[i]-'a';
18578c68143SDavid Wang         else if (p[i]>='A'&&p[i]<='F') v=10+p[i]-'A';
18678c68143SDavid Wang         else break;
18778c68143SDavid Wang         r=(r<<4)+v;
18878c68143SDavid Wang         i++;
18978c68143SDavid Wang     }
19078c68143SDavid Wang     *n = i;
19178c68143SDavid Wang     return r;
19278c68143SDavid Wang }
19378c68143SDavid Wang 
load_symbol_pid(int pid)19478c68143SDavid Wang STORE_T*  load_symbol_pid(int pid) {
19578c68143SDavid Wang     printf("loading symbols for %d\n", pid);
19693979a5eSDavid Wang     char bb[256];
19778c68143SDavid Wang     sprintf(bb, "/proc/%d/maps", pid);
19878c68143SDavid Wang     FILE* fp = fopen(bb, "r");
19978c68143SDavid Wang     if (fp==NULL) return NULL;
20078c68143SDavid Wang     STORE_T *store = new STORE_T();
20193979a5eSDavid Wang     unsigned long long start, end, offset, inode;
20278c68143SDavid Wang     char *p;
20378c68143SDavid Wang     int i, c, j;
20493979a5eSDavid Wang     char fname[128], xx[64], xxx[32], mod[16], idx[16];
20578c68143SDavid Wang     while(1) {
20678c68143SDavid Wang         p=fgets(bb, sizeof(bb), fp); if (p==NULL) break;
20793979a5eSDavid Wang         if (sscanf(p, "%s %s %s %s %lld %s", xx, mod, xxx, idx, &inode, fname)!=6) continue;
20878c68143SDavid Wang         i=0; c=0;
20993979a5eSDavid Wang         start = parse_hex(xx, &c); if (c==0) continue; i+=c; if (p[i]!='-') continue; i++;
21093979a5eSDavid Wang         end = parse_hex(xx+i, &c); if (c==0) continue;
21178c68143SDavid Wang         // parse type
21293979a5eSDavid Wang         for (j=0; j<8; j++) if (mod[j]=='x') break; if (j>=8) continue;
21393979a5eSDavid Wang         if (fname[0]!='/') continue;
21493979a5eSDavid Wang         offset = parse_hex(xxx, &c); if (c==0) continue;
21578c68143SDavid Wang         // remaining should contains '/' indicating this mmap is refering to a file
21693979a5eSDavid Wang         sprintf(bb, "/proc/%d/root%s", pid, fname);
21778c68143SDavid Wang         load_symbol_from_file(bb, start, end-start, offset, *store);
21878c68143SDavid Wang     }
21978c68143SDavid Wang     fclose(fp);
220500cf4deSDavid Wang     if (store->size()==0) {
221500cf4deSDavid Wang         delete store;
222500cf4deSDavid Wang         store = NULL;
223500cf4deSDavid Wang     }
22478c68143SDavid Wang     return store;
22578c68143SDavid Wang }
22678c68143SDavid Wang 
22778c68143SDavid Wang /* parse kernel func symbols from /proc/kallsyms */
load_kernel()22878c68143SDavid Wang K_STORE_T* load_kernel() {
22978c68143SDavid Wang     FILE* fp = fopen("/proc/kallsyms", "r");
23078c68143SDavid Wang     if (fp == NULL) return NULL;
23178c68143SDavid Wang     char *p;
23278c68143SDavid Wang     unsigned long long addr;
23378c68143SDavid Wang     int c;
23478c68143SDavid Wang     K_STORE_T* store = new K_STORE_T();
23578c68143SDavid Wang     char bb[128], adr[128], type[8], name[128];
23678c68143SDavid Wang     while(1) {
23778c68143SDavid Wang         p = fgets(bb, sizeof(bb), fp); if (p==NULL) break;
23878c68143SDavid Wang         if (sscanf(p, "%s %s %s", adr, type, name)!=3) continue;;
23978c68143SDavid Wang         if (type[0]!='t'&&type[0]!='T') continue;
24078c68143SDavid Wang         addr=parse_hex(adr, &c); if (c==0) continue;
24178c68143SDavid Wang         (*store)[addr] = string(name);
24278c68143SDavid Wang     }
24378c68143SDavid Wang     return store;
24478c68143SDavid Wang     fclose(fp);
24578c68143SDavid Wang }
24678c68143SDavid Wang 
24778c68143SDavid Wang //------------------------------perf profiler-------------------------
perf_event_open(struct perf_event_attr * perf_event,pid_t pid,int cpu,int group_fd,unsigned long flags)24878c68143SDavid Wang static long perf_event_open(struct perf_event_attr *perf_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
24978c68143SDavid Wang     return syscall(__NR_perf_event_open, perf_event, pid, cpu, group_fd, flags);
25078c68143SDavid Wang }
25178c68143SDavid Wang unordered_map<int, STORE_T*> pid_symbols;
25278c68143SDavid Wang K_STORE_T* kernel_symbols = NULL;
25378c68143SDavid Wang 
25478c68143SDavid Wang struct pollfd polls[MAXCPU];
25578c68143SDavid Wang // res for cleanup
25678c68143SDavid Wang static long long psize;
25778c68143SDavid Wang map<int, pair<void*, long long>> res;
25878c68143SDavid Wang TNode* gnode = NULL;
25978c68143SDavid Wang 
26093979a5eSDavid Wang unordered_map<unsigned long long, string> unknowns;
int_exit(int _)26178c68143SDavid Wang void int_exit(int _) {
26278c68143SDavid Wang     for (auto x: res) {
26378c68143SDavid Wang         auto y = x.second;
26478c68143SDavid Wang         void* addr = y.first;
26578c68143SDavid Wang         munmap(addr, (1+MAXN)*psize);
26678c68143SDavid Wang         close(x.first);
26778c68143SDavid Wang     }
26878c68143SDavid Wang     res.clear();
26978c68143SDavid Wang     if (gnode!=NULL) {
27078c68143SDavid Wang         FILE* fp = fopen("./report.html", "w");
27178c68143SDavid Wang         if (fp) {
27278c68143SDavid Wang             fprintf(fp, "<head> <link rel=\"stylesheet\" href=\"report.css\"> <script src=\"report.js\"> </script> </head>\n");
27378c68143SDavid Wang             fprintf(fp, "<ul class=\"tree\">\n");
27478c68143SDavid Wang             gnode->printit(fp, 0);
27578c68143SDavid Wang             fprintf(fp, "</ul>\n");
27678c68143SDavid Wang             fclose(fp);
27778c68143SDavid Wang             printf("report done\n");
27878c68143SDavid Wang         }
27978c68143SDavid Wang         gnode = NULL;
28078c68143SDavid Wang     }
28193979a5eSDavid Wang     printf("---------------------unknowns-----------------\n");
28293979a5eSDavid Wang     for (auto x=unknowns.begin(); x!=unknowns.end(); x++) {
28393979a5eSDavid Wang         printf("0x%llx  --?>  %s\n", (*x).first, (*x).second.c_str());
28493979a5eSDavid Wang     }
285a7aacf28SDavid Wang     exit(0);
28678c68143SDavid Wang }
28778c68143SDavid Wang /*
28878c68143SDavid Wang perf call chain process
28978c68143SDavid Wang For now, if a address would not be located to some function, the address would be skipped.
29078c68143SDavid Wang  */
process_event(char * base,unsigned long long size,unsigned long long offset)29178c68143SDavid Wang int process_event(char *base, unsigned long long size, unsigned long long offset) {
29278c68143SDavid Wang     struct perf_event_header* p = NULL;
29378c68143SDavid Wang     int pid, xpid;
29478c68143SDavid Wang     unsigned long long time;
29578c68143SDavid Wang     offset%=size;
29678c68143SDavid Wang     // assuming the header would fit within size
29778c68143SDavid Wang     p = (struct perf_event_header*) (base+offset);
29878c68143SDavid Wang     offset+=sizeof(*p); if (offset>=size) offset-=size;
29978c68143SDavid Wang     if (p->type != PERF_RECORD_SAMPLE) return p->size;
30078c68143SDavid Wang     // pid, tip;
30178c68143SDavid Wang     pid = *((int *)(base+offset));  offset+=8; if (offset>=size) offset-=size;
30278c68143SDavid Wang     unsigned long long nr = *((unsigned long long*)(base+offset)); offset+=8; if (offset>=size) offset-=size;
30393979a5eSDavid Wang     if (nr>128) return -1;
30478c68143SDavid Wang     unsigned long long addr, o, addr0;
30578c68143SDavid Wang     if (nr) {
30678c68143SDavid Wang         if (gnode==NULL) gnode=new TNode();
30778c68143SDavid Wang         char bb[64];
30878c68143SDavid Wang         TNode* r = gnode;
30978c68143SDavid Wang         if (pid_symbols.count(pid)==0) pid_symbols[pid] = load_symbol_pid(pid);
31078c68143SDavid Wang         STORE_T* px = pid_symbols[pid];
31178c68143SDavid Wang         addr0 = *((unsigned long long *)(base+offset));
312*abe8ce50SDavid Wang         char user_mark=0, start_mark=0;
31378c68143SDavid Wang         for (int i=nr-1; i>=0; i--) {
31478c68143SDavid Wang             o = i*8+offset; if (o>=size) o-=size;
31578c68143SDavid Wang             addr = *((unsigned long long*)(base+o));
31693979a5eSDavid Wang             if (addr==0) continue; // something wrong?
31778c68143SDavid Wang             if ((addr>>56)==(addr0>>56) && (p->misc&PERF_RECORD_MISC_KERNEL)) {
31878c68143SDavid Wang                 // skip the cross line command, no idear how to correctly resolve it now.
31978c68143SDavid Wang                 if (user_mark) { user_mark=0; continue; }
32078c68143SDavid Wang                 // check in kernel
32178c68143SDavid Wang                 if (kernel_symbols&&!kernel_symbols->empty()) {
32278c68143SDavid Wang                     auto x = kernel_symbols->upper_bound(addr);
32378c68143SDavid Wang                     if (x==kernel_symbols->begin()) {
32478c68143SDavid Wang                         // sprintf(bb, "0x%llx", addr); r = r->add(string(bb));
325a7aacf28SDavid Wang                         r = r->add(string("unknown"));
32678c68143SDavid Wang                     } else {
32778c68143SDavid Wang                         x--;
32878c68143SDavid Wang                         r = r->add((*x).second);
32978c68143SDavid Wang                     }
33078c68143SDavid Wang                 } else {
33178c68143SDavid Wang                     // sprintf(bb, "0x%llx", addr); r = r->add(string(bb));
332a7aacf28SDavid Wang                     r = r->add(string("unknown"));
33378c68143SDavid Wang                 }
33478c68143SDavid Wang             } else {
335abbb67abSDavid Wang                 if (gflag_kernel_only) continue;
33678c68143SDavid Wang                 if (px) {
33778c68143SDavid Wang                     auto x = px->upper_bound(addr);
33878c68143SDavid Wang                     if (x==px->begin()) {
33978c68143SDavid Wang                         // sprintf(bb, "0x%llx", addr); r = r->add(string(bb));
340*abe8ce50SDavid Wang                         if (start_mark) {
341f82de63eSDavid Wang                             auto y = (*x).second;
342f82de63eSDavid Wang                             r = r->add(y.first+"?");
343*abe8ce50SDavid Wang                         }
34478c68143SDavid Wang                     } else {
34578c68143SDavid Wang                         x--;
34678c68143SDavid Wang                         auto y = (*x).second;
34793979a5eSDavid Wang                         if (y.second && addr>(*x).first+y.second) {
34878c68143SDavid Wang                             // r = r->add(y.first);
34978c68143SDavid Wang                             // sprintf(bb, "0x%llx", addr); r = r->add(string(bb));
350*abe8ce50SDavid Wang                             if (start_mark) {
351f82de63eSDavid Wang                                 x++;
352b31fb5e8SDavid Wang                                 if (x==px->end()) r = r->add(y.first+"??");
353f82de63eSDavid Wang                                 else {
354603b7a2cSDavid Wang                                     auto z = (*x).second;
355b31fb5e8SDavid Wang                                     r = r->add(y.first+"?"+z.first);
356f82de63eSDavid Wang                                 }
357*abe8ce50SDavid Wang                             }
35878c68143SDavid Wang                         } else {
359*abe8ce50SDavid Wang                             start_mark=1;
36078c68143SDavid Wang                             r = r->add(y.first);
36178c68143SDavid Wang                         }
36278c68143SDavid Wang                     }
36378c68143SDavid Wang                 } else {
36478c68143SDavid Wang                     // sprintf(bb, "0x%llx", addr); r = r->add(string(bb));
36593979a5eSDavid Wang                     // r = r->add(string("unknown"));
36678c68143SDavid Wang                 }
36778c68143SDavid Wang                 user_mark=1;
36878c68143SDavid Wang             }
36978c68143SDavid Wang         }
37078c68143SDavid Wang     }
37178c68143SDavid Wang     return p->size;
37278c68143SDavid Wang }
37378c68143SDavid Wang 
main(int argc,char * argv[])37478c68143SDavid Wang int main(int argc, char *argv[]) {
37578c68143SDavid Wang     kernel_symbols = load_kernel();
3764092c028SDavid Wang     if (argc<2) { printf("Need pid\n"); return 1; }
377abbb67abSDavid Wang     int pid = atoi(argv[1]);
378abbb67abSDavid Wang     if (pid<0) { gflag_kernel_only = 1; pid=-pid; }
379abbb67abSDavid Wang     if (pid==0) { printf("invalid pid %s\n", argv[1]); return 1; }
38078c68143SDavid Wang     // find cgroup
38178c68143SDavid Wang     char xb[256], xb2[256];
38278c68143SDavid Wang     int i, j, k, fd;
38378c68143SDavid Wang     void* addr;
38478c68143SDavid Wang     sprintf(xb, "/proc/%d/cgroup", pid);
38578c68143SDavid Wang     FILE* fp = fopen(xb, "r");
38678c68143SDavid Wang     if (fp==NULL) error("fail to open cgroup file");
38778c68143SDavid Wang     char *p;
38878c68143SDavid Wang     xb2[0]=0;
3894092c028SDavid Wang     int cgroup_name_len=0;
39078c68143SDavid Wang     while(1) {
39178c68143SDavid Wang         p = fgets(xb, sizeof(xb), fp); if (p==NULL) break;
39278c68143SDavid Wang         i=0; while(p[i]&&p[i]!=':') i++; if (p[i]==0) continue;
39378c68143SDavid Wang         if (strstr(p, "perf_event")) {
39478c68143SDavid Wang             i++; while(p[i]!=':'&&p[i]) i++;  if (p[i]!=':') continue; i++;
39578c68143SDavid Wang             j=i; while(p[j]!='\r'&&p[j]!='\n'&&p[j]!=0) j++; p[j]=0;
39678c68143SDavid Wang             sprintf(xb2, "/sys/fs/cgroup/perf_event%s", p+i);
3974092c028SDavid Wang             cgroup_name_len=j-i;
39878c68143SDavid Wang             break;
39978c68143SDavid Wang         } else if (p[i+1]==':') {
40078c68143SDavid Wang             i+=2; j=i; while(p[j]!='\r'&&p[j]!='\n'&&p[j]!=0) j++; p[j]=0;
40178c68143SDavid Wang             sprintf(xb2, "/sys/fs/cgroup/%s", p+i);
4024092c028SDavid Wang             cgroup_name_len=j-i;
40378c68143SDavid Wang         }
40478c68143SDavid Wang     }
40578c68143SDavid Wang     fclose(fp);
40678c68143SDavid Wang     if (xb2[0]==0) error("no proper cgroup found\n");
4074092c028SDavid Wang     if (cgroup_name_len<2) {
4084092c028SDavid Wang         printf("cgroup %s seems to be root, not allowed\n", xb2);
4094092c028SDavid Wang         return -1;
4104092c028SDavid Wang     }
41178c68143SDavid Wang     printf("try to use cgroup %s\n", xb2);
41278c68143SDavid Wang     int cgroup_id = open(xb2, O_CLOEXEC);
41378c68143SDavid Wang     if (cgroup_id<=0) { perror("error open cgroup dir"); return 1; }
41478c68143SDavid Wang     // start perf event
41578c68143SDavid Wang     psize = sysconf(_SC_PAGE_SIZE); // getpagesize();
41678c68143SDavid Wang     int cpu_num = sysconf(_SC_NPROCESSORS_ONLN);
41778c68143SDavid Wang 	struct perf_event_attr attr;
41878c68143SDavid Wang     memset(&attr, 0, sizeof(attr));
41978c68143SDavid Wang     attr.type = PERF_TYPE_SOFTWARE;
42078c68143SDavid Wang     attr.size = sizeof(attr);
42178c68143SDavid Wang     attr.config = PERF_COUNT_SW_CPU_CLOCK;
42293979a5eSDavid Wang     attr.sample_freq = 777; // adjust it
42378c68143SDavid Wang     attr.freq = 1;
42493979a5eSDavid Wang     attr.wakeup_events = 16;
42578c68143SDavid Wang     attr.sample_type = PERF_SAMPLE_TID|PERF_SAMPLE_CALLCHAIN;
426abbb67abSDavid Wang     attr.sample_max_stack = 32;
427abbb67abSDavid Wang     if (gflag_kernel_only) attr.exclude_callchain_user = 1;
42878c68143SDavid Wang     for (i=0, k=0; i<cpu_num&&i<MAXCPU; i++) {
42978c68143SDavid Wang         printf("attaching cpu %d\n", i);
43078c68143SDavid Wang         fd = perf_event_open(&attr, cgroup_id, i, -1, PERF_FLAG_FD_CLOEXEC|PERF_FLAG_PID_CGROUP);
43178c68143SDavid Wang         if (fd<0) { perror("fail to open perf event"); continue; }
43278c68143SDavid Wang         addr = mmap(NULL, (1+MAXN)*psize, PROT_READ, MAP_SHARED, fd, 0);
43378c68143SDavid Wang         if (addr == MAP_FAILED) { perror("mmap failed"); close(fd); continue; }
43478c68143SDavid Wang         res[fd] = make_pair(addr, 0);
43578c68143SDavid Wang         polls[k].fd = fd;
43678c68143SDavid Wang         polls[k].events = POLLIN;
43778c68143SDavid Wang         polls[k].revents = 0;
43878c68143SDavid Wang         k++;
43978c68143SDavid Wang     }
44078c68143SDavid Wang     if (k==0) { printf("no cpu event attached at all\n"); return 1; }
44178c68143SDavid Wang 
44278c68143SDavid Wang 	signal(SIGINT, int_exit);
44378c68143SDavid Wang 	signal(SIGTERM, int_exit);
44478c68143SDavid Wang 
44578c68143SDavid Wang     unsigned long long head;
44693979a5eSDavid Wang     int event_size;
44778c68143SDavid Wang     struct perf_event_mmap_page *mp;
44878c68143SDavid Wang     while (poll(polls, k, -1)>0) {
44978c68143SDavid Wang         // printf("wake\n");
45078c68143SDavid Wang         for (i=0; i<k; i++) {
45178c68143SDavid Wang             if ((polls[i].revents&POLLIN)==0) continue;
45278c68143SDavid Wang             fd = polls[i].fd;
45378c68143SDavid Wang             addr = res[fd].first;
45478c68143SDavid Wang             mp = (struct perf_event_mmap_page *)addr;
45578c68143SDavid Wang             head = res[fd].second;
45678c68143SDavid Wang             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 1);
45793979a5eSDavid Wang             if (head>mp->data_head) head=mp->data_head;
45878c68143SDavid Wang             head = mp->data_head-((mp->data_head-head)%mp->data_size);
45993979a5eSDavid Wang             while(head<mp->data_head) {
46093979a5eSDavid Wang                 event_size = process_event((char*)addr+mp->data_offset, mp->data_size, head);
46193979a5eSDavid Wang                 if (event_size<0) {
46293979a5eSDavid Wang                     // resync
46393979a5eSDavid Wang                     head=mp->data_head;
46493979a5eSDavid Wang                     break;
46593979a5eSDavid Wang                 }
46693979a5eSDavid Wang                 head += event_size;
46793979a5eSDavid Wang             }
46878c68143SDavid Wang             res[fd].second = mp->data_head;
46993979a5eSDavid Wang             ioctl(fd, PERF_EVENT_IOC_PAUSE_OUTPUT, 0);
47078c68143SDavid Wang         }
47178c68143SDavid Wang     }
47278c68143SDavid Wang 
47378c68143SDavid Wang     int_exit(0);
47478c68143SDavid Wang     return 0;
47578c68143SDavid Wang }
476