Skip to content

Build instruction profiler runtime as part of compiler-rt #38608

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions configure
Original file line number Diff line number Diff line change
@@ -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"
9 changes: 9 additions & 0 deletions src/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/bootstrap/check.rs
Original file line number Diff line number Diff line change
@@ -268,6 +268,10 @@ pub fn compiletest(build: &Build,
cmd.env("SANITIZER_SUPPORT", "1");
}

if build.config.profiler {
cmd.env("PROFILER_SUPPORT", "1");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that this is ever read, is it still needed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's needed for a test. That's yet to be written.

}

cmd.arg("--adb-path").arg("adb");
cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
if target.contains("android") {
4 changes: 4 additions & 0 deletions src/bootstrap/config.rs
Original file line number Diff line number Diff line change
@@ -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,
@@ -158,6 +159,7 @@ struct Build {
extended: Option<bool>,
verbose: Option<usize>,
sanitizers: Option<bool>,
profiler: Option<bool>,
openssl_static: Option<bool>,
}

@@ -311,6 +313,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 {
@@ -462,6 +465,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),
}
3 changes: 3 additions & 0 deletions src/bootstrap/config.toml.example
Original file line number Diff line number Diff line change
@@ -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.
3 changes: 3 additions & 0 deletions src/bootstrap/lib.rs
Original file line number Diff line number Diff line change
@@ -667,6 +667,9 @@ impl Build {
if self.config.backtrace {
features.push_str(" backtrace");
}
if self.config.profiler {
features.push_str(" profiler");
}
return features
}

3 changes: 3 additions & 0 deletions src/doc/unstable-book/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -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)
@@ -71,6 +72,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)
- [relaxed_adts](language-features/relaxed-adts.md)
- [repr_align](language-features/repr-align.md)
@@ -180,6 +182,7 @@
- [print](library-features/print.md)
- [proc_macro_internals](library-features/proc-macro-internals.md)
- [process_try_wait](library-features/process-try-wait.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)
5 changes: 5 additions & 0 deletions src/doc/unstable-book/src/compiler-flags/profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `profile`

The tracking issue for this feature is: None

------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `profiler_runtime`

The tracking issue for this feature is: None.

------------------------
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# `profiler_runtime_lib`

This feature is internal to the Rust compiler and is not intended for general use.

------------------------
18 changes: 18 additions & 0 deletions src/libprofiler_builtins/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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.27"
56 changes: 56 additions & 0 deletions src/libprofiler_builtins/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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();

if target.contains("msvc") {
// Don't pull in extra libraries on MSVC
cfg.flag("/Zl");
} 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);
}

let profile_sources = &["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"];

for src in profile_sources {
cfg.file(Path::new("../compiler-rt/lib/profile").join(src));
}

cfg.compile("libprofiler-rt.a");
}
20 changes: 20 additions & 0 deletions src/libprofiler_builtins/lib.rs
Original file line number Diff line number Diff line change
@@ -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)]
2 changes: 2 additions & 0 deletions src/librustc/middle/cstore.rs
Original file line number Diff line number Diff line change
@@ -220,6 +220,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
@@ -356,6 +357,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")
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Original file line number Diff line number Diff line change
@@ -1025,6 +1025,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"add a source pattern to the file path remapping config"),
remap_path_prefix_to: Vec<String> = (vec![], parse_string_push, [TRACKED],
"add a mapping target to the file path remapping config"),
profile: bool = (false, parse_bool, [TRACKED],
"insert profiling code"),
}

pub fn default_lib_output() -> CrateType {
8 changes: 5 additions & 3 deletions src/librustc_driver/driver.rs
Original file line number Diff line number Diff line change
@@ -203,7 +203,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");
@@ -1029,7 +1030,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();

@@ -1077,7 +1079,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",
4 changes: 4 additions & 0 deletions src/librustc_llvm/ffi.rs
Original file line number Diff line number Diff line change
@@ -587,7 +587,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;
@@ -1328,6 +1330,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);
28 changes: 28 additions & 0 deletions src/librustc_metadata/creader.rs
Original file line number Diff line number Diff line change
@@ -870,6 +870,33 @@ impl<'a> CrateLoader<'a> {
}
}

