1
Fork 0

auto merge of #16322 : michaelwoerister/rust/gdb-pretty, r=alexcrichton

Also extends the autotest framework to let a test case choose if pretty printing should be enabled.
This commit is contained in:
bors 2014-08-30 04:01:24 +00:00
commit c8e86e977f
11 changed files with 697 additions and 30 deletions

7
configure vendored
View file

@ -515,6 +515,13 @@ probe CFG_LUALATEX lualatex
probe CFG_GDB gdb probe CFG_GDB gdb
probe CFG_LLDB lldb probe CFG_LLDB lldb
if [ ! -z "$CFG_GDB" ]
then
# Extract the version
CFG_GDB_VERSION=$($CFG_GDB --version 2>/dev/null | head -1)
putvar CFG_GDB_VERSION
fi
if [ ! -z "$CFG_LLDB" ] if [ ! -z "$CFG_LLDB" ]
then then
# If CFG_LLDB_PYTHON_DIR is not already set from the outside and valid, try to read it from # If CFG_LLDB_PYTHON_DIR is not already set from the outside and valid, try to read it from

View file

@ -623,6 +623,7 @@ CTEST_COMMON_ARGS$(1)-T-$(2)-H-$(3) := \
--stage-id stage$(1)-$(2) \ --stage-id stage$(1)-$(2) \
--target $(2) \ --target $(2) \
--host $(3) \ --host $(3) \
--gdb-version="$(CFG_GDB_VERSION)" \
--android-cross-path=$(CFG_ANDROID_CROSS_PATH) \ --android-cross-path=$(CFG_ANDROID_CROSS_PATH) \
--adb-path=$(CFG_ADB) \ --adb-path=$(CFG_ADB) \
--adb-test-dir=$(CFG_ADB_TEST_DIR) \ --adb-test-dir=$(CFG_ADB_TEST_DIR) \

View file

@ -130,6 +130,9 @@ pub struct Config {
// Host triple for the compiler being invoked // Host triple for the compiler being invoked
pub host: String, pub host: String,
// Version of GDB
pub gdb_version: Option<String>,
// Path to the android tools // Path to the android tools
pub android_cross_path: Path, pub android_cross_path: Path,

View file

@ -81,6 +81,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
optflag("", "jit", "run tests under the JIT"), optflag("", "jit", "run tests under the JIT"),
optopt("", "target", "the target to build for", "TARGET"), optopt("", "target", "the target to build for", "TARGET"),
optopt("", "host", "the host to build for", "HOST"), optopt("", "host", "the host to build for", "HOST"),
optopt("", "gdb-version", "the version of GDB used", "MAJOR.MINOR"),
optopt("", "android-cross-path", "Android NDK standalone path", "PATH"), optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
optopt("", "adb-path", "path to the android debugger", "PATH"), optopt("", "adb-path", "path to the android debugger", "PATH"),
optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"), optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
@ -157,6 +158,7 @@ pub fn parse_config(args: Vec<String> ) -> Config {
jit: matches.opt_present("jit"), jit: matches.opt_present("jit"),
target: opt_str2(matches.opt_str("target")), target: opt_str2(matches.opt_str("target")),
host: opt_str2(matches.opt_str("host")), host: opt_str2(matches.opt_str("host")),
gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
android_cross_path: opt_path(matches, "android-cross-path"), android_cross_path: opt_path(matches, "android-cross-path"),
adb_path: opt_str2(matches.opt_str("adb-path")), adb_path: opt_str2(matches.opt_str("adb-path")),
adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")), adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
@ -376,3 +378,26 @@ pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::Test
runtest::run_metrics(config, testfile, mm) runtest::run_metrics(config, testfile, mm)
}) })
} }
fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
match full_version_line {
Some(ref full_version_line)
if full_version_line.as_slice().trim().len() > 0 => {
let full_version_line = full_version_line.as_slice().trim();
let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
match re.captures(full_version_line) {
Some(captures) => {
Some(captures.at(2).to_string())
}
None => {
println!("Could not extract GDB version from line '{}'",
full_version_line);
None
}
}
},
_ => None
}
}

View file

