162aa74f8SRafael Auler //===-- instr.cpp -----------------------------------------------*- C++ -*-===// 262aa74f8SRafael Auler // 362aa74f8SRafael Auler // The LLVM Compiler Infrastructure 462aa74f8SRafael Auler // 562aa74f8SRafael Auler // This file is distributed under the University of Illinois Open Source 662aa74f8SRafael Auler // License. See LICENSE.TXT for details. 762aa74f8SRafael Auler // This file contains code that is linked to the final binary with a function 862aa74f8SRafael Auler // that is called at program exit to dump instrumented data collected during 962aa74f8SRafael Auler // execution. 1062aa74f8SRafael Auler // 1162aa74f8SRafael Auler //===----------------------------------------------------------------------===// 1262aa74f8SRafael Auler // 13*821480d2SRafael Auler // BOLT runtime instrumentation library for x86 Linux. 1462aa74f8SRafael Auler // 1562aa74f8SRafael Auler //===----------------------------------------------------------------------===// 1662aa74f8SRafael Auler 1762aa74f8SRafael Auler #include <cstdint> 18*821480d2SRafael Auler #include <elf.h> 1962aa74f8SRafael Auler 2062aa74f8SRafael Auler // All extern declarations here need to be defined by BOLT itself. 2162aa74f8SRafael Auler 2262aa74f8SRafael Auler // Counters inserted by instrumentation, incremented during runtime when 2362aa74f8SRafael Auler // points of interest (locations) in the program are reached. 2462aa74f8SRafael Auler extern uint64_t __bolt_instr_locations[]; 2562aa74f8SRafael Auler // Number of counters. 2662aa74f8SRafael Auler extern uint32_t __bolt_instr_num_locs; 2762aa74f8SRafael Auler // Filename to dump data to. 2862aa74f8SRafael Auler extern char __bolt_instr_filename[]; 2962aa74f8SRafael Auler 3062aa74f8SRafael Auler // A location is a function name plus offset. Function name needs to be 3162aa74f8SRafael Auler // retrieved from the string table and is stored as an index to this table. 32*821480d2SRafael Auler struct Location { 3362aa74f8SRafael Auler uint32_t FunctionName; 3462aa74f8SRafael Auler uint32_t Offset; 35*821480d2SRafael Auler }; 3662aa74f8SRafael Auler 3762aa74f8SRafael Auler // An edge description defines an instrumented edge in the program, fully 3862aa74f8SRafael Auler // identified by where the jump is located and its destination. 39*821480d2SRafael Auler struct EdgeDescription { 4062aa74f8SRafael Auler Location From; 4162aa74f8SRafael Auler Location To; 42*821480d2SRafael Auler }; 4362aa74f8SRafael Auler 44*821480d2SRafael Auler // These need to be read from disk. They are generated by BOLT and written to 45*821480d2SRafael Auler // an ELF note section in the binary itself. 46*821480d2SRafael Auler struct InstrumentationInfo { 47*821480d2SRafael Auler EdgeDescription *Descriptions; 48*821480d2SRafael Auler char *Strings; // String table with function names used in this binary 49*821480d2SRafael Auler int FileDesc; // File descriptor for the file on disk backing this 50*821480d2SRafael Auler // information in memory via mmap 51*821480d2SRafael Auler uint8_t *MMapPtr; // The mmap ptr 52*821480d2SRafael Auler int MMapSize; // The mmap size 53*821480d2SRafael Auler }; 5462aa74f8SRafael Auler 5562aa74f8SRafael Auler // Declare some syscall wrappers we use throughout this code to avoid linking 5662aa74f8SRafael Auler // against system libc. 5762aa74f8SRafael Auler static uint64_t 58*821480d2SRafael Auler __open(const char *pathname, 5962aa74f8SRafael Auler uint64_t flags, 6062aa74f8SRafael Auler uint64_t mode) { 6162aa74f8SRafael Auler uint64_t ret; 6262aa74f8SRafael Auler __asm__ __volatile__ ( 6362aa74f8SRafael Auler "movq $2, %%rax\n" 6462aa74f8SRafael Auler "syscall" 6562aa74f8SRafael Auler : "=a"(ret) 6662aa74f8SRafael Auler : "D"(pathname), "S"(flags), "d"(mode) 6762aa74f8SRafael Auler : "cc", "rcx", "r11", "memory"); 6862aa74f8SRafael Auler return ret; 6962aa74f8SRafael Auler } 7062aa74f8SRafael Auler 71*821480d2SRafael Auler static uint64_t __write(uint64_t fd, const void *buf, uint64_t count) { 7262aa74f8SRafael Auler uint64_t ret; 7362aa74f8SRafael Auler __asm__ __volatile__ ( 7462aa74f8SRafael Auler "movq $1, %%rax\n" 7562aa74f8SRafael Auler "syscall\n" 7662aa74f8SRafael Auler : "=a"(ret) 7762aa74f8SRafael Auler : "D"(fd), "S"(buf), "d"(count) 7862aa74f8SRafael Auler : "cc", "rcx", "r11", "memory"); 7962aa74f8SRafael Auler return ret; 8062aa74f8SRafael Auler } 8162aa74f8SRafael Auler 82*821480d2SRafael Auler static uint64_t __lseek(uint64_t fd, uint64_t pos, uint64_t whence) { 83*821480d2SRafael Auler uint64_t ret; 84*821480d2SRafael Auler __asm__ __volatile__ ( 85*821480d2SRafael Auler "movq $8, %%rax\n" 86*821480d2SRafael Auler "syscall\n" 87*821480d2SRafael Auler : "=a"(ret) 88*821480d2SRafael Auler : "D"(fd), "S"(pos), "d"(whence) 89*821480d2SRafael Auler : "cc", "rcx", "r11", "memory"); 90*821480d2SRafael Auler return ret; 91*821480d2SRafael Auler } 92*821480d2SRafael Auler 93*821480d2SRafael Auler static int __close(uint64_t fd) { 9462aa74f8SRafael Auler uint64_t ret; 9562aa74f8SRafael Auler __asm__ __volatile__ ( 9662aa74f8SRafael Auler "movq $3, %%rax\n" 9762aa74f8SRafael Auler "syscall\n" 9862aa74f8SRafael Auler : "=a"(ret) 9962aa74f8SRafael Auler : "D"(fd) 10062aa74f8SRafael Auler : "cc", "rcx", "r11", "memory"); 10162aa74f8SRafael Auler return ret; 10262aa74f8SRafael Auler } 10362aa74f8SRafael Auler 104*821480d2SRafael Auler static void *__mmap(uint64_t addr, uint64_t size, uint64_t prot, 105*821480d2SRafael Auler uint64_t flags, uint64_t fd, uint64_t offset) { 106*821480d2SRafael Auler void *ret; 107*821480d2SRafael Auler register uint64_t r8 asm("r8") = fd; 108*821480d2SRafael Auler register uint64_t r9 asm("r9") = offset; 109*821480d2SRafael Auler register uint64_t r10 asm("r10") = flags; 110*821480d2SRafael Auler __asm__ __volatile__ ( 111*821480d2SRafael Auler "movq $9, %%rax\n" 112*821480d2SRafael Auler "syscall\n" 113*821480d2SRafael Auler : "=a"(ret) 114*821480d2SRafael Auler : "D"(addr), "S"(size), "d"(prot), "r"(r10), "r"(r8), "r"(r9) 115*821480d2SRafael Auler : "cc", "rcx", "r11", "memory"); 116*821480d2SRafael Auler return ret; 117*821480d2SRafael Auler } 118*821480d2SRafael Auler 119*821480d2SRafael Auler static uint64_t __munmap(void *addr, uint64_t size) { 120*821480d2SRafael Auler uint64_t ret; 121*821480d2SRafael Auler __asm__ __volatile__ ( 122*821480d2SRafael Auler "movq $11, %%rax\n" 123*821480d2SRafael Auler "syscall\n" 124*821480d2SRafael Auler : "=a"(ret) 125*821480d2SRafael Auler : "D"(addr), "S"(size) 126*821480d2SRafael Auler : "cc", "rcx", "r11", "memory"); 127*821480d2SRafael Auler return ret; 128*821480d2SRafael Auler } 129*821480d2SRafael Auler 130*821480d2SRafael Auler static uint64_t __exit(uint64_t code) { 131*821480d2SRafael Auler uint64_t ret; 132*821480d2SRafael Auler __asm__ __volatile__ ( 133*821480d2SRafael Auler "movq $231, %%rax\n" 134*821480d2SRafael Auler "syscall\n" 135*821480d2SRafael Auler : "=a"(ret) 136*821480d2SRafael Auler : "D"(code) 137*821480d2SRafael Auler : "cc", "rcx", "r11", "memory"); 138*821480d2SRafael Auler return ret; 139*821480d2SRafael Auler } 140*821480d2SRafael Auler 141*821480d2SRafael Auler // Helper functions for writing strings to the .fdata file 142*821480d2SRafael Auler 143*821480d2SRafael Auler // Write number Num using Base to the buffer in OutBuf, returns a pointer to 144*821480d2SRafael Auler // the end of the string. 14562aa74f8SRafael Auler static char *intToStr(char *OutBuf, uint32_t Num, uint32_t Base) { 14662aa74f8SRafael Auler const char *Chars = "0123456789abcdef"; 14762aa74f8SRafael Auler char Buf[20]; 14862aa74f8SRafael Auler char *Ptr = Buf; 14962aa74f8SRafael Auler while (Num) { 15062aa74f8SRafael Auler *Ptr++ = *(Chars + (Num % Base)); 15162aa74f8SRafael Auler Num /= Base; 15262aa74f8SRafael Auler } 15362aa74f8SRafael Auler if (Ptr == Buf) { 15462aa74f8SRafael Auler *OutBuf++ = '0'; 15562aa74f8SRafael Auler return OutBuf; 15662aa74f8SRafael Auler } 15762aa74f8SRafael Auler while (Ptr != Buf) { 15862aa74f8SRafael Auler *OutBuf++ = *--Ptr; 15962aa74f8SRafael Auler } 16062aa74f8SRafael Auler return OutBuf; 16162aa74f8SRafael Auler } 16262aa74f8SRafael Auler 163*821480d2SRafael Auler // Copy Str to OutBuf, returns a pointer to the end of the copied string. 164*821480d2SRafael Auler static char *strCopy(char *OutBuf, const char *Str) { 165*821480d2SRafael Auler while (*Str) 166*821480d2SRafael Auler *OutBuf++ = *Str++; 167*821480d2SRafael Auler return OutBuf; 168*821480d2SRafael Auler } 169*821480d2SRafael Auler 170*821480d2SRafael Auler // Print Msg to STDERR and quits with error code 1. 171*821480d2SRafael Auler static void reportError(const char *Msg, uint64_t Size) { 172*821480d2SRafael Auler __write(2, Msg, Size); 173*821480d2SRafael Auler __exit(1); 174*821480d2SRafael Auler } 175*821480d2SRafael Auler 176*821480d2SRafael Auler // Perform a string comparison and returns zero if Str1 matches Str2. Compares 177*821480d2SRafael Auler // at most Size characters. 178*821480d2SRafael Auler static int compareStr(const char *Str1, const char *Str2, int Size) { 179*821480d2SRafael Auler while (*Str1 == *Str2) { 180*821480d2SRafael Auler if (*Str1 == '\0' || --Size == 0) 181*821480d2SRafael Auler return 0; 182*821480d2SRafael Auler ++Str1; 183*821480d2SRafael Auler ++Str2; 184*821480d2SRafael Auler } 185*821480d2SRafael Auler return 1; 186*821480d2SRafael Auler } 187*821480d2SRafael Auler 188*821480d2SRafael Auler // Write as a string in OutBuf an identifier for the program point at function 189*821480d2SRafael Auler // whose name is in the string table index FuncStrIndex plus Offset. 190*821480d2SRafael Auler static char *serializeLoc(const InstrumentationInfo &Info, char *OutBuf, 191*821480d2SRafael Auler const Location Loc) { 192*821480d2SRafael Auler // fdata location format: Type Name Offset 193*821480d2SRafael Auler // Type 1 - regular symbol 194*821480d2SRafael Auler OutBuf = strCopy(OutBuf, "1 "); 195*821480d2SRafael Auler const char *Str = Info.Strings + Loc.FunctionName; 19662aa74f8SRafael Auler while (*Str) { 19762aa74f8SRafael Auler *OutBuf++ = *Str++; 19862aa74f8SRafael Auler } 19962aa74f8SRafael Auler *OutBuf++ = ' '; 200*821480d2SRafael Auler OutBuf = intToStr(OutBuf, Loc.Offset, 16); 20162aa74f8SRafael Auler *OutBuf++ = ' '; 20262aa74f8SRafael Auler return OutBuf; 20362aa74f8SRafael Auler } 20462aa74f8SRafael Auler 205*821480d2SRafael Auler // Read and map to memory the descriptions written by BOLT into the executable's 206*821480d2SRafael Auler // notes section 207*821480d2SRafael Auler static InstrumentationInfo readDescriptions() { 208*821480d2SRafael Auler InstrumentationInfo Result; 209*821480d2SRafael Auler uint64_t FD = __open("/proc/self/exe", 210*821480d2SRafael Auler /*flags=*/0 /*O_RDONLY*/, 211*821480d2SRafael Auler /*mode=*/0666); 212*821480d2SRafael Auler Result.FileDesc = FD; 213*821480d2SRafael Auler 214*821480d2SRafael Auler // mmap our binary to memory 215*821480d2SRafael Auler uint64_t Size = __lseek(FD, 0, 2 /*SEEK_END*/); 216*821480d2SRafael Auler uint8_t *BinContents = reinterpret_cast<uint8_t *>( 217*821480d2SRafael Auler __mmap(0, Size, 0x1 /* PROT_READ*/, 0x2 /* MAP_PRIVATE*/, FD, 0)); 218*821480d2SRafael Auler Result.MMapPtr = BinContents; 219*821480d2SRafael Auler Result.MMapSize = Size; 220*821480d2SRafael Auler Elf64_Ehdr *Hdr = reinterpret_cast<Elf64_Ehdr *>(BinContents); 221*821480d2SRafael Auler Elf64_Shdr *Shdr = reinterpret_cast<Elf64_Shdr *>(BinContents + Hdr->e_shoff); 222*821480d2SRafael Auler Elf64_Shdr *StringTblHeader = reinterpret_cast<Elf64_Shdr *>( 223*821480d2SRafael Auler BinContents + Hdr->e_shoff + Hdr->e_shstrndx * Hdr->e_shentsize); 224*821480d2SRafael Auler 225*821480d2SRafael Auler // Find .bolt.instr.tables with the data we need and set pointers to it 226*821480d2SRafael Auler for (int I = 0; I < Hdr->e_shnum; ++I) { 227*821480d2SRafael Auler char *SecName = reinterpret_cast<char *>( 228*821480d2SRafael Auler BinContents + StringTblHeader->sh_offset + Shdr->sh_name); 229*821480d2SRafael Auler if (compareStr(SecName, ".bolt.instr.tables", 64) != 0) { 230*821480d2SRafael Auler Shdr = reinterpret_cast<Elf64_Shdr *>(BinContents + Hdr->e_shoff + 231*821480d2SRafael Auler (I + 1) * Hdr->e_shentsize); 232*821480d2SRafael Auler continue; 233*821480d2SRafael Auler } 234*821480d2SRafael Auler // Actual contents of the ELF note start after offset 20 decimal: 235*821480d2SRafael Auler // Offset 0: Producer name size (4 bytes) 236*821480d2SRafael Auler // Offset 4: Contents size (4 bytes) 237*821480d2SRafael Auler // Offset 8: Note type (4 bytes) 238*821480d2SRafael Auler // Offset 12: Producer name (BOLT\0) (5 bytes + align to 4-byte boundary) 239*821480d2SRafael Auler // Offset 20: Contents 240*821480d2SRafael Auler Result.Descriptions = 241*821480d2SRafael Auler reinterpret_cast<EdgeDescription *>(BinContents + Shdr->sh_offset + 20); 242*821480d2SRafael Auler // String table is located after the full EdgeDescriptions table containing 243*821480d2SRafael Auler // __bolt_instr_num_locs entries is finished 244*821480d2SRafael Auler Result.Strings = reinterpret_cast<char *>( 245*821480d2SRafael Auler BinContents + Shdr->sh_offset + 20 + 246*821480d2SRafael Auler (__bolt_instr_num_locs * sizeof(EdgeDescription))); 247*821480d2SRafael Auler return Result; 248*821480d2SRafael Auler } 249*821480d2SRafael Auler const char ErrMsg[] = 250*821480d2SRafael Auler "BOLT instrumentation runtime error: could not find section " 251*821480d2SRafael Auler ".bolt.instr.tables\n"; 252*821480d2SRafael Auler reportError(ErrMsg, sizeof(ErrMsg)); 253*821480d2SRafael Auler return Result; 254*821480d2SRafael Auler } 255*821480d2SRafael Auler 256*821480d2SRafael Auler // This is the entry point called at program exit. BOLT patches the executable's 257*821480d2SRafael Auler // FINI entry in the .dynamic section with the address of this function. Our 258*821480d2SRafael Auler // goal here is to flush to disk all instrumentation data in memory, using 259*821480d2SRafael Auler // BOLT's fdata format. 26062aa74f8SRafael Auler extern "C" void __bolt_instr_data_dump() { 261*821480d2SRafael Auler const InstrumentationInfo Info = readDescriptions(); 262*821480d2SRafael Auler 263*821480d2SRafael Auler uint64_t FD = __open(__bolt_instr_filename, 26462aa74f8SRafael Auler /*flags=*/0x241 /*O_WRONLY|O_TRUNC|O_CREAT*/, 26562aa74f8SRafael Auler /*mode=*/0666); 26662aa74f8SRafael Auler 26762aa74f8SRafael Auler for (int I = 0, E = __bolt_instr_num_locs; I < E; ++I) { 26862aa74f8SRafael Auler char LineBuf[2000]; 26962aa74f8SRafael Auler char *Ptr = LineBuf; 27062aa74f8SRafael Auler uint32_t HitCount = __bolt_instr_locations[I]; 27162aa74f8SRafael Auler if (!HitCount) 27262aa74f8SRafael Auler continue; 27362aa74f8SRafael Auler 274*821480d2SRafael Auler EdgeDescription *Desc = &Info.Descriptions[I]; 275*821480d2SRafael Auler Ptr = serializeLoc(Info, Ptr, Desc->From); 276*821480d2SRafael Auler Ptr = serializeLoc(Info, Ptr, Desc->To); 277*821480d2SRafael Auler Ptr = strCopy(Ptr, "0 "); 27862aa74f8SRafael Auler Ptr = intToStr(Ptr, HitCount, 10); 27962aa74f8SRafael Auler *Ptr++ = '\n'; 280*821480d2SRafael Auler __write(FD, LineBuf, Ptr - LineBuf); 28162aa74f8SRafael Auler } 282*821480d2SRafael Auler __close(FD); 283*821480d2SRafael Auler __munmap(Info.MMapPtr, Info.MMapSize); 284*821480d2SRafael Auler __close(Info.FileDesc); 28562aa74f8SRafael Auler } 286