about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2017-06-14 08:46:14 +0000
committerbors <bors@rust-lang.org>2017-06-14 08:46:14 +0000
commitdfa7e21e4ee555d04c0fb86069f5acffee3550ad (patch)
tree8e29f75d4d8a37b5205a1ae76c9f0a4588ddec43
parente40ef964fe491b19c22dfb8dd36d1eab14223c36 (diff)
parent5c084fd8edd986d8a4bd9ff37b303f8777623a56 (diff)
downloadrust-dfa7e21e4ee555d04c0fb86069f5acffee3550ad.tar.gz
rust-dfa7e21e4ee555d04c0fb86069f5acffee3550ad.zip
Auto merge of #42433 - marco-c:profiling, r=alexcrichton
Build instruction profiler runtime as part of compiler-rt

r? @alexcrichton

This is #38608 with some fixes.

Still missing:
- [x] testing with profiler enabled on some builders (on which ones? Should I add the option to some of the already existing configurations, or create a new configuration?);
- [x] enabling distribution (on which builders?);
- [x] documentation.
-rw-r--r--.travis.yml6
-rw-r--r--appveyor.yml4
-rwxr-xr-xconfigure1
-rw-r--r--src/Cargo.lock9
-rw-r--r--src/bootstrap/check.rs4
-rw-r--r--src/bootstrap/config.rs4
-rw-r--r--src/bootstrap/config.toml.example3
-rw-r--r--src/bootstrap/dist.rs1
-rw-r--r--src/bootstrap/lib.rs3
-rw-r--r--src/ci/docker/dist-i686-linux/Dockerfile3
-rw-r--r--src/ci/docker/dist-x86_64-linux/Dockerfile3
-rw-r--r--src/ci/docker/x86_64-gnu/Dockerfile2
-rw-r--r--src/doc/unstable-book/src/SUMMARY.md3
-rw-r--r--src/doc/unstable-book/src/compiler-flags/profile.md21
-rw-r--r--src/doc/unstable-book/src/language-features/profiler-runtime.md5
-rw-r--r--src/doc/unstable-book/src/library-features/profiler-runtime-lib.md5
-rw-r--r--src/libprofiler_builtins/Cargo.toml18
-rw-r--r--src/libprofiler_builtins/build.rs60
-rw-r--r--src/libprofiler_builtins/lib.rs20
-rw-r--r--src/librustc/middle/cstore.rs2
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc_driver/driver.rs8
-rw-r--r--src/librustc_llvm/ffi.rs4
-rw-r--r--src/librustc_metadata/creader.rs19
-rw-r--r--src/librustc_metadata/cstore.rs5
-rw-r--r--src/librustc_metadata/cstore_impl.rs4
-rw-r--r--src/librustc_trans/back/link.rs3
-rw-r--r--src/librustc_trans/back/write.rs4
-rw-r--r--src/librustc_trans/base.rs8
-rw-r--r--src/librustc_trans/context.rs12
-rw-r--r--src/librustc_trans/debuginfo/metadata.rs38
-rw-r--r--src/librustc_trans/debuginfo/mod.rs2
-rw-r--r--src/libstd/Cargo.toml2
-rw-r--r--src/libsyntax/feature_gate.rs11
-rw-r--r--src/rustllvm/RustWrapper.cpp4
-rw-r--r--src/test/compile-fail/feature-gate-profiler-runtime.rs13
-rw-r--r--src/test/run-make/profile/Makefile9
-rw-r--r--src/test/run-make/profile/test.rs11
38 files changed, 319 insertions, 17 deletions
diff --git a/.travis.yml b/.travis.yml
index 40fecb3ee5e..376711517e1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -54,7 +54,7 @@ matrix:
     # version that we're using, 8.2, cannot compile LLVM for OSX 10.7.
     - env: >
         RUST_CHECK_TARGET=check
-        RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers"
+        RUST_CONFIGURE_ARGS="--build=x86_64-apple-darwin --enable-sanitizers --enable-profiler"
         SRC=.
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
         SCCACHE_ERROR_LOG=/tmp/sccache.log
@@ -87,7 +87,7 @@ matrix:
     # OSX 10.7 and `xcode7` is the latest Xcode able to compile LLVM for 10.7.
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended"
+        RUST_CONFIGURE_ARGS="--build=i686-apple-darwin --enable-extended --enable-profiler"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
@@ -101,7 +101,7 @@ matrix:
         - *osx_install_sccache
     - env: >
         RUST_CHECK_TARGET=dist
-        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers"
+        RUST_CONFIGURE_ARGS="--target=aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,i386-apple-ios,x86_64-apple-ios --enable-extended --enable-sanitizers --enable-profiler"
         SRC=.
         DEPLOY=1
         RUSTC_RETRY_LINKER_ON_SEGFAULT=1
diff --git a/appveyor.yml b/appveyor.yml
index 96de1d90f25..4711f34f830 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -7,7 +7,7 @@ environment:
   matrix:
   # 32/64 bit MSVC tests
   - MSYS_BITS: 64