@ -12,6 +12,8 @@ use common::Config;
use common; use common;
use util; use util;
use std::from_str::FromStr;
pub struct TestProps { pub struct TestProps {
// Lines that should be expected, in order, on standard out // Lines that should be expected, in order, on standard out
pub error_patterns: Vec<String> , pub error_patterns: Vec<String> ,
@ -142,23 +144,42 @@ pub fn is_test_ignored(config: &Config, testfile: &Path) -> bool {
format!("ignore-{}", format!("ignore-{}",
config.stage_id.as_slice().split('-').next().unwrap()) config.stage_id.as_slice().split('-').next().unwrap())
} }
fn ignore_gdb(config: &Config, line: &str) -> bool {
if config.mode != common::DebugInfoGdb {
return false;
}
if parse_name_directive(line, "ignore-gdb") {
return true;
}
match config.gdb_version {
Some(ref actual_version) => {
if line.contains("min-gdb-version") {
let min_version = line.trim()
.split(' ')
.last()
.expect("Malformed GDB version directive");
// Ignore if actual version is smaller the minimum required
// version
gdb_version_to_int(actual_version.as_slice()) <
gdb_version_to_int(min_version.as_slice())
} else {
false
}
}
None => false
}
}
let val = iter_header(testfile, |ln| { let val = iter_header(testfile, |ln| {
if parse_name_directive(ln, "ignore-test") { !parse_name_directive(ln, "ignore-test") &&
false !parse_name_directive(ln, ignore_target(config).as_slice()) &&
} else if parse_name_directive(ln, ignore_target(config).as_slice()) { !parse_name_directive(ln, ignore_stage(config).as_slice()) &&
false !(config.mode == common::Pretty && parse_name_directive(ln, "ignore-pretty")) &&
} else if parse_name_directive(ln, ignore_stage(config).as_slice()) { !(config.target != config.host && parse_name_directive(ln, "ignore-cross-compile")) &&
false !ignore_gdb(config, ln) &&
} else if config.mode == common::Pretty && !(config.mode == common::DebugInfoLldb && parse_name_directive(ln, "ignore-lldb"))
parse_name_directive(ln, "ignore-pretty") {
false
} else if config.target != config.host &&
parse_name_directive(ln, "ignore-cross-compile") {
false
} else {
true
}
}); });
!val !val
@ -278,3 +299,21 @@ pub fn parse_name_value_directive(line: &str, directive: &str)
None => None None => None
} }
} }
pub fn gdb_version_to_int(version_string: &str) -> int {
let error_string = format!(
"Encountered GDB version string with unexpected format: {}",
version_string);
let error_string = error_string.as_slice();
let components: Vec<&str> = version_string.trim().split('.').collect();
if components.len() != 2 {
fail!("{}", error_string);
}
let major: int = FromStr::from_str(components[0]).expect(error_string);
let minor: int = FromStr::from_str(components[1]).expect(error_string);
return major * 1000 + minor;
}

View file