fn inject_profiler_runtime(&mut self) {
if self.sess.opts.debugging_opts.profile {
let mut uses_std = false;
self.cstore.iter_crate_data(|_, data| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@japaric can you remind me why this is needed?

Naively I'd expect that a crate would require the profiler runtime regardless of whether it links to std or not, but I forget why this clause is above.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I orignially copied that over from compiler_builtins. I think it's useful because a no_std crate may want to supply their own profiler builtins, e.g. to submit the results somewhere in a semihosting implementation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcrichton iirc, because the sanitizers only work with alloc_system (they depend on memcpy and co.), use libc functions like write to print results to the console and pretty much depend on the Linux kernel so you need an OS to use them and that's guaranteed to be there when you link to std.

But since -Z sanitizer is restricted to only work with the x86_64-unknown-linux-gnu target, this check may not be that necessary. Still no_std + x86_64-unknown-linux-gnu will not work unless you link to libc.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah ok. @whitequark perhaps this clause (checking uses_std) be removed for now?

Is it really feasible for someone to write their own profiler builtins?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alexcrichton Sure, I'll remove it.

I imagine someone would replace the libc calls in the stock profiler builtins with something else and then link to that, not write them from scratch. I am thinking of doing this for my embedded system work, incidentally.

if data.name == "std" {
uses_std = true;
}
});

if uses_std {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mentioned above, we can remove this clause, right?

(just execute the code below if -Z profile is provided)

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.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!
@@ -1071,6 +1098,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);

5 changes: 5 additions & 0 deletions src/librustc_metadata/cstore.rs
Original file line number Diff line number Diff line change
@@ -308,6 +308,11 @@ impl CrateMetadata {
attr::contains_name(&attrs, "sanitizer_runtime")
}

pub fn is_profiler_runtime(&self) -> bool {
let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
attr::contains_name(&attrs, "profiler_runtime")
}

pub fn is_no_builtins(&self) -> bool {
let attrs = self.get_item_attrs(CRATE_DEF_INDEX);
attr::contains_name(&attrs, "no_builtins")
4 changes: 4 additions & 0 deletions src/librustc_metadata/cstore_impl.rs
Original file line number Diff line number Diff line change
@@ -275,6 +275,10 @@ impl CrateStore for cstore::CStore {
self.get_crate_data(cnum).is_sanitizer_runtime()
}

fn is_profiler_runtime(&self, cnum: CrateNum) -> bool {
self.get_crate_data(cnum).is_profiler_runtime()
}

fn panic_strategy(&self, cnum: CrateNum) -> PanicStrategy {
self.get_crate_data(cnum).panic_strategy()
}
3 changes: 3 additions & 0 deletions src/librustc_trans/back/link.rs
Original file line number Diff line number Diff line change
@@ -1074,6 +1074,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);
}
4 changes: 4 additions & 0 deletions src/librustc_trans/back/write.rs
Original file line number Diff line number Diff line change
@@ -699,6 +699,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));

8 changes: 5 additions & 3 deletions src/librustc_trans/base.rs
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ use rustc::dep_graph::AssertDepGraphSafe;
use rustc::middle::cstore::LinkMeta;
use rustc::hir::map as hir_map;
use rustc::util::common::time;
use session::config::{self, NoDebugInfo};
use session::config::{self, NoDebugInfo, OutputFilenames};
use rustc_incremental::IncrementalHashesMap;
use session::{self, DataTypeKind, Session};
use abi;
@@ -1053,7 +1053,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
@@ -1070,7 +1071,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", || {
11 changes: 10 additions & 1 deletion src/librustc_trans/context.rs
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ use rustc::ty::subst::Substs;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::layout::{LayoutTyper, TyLayout};
use session::config::NoDebugInfo;
use session::config::OutputFilenames;
use session::Session;
use session::config;
use util::nodemap::{NodeSet, DefIdMap, FxHashMap};
@@ -83,6 +84,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`
@@ -266,7 +269,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`
@@ -319,6 +323,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,
}
}

