[lld] 50c1b21 - [lld-macho] minimal TimeTrace support
Thorsten Schütt via llvm-commits
llvm-commits at lists.llvm.org
Thu Mar 11 06:30:51 PST 2021
Author: Thorsten Schütt
Date: 2021-03-11T15:30:45+01:00
New Revision: 50c1b21851a175a38d82c482b7a589dfb0bf4c68
URL: https://github.com/llvm/llvm-project/commit/50c1b21851a175a38d82c482b7a589dfb0bf4c68
DIFF: https://github.com/llvm/llvm-project/commit/50c1b21851a175a38d82c482b7a589dfb0bf4c68.diff
LOG: [lld-macho] minimal TimeTrace support
This is the minimal port from ELF. Any extension should easy from here
Test plan: ninja check-all-macho
Reviewed By: #lld-macho, thakis
Differential Revision: https://reviews.llvm.org/D98419
Added:
lld/test/MachO/time-trace.s
Modified:
lld/MachO/Config.h
lld/MachO/Driver.cpp
lld/MachO/Options.td
Removed:
################################################################################
diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index 6c85cd595070..53c2c6ae1574 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -81,9 +81,12 @@ struct Configuration {
bool searchDylibsFirst = false;
bool saveTemps = false;
bool adhocCodesign = false;
+ bool timeTraceEnabled = false;
uint32_t headerPad;
uint32_t dylibCompatibilityVersion = 0;
uint32_t dylibCurrentVersion = 0;
+ uint32_t timeTraceGranularity;
+ std::string progName;
llvm::StringRef installName;
llvm::StringRef outputFile;
llvm::StringRef ltoObjPath;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index b0c255bee455..29582e00d0c9 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -41,6 +41,7 @@
#include "llvm/Support/Path.h"
#include "llvm/Support/TarWriter.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/TimeProfiler.h"
#include "llvm/TextAPI/MachO/PackedVersion.h"
#include <algorithm>
@@ -935,114 +936,137 @@ bool macho::link(ArrayRef<const char *> argsArr, bool canExitEarly,
: ""));
}
- initLLVM(); // must be run before any call to addFile()
+ config->progName = argsArr[0];
- // This loop should be reserved for options whose exact ordering matters.
- // Other options should be handled via filtered() and/or getLastArg().
- for (const Arg *arg : args) {
- const Option &opt = arg->getOption();
- warnIfDeprecatedOption(opt);
- warnIfUnimplementedOption(opt);
+ config->timeTraceEnabled = args.hasArg(OPT_time_trace);
- switch (opt.getID()) {
- case OPT_INPUT:
- addFile(arg->getValue(), false);
- break;
- case OPT_weak_library:
- if (auto *dylibFile =
- dyn_cast_or_null<DylibFile>(addFile(arg->getValue(), false)))
- dylibFile->forceWeakImport = true;
- break;
- case OPT_filelist:
- addFileList(arg->getValue());
- break;
- case OPT_force_load:
- addFile(arg->getValue(), true);
- break;
- case OPT_l:
- case OPT_weak_l:
- addLibrary(arg->getValue(), opt.getID() == OPT_weak_l);
- break;
- case OPT_framework:
- case OPT_weak_framework:
- addFramework(arg->getValue(), opt.getID() == OPT_weak_framework);
- break;
- default:
- break;
+ // Initialize time trace profiler.
+ if (config->timeTraceEnabled)
+ timeTraceProfilerInitialize(config->timeTraceGranularity, config->progName);
+
+ {
+ llvm::TimeTraceScope timeScope("Link", StringRef("ExecuteLinker"));
+
+ initLLVM(); // must be run before any call to addFile()
+
+ // This loop should be reserved for options whose exact ordering matters.
+ // Other options should be handled via filtered() and/or getLastArg().
+ for (const Arg *arg : args) {
+ const Option &opt = arg->getOption();
+ warnIfDeprecatedOption(opt);
+ warnIfUnimplementedOption(opt);
+
+ switch (opt.getID()) {
+ case OPT_INPUT:
+ addFile(arg->getValue(), false);
+ break;
+ case OPT_weak_library:
+ if (auto *dylibFile =
+ dyn_cast_or_null<DylibFile>(addFile(arg->getValue(), false)))
+ dylibFile->forceWeakImport = true;
+ break;
+ case OPT_filelist:
+ addFileList(arg->getValue());
+ break;
+ case OPT_force_load:
+ addFile(arg->getValue(), true);
+ break;
+ case OPT_l:
+ case OPT_weak_l:
+ addLibrary(arg->getValue(), opt.getID() == OPT_weak_l);
+ break;
+ case OPT_framework:
+ case OPT_weak_framework:
+ addFramework(arg->getValue(), opt.getID() == OPT_weak_framework);
+ break;
+ default:
+ break;
+ }
}
- }
- config->isPic = config->outputType == MH_DYLIB ||
- config->outputType == MH_BUNDLE || isPie(args);
-
- // Now that all dylibs have been loaded, search for those that should be
- // re-exported.
- for (const Arg *arg : args.filtered(OPT_sub_library, OPT_sub_umbrella)) {
- config->hasReexports = true;
- StringRef searchName = arg->getValue();
- std::vector<StringRef> extensions;
- if (arg->getOption().getID() == OPT_sub_library)
- extensions = {".dylib", ".tbd"};
- else
- extensions = {".tbd"};
- if (!markReexport(searchName, extensions))
- error(arg->getSpelling() + " " + searchName +
- " does not match a supplied dylib");
- }
+ config->isPic = config->outputType == MH_DYLIB ||
+ config->outputType == MH_BUNDLE || isPie(args);
+
+ // Now that all dylibs have been loaded, search for those that should be
+ // re-exported.
+ for (const Arg *arg : args.filtered(OPT_sub_library, OPT_sub_umbrella)) {
+ config->hasReexports = true;
+ StringRef searchName = arg->getValue();
+ std::vector<StringRef> extensions;
+ if (arg->getOption().getID() == OPT_sub_library)
+ extensions = {".dylib", ".tbd"};
+ else
+ extensions = {".tbd"};
+ if (!markReexport(searchName, extensions))
+ error(arg->getSpelling() + " " + searchName +
+ " does not match a supplied dylib");
+ }
- // Parse LTO options.
- if (const Arg *arg = args.getLastArg(OPT_mcpu))
- parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
- arg->getSpelling());
+ // Parse LTO options.
+ if (const Arg *arg = args.getLastArg(OPT_mcpu))
+ parseClangOption(saver.save("-mcpu=" + StringRef(arg->getValue())),
+ arg->getSpelling());
- for (const Arg *arg : args.filtered(OPT_mllvm))
- parseClangOption(arg->getValue(), arg->getSpelling());
+ for (const Arg *arg : args.filtered(OPT_mllvm))
+ parseClangOption(arg->getValue(), arg->getSpelling());
- compileBitcodeFiles();
- replaceCommonSymbols();
+ compileBitcodeFiles();
+ replaceCommonSymbols();
- StringRef orderFile = args.getLastArgValue(OPT_order_file);
- if (!orderFile.empty())
- parseOrderFile(orderFile);
+ StringRef orderFile = args.getLastArgValue(OPT_order_file);
+ if (!orderFile.empty())
+ parseOrderFile(orderFile);
- if (config->outputType == MH_EXECUTE && isa<Undefined>(config->entry)) {
- error("undefined symbol: " + toString(*config->entry));
- return false;
- }
- // FIXME: This prints symbols that are undefined both in input files and
- // via -u flag twice.
- for (const Symbol *undefined : config->explicitUndefineds) {
- if (isa<Undefined>(undefined)) {
- error("undefined symbol: " + toString(*undefined) +
- "\n>>> referenced by flag -u " + toString(*undefined));
+ if (config->outputType == MH_EXECUTE && isa<Undefined>(config->entry)) {
+ error("undefined symbol: " + toString(*config->entry));
return false;
}
- }
+ // FIXME: This prints symbols that are undefined both in input files and
+ // via -u flag twice.
+ for (const Symbol *undefined : config->explicitUndefineds) {
+ if (isa<Undefined>(undefined)) {
+ error("undefined symbol: " + toString(*undefined) +
+ "\n>>> referenced by flag -u " + toString(*undefined));
+ return false;
+ }
+ }
- createSyntheticSections();
- symtab->addDSOHandle(in.header);
+ createSyntheticSections();
+ symtab->addDSOHandle(in.header);
- for (const Arg *arg : args.filtered(OPT_sectcreate)) {
- StringRef segName = arg->getValue(0);
- StringRef sectName = arg->getValue(1);
- StringRef fileName = arg->getValue(2);
- Optional<MemoryBufferRef> buffer = readFile(fileName);
- if (buffer)
- inputFiles.insert(make<OpaqueFile>(*buffer, segName, sectName));
- }
+ for (const Arg *arg : args.filtered(OPT_sectcreate)) {
+ StringRef segName = arg->getValue(0);
+ StringRef sectName = arg->getValue(1);
+ StringRef fileName = arg->getValue(2);
+ Optional<MemoryBufferRef> buffer = readFile(fileName);
+ if (buffer)
+ inputFiles.insert(make<OpaqueFile>(*buffer, segName, sectName));
+ }
- // Initialize InputSections.
- for (const InputFile *file : inputFiles) {
- for (const SubsectionMap &map : file->subsections) {
- for (const auto &p : map) {
- InputSection *isec = p.second;
- inputSections.push_back(isec);
+ // Initialize InputSections.
+ for (const InputFile *file : inputFiles) {
+ for (const SubsectionMap &map : file->subsections) {
+ for (const auto &p : map) {
+ InputSection *isec = p.second;
+ inputSections.push_back(isec);
+ }
}
}
+
+ // Write to an output file.
+ writeResult();
}
- // Write to an output file.
- writeResult();
+ if (config->timeTraceEnabled) {
+ if (auto E = timeTraceProfilerWrite(
+ args.getLastArgValue(OPT_time_trace_file_eq).str(),
+ config->outputFile)) {
+ handleAllErrors(std::move(E),
+ [&](const StringError &SE) { error(SE.getMessage()); });
+ }
+
+ timeTraceProfilerCleanup();
+ }
if (canExitEarly)
exitLld(errorCount() ? 1 : 0);
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index 74eaabcaded4..6af0d2c4152f 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -35,7 +35,10 @@ def lto_legacy_pass_manager: Flag<["--"], "lto-legacy-pass-manager">,
def no_lto_legacy_pass_manager : Flag<["--"], "no-lto-legacy-pass-manager">,
HelpText<"Use the new pass manager in LLVM">,
Group<grp_lld>;
-
+def time_trace: Flag<["--"], "time-trace">, HelpText<"Record time trace">;
+def time_trace_granularity: Flag<["--"], "time-trace-granularity">,
+ HelpText<"Minimum time granularity (in microseconds) traced by time profiler">;
+def time_trace_file_eq: Flag<["--"], "time-trace-file=">, HelpText<"Specify time trace output file">;
// This is a complete Options.td compiled from Apple's ld(1) manpage
// dated 2018-03-07 and cross checked with ld64 source code in repo
diff --git a/lld/test/MachO/time-trace.s b/lld/test/MachO/time-trace.s
new file mode 100644
index 000000000000..cb31dd706f9d
--- /dev/null
+++ b/lld/test/MachO/time-trace.s
@@ -0,0 +1,42 @@
+# REQUIRES: x86
+# RUN: llvm-mc -filetype=obj -triple=x86_64 %s -o %t.o
+
+# Test implicit trace file name
+# RUN: ld.lld --time-trace --time-trace-granularity=0 -o %t1.macho %t.o
+# RUN: cat %t1.macho.time-trace \
+# RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
+# RUN: | FileCheck %s
+
+# Test specified trace file name
+# RUN: ld.lld --time-trace --time-trace-file=%t2.json --time-trace-granularity=0 -o %t2.macho %t.o
+# RUN: cat %t2.json \
+# RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
+# RUN: | FileCheck %s
+
+# Test trace requested to stdout
+# RUN: ld.lld --time-trace --time-trace-file=- --time-trace-granularity=0 -o %t3.macho %t.o \
+# RUN: | %python -c 'import json, sys; json.dump(json.loads(sys.stdin.read()), sys.stdout, sort_keys=True, indent=2)' \
+# RUN: | FileCheck %s
+
+# CHECK: "beginningOfTime": {{[0-9]{16},}}
+# CHECK-NEXT: "traceEvents": [
+
+# Check one event has correct fields
+# CHECK: "dur":
+# CHECK-NEXT: "name":
+# CHECK-NEXT: "ph":
+# CHECK-NEXT: "pid":
+# CHECK-NEXT: "tid":
+# CHECK-NEXT: "ts":
+
+# Check there is an ExecuteLinker event
+# CHECK: "name": "ExecuteLinker"
+
+# Check process_name entry field
+# CHECK: "name": "ld.lld{{(.exe)?}}"
+# CHECK: "name": "process_name"
+# CHECK: "name": "thread_name"
+
+.globl _start
+_start:
+ ret
More information about the llvm-commits
mailing list