Store num args instead of offset in prologue and func entry SrcKeys
[hiphop-php.git] / hphp / tools / tc-print / offline-code.cpp
blob35ba042a2e95f4d1a19bfdb878c76631735bff03
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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"
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"
29 #define MAX_SYM_LEN 10240
31 using std::string;
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",
41 "/tc_dump_acold",
42 "/tc_dump_afrozen" };
44 static size_t fileSize(FILE* f) {
45 auto fd = fileno(f);
46 struct stat st;
47 fstat(fd, &st);
48 return st.st_size;
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;
84 return TCRCount;
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,
98 jmpTargets);
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
102 // there.
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);
113 return aFallThru;
116 void OfflineCode::printDisasm(std::ostream& os,
117 TCA startAddr,
118 uint32_t len,
119 const vector<TransBCMapping>& bcMap,
120 const PerfEventsMap<TCA>& perfEvents,
121 bool hostOpcodes) {
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(),
143 annotations.end(),
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,
152 uint32_t len,
153 const vector<TransBCMapping>& bcMap,
154 const PerfEventsMap<TCA>& perfEvents,
155 bool hostOpcodes,
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,
164 startAddr,
165 len,
166 perfEvents,
167 mappingInfo);
168 return regionInfo.toDynamic();
171 void OfflineCode::loadSymbolsMap() {
172 FILE* nmMapFile;
174 string nmFileName = dumpDir + nmMapFileName;
175 nmMapFile = fopen(nmFileName.c_str(), "rt");
177 if (!nmMapFile) return;
179 TCA symAddr;
180 char symName[MAX_SYM_LEN], line[2*MAX_SYM_LEN];
181 uint32_t count=0;
183 while (fgets(line, 2*MAX_SYM_LEN, nmMapFile) != nullptr) {
184 if (sscanf(line, "%p %*s %s", &symAddr, symName) == 2) {
186 int status;
187 char* demangledName = abi::__cxa_demangle(symName, 0, 0, &status);
188 if (demangledName) {
189 addr2SymMap[symAddr] = string(demangledName);
190 free(demangledName);
191 } else {
192 addr2SymMap[symAddr] = string(symName);
195 count++;
197 printf("# Read %u symbols from file %s\n", count, nmFileName.c_str());
199 fclose(nmMapFile);
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) {
206 string sym;
207 auto it = addr2SymMap.find(addr);
208 if (it != addr2SymMap.end()) {
209 sym = " # " + it->second;
210 } else {
211 char addrStr[50];
212 sprintf(addrStr, "%p", addr);
213 sym = " # SYMBOL @ " + string(addrStr);
215 return sym;
218 TCA OfflineCode::getRegionStart(TCRegion region, TransBCMapping transBCMap) {
219 switch (region) {
220 case TCRMain:
221 return transBCMap.aStart;
222 case TCRCold:
223 return transBCMap.acoldStart;
224 case TCRFrozen:
225 return transBCMap.afrozenStart;
226 case TCRCount:
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, ' ');
236 return;
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),
244 count) :
246 os << folly::format("{:<10} ", eventStr);
250 EventCounts OfflineCode::getEventCounts(TCA address,
251 uint32_t instrLen,
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,
261 event);
262 eventCounts[i] = eventCount;
264 return eventCounts;
267 void OfflineCode::disasm(std::ostream& os,
268 FILE* file,
269 TCA fileStartAddr,
270 TCA codeStartAddr,
271 uint64_t codeLen,
272 const PerfEventsMap<TCA>& perfEvents,
273 OfflineCode::BCMappingInfo bcMappingInfo,
274 bool printAddr,
275 bool printBinary) {
277 auto const regionInfo = getRegionInfo(file,
278 fileStartAddr,
279 codeStartAddr,
280 codeLen,
281 perfEvents,
282 bcMappingInfo);
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;
296 if (sk.valid()) {
297 os << std::setw(4) << sk.printableOffset() << ": "
298 << sk.showInst() << std::endl;
299 } else {
300 auto const currSha1 = rangeInfo.sha1
301 ? rangeInfo.sha1->toString() : "\"missing SHA1\"";
302 os << folly::format(
303 "<<< couldn't find unit {} to print bytecode at {} {} >>>\n",
304 currSha1,
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) {
320 if (printAddr) {
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;
346 ++currAnnotItr;
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);
364 progressRangeItr();
365 } else {
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
368 // second part
369 auto const splitTCRange = lastRange.split(annotStart);
370 annotatedRanges.push_back(splitTCRange.first);
371 lastRange = splitTCRange.second;
373 } else {
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);
379 progressRangeItr();
380 } else {
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);
395 progressRangeItr();
398 return annotatedRanges;
401 vector<TCRangeInfo>
402 OfflineCode::getRanges(const BCMappingInfo& bcMappingInfo,
403 const TCA start,
404 const TCA end) {
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
411 // bcMapping starts.
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()) {
430 return ranges;
433 return annotateRanges(ranges, bcMappingInfo.annotations);
436 TCRangeInfo OfflineCode::getRangeInfo(const TransBCMapping& transBCMap,
437 const TCA start,
438 const TCA end) {
439 TCRangeInfo rangeInfo{start, end, transBCMap.sk, transBCMap.sha1};
440 auto const sk = transBCMap.sk;
441 if (sk.valid()) {
442 rangeInfo.unit = sk.func()->unit();
443 rangeInfo.func = sk.func();
444 auto const lineNum = sk.lineNumber();
445 if (lineNum != -1) rangeInfo.lineNum = lineNum;
448 return rangeInfo;
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,
459 callDest,
460 codeStr,
461 eventCounts,
463 instrLen};
466 void OfflineCode::readDisasmFile(FILE* file,
467 const Offset offset,
468 const uint64_t codeLen,
469 void* code) {
470 if (fseek(file, offset, SEEK_SET)) {
471 error("disasm error: seeking file");
474 size_t readLen = fread(code, codeLen, 1, file);
475 if (readLen != 1) {
476 error("Failed to read {} bytes at offset {} from code file due to {}",
477 codeLen, offset, feof(file) ? "EOF" : "read error");
481 } } // HPHP::jit