2 +----------------------------------------------------------------------+
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"
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
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",
49 static size_t fileSize(FILE* f
) {
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
;
92 void OfflineX86Code::xedInit() {
93 xed_state_init(&xed_state
, XED_MACHINE_MODE_LONG_64
,
94 XED_ADDRESS_WIDTH_64b
, XED_ADDRESS_WIDTH_64b
);
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
,
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
);
123 TCA
OfflineX86Code::collectJmpTargets(FILE *file
,
127 vector
<TCA
> *jmpTargets
) {
129 xed_uint8_t
* code
= (xed_uint8_t
*) alloca(codeLen
);
130 xed_uint8_t
* frontier
;
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
;
176 // If the code sequence falls thru, then add the next instruction as a
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
);
183 jmpTargets
->push_back(ip
);
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
,
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
;
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
);
252 for (i
=0; i
< instrLen
; i
++) {
253 printf("%02X", frontier
[i
]);
255 for (; i
< 16; i
++) {
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
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
);
304 if (!perfEvents
.empty()) {
305 printEventStats((TCA
)ip
, instrLen
, perfEvents
);
309 printf("%s%s\n", codeStr
, callDest
.c_str());
311 frontier
+= instrLen
;
316 void OfflineX86Code::loadSymbolsMap() {
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
];
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
;
349 printf("# Read %u symbols from file %s\n", count
, helpersFileName
.c_str());
351 fclose(helpersMapFile
);
355 void OfflineX86Code::loadSymbolsMapNm() {
358 string nmFileName
= dumpDir
+ nmMapFileName
;
359 nmMapFile
= fopen(nmFileName
.c_str(), "rt");
361 if (!nmMapFile
) return;
364 char symName
[MAX_SYM_LEN
], line
[2*MAX_SYM_LEN
];
367 while (fgets(line
, 2*MAX_SYM_LEN
, nmMapFile
) != nullptr) {
368 if (sscanf(line
, "%p %*s %s", &symAddr
, symName
) == 2) {
371 char* demangledName
= abi::__cxa_demangle(symName
, 0, 0, &status
);
373 addr2SymMap
[symAddr
] = string(demangledName
);
376 addr2SymMap
[symAddr
] = string(symName
);
381 printf("# Read %u symbols from file %s\n", count
, nmFileName
.c_str());
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
) {
391 auto it
= addr2SymMap
.find(addr
);
392 if (it
!= addr2SymMap
.end()) {
393 sym
= " # " + it
->second
;
396 sprintf(addrStr
, "%p", addr
);
397 sym
= " # SYMBOL @ " + string(addrStr
);
402 size_t OfflineX86Code::printBCMapping(BCMappingInfo bcMappingInfo
,
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
) {
426 x86Start
= curr
.aStart
;
427 x86Stop
= next
.aStart
;
430 x86Start
= curr
.acoldStart
;
431 x86Stop
= next
.acoldStart
;
434 x86Start
= curr
.afrozenStart
;
435 x86Stop
= next
.afrozenStart
;
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
,
453 std::cout
<< folly::format(
454 "<<< couldn't find unit {} to print bytecode at offset {} >>>\n",
455 curr
.md5
, curr
.bcStart
);
464 void OfflineX86Code::printEventStats(TCA address
,
466 const PerfEventsMap
<TCA
>& perfEvents
) {
467 static const PerfEventType AnnotatedEvents
[] = {
477 const size_t NumAnnotatedEvents
=
478 sizeof(AnnotatedEvents
) / sizeof(AnnotatedEvents
[0]);
480 static const char* SmallCaptions
[] = {"cy", "bm", "ic", "dc", "lc", "it",
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,
489 std::string eventStr
;
491 eventStr
= folly::format("{:>3}:{:>4}",
492 SmallCaptions
[i
], eventCount
).str();
494 std::cout
<< folly::format("{:<10} ", eventStr
);