@ -323,7 +323,12 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
}; };
let config = &mut config; let config = &mut config;
let DebuggerCommands { commands, check_lines, .. } = parse_debugger_commands(testfile, "gdb"); let DebuggerCommands {
commands,
check_lines,
use_gdb_pretty_printer,
..
} = parse_debugger_commands(testfile, "gdb");
let mut cmds = commands.connect("\n"); let mut cmds = commands.connect("\n");
// compile test file (it should have 'compile-flags:-g' in the header) // compile test file (it should have 'compile-flags:-g' in the header)
@ -334,7 +339,6 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
let exe_file = make_exe_name(config, testfile); let exe_file = make_exe_name(config, testfile);
let mut proc_args;
let debugger_run_result; let debugger_run_result;
match config.target.as_slice() { match config.target.as_slice() {
"arm-linux-androideabi" => { "arm-linux-androideabi" => {
@ -454,18 +458,65 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
} }
_=> { _=> {
let rust_src_root = find_rust_src_root(config)
.expect("Could not find Rust source root");
let rust_pp_module_rel_path = Path::new("./src/etc");
let rust_pp_module_abs_path = rust_src_root.join(rust_pp_module_rel_path)
.as_str()
.unwrap()
.to_string();
// write debugger script // write debugger script
let script_str = [ let mut script_str = String::with_capacity(2048);
"set charset UTF-8".to_string(),
cmds, script_str.push_str("set charset UTF-8\n");
"quit\n".to_string() script_str.push_str("show version\n");
].connect("\n");
match config.gdb_version {
Some(ref version) => {
println!("NOTE: compiletest thinks it is using GDB version {}",
version.as_slice());
if header::gdb_version_to_int(version.as_slice()) >
header::gdb_version_to_int("7.4") {
// Add the directory containing the pretty printers to
// GDB's script auto loading safe path ...
script_str.push_str(
format!("add-auto-load-safe-path {}\n",
rust_pp_module_abs_path.as_slice())
.as_slice());
// ... and also the test directory
script_str.push_str(
format!("add-auto-load-safe-path {}\n",
config.build_base.as_str().unwrap())
.as_slice());
}
}
_ => {
println!("NOTE: compiletest does not know which version of \
GDB it is using");
}
}
// Load the target executable
script_str.push_str(format!("file {}\n",
exe_file.as_str().unwrap())
.as_slice());
script_str.push_str(cmds.as_slice());
script_str.push_str("quit\n");
debug!("script_str = {}", script_str); debug!("script_str = {}", script_str);
dump_output_file(config, dump_output_file(config,
testfile, testfile,
script_str.as_slice(), script_str.as_slice(),
"debugger.script"); "debugger.script");
if use_gdb_pretty_printer {
// Only emit the gdb auto-loading script if pretty printers
// should actually be loaded
dump_gdb_autoload_script(config, testfile);
}
// run debugger script with gdb // run debugger script with gdb
#[cfg(windows)] #[cfg(windows)]
fn debugger() -> String { fn debugger() -> String {
@ -483,16 +534,19 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
vec!("-quiet".to_string(), vec!("-quiet".to_string(),
"-batch".to_string(), "-batch".to_string(),
"-nx".to_string(), "-nx".to_string(),
format!("-command={}", debugger_script.as_str().unwrap()), format!("-command={}", debugger_script.as_str().unwrap()));
exe_file.as_str().unwrap().to_string());
proc_args = ProcArgs { let proc_args = ProcArgs {
prog: debugger(), prog: debugger(),
args: debugger_opts, args: debugger_opts,
}; };
let environment = vec![("PYTHONPATH".to_string(), rust_pp_module_abs_path)];
debugger_run_result = compose_and_run(config, debugger_run_result = compose_and_run(config,
testfile, testfile,
proc_args, proc_args,
Vec::new(), environment,
config.run_lib_path.as_slice(), config.run_lib_path.as_slice(),
None, None,
None); None);
@ -504,6 +558,32 @@ fn run_debuginfo_gdb_test(config: &Config, props: &TestProps, testfile: &Path) {
} }
check_debugger_output(&debugger_run_result, check_lines.as_slice()); check_debugger_output(&debugger_run_result, check_lines.as_slice());
fn dump_gdb_autoload_script(config: &Config, testfile: &Path) {
let mut script_path = output_base_name(config, testfile);
let mut script_file_name = script_path.filename().unwrap().to_vec();
script_file_name.push_all("-gdb.py".as_bytes());
script_path.set_filename(script_file_name.as_slice());
let script_content = "import gdb_rust_pretty_printing\n\
gdb_rust_pretty_printing.register_printers(gdb.current_objfile())\n"
.as_bytes();
File::create(&script_path).write(script_content).unwrap();
}
}
fn find_rust_src_root(config: &Config) -> Option<Path> {
let mut path = config.src_base.clone();
let path_postfix = Path::new("src/etc/lldb_batchmode.py");
while path.pop() {
if path.join(path_postfix.clone()).is_file() {
return Some(path);
}
}
return None;
} }
fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) { fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path) {
@ -533,7 +613,8 @@ fn run_debuginfo_lldb_test(config: &Config, props: &TestProps, testfile: &Path)
let DebuggerCommands { let DebuggerCommands {
commands, commands,
check_lines, check_lines,
breakpoint_lines breakpoint_lines,
..
} = parse_debugger_commands(testfile, "lldb"); } = parse_debugger_commands(testfile, "lldb");
// Write debugger script: // Write debugger script:
@ -619,6 +700,7 @@ struct DebuggerCommands {
commands: Vec<String>, commands: Vec<String>,
check_lines: Vec<String>, check_lines: Vec<String>,
breakpoint_lines: Vec<uint>, breakpoint_lines: Vec<uint>,
use_gdb_pretty_printer: bool
} }
fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str) fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
@ -631,6 +713,7 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
let mut breakpoint_lines = vec!(); let mut breakpoint_lines = vec!();
let mut commands = vec!(); let mut commands = vec!();
let mut check_lines = vec!(); let mut check_lines = vec!();
let mut use_gdb_pretty_printer = false;
let mut counter = 1; let mut counter = 1;
let mut reader = BufferedReader::new(File::open(file_path).unwrap()); let mut reader = BufferedReader::new(File::open(file_path).unwrap());
for line in reader.lines() { for line in reader.lines() {
@ -640,6 +723,10 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
breakpoint_lines.push(counter); breakpoint_lines.push(counter);
} }
if line.as_slice().contains("gdb-use-pretty-printer") {
use_gdb_pretty_printer = true;
}
header::parse_name_value_directive( header::parse_name_value_directive(
line.as_slice(), line.as_slice(),
command_directive.as_slice()).map(|cmd| { command_directive.as_slice()).map(|cmd| {
@ -663,7 +750,8 @@ fn parse_debugger_commands(file_path: &Path, debugger_prefix: &str)
DebuggerCommands { DebuggerCommands {
commands: commands, commands: commands,
check_lines: check_lines, check_lines: check_lines,
breakpoint_lines: breakpoint_lines breakpoint_lines: breakpoint_lines,
use_gdb_pretty_printer: use_gdb_pretty_printer,
} }
} }

View file

@ -0,0 +1,231 @@
# Copyright 2013-2014 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.
import gdb
#===============================================================================
# GDB Pretty Printing Module for Rust
#===============================================================================
def register_printers(objfile):
"Registers Rust pretty printers for the given objfile"
objfile.pretty_printers.append(rust_pretty_printer_lookup_function)
def rust_pretty_printer_lookup_function(val):
"Returns the correct Rust pretty printer for the given value if there is one"
type_code = val.type.code
if type_code == gdb.TYPE_CODE_STRUCT:
struct_kind = classify_struct(val.type)
if struct_kind == STRUCT_KIND_STR_SLICE:
return RustStringSlicePrinter(val)
if struct_kind == STRUCT_KIND_TUPLE:
return RustTuplePrinter(val)
if struct_kind == STRUCT_KIND_TUPLE_STRUCT:
return RustTupleStructPrinter(val, False)
if struct_kind == STRUCT_KIND_CSTYLE_VARIANT:
return RustCStyleEnumPrinter(val[get_field_at_index(val, 0)])
if struct_kind == STRUCT_KIND_TUPLE_VARIANT:
return RustTupleStructPrinter(val, True)
if struct_kind == STRUCT_KIND_STRUCT_VARIANT:
return RustStructPrinter(val, True)
return RustStructPrinter(val, False)
# Enum handling
if type_code == gdb.TYPE_CODE_UNION:
enum_members = list(val.type.fields())
enum_member_count = len(enum_members)
if enum_member_count == 0:
return RustStructPrinter(val, false)
if enum_member_count == 1:
if enum_members[0].name == None:
# This is a singleton enum
return rust_pretty_printer_lookup_function(val[enum_members[0]])
else:
assert enum_members[0].name.startswith("RUST$ENCODED$ENUM$")
# This is a space-optimized enum
last_separator_index = enum_members[0].name.rfind("$")
second_last_separator_index = first_variant_name.rfind("$", 0, last_separator_index)
disr_field_index = first_variant_name[second_last_separator_index + 1 :
last_separator_index]
disr_field_index = int(disr_field_index)
sole_variant_val = val[enum_members[0]]
disr_field = get_field_at_index(sole_variant_val, disr_field_index)
discriminant = int(sole_variant_val[disr_field])
if discriminant == 0:
null_variant_name = first_variant_name[last_separator_index + 1:]
return IdentityPrinter(null_variant_name)
return rust_pretty_printer_lookup_function(sole_variant_val)
# This is a regular enum, extract the discriminant
discriminant_name, discriminant_val = extract_discriminant_value(val)
return rust_pretty_printer_lookup_function(val[enum_members[discriminant_val]])
# No pretty printer has been found
return None
#=------------------------------------------------------------------------------
# Pretty Printer Classes
#=------------------------------------------------------------------------------
class RustStructPrinter:
def __init__(self, val, hide_first_field):
self.val = val
self.hide_first_field = hide_first_field
def to_string(self):
return self.val.type.tag
def children(self):
cs = []
for field in self.val.type.fields():
field_name = field.name
# Normally the field name is used as a key to access the field value,
# because that's also supported in older versions of GDB...
field_key = field_name
if field_name == None:
field_name = ""
# ... but for fields without a name (as in tuples), we have to fall back
# to the newer method of using the field object directly as key. In
# older versions of GDB, this will just fail.
field_key = field
name_value_tuple = ( field_name, self.val[field_key] )
cs.append( name_value_tuple )
if self.hide_first_field:
cs = cs[1:]
return cs
class RustTuplePrinter:
def __init__(self, val):
self.val = val
def to_string(self):
return None
def children(self):
cs = []
for field in self.val.type.fields():
cs.append( ("", self.val[field]) )
return cs
def display_hint(self):
return "array"
class RustTupleStructPrinter:
def __init__(self, val, hide_first_field):
self.val = val
self.hide_first_field = hide_first_field
def to_string(self):
return self.val.type.tag
def children(self):
cs = []
for field in self.val.type.fields():
cs.append( ("", self.val[field]) )
if self.hide_first_field:
cs = cs[1:]
return cs
def display_hint(self):
return "array"
class RustStringSlicePrinter:
def __init__(self, val):
self.val = val
def to_string(self):
slice_byte_len = self.val["length"]
return '"%s"' % self.val["data_ptr"].string(encoding = "utf-8",
length = slice_byte_len)
class RustCStyleEnumPrinter:
def __init__(self, val):
assert val.type.code == gdb.TYPE_CODE_ENUM
self.val = val
def to_string(self):
return str(self.val)
class IdentityPrinter:
def __init__(self, string):
self.string
def to_string(self):
return self.string
STRUCT_KIND_REGULAR_STRUCT = 0
STRUCT_KIND_TUPLE_STRUCT = 1
STRUCT_KIND_TUPLE = 2
STRUCT_KIND_TUPLE_VARIANT = 3
STRUCT_KIND_STRUCT_VARIANT = 4
STRUCT_KIND_CSTYLE_VARIANT = 5
STRUCT_KIND_STR_SLICE = 6
def classify_struct(type):
if type.tag == "&str":
return STRUCT_KIND_STR_SLICE
fields = list(type.fields())
field_count = len(fields)
if field_count == 0:
return STRUCT_KIND_REGULAR_STRUCT
if fields[0].artificial:
if field_count == 1:
return STRUCT_KIND_CSTYLE_VARIANT
elif fields[1].name == None:
return STRUCT_KIND_TUPLE_VARIANT
else:
return STRUCT_KIND_STRUCT_VARIANT
if fields[0].name == None:
if type.tag.startswith("("):
return STRUCT_KIND_TUPLE
else:
return STRUCT_KIND_TUPLE_STRUCT
return STRUCT_KIND_REGULAR_STRUCT
def extract_discriminant_value(enum_val):
assert enum_val.type.code == gdb.TYPE_CODE_UNION
for variant_descriptor in enum_val.type.fields():
variant_val = enum_val[variant_descriptor]
for field in variant_val.type.fields():
return (field.name, int(variant_val[field]))
def first_field(val):
for field in val.type.fields():
return field
def get_field_at_index(val, index):
i = 0
for field in val.type.fields():
if i == index:
return field
return None

View file

@ -235,6 +235,9 @@ static UNKNOWN_COLUMN_NUMBER: c_uint = 0;
static UNKNOWN_FILE_METADATA: DIFile = (0 as DIFile); static UNKNOWN_FILE_METADATA: DIFile = (0 as DIFile);
static UNKNOWN_SCOPE_METADATA: DIScope = (0 as DIScope); static UNKNOWN_SCOPE_METADATA: DIScope = (0 as DIScope);
static FLAGS_NONE: c_uint = 0;
static FLAGS_ARTIFICAL: c_uint = llvm::debuginfo::FlagArtificial as c_uint;
//=----------------------------------------------------------------------------- //=-----------------------------------------------------------------------------
// Public Interface of debuginfo module // Public Interface of debuginfo module
//=----------------------------------------------------------------------------- //=-----------------------------------------------------------------------------
@ -1732,6 +1735,7 @@ struct MemberDescription {
llvm_type: Type, llvm_type: Type,
type_metadata: DIType, type_metadata: DIType,
offset: MemberOffset, offset: MemberOffset,
flags: c_uint
} }
// A factory for MemberDescriptions. It produces a list of member descriptions // A factory for MemberDescriptions. It produces a list of member descriptions
@ -1890,6 +1894,7 @@ impl StructMemberDescriptionFactory {
llvm_type: type_of::type_of(cx, field.mt.ty), llvm_type: type_of::type_of(cx, field.mt.ty),
type_metadata: type_metadata(cx, field.mt.ty, self.span), type_metadata: type_metadata(cx, field.mt.ty, self.span),
offset: offset, offset: offset,
flags: FLAGS_NONE,
} }
}).collect() }).collect()
} }
@ -1950,6 +1955,7 @@ impl TupleMemberDescriptionFactory {
llvm_type: type_of::type_of(cx, component_type), llvm_type: type_of::type_of(cx, component_type),
type_metadata: type_metadata(cx, component_type, self.span), type_metadata: type_metadata(cx, component_type, self.span),
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_NONE,
} }
}).collect() }).collect()
} }
@ -2035,6 +2041,7 @@ impl EnumMemberDescriptionFactory {
llvm_type: variant_llvm_type, llvm_type: variant_llvm_type,
type_metadata: variant_type_metadata, type_metadata: variant_type_metadata,
offset: FixedMemberOffset { bytes: 0 }, offset: FixedMemberOffset { bytes: 0 },
flags: FLAGS_NONE
} }
}).collect() }).collect()
}, },
@ -2068,6 +2075,7 @@ impl EnumMemberDescriptionFactory {
llvm_type: variant_llvm_type, llvm_type: variant_llvm_type,
type_metadata: variant_type_metadata, type_metadata: variant_type_metadata,
offset: FixedMemberOffset { bytes: 0 }, offset: FixedMemberOffset { bytes: 0 },
flags: FLAGS_NONE
} }
] ]
} }
@ -2101,6 +2109,7 @@ impl EnumMemberDescriptionFactory {
llvm_type: non_null_llvm_type, llvm_type: non_null_llvm_type,
type_metadata: non_null_type_metadata, type_metadata: non_null_type_metadata,
offset: FixedMemberOffset { bytes: 0 }, offset: FixedMemberOffset { bytes: 0 },
flags: FLAGS_NONE
}; };
let unique_type_id = debug_context(cx).type_map let unique_type_id = debug_context(cx).type_map
@ -2138,6 +2147,7 @@ impl EnumMemberDescriptionFactory {
llvm_type: artificial_struct_llvm_type, llvm_type: artificial_struct_llvm_type,
type_metadata: artificial_struct_metadata, type_metadata: artificial_struct_metadata,
offset: FixedMemberOffset { bytes: 0 }, offset: FixedMemberOffset { bytes: 0 },
flags: FLAGS_NONE
} }
] ]
}, },
@ -2182,6 +2192,7 @@ impl EnumMemberDescriptionFactory {
llvm_type: variant_llvm_type, llvm_type: variant_llvm_type,
type_metadata: variant_type_metadata, type_metadata: variant_type_metadata,
offset: FixedMemberOffset { bytes: 0 }, offset: FixedMemberOffset { bytes: 0 },
flags: FLAGS_NONE
} }
] ]
}, },
@ -2208,6 +2219,11 @@ impl VariantMemberDescriptionFactory {
_ => type_metadata(cx, ty, self.span) _ => type_metadata(cx, ty, self.span)
}, },
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: if self.discriminant_type_metadata.is_some() && i == 0 {
FLAGS_ARTIFICAL
} else {
FLAGS_NONE
}
} }
}).collect() }).collect()
} }
@ -2523,7 +2539,7 @@ fn set_members_of_composite_type(cx: &CrateContext,
bytes_to_bits(member_size), bytes_to_bits(member_size),
bytes_to_bits(member_align), bytes_to_bits(member_align),
bytes_to_bits(member_offset), bytes_to_bits(member_offset),
0, member_description.flags,
member_description.type_metadata) member_description.type_metadata)
} }
}) })
@ -2610,30 +2626,35 @@ fn at_box_metadata(cx: &CrateContext,
llvm_type: *member_llvm_types.get(0), llvm_type: *member_llvm_types.get(0),
type_metadata: type_metadata(cx, int_type, codemap::DUMMY_SP), type_metadata: type_metadata(cx, int_type, codemap::DUMMY_SP),
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL,
}, },
MemberDescription { MemberDescription {
name: "drop_glue".to_string(), name: "drop_glue".to_string(),
llvm_type: *member_llvm_types.get(1), llvm_type: *member_llvm_types.get(1),
type_metadata: nil_pointer_type_metadata, type_metadata: nil_pointer_type_metadata,
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL,
}, },
MemberDescription { MemberDescription {
name: "prev".to_string(), name: "prev".to_string(),
llvm_type: *member_llvm_types.get(2), llvm_type: *member_llvm_types.get(2),
type_metadata: nil_pointer_type_metadata, type_metadata: nil_pointer_type_metadata,
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL,
}, },
MemberDescription { MemberDescription {
name: "next".to_string(), name: "next".to_string(),
llvm_type: *member_llvm_types.get(3), llvm_type: *member_llvm_types.get(3),
type_metadata: nil_pointer_type_metadata, type_metadata: nil_pointer_type_metadata,
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL,
}, },
MemberDescription { MemberDescription {
name: "val".to_string(), name: "val".to_string(),
llvm_type: *member_llvm_types.get(4), llvm_type: *member_llvm_types.get(4),
type_metadata: content_type_metadata, type_metadata: content_type_metadata,
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL,
} }
]; ];
@ -2734,12 +2755,14 @@ fn vec_slice_metadata(cx: &CrateContext,
llvm_type: *member_llvm_types.get(0), llvm_type: *member_llvm_types.get(0),
type_metadata: element_type_metadata, type_metadata: element_type_metadata,
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL
}, },
MemberDescription { MemberDescription {
name: "length".to_string(), name: "length".to_string(),
llvm_type: *member_llvm_types.get(1), llvm_type: *member_llvm_types.get(1),
type_metadata: type_metadata(cx, ty::mk_uint(), span), type_metadata: type_metadata(cx, ty::mk_uint(), span),
offset: ComputedMemberOffset, offset: ComputedMemberOffset,
flags: FLAGS_ARTIFICAL
}, },
]; ];