-    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc
+    RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-profiler
     SCRIPT: python x.py test
   - MSYS_BITS: 32
     RUST_CONFIGURE_ARGS: --build=i686-pc-windows-msvc --target=i586-pc-windows-msvc
@@ -48,12 +48,14 @@ environment:
   - RUST_CONFIGURE_ARGS: >
       --build=x86_64-pc-windows-msvc
       --enable-extended
+      --enable-profiler
     SCRIPT: python x.py dist
     DEPLOY: 1
   - RUST_CONFIGURE_ARGS: >
       --build=i686-pc-windows-msvc
       --target=i586-pc-windows-msvc
       --enable-extended
+      --enable-profiler
     SCRIPT: python x.py dist
     DEPLOY: 1
   - MSYS_BITS: 32
diff --git a/configure b/configure
index af59d5b0bb8..2b493ee91b5 100755
--- a/configure
+++ b/configure
@@ -452,6 +452,7 @@ opt vendor 0 "enable usage of vendored Rust crates"
 opt sanitizers 0 "build the sanitizer runtimes (asan, lsan, msan, tsan)"
 opt dist-src 1 "when building tarballs enables building a source tarball"
 opt cargo-openssl-static 0 "static openssl in cargo"
+opt profiler 0 "build the profiler runtime"
 
 # Optimization and debugging options. These may be overridden by the release channel, etc.
 opt_nosave optimize 1 "build optimized rust code"
diff --git a/src/Cargo.lock b/src/Cargo.lock
index efbbe36c981..4c97b8923ec 100644
--- a/src/Cargo.lock
+++ b/src/Cargo.lock
@@ -892,6 +892,14 @@ dependencies = [
 ]
 
 [[package]]
