Default to AT&T disassembly syntax
[hiphop-php.git] / hphp / tools / tc-print / offline-x86-code.cpp
blob352bfe131640eab17e7512f584958417def0db02
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2015 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
16 #include "hphp/tools/tc-print/offline-x86-code.h"
18 #include <stdio.h>
19 #include <cxxabi.h>
20 #include <vector>
21 #include <assert.h>
22 #include <iomanip>
23 #include <sys/stat.h>
25 #include "hphp/tools/tc-print/tc-print.h"
26 #include "hphp/tools/tc-print/offline-trans-data.h"
28 #define MAX_INSTR_ASM_LEN 128
29 #define MAX_SYM_LEN 10240
31 using std::string;
32 using std::vector;
33 using std::pair;
35 namespace HPHP { namespace jit {
37 string TCRegionString[] = {
38 "hot", "main", "profile", "cold", "frozen"
41 static string helpersMapFileName("/tc_dump_helpers_addrs.txt");
42 static string nmMapFileName("/hhvm.nm");
43 static string tcRegionFileNames[TCRCount] = { "/tc_dump_ahot",
44 "/tc_dump_a",
45 "/tc_dump_aprof",
46 "/tc_dump_acold",
47 "/tc_dump_afrozen" };
49 static size_t fileSize(FILE* f) {
50 auto fd = fileno(f);
51 struct stat st;
52 fstat(fd, &st);
53 return st.st_size;
56 void OfflineX86Code::openFiles(TCA tcRegionBases[TCRCount]) {
58 for (size_t i = 0; i < TCRCount; i++) {
59 string fileName = dumpDir + tcRegionFileNames[i];
60 tcRegions[i].file = fopen(fileName.c_str(), "rb");
61 if (!tcRegions[i].file) {
62 for (size_t o = 0; o < i; o++) {
63 fclose(tcRegions[o].file);
65 error("Error opening file " + fileName);
67 tcRegions[i].baseAddr = tcRegionBases[i];
68 tcRegions[i].len = fileSize(tcRegions[i].file);
72 void OfflineX86Code::closeFiles() {
73 for (size_t i = 0; i < TCRCount; i++) {
74 fclose(tcRegions[i].file);
78 bool OfflineX86Code::tcRegionContains(TCRegion tcr, TCA addr) const {
79 assert(tcr >= 0 && tcr < TCRCount);
80 return (addr >= tcRegions[tcr].baseAddr &&
81 addr < tcRegions[tcr].baseAddr + tcRegions[tcr].len);
84 // Returns TCRegion containing addr if any, TCRCount otherwise.
85 TCRegion OfflineX86Code::findTCRegionContaining(TCA addr) const {
86 for (int tcr = 0; tcr < TCRCount; tcr++) {
87 if (tcRegionContains((TCRegion)tcr, addr)) return (TCRegion)tcr;
89 return TCRCount;
92 void OfflineX86Code::xedInit() {
93 xed_state_init(&xed_state, XED_MACHINE_MODE_LONG_64,
94 XED_ADDRESS_WIDTH_64b, XED_ADDRESS_WIDTH_64b);
95 xed_tables_init();
96 xed_syntax = getenv("HHVM_INTEL_DISAS") ? XED_SYNTAX_INTEL : XED_SYNTAX_ATT;
100 TCA OfflineX86Code::getTransJmpTargets(const TransRec *transRec,
101 vector<TCA> *jmpTargets) {
103 TCRegion tcrMain = findTCRegionContaining(transRec->aStart);
105 assert(tcrMain == TCRHot || tcrMain == TCRMain || tcrMain == TCRProfile);
107 TCA aFallThru = collectJmpTargets(tcRegions[tcrMain].file,
108 tcRegions[tcrMain].baseAddr,
109 transRec->aStart, transRec->aLen,
110 jmpTargets);
112 collectJmpTargets(tcRegions[TCRCold].file,
113 tcRegions[TCRCold].baseAddr,
114 transRec->acoldStart, transRec->acoldLen, jmpTargets);
116 collectJmpTargets(tcRegions[TCRFrozen].file,
117 tcRegions[TCRFrozen].baseAddr,
118 transRec->afrozenStart, transRec->afrozenLen, jmpTargets);
120 return aFallThru;
123 TCA OfflineX86Code::collectJmpTargets(FILE *file,
124 TCA fileStartAddr,
125 TCA codeStartAddr,
126 uint64_t codeLen,
127 vector<TCA> *jmpTargets) {
129 xed_uint8_t* code = (xed_uint8_t*) alloca(codeLen);
130 xed_uint8_t* frontier;
131 TCA ip;
133 if (codeLen == 0) return 0;
135 if (fseek(file, codeStartAddr - fileStartAddr, SEEK_SET)) {
136 error("collectJmpTargets error: seeking file");
139 size_t readLen = fread(code, codeLen, 1, file);
140 if (readLen != 1) error("collectJmpTargets error: reading file");
142 xed_decoded_inst_t xedd;
143 xed_iclass_enum_t iclass = XED_ICLASS_NOP;
145 // Decode each instruction
146 for (frontier = code, ip = codeStartAddr; frontier < code + codeLen; ) {
148 xed_decoded_inst_zero_set_mode(&xedd, &xed_state);
149 xed_decoded_inst_set_input_chip(&xedd, XED_CHIP_INVALID);
150 xed_error_enum_t xed_error = xed_decode(&xedd, frontier, 15);
152 if (xed_error != XED_ERROR_NONE) break;
154 uint32_t instrLen = xed_decoded_inst_get_length(&xedd);
156 iclass = xed_decoded_inst_get_iclass(&xedd);
158 if (iclass >= XED_ICLASS_JB && iclass <= XED_ICLASS_JZ) {
159 const xed_inst_t *xi = xed_decoded_inst_inst(&xedd);
160 always_assert(xed_inst_noperands(xi) >= 1);
161 const xed_operand_t *opnd = xed_inst_operand(xi, 0);
162 xed_operand_enum_t opndName = xed_operand_name(opnd);
164 if (opndName == XED_OPERAND_RELBR) {
165 always_assert(xed_decoded_inst_get_branch_displacement_width(&xedd));
166 xed_int32_t disp = xed_decoded_inst_get_branch_displacement(&xedd);
167 TCA addr = ip + instrLen + disp;
168 jmpTargets->push_back(addr);
172 frontier += instrLen;
173 ip += instrLen;
176 // If the code sequence falls thru, then add the next instruction as a
177 // possible target
178 bool fallsThru = (iclass != XED_ICLASS_JMP &&
179 iclass != XED_ICLASS_JMP_FAR &&
180 iclass != XED_ICLASS_RET_NEAR &&
181 iclass != XED_ICLASS_RET_FAR);
182 if (fallsThru) {
183 jmpTargets->push_back(ip);
184 return ip;
186 return 0;
189 void OfflineX86Code::printDisasm(TCA startAddr, uint32_t len,
190 const vector<TransBCMapping>& bcMap,
191 const PerfEventsMap<TCA>& perfEvents) {
192 TCRegion tcr = findTCRegionContaining(startAddr);
193 disasm(tcRegions[tcr].file, tcRegions[tcr].baseAddr, startAddr, len,
194 perfEvents, BCMappingInfo(tcr, bcMap));
197 // Disassemble the code from the given raw file, whose initial address is given
198 // by fileStartAddr, for the address range given by
199 // [codeStartAddr, codeStartAddr + codeLen)
201 void OfflineX86Code::disasm(FILE* file,
202 TCA fileStartAddr,
203 TCA codeStartAddr,
204 uint64_t codeLen,
205 const PerfEventsMap<TCA>& perfEvents,
206 BCMappingInfo bcMappingInfo,
207 bool printAddr /* =true */,
208 bool printBinary /* =false */) {
210 char codeStr[MAX_INSTR_ASM_LEN];
211 xed_uint8_t* code = (xed_uint8_t*) alloca(codeLen);
212 xed_uint8_t* frontier;
213 TCA ip;
214 TCA r10val = 0;
215 size_t currBC = 0;
217 if (codeLen == 0) return;
219 if (fseek(file, codeStartAddr - fileStartAddr, SEEK_SET)) {
220 error("disasm error: seeking file");
223 size_t readLen = fread(code, codeLen, 1, file);
224 if (readLen != 1) error("disasm error: reading file");
226 xed_decoded_inst_t xedd;
228 // Decode and print each instruction
229 for (frontier = code, ip = codeStartAddr; frontier < code + codeLen; ) {
231 xed_decoded_inst_zero_set_mode(&xedd, &xed_state);
232 xed_decoded_inst_set_input_chip(&xedd, XED_CHIP_INVALID);
233 xed_error_enum_t xed_error = xed_decode(&xedd, frontier, 15);
235 if (xed_error != XED_ERROR_NONE) break;
237 // Get disassembled instruction in codeStr
238 if (!xed_format_context(xed_syntax, &xedd, codeStr,
239 MAX_INSTR_ASM_LEN, (uint64_t)ip, nullptr)) {
240 error("disasm error: xed_format_context failed");
243 // Annotate the x86 with its bytecode.
244 currBC = printBCMapping(bcMappingInfo, currBC, (TCA)ip);
246 if (printAddr) printf("%14p: ", ip);
248 uint32_t instrLen = xed_decoded_inst_get_length(&xedd);
250 if (printBinary) {
251 uint32_t i;
252 for (i=0; i < instrLen; i++) {
253 printf("%02X", frontier[i]);
255 for (; i < 16; i++) {
256 printf(" ");
260 // For calls, we try to figure out the destination symbol name.
261 // We look both at relative branches and the pattern:
262 // move r10, IMMEDIATE
263 // call r10
264 xed_iclass_enum_t iclass = xed_decoded_inst_get_iclass(&xedd);
265 string callDest = "";
267 if (iclass == XED_ICLASS_CALL_NEAR || iclass == XED_ICLASS_CALL_FAR) {
268 const xed_inst_t *xi = xed_decoded_inst_inst(&xedd);
269 always_assert(xed_inst_noperands(xi) >= 1);
270 const xed_operand_t *opnd = xed_inst_operand(xi, 0);
271 xed_operand_enum_t opndName = xed_operand_name(opnd);
273 if (opndName == XED_OPERAND_RELBR) {
274 if (xed_decoded_inst_get_branch_displacement_width(&xedd)) {
275 xed_int32_t disp = xed_decoded_inst_get_branch_displacement(&xedd);
276 TCA addr = ip + instrLen + disp;
277 callDest = getSymbolName(addr);
279 } else if (opndName == XED_OPERAND_REG0) {
280 if (xed_decoded_inst_get_reg(&xedd, opndName) == XED_REG_R10) {
281 callDest = getSymbolName(r10val);
284 } else if (iclass == XED_ICLASS_MOV) {
285 // Look for moves into r10 and keep r10val updated
286 const xed_inst_t* xi = xed_decoded_inst_inst(&xedd);
288 always_assert(xed_inst_noperands(xi) >= 2);
290 const xed_operand_t *destOpnd = xed_inst_operand(xi, 0);
291 xed_operand_enum_t destOpndName = xed_operand_name(destOpnd);
293 if (destOpndName == XED_OPERAND_REG0 &&
294 xed_decoded_inst_get_reg(&xedd, destOpndName) == XED_REG_R10) {
295 const xed_operand_t *srcOpnd = xed_inst_operand(xi, 1);
296 xed_operand_enum_t srcOpndName = xed_operand_name(srcOpnd);
297 if (srcOpndName == XED_OPERAND_IMM0) {
298 TCA addr = (TCA)xed_decoded_inst_get_unsigned_immediate(&xedd);
299 r10val = addr;
304 if (!perfEvents.empty()) {
305 printEventStats((TCA)ip, instrLen, perfEvents);
306 } else {
307 printf("%48s", "");
309 printf("%s%s\n", codeStr, callDest.c_str());
311 frontier += instrLen;
312 ip += instrLen;
316 void OfflineX86Code::loadSymbolsMap() {
317 loadSymbolsMapNm();
318 loadSymbolsMapTramp();
322 void OfflineX86Code::loadSymbolsMapTramp() {
323 FILE* helpersMapFile;
325 string helpersFileName = dumpDir + helpersMapFileName;
326 helpersMapFile = fopen(helpersFileName.c_str(), "rt");
328 if (!helpersMapFile) return;
330 TCA trampAddr, helperAddr;
331 char symName[MAX_SYM_LEN];
332 uint32_t count=0;
334 while (fscanf(helpersMapFile, "%p %p ", (void**)&trampAddr,
335 (void**)&helperAddr) == 2) {
336 if (fgets(symName, MAX_SYM_LEN, helpersMapFile) == nullptr) break;
338 // remove trailing '\n'
339 size_t symLen = strlen(symName);
340 if (symLen && symName[symLen - 1] == '\n') {
341 symName[symLen - 1] = 0;
343 string strSymName = symName;
344 addr2SymMap[trampAddr] = strSymName;
345 addr2SymMap[helperAddr] = strSymName;
346 count++;
349 printf("# Read %u symbols from file %s\n", count, helpersFileName.c_str());
351 fclose(helpersMapFile);
355 void OfflineX86Code::loadSymbolsMapNm() {
356 FILE* nmMapFile;
358 string nmFileName = dumpDir + nmMapFileName;
359 nmMapFile = fopen(nmFileName.c_str(), "rt");
361 if (!nmMapFile) return;
363 TCA symAddr;
364 char symName[MAX_SYM_LEN], line[2*MAX_SYM_LEN];
365 uint32_t count=0;
367 while (fgets(line, 2*MAX_SYM_LEN, nmMapFile) != nullptr) {
368 if (sscanf(line, "%p %*s %s", &symAddr, symName) == 2) {
370 int status;
371 char* demangledName = abi::__cxa_demangle(symName, 0, 0, &status);
372 if (demangledName) {
373 addr2SymMap[symAddr] = string(demangledName);
374 free(demangledName);
375 } else {
376 addr2SymMap[symAddr] = string(symName);
379 count++;
381 printf("# Read %u symbols from file %s\n", count, nmFileName.c_str());
383 fclose(nmMapFile);
387 // Returns the name of the symbol of the given address if available, otherwise
388 // just returns the address
389 string OfflineX86Code::getSymbolName(TCA addr) {
390 string sym;
391 auto it = addr2SymMap.find(addr);
392 if (it != addr2SymMap.end()) {
393 sym = " # " + it->second;
394 } else {
395 char addrStr[50];
396 sprintf(addrStr, "%p", addr);
397 sym = " # SYMBOL @ " + string(addrStr);
399 return sym;
402 size_t OfflineX86Code::printBCMapping(BCMappingInfo bcMappingInfo,
403 size_t currBC,
404 TCA ip) {
406 TransBCMapping curr, next;
407 TCA x86Start, x86Stop;
408 auto const& bcMap = bcMappingInfo.bcMapping;
410 curr = next = TransBCMapping { MD5(), 0, 0, 0, 0 };
411 x86Start = x86Stop = 0;
413 // Account for the sentinel.
414 size_t mappingSize = bcMap.size() - 1;
416 // Starting from currBC, find the next bytecode with a non-empty x86 range
417 // that could potentially correspond to instruction ip.
418 for (; currBC < mappingSize; ++currBC) {
419 curr = bcMap[currBC];
420 next = bcMap[currBC + 1];
422 switch (bcMappingInfo.tcRegion) {
423 case TCRHot:
424 case TCRMain:
425 case TCRProfile:
426 x86Start = curr.aStart;
427 x86Stop = next.aStart;
428 break;
429 case TCRCold:
430 x86Start = curr.acoldStart;
431 x86Stop = next.acoldStart;
432 break;
433 case TCRFrozen:
434 x86Start = curr.afrozenStart;
435 x86Stop = next.afrozenStart;
436 break;
437 default:
438 error("printBCMapping: unexpected TCRegion");
441 always_assert(x86Start <= x86Stop);
442 if (x86Start >= ip && x86Start < x86Stop) break;
445 if (currBC < mappingSize && x86Start == ip) {
446 if (auto currUnit = g_repo->getUnit(curr.md5)) {
447 auto bcPast = curr.bcStart + instrLen(currUnit->at(curr.bcStart));
449 currUnit->prettyPrint(std::cout,
450 Unit::PrintOpts().range(curr.bcStart,
451 bcPast));
452 } else {
453 std::cout << folly::format(
454 "<<< couldn't find unit {} to print bytecode at offset {} >>>\n",
455 curr.md5, curr.bcStart);
458 currBC++;
461 return currBC;
464 void OfflineX86Code::printEventStats(TCA address,
465 uint32_t instrLen,
466 const PerfEventsMap<TCA>& perfEvents) {
467 static const PerfEventType AnnotatedEvents[] = {
468 EVENT_CYCLES,
469 EVENT_BRANCH_MISSES,
470 EVENT_ICACHE_MISSES,
471 EVENT_DCACHE_MISSES,
472 EVENT_LLC_MISSES,
473 EVENT_ITLB_MISSES,
474 EVENT_DTLB_MISSES,
477 const size_t NumAnnotatedEvents =
478 sizeof(AnnotatedEvents) / sizeof(AnnotatedEvents[0]);
480 static const char* SmallCaptions[] = {"cy", "bm", "ic", "dc", "lc", "it",
481 "dt"};
483 assert(sizeof(SmallCaptions)/sizeof(SmallCaptions[0]) == NumAnnotatedEvents);
485 for (size_t i = 0; i < NumAnnotatedEvents; i++) {
486 uint64_t eventCount = perfEvents.getEventCount(address,
487 address + instrLen - 1,
488 AnnotatedEvents[i]);
489 std::string eventStr;
490 if (eventCount) {
491 eventStr = folly::format("{:>3}:{:>4}",
492 SmallCaptions[i], eventCount).str();
494 std::cout << folly::format("{:<10} ", eventStr);
498 } } // HPHP::jit