View file

@ -428,7 +428,10 @@ pub mod debuginfo {
FlagObjcClassComplete = 1 << 9, FlagObjcClassComplete = 1 << 9,
FlagObjectPointer = 1 << 10, FlagObjectPointer = 1 << 10,
FlagVector = 1 << 11, FlagVector = 1 << 11,
FlagStaticMember = 1 << 12 FlagStaticMember = 1 << 12,
FlagIndirectVariable = 1 << 13,
FlagLValueReference = 1 << 14,
FlagRValueReference = 1 << 15
} }
} }

View file

@ -0,0 +1,75 @@
// Copyright 2013-2014 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.
// This test uses only GDB Python API features which should be available in
// older versions of GDB too. A more extensive test can be found in
// gdb-pretty-struct-and-enums.rs
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g
// gdb-use-pretty-printer
// The following line actually doesn't have to do anything with pretty printing,
// it just tells GDB to print values on one line:
// gdb-command: set print pretty off
// gdb-command: rbreak zzz
// gdb-command: run
// gdb-command: finish
// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false}
// gdb-command: print empty_struct
// gdb-check:$2 = EmptyStruct
// gdb-command: print c_style_enum1
// gdb-check:$3 = CStyleEnumVar1
// gdb-command: print c_style_enum2
// gdb-check:$4 = CStyleEnumVar2
// gdb-command: print c_style_enum3
// gdb-check:$5 = CStyleEnumVar3
struct RegularStruct {
the_first_field: int,
the_second_field: f64,
the_third_field: bool,
}
struct EmptyStruct;
enum CStyleEnum {
CStyleEnumVar1,
CStyleEnumVar2,
CStyleEnumVar3,
}
fn main() {
let regular_struct = RegularStruct {
the_first_field: 101,
the_second_field: 102.5,
the_third_field: false
};
let empty_struct = EmptyStruct;
let c_style_enum1 = CStyleEnumVar1;
let c_style_enum2 = CStyleEnumVar2;
let c_style_enum3 = CStyleEnumVar3;
zzz();
}
fn zzz() { () }