@@ -353,6 +358,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> {
40 changes: 38 additions & 2 deletions src/librustc_trans/debuginfo/metadata.rs
Original file line number Diff line number Diff line change
@@ -39,10 +39,12 @@ use rustc::ty::{self, AdtKind, Ty};
use rustc::ty::layout::{self, LayoutTyper};
use session::config;
use 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};
@@ -788,20 +790,54 @@ pub fn compile_unit_metadata(scc: &SharedCrateContext,
let file_metadata = llvm::LLVMRustDIBuilderCreateFile(
debug_context.builder, compile_unit_name, work_dir.as_ptr());

return llvm::LLVMRustDIBuilderCreateCompileUnit(
let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit(
debug_context.builder,
DW_LANG_RUST,
file_metadata,
producer.as_ptr(),
sess.opts.optimize != config::OptLevel::No,
flags.as_ptr() as *const _,
0,
split_name.as_ptr() as *const _)
split_name.as_ptr() as *const _);

let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext,
unit_metadata);

let gcov_cu_info = [
// Ideally we would be using the three-element form of !llvm.gcov metadata,
// which allows us to specify gcno/gcda files explicitly, but that's only
// available in LLVM 3.9+; so we rely on LLVM chopping off the extension
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be using 3.9+, right? (and soon 4.0!)

Is this using a different form for compatibility with older llvm version? (if so, I wouldn't worry about that and just use the newer version)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should be using 3.9+, right? (and soon 4.0!)

Is that really the case? The HEAD in the LLVM submodule is pre-3.9 isn't it? (Or at least it did not have the functionality I tried to rely on here.) Also, it is my understanding that the LLVM version check only rejects LLVM <3.7, so someone could still make their own build with that.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes someone can make their own build, but we're not obligated to support literally everything on all LLVM versions, it's fine if profiling just doesn't work on previous LLVM versions (no one will slight us)

// and replacing it with gcno/gcda, instead.
path_to_mdstring(debug_context.llcontext,
&scc.output_filenames().with_extension("gcno")),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this all happen conditionally? Only if -Zprofile is enabled?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The !llvm.gcov named metadata is only ever parsed by the GCOVProfiling pass, so there doesn't seem to be any harm in doing this unconditionally...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm perhaps this could be behind a guard for clarity? I'd be wary of an update to LLVM randomly starting to read this by accident...

// 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 fallback_path(scc: &SharedCrateContext) -> CString {
CString::new(scc.tcx().crate_name(LOCAL_CRATE).to_string()).unwrap()
}

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 {
2 changes: 2 additions & 0 deletions src/librustc_trans/debuginfo/mod.rs
Original file line number Diff line number Diff line change
@@ -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()),
2 changes: 2 additions & 0 deletions src/libstd/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]
11 changes: 11 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
@@ -331,6 +331,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)),

@@ -693,6 +697,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",
4 changes: 4 additions & 0 deletions src/rustllvm/RustWrapper.cpp
Original file line number Diff line number Diff line change
@@ -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));
}
13 changes: 13 additions & 0 deletions src/test/compile-fail/feature-gate-profiler-runtime.rs
Original file line number Diff line number Diff line change
@@ -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() {}
7 changes: 7 additions & 0 deletions src/test/run-make/profile/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
-include ../tools.mk

all:
$(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)
11 changes: 11 additions & 0 deletions src/test/run-make/profile/test.rs
Original file line number Diff line number Diff line change
@@ -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() {}