about summary refs log tree commit diff
diff options
context:
space:
mode:
authorWesley Wiser <wwiser@gmail.com>2020-01-31 18:58:28 -0500
committerWesley Wiser <wwiser@gmail.com>2020-02-01 14:19:17 -0500
commitf5f86be1d40f30b3183ec148f443afa73d0cbe15 (patch)
treeb502ea8bf896068391dadc47ac11b9f6858fc219
parentcd1ef390e731ed77b90b11b1f77e2c5ca641b261 (diff)
downloadrust-f5f86be1d40f30b3183ec148f443afa73d0cbe15.tar.gz
rust-f5f86be1d40f30b3183ec148f443afa73d0cbe15.zip
Add support for enabling the LLVM time-trace feature
I found this helpful while investigating an LLVM performance issue.
Passing `-Z llvm-time-trace` causes a `llvm_timings.json` file to be
created. This file can be inspected in either the Chrome Profiler tools
or with any other compatible tool like SpeedScope.

More information on the LLVM feature:

- https://aras-p.info/blog/2019/01/16/time-trace-timeline-flame-chart-profiler-for-Clang/

- https://reviews.llvm.org/rL357340
-rw-r--r--src/librustc_codegen_llvm/lib.rs6
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs4
-rw-r--r--src/librustc_codegen_llvm/llvm_util.rs18
-rw-r--r--src/librustc_session/options.rs2
-rw-r--r--src/rustllvm/PassWrapper.cpp18
5 files changed, 48 insertions, 0 deletions
diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs
index 70e3874035b..60771d385ae 100644
--- a/src/librustc_codegen_llvm/lib.rs
+++ b/src/librustc_codegen_llvm/lib.rs
@@ -332,6 +332,12 @@ impl CodegenBackend for LlvmCodegenBackend {
         // any more, we can finalize it (which involves renaming it)
         rustc_incremental::finalize_session_directory(sess, codegen_results.crate_hash);
 
+        sess.time("llvm_dump_timing_file", || {
+            if sess.opts.debugging_opts.llvm_time_trace {
+                llvm_util::time_trace_profiler_finish("llvm_timings.json");
+            }
+        });
+
         Ok(())
     }
 }
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index 875b2c47b3b..3f37f86676c 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -1454,6 +1454,10 @@ extern "C" {
 
     pub fn LLVMInitializePasses();
 
+    pub fn LLVMTimeTraceProfilerInitialize();
+
+    pub fn LLVMTimeTraceProfilerFinish(FileName: *const c_char);
+
     pub fn LLVMAddAnalysisPasses(T: &'a TargetMachine, PM: &PassManager<'a>);
 
     pub fn LLVMPassManagerBuilderCreate() -> &'static mut PassManagerBuilder;
diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs
index 4823fe10c46..6d3498f8b80 100644
--- a/src/librustc_codegen_llvm/llvm_util.rs
+++ b/src/librustc_codegen_llvm/llvm_util.rs
@@ -113,6 +113,15 @@ unsafe fn configure_llvm(sess: &Session) {
         }
     }
 
+    if sess.opts.debugging_opts.llvm_time_trace && get_major_version() >= 9 {
+        // time-trace is not thread safe and running it in parallel will cause seg faults.
+        if !sess.opts.debugging_opts.no_parallel_llvm {
+            bug!("`-Z llvm-time-trace` requires `-Z no-parallel-llvm")
+        }
+
+        llvm::LLVMTimeTraceProfilerInitialize();
+    }
+
     llvm::LLVMInitializePasses();
 
     ::rustc_llvm::initialize_available_targets();
@@ -120,6 +129,15 @@ unsafe fn configure_llvm(sess: &Session) {
     llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, llvm_args.as_ptr());
 }
 
+pub fn time_trace_profiler_finish(file_name: &str) {
+    unsafe {
+        if get_major_version() >= 9 {
+            let file_name = CString::new(file_name).unwrap();
+            llvm::LLVMTimeTraceProfilerFinish(file_name.as_ptr());
+        }
+    }
+}
+
 // WARNING: the features after applying `to_llvm_feature` must be known
 // to LLVM or the feature detection code will walk past the end of the feature
 // array, leading to crashes.
diff --git a/src/librustc_session/options.rs b/src/librustc_session/options.rs
index 34da2188a51..a18b6eb4402 100644
--- a/src/librustc_session/options.rs
+++ b/src/librustc_session/options.rs
@@ -718,6 +718,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "measure time of rustc processes"),
     time_llvm_passes: bool = (false, parse_bool, [UNTRACKED],
         "measure time of each LLVM pass"),
+    llvm_time_trace: bool = (false, parse_bool, [UNTRACKED],
+        "generate JSON tracing data file from LLVM data"),
     input_stats: bool = (false, parse_bool, [UNTRACKED],
         "gather statistics about the input"),
     asm_comments: bool = (false, parse_bool, [TRACKED],
diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp
index fad70369807..4ac7e0e6e1f 100644
--- a/src/rustllvm/PassWrapper.cpp
+++ b/src/rustllvm/PassWrapper.cpp
@@ -26,6 +26,7 @@
 #include "llvm/Transforms/Instrumentation.h"
 #if LLVM_VERSION_GE(9, 0)
 #include "llvm/Transforms/Instrumentation/AddressSanitizer.h"
+#include "llvm/Support/TimeProfiler.h"
 #endif
 #if LLVM_VERSION_GE(8, 0)
 #include "llvm/Transforms/Instrumentation/ThreadSanitizer.h"
@@ -57,6 +58,23 @@ extern "C" void LLVMInitializePasses() {
   initializeTarget(Registry);
 }
 
+extern "C" void LLVMTimeTraceProfilerInitialize() {
+#if LLVM_VERSION_GE(9, 0)
+  timeTraceProfilerInitialize();
+#endif
+}
+
+extern "C" void LLVMTimeTraceProfilerFinish(const char* FileName) {
+#if LLVM_VERSION_GE(9, 0)
+  StringRef FN(FileName);
+  std::error_code EC;
+  raw_fd_ostream OS(FN, EC, sys::fs::CD_CreateAlways);
+
+  timeTraceProfilerWrite(OS);
+  timeTraceProfilerCleanup();
+#endif
+}
+
 enum class LLVMRustPassKind {
   Other,
   Function,