View file

@ -0,0 +1,172 @@
// Copyright 2013-2014 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.
// ignore-tidy-linelength
// ignore-lldb
// ignore-android: FIXME(#10381)
// compile-flags:-g
// gdb-use-pretty-printer
// This test uses some GDB Python API features (e.g. accessing anonymous fields)
// which are only available in newer GDB version. The following directive will
// case the test runner to ignore this test if an older GDB version is used:
// min-gdb-version 7.7
// The following line actually doesn't have to do anything with pretty printing,
// it just tells GDB to print values on one line:
// gdb-command: set print pretty off
// gdb-command: rbreak zzz
// gdb-command: run
// gdb-command: finish
// gdb-command: print regular_struct
// gdb-check:$1 = RegularStruct = {the_first_field = 101, the_second_field = 102.5, the_third_field = false, the_fourth_field = "I'm so pretty, oh so pretty..."}
// gdb-command: print tuple
// gdb-check:$2 = {true, 103, "blub"}
// gdb-command: print tuple_struct
// gdb-check:$3 = TupleStruct = {-104.5, 105}
// gdb-command: print empty_struct
// gdb-check:$4 = EmptyStruct
// gdb-command: print c_style_enum1
// gdb-check:$5 = CStyleEnumVar1
// gdb-command: print c_style_enum2
// gdb-check:$6 = CStyleEnumVar2
// gdb-command: print c_style_enum3
// gdb-check:$7 = CStyleEnumVar3
// gdb-command: print mixed_enum_c_style_var
// gdb-check:$8 = MixedEnumCStyleVar
// gdb-command: print mixed_enum_tuple_var
// gdb-check:$9 = MixedEnumTupleVar = {106, 107, false}
// gdb-command: print mixed_enum_struct_var
// gdb-check:$10 = MixedEnumStructVar = {field1 = 108.5, field2 = 109}
// gdb-command: print some
// gdb-check:$11 = Some = {110}
// gdb-command: print none
// gdb-check:$12 = None
// gdb-command: print nested_variant1
// gdb-check:$13 = NestedVariant1 = {NestedStruct = {regular_struct = RegularStruct = {the_first_field = 111, the_second_field = 112.5, the_third_field = true, the_fourth_field = "NestedStructString1"}, tuple_struct = TupleStruct = {113.5, 114}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar2, mixed_enum = MixedEnumTupleVar = {115, 116, false}}}
// gdb-command: print nested_variant2
// gdb-check:$14 = NestedVariant2 = {abc = NestedStruct = {regular_struct = RegularStruct = {the_first_field = 117, the_second_field = 118.5, the_third_field = false, the_fourth_field = "NestedStructString10"}, tuple_struct = TupleStruct = {119.5, 120}, empty_struct = EmptyStruct, c_style_enum = CStyleEnumVar3, mixed_enum = MixedEnumStructVar = {field1 = 121.5, field2 = -122}}}
#![feature(struct_variant)]
struct RegularStruct {
the_first_field: int,
the_second_field: f64,
the_third_field: bool,
the_fourth_field: &'static str,
}
struct TupleStruct(f64, i16);
struct EmptyStruct;
enum CStyleEnum {
CStyleEnumVar1,
CStyleEnumVar2,
CStyleEnumVar3,
}
enum MixedEnum {
MixedEnumCStyleVar,
MixedEnumTupleVar(u32, u16, bool),
MixedEnumStructVar { field1: f64, field2: i32 }
}
struct NestedStruct {
regular_struct: RegularStruct,
tuple_struct: TupleStruct,
empty_struct: EmptyStruct,
c_style_enum: CStyleEnum,
mixed_enum: MixedEnum,
}
enum NestedEnum {
NestedVariant1(NestedStruct),
NestedVariant2 { abc: NestedStruct }
}
fn main() {
let regular_struct = RegularStruct {
the_first_field: 101,
the_second_field: 102.5,
the_third_field: false,
the_fourth_field: "I'm so pretty, oh so pretty..."
};
let tuple = ( true, 103u32, "blub" );
let tuple_struct = TupleStruct(-104.5, 105);
let empty_struct = EmptyStruct;
let c_style_enum1 = CStyleEnumVar1;
let c_style_enum2 = CStyleEnumVar2;
let c_style_enum3 = CStyleEnumVar3;
let mixed_enum_c_style_var = MixedEnumCStyleVar;
let mixed_enum_tuple_var = MixedEnumTupleVar(106, 107, false);
let mixed_enum_struct_var = MixedEnumStructVar { field1: 108.5, field2: 109 };
let some = Some(110u);
let none: Option<int> = None;
let nested_variant1 = NestedVariant1(
NestedStruct {
regular_struct: RegularStruct {
the_first_field: 111,
the_second_field: 112.5,
the_third_field: true,
the_fourth_field: "NestedStructString1",
},
tuple_struct: TupleStruct(113.5, 114),
empty_struct: EmptyStruct,
c_style_enum: CStyleEnumVar2,
mixed_enum: MixedEnumTupleVar(115, 116, false)
}
);
let nested_variant2 = NestedVariant2 {
abc: NestedStruct {
regular_struct: RegularStruct {
the_first_field: 117,
the_second_field: 118.5,
the_third_field: false,
the_fourth_field: "NestedStructString10",
},
tuple_struct: TupleStruct(119.5, 120),
empty_struct: EmptyStruct,
c_style_enum: CStyleEnumVar3,
mixed_enum: MixedEnumStructVar {
field1: 121.5,
field2: -122
}
}
};
zzz();
}
fn zzz() { () }