diff --git a/crates/cargo-util-schemas/src/manifest/mod.rs b/crates/cargo-util-schemas/src/manifest/mod.rs
index 563b7d329c7..beda04ecd89 100644
--- a/crates/cargo-util-schemas/src/manifest/mod.rs
+++ b/crates/cargo-util-schemas/src/manifest/mod.rs
@@ -899,6 +899,7 @@ pub struct TomlProfile {
     pub dir_name: Option<String>,
     pub inherits: Option<String>,
     pub strip: Option<StringOrBool>,
+    pub force_frame_pointers: Option<bool>,
     // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
     pub rustflags: Option<Vec<String>>,
     // These two fields must be last because they are sub-tables, and TOML
@@ -995,6 +996,10 @@ impl TomlProfile {
             self.strip = Some(v.clone());
         }
 
+        if let Some(v) = &profile.force_frame_pointers {
+            self.force_frame_pointers = Some(v.clone());
+        }
+
         if let Some(v) = &profile.trim_paths {
             self.trim_paths = Some(v.clone())
         }
diff --git a/src/cargo/core/compiler/mod.rs b/src/cargo/core/compiler/mod.rs
index 2a0fd8b40f3..65be618ba9d 100644
--- a/src/cargo/core/compiler/mod.rs
+++ b/src/cargo/core/compiler/mod.rs
@@ -1106,6 +1106,7 @@ fn build_base_args(
         ref panic,
         incremental,
         strip,
+        force_frame_pointers,
         rustflags: profile_rustflags,
         trim_paths,
         ..
@@ -1285,6 +1286,10 @@ fn build_base_args(
         cmd.arg("-C").arg(format!("strip={}", strip));
     }
 
+    if force_frame_pointers {
+        cmd.arg("-C").arg("force-frame-pointers=on");
+    }
+
     if unit.is_std {
         // -Zforce-unstable-if-unmarked prevents the accidental use of
         // unstable crates within the sysroot (such as "extern crate libc" or
diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs
index 86d64d668d8..077f6d19281 100644
--- a/src/cargo/core/profiles.rs
+++ b/src/cargo/core/profiles.rs
@@ -331,6 +331,7 @@ impl Profiles {
         result.debuginfo = for_unit_profile.debuginfo;
         result.opt_level = for_unit_profile.opt_level;
         result.trim_paths = for_unit_profile.trim_paths.clone();
+        result.force_frame_pointers = for_unit_profile.force_frame_pointers;
         result
     }
 
@@ -574,6 +575,9 @@ fn merge_profile(profile: &mut Profile, toml: &TomlProfile) {
     if let Some(flags) = &toml.rustflags {
         profile.rustflags = flags.iter().map(InternedString::from).collect();
     }
+    if let Some(force_frame_pointers) = toml.force_frame_pointers {
+        profile.force_frame_pointers = force_frame_pointers;
+    }
     if let Some(trim_paths) = &toml.trim_paths {
         profile.trim_paths = Some(trim_paths.clone());
     }
@@ -624,6 +628,7 @@ pub struct Profile {
     pub incremental: bool,
     pub panic: PanicStrategy,
     pub strip: Strip,
+    pub force_frame_pointers: bool,
     #[serde(skip_serializing_if = "Vec::is_empty")] // remove when `rustflags` is stablized
     // Note that `rustflags` is used for the cargo-feature `profile_rustflags`
     pub rustflags: Vec<InternedString>,
@@ -649,6 +654,7 @@ impl Default for Profile {
             incremental: false,
             panic: PanicStrategy::Unwind,
             strip: Strip::Deferred(StripInner::None),
+            force_frame_pointers: false,
             rustflags: vec![],
             trim_paths: None,
         }
@@ -678,6 +684,7 @@ compact_debug! {
                 incremental
                 panic
                 strip
+                force_frame_pointers
                 rustflags
                 trim_paths
             )]
@@ -746,7 +753,12 @@ impl Profile {
             self.debug_assertions,
             self.overflow_checks,
             self.rpath,
-            (self.incremental, self.panic, self.strip),
+            (
+                self.incremental,
+                self.panic,
+                self.strip,
+                self.force_frame_pointers,
+            ),
             &self.rustflags,
             &self.trim_paths,
         )
diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs
index 80be8dabc1c..36b393d0828 100644
--- a/tests/testsuite/config.rs
+++ b/tests/testsuite/config.rs
@@ -1662,6 +1662,7 @@ fn all_profile_options() {
         dir_name: Some(String::from("dir_name")),
         inherits: Some(String::from("debug")),
         strip: Some(cargo_toml::StringOrBool::String("symbols".to_string())),
+        force_frame_pointers: Some(true),
         package: None,
         build_override: None,
         rustflags: None,
diff --git a/tests/testsuite/profiles.rs b/tests/testsuite/profiles.rs
index 36a70391050..50d2bbed143 100644
--- a/tests/testsuite/profiles.rs
+++ b/tests/testsuite/profiles.rs
@@ -704,6 +704,79 @@ fn do_not_strip_debuginfo_with_requested_debug() {
         .run();
 }
 
+#[cargo_test]
+fn force_frame_pointers_on_works() {
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+            [profile.dev]
+            force-frame-pointers = true
+
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            edition = "2015"
+            "#,
+        )
+        .file("src/main.rs", "fn main() {}")
+        .build();
+
+    p.cargo("build -v")
+        .with_stderr_data(str![[r#"
+[COMPILING] foo v0.0.1 ([ROOT]/foo)
+[RUNNING] `rustc --crate-name foo [..] -C force-frame-pointers=on [..]`
+[FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s
+
+"#]])
+        .run();
+}
+
+#[cargo_test]
+fn force_frame_pointers_off_works() {
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+            [profile.dev]
+            force-frame-pointers = false
+
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            edition = "2015"
+            "#,
+        )
+        .file("src/main.rs", "fn main() {}")
+        .build();
+
+    p.cargo("build -v")
+        .with_stdout_does_not_contain(str!["force-frame-pointers"])
+        .with_stderr_does_not_contain(str!["force-frame-pointers"])
+        .run();
+}
+
+#[cargo_test]
+fn force_frame_pointers_unspecified_works() {
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+            [package]
+            name = "foo"
+            version = "0.0.1"
+            edition = "2015"
+            "#,
+        )
+        .file("src/main.rs", "fn main() {}")
+        .build();
+
+    p.cargo("build -v")
+        .with_stdout_does_not_contain(str!["force-frame-pointers"])
+        .with_stderr_does_not_contain(str!["force-frame-pointers"])
+        .run();
+}
+
 #[cargo_test]
 fn rustflags_works() {
     let p = project()
diff --git a/tests/testsuite/unit_graph.rs b/tests/testsuite/unit_graph.rs
index cccc79f1295..b959065e3f9 100644
--- a/tests/testsuite/unit_graph.rs
+++ b/tests/testsuite/unit_graph.rs
@@ -75,6 +75,7 @@ fn simple() {
         "codegen_units": null,
         "debug_assertions": true,
         "debuginfo": 2,
+        "force_frame_pointers": false,
         "incremental": false,
         "lto": "false",
         "name": "dev",
@@ -120,6 +121,7 @@ fn simple() {
         "codegen_units": null,
         "debug_assertions": true,
         "debuginfo": 2,
+        "force_frame_pointers": false,
         "incremental": false,
         "lto": "false",
         "name": "dev",
@@ -158,6 +160,7 @@ fn simple() {
         "codegen_units": null,
         "debug_assertions": true,
         "debuginfo": 2,
+        "force_frame_pointers": false,
         "incremental": false,
         "lto": "false",
         "name": "dev",
@@ -201,6 +204,7 @@ fn simple() {
         "codegen_units": null,
         "debug_assertions": true,
         "debuginfo": 2,
+        "force_frame_pointers": false,
         "incremental": false,
         "lto": "false",
         "name": "dev",