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 +----------------------------------------------------------------------+
17 #include "hphp/tools/tc-print/tc-print.h"
28 #include <boost/uuid/uuid_io.hpp>
29 #include <boost/uuid/uuid_generators.hpp>
31 #include <folly/Format.h>
32 #include <folly/dynamic.h>
33 #include <folly/DynamicConverter.h>
34 #include <folly/json.h>
35 #include <folly/Singleton.h>
37 #include "hphp/util/build-info.h"
39 #include "hphp/runtime/base/preg.h"
40 #include "hphp/runtime/base/program-functions.h"
42 #include "hphp/tools/tc-print/perf-events.h"
43 #include "hphp/tools/tc-print/printir-annotation.h"
44 #include "hphp/tools/tc-print/offline-trans-data.h"
45 #include "hphp/tools/tc-print/offline-code.h"
46 #include "hphp/tools/tc-print/mappers.h"
47 #include "hphp/tools/tc-print/repo-wrapper.h"
48 #include "hphp/tools/tc-print/std-logger.h"
49 #include "hphp/tools/tc-print/tc-print-logger.h"
51 #include "hphp/facebook/extensions/scribe/ext_scribe.h"
52 #include "hphp/tools/tc-print/facebook/db-logger.h"
56 using namespace HPHP::jit
;
58 #define MAX_SYM_LEN 10240
60 std::string
dumpDir("/tmp");
61 std::string profFileName
;
62 std::string repoFileName
;
63 uint32_t nTopTrans
= 0;
64 uint32_t nTopFuncs
= 0;
66 bool creationOrder
= false;
67 bool transCFG
= false;
68 bool collectBCStats
= false;
69 bool inclusiveStats
= false;
70 bool verboseStats
= false;
71 bool hostOpcodes
= false;
72 Optional
<SHA1
> sha1Filter
;
73 PerfEventType sortBy
= EVENT_CYCLES
;
74 bool sortByDensity
= false;
75 bool sortBySize
= false;
76 double helpersMinPercentage
= 0;
77 ExtOpcode filterByOpcode
= 0;
78 std::string kindFilter
= "all";
79 uint32_t selectedFuncId
= INVALID_ID
;
80 std::string selectedFuncName
;
82 TCA maxAddr
= (TCA
)-1;
83 uint32_t annotationsVerbosity
= 2;
85 bool printToDB
= false;
86 std::string hiveTable
;
89 std::vector
<uint32_t> transPrintOrder
;
92 OfflineTransData
* g_transData
;
93 OfflineCode
* transCode
;
95 std::unique_ptr
<AnnotationCache
> g_annotations
;
97 const char* kListKeyword
= "list";
98 TCPrintLogger
* g_logger
;
100 PerfEventsMap
<TCA
> tcaPerfEvents
;
101 PerfEventsMap
<TransID
> transPerfEvents
;
103 #define NTRANS (g_transData->getNumTrans())
104 #define NFUNCS (g_transData->getNumFuncs())
105 #define TREC(TID) (g_transData->getTransRec(TID))
107 void warnTooFew(const std::string
& name
,
109 uint32_t available
) {
111 "Requested top %u %s, but there are only %u available.\n",
118 std::string
toString(T value
) {
119 std::stringstream ss
;
125 printf("Usage: tc-print [OPTIONS]\n"
127 " -D : used along with -t, this option sorts the top "
128 "translations by density (count / size) of the selected perf event\n"
129 " -d <DIRECTORY> : looks for dump file in <DIRECTORY> "
131 " -F <FUNC_NAME> : prints the translations for the given function\n"
132 " -f <FUNC_ID> : prints the translations for the given "
133 "<FUNC_ID>, sorted by start offset\n"
134 " -g <FUNC_ID> : prints the CFG among the translations for the "
136 " -p <FILE> : uses raw profile data from <FILE>\n"
137 " -r <REPO_FILE> : specifies the bytecode repo file to use\n"
138 " -s : prints all translations sorted by creation "
140 " -u <SHA1> : prints all translations from the specified "
142 " -t <NUMBER> : prints top <NUMBER> translations according to "
144 " -k <TRANS_KIND> : used with -t, filters only translations of the "
145 "given kind, e.g. TransLive (default: all)\n"
146 " -a <ADDR> : used with -t, filters only events at addresses "
148 " -A <ADDR> : used with -t, filters only events at addresses "
150 " -T <NUMBER> : prints top <NUMBER> functions according to "
152 " -e <EVENT> : sorts by the specified perf event. Should either "
153 "be one of the predefined events or one passed with -E.\n"
154 " Pass '%s' to get a list of valid predefined event "
156 " -E <EVENTS> : specifies a new replacement set of perf events. "
157 "Pass a comma-separated list of events to replace the default set.\n"
158 " -b : prints bytecode stats\n"
159 " -B <OPCODE> : used in conjunction with -e, prints the top "
160 "bytecode translation event type. Pass '%s' to get a "
161 "list of valid opcodes.\n"
162 " -i : reports inclusive stats by including helpers "
163 "(perf data must include call graph information)\n"
164 " -n <level> : level of verbosity for annotations. Use 0 for "
165 "no annotations, 1 - for inline, 2 - to print all annotations "
166 "including from a file (default: 2).\n"
167 " -o : print host opcodes\n"
168 " -v <PERCENTAGE> : sets the minimum percentage to <PERCENTAGE> "
169 "when printing the top helpers (implies -i). The lower the percentage,"
170 " the more helpers that will show up.\n"
171 " -j : outputs tc-dump in JSON format (not compatible with "
172 "some other flags).\n"
173 // TODO(T52857399) - investigate compatibility with other flags
175 " -H <HIVE_TABLE> : used with -j, write the JSON output to Hive in the "
176 "table <HIVE_TABLE>\n"
177 " -x : log translations to database\n"
179 " -h : prints help message\n",
184 void printValidBytecodes() {
185 g_logger
->printGeneric("<OPCODE>:\n");
186 auto validOpcodes
= getValidOpcodeNames();
187 for (size_t i
= 0; i
< validOpcodes
.size(); i
++) {
188 g_logger
->printGeneric(" * %s\n", validOpcodes
[i
].first
.c_str());
192 void printValidEventTypes() {
193 g_logger
->printGeneric("<EVENT>:\n");
194 // Note: The -e list option is only for printing the predefined (default)
195 // event types; thus ranging from [0,NUM_PREDEFINED_EVENT_TYPES).
196 for (size_t i
= 0; i
< NUM_PREDEFINED_EVENT_TYPES
; i
++) {
197 g_logger
->printGeneric(" * %s\n",
198 eventTypeToCommandLineArgument((PerfEventType
)i
));
202 void parseOptions(int argc
, char *argv
[]) {
205 char* sortByArg
= nullptr;
206 while ((c
= getopt(argc
, argv
,
207 "hDd:F:f:G:g:ip:st:u:S:T:o:r:e:E:bB:v:k:a:A:n:jH:x"))
211 if (sscanf(optarg
, "%p", &maxAddr
) != 1) {
217 if (sscanf(optarg
, "%p", &minAddr
) != 1) {
229 creationOrder
= true;
230 selectedFuncName
= optarg
;
233 creationOrder
= true;
234 if (sscanf(optarg
, "%u", &selectedFuncId
) != 1) {
241 selectedFuncName
= optarg
;
245 if (sscanf(optarg
, "%u", &selectedFuncId
) != 1) {
251 profFileName
= optarg
;
254 repoFileName
= optarg
;
257 creationOrder
= true;
260 if (sscanf(optarg
, "%u", &nTopTrans
) != 1) {
266 if (strlen(optarg
) == 32) {
267 sha1Filter
= SHA1(optarg
);
276 if (sscanf(optarg
, "%u", &nTopFuncs
) != 1) {
285 sortByDensity
= true;
288 if (!strcmp(optarg
, kListKeyword
)) {
289 printValidEventTypes();
292 // Will lookup event type after parsing all args
297 char* p
= strtok(optarg
, ",");
299 addEventType(std::string(p
));
300 p
= strtok(nullptr, ",");
305 collectBCStats
= true;
308 if (!strcmp(optarg
, kListKeyword
)) {
309 printValidBytecodes();
312 filterByOpcode
= stringToExtOpcode(optarg
);
313 if (!filterByOpcode
) {
319 inclusiveStats
= true;
322 if (sscanf(optarg
, "%u", &annotationsVerbosity
) != 1) {
330 inclusiveStats
= true;
331 if (sscanf(optarg
, "%lf", &helpersMinPercentage
) != 1) {
337 if (optopt
== 'd' || optopt
== 'c' || optopt
== 'p' || optopt
== 't') {
338 fprintf (stderr
, "Error: -%c expects an argument\n\n", optopt
);
360 // Lookup the event type of a event to sort by was specified with -e
362 sortBy
= commandLineArgumentToEventType(sortByArg
);
363 if (sortBy
< getFirstEventType() || sortBy
>= getNumEventTypes()) {
368 // If not specified, then default the sorting to the first event type.
369 // If using predefined events, then EVENT_CYCLES.
370 sortBy
= static_cast<PerfEventType
>(getFirstEventType());
375 for (uint32_t tid
= 0; tid
< NTRANS
; tid
++) {
376 const auto trec
= TREC(tid
);
377 if (trec
->isValid() &&
378 (selectedFuncId
== INVALID_ID
||
379 selectedFuncId
== trec
->src
.funcID().toInt()) &&
380 (kindFilter
== "all" || kindFilter
== show(trec
->kind
).c_str())) {
381 transPrintOrder
.push_back(tid
);
384 std::sort(transPrintOrder
.begin(), transPrintOrder
.end(),
385 [] (uint32_t tid1
, uint32_t tid2
) {
386 auto const tr1
= TREC(tid1
);
387 auto const tr2
= TREC(tid2
);
389 if (tr1
->aStart
!= tr2
->aStart
) {
390 return std::less
<void*>{}(tr1
->aStart
, tr2
->aStart
);
396 void loadPerfEvents() {
399 profFile
= fopen(profFileName
.c_str(), "rt");
402 error("Error opening file " + profFileName
);
405 char program
[MAX_SYM_LEN
];
406 char eventCaption
[MAX_SYM_LEN
];
407 char line
[2*MAX_SYM_LEN
];
409 vector
<uint32_t> tcSamples(getNumEventTypes(), 0);
410 vector
<uint32_t> hhvmSamples(getNumEventTypes(), 0);
411 size_t numEntries
= 0;
412 PerfEventType eventType
= EVENT_NULL
;
413 // samplesPerKind[event][kind], samplesPerTCRegion[event][TCRegion]
414 vector
< vector
<uint32_t> > samplesPerKind(getNumEventTypes(),
415 vector
<uint32_t>(NumTransKinds
, 0));
416 vector
< vector
<uint32_t> > samplesPerTCRegion(getNumEventTypes(),
417 vector
<uint32_t>(TCRCount
, 0));
419 while (fgets(line
, 2*MAX_SYM_LEN
, profFile
) != nullptr) {
420 always_assert(sscanf(line
, "%s %s %lu", program
, eventCaption
, &numEntries
)
422 always_assert(numEntries
);
424 std::vector
<std::pair
<TCA
,std::string
>> entries
;
426 for (size_t i
= 0; i
< numEntries
; i
++) {
427 fscanf(profFile
, "%p %s\n", &addr
, line
);
428 entries
.push_back(std::pair
<TCA
,std::string
>(addr
, line
));
431 // Strip :p+ PEBS suffix.
432 if (auto pos
= strchr(eventCaption
, ':')) {
436 if (strncmp(program
, "hhvm", 4) == 0) {
437 eventType
= perfScriptOutputToEventType(eventCaption
);
438 if (eventType
== EVENT_NULL
) {
439 error(folly::sformat("loadProfData: invalid event caption {}",
443 hhvmSamples
[eventType
]++;
446 addr
= entries
[0].first
;
448 if (inclusiveStats
) {
449 for (size_t i
= 0; i
< entries
.size(); i
++) {
450 if (g_transData
->isAddrInSomeTrans(entries
[i
].first
)) {
451 addr
= entries
[i
].first
;
458 if (!(minAddr
<= addr
&& addr
<= maxAddr
)) continue;
459 if (!g_transData
->isAddrInSomeTrans(addr
)) continue;
460 TransID transId
= g_transData
->getTransContaining(addr
);
461 always_assert(transId
!= INVALID_ID
);
462 tcSamples
[eventType
]++;
464 const TransRec
* trec
= g_transData
->getTransRec(transId
);
465 TransKind kind
= trec
->kind
;
466 samplesPerKind
[eventType
][static_cast<uint32_t>(kind
)]++;
467 TCRegion region
= transCode
->findTCRegionContaining(addr
);
468 always_assert(region
!= TCRCount
);
469 samplesPerTCRegion
[eventType
][region
]++;
471 std::vector
<std::string
> stackTrace
;
473 for (size_t i
= 0; i
< selIdx
; i
++) {
475 if (!strcmp(entries
[i
].second
.c_str(), "[unknown]")) {
477 // Append the address to disambiguate.
478 entries
[i
].second
+= std::string("@")
479 + toString((void*)entries
[i
].first
);
482 stackTrace
.push_back(entries
[i
].second
);
484 reverse(stackTrace
.begin(), stackTrace
.end());
488 tcaPerfEvents
.addEvent(addr
, (PerfEvent
){eventType
, 1}, stackTrace
);
492 AddrToTransMapper
transMapper(g_transData
);
493 transPerfEvents
= tcaPerfEvents
.mapTo(transMapper
);
495 g_logger
->printGeneric("# Number of hhvm samples read "
496 "(%% in TC) from file %s\n",
497 profFileName
.c_str());
499 for (size_t i
= getFirstEventType(); i
< getNumEventTypes(); i
++) {
500 if (!hhvmSamples
[i
]) continue;
502 g_logger
->printGeneric("# %-19s TOTAL: %10u (%u in TC = %5.2lf%%)\n",
503 eventTypeToCommandLineArgument((PerfEventType
)i
),
506 100.0 * tcSamples
[i
] / hhvmSamples
[i
]);
508 for (size_t j
= 0; j
< NumTransKinds
; ++j
) {
509 auto ct
= samplesPerKind
[i
][j
];
511 std::string kind
= show(static_cast<TransKind
>(j
));
512 g_logger
->printGeneric("# %26s: %-8u (%5.2lf%%)\n",
513 kind
.c_str(), ct
, 100.0 * ct
/ tcSamples
[i
]);
515 g_logger
->printGeneric("#\n");
517 g_logger
->printGeneric("\n");
519 // print per-TCRegion information
522 g_logger
->printGeneric("# TCRegion ");
523 for (size_t i
= getFirstEventType(); i
< getNumEventTypes(); i
++) {
524 g_logger
->printGeneric("%17s ",
525 eventTypeToCommandLineArgument((PerfEventType
)i
));
527 g_logger
->printGeneric("\n");
529 // HW events for each region
530 for (size_t i
= 0; i
< TCRCount
; i
++) {
531 g_logger
->printGeneric("# %8s ",
532 tcRegionToString(static_cast<TCRegion
>(i
)).c_str());
533 for (size_t j
= getFirstEventType(); j
< getNumEventTypes(); j
++) {
534 auto ct
= samplesPerTCRegion
[j
][i
];
535 g_logger
->printGeneric("%8u (%5.2lf%%) ", ct
,
536 ct
? (100.0 * ct
/ tcSamples
[j
]) : 0);
538 g_logger
->printGeneric("\n");
540 g_logger
->printGeneric("#\n\n");
545 void loadProfData() {
546 if (!profFileName
.empty()) {
551 std::string
getDisasmStr(OfflineCode
* code
,
554 const std::vector
<TransBCMapping
>& bcMap
,
555 const PerfEventsMap
<TCA
>& perfEvents
,
557 std::ostringstream os
;
558 code
->printDisasm(os
, startAddr
, len
, bcMap
, perfEvents
, hostOpcodes
);
564 using folly::dynamic
;
566 std::string
show(TCA tca
) {
567 return folly::sformat("{}", static_cast<void*>(tca
));
570 dynamic
getAnnotation(const Annotations
& annotations
) {
571 // for JSON outputs, we only care about the annotation "after code gen"
572 std::string annotationStr
;
573 for (auto const& annotation
: annotations
) {
574 if (annotation
.first
== " after code gen ") {
575 annotationStr
= annotation
.second
;
579 if (annotationStr
.empty()) return dynamic();
581 auto const rawValue
= g_annotations
->getAnnotation(annotationStr
);
582 if (rawValue
.subpiece(0, 5) != "json:") return rawValue
;
585 return folly::parseJson(rawValue
.subpiece(5));
587 catch (const std::runtime_error
& re
){
588 std::cerr
<< re
.what() << std::endl
;
589 std::cerr
<< "Parsing annotation as JSON failed. "
590 << "Annotation included as raw value\n";
595 dynamic
getTransRec(const TransRec
* tRec
,
596 const PerfEventsMap
<TransID
>& transPerfEvents
) {
597 auto const guards
= dynamic(tRec
->guards
.begin(), tRec
->guards
.end());
599 auto const sk
= tRec
->src
;
600 auto const resumeMode
= [&] {
601 switch (sk
.resumeMode()) {
602 case ResumeMode::None
: return "None";
603 case ResumeMode::Async
: return "Async";
604 case ResumeMode::GenIter
: return "GenIter";
606 always_assert(false);
609 auto const offset
= sk
.prologue() || sk
.funcEntry()
610 ? 0 // Unable to lookup entry offset, assume main entry.
613 dynamic src
= dynamic::object("sha1", tRec
->sha1
.toString())
614 ("funcId", sk
.funcID().toInt())
615 ("funcName", tRec
->funcName
)
616 ("resumeMode", resumeMode
)
617 ("hasThis", sk
.hasThis())
618 ("prologue", sk
.prologue())
619 ("funcEntry", sk
.funcEntry())
620 ("bcStartOffset", offset
)
623 if (sk
.prologue() || sk
.funcEntry()) {
624 src
["numEntryArgs"] = sk
.numEntryArgs();
627 const dynamic result
= dynamic::object("id", tRec
->id
)
629 ("kind", show(tRec
->kind
))
630 ("hasLoop", tRec
->hasLoop
)
631 ("aStart", show(tRec
->aStart
))
633 ("coldStart", show(tRec
->acoldStart
))
634 ("coldLen", tRec
->acoldLen
)
636 show(tRec
->afrozenStart
))
637 ("frozenLen", tRec
->afrozenLen
);
642 dynamic
getPerfEvents(const PerfEventsMap
<TransID
>& transPerfEvents
,
643 const TransID transId
) {
644 dynamic eventsObj
= dynamic::object();
645 auto const events
= transPerfEvents
.getAllEvents(transId
);
647 bool hasEvents
= false;
648 for (auto const c
: events
) {
649 if (c
) hasEvents
= true;
651 if (!hasEvents
) return eventsObj
;
653 auto const numEvents
= getNumEventTypes();
654 for (int i
= 0; i
< numEvents
; i
++) {
655 auto const event
= static_cast<PerfEventType
>(i
);
656 auto const eventName
= eventTypeToCommandLineArgument(event
);
657 eventsObj
[eventName
] = events
[i
];
663 dynamic
getTrans(TransID transId
) {
664 always_assert(transId
< NTRANS
);
666 auto const* tRec
= TREC(transId
);
667 if (!tRec
->isValid()) return dynamic();
669 auto const transRec
= getTransRec(tRec
, transPerfEvents
);
670 auto const perfEvents
= getPerfEvents(transPerfEvents
, transId
);
672 dynamic blocks
= dynamic::array
;
673 for (auto const& block
: tRec
->blocks
) {
674 std::stringstream byteInfo
; // TODO(T52857125) - translate to actual data
678 assertx(!sk
.prologue());
679 if (sk
.funcEntry()) sk
.advance();
680 sk
.func()->prettyPrint(
682 HPHP::Func::PrintOpts()
685 .range(sk
.offset(), block
.bcPast
)
689 auto const offset
= [&]() {
690 if (!sk
.prologue() && !sk
.funcEntry()) return sk
.offset();
691 if (sk
.valid()) return sk
.entryOffset();
692 // Unable to lookup entry offset, assume main entry.
696 blocks
.push_back(dynamic::object("sha1", block
.sha1
.toString())
698 ("end", block
.bcPast
)
699 ("unit", sk
.valid() ?
704 auto const annotationDynamic
= getAnnotation(tRec
->annotations
);
706 auto const maybeUnit
= [&]() -> Optional
<printir::Unit
> {
707 if (!annotationDynamic
.isObject()) return std::nullopt
;
709 return folly::convertTo
<printir::Unit
>(annotationDynamic
);
711 catch (const printir::ParseError
& pe
) {
712 std::cerr
<< pe
.what() << std::endl
;
717 const dynamic mainDisasm
= tRec
->aLen
?
718 transCode
->getDisasm(tRec
->aStart
,
726 auto const coldIsFrozen
= tRec
->acoldStart
== tRec
->afrozenStart
;
727 const dynamic coldDisasm
= !coldIsFrozen
&& tRec
->acoldLen
?
728 transCode
->getDisasm(tRec
->acoldStart
,
735 const dynamic frozenDisasm
= tRec
-> afrozenLen
?
736 transCode
->getDisasm(tRec
->afrozenStart
,
743 const dynamic disasmObj
= dynamic::object("main", mainDisasm
)
745 ("frozen", frozenDisasm
);
747 return dynamic::object("transRec", transRec
)
749 ("archName", transCode
->getArchName())
750 ("regions", disasmObj
)
751 ("perfEvents", perfEvents
)
753 ("ir_annotation", maybeUnit
?
754 folly::toDynamic(*maybeUnit
) :
756 // if annotation fails to parse, give raw to the user in annotation field
760 dynamic translations
= dynamic::array
;
761 for (uint32_t t
= 0; t
< NTRANS
; t
++) {
762 translations
.push_back(getTrans(t
));
765 return dynamic::object("repoSchema", repoSchemaId().begin())
766 ("translations", translations
);
770 // Prints the metadata, bytecode, and disassembly for the given translation
771 void printTrans(TransID transId
) {
772 always_assert(transId
< NTRANS
);
774 g_logger
->printGeneric("\n====================\n");
775 g_transData
->printTransRec(transId
, transPerfEvents
);
777 const TransRec
* tRec
= TREC(transId
);
778 if (!tRec
->isValid()) return;
780 if (!tRec
->blocks
.empty()) {
781 g_logger
->printGeneric("----------\nbytecode:\n----------\n");
782 const Func
* curFunc
= nullptr;
783 for (auto& block
: tRec
->blocks
) {
784 std::stringstream byteInfo
;
787 byteInfo
<< folly::format(
788 "<<< couldn't find func in {} to print bytecode range [{}{},{}) >>>\n",
790 block
.sk
.prologue() || block
.sk
.funcEntry() ? "numEntryArgs " : "",
791 block
.sk
.prologue() || block
.sk
.funcEntry()
792 ? block
.sk
.numEntryArgs() : block
.sk
.offset(),
797 if (sk
.func() != curFunc
) {
800 curFunc
->prettyPrint(
802 Func::PrintOpts().noMetadata().noBytecode()
806 assertx(!sk
.prologue());
807 if (sk
.funcEntry()) sk
.advance();
809 curFunc
->prettyPrint(
814 .range(sk
.offset(), block
.bcPast
)
816 g_logger
->printBytecode(byteInfo
.str());
820 g_logger
->printGeneric("----------\n%s: main\n----------\n",
821 transCode
->getArchName());
822 g_logger
->printAsm("%s", getDisasmStr(transCode
,
827 hostOpcodes
).c_str());
829 g_logger
->printGeneric("----------\n%s: cold\n----------\n",
830 transCode
->getArchName());
831 // Sometimes acoldStart is the same as afrozenStart. Avoid printing the code
832 // twice in such cases.
833 if (tRec
->acoldStart
!= tRec
->afrozenStart
) {
834 g_logger
->printAsm("%s", getDisasmStr(transCode
,
839 hostOpcodes
).c_str());
842 g_logger
->printGeneric("----------\n%s: frozen\n----------\n",
843 transCode
->getArchName());
844 g_logger
->printAsm("%s", getDisasmStr(transCode
,
849 hostOpcodes
).c_str());
850 g_logger
->printGeneric("----------\n");
855 void printCFGOutArcs(TransID transId
) {
856 std::vector
<TCA
> jmpTargets
;
858 TCA fallThru
= transCode
->getTransJmpTargets(
859 g_transData
->getTransRec(transId
), &jmpTargets
);
861 auto const srcFuncId
= TREC(transId
)->src
.funcID();
863 for (size_t i
= 0; i
< jmpTargets
.size(); i
++) {
864 TransID targetId
= g_transData
->getTransStartingAt(jmpTargets
[i
]);
865 if (targetId
!= INVALID_ID
&&
866 // filter jumps to prologues of other funcs
867 TREC(targetId
)->src
.funcID() == srcFuncId
&&
868 TREC(targetId
)->kind
!= TransKind::Anchor
) {
870 bool retrans
= TREC(transId
)->src
== TREC(targetId
)->src
;
873 color
= "darkorange";
874 } else if (jmpTargets
[i
] == fallThru
) {
879 g_logger
->printGeneric("t%u -> t%u [color=%s] ;\n", transId
, targetId
,
887 std::vector
<TransID
> inodes
;
889 g_logger
->printGeneric("digraph CFG {\n");
891 g_transData
->findFuncTrans(selectedFuncId
, &inodes
);
894 for (auto& tid
: inodes
) {
895 auto const sk
= TREC(tid
)->src
;
897 g_logger
->printGeneric(
898 "t%u [shape=invtrapezium,label=\"T: %u\\nprologue\","
899 "style=filled,color=blue];\n",
905 auto const bcStart
= sk
.prologue() || sk
.funcEntry()
906 ? folly::sformat("numEntryArgs {}", TREC(tid
)->src
.numEntryArgs())
907 : TREC(tid
)->src
.printableOffset();
908 uint32_t bcStop
= TREC(tid
)->bcPast();
909 auto const shape
= [&] {
910 switch (TREC(tid
)->kind
) {
911 case TransKind::Optimize
: return "oval";
912 case TransKind::Profile
: return "hexagon";
913 default: return "box";
916 g_logger
->printGeneric(
917 "t%u [shape=%s,label=\"T: %u\\nbc: [%s-%d)\",style=filled];\n",
918 tid
, shape
, tid
, bcStart
.c_str(), bcStop
923 for (uint32_t i
= 0; i
< inodes
.size(); i
++) {
924 uint32_t tid
= inodes
[i
];
925 printCFGOutArcs(tid
);
928 g_logger
->printGeneric("}\n");
931 void printTopFuncs() {
932 if (!nTopFuncs
) return;
933 std::map
<FuncId
,std::string
> funcStrs
;
934 for (uint32_t t
= 0; t
< NTRANS
; t
++) {
936 if (!tRec
->isValid()) continue;
937 auto funcId
= tRec
->src
.funcID();
938 if (funcStrs
.count(funcId
)) continue;
939 auto funcName
= tRec
->funcName
;
940 funcStrs
[funcId
] = folly::sformat("{:<6}: {}", funcId
, funcName
);
942 TransToFuncMapper
funcMapper(g_transData
);
943 PerfEventsMap
<FuncId
> funcPerfEvents
= transPerfEvents
.mapTo(funcMapper
);
944 funcPerfEvents
.printEventsSummary(sortBy
,
949 helpersMinPercentage
,
953 void printTopFuncsBySize() {
954 std::unordered_map
<FuncId
, size_t> funcSize
;
955 for (TransID t
= 0; t
< NTRANS
; t
++) {
956 const auto trec
= TREC(t
);
957 if (trec
->isValid()) {
958 const auto funcId
= trec
->src
.funcID();
959 funcSize
[funcId
] += trec
->aLen
;
962 std::vector
<std::pair
<FuncId
, size_t>> topN(nTopFuncs
);
963 std::partial_sort_copy(funcSize
.begin(), funcSize
.end(),
964 topN
.begin(), topN
.end(),
965 [&](auto const& a
, auto const& b
) {
966 return a
.second
> b
.second
;
968 g_logger
->printGeneric("FuncID: \tSize (total aLen in bytes):\n");
969 for (auto const& [fid
, size
] : topN
) {
970 g_logger
->printGeneric("%10u\t%10lu\n", fid
.toInt(), size
);
976 const PerfEventsMap
<TransID
>& transPerfEvents
;
977 const PerfEventType etype
;
980 CompTrans(const PerfEventsMap
<TransID
>& _transPerfEvents
,
981 PerfEventType _etype
) :
982 transPerfEvents(_transPerfEvents
), etype(_etype
) {}
984 bool operator()(TransID t1
, TransID t2
) const {
985 const auto count1
= transPerfEvents
.getEventCount(t1
, etype
);
986 const auto count2
= transPerfEvents
.getEventCount(t2
, etype
);
988 const auto size1
= TREC(t1
)->aLen
;
989 const auto size2
= TREC(t2
)->aLen
;
990 return count1
* size2
> count2
* size1
;
992 return count1
> count2
;
996 void printTopTrans() {
997 if (!nTopTrans
) return;
999 // The summary currently includes all translations, so it's misleading
1000 // if we're filtering a specific kind of translations or address range.
1001 // It also doesn't sort by density, so do print it if sortByDensity is set.
1002 if (kindFilter
== "all" && minAddr
== 0 && maxAddr
== (TCA
)-1 &&
1004 transPerfEvents
.printEventsSummary(sortBy
,
1009 helpersMinPercentage
);
1012 // Sort and print the top translations.
1013 std::vector
<TransID
> transIds
;
1015 for (TransID t
= 0; t
< NTRANS
; t
++) {
1016 if (TREC(t
)->isValid() &&
1017 (kindFilter
== "all" || kindFilter
== show(TREC(t
)->kind
).c_str()) &&
1018 ((minAddr
<= TREC(t
)->aStart
&& TREC(t
)->aStart
<= maxAddr
) ||
1019 (minAddr
<= TREC(t
)->acoldStart
&& TREC(t
)->acoldStart
<= maxAddr
))) {
1020 transIds
.push_back(t
);
1024 CompTrans
compTrans(transPerfEvents
, sortBy
);
1025 sort(transIds
.begin(), transIds
.end(), compTrans
);
1027 size_t nPrint
= nTopTrans
;
1028 if (transIds
.size() < nTopTrans
) {
1029 fprintf(stderr
, "Warning: too few translations selected (%lu)\n",
1031 nPrint
= transIds
.size();
1033 for (size_t i
= 0; i
< nPrint
; i
++) printTrans(transIds
[i
]);
1036 void printBytecodeStats(const OfflineTransData
* tdata
,
1037 const PerfEventsMap
<TCA
>& events
,
1038 PerfEventType etype
) {
1040 if (!g_repo
) error("printBytecodeStats: null repo");
1041 if (!tdata
) error("printBytecodeStats: null g_transData");
1043 AddrToBcMapper
bcMapper(tdata
);
1044 PerfEventsMap
<ExtOpcode
> bcPerfEvents
= events
.mapTo(bcMapper
);
1046 std::map
<ExtOpcode
,std::string
> opcodeToName
;
1047 PerfEventsMap
<ExtOpcode
>::const_iterator it
;
1049 for (it
= bcPerfEvents
.begin(); it
!= bcPerfEvents
.end(); it
++) {
1050 opcodeToName
[it
->first
] = extOpcodeToString(it
->first
);
1053 bcPerfEvents
.printEventsSummary(etype
,
1056 PerfEventsMap
<ExtOpcode
>::kAllEntries
,
1058 helpersMinPercentage
,
1062 void printTopBytecodes(const OfflineTransData
* tdata
,
1063 OfflineCode
* olCode
,
1064 const PerfEventsMap
<TCA
>& samples
,
1065 PerfEventType etype
,
1066 ExtOpcode filterBy
) {
1068 always_assert(etype
>= getFirstEventType() && etype
< getNumEventTypes());
1070 AddrToTransFragmentMapper
mapper(tdata
, filterBy
);
1071 PerfEventsMap
<TransFragment
> tfragPerfEvents
= samples
.mapTo(mapper
);
1073 std::vector
<std::pair
<uint64_t, TransFragment
> > ranking
;
1074 PerfEventsMap
<TransFragment
>::const_iterator it
;
1076 for (it
= tfragPerfEvents
.begin(); it
!= tfragPerfEvents
.end(); it
++) {
1077 ranking
.push_back(std::make_pair(it
->second
[etype
], it
->first
));
1080 sort(ranking
.rbegin(), ranking
.rend());
1082 for (size_t i
= 0; i
< ranking
.size(); i
++) {
1083 const TransFragment
& tfrag
= ranking
[i
].second
;
1084 const TransRec
* trec
= tdata
->getTransRec(tfrag
.tid
);
1086 Unit
* unit
= g_repo
->getUnit(trec
->sha1
);
1087 always_assert(unit
);
1089 g_logger
->printGeneric("\n====================\n");
1090 g_logger
->printGeneric("{\n");
1091 g_logger
->printGeneric(" FuncID = %u\n", trec
->src
.funcID());
1092 g_logger
->printGeneric(" TransID = %u\n", tfrag
.tid
);
1093 tfragPerfEvents
.printEventsHeader(tfrag
);
1094 g_logger
->printGeneric("}\n\n");
1096 g_logger
->printGeneric("----------\n%s: main\n----------\n",
1097 olCode
->getArchName());
1098 g_logger
->printAsm("%s", getDisasmStr(olCode
,
1103 hostOpcodes
).c_str());
1105 g_logger
->printGeneric("----------\n%s: cold\n----------\n",
1106 olCode
->getArchName());
1107 g_logger
->printAsm("%s", getDisasmStr(olCode
,
1112 hostOpcodes
).c_str());
1114 g_logger
->printGeneric("----------\n%s: frozen\n----------\n",
1115 olCode
->getArchName());
1116 g_logger
->printAsm("%s", getDisasmStr(olCode
,
1121 hostOpcodes
).c_str());
1125 static uint32_t findSelectedFuncId() {
1126 if (selectedFuncName
.empty()) return INVALID_ID
;
1127 for (uint32_t t
= 0; t
< NTRANS
; t
++) {
1128 auto tRec
= TREC(t
);
1129 if (tRec
->isValid() && tRec
->funcName
== selectedFuncName
) {
1130 return tRec
->src
.funcID().toInt();
1136 int main(int argc
, char *argv
[]) {
1137 StaticString::CreateAll();
1138 folly::SingletonVault::singleton()->registrationComplete();
1142 // Initially use stdout logger while parsing arguments. Optionally switch
1143 // to DBLogger below if the printToDb argument was passed.
1144 StdLogger stdoutlogger
{};
1145 g_logger
= &stdoutlogger
;
1147 parseOptions(argc
, argv
);
1150 Optional
<DBLogger
> dblogger
= std::nullopt
;
1152 dblogger
= DBLogger
{};
1153 g_logger
= &dblogger
.value();
1154 g_logger
->printGeneric("Printing to database");
1158 g_transData
= new OfflineTransData(dumpDir
);
1159 transCode
= new OfflineCode(dumpDir
,
1160 g_transData
->getMainBase(),
1161 g_transData
->getColdBase(),
1162 g_transData
->getFrozenBase());
1163 g_repo
= new RepoWrapper(g_transData
->getRepoSchema(), repoFileName
, !useJSON
);
1164 g_transData
->loadTCData(g_repo
);
1165 g_annotations
= std::make_unique
<AnnotationCache
>(dumpDir
);
1169 g_transData
->setAnnotationsVerbosity(annotationsVerbosity
);
1171 if (selectedFuncId
== INVALID_ID
) {
1172 selectedFuncId
= findSelectedFuncId();
1176 if (nTopFuncs
> NFUNCS
) {
1177 warnTooFew("functions", nTopFuncs
, NFUNCS
);
1181 printTopFuncsBySize();
1185 } else if (nTopTrans
) {
1186 if (nTopTrans
> NTRANS
) {
1187 warnTooFew("translations", nTopTrans
, NTRANS
);
1191 } else if (transCFG
) {
1193 } else if (creationOrder
) {
1194 // Print translations (all or for a given funcId) in the order
1195 // they were created.
1197 for (uint32_t i
=0; i
< transPrintOrder
.size(); i
++) {
1198 printTrans(transPrintOrder
[i
]);
1200 } else if (collectBCStats
) {
1201 printBytecodeStats(g_transData
, tcaPerfEvents
, sortBy
);
1202 } else if (filterByOpcode
) {
1203 printTopBytecodes(g_transData
,
1208 } else if (useJSON
) {
1209 auto const tcObj
= get_json::getTC();
1211 auto const jsonStr
= folly::toJson(tcObj
);
1212 std::cout
<< jsonStr
<< std::endl
;
1215 if (!hiveTable
.empty()) {
1216 auto const uuid
= boost::uuids::random_generator()();
1217 auto const uuidStr
= boost::uuids::to_string(uuid
);
1219 for (auto const& translation
: tcObj
["translations"]) {
1220 StructuredLogEntry entry
;
1221 entry
.force_init
= true;
1223 entry
.setStr("translation_json", folly::toJson(translation
));
1224 entry
.setInt("trans_id", translation
["transId"].asInt());
1225 entry
.setStr("repoSchema", repoSchemaId().begin());
1226 entry
.setStr("func_name",
1227 translation
["transRec"]["src"]["funcName"].asString());
1228 entry
.setStr("username" , std::string(getlogin()));
1229 entry
.setStr("uuid", uuidStr
);
1231 writeStructLogLazyInit(hiveTable
, entry
);
1236 // Print all translations in original order, filtered by unit if desired.
1237 for (uint32_t t
= 0; t
< NTRANS
; t
++) {
1238 auto tRec
= TREC(t
);
1239 if (!tRec
->isValid()) continue;
1240 if (tRec
->kind
== TransKind::Anchor
) continue;
1241 if (sha1Filter
&& tRec
->sha1
!= *sha1Filter
) continue;
1244 bool opt
= (tRec
->kind
== TransKind::OptPrologue
1245 || tRec
->kind
== TransKind::Optimize
);
1246 g_logger
->flushTranslation(tRec
->funcName
, opt
);