Skip to content

Improve debugging experience for enums on windows-msvc #85292

New issue

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

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

Already on GitHub? # to your account

Merged
merged 10 commits into from
Jun 3, 2021
251 changes: 149 additions & 102 deletions compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion compiler/rustc_codegen_llvm/src/llvm/ffi.rs
Original file line number Diff line number Diff line change
@@ -2038,7 +2038,7 @@ extern "C" {

pub fn LLVMRustDIBuilderCreateUnionType(
Builder: &DIBuilder<'a>,
Scope: &'a DIScope,
Scope: Option<&'a DIScope>,
Name: *const c_char,
NameLen: size_t,
File: &'a DIFile,
59 changes: 56 additions & 3 deletions compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs
Original file line number Diff line number Diff line change
@@ -3,7 +3,8 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
use rustc_middle::ty::{self, subst::SubstsRef, Ty, TyCtxt};
use rustc_middle::ty::{self, subst::SubstsRef, AdtDef, Ty, TyCtxt};
use rustc_target::abi::{TagEncoding, Variants};

use std::fmt::Write;

@@ -45,8 +46,12 @@ pub fn push_debuginfo_type_name<'tcx>(
ty::Float(float_ty) => output.push_str(float_ty.name_str()),
ty::Foreign(def_id) => push_item_name(tcx, def_id, qualified, output),
ty::Adt(def, substs) => {
push_item_name(tcx, def.did, qualified, output);
push_type_params(tcx, substs, output, visited);
if def.is_enum() && cpp_like_names {
msvc_enum_fallback(tcx, t, def, substs, output, visited);
} else {
push_item_name(tcx, def.did, qualified, output);
push_type_params(tcx, substs, output, visited);
}
}
ty::Tuple(component_types) => {
if cpp_like_names {
@@ -233,6 +238,54 @@ pub fn push_debuginfo_type_name<'tcx>(
}
}

/// MSVC names enums differently than other platforms so that the debugging visualization
// format (natvis) is able to understand enums and render the active variant correctly in the
// debugger. For more information, look in `src/etc/natvis/intrinsic.natvis` and
// `EnumMemberDescriptionFactor::create_member_descriptions`.
fn msvc_enum_fallback(
tcx: TyCtxt<'tcx>,
ty: Ty<'tcx>,
def: &AdtDef,
substs: SubstsRef<'tcx>,
output: &mut String,
visited: &mut FxHashSet<Ty<'tcx>>,
) {
let layout = tcx.layout_of(tcx.param_env(def.did).and(ty)).expect("layout error");

if let Variants::Multiple {
tag_encoding: TagEncoding::Niche { dataful_variant, .. },
tag,
variants,
..
} = &layout.variants
{
let dataful_variant_layout = &variants[*dataful_variant];

// calculate the range of values for the dataful variant
let dataful_discriminant_range =
&dataful_variant_layout.largest_niche.as_ref().unwrap().scalar.valid_range;

let min = dataful_discriminant_range.start();
let min = tag.value.size(&tcx).truncate(*min);

let max = dataful_discriminant_range.end();
let max = tag.value.size(&tcx).truncate(*max);

output.push_str("enum$<");
push_item_name(tcx, def.did, true, output);
push_type_params(tcx, substs, output, visited);

let dataful_variant_name = def.variants[*dataful_variant].ident.as_str();

output.push_str(&format!(", {}, {}, {}>", min, max, dataful_variant_name));
} else {
output.push_str("enum$<");
push_item_name(tcx, def.did, true, output);
push_type_params(tcx, substs, output, visited);
output.push('>');
}
}

fn push_item_name(tcx: TyCtxt<'tcx>, def_id: DefId, qualified: bool, output: &mut String) {
if qualified {
output.push_str(&tcx.crate_name(def_id.krate).as_str());
53 changes: 53 additions & 0 deletions src/etc/natvis/intrinsic.natvis
Original file line number Diff line number Diff line change
@@ -149,4 +149,57 @@
<Synthetic Name="[...]"><DisplayString>...</DisplayString></Synthetic>
</Expand>
</Type>
<Type Name="enum$&lt;*&gt;">
<Intrinsic Name="tag" Expression="variant0.variant$" />
<DisplayString Condition="tag() == 0">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 1" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 2" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 3" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 4" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 5" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 6" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 7" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 8" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 9" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 10" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 11" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 12" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 13" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 14" Optional="true">{tag(),en}</DisplayString>
<DisplayString Condition="tag() == 15" Optional="true">{tag(),en}</DisplayString>

<Expand>
<ExpandedItem Condition="tag() == 0">variant0</ExpandedItem>
<ExpandedItem Condition="tag() == 1" Optional="true">variant1</ExpandedItem>
<ExpandedItem Condition="tag() == 2" Optional="true">variant2</ExpandedItem>
<ExpandedItem Condition="tag() == 3" Optional="true">variant3</ExpandedItem>
<ExpandedItem Condition="tag() == 4" Optional="true">variant4</ExpandedItem>
<ExpandedItem Condition="tag() == 5" Optional="true">variant5</ExpandedItem>
<ExpandedItem Condition="tag() == 6" Optional="true">variant6</ExpandedItem>
<ExpandedItem Condition="tag() == 7" Optional="true">variant7</ExpandedItem>
<ExpandedItem Condition="tag() == 8" Optional="true">variant8</ExpandedItem>
<ExpandedItem Condition="tag() == 9" Optional="true">variant9</ExpandedItem>
<ExpandedItem Condition="tag() == 10" Optional="true">variant10</ExpandedItem>
<ExpandedItem Condition="tag() == 11" Optional="true">variant11</ExpandedItem>
<ExpandedItem Condition="tag() == 12" Optional="true">variant12</ExpandedItem>
<ExpandedItem Condition="tag() == 13" Optional="true">variant13</ExpandedItem>
<ExpandedItem Condition="tag() == 14" Optional="true">variant14</ExpandedItem>
<ExpandedItem Condition="tag() == 15" Optional="true">variant15</ExpandedItem>
</Expand>
</Type>

<!-- $T1 is the name of the enum, $T2 is the low value of the dataful variant tag,
$T3 is the high value of the dataful variant tag, $T4 is the name of the dataful variant -->
<Type Name="enum$&lt;*, *, *, *&gt;">
<Intrinsic Name="tag" Expression="discriminant" />
<Intrinsic Name="is_dataful" Expression="tag() &gt;= $T2 &amp;&amp; tag() &lt;= $T3" />
<DisplayString Condition="is_dataful()">{"$T4",sb}({dataful_variant})</DisplayString>
<DisplayString Condition="!is_dataful()">{discriminant,en}</DisplayString>
<Expand>
<ExpandedItem Condition="is_dataful()">dataful_variant</ExpandedItem>
<Synthetic Condition="is_dataful()" Name="[variant]">
<DisplayString>{"$T4",sb}</DisplayString>
</Synthetic>
</Expand>
</Type>
</AutoVisualizer>
17 changes: 0 additions & 17 deletions src/etc/natvis/libcore.natvis
Original file line number Diff line number Diff line change
@@ -14,14 +14,6 @@
</Expand>
</Type>

<Type Name="core::option::Option&lt;*&gt;">
<DisplayString Condition="RUST$ENUM$DISR == 0x0">None</DisplayString>
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Some({__0})</DisplayString>
<Expand>
<Item Name="[value]" ExcludeView="simple" Condition="RUST$ENUM$DISR == 1">__0</Item>
</Expand>
</Type>

<Type Name="core::option::Option&lt;*&gt;" Priority="MediumLow">
<DisplayString Condition="*(void**)this == nullptr">None</DisplayString>
<DisplayString>Some({($T1 *)this})</DisplayString>
@@ -30,15 +22,6 @@
</Expand>
</Type>

<Type Name="core::result::Result&lt;*&gt;">
<DisplayString Condition="RUST$ENUM$DISR == 0x0">Ok({__0})</DisplayString>
<DisplayString Condition="RUST$ENUM$DISR == 0x1">Err({(*($T2*) &amp;__0)})</DisplayString>
<Expand>
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x0">__0</Item>
<Item Name="[value]" Condition="RUST$ENUM$DISR == 0x1">(*($T2*) &amp;__0)</Item>
</Expand>
</Type>

<Type Name="core::ptr::non_null::NonNull&lt;*&gt;">
<DisplayString>{(void*) pointer}</DisplayString>
<Expand>
16 changes: 8 additions & 8 deletions src/test/codegen/async-fn-debug-msvc.rs
Original file line number Diff line number Diff line change
@@ -17,33 +17,33 @@ async fn async_fn_test() {
// FIXME: No way to reliably check the filename.

// CHECK-DAG: [[ASYNC_FN:!.*]] = !DINamespace(name: "async_fn_test"
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0", scope: [[ASYNC_FN]]
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
// For brevity, we only check the struct name and members of the last variant.
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 11,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 12,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 14,
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[ASYNC_FN]],
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
// CHECK-SAME: flags: DIFlagArtificial
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
// CHECK-NOT: flags: DIFlagArtificial
16 changes: 8 additions & 8 deletions src/test/codegen/generator-debug-msvc.rs
Original file line number Diff line number Diff line change
@@ -21,33 +21,33 @@ fn generator_test() -> impl Generator<Yield = i32, Return = ()> {
// FIXME: No way to reliably check the filename.

// CHECK-DAG: [[GEN_FN:!.*]] = !DINamespace(name: "generator_test"
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0", scope: [[GEN_FN]]
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK-DAG: [[GEN:!.*]] = !DICompositeType(tag: DW_TAG_union_type, name: "generator-0"
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant0", scope: [[GEN]],
// For brevity, we only check the struct name and members of the last variant.
// CHECK-SAME: file: [[FILE:![0-9]*]], line: 14,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant1", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 18,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant2", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 18,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant3", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 15,
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, scope: [[GEN]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant4", scope: [[GEN]],
// CHECK-SAME: file: [[FILE]], line: 17,
// CHECK-SAME: baseType: [[VARIANT:![0-9]*]]
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN_FN]],
// CHECK: [[S1:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "Suspend1", scope: [[GEN]],
// CHECK-NOT: flags: DIFlagArtificial
// CHECK-SAME: )
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "RUST$ENUM$DISR", scope: [[S1]],
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "variant$", scope: [[S1]],
// CHECK-SAME: flags: DIFlagArtificial
// CHECK: {{!.*}} = !DIDerivedType(tag: DW_TAG_member, name: "s", scope: [[S1]]
// CHECK-NOT: flags: DIFlagArtificial
97 changes: 97 additions & 0 deletions src/test/debuginfo/msvc-pretty-enums.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// only-cdb
// ignore-tidy-linelength
// compile-flags:-g

// cdb-command: g

// Note: The natvis used to visualize niche-layout enums don't work correctly in cdb
// so the best we can do is to make sure we are generating the right debuginfo

// cdb-command: dx -r2 a,!
// cdb-check:a,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
// cdb-check: [+0x000] __0 : Low (0x2) [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : 0x2 [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]

// cdb-command: dx -r2 b,!
// cdb-check:b,! [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Some]
// cdb-check: [+0x000] __0 : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : None (0x11) [Type: enum$<core::option::Option<enum$<msvc_pretty_enums::CStyleEnum>>, 2, 16, Some>::Discriminant$]

// cdb-command: dx -r2 c,!
// cdb-check:c,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : 0x11 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : Tag1 (0x11) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 d,!
// cdb-check:d,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : 0x10 [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 e,!
// cdb-check:e,! [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Data]
// cdb-check: [+0x000] my_data : 0x13 [Type: msvc_pretty_enums::CStyleEnum]
// cdb-check: [+0x000] discriminant : Tag2 (0x13) [Type: enum$<msvc_pretty_enums::NicheLayoutEnum, 2, 16, Data>::Discriminant$]

// cdb-command: dx -r2 f,!
// cdb-check:f,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 : 0x[...] : 0x1 [Type: unsigned int *]
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]

// cdb-command: dx -r2 g,!
// cdb-check:g,! [Type: enum$<core::option::Option<u32*>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 : 0x0 [Type: unsigned int *]
// cdb-check: [+0x000] discriminant : None (0x0) [Type: enum$<core::option::Option<u32*>, 1, [...], Some>::Discriminant$]

// cdb-command: dx h
// cdb-check:h : Some [Type: enum$<core::option::Option<u32>>]
// cdb-check: [+0x000] variant$ : Some (0x1) [Type: core::option::Option]
// cdb-check: [+0x004] __0 : 0xc [Type: unsigned int]

// cdb-command: dx i
// cdb-check:i : None [Type: enum$<core::option::Option<u32>>]
// cdb-check: [+0x000] variant$ : None (0x0) [Type: core::option::Option]

// cdb-command: dx j
// cdb-check:j : High (0x10) [Type: msvc_pretty_enums::CStyleEnum]

// cdb-command: dx -r2 k,!
// cdb-check:k,! [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]
// cdb-check: [+0x000] dataful_variant [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Some]
// cdb-check: [+0x000] __0 [Type: alloc::string::String]
// cdb-check: [+0x000] discriminant : 0x[...] [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>::Discriminant$]

pub enum CStyleEnum {
Low = 2,
High = 16,
}

pub enum NicheLayoutEnum {
Tag1,
Data { my_data: CStyleEnum },
Tag2,
}

fn main() {
let a = Some(CStyleEnum::Low);
let b = Option::<CStyleEnum>::None;
let c = NicheLayoutEnum::Tag1;
let d = NicheLayoutEnum::Data { my_data: CStyleEnum::High };
let e = NicheLayoutEnum::Tag2;
let f = Some(&1u32);
let g = Option::<&'static u32>::None;
let h = Some(12u32);
let i = Option::<u32>::None;
let j = CStyleEnum::High;
let k = Some("IAMA optional string!".to_string());

zzz(); // #break
}

fn zzz() { () }
7 changes: 4 additions & 3 deletions src/test/debuginfo/pretty-std.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// ignore-freebsd: gdb package too new
// only-cdb // "Temporarily" ignored on GDB/LLDB due to debuginfo tests being disabled, see PR 47155
// ignore-android: FIXME(#10381)
// ignore-tidy-linelength
// compile-flags:-g
// min-gdb-version: 7.7
// min-lldb-version: 310
@@ -111,11 +112,11 @@
// NOTE: OsString doesn't have a .natvis entry yet.

// cdb-command: dx some
// cdb-check:some : Some(8) [Type: [...]::Option<i16>]
// cdb-check:some : Some [Type: enum$<core::option::Option<i16>>]
// cdb-command: dx none
// cdb-check:none : None [Type: [...]::Option<i64>]
// cdb-check:none : None [Type: enum$<core::option::Option<i64>>]
// cdb-command: dx some_string
// cdb-check:some_string : Some("IAMA optional string!") [[...]::Option<[...]::String>]
// cdb-check:some_string [Type: enum$<core::option::Option<alloc::string::String>, 1, [...], Some>]

#![allow(unused_variables)]
use std::ffi::OsString;