[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