+name = "profiler_builtins"
+version = "0.0.0"
+dependencies = [
+ "core 0.0.0",
+ "gcc 0.3.50 (registry+https://github.com/rust-lang/crates.io-index)",
+]
+
+[[package]]
 name = "psapi-sys"
 version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1593,6 +1601,7 @@ dependencies = [
  "libc 0.0.0",
  "panic_abort 0.0.0",
  "panic_unwind 0.0.0",
+ "profiler_builtins 0.0.0",
  "rand 0.0.0",
  "rustc_asan 0.0.0",
  "rustc_lsan 0.0.0",
diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs
index 385376333c1..5ecd752d0ce 100644
--- a/src/bootstrap/check.rs
+++ b/src/bootstrap/check.rs
@@ -299,6 +299,10 @@ pub fn compiletest(build: &Build,
         cmd.env("SANITIZER_SUPPORT", "1");
     }
 
+    if build.config.profiler {
+        cmd.env("PROFILER_SUPPORT", "1");
+    }
+
     cmd.arg("--adb-path").arg("adb");
     cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
     if target.contains("android") {
diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs
index abad216d89b..64b2a665e25 100644
--- a/src/bootstrap/config.rs
+++ b/src/bootstrap/config.rs
@@ -50,6 +50,7 @@ pub struct Config {
     pub full_bootstrap: bool,
     pub extended: bool,
     pub sanitizers: bool,
+    pub profiler: bool,
 
     // llvm codegen options
     pub llvm_assertions: bool,
@@ -162,6 +163,7 @@ struct Build {
     extended: Option<bool>,
     verbose: Option<usize>,
     sanitizers: Option<bool>,
+    profiler: Option<bool>,
     openssl_static: Option<bool>,
 }
 
@@ -318,6 +320,7 @@ impl Config {
         set(&mut config.extended, build.extended);
         set(&mut config.verbose, build.verbose);
         set(&mut config.sanitizers, build.sanitizers);
+        set(&mut config.profiler, build.profiler);
         set(&mut config.openssl_static, build.openssl_static);
 
         if let Some(ref install) = toml.install {
@@ -471,6 +474,7 @@ impl Config {
                 ("FULL_BOOTSTRAP", self.full_bootstrap),
                 ("EXTENDED", self.extended),
                 ("SANITIZERS", self.sanitizers),
+                ("PROFILER", self.profiler),
                 ("DIST_SRC", self.rust_dist_src),
                 ("CARGO_OPENSSL_STATIC", self.openssl_static),
             }
diff --git a/src/bootstrap/config.toml.example b/src/bootstrap/config.toml.example
index 0eb6c4c82c4..3a467dafbfb 100644
--- a/src/bootstrap/config.toml.example
+++ b/src/bootstrap/config.toml.example
@@ -147,6 +147,9 @@
 # Build the sanitizer runtimes
 #sanitizers = false
 
+# Build the profiler runtime
+#profiler = false
+
 # Indicates whether the OpenSSL linked into Cargo will be statically linked or
 # not. If static linkage is specified then the build system will download a
 # known-good version of OpenSSL, compile it, and link it to Cargo.
diff --git a/src/bootstrap/dist.rs b/src/bootstrap/dist.rs
index 26d44ae7693..ebf602373c9 100644
--- a/src/bootstrap/dist.rs
+++ b/src/bootstrap/dist.rs
@@ -570,6 +570,7 @@ pub fn rust_src(build: &Build) {
         "src/libgetopts",
         "src/compiler-rt",
         "src/jemalloc",
+        "src/libprofiler_builtins",
     ];
     let std_src_dirs_exclude = [
         "src/compiler-rt/test",
diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs
index 2fe6a2a3ae8..c8cd71f8f28 100644
--- a/src/bootstrap/lib.rs
+++ b/src/bootstrap/lib.rs
@@ -594,6 +594,9 @@ impl Build {
         if self.config.backtrace {
             features.push_str(" backtrace");
         }
+        if self.config.profiler {
+            features.push_str(" profiler");
+        }
         return features
     }
 
diff --git a/src/ci/docker/dist-i686-linux/Dockerfile b/src/ci/docker/dist-i686-linux/Dockerfile
index a3c08e93ed1..9145e9dfc8d 100644
--- a/src/ci/docker/dist-i686-linux/Dockerfile
+++ b/src/ci/docker/dist-i686-linux/Dockerfile
@@ -90,7 +90,8 @@ ENV HOSTS=i686-unknown-linux-gnu
 ENV RUST_CONFIGURE_ARGS \
       --host=$HOSTS \
       --enable-extended \
-      --enable-sanitizers
+      --enable-sanitizers \
+      --enable-profiler
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 
 # This is the only builder which will create source tarballs
diff --git a/src/ci/docker/dist-x86_64-linux/Dockerfile b/src/ci/docker/dist-x86_64-linux/Dockerfile
index e2e42836dcd..78b62839a35 100644
--- a/src/ci/docker/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/dist-x86_64-linux/Dockerfile
@@ -90,7 +90,8 @@ ENV HOSTS=x86_64-unknown-linux-gnu
 ENV RUST_CONFIGURE_ARGS \
       --host=$HOSTS \
       --enable-extended \
-      --enable-sanitizers
+      --enable-sanitizers \
+      --enable-profiler
 ENV SCRIPT python2.7 ../x.py dist --host $HOSTS --target $HOSTS
 
 # This is the only builder which will create source tarballs
diff --git a/src/ci/docker/x86_64-gnu/Dockerfile b/src/ci/docker/x86_64-gnu/Dockerfile
index de85e1ff36a..0bbbded57f2 100644
--- a/src/ci/docker/x86_64-gnu/Dockerfile
+++ b/src/ci/docker/x86_64-gnu/Dockerfile
@@ -22,5 +22,5 @@ RUN curl -OL https://github.com/Yelp/dumb-init/releases/download/v1.2.0/dumb-ini
     rm dumb-init_*.deb
 ENTRYPOINT ["/usr/bin/dumb-init", "--"]
 
-ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-sanitizers
+ENV RUST_CONFIGURE_ARGS --build=x86_64-unknown-linux-gnu --enable-sanitizers --enable-profiler
 ENV SCRIPT python2.7 ../x.py test
diff --git a/src/doc/unstable-book/src/SUMMARY.md b/src/doc/unstable-book/src/SUMMARY.md
index b278b5dbb2c..39922b7bbcf 100644
--- a/src/doc/unstable-book/src/SUMMARY.md
+++ b/src/doc/unstable-book/src/SUMMARY.md
@@ -2,6 +2,7 @@
 
 - [Compiler flags](compiler-flags.md)
     - [linker_flavor](compiler-flags/linker-flavor.md)
+    - [profile](compiler-flags/profile.md)
     - [remap_path_prefix](compiler-flags/remap-path-prefix.md)
 - [Language features](language-features.md)
     - [abi_msp430_interrupt](language-features/abi-msp430-interrupt.md)
@@ -70,6 +71,7 @@
     - [plugin_registrar](language-features/plugin-registrar.md)
     - [prelude_import](language-features/prelude-import.md)
     - [proc_macro](language-features/proc-macro.md)
+    - [profiler_runtime](language-features/profiler-runtime.md)
     - [quote](language-features/quote.md)
     - [repr_align](language-features/repr-align.md)
     - [repr_simd](language-features/repr-simd.md)
@@ -178,6 +180,7 @@
     - [placement_new_protocol](library-features/placement-new-protocol.md)
     - [print_internals](library-features/print-internals.md)
     - [proc_macro_internals](library-features/proc-macro-internals.md)
+    - [profiler_runtime_lib](library-features/sanitizer-runtime-lib.md)
     - [question_mark_carrier](library-features/question-mark-carrier.md)
     - [rand](library-features/rand.md)
     - [range_contains](library-features/range-contains.md)
diff --git a/src/doc/unstable-book/src/compiler-flags/profile.md b/src/doc/unstable-book/src/compiler-flags/profile.md
new file mode 100644
index 00000000000..66d14fd0997
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/profile.md
@@ -0,0 +1,21 @@
+# `profile`
+
+The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).
+
+------------------------
+
+This feature allows the generation of code coverage reports.
+
+Set the `-Zprofile` compiler flag in order to enable gcov profiling.
+
+For example:
+```Bash
+cargo new testgcov --bin
+cd testgcov
+export RUSTFLAGS="-Zprofile"
+cargo build
+cargo run
+```
+
+Once you've built and run your program, files with the `gcno` (after build) and `gcda` (after execution) extensions will be created.
+You can parse them with [llvm-cov gcov](http://llvm.org/docs/CommandGuide/llvm-cov.html#llvm-cov-gcov) or [grcov](https://github.com/marco-c/grcov).
diff --git a/src/doc/unstable-book/src/language-features/profiler-runtime.md b/src/doc/unstable-book/src/language-features/profiler-runtime.md
new file mode 100644
index 00000000000..aee86f63952
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/profiler-runtime.md
@@ -0,0 +1,5 @@
+# `profiler_runtime`
+
+The tracking issue for this feature is: [#42524](https://github.com/rust-lang/rust/issues/42524).
+
+------------------------
diff --git a/src/doc/unstable-book/src/library-features/profiler-runtime-lib.md b/src/doc/unstable-book/src/library-features/profiler-runtime-lib.md
new file mode 100644
index 00000000000..a01f1e73ab4
--- /dev/null
+++ b/src/doc/unstable-book/src/library-features/profiler-runtime-lib.md
@@ -0,0 +1,5 @@
+# `profiler_runtime_lib`
+
+This feature is internal to the Rust compiler and is not intended for general use.
+
+------------------------
diff --git a/src/libprofiler_builtins/Cargo.toml b/src/libprofiler_builtins/Cargo.toml
new file mode 100644
index 00000000000..a60db313679
--- /dev/null
+++ b/src/libprofiler_builtins/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+authors = ["The Rust Project Developers"]
+build = "build.rs"
+name = "profiler_builtins"
+version = "0.0.0"
+
+[lib]
+name = "profiler_builtins"
+path = "lib.rs"
+test = false
+bench = false
+doc = false
+
+[dependencies]
+core = { path = "../libcore" }
+
+[build-dependencies]
+gcc = "0.3.50"
diff --git a/src/libprofiler_builtins/build.rs b/src/libprofiler_builtins/build.rs
new file mode 100644
index 00000000000..1ee284ff4da
--- /dev/null
+++ b/src/libprofiler_builtins/build.rs
@@ -0,0 +1,60 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Compiles the profiler part of the `compiler-rt` library.
+//!
+//! See the build.rs for libcompiler_builtins crate for details.
+
+extern crate gcc;
+
+use std::env;
+use std::path::Path;
+
+fn main() {
+    let target = env::var("TARGET").expect("TARGET was not set");
+    let cfg = &mut gcc::Config::new();
+
+    let mut profile_sources = vec!["GCDAProfiling.c",
+                                   "InstrProfiling.c",
+                                   "InstrProfilingBuffer.c",
+                                   "InstrProfilingFile.c",
+                                   "InstrProfilingMerge.c",
+                                   "InstrProfilingMergeFile.c",
+                                   "InstrProfilingPlatformDarwin.c",
+                                   "InstrProfilingPlatformLinux.c",
+                                   "InstrProfilingPlatformOther.c",
+                                   "InstrProfilingRuntime.cc",
+                                   "InstrProfilingUtil.c",
+                                   "InstrProfilingValue.c",
+                                   "InstrProfilingWriter.c"];
+
+    if target.contains("msvc") {
+        // Don't pull in extra libraries on MSVC
+        cfg.flag("/Zl");
+        profile_sources.push("WindowsMMap.c");
+        cfg.define("strdup", Some("_strdup"));
+        cfg.define("open", Some("_open"));
+        cfg.define("fdopen", Some("_fdopen"));
+    } else {
+        // Turn off various features of gcc and such, mostly copying
+        // compiler-rt's build system already
+        cfg.flag("-fno-builtin");
+        cfg.flag("-fvisibility=hidden");
+        cfg.flag("-fomit-frame-pointer");
+        cfg.flag("-ffreestanding");
+        cfg.define("VISIBILITY_HIDDEN", None);
+    }
+
+    for src in profile_sources {
+        cfg.file(Path::new("../compiler-rt/lib/profile").join(src));
+    }
+
+    cfg.compile("libprofiler-rt.a");
+}
diff --git a/src/libprofiler_builtins/lib.rs b/src/libprofiler_builtins/lib.rs
new file mode 100644
index 00000000000..087cc444185
--- /dev/null
+++ b/src/libprofiler_builtins/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![no_std]
+#![cfg_attr(not(stage0), feature(profiler_runtime))]
+#![cfg_attr(not(stage0), profiler_runtime)]
+#![unstable(feature = "profiler_runtime_lib",
+            reason = "internal implementation detail of rustc right now",
+            issue = "0")]
+#![crate_name = "profiler_builtins"]
+#![crate_type = "rlib"]
+#![allow(unused_features)]
+#![feature(staged_api)]
diff --git a/src/librustc/middle/cstore.rs b/src/librustc/middle/cstore.rs
index 08c0adf110a..15cfcbb244a 100644
--- a/src/librustc/middle/cstore.rs
+++ b/src/librustc/middle/cstore.rs
@@ -258,6 +258,7 @@ pub trait CrateStore {
     fn is_panic_runtime(&self, cnum: CrateNum) -> bool;
     fn is_compiler_builtins(&self, cnum: CrateNum) -> bool;
     fn is_sanitizer_runtime(&self, cnum: CrateNum) -> bool;
+    fn is_profiler_runtime(&self, cnum: CrateNum) -> bool;
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy;
     fn extern_crate(&self, cnum: CrateNum) -> Option<ExternCrate>;
     /// The name of the crate as it is referred to in source code of the current
@@ -381,6 +382,7 @@ impl CrateStore for DummyCrateStore {
     fn is_allocator(&self, cnum: CrateNum) -> bool { bug!("is_allocator") }
     fn is_panic_runtime(&self, cnum: CrateNum) -> bool { bug!("is_panic_runtime") }
     fn is_compiler_builtins(&self, cnum: CrateNum) -> bool { bug!("is_compiler_builtins") }
+    fn is_profiler_runtime(&self, cnum: CrateNum) -> bool { bug!("is_profiler_runtime") }
     fn is_sanitizer_runtime(&self, cnum: CrateNum) -> bool { bug!("is_sanitizer_runtime") }
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy {
         bug!("panic_strategy")
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 589489b49b4..f9459953518 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1033,6 +1033,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
         "a single extra argument to prepend the linker invocation (can be used several times)"),
     pre_link_args: Option<Vec<String>> = (None, parse_opt_list, [UNTRACKED],
         "extra arguments to prepend to the linker invocation (space separated)"),
+    profile: bool = (false, parse_bool, [TRACKED],
+                     "insert profiling code"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs
index bca82ff9a46..142454e0c4e 100644
--- a/src/librustc_driver/driver.rs
+++ b/src/librustc_driver/driver.rs
@@ -204,7 +204,8 @@ pub fn compile_input(sess: &Session,
                 println!("Pre-trans");
                 tcx.print_debug_stats();
             }
-            let trans = phase_4_translate_to_llvm(tcx, analysis, &incremental_hashes_map);
+            let trans = phase_4_translate_to_llvm(tcx, analysis, &incremental_hashes_map,
+                                                  &outputs);
 
             if log_enabled!(::log::LogLevel::Info) {
                 println!("Post-trans");
@@ -1042,7 +1043,8 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
 /// be discarded.
 pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                            analysis: ty::CrateAnalysis,
-                                           incremental_hashes_map: &IncrementalHashesMap)
+                                           incremental_hashes_map: &IncrementalHashesMap,
+                                           output_filenames: &OutputFilenames)
                                            -> trans::CrateTranslation {
     let time_passes = tcx.sess.time_passes();
 
@@ -1053,7 +1055,7 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     let translation =
         time(time_passes,
              "translation",
-             move || trans::trans_crate(tcx, analysis, &incremental_hashes_map));
+             move || trans::trans_crate(tcx, analysis, &incremental_hashes_map, output_filenames));
 
     time(time_passes,
          "assert dep graph",
diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs
index d7b575d90a6..312b5a38d6e 100644
--- a/src/librustc_llvm/ffi.rs
+++ b/src/librustc_llvm/ffi.rs
@@ -591,7 +591,9 @@ extern "C" {
     pub fn LLVMIsUndef(Val: ValueRef) -> Bool;
 
     // Operations on metadata
+    pub fn LLVMMDStringInContext(C: ContextRef, Str: *const c_char, SLen: c_uint) -> ValueRef;
     pub fn LLVMMDNodeInContext(C: ContextRef, Vals: *const ValueRef, Count: c_uint) -> ValueRef;
+    pub fn LLVMAddNamedMetadataOperand(M: ModuleRef, Name: *const c_char, Val: ValueRef);
 
     // Operations on scalar constants
     pub fn LLVMConstInt(IntTy: TypeRef, N: c_ulonglong, SignExtend: Bool) -> ValueRef;
@@ -1332,6 +1334,8 @@ extern "C" {
 
     pub fn LLVMRustAddModuleFlag(M: ModuleRef, name: *const c_char, value: u32);
 
+    pub fn LLVMRustMetadataAsValue(C: ContextRef, MD: MetadataRef) -> ValueRef;
+
     pub fn LLVMRustDIBuilderCreate(M: ModuleRef) -> DIBuilderRef;
 
     pub fn LLVMRustDIBuilderDispose(Builder: DIBuilderRef);
diff --git a/src/librustc_metadata/creader.rs b/src/librustc_metadata/creader.rs
index c1b57a543a4..27c2d22168c 100644
--- a/src/librustc_metadata/creader.rs
+++ b/src/librustc_metadata/creader.rs
@@ -902,6 +902,24 @@ impl<'a> CrateLoader<'a> {
         }
     }
 
+    fn inject_profiler_runtime(&mut self) {
+        if self.sess.opts.debugging_opts.profile {
+            info!("loading profiler");
+
+            let symbol = Symbol::intern("profiler_builtins");
+            let dep_kind = DepKind::Implicit;
+            let (_, data) =
+                self.resolve_crate(&None, symbol, symbol, None, DUMMY_SP,
+                                   PathKind::Crate, dep_kind);
+
+            // Sanity check the loaded crate to ensure it is indeed a profiler runtime
+            if !data.is_profiler_runtime(&self.sess.dep_graph) {
+                self.sess.err(&format!("the crate `profiler_builtins` is not \
+                                        a profiler runtime"));
+            }
+        }
+    }
+
     fn inject_allocator_crate(&mut self) {
         // Make sure that we actually need an allocator, if none of our
         // dependencies need one then we definitely don't!
@@ -1104,6 +1122,7 @@ impl<'a> middle::cstore::CrateLoader for CrateLoader<'a> {
         // inject the sanitizer runtime before the allocator runtime because all
         // sanitizers force the use of the `alloc_system` allocator
         self.inject_sanitizer_runtime();
+        self.inject_profiler_runtime();
         self.inject_allocator_crate();
         self.inject_panic_runtime(krate);
 
diff --git a/src/librustc_metadata/cstore.rs b/src/librustc_metadata/cstore.rs
index e572be9ffe7..24647f26cf6 100644
--- a/src/librustc_metadata/cstore.rs
+++ b/src/librustc_metadata/cstore.rs
@@ -305,6 +305,11 @@ impl CrateMetadata {
         attr::contains_name(&attrs, "sanitizer_runtime")
     }
 
+    pub fn is_profiler_runtime(&self, dep_graph: &DepGraph) -> bool {
+        let attrs = self.get_item_attrs(CRATE_DEF_INDEX, dep_graph);
+        attr::contains_name(&attrs, "profiler_runtime")
+    }
+
     pub fn is_no_builtins(&self, dep_graph: &DepGraph) -> bool {
         let attrs = self.get_item_attrs(CRATE_DEF_INDEX, dep_graph);
         attr::contains_name(&attrs, "no_builtins")
diff --git a/src/librustc_metadata/cstore_impl.rs b/src/librustc_metadata/cstore_impl.rs
index 4cbcfe15c0c..34bd309a09c 100644
--- a/src/librustc_metadata/cstore_impl.rs
+++ b/src/librustc_metadata/cstore_impl.rs
@@ -245,6 +245,10 @@ impl CrateStore for cstore::CStore {
         self.get_crate_data(cnum).is_sanitizer_runtime(&self.dep_graph)
     }
 
+    fn is_profiler_runtime(&self, cnum: CrateNum) -> bool {
+        self.get_crate_data(cnum).is_profiler_runtime(&self.dep_graph)
+    }
+
     fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy {
         self.get_crate_data(cnum).panic_strategy(&self.dep_graph)
     }
diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs
index e4939db5759..087be9a9a19 100644
--- a/src/librustc_trans/back/link.rs
+++ b/src/librustc_trans/back/link.rs
@@ -1101,6 +1101,9 @@ fn add_upstream_rust_crates(cmd: &mut Linker,
         // symbols from the dylib.
         let src = sess.cstore.used_crate_source(cnum);
         match data[cnum.as_usize() - 1] {
+            _ if sess.cstore.is_profiler_runtime(cnum) => {
+                add_static_crate(cmd, sess, tmpdir, crate_type, cnum);
+            }
             _ if sess.cstore.is_sanitizer_runtime(cnum) => {
                 link_sanitizer_runtime(cmd, sess, tmpdir, cnum);
             }
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index 6ed0cb0092f..4871d638d12 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -701,6 +701,10 @@ pub fn run_passes(sess: &Session,
         }
     }
 
+    if sess.opts.debugging_opts.profile {
+        modules_config.passes.push("insert-gcov-profiling".to_owned())
+    }
+
     modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize));
     modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize));
 
diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs
index 88c30cd8665..2685c22d60d 100644
--- a/src/librustc_trans/base.rs
+++ b/src/librustc_trans/base.rs
@@ -43,7 +43,7 @@ use rustc::dep_graph::AssertDepGraphSafe;
 use rustc::middle::cstore::LinkMeta;
 use rustc::hir::map as hir_map;
 use rustc::util::common::time;
-use rustc::session::config::{self, NoDebugInfo};
+use rustc::session::config::{self, NoDebugInfo, OutputFilenames};
 use rustc::session::Session;
 use rustc_incremental::IncrementalHashesMap;
 use abi;
@@ -1049,7 +1049,8 @@ pub fn find_exported_symbols(tcx: TyCtxt, reachable: &NodeSet) -> NodeSet {
 
 pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
                              analysis: ty::CrateAnalysis,
-                             incremental_hashes_map: &IncrementalHashesMap)
+                             incremental_hashes_map: &IncrementalHashesMap,
+                             output_filenames: &OutputFilenames)
                              -> CrateTranslation {
     // Be careful with this krate: obviously it gives access to the
     // entire contents of the krate. So if you push any subtasks of
@@ -1066,7 +1067,8 @@ pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
 
     let shared_ccx = SharedCrateContext::new(tcx,
                                              exported_symbols,
-                                             check_overflow);
+                                             check_overflow,
+                                             output_filenames);
     // Translate the metadata.
     let (metadata_llcx, metadata_llmod, metadata) =
         time(tcx.sess.time_passes(), "write metadata", || {
diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs
index 6266452419e..208e994653b 100644
--- a/src/librustc_trans/context.rs
+++ b/src/librustc_trans/context.rs
@@ -23,7 +23,7 @@ use monomorphize::Instance;
 use partitioning::CodegenUnit;
 use type_::Type;
 use rustc_data_structures::base_n;
-use rustc::session::config::{self, NoDebugInfo};
+use rustc::session::config::{self, NoDebugInfo, OutputFilenames};
 use rustc::session::Session;
 use rustc::ty::subst::Substs;
 use rustc::ty::{self, Ty, TyCtxt};
@@ -81,6 +81,8 @@ pub struct SharedCrateContext<'a, 'tcx: 'a> {
     check_overflow: bool,
 
     use_dll_storage_attrs: bool,
+
+    output_filenames: &'a OutputFilenames,
 }
 
 /// The local portion of a `CrateContext`.  There is one `LocalCrateContext`
@@ -264,7 +266,8 @@ pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (Cont
 impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
     pub fn new(tcx: TyCtxt<'b, 'tcx, 'tcx>,
                exported_symbols: NodeSet,
-               check_overflow: bool)
+               check_overflow: bool,
+               output_filenames: &'b OutputFilenames)
                -> SharedCrateContext<'b, 'tcx> {
         // An interesting part of Windows which MSVC forces our hand on (and
         // apparently MinGW didn't) is the usage of `dllimport` and `dllexport`
@@ -316,6 +319,7 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
             tcx: tcx,
             check_overflow: check_overflow,
             use_dll_storage_attrs: use_dll_storage_attrs,
+            output_filenames: output_filenames,
         }
     }
 
@@ -350,6 +354,10 @@ impl<'b, 'tcx> SharedCrateContext<'b, 'tcx> {
     pub fn use_dll_storage_attrs(&self) -> bool {
         self.use_dll_storage_attrs
     }
+
+    pub fn output_filenames(&self) -> &OutputFilenames {
+        self.output_filenames
+    }
 }
 
 impl<'a, 'tcx> LocalCrateContext<'a, 'tcx> {
diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs
index fea24e6da87..95ceec610ea 100644
--- a/src/librustc_trans/debuginfo/metadata.rs
+++ b/src/librustc_trans/debuginfo/metadata.rs
@@ -38,10 +38,12 @@ use rustc::ty::{self, AdtKind, Ty};
 use rustc::ty::layout::{self, LayoutTyper};
 use rustc::session::{Session, config};
 use rustc::util::nodemap::FxHashMap;
+use rustc::util::common::path2cstr;
 
 use libc::{c_uint, c_longlong};
 use std::ffi::CString;
 use std::ptr;
+use std::path::Path;
 use syntax::ast;
 use syntax::symbol::{Interner, InternedString, Symbol};
 use syntax_pos::{self, Span};
@@ -794,7 +796,7 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext,
         let file_metadata = llvm::LLVMRustDIBuilderCreateFile(
             debug_context.builder, name_in_debuginfo.as_ptr(), work_dir.as_ptr());
 
-        return llvm::LLVMRustDIBuilderCreateCompileUnit(
+        let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit(
             debug_context.builder,
             DW_LANG_RUST,
             file_metadata,
@@ -802,8 +804,40 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext,
             sess.opts.optimize != config::OptLevel::No,
             flags.as_ptr() as *const _,
             0,
-            split_name.as_ptr() as *const _)
+            split_name.as_ptr() as *const _);
+
+        if sess.opts.debugging_opts.profile {
+            let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext,
+                                                                 unit_metadata);
+
+            let gcov_cu_info = [
+                path_to_mdstring(debug_context.llcontext,
+                                 &scc.output_filenames().with_extension("gcno")),
+                path_to_mdstring(debug_context.llcontext,
+                                 &scc.output_filenames().with_extension("gcda")),
+                cu_desc_metadata,
+            ];
+            let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext,
+                                                          gcov_cu_info.as_ptr(),
+                                                          gcov_cu_info.len() as c_uint);
+
+            let llvm_gcov_ident = CString::new("llvm.gcov").unwrap();
+            llvm::LLVMAddNamedMetadataOperand(debug_context.llmod,
+                                              llvm_gcov_ident.as_ptr(),
+                                              gcov_metadata);
+        }
+
+        return unit_metadata;
     };
+
+    fn path_to_mdstring(llcx: llvm::ContextRef, path: &Path) -> llvm::ValueRef {
+        let path_str = path2cstr(path);
+        unsafe {
+            llvm::LLVMMDStringInContext(llcx,
+                                        path_str.as_ptr(),
+                                        path_str.as_bytes().len() as c_uint)
+        }
+    }
 }
 
 struct MetadataCreationResult {
diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs
index 4d781d6f77d..d4d098ee5e7 100644
--- a/src/librustc_trans/debuginfo/mod.rs
+++ b/src/librustc_trans/debuginfo/mod.rs
@@ -67,6 +67,7 @@ const DW_TAG_arg_variable: c_uint = 0x101;
 /// A context object for maintaining all state needed by the debuginfo module.
 pub struct CrateDebugContext<'tcx> {
     llcontext: ContextRef,
+    llmod: ModuleRef,
     builder: DIBuilderRef,
     created_files: RefCell<FxHashMap<(Symbol, Symbol), DIFile>>,
     created_enum_disr_types: RefCell<FxHashMap<(DefId, layout::Integer), DIType>>,
@@ -87,6 +88,7 @@ impl<'tcx> CrateDebugContext<'tcx> {
         let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) };
         CrateDebugContext {
             llcontext: llcontext,
+            llmod: llmod,
             builder: builder,
             created_files: RefCell::new(FxHashMap()),
             created_enum_disr_types: RefCell::new(FxHashMap()),
diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml
index e17918506fe..b516cbd08ca 100644
--- a/src/libstd/Cargo.toml
+++ b/src/libstd/Cargo.toml
@@ -20,6 +20,7 @@ core = { path = "../libcore" }
 libc = { path = "../rustc/libc_shim" }
 rand = { path = "../librand" }
 compiler_builtins = { path = "../libcompiler_builtins" }
+profiler_builtins = { path = "../libprofiler_builtins", optional = true }
 std_unicode = { path = "../libstd_unicode" }
 unwind = { path = "../libunwind" }
 
@@ -43,3 +44,4 @@ debug-jemalloc = ["alloc_jemalloc/debug"]
 jemalloc = ["alloc_jemalloc"]
 force_alloc_system = []
 panic-unwind = ["panic_unwind"]
+profiler = ["profiler_builtins"]
diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs
index b2f52d11db2..7ab0529f518 100644
--- a/src/libsyntax/feature_gate.rs
+++ b/src/libsyntax/feature_gate.rs
@@ -325,6 +325,10 @@ declare_features! (
     // rustc internal
     (active, sanitizer_runtime, "1.17.0", None),
 
+    // Used to identify crates that contain the profiler runtime
+    // rustc internal
+    (active, profiler_runtime, "1.18.0", None),
+
     // `extern "x86-interrupt" fn()`
     (active, abi_x86_interrupt, "1.17.0", Some(40180)),
 
@@ -691,6 +695,13 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG
                                               identify crates that contain the runtime of a \
                                               sanitizer and will never be stable",
                                              cfg_fn!(sanitizer_runtime))),
+    ("profiler_runtime", Whitelisted, Gated(Stability::Unstable,
+                                             "profiler_runtime",
+                                             "the `#[profiler_runtime]` attribute is used to \
+                                              identify the `profiler_builtins` crate which \
+                                              contains the profiler runtime and will never be \
+                                              stable",
+                                             cfg_fn!(profiler_runtime))),
 
     ("allow_internal_unstable", Normal, Gated(Stability::Unstable,
                                               "allow_internal_unstable",
diff --git a/src/rustllvm/RustWrapper.cpp b/src/rustllvm/RustWrapper.cpp
index 838c180c70b..c11c5f4186e 100644
--- a/src/rustllvm/RustWrapper.cpp
+++ b/src/rustllvm/RustWrapper.cpp
@@ -466,6 +466,10 @@ extern "C" void LLVMRustAddModuleFlag(LLVMModuleRef M, const char *Name,
   unwrap(M)->addModuleFlag(Module::Warning, Name, Value);
 }
 
+extern "C" void LLVMRustMetadataAsValue(LLVMContextRef C, LLVMRustMetadataRef MD) {
+  wrap(MetadataAsValue::get(*unwrap(C), unwrap(MD)));
+}
+
 extern "C" LLVMRustDIBuilderRef LLVMRustDIBuilderCreate(LLVMModuleRef M) {
   return new DIBuilder(*unwrap(M));
 }
diff --git a/src/test/compile-fail/feature-gate-profiler-runtime.rs b/src/test/compile-fail/feature-gate-profiler-runtime.rs
new file mode 100644
index 00000000000..c6b2cb2eb07
--- /dev/null
+++ b/src/test/compile-fail/feature-gate-profiler-runtime.rs
@@ -0,0 +1,13 @@
+// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![profiler_runtime] //~ ERROR the `#[profiler_runtime]` attribute is
+
+fn main() {}
diff --git a/src/test/run-make/profile/Makefile b/src/test/run-make/profile/Makefile
new file mode 100644
index 00000000000..7300bfc9553
--- /dev/null
+++ b/src/test/run-make/profile/Makefile
@@ -0,0 +1,9 @@
+-include ../tools.mk
+
+all:
+ifeq ($(PROFILER_SUPPORT),1)
+	$(RUSTC) -g -Z profile test.rs
+	$(call RUN,test) || exit 1
+	[ -e "$(TMPDIR)/test.gcno" ] || (echo "No .gcno file"; exit 1)
+	[ -e "$(TMPDIR)/test.gcda" ] || (echo "No .gcda file"; exit 1)
+endif
diff --git a/src/test/run-make/profile/test.rs b/src/test/run-make/profile/test.rs
new file mode 100644
index 00000000000..046d27a9f0f
--- /dev/null
+++ b/src/test/run-make/profile/test.rs
@@ -0,0 +1,11 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {}