diff --git a/src/codegen/mod.rs b/src/codegen/mod.rs
index 553dd24f95..f47787afc7 100644
--- a/src/codegen/mod.rs
+++ b/src/codegen/mod.rs
@@ -3479,6 +3479,7 @@ impl CodeGenerator for ObjCInterface {
pub fn codegen(context: &mut BindgenContext) -> Vec
> {
context.gen(|context| {
+ let _t = context.timer("codegen");
let counter = Cell::new(0);
let mut result = CodegenResult::new(&counter);
diff --git a/src/ir/context.rs b/src/ir/context.rs
index 3f503fddb0..788c3f98b4 100644
--- a/src/ir/context.rs
+++ b/src/ir/context.rs
@@ -15,6 +15,7 @@ use super::module::{Module, ModuleKind};
use super::template::{TemplateInstantiation, TemplateParameters};
use super::traversal::{self, Edge, ItemTraversal};
use super::ty::{FloatKind, Type, TypeKind};
+use super::super::time::Timer;
use BindgenOptions;
use callbacks::ParseCallbacks;
use cexpr;
@@ -406,6 +407,13 @@ impl<'ctx> BindgenContext<'ctx> {
me
}
+ /// Creates a timer for the current bindgen phase. If time_phases is `true`,
+ /// the timer will print to stderr when it is dropped, otherwise it will do
+ /// nothing.
+ pub fn timer<'a>(&self, name: &'a str) -> Timer<'a> {
+ Timer::new(name).with_output(self.options.time_phases)
+ }
+
/// Get the stack of partially parsed types that we are in the middle of
/// parsing.
pub fn currently_parsed_types(&self) -> &[PartialType] {
@@ -731,6 +739,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Iterate over all items and replace any item that has been named in a
/// `replaces="SomeType"` annotation with the replacement type.
fn process_replacements(&mut self) {
+ let _t = self.timer("process_replacements");
if self.replacements.is_empty() {
debug!("No replacements to process");
return;
@@ -993,6 +1002,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether the type has vtable.
fn compute_has_vtable(&mut self) {
+ let _t = self.timer("compute_has_vtable");
assert!(self.have_vtable.is_none());
self.have_vtable = Some(analyze::(self));
}
@@ -1011,6 +1021,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether the type has a destructor.
fn compute_has_destructor(&mut self) {
+ let _t = self.timer("compute_has_destructor");
assert!(self.have_destructor.is_none());
self.have_destructor = Some(analyze::(self));
}
@@ -1026,6 +1037,7 @@ impl<'ctx> BindgenContext<'ctx> {
}
fn find_used_template_parameters(&mut self) {
+ let _t = self.timer("find_used_template_parameters");
if self.options.whitelist_recursively {
let used_params = analyze::(self);
self.used_template_parameters = Some(used_params);
@@ -1869,6 +1881,7 @@ impl<'ctx> BindgenContext<'ctx> {
assert!(self.in_codegen_phase());
assert!(self.current_module == self.root_module);
assert!(self.whitelisted.is_none());
+ let _t = self.timer("compute_whitelisted_and_codegen_items");
let roots = {
let mut roots = self.items()
@@ -1991,6 +2004,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether we can derive debug.
fn compute_cannot_derive_debug(&mut self) {
+ let _t = self.timer("compute_cannot_derive_debug");
assert!(self.cannot_derive_debug.is_none());
if self.options.derive_debug {
self.cannot_derive_debug = Some(analyze::(self));
@@ -2012,6 +2026,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether we can derive default.
fn compute_cannot_derive_default(&mut self) {
+ let _t = self.timer("compute_cannot_derive_default");
assert!(self.cannot_derive_default.is_none());
if self.options.derive_default {
self.cannot_derive_default =
@@ -2034,12 +2049,14 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether we can derive copy.
fn compute_cannot_derive_copy(&mut self) {
+ let _t = self.timer("compute_cannot_derive_copy");
assert!(self.cannot_derive_copy.is_none());
self.cannot_derive_copy = Some(analyze::(self));
}
/// Compute whether we can derive hash.
fn compute_cannot_derive_hash(&mut self) {
+ let _t = self.timer("compute_cannot_derive_hash");
assert!(self.cannot_derive_hash.is_none());
if self.options.derive_hash {
self.cannot_derive_hash = Some(analyze::(self));
@@ -2062,6 +2079,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether we can derive PartialEq. This method is also used in calculating
/// whether we can derive Eq
fn compute_cannot_derive_partialeq_or_eq(&mut self) {
+ let _t = self.timer("compute_cannot_derive_partialeq_or_eq");
assert!(self.cannot_derive_partialeq.is_none());
if self.options.derive_partialeq || self.options.derive_eq {
self.cannot_derive_partialeq = Some(analyze::(self));
@@ -2097,6 +2115,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether the type has type parameter in array.
fn compute_has_type_param_in_array(&mut self) {
+ let _t = self.timer("compute_has_type_param_in_array");
assert!(self.has_type_param_in_array.is_none());
self.has_type_param_in_array =
Some(analyze::(self));
@@ -2116,6 +2135,7 @@ impl<'ctx> BindgenContext<'ctx> {
/// Compute whether the type has float.
fn compute_has_float(&mut self) {
+ let _t = self.timer("compute_has_float");
assert!(self.has_float.is_none());
if self.options.derive_eq {
self.has_float = Some(analyze::(self));
diff --git a/src/lib.rs b/src/lib.rs
index d5dbefae60..8ca2280331 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -64,6 +64,7 @@ mod features;
mod ir;
mod parse;
mod regex_set;
+mod time;
pub mod callbacks;
@@ -774,6 +775,13 @@ impl Builder {
self
}
+ /// Set whether or not to time bindgen phases, and print
+ /// information to stderr.
+ pub fn time_phases(mut self, doit: bool) -> Self {
+ self.options.time_phases = doit;
+ self
+ }
+
/// Emit Clang AST.
pub fn emit_clang_ast(mut self) -> Builder {
self.options.emit_ast = true;
@@ -1122,6 +1130,9 @@ pub struct BindgenOptions {
/// An optional prefix for the "raw" types, like `c_int`, `c_void`...
pub ctypes_prefix: Option,
+ /// Whether to time the bindgen phases.
+ pub time_phases: bool,
+
/// True if we should generate constant names that are **directly** under
/// namespaces.
pub namespaced_constants: bool,
@@ -1280,6 +1291,7 @@ impl Default for BindgenOptions {
objc_extern_crate: false,
enable_mangling: true,
prepend_enum_name: true,
+ time_phases: false,
rustfmt_bindings: false,
rustfmt_configuration_file: None,
}
@@ -1403,8 +1415,13 @@ impl<'ctx> Bindings<'ctx> {
options.clang_args.push(f.name.to_str().unwrap().to_owned())
}
+ let time_phases = options.time_phases;
let mut context = BindgenContext::new(options);
- try!(parse(&mut context));
+ {
+ let _t = time::Timer::new("parse")
+ .with_output(time_phases);
+ try!(parse(&mut context));
+ }
let module = ast::Mod {
inner: span,
diff --git a/src/options.rs b/src/options.rs
index 780959e2f4..c6f5c0108c 100644
--- a/src/options.rs
+++ b/src/options.rs
@@ -107,6 +107,9 @@ where
::std::os::raw.")
.value_name("prefix")
.takes_value(true),
+ Arg::with_name("time-phases")
+ .long("time-phases")
+ .help("Time the different bindgen phases and print to stderr"),
// All positional arguments after the end of options marker, `--`
Arg::with_name("clang-args")
.multiple(true),
@@ -340,6 +343,10 @@ where
builder = builder.prepend_enum_name(false);
}
+ if matches.is_present("time-phases") {
+ builder = builder.time_phases(true);
+ }
+
if let Some(prefix) = matches.value_of("ctypes-prefix") {
builder = builder.ctypes_prefix(prefix);
}
diff --git a/src/time.rs b/src/time.rs
new file mode 100644
index 0000000000..ebbac702ec
--- /dev/null
+++ b/src/time.rs
@@ -0,0 +1,57 @@
+use std::io::{self, Write};
+use std::time::{Instant, Duration};
+
+
+/// RAII timer to measure how long phases take.
+#[derive(Debug)]
+pub struct Timer<'a> {
+ output: bool,
+ name: &'a str,
+ start: Instant,
+}
+
+
+impl<'a> Timer<'a> {
+ /// Creates a Timer with the given name, and starts it. By default,
+ /// will print to stderr when it is `drop`'d
+ pub fn new(name: &'a str) -> Self {
+ Timer {
+ output: true,
+ name,
+ start: Instant::now()
+ }
+ }
+
+ /// Sets whether or not the Timer will print a mesage
+ /// when it is dropped.
+ pub fn with_output(mut self, output: bool) -> Self {
+ self.output = output;
+ self
+ }
+
+ /// Returns the time elapsed since the timer's creation
+ pub fn elapsed(&self) -> Duration {
+ Instant::now() - self.start
+ }
+
+ fn print_elapsed(&mut self) {
+ if self.output {
+ let elapsed = self.elapsed();
+ let time = (elapsed.as_secs() as f32)
+ + (elapsed.subsec_nanos() as f32) / 1e9;
+ let stderr = io::stderr();
+ // Arbitrary output format, subject to change.
+ writeln!(stderr.lock(),
+ " time: {:.3} ms.\t{}",
+ time, self.name)
+ .expect("timer write should not fail");
+ }
+ }
+}
+
+
+impl<'a> Drop for Timer<'a> {
+ fn drop(&mut self) {
+ self.print_elapsed();
+ }
+}