diff --git a/bolt/docs/Heatmaps.md b/bolt/docs/Heatmaps.md --- a/bolt/docs/Heatmaps.md +++ b/bolt/docs/Heatmaps.md @@ -23,11 +23,10 @@ Note that at the moment running with LBR (`-j any,u` or `-b`) is a requirement. -Once the run is complete, and `perf.data` is generated, run BOLT in -a heatmap mode: +Once the run is complete, and `perf.data` is generated, run llvm-bolt-heatmap: ```bash -$ llvm-bolt heatmap -p perf.data +$ llvm-bolt-heatmap -p perf.data ``` By default the heatmap will be dumped to *stdout*. You can change it diff --git a/bolt/include/bolt/Utils/CommandLineOpts.h b/bolt/include/bolt/Utils/CommandLineOpts.h --- a/bolt/include/bolt/Utils/CommandLineOpts.h +++ b/bolt/include/bolt/Utils/CommandLineOpts.h @@ -27,8 +27,7 @@ extern llvm::cl::OptionCategory BoltOutputCategory; extern llvm::cl::OptionCategory AggregatorCategory; extern llvm::cl::OptionCategory BoltInstrCategory; - -extern llvm::cl::SubCommand HeatmapCommand; +extern llvm::cl::OptionCategory HeatmapCategory; extern llvm::cl::opt AlignText; extern llvm::cl::opt AggregateOnly; @@ -38,7 +37,6 @@ extern llvm::cl::opt RemoveSymtab; extern llvm::cl::opt ExecutionCountThreshold; extern llvm::cl::opt HeatmapBlock; -extern llvm::cl::opt HeatmapFile; extern llvm::cl::opt HeatmapMaxAddress; extern llvm::cl::opt HeatmapMinAddress; extern llvm::cl::opt HotData; diff --git a/bolt/lib/Profile/DataAggregator.cpp b/bolt/lib/Profile/DataAggregator.cpp --- a/bolt/lib/Profile/DataAggregator.cpp +++ b/bolt/lib/Profile/DataAggregator.cpp @@ -1348,11 +1348,11 @@ exit(1); } - HM.print(opts::HeatmapFile); - if (opts::HeatmapFile == "-") - HM.printCDF(opts::HeatmapFile); + HM.print(opts::OutputFilename); + if (opts::OutputFilename == "-") + HM.printCDF(opts::OutputFilename); else - HM.printCDF(opts::HeatmapFile + ".csv"); + HM.printCDF(opts::OutputFilename + ".csv"); return std::error_code(); } diff --git a/bolt/lib/Utils/CommandLineOpts.cpp b/bolt/lib/Utils/CommandLineOpts.cpp --- a/bolt/lib/Utils/CommandLineOpts.cpp +++ b/bolt/lib/Utils/CommandLineOpts.cpp @@ -33,8 +33,7 @@ cl::OptionCategory BoltOutputCategory("Output options"); cl::OptionCategory AggregatorCategory("Data aggregation options"); cl::OptionCategory BoltInstrCategory("BOLT instrumentation options"); - -cl::SubCommand HeatmapCommand("heatmap", "generate heatmap"); +cl::OptionCategory HeatmapCategory("Heatmap options"); cl::opt AlignText("align-text", @@ -50,11 +49,9 @@ cl::cat(AggregatorCategory)); cl::opt -BucketsPerLine("line-size", - cl::desc("number of entries per line (default 256)"), - cl::init(256), - cl::Optional, - cl::sub(HeatmapCommand)); + BucketsPerLine("line-size", + cl::desc("number of entries per line (default 256)"), + cl::init(256), cl::Optional, cl::cat(HeatmapCategory)); cl::opt DiffOnly("diff-only", @@ -83,31 +80,19 @@ cl::cat(BoltOptCategory)); cl::opt -HeatmapBlock("block-size", - cl::desc("size of a heat map block in bytes (default 64)"), - cl::init(64), - cl::sub(HeatmapCommand)); - -cl::opt -HeatmapFile("o", - cl::init("-"), - cl::desc("heatmap output file (default stdout)"), - cl::Optional, - cl::sub(HeatmapCommand)); - -cl::opt -HeatmapMaxAddress("max-address", - cl::init(0xffffffff), - cl::desc("maximum address considered valid for heatmap (default 4GB)"), - cl::Optional, - cl::sub(HeatmapCommand)); - -cl::opt -HeatmapMinAddress("min-address", - cl::init(0x0), - cl::desc("minimum address considered valid for heatmap (default 0)"), - cl::Optional, - cl::sub(HeatmapCommand)); + HeatmapBlock("block-size", + cl::desc("size of a heat map block in bytes (default 64)"), + cl::init(64), cl::cat(HeatmapCategory)); + +cl::opt HeatmapMaxAddress( + "max-address", cl::init(0xffffffff), + cl::desc("maximum address considered valid for heatmap (default 4GB)"), + cl::Optional, cl::cat(HeatmapCategory)); + +cl::opt HeatmapMinAddress( + "min-address", cl::init(0x0), + cl::desc("minimum address considered valid for heatmap (default 0)"), + cl::Optional, cl::cat(HeatmapCategory)); cl::opt HotData("hot-data", diff --git a/bolt/test/heatmap.test b/bolt/test/heatmap.test --- a/bolt/test/heatmap.test +++ b/bolt/test/heatmap.test @@ -1,12 +1,6 @@ # Verifies basic functioning of heatmap mode REQUIRES: system-linux -XFAIL: * -RUN: llvm-bolt heatmap --help | FileCheck %s --check-prefix=CHECK-SUBCOMMAND -CHECK-SUBCOMMAND: SUBCOMMAND 'heatmap': generate heatmap -CHECK-SUBCOMMAND: USAGE: llvm-bolt heatmap [options] - -RUN: llvm-bolt-heatmap --help | FileCheck %s --check-prefix=CHECK-STANDALONE -CHECK-STANDALONE: SUBCOMMAND 'heatmap': generate heatmap -CHECK-STANDALONE: USAGE: llvm-bolt-heatmap heatmap [options] +RUN: llvm-bolt-heatmap --help | FileCheck %s +CHECK: USAGE: llvm-bolt-heatmap [options] diff --git a/bolt/tools/CMakeLists.txt b/bolt/tools/CMakeLists.txt --- a/bolt/tools/CMakeLists.txt +++ b/bolt/tools/CMakeLists.txt @@ -1,2 +1,3 @@ add_subdirectory(driver) add_subdirectory(merge-fdata) +add_subdirectory(heatmap) diff --git a/bolt/tools/driver/CMakeLists.txt b/bolt/tools/driver/CMakeLists.txt --- a/bolt/tools/driver/CMakeLists.txt +++ b/bolt/tools/driver/CMakeLists.txt @@ -23,13 +23,11 @@ add_llvm_tool_symlink(perf2bolt llvm-bolt) add_llvm_tool_symlink(llvm-boltdiff llvm-bolt) -add_llvm_tool_symlink(llvm-bolt-heatmap llvm-bolt) set(BOLT_DEPENDS llvm-bolt perf2bolt llvm-boltdiff - llvm-bolt-heatmap ) add_custom_target(bolt DEPENDS ${BOLT_DEPENDS}) @@ -37,7 +35,6 @@ ${CMAKE_BINARY_DIR}/bin/llvm-bolt ${CMAKE_BINARY_DIR}/bin/perf2bolt ${CMAKE_BINARY_DIR}/bin/llvm-boltdiff - ${CMAKE_BINARY_DIR}/bin/llvm-bolt-heatmap TYPE BIN COMPONENT bolt ) diff --git a/bolt/tools/driver/llvm-bolt.cpp b/bolt/tools/driver/llvm-bolt.cpp --- a/bolt/tools/driver/llvm-bolt.cpp +++ b/bolt/tools/driver/llvm-bolt.cpp @@ -125,34 +125,6 @@ opts::AggregateOnly = true; } -void heatmapMode(int argc, char **argv) { - // Insert a fake subcommand if invoked via a command alias. - std::unique_ptr FakeArgv; - if (argc == 1 || strcmp(argv[1], "heatmap")) { - ++argc; - FakeArgv.reset(new char *[argc + 1]); - FakeArgv[0] = argv[0]; - FakeArgv[1] = const_cast("heatmap"); - for (int I = 2; I < argc; ++I) - FakeArgv[I] = argv[I - 1]; - FakeArgv[argc] = nullptr; - argv = FakeArgv.get(); - } - - cl::ParseCommandLineOptions(argc, argv, ""); - - if (!sys::fs::exists(opts::InputFilename)) - report_error(opts::InputFilename, errc::no_such_file_or_directory); - - if (opts::PerfData.empty()) { - errs() << ToolName << ": expected -perfdata= option.\n"; - exit(1); - } - - opts::HeatmapMode = true; - opts::AggregateOnly = true; -} - void boltDiffMode(int argc, char **argv) { cl::HideUnrelatedOptions(makeArrayRef(opts::BoltDiffCategories)); cl::AddExtraVersionPrinter(printBoltRevision); @@ -194,8 +166,8 @@ } } -std::string GetExecutablePath(const char *Argv0) { - SmallString<128> ExecutablePath(Argv0); +static std::string GetExecutablePath(const char *Argv0) { + SmallString<256> ExecutablePath(Argv0); // Do a PATH lookup if Argv0 isn't a valid path. if (!llvm::sys::fs::exists(ExecutablePath)) if (llvm::ErrorOr P = @@ -224,19 +196,10 @@ ToolName = argv[0]; - // Pre-process subcommands. - if (argc > 1 && *argv[1] != '-') { - if (!strcmp(argv[1], "heatmap")) - opts::HeatmapMode = true; - } - if (llvm::sys::path::filename(ToolName) == "perf2bolt") perf2boltMode(argc, argv); else if (llvm::sys::path::filename(ToolName) == "llvm-boltdiff") boltDiffMode(argc, argv); - else if (llvm::sys::path::filename(ToolName) == "llvm-bolt-heatmap" || - opts::HeatmapMode) - heatmapMode(argc, argv); else boltMode(argc, argv); diff --git a/bolt/tools/heatmap/CMakeLists.txt b/bolt/tools/heatmap/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/bolt/tools/heatmap/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + BOLTRewrite + ) + +add_llvm_tool(llvm-bolt-heatmap + heatmap.cpp + ) + +set_target_properties(llvm-bolt-heatmap PROPERTIES FOLDER "BOLT") diff --git a/bolt/tools/heatmap/heatmap.cpp b/bolt/tools/heatmap/heatmap.cpp new file mode 100644 --- /dev/null +++ b/bolt/tools/heatmap/heatmap.cpp @@ -0,0 +1,95 @@ +#include "bolt/Profile/DataAggregator.h" +#include "bolt/Rewrite/RewriteInstance.h" +#include "bolt/Utils/CommandLineOpts.h" +#include "llvm/MC/TargetRegistry.h" +#include "llvm/Object/Binary.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetSelect.h" + +using namespace llvm; +using namespace bolt; + +namespace opts { + +static cl::OptionCategory *HeatmapCategories[] = {&HeatmapCategory, + &BoltOutputCategory}; + +cl::opt InputFilename(cl::Positional, cl::desc(""), + cl::Required, cl::cat(HeatmapCategory)); + +} // namespace opts + +static StringRef ToolName; + +static void report_error(StringRef Message, std::error_code EC) { + assert(EC); + errs() << ToolName << ": '" << Message << "': " << EC.message() << ".\n"; + exit(1); +} + +static void report_error(StringRef Message, Error E) { + assert(E); + errs() << ToolName << ": '" << Message << "': " << toString(std::move(E)) + << ".\n"; + exit(1); +} + +static std::string GetExecutablePath(const char *Argv0) { + SmallString<256> ExecutablePath(Argv0); + // Do a PATH lookup if Argv0 isn't a valid path. + if (!llvm::sys::fs::exists(ExecutablePath)) + if (llvm::ErrorOr P = + llvm::sys::findProgramByName(ExecutablePath)) + ExecutablePath = *P; + return std::string(ExecutablePath.str()); +} + +int main(int argc, char **argv) { + cl::HideUnrelatedOptions(makeArrayRef(opts::HeatmapCategories)); + cl::ParseCommandLineOptions(argc, argv, ""); + + if (opts::PerfData.empty()) { + errs() << ToolName << ": expected -perfdata= option.\n"; + exit(1); + } + + opts::HeatmapMode = true; + opts::AggregateOnly = true; + if (!sys::fs::exists(opts::InputFilename)) + report_error(opts::InputFilename, errc::no_such_file_or_directory); + + // Output to stdout by default + if (opts::OutputFilename.empty()) + opts::OutputFilename = "-"; + + // Initialize targets and assembly printers/parsers. + llvm::InitializeAllTargetInfos(); + llvm::InitializeAllTargetMCs(); + llvm::InitializeAllAsmParsers(); + llvm::InitializeAllDisassemblers(); + + llvm::InitializeAllTargets(); + llvm::InitializeAllAsmPrinters(); + + ToolName = argv[0]; + std::string ToolPath = GetExecutablePath(argv[0]); + Expected> BinaryOrErr = + createBinary(opts::InputFilename); + if (Error E = BinaryOrErr.takeError()) + report_error(opts::InputFilename, std::move(E)); + Binary &Binary = *BinaryOrErr.get().getBinary(); + + if (auto *e = dyn_cast(&Binary)) { + RewriteInstance RI(e, argc, argv, ToolPath); + if (Error E = RI.setProfile(opts::PerfData)) + report_error(opts::PerfData, std::move(E)); + + RI.run(); + } else { + report_error(opts::InputFilename, object_error::invalid_file_type); + } + + return EXIT_SUCCESS; +}