2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present 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-code.h"
25 #include "hphp/tools/tc-print/tc-print.h"
26 #include "hphp/tools/tc-print/offline-trans-data.h"
29 #define MAX_SYM_LEN 10240
33 namespace HPHP
{ namespace jit
{
35 string TCRegionString
[] = {
36 "main", "cold", "frozen"
39 static string
nmMapFileName("/hhvm.nm");
40 static string tcRegionFileNames
[TCRCount
] = { "/tc_dump_a",
44 static size_t fileSize(FILE* f
) {
51 void OfflineCode::openFiles(TCA tcRegionBases
[TCRCount
]) {
53 for (size_t i
= 0; i
< TCRCount
; i
++) {
54 string fileName
= dumpDir
+ tcRegionFileNames
[i
];
55 tcRegions
[i
].file
= fopen(fileName
.c_str(), "rb");
56 if (!tcRegions
[i
].file
) {
57 for (size_t o
= 0; o
< i
; o
++) {
58 fclose(tcRegions
[o
].file
);
60 error("Error opening file " + fileName
);
62 tcRegions
[i
].baseAddr
= tcRegionBases
[i
];
63 tcRegions
[i
].len
= fileSize(tcRegions
[i
].file
);
67 void OfflineCode::closeFiles() {
68 for (size_t i
= 0; i
< TCRCount
; i
++) {
69 fclose(tcRegions
[i
].file
);
73 bool OfflineCode::tcRegionContains(TCRegion tcr
, TCA addr
) const {
74 assert(tcr
>= 0 && tcr
< TCRCount
);
75 return (addr
>= tcRegions
[tcr
].baseAddr
&&
76 addr
< tcRegions
[tcr
].baseAddr
+ tcRegions
[tcr
].len
);
79 // Returns TCRegion containing addr if any, TCRCount otherwise.
80 TCRegion
OfflineCode::findTCRegionContaining(TCA addr
) const {
81 for (int tcr
= 0; tcr
< TCRCount
; tcr
++) {
82 if (tcRegionContains((TCRegion
)tcr
, addr
)) return (TCRegion
)tcr
;
88 TCA
OfflineCode::getTransJmpTargets(const TransRec
*transRec
,
89 vector
<TCA
> *jmpTargets
) {
91 TCRegion tcrMain
= findTCRegionContaining(transRec
->aStart
);
93 assert(tcrMain
== TCRMain
);
95 TCA aFallThru
= collectJmpTargets(tcRegions
[tcrMain
].file
,
96 tcRegions
[tcrMain
].baseAddr
,
97 transRec
->aStart
, transRec
->aLen
,
100 // Sometimes acoldStart is the same as afrozenStart. In these cases, don't
101 // look up the address range in the "cold" file, since it the range isn't
103 if (transRec
->acoldStart
!= transRec
->afrozenStart
) {
104 collectJmpTargets(tcRegions
[TCRCold
].file
,
105 tcRegions
[TCRCold
].baseAddr
,
106 transRec
->acoldStart
, transRec
->acoldLen
, jmpTargets
);
109 collectJmpTargets(tcRegions
[TCRFrozen
].file
,
110 tcRegions
[TCRFrozen
].baseAddr
,
111 transRec
->afrozenStart
, transRec
->afrozenLen
, jmpTargets
);
116 void OfflineCode::printDisasm(std::ostream
& os
,
119 const vector
<TransBCMapping
>& bcMap
,
120 const PerfEventsMap
<TCA
>& perfEvents
,
122 TCRegion tcr
= findTCRegionContaining(startAddr
);
123 disasm(os
, tcRegions
[tcr
].file
, tcRegions
[tcr
].baseAddr
, startAddr
, len
,
124 perfEvents
, BCMappingInfo(tcr
, bcMap
), true, hostOpcodes
);
127 void OfflineCode::setAnnotationRanges(BCMappingInfo
& bc
, printir::Unit unit
) {
128 vector
<printir::TCRange
> annotations
;
130 for (auto const& block
: unit
.blocks
) {
131 for (auto const& instr
: block
.second
.instrs
) {
132 for (auto const& tcr
: instr
.tcRanges
) {
133 if (tcr
.start
!= nullptr &&
134 tcr
.end
!= nullptr &&
135 tcr
.start
!= tcr
.end
) {
136 annotations
.push_back(tcr
);
142 std::sort(annotations
.begin(),
144 [](const printir::TCRange
& a
, const printir::TCRange
& b
) {
145 return a
.start
< b
.start
;
148 bc
.annotations
= annotations
;
151 folly::dynamic
OfflineCode::getDisasm(TCA startAddr
,
153 const vector
<TransBCMapping
>& bcMap
,
154 const PerfEventsMap
<TCA
>& perfEvents
,
156 Optional
<printir::Unit
> unit
) {
157 auto const tcr
= findTCRegionContaining(startAddr
);
158 auto mappingInfo
= BCMappingInfo(tcr
, bcMap
);
160 if (unit
) setAnnotationRanges(mappingInfo
, *unit
);
162 auto const regionInfo
= getRegionInfo(tcRegions
[tcr
].file
,
163 tcRegions
[tcr
].baseAddr
,
168 return regionInfo
.toDynamic();
171 void OfflineCode::loadSymbolsMap() {
174 string nmFileName
= dumpDir
+ nmMapFileName
;
175 nmMapFile
= fopen(nmFileName
.c_str(), "rt");
177 if (!nmMapFile
) return;
180 char symName
[MAX_SYM_LEN
], line
[2*MAX_SYM_LEN
];
183 while (fgets(line
, 2*MAX_SYM_LEN
, nmMapFile
) != nullptr) {
184 if (sscanf(line
, "%p %*s %s", &symAddr
, symName
) == 2) {
187 char* demangledName
= abi::__cxa_demangle(symName
, 0, 0, &status
);
189 addr2SymMap
[symAddr
] = string(demangledName
);
192 addr2SymMap
[symAddr
] = string(symName
);
197 printf("# Read %u symbols from file %s\n", count
, nmFileName
.c_str());
203 // Returns the name of the symbol of the given address if available, otherwise
204 // just returns the address
205 string
OfflineCode::getSymbolName(TCA addr
) {
207 auto it
= addr2SymMap
.find(addr
);
208 if (it
!= addr2SymMap
.end()) {
209 sym
= " # " + it
->second
;
212 sprintf(addrStr
, "%p", addr
);
213 sym
= " # SYMBOL @ " + string(addrStr
);
218 TCA
OfflineCode::getRegionStart(TCRegion region
, TransBCMapping transBCMap
) {
221 return transBCMap
.aStart
;
223 return transBCMap
.acoldStart
;
225 return transBCMap
.afrozenStart
;
227 error("printBCMapping: unexpected TCRegion");
229 always_assert(false);
232 void OfflineCode::printEventStats(std::ostream
& os
,
233 EventCounts events
) {
234 if (events
.empty()) {
235 os
<< string(48, ' ');
238 for (int i
= 0; i
< events
.size(); i
++) {
239 auto const event
= static_cast<PerfEventType
>(i
);
240 auto const count
= events
[i
];
241 auto const eventStr
= count
?
242 folly::sformat("{:>3}:{:>4}",
243 eventTypeToSmallCaption(event
),
246 os
<< folly::format("{:<10} ", eventStr
);
250 EventCounts
OfflineCode::getEventCounts(TCA address
,
252 const PerfEventsMap
<TCA
>& perfEvents
) {
253 if (perfEvents
.empty()) return EventCounts();
255 auto const numEvents
= getNumEventTypes();
256 EventCounts
eventCounts(numEvents
);
257 for (int i
= 0; i
< numEvents
; i
++) {
258 auto const event
= static_cast<PerfEventType
>(i
);
259 auto const eventCount
= perfEvents
.getEventCount(address
,
260 address
+ instrLen
- 1,
262 eventCounts
[i
] = eventCount
;
267 void OfflineCode::disasm(std::ostream
& os
,
272 const PerfEventsMap
<TCA
>& perfEvents
,
273 OfflineCode::BCMappingInfo bcMappingInfo
,
277 auto const regionInfo
= getRegionInfo(file
,
284 for (auto const& rangeInfo
: regionInfo
.ranges
) {
285 printRangeInfo(os
, rangeInfo
, printAddr
, printBinary
);
289 void OfflineCode::printRangeInfo(std::ostream
& os
,
290 const TCRangeInfo
& rangeInfo
,
291 const bool printAddr
,
292 const bool printBinary
) {
293 if (rangeInfo
.disasm
.empty()) return;
294 if (rangeInfo
.sk
&& rangeInfo
.disasm
[0].ip
== rangeInfo
.start
) {
295 auto const sk
= *rangeInfo
.sk
;
297 os
<< std::setw(4) << sk
.printableOffset() << ": "
298 << sk
.showInst() << std::endl
;
300 auto const currSha1
= rangeInfo
.sha1
301 ? rangeInfo
.sha1
->toString() : "\"missing SHA1\"";
303 "<<< couldn't find unit {} to print bytecode at {} {} >>>\n",
305 sk
.prologue() || sk
.funcEntry() ? "numEntryArgs" : "offset",
306 sk
.prologue() || sk
.funcEntry()
307 ? sk
.numEntryArgs() : sk
.offset()
311 for (auto const& disasmInfo
: rangeInfo
.disasm
) {
312 printDisasmInfo(os
, disasmInfo
, printAddr
, printBinary
);
316 void OfflineCode::printDisasmInfo(std::ostream
& os
,
317 const TCDisasmInfo
& disasmInfo
,
318 const bool printAddr
,
319 const bool printBinary
) {
321 os
<< folly::format("{:>#14x}: ",
322 reinterpret_cast<uintptr_t>(disasmInfo
.ip
));
324 if (printBinary
) os
<< disasmInfo
.binaryStr
;
325 printEventStats(os
, disasmInfo
.eventCounts
);
326 os
<< folly::format("{}{}\n", disasmInfo
.codeStr
, disasmInfo
.callDest
);
329 vector
<TCRangeInfo
> annotateRanges(const vector
<TCRangeInfo
>& ranges
,
330 const vector
<printir::TCRange
>& annotations
){
331 if (ranges
.empty() || annotations
.empty()) return ranges
;
333 vector
<TCRangeInfo
> annotatedRanges
;
335 auto currRangeItr
= ranges
.begin();
336 TCRangeInfo lastRange
= *currRangeItr
;
337 auto const progressRangeItr
= [&]() {
338 if (currRangeItr
!= ranges
.end()) ++currRangeItr
;
339 if (currRangeItr
!= ranges
.end()) lastRange
= *currRangeItr
;
342 auto currAnnotItr
= annotations
.begin();
343 auto const progressAnnotItr
= [&](const TCA tcStart
) {
344 while (tcStart
>= currAnnotItr
->end
) {
345 if (currAnnotItr
== annotations
.end()) return;
350 while (currRangeItr
!= ranges
.end()) {
351 progressAnnotItr(lastRange
.start
);
352 if (currAnnotItr
== annotations
.end()) break;
354 auto const tcStart
= lastRange
.start
;
355 auto const tcEnd
= lastRange
.end
;
356 auto const annotStart
= currAnnotItr
->start
;
357 auto const annotEnd
= currAnnotItr
->end
;
359 if (tcStart
< annotStart
) {
360 if (tcEnd
<= annotStart
) {
361 // this range both starts and ends before our next annotation, so this
362 // range gets added annotationless
363 annotatedRanges
.push_back(lastRange
);
366 // the first part of this range happened before our next annotation, so
367 // split that part off and add it annotationless, then reprocess the
369 auto const splitTCRange
= lastRange
.split(annotStart
);
370 annotatedRanges
.push_back(splitTCRange
.first
);
371 lastRange
= splitTCRange
.second
;
374 if (tcEnd
<= annotEnd
) {
375 // this range ends before our next annotation, so this
376 // range gets added with the current annotation
377 lastRange
.annotation
= *currAnnotItr
;
378 annotatedRanges
.push_back(lastRange
);
381 // the range is split among multiple annotations, so split this first
382 // part off and annotate it, then process the rest
383 auto splitTCRange
= lastRange
.split(annotEnd
);
384 splitTCRange
.first
.annotation
= *currAnnotItr
;
385 annotatedRanges
.push_back(splitTCRange
.first
);
386 lastRange
= splitTCRange
.second
;
391 // Whatever ranges might be left after we're done with our annotations, make
392 // sure that we add those as well
393 while (currRangeItr
!= ranges
.end()) {
394 annotatedRanges
.push_back(lastRange
);
398 return annotatedRanges
;
402 OfflineCode::getRanges(const BCMappingInfo
& bcMappingInfo
,
405 auto const& bcMap
= bcMappingInfo
.bcMapping
;
406 auto const region
= bcMappingInfo
.tcRegion
;
407 auto const numRanges
= bcMap
.size();
408 vector
<TCRangeInfo
> ranges
;
410 // For "prologue" translations, we need another range in front of where the
412 auto const actualStart
= getRegionStart(region
, bcMap
[0]);
413 if (start
!= actualStart
) {
414 ranges
.push_back(TCRangeInfo
{start
, actualStart
});
417 for (int i
= 0; i
< numRanges
; i
++) {
418 auto const& curr
= bcMap
[i
];
420 auto const tcaStart
= getRegionStart(bcMappingInfo
.tcRegion
, curr
);
421 auto const tcaEnd
= (i
< numRanges
- 1) ?
422 getRegionStart(bcMappingInfo
.tcRegion
, bcMap
[i
+ 1]) :
423 end
; // use the provided end for the last element
424 if (tcaStart
!= tcaEnd
) {
425 ranges
.push_back(getRangeInfo(curr
, tcaStart
, tcaEnd
));
429 if (ranges
.empty() || bcMappingInfo
.annotations
.empty()) {
433 return annotateRanges(ranges
, bcMappingInfo
.annotations
);
436 TCRangeInfo
OfflineCode::getRangeInfo(const TransBCMapping
& transBCMap
,
439 TCRangeInfo rangeInfo
{start
, end
, transBCMap
.sk
, transBCMap
.sha1
};
440 auto const sk
= transBCMap
.sk
;
442 rangeInfo
.unit
= sk
.func()->unit();
443 rangeInfo
.func
= sk
.func();
444 auto const lineNum
= sk
.lineNumber();
445 if (lineNum
!= -1) rangeInfo
.lineNum
= lineNum
;
451 TCDisasmInfo
OfflineCode::getDisasmInfo(const TCA ip
,
452 const uint32_t instrLen
,
453 const PerfEventsMap
<TCA
>& perfEvents
,
454 const std::string
& binaryStr
,
455 const std::string
& callDest
,
456 const std::string
& codeStr
) {
457 auto const eventCounts
= getEventCounts(ip
, instrLen
, perfEvents
);
458 return TCDisasmInfo
{binaryStr
,
466 void OfflineCode::readDisasmFile(FILE* file
,
468 const uint64_t codeLen
,
470 if (fseek(file
, offset
, SEEK_SET
)) {
471 error("disasm error: seeking file");
474 size_t readLen
= fread(code
, codeLen
, 1, file
);
476 error("Failed to read {} bytes at offset {} from code file due to {}",
477 codeLen
, offset
, feof(file
) ? "EOF" : "read error");