Custom logging class within TC-Print
[hiphop-php.git] / hphp / tools / tc-print / tc-print.cpp
blobe7b55bf532a26401d16f0005520f6edf7a169611
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 +----------------------------------------------------------------------+
17 #include "hphp/tools/tc-print/tc-print.h"
19 #include <stdio.h>
20 #include <assert.h>
22 #include <cstdint>
23 #include <string>
24 #include <vector>
25 #include <sstream>
27 #include <folly/Singleton.h>
29 #include "hphp/runtime/vm/repo.h"
30 #include "hphp/runtime/base/preg.h"
31 #include "hphp/runtime/base/program-functions.h"
33 #include "hphp/tools/tc-print/perf-events.h"
34 #include "hphp/tools/tc-print/offline-trans-data.h"
35 #include "hphp/tools/tc-print/offline-code.h"
36 #include "hphp/tools/tc-print/mappers.h"
37 #include "hphp/tools/tc-print/repo-wrapper.h"
38 #include "hphp/tools/tc-print/std-logger.h"
39 #include "hphp/tools/tc-print/tc-print-logger.h"
41 using namespace HPHP;
42 using namespace HPHP::jit;
44 #define MAX_SYM_LEN 10240
46 std::string dumpDir("/tmp");
47 std::string configFile;
48 std::string profFileName;
49 uint32_t nTopTrans = 0;
50 uint32_t nTopFuncs = 0;
51 bool creationOrder = false;
52 bool transCFG = false;
53 bool collectBCStats = false;
54 bool inclusiveStats = false;
55 bool verboseStats = false;
56 bool hostOpcodes = false;
57 folly::Optional<MD5> md5Filter;
58 PerfEventType sortBy = EVENT_CYCLES;
59 bool sortByDensity = false;
60 bool sortBySize = false;
61 double helpersMinPercentage = 0;
62 ExtOpcode filterByOpcode = 0;
63 std::string kindFilter = "all";
64 uint32_t selectedFuncId = INVALID_ID;
65 TCA minAddr = 0;
66 TCA maxAddr = (TCA)-1;
67 uint32_t annotationsVerbosity = 2;
69 std::vector<uint32_t> transPrintOrder;
71 RepoWrapper* g_repo;
72 OfflineTransData* g_transData;
73 OfflineCode* transCode;
75 char errMsgBuff[MAX_SYM_LEN];
76 const char* kListKeyword = "list";
77 TCPrintLogger* g_logger;
79 PerfEventsMap<TCA> tcaPerfEvents;
80 PerfEventsMap<TransID> transPerfEvents;
82 #define NTRANS (g_transData->getNumTrans())
83 #define NFUNCS (g_transData->getNumFuncs())
84 #define TREC(TID) (g_transData->getTransRec(TID))
86 void warnTooFew(const std::string& name,
87 uint32_t requested,
88 uint32_t available) {
89 fprintf(stderr,
90 "Requested top %u %s, but there are only %u available.\n",
91 requested,
92 name.c_str(),
93 available);
96 template<typename T>
97 std::string toString(T value) {
98 std::stringstream ss;
99 ss << value;
100 return ss.str();
103 void usage() {
104 printf("Usage: tc-print [OPTIONS]\n"
105 " Options:\n"
106 " -c <FILE> : uses the given config file\n"
107 " -D : used along with -t, this option sorts the top "
108 "translations by density (count / size) of the selected perf event\n"
109 " -d <DIRECTORY> : looks for dump file in <DIRECTORY> "
110 "(default: /tmp)\n"
111 " -f <FUNC_ID> : prints the translations for the given "
112 "<FUNC_ID>, sorted by start offset\n"
113 " -g <FUNC_ID> : prints the CFG among the translations for the "
114 "given <FUNC_ID>\n"
115 " -p <FILE> : uses raw profile data from <FILE>\n"
116 " -s : prints all translations sorted by creation "
117 "order\n"
118 " -u <MD5> : prints all translations from the specified "
119 "unit\n"
120 " -t <NUMBER> : prints top <NUMBER> translations according to "
121 "profiling info\n"
122 " -k <TRANS_KIND> : used with -t, filters only translations of the "
123 "given kind, e.g. TransLive (default: all)\n"
124 " -a <ADDR> : used with -t, filters only events at addresses "
125 ">= <ADDR>\n"
126 " -A <ADDR> : used with -t, filters only events at addresses "
127 "<= <ADDR>\n"
128 " -T <NUMBER> : prints top <NUMBER> functions according to "
129 "profiling info\n"
130 " -e <EVENT_TYPE> : sorts by the specified perf event. Pass '%s' "
131 "to get a list of valid event types.\n"
132 " -b : prints bytecode stats\n"
133 " -B <OPCODE> : used in conjunction with -e, prints the top "
134 "bytecode translationc event type. Pass '%s' to get a "
135 "list of valid opcodes.\n"
136 " -i : reports inclusive stats by including helpers "
137 "(perf data must include call graph information)\n"
138 " -n <level> : level of verbosity for annotations. Use 0 for "
139 "no annotations, 1 - for inline, 2 - to print all annotations "
140 "including from a file (default: 2).\n"
141 " -o : print host opcodes\n"
142 " -v <PERCENTAGE> : sets the minimum percentage to <PERCENTAGE> "
143 "when printing the top helpers (implies -i). The lower the percentage,"
144 " the more helpers that will show up.\n"
145 " -h : prints help message\n",
146 kListKeyword,
147 kListKeyword);
150 void printValidBytecodes() {
151 g_logger->printGeneric("<OPCODE>:\n");
152 auto validOpcodes = getValidOpcodeNames();
153 for (size_t i = 0; i < validOpcodes.size(); i++) {
154 g_logger->printGeneric(" * %s\n", validOpcodes[i].first.c_str());
158 void printValidEventTypes() {
159 g_logger->printGeneric("<EVENT_TYPE>:\n");
160 for (size_t i = 0; i < NUM_EVENT_TYPES; i++) {
161 g_logger->printGeneric(" * %s\n",
162 eventTypeToCommandLineArgument((PerfEventType)i));
166 void parseOptions(int argc, char *argv[]) {
167 int c;
168 opterr = 0;
169 while ((c = getopt (argc, argv, "hc:Dd:f:g:ip:st:u:S:T:o:e:bB:v:k:a:A:n:"))
170 != -1) {
171 switch (c) {
172 case 'A':
173 if (sscanf(optarg, "%p", &maxAddr) != 1) {
174 usage();
175 exit(1);
177 break;
178 case 'a':
179 if (sscanf(optarg, "%p", &minAddr) != 1) {
180 usage();
181 exit(1);
183 break;
184 case 'h':
185 usage();
186 exit(0);
187 case 'c':
188 configFile = optarg;
189 break;
190 case 'd':
191 dumpDir = optarg;
192 break;
193 case 'f':
194 creationOrder = true;
195 if (sscanf(optarg, "%u", &selectedFuncId) != 1) {
196 usage();
197 exit(1);
199 break;
200 case 'g':
201 transCFG = true;
202 if (sscanf(optarg, "%u", &selectedFuncId) != 1) {
203 usage();
204 exit(1);
206 break;
207 case 'p':
208 profFileName = optarg;
209 break;
210 case 's':
211 creationOrder = true;
212 break;
213 case 't':
214 if (sscanf(optarg, "%u", &nTopTrans) != 1) {
215 usage();
216 exit(1);
218 break;
219 case 'u':
220 if (strlen(optarg) == 32) {
221 md5Filter = MD5(optarg);
222 } else {
223 usage();
224 exit(1);
226 break;
227 case 'S':
228 sortBySize = true;
229 case 'T':
230 if (sscanf(optarg, "%u", &nTopFuncs) != 1) {
231 usage();
232 exit(1);
234 break;
235 case 'k':
236 kindFilter = optarg;
237 break;
238 case 'D':
239 sortByDensity = true;
240 break;
241 case 'e':
242 if (!strcmp(optarg, kListKeyword)) {
243 printValidEventTypes();
244 exit(0);
246 sortBy = commandLineArgumentToEventType(optarg);
247 if (sortBy == NUM_EVENT_TYPES) {
248 usage();
249 exit(1);
251 break;
252 case 'b':
253 collectBCStats = true;
254 break;
255 case 'B':
256 if (!strcmp(optarg, kListKeyword)) {
257 printValidBytecodes();
258 exit(0);
260 filterByOpcode = stringToExtOpcode(optarg);
261 if (!filterByOpcode) {
262 usage();
263 exit(1);
265 break;
266 case 'i':
267 inclusiveStats = true;
268 break;
269 case 'n':
270 if (sscanf(optarg, "%u", &annotationsVerbosity) != 1) {
271 usage();
272 exit(1);
274 break;
275 case 'v':
276 verboseStats = true;
277 // -v implies -i
278 inclusiveStats = true;
279 if (sscanf(optarg, "%lf", &helpersMinPercentage) != 1) {
280 usage();
281 exit(1);
283 break;
284 case '?':
285 if (optopt == 'd' || optopt == 'c' || optopt == 'p' || optopt == 't') {
286 fprintf (stderr, "Error: -%c expects an argument\n\n", optopt);
288 case 'o':
289 hostOpcodes = true;
290 break;
291 default:
292 usage();
293 exit(1);
298 void sortTrans() {
299 for (uint32_t tid = 0; tid < NTRANS; tid++) {
300 const auto trec = TREC(tid);
301 if (trec->isValid() &&
302 (selectedFuncId == INVALID_ID ||
303 selectedFuncId == trec->src.funcID()) &&
304 (kindFilter == "all" || kindFilter == show(trec->kind).c_str())) {
305 transPrintOrder.push_back(tid);
310 void loadPerfEvents() {
311 FILE* profFile;
313 profFile = fopen(profFileName.c_str(), "rt");
315 if (!profFile) {
316 error("Error opening file " + profFileName);
319 char program[MAX_SYM_LEN];
320 char eventCaption[MAX_SYM_LEN];
321 char line[2*MAX_SYM_LEN];
322 TCA addr;
323 uint32_t tcSamples[NUM_EVENT_TYPES];
324 uint32_t hhvmSamples[NUM_EVENT_TYPES];
325 size_t numEntries = 0;
326 PerfEventType eventType = NUM_EVENT_TYPES;
327 // samplesPerKind[event][kind]
328 uint32_t samplesPerKind[NUM_EVENT_TYPES][NumTransKinds];
329 uint32_t samplesPerTCRegion[NUM_EVENT_TYPES][TCRCount];
331 memset(tcSamples , 0, sizeof(tcSamples));
332 memset(hhvmSamples, 0, sizeof(hhvmSamples));
333 memset(samplesPerKind, 0, sizeof(samplesPerKind));
334 memset(samplesPerTCRegion, 0, sizeof(samplesPerTCRegion));
336 while (fgets(line, 2*MAX_SYM_LEN, profFile) != nullptr) {
337 always_assert(sscanf(line, "%s %s %lu", program, eventCaption, &numEntries)
338 == 3);
339 always_assert(numEntries);
341 std::vector<std::pair<TCA,std::string>> entries;
343 for (size_t i = 0; i < numEntries; i++) {
344 fscanf(profFile, "%p %s\n", &addr, line);
345 entries.push_back(std::pair<TCA,std::string>(addr, line));
348 // Strip :p+ PEBS suffix.
349 if (auto pos = strchr(eventCaption, ':')) {
350 *pos = '\0';
353 if (strncmp(program, "hhvm", 4) == 0) {
354 eventType = perfScriptOutputToEventType(eventCaption);
355 if (eventType == NUM_EVENT_TYPES) {
356 snprintf(errMsgBuff,
357 MAX_SYM_LEN,
358 "loadProfData: invalid event caption '%s'",
359 eventCaption);
360 error(errMsgBuff);
363 hhvmSamples[eventType]++;
365 size_t selIdx = 0;
366 addr = entries[0].first;
368 if (inclusiveStats) {
369 for (size_t i = 0; i < entries.size(); i++) {
370 if (g_transData->isAddrInSomeTrans(entries[i].first)) {
371 addr = entries[i].first;
372 selIdx = i;
373 break;
378 if (!(minAddr <= addr && addr <= maxAddr)) continue;
379 if (!g_transData->isAddrInSomeTrans(addr)) continue;
380 TransID transId = g_transData->getTransContaining(addr);
381 always_assert(transId != INVALID_ID);
382 tcSamples[eventType]++;
384 const TransRec* trec = g_transData->getTransRec(transId);
385 TransKind kind = trec->kind;
386 samplesPerKind[eventType][static_cast<uint32_t>(kind)]++;
387 TCRegion region = transCode->findTCRegionContaining(addr);
388 always_assert(region != TCRCount);
389 samplesPerTCRegion[eventType][region]++;
391 std::vector<std::string> stackTrace;
392 if (verboseStats) {
393 for (size_t i = 0; i < selIdx; i++) {
395 if (!strcmp(entries[i].second.c_str(), "[unknown]")) {
397 // Append the address to disambiguate.
398 entries[i].second += std::string("@")
399 + toString((void*)entries[i].first);
402 stackTrace.push_back(entries[i].second);
404 reverse(stackTrace.begin(), stackTrace.end());
407 if (selIdx) addr--;
408 tcaPerfEvents.addEvent(addr, (PerfEvent){eventType, 1}, stackTrace);
412 AddrToTransMapper transMapper(g_transData);
413 transPerfEvents = tcaPerfEvents.mapTo(transMapper);
415 g_logger->printGeneric("# Number of hhvm samples read "
416 "(%% in TC) from file %s\n",
417 profFileName.c_str());
419 for (size_t i = 0; i < NUM_EVENT_TYPES; i++) {
420 if (!hhvmSamples[i]) continue;
422 g_logger->printGeneric("# %-19s TOTAL: %10u (%u in TC = %5.2lf%%)\n",
423 eventTypeToCommandLineArgument((PerfEventType)i),
424 hhvmSamples[i],
425 tcSamples[i],
426 100.0 * tcSamples[i] / hhvmSamples[i]);
428 for (size_t j = 0; j < NumTransKinds; ++j) {
429 auto ct = samplesPerKind[i][j];
430 if (!ct) continue;
431 std::string kind = show(static_cast<TransKind>(j));
432 g_logger->printGeneric("# %26s: %-8u (%5.2lf%%)\n",
433 kind.c_str(), ct, 100.0 * ct / tcSamples[i]);
435 g_logger->printGeneric("#\n");
437 g_logger->printGeneric("\n");
439 // print per-TCRegion information
441 // header
442 g_logger->printGeneric("# TCRegion ");
443 for (size_t i = 0; i < NUM_EVENT_TYPES; i++) {
444 g_logger->printGeneric("%17s ",
445 eventTypeToCommandLineArgument((PerfEventType)i));
447 g_logger->printGeneric("\n");
449 // HW events for each region
450 for (size_t i = 0; i < TCRCount; i++) {
451 g_logger->printGeneric("# %8s ",
452 tcRegionToString(static_cast<TCRegion>(i)).c_str());
453 for (size_t j = 0; j < NUM_EVENT_TYPES; j++) {
454 auto ct = samplesPerTCRegion[j][i];
455 g_logger->printGeneric("%8u (%5.2lf%%) ", ct,
456 ct ? (100.0 * ct / tcSamples[j]) : 0);
458 g_logger->printGeneric("\n");
460 g_logger->printGeneric("#\n\n");
462 fclose(profFile);
465 void loadProfData() {
466 if (!profFileName.empty()) {
467 loadPerfEvents();
471 // Prints the metadata, bytecode, and disassembly for the given translation
472 void printTrans(TransID transId) {
473 always_assert(transId < NTRANS);
475 g_logger->printGeneric("\n====================\n");
476 g_transData->printTransRec(transId, transPerfEvents);
478 const TransRec* tRec = TREC(transId);
479 if (!tRec->isValid()) return;
481 if (!tRec->blocks.empty()) {
482 g_logger->printGeneric("----------\nbytecode:\n----------\n");
483 const Func* curFunc = nullptr;
484 for (auto& block : tRec->blocks) {
485 std::stringstream byteInfo;
486 auto unit = g_repo->getUnit(block.md5);
487 if (!unit) {
488 byteInfo << folly::format(
489 "<<< couldn't find unit {} to print bytecode range [{},{}) >>>\n",
490 block.md5, block.bcStart, block.bcPast);
491 continue;
494 auto newFunc = unit->getFunc(block.bcStart);
495 always_assert(newFunc);
496 if (newFunc != curFunc) {
497 byteInfo << '\n';
498 newFunc->prettyPrint(byteInfo, Func::PrintOpts().noFpi().noMetadata());
500 curFunc = newFunc;
502 unit->prettyPrint(
503 byteInfo,
504 Unit::PrintOpts().range(block.bcStart, block.bcPast).noFuncs());
505 g_logger->printBytecode(byteInfo.str());
509 g_logger->printGeneric("----------\n%s: main\n----------\n",
510 transCode->getArchName());
511 transCode->printDisasm(tRec->aStart, tRec->aLen, tRec->bcMapping,
512 tcaPerfEvents, hostOpcodes);
514 g_logger->printGeneric("----------\n%s: cold\n----------\n",
515 transCode->getArchName());
516 // Sometimes acoldStart is the same as afrozenStart. Avoid printing the code
517 // twice in such cases.
518 if (tRec->acoldStart != tRec->afrozenStart) {
519 transCode->printDisasm(tRec->acoldStart, tRec->acoldLen, tRec->bcMapping,
520 tcaPerfEvents, hostOpcodes);
523 g_logger->printGeneric("----------\n%s: frozen\n----------\n",
524 transCode->getArchName());
525 transCode->printDisasm(tRec->afrozenStart, tRec->afrozenLen, tRec->bcMapping,
526 tcaPerfEvents, hostOpcodes);
528 g_logger->printGeneric("----------\n");
532 void printCFGOutArcs(TransID transId) {
533 std::vector<TCA> jmpTargets;
535 TCA fallThru = transCode->getTransJmpTargets(
536 g_transData->getTransRec(transId), &jmpTargets);
538 auto const srcFuncId = TREC(transId)->src.funcID();
540 for (size_t i = 0; i < jmpTargets.size(); i++) {
541 TransID targetId = g_transData->getTransStartingAt(jmpTargets[i]);
542 if (targetId != INVALID_ID &&
543 // filter jumps to prologues of other funcs
544 TREC(targetId)->src.funcID() == srcFuncId &&
545 TREC(targetId)->kind != TransKind::Anchor) {
547 bool retrans = (TREC(transId)->src.offset() ==
548 TREC(targetId)->src.offset());
549 const char* color;
550 if (retrans) {
551 color = "darkorange";
552 } else if (jmpTargets[i] == fallThru) {
553 color = "brown";
554 } else {
555 color = "green4";
557 g_logger->printGeneric("t%u -> t%u [color=%s] ;\n", transId, targetId,
558 color);
564 void printCFG() {
565 std::vector<TransID> inodes;
567 g_logger->printGeneric("digraph CFG {\n");
569 g_transData->findFuncTrans(selectedFuncId, &inodes);
571 // Print nodes
572 for (uint32_t i = 0; i < inodes.size(); i++) {
573 auto tid = inodes[i];
574 uint32_t bcStart = TREC(tid)->src.offset();
575 uint32_t bcStop = TREC(tid)->bcPast();
576 const auto kind = TREC(tid)->kind;
577 bool isPrologue = kind == TransKind::LivePrologue ||
578 kind == TransKind::OptPrologue;
579 const char* shape = "box";
580 switch (TREC(tid)->kind) {
581 case TransKind::Optimize: shape = "oval"; break;
582 case TransKind::Profile: shape = "hexagon"; break;
583 case TransKind::LivePrologue:
584 case TransKind::ProfPrologue:
585 case TransKind::OptPrologue : shape = "invtrapezium"; break;
586 default: shape = "box";
588 g_logger->printGeneric("t%u [shape=%s,label=\"T: %u\\nbc: [0x%x-0x%x)\","
589 "style=filled%s];\n", tid, shape, tid, bcStart,
590 bcStop, (isPrologue ? ",color=blue" : ""));
593 // Print arcs
594 for (uint32_t i = 0; i < inodes.size(); i++) {
595 uint32_t tid = inodes[i];
596 printCFGOutArcs(tid);
599 g_logger->printGeneric("}\n");
602 void printTopFuncs() {
603 if (!nTopFuncs) return;
604 TransToFuncMapper funcMapper(g_transData);
605 PerfEventsMap<FuncId> funcPerfEvents = transPerfEvents.mapTo(funcMapper);
606 funcPerfEvents.printEventsSummary(sortBy,
607 "FuncId",
608 nTopFuncs,
609 verboseStats,
610 helpersMinPercentage);
613 void printTopFuncsBySize() {
614 std::unordered_map<FuncId,size_t> funcSize;
615 FuncId maxFuncId = 0;
616 for (TransID t = 0; t < NTRANS; t++) {
617 const auto trec = TREC(t);
618 if (trec->isValid()) {
619 const auto funcId = trec->src.funcID();
620 funcSize[funcId] += trec->aLen;
621 if (funcId > maxFuncId) {
622 maxFuncId = funcId;
626 std::vector<FuncId> funcIds(maxFuncId+1);
627 for (FuncId fid = 0; fid <= maxFuncId; fid++) {
628 funcIds[fid] = fid;
630 std::sort(funcIds.begin(), funcIds.end(),
631 [&](FuncId fid1, FuncId fid2) {
632 return funcSize[fid1] > funcSize[fid2];
634 g_logger->printGeneric("FuncID: \tSize (total aLen in bytes):\n");
635 for (size_t i = 0; i < nTopFuncs; i++) {
636 const auto fid = funcIds[i];
637 g_logger->printGeneric("%10u\t%10lu\n", fid, funcSize[funcIds[i]]);
641 struct CompTrans {
642 private:
643 const PerfEventsMap<TransID>& transPerfEvents;
644 const PerfEventType etype;
646 public:
647 CompTrans(const PerfEventsMap<TransID>& _transPerfEvents,
648 PerfEventType _etype) :
649 transPerfEvents(_transPerfEvents), etype(_etype) {}
651 bool operator()(TransID t1, TransID t2) const {
652 const auto count1 = transPerfEvents.getEventCount(t1, etype);
653 const auto count2 = transPerfEvents.getEventCount(t2, etype);
654 if (sortByDensity) {
655 const auto size1 = TREC(t1)->aLen;
656 const auto size2 = TREC(t2)->aLen;
657 return count1 * size2 > count2 * size1;
659 return count1 > count2;
663 void printTopTrans() {
664 if (!nTopTrans) return;
666 // The summary currently includes all translations, so it's misleading
667 // if we're filtering a specific kind of translations or address range.
668 // It also doesn't sort by density, so do print it if sortByDensity is set.
669 if (kindFilter == "all" && minAddr == 0 && maxAddr == (TCA)-1 &&
670 !sortByDensity) {
671 transPerfEvents.printEventsSummary(sortBy,
672 "TransId",
673 nTopTrans,
674 verboseStats,
675 helpersMinPercentage);
678 // Sort and print the top translations.
679 std::vector<TransID> transIds;
681 for (TransID t = 0; t < NTRANS; t++) {
682 if (TREC(t)->isValid() &&
683 (kindFilter == "all" || kindFilter == show(TREC(t)->kind).c_str()) &&
684 ((minAddr <= TREC(t)->aStart && TREC(t)->aStart <= maxAddr) ||
685 (minAddr <= TREC(t)->acoldStart && TREC(t)->acoldStart <= maxAddr))) {
686 transIds.push_back(t);
690 CompTrans compTrans(transPerfEvents, sortBy);
691 sort(transIds.begin(), transIds.end(), compTrans);
693 size_t nPrint = nTopTrans;
694 if (transIds.size() < nTopTrans) {
695 fprintf(stderr, "Warning: too few translations selected (%lu)\n",
696 transIds.size());
697 nPrint = transIds.size();
699 for (size_t i = 0; i < nPrint; i++) printTrans(transIds[i]);
702 void printBytecodeStats(const OfflineTransData* tdata,
703 const PerfEventsMap<TCA>& events,
704 PerfEventType etype) {
706 if (!g_repo) error("printBytecodeStats: null repo");
707 if (!tdata) error("printBytecodeStats: null g_transData");
709 AddrToBcMapper bcMapper(tdata);
710 PerfEventsMap<ExtOpcode> bcPerfEvents = events.mapTo(bcMapper);
712 std::map<ExtOpcode,std::string> opcodeToName;
713 PerfEventsMap<ExtOpcode>::const_iterator it;
715 for (it = bcPerfEvents.begin(); it != bcPerfEvents.end(); it++) {
716 opcodeToName[it->first] = extOpcodeToString(it->first);
719 bcPerfEvents.printEventsSummary(etype,
720 "Opcode",
721 PerfEventsMap<ExtOpcode>::kAllEntries,
722 verboseStats,
723 helpersMinPercentage,
724 opcodeToName);
727 void printTopBytecodes(const OfflineTransData* tdata,
728 OfflineCode* olCode,
729 const PerfEventsMap<TCA>& samples,
730 PerfEventType etype,
731 ExtOpcode filterBy) {
733 always_assert(etype < NUM_EVENT_TYPES);
735 AddrToTransFragmentMapper mapper(tdata, filterBy);
736 PerfEventsMap<TransFragment> tfragPerfEvents = samples.mapTo(mapper);
738 std::vector<std::pair<uint64_t, TransFragment> > ranking;
739 PerfEventsMap<TransFragment>::const_iterator it;
741 for (it = tfragPerfEvents.begin(); it != tfragPerfEvents.end(); it++) {
742 ranking.push_back(std::make_pair(it->second[etype], it->first));
745 sort(ranking.rbegin(), ranking.rend());
747 for (size_t i = 0; i < ranking.size(); i++) {
748 const TransFragment& tfrag = ranking[i].second;
749 const TransRec* trec = tdata->getTransRec(tfrag.tid);
751 Unit* unit = g_repo->getUnit(trec->md5);
752 always_assert(unit);
754 g_logger->printGeneric("\n====================\n");
755 g_logger->printGeneric("{\n");
756 g_logger->printGeneric(" FuncID = %u\n", trec->src.funcID());
757 g_logger->printGeneric(" TransID = %u\n", tfrag.tid);
758 tfragPerfEvents.printEventsHeader(tfrag);
759 g_logger->printGeneric("}\n\n");
761 g_logger->printGeneric("----------\n%s: main\n----------\n",
762 olCode->getArchName());
763 olCode->printDisasm(tfrag.aStart,
764 tfrag.aLen,
765 trec->bcMapping,
766 samples,
767 hostOpcodes);
769 g_logger->printGeneric("----------\n%s: cold\n----------\n",
770 olCode->getArchName());
771 olCode->printDisasm(tfrag.acoldStart,
772 tfrag.acoldLen,
773 trec->bcMapping,
774 samples,
775 hostOpcodes);
777 g_logger->printGeneric("----------\n%s: frozen\n----------\n",
778 olCode->getArchName());
779 olCode->printDisasm(tfrag.afrozenStart,
780 tfrag.afrozenLen,
781 trec->bcMapping,
782 samples,
783 hostOpcodes);
787 int main(int argc, char *argv[]) {
788 folly::SingletonVault::singleton()->registrationComplete();
790 pcre_init();
792 parseOptions(argc, argv);
794 StdLogger stdoutlogger{};
795 g_logger = &stdoutlogger;
796 g_transData = new OfflineTransData(dumpDir);
797 transCode = new OfflineCode(dumpDir,
798 g_transData->getHotBase(),
799 g_transData->getMainBase(),
800 g_transData->getProfBase(),
801 g_transData->getColdBase(),
802 g_transData->getFrozenBase());
803 g_repo = new RepoWrapper(g_transData->getRepoSchema(), configFile);
805 loadProfData();
807 g_transData->setAnnotationsVerbosity(annotationsVerbosity);
809 if (nTopFuncs) {
810 if (nTopFuncs > NFUNCS) {
811 warnTooFew("functions", nTopFuncs, NFUNCS);
812 nTopFuncs = NFUNCS;
814 if (sortBySize) {
815 printTopFuncsBySize();
816 } else {
817 printTopFuncs();
819 } else if (nTopTrans) {
820 if (nTopTrans > NTRANS) {
821 warnTooFew("translations", nTopTrans, NTRANS);
822 nTopTrans = NTRANS;
824 printTopTrans();
825 } else if (transCFG) {
826 printCFG();
827 } else if (creationOrder) {
828 // Print translations (all or for a given funcId) in the order
829 // they were created.
830 sortTrans();
831 for (uint32_t i=0; i < transPrintOrder.size(); i++) {
832 printTrans(transPrintOrder[i]);
834 } else if (collectBCStats) {
835 printBytecodeStats(g_transData, tcaPerfEvents, sortBy);
836 } else if (filterByOpcode) {
837 printTopBytecodes(g_transData,
838 transCode,
839 tcaPerfEvents,
840 sortBy,
841 filterByOpcode);
842 } else {
843 // Print all translations in original order, filtered by unit if desired.
844 for (uint32_t t = 0; t < NTRANS; t++) {
845 auto tRec = TREC(t);
846 if (!tRec->isValid()) continue;
847 if (tRec->kind == TransKind::Anchor) continue;
848 if (md5Filter && tRec->md5 != *md5Filter) continue;
850 printTrans(t);
851 g_logger->flushTranslation(tRec->funcName);
855 delete g_transData;
856 delete transCode;
857 delete g_repo;
859 return 0;