Bug 1714465 - Add ThreadNamesStream support to minidump_writer_linux. r=gsvelto

Differential Revision: https://phabricator.services.mozilla.com/D117154
This commit is contained in:
M. Sirringhaus 2021-06-30 15:03:52 +00:00
Родитель c4f6df0559
Коммит 086d5c7b45
21 изменённых файлов: 238 добавлений и 71 удалений

Просмотреть файл

@ -10,7 +10,7 @@ rev = "4af6c367603869a30fddb5ffb0aba2b9477ba92e"
[source."https://github.com/msirringhaus/minidump_writer_linux.git"]
git = "https://github.com/msirringhaus/minidump_writer_linux.git"
replace-with = "vendored-sources"
rev = "5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f"
rev = "85551909b95a5cf553a85dbcddfa5f117cfbbe0e"
[source."https://github.com/mozilla/neqo"]
git = "https://github.com/mozilla/neqo"

2
Cargo.lock сгенерированный
Просмотреть файл

@ -2948,7 +2948,7 @@ dependencies = [
[[package]]
name = "minidump_writer_linux"
version = "0.1.0"
source = "git+https://github.com/msirringhaus/minidump_writer_linux.git?rev=5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f#5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f"
source = "git+https://github.com/msirringhaus/minidump_writer_linux.git?rev=85551909b95a5cf553a85dbcddfa5f117cfbbe0e#85551909b95a5cf553a85dbcddfa5f117cfbbe0e"
dependencies = [
"byteorder",
"goblin",

Просмотреть файл

@ -82,7 +82,8 @@ packed_simd = { git = "https://github.com/hsivonen/packed_simd", rev="0917fe7800
rlbox_lucet_sandbox = { git = "https://github.com/PLSysSec/rlbox_lucet_sandbox/", rev="5c8e79048d3ff6f434109e19d4aee4ff8624d3d7" }
nix = { git = "https://github.com/shravanrn/nix/", rev="4af6c367603869a30fddb5ffb0aba2b9477ba92e" }
spirv_cross = { git = "https://github.com/kvark/spirv_cross", branch = "wgpu5" }
minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "5cea1c9a3d8ed3ed2d7bdd5be3285e7821400b7f" }
minidump_writer_linux = { git = "https://github.com/msirringhaus/minidump_writer_linux.git", rev = "85551909b95a5cf553a85dbcddfa5f117cfbbe0e" }
[patch.crates-io.cranelift-codegen]
git = "https://github.com/bytecodealliance/wasmtime"
@ -96,6 +97,7 @@ rev = "824fa69756523f2b6d49029fe25de94130b1f144"
[patch.crates-io.autocfg]
path = "third_party/rust/autocfg"
# Patch mio 0.6 to use winapi 0.3 and miow 0.3, getting rid of winapi 0.2.
# There is not going to be new version of mio 0.6, mio now being >= 0.7.11.
[patch.crates-io.mio]
@ -103,4 +105,4 @@ path = "third_party/rust/mio"
# Patch failure 0.1.8 to disable the backtrace feature by default. See bug 1608157.
[patch.crates-io.failure]
path = "third_party/rust/failure"
path = "third_party/rust/failure"

Просмотреть файл

@ -1 +1 @@
{"files":{"Cargo.toml":"ce8acecb8ab04bca807b956eda6b8f883e491cb8ded8ac4fe947e21a1a8b5b0e","LICENSE":"1ecdd8e8977af83c07c5f97bec87b47d27059b7ea323ca3160fbfa2314f5d99c","src/android.rs":"70f79d68e0b2a5afcd13788d3c11df094750b83995a03c8100484f20c295a1fb","src/app_memory.rs":"909676c916c0ffaa3a813632c162f5b1207f8502408b6b3bab48a5f842948c71","src/auxv_reader.rs":"29a03c082c179733ddefee59e903dc5a43b7e681f55a1fdd9cfdec7587bd7b35","src/bin/test.rs":"b4b119a25ab1fcd71958061c23f741e956b81db28c4d7f8c5728b62eff0913e0","src/crash_context/crash_context_aarch64.rs":"ce477360b5d16f11814b195fb12c461183bd06fd08cdd14e76c9c2bdcbaccd47","src/crash_context/crash_context_arm.rs":"6e2f1db21b93fadfa034a5246c70687d6654a40627f1ecf849511cf480a96bad","src/crash_context/crash_context_mips.rs":"430669c21f03cefd479e46265dc3c4d3495acaf8b01da09dc0b0e4168306b508","src/crash_context/crash_context_x86.rs":"136a1b0f8107986505df95a1a5423f352ae3ea383e25335e60c6242acdff34da","src/crash_context/crash_context_x86_64.rs":"8fb5f86944ec5a02bc6a7dba4ef698d088686de2cf5fb0a6014592ae394c2e47","src/crash_context/mod.rs":"2b61fad8c0cab1a2496db36977bd0b8e664b095bc51e25f9645ac90f3322e9ef","src/dso_debug.rs":"3697ade2a6982663cb888521119eedfa1613b750f26e65cd41d8a4c706001e93","src/dumper_cpu_info/cpu_info_arm.rs":"61671737bb2187f92423fbfb6f0f04ecaa86937121868d9c545648a0e7d4be05","src/dumper_cpu_info/cpu_info_x86_mips.rs":"331f10edc5385282f3615bfa93049fe8a97bc81ebea761100fc56752ed2ad6b8","src/dumper_cpu_info/mod.rs":"5322108f35021cf03d61ccb4c54a2b4875dfdff774a4297148925d76525d125c","src/errors.rs":"a94d9d8d3b3ff89a61cb907990d9f75c927881018d7bdbf906f154960a6453fc","src/lib.rs":"d475c9a464370348e0b5f0d8c97923e09b11d476b9da9d3c4cfb4d393e2505a1","src/linux_ptrace_dumper.rs":"1f3becdde18f03d8707b32328d81e6d217219c5a7388a8b9f8a5abe941b4f5ed","src/maps_reader.rs":"5c01299dea57a92bd331a27f2f64b433cf9c8103082940769de0894dbf7e611b","src/minidump_cpu/minidump_cpu_aarch64.rs":"18524f8a3a3d3eda7d0fafb1c168ff06dee1886da0f4278f3c2a54147ef79bc9","src/minidump_cpu/minidump_cpu_amd64.rs":"c4b3cef86ea53864c026d7c598d8a7f59ca6b1ad7e881f94ce34f39dd6b72e33","src/minidump_cpu/minidump_cpu_arm.rs":"bfbcbf280da47d715189f76c8d9749cb2808d7730bb1fe906a21b42a16c3b044","src/minidump_cpu/minidump_cpu_mips.rs":"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b","src/minidump_cpu/minidump_cpu_ppc.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_ppc64.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_sparc.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_x86.rs":"3dcb2aa4264d76f0727b9750ffce0032738bea97be95fde92089a9d11926c63c","src/minidump_cpu/mod.rs":"50e8df5e294e9d162c97dacb9448bba0ffc1e4bf992f8a2b9cc718f12b92a370","src/minidump_format.rs":"5db188e14a458f03b0bfaedf385c1b729a52700e03e3034f23d44749a0ca9ab1","src/minidump_writer.rs":"618c7b6eb269ccc171face0e16ef2ec00763aecf64d3416bd1dda3b91598083b","src/sections/app_memory.rs":"6fb616e0aa495fd5d9a487231750ac3c2ffa275802adf0f7e60c7666f5e14a60","src/sections/exception_stream.rs":"5c49b80b5acf3694c0c2e275b083ae8f544ed7ba7b8d417303706bfaadf3419e","src/sections/mappings.rs":"ce3babbc55b7e7e8a5e3036fcd0fadd3b8d5402ac484bd066f84997a6419d886","src/sections/memory_list_stream.rs":"02e678bd28de10e854d673ccefeb574067e64bdaefda5212a8161ea194280a7d","src/sections/mod.rs":"616c70e74d01b1116df0c9c1883f2c80aa74ab3056aeea4ed4e3f8b7c8866582","src/sections/systeminfo_stream.rs":"d481a86c6aa86a32378bb3d60b6f7f09fd57155dfa728d0dfa50aa17ef72c836","src/sections/thread_list_stream.rs":"4dd6a6342d481307f25c3f49e8a1cd080aa4dd46338bf50b6bb2ab7c01a143c7","src/thread_info/mod.rs":"a72919ae6fca365db504669bfe4fd7f80f92da8f5d3b4d6aef0bc4b8e86df440","src/thread_info/thread_info_aarch64.rs":"6e443b2401c651d7607df251015566e7e6183a7522594d51da210a4594d62434","src/thread_info/thread_info_arm.rs":"95b15496cdaa833bec80d3acb53093b38aef535030ab483fdcdef4a2cda52c61","src/thread_info/thread_info_mips.rs":"332d7888bb08a36d3b77023a4a0385193a07b1c6c5bf0e91aacb5d948ba8b903","src/thread_info/thread_info_x86.rs":"fbde8930f8d277d58faa34d90ddf6f27d6438cd5a3c017df4f878897f6f84eca","tests/common/mod.rs":"57b9df4b02b48a82451a2352359ed78a6a7f05e8157762d324bfa5344c208715","tests/minidump_writer.rs":"e560c7f449b6837de506f1038b0224622c2fa6bb75706a3fd6d84d99529829df","tests/ptrace_dumper.rs":"47a31bcd5738d84a3699fb0753696ad6bf5caef77240b4ae9c30e5ca236d1132"},"package":null}
{"files":{"Cargo.toml":"4b4763fa63d2bb2f062e5e276f84849454768810b925d548511ece424555e95a","LICENSE":"1ecdd8e8977af83c07c5f97bec87b47d27059b7ea323ca3160fbfa2314f5d99c","src/android.rs":"70f79d68e0b2a5afcd13788d3c11df094750b83995a03c8100484f20c295a1fb","src/app_memory.rs":"909676c916c0ffaa3a813632c162f5b1207f8502408b6b3bab48a5f842948c71","src/auxv_reader.rs":"b8817a185fe8fb7a2b9624cce3cfd51aad43d7c59fc15b3337808398bf372795","src/bin/test.rs":"4172d3e8ad10b8cbde4632e0248b012eb59de55f68a3e1a69a52d8cc61c9b01a","src/crash_context/crash_context_aarch64.rs":"ce477360b5d16f11814b195fb12c461183bd06fd08cdd14e76c9c2bdcbaccd47","src/crash_context/crash_context_arm.rs":"6e2f1db21b93fadfa034a5246c70687d6654a40627f1ecf849511cf480a96bad","src/crash_context/crash_context_mips.rs":"430669c21f03cefd479e46265dc3c4d3495acaf8b01da09dc0b0e4168306b508","src/crash_context/crash_context_x86.rs":"136a1b0f8107986505df95a1a5423f352ae3ea383e25335e60c6242acdff34da","src/crash_context/crash_context_x86_64.rs":"8fb5f86944ec5a02bc6a7dba4ef698d088686de2cf5fb0a6014592ae394c2e47","src/crash_context/mod.rs":"2b61fad8c0cab1a2496db36977bd0b8e664b095bc51e25f9645ac90f3322e9ef","src/dso_debug.rs":"3697ade2a6982663cb888521119eedfa1613b750f26e65cd41d8a4c706001e93","src/dumper_cpu_info/cpu_info_arm.rs":"61671737bb2187f92423fbfb6f0f04ecaa86937121868d9c545648a0e7d4be05","src/dumper_cpu_info/cpu_info_x86_mips.rs":"e8523a022a1f2c98c40f00fc18286ed6f4b9b7a7d1ef993638979c581c2b6af8","src/dumper_cpu_info/mod.rs":"5322108f35021cf03d61ccb4c54a2b4875dfdff774a4297148925d76525d125c","src/errors.rs":"05f689858847467bcc1afcd91dc483d3307331d3d1dad46f199a6583a8ae641f","src/lib.rs":"d475c9a464370348e0b5f0d8c97923e09b11d476b9da9d3c4cfb4d393e2505a1","src/linux_ptrace_dumper.rs":"368721d40c415a1b86dc147a63c74537e99e22df4fecc2e63f3969e9090dcc7c","src/maps_reader.rs":"d322d0509340e2c9246feb3e2bff0ad21d866c4c64d2287269c16bf413f89240","src/minidump_cpu/minidump_cpu_aarch64.rs":"18524f8a3a3d3eda7d0fafb1c168ff06dee1886da0f4278f3c2a54147ef79bc9","src/minidump_cpu/minidump_cpu_amd64.rs":"c4b3cef86ea53864c026d7c598d8a7f59ca6b1ad7e881f94ce34f39dd6b72e33","src/minidump_cpu/minidump_cpu_arm.rs":"bfbcbf280da47d715189f76c8d9749cb2808d7730bb1fe906a21b42a16c3b044","src/minidump_cpu/minidump_cpu_mips.rs":"01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b","src/minidump_cpu/minidump_cpu_ppc.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_ppc64.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_sparc.rs":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","src/minidump_cpu/minidump_cpu_x86.rs":"3dcb2aa4264d76f0727b9750ffce0032738bea97be95fde92089a9d11926c63c","src/minidump_cpu/mod.rs":"50e8df5e294e9d162c97dacb9448bba0ffc1e4bf992f8a2b9cc718f12b92a370","src/minidump_format.rs":"53c385e743297f6a0abf9463b517ede5f094136c4712a4c5e5fa3d948be67171","src/minidump_writer.rs":"b4ac20e56e3ba393640375b85cc5f271a131633beb444b1205ba1efe95635fc4","src/sections/app_memory.rs":"6fb616e0aa495fd5d9a487231750ac3c2ffa275802adf0f7e60c7666f5e14a60","src/sections/exception_stream.rs":"5c49b80b5acf3694c0c2e275b083ae8f544ed7ba7b8d417303706bfaadf3419e","src/sections/mappings.rs":"1c82bc330344556afd469326ff0d04b17c387e1f863820fd252de95dfcb90d8b","src/sections/memory_list_stream.rs":"02e678bd28de10e854d673ccefeb574067e64bdaefda5212a8161ea194280a7d","src/sections/mod.rs":"c75612b0fc7d632dc1d33961d5c4b0df42a7c25e0ed3e4af4586e0efe7f0ae48","src/sections/systeminfo_stream.rs":"d481a86c6aa86a32378bb3d60b6f7f09fd57155dfa728d0dfa50aa17ef72c836","src/sections/thread_list_stream.rs":"8e0ca140e292804aa32686c0c65ac6803b22f7b853006ca3124b2fcee19cb2f5","src/sections/thread_names_stream.rs":"aed2ef7c0107092f79069dcde3322b99fc1a31b0ae51f8b011ed0aa4f8ba3b9c","src/thread_info/mod.rs":"ae6256f358bd371a573723d88a61ccc77c31fbe216bc2a086a848611099ca841","src/thread_info/thread_info_aarch64.rs":"6e443b2401c651d7607df251015566e7e6183a7522594d51da210a4594d62434","src/thread_info/thread_info_arm.rs":"95b15496cdaa833bec80d3acb53093b38aef535030ab483fdcdef4a2cda52c61","src/thread_info/thread_info_mips.rs":"332d7888bb08a36d3b77023a4a0385193a07b1c6c5bf0e91aacb5d948ba8b903","src/thread_info/thread_info_x86.rs":"fbde8930f8d277d58faa34d90ddf6f27d6438cd5a3c017df4f878897f6f84eca","tests/common/mod.rs":"9d063d7729840bf11c5769b6984ecd6c6d36a3284e55d6cea1b65f832aa4f57f","tests/minidump_writer.rs":"e6e381b1b4cd5cfcea93375a236c2a45f655ea25f5afa4053ee1e108a002c516","tests/ptrace_dumper.rs":"987be63e12f2d82d6e0ccd09234761ff0b6544341e4201a1b4a56fa5c64dbc2f"},"package":null}

Просмотреть файл

@ -18,5 +18,5 @@ goblin = "0.1.2"
thiserror = "1.0.21"
[dev-dependencies]
minidump = {git = "https://github.com/luser/rust-minidump" }
minidump-common = {git = "https://github.com/luser/rust-minidump" }
minidump = {git = "https://github.com/luser/rust-minidump", rev="9652b3b" }
minidump-common = {git = "https://github.com/luser/rust-minidump", rev="9652b3b" }

Просмотреть файл

@ -56,11 +56,7 @@ impl Iterator for ProcfsAuxvIter {
// assume something will fail
self.keep_going = false;
self.buf.clear();
// fill vec so we can slice into it
for _ in 0..self.pair_size {
self.buf.push(0);
}
self.buf = vec![0; self.pair_size];
let mut read_bytes: usize = 0;
while read_bytes < self.pair_size {

Просмотреть файл

@ -31,12 +31,12 @@ fn test_setup() -> Result<()> {
fn test_thread_list() -> Result<()> {
let ppid = getppid();
let dumper = linux_ptrace_dumper::LinuxPtraceDumper::new(ppid.as_raw())?;
test!(dumper.threads.len() >= 1, "No threads")?;
test!(!dumper.threads.is_empty(), "No threads")?;
test!(
dumper
.threads
.iter()
.filter(|&x| x == &ppid.as_raw())
.filter(|x| x.tid == ppid.as_raw())
.count()
== 1,
"Thread found multiple times"
@ -133,7 +133,7 @@ fn test_linux_gate_mapping_id() -> Result<()> {
break;
}
}
test!(found_linux_gate == true, "found no linux_gate")?;
test!(found_linux_gate, "found no linux_gate")?;
Ok(())
}
@ -163,7 +163,7 @@ fn test_mappings_include_linux_gate() -> Result<()> {
break;
}
}
test!(found_linux_gate == true, "found no linux_gate")?;
test!(found_linux_gate, "found no linux_gate")?;
Ok(())
}
@ -183,6 +183,23 @@ fn spawn_and_wait(num: usize) -> Result<()> {
}
}
fn spawn_name_wait(num: usize) -> Result<()> {
// One less than the requested amount, as the main thread counts as well
for id in 1..num {
std::thread::Builder::new()
.name(format!("thread_{}", id))
.spawn(|| {
println!("1");
loop {
std::thread::park();
}
})?;
}
println!("1");
loop {
std::thread::park();
}
}
fn spawn_mmap_wait() -> Result<()> {
let page_size = nix::unistd::sysconf(nix::unistd::SysconfVar::PAGE_SIZE).unwrap();
let memory_size = page_size.unwrap() as usize;
@ -233,14 +250,17 @@ fn main() -> Result<()> {
"spawn_alloc_wait" => spawn_alloc_wait(),
_ => Err("Len 1: Unknown test option".into()),
},
2 => {
if args[0] == "spawn_and_wait" {
2 => match args[0].as_ref() {
"spawn_and_wait" => {
let num_of_threads: usize = args[1].parse().unwrap();
spawn_and_wait(num_of_threads)
} else {
Err(format!("Len 2: Unknown test option: {}", args[0]).into())
}
}
"spawn_name_wait" => {
let num_of_threads: usize = args[1].parse().unwrap();
spawn_name_wait(num_of_threads)
}
_ => Err(format!("Len 2: Unknown test option: {}", args[0]).into()),
},
3 => {
if args[0] == "find_mappings" {
let addr1: usize = args[1].parse().unwrap();

Просмотреть файл

@ -59,7 +59,7 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
continue;
}
let split: Vec<_> = line.split(":").map(|x| x.trim()).collect();
let split: Vec<_> = line.split(':').map(|x| x.trim()).collect();
let field = split[0];
let value = split.get(1); // Option, might be missing
@ -90,7 +90,7 @@ pub fn write_cpu_information(sys_info: &mut MDRawSystemInfo) -> Result<()> {
}
}
// make sure we got everything we wanted
if !cpu_info_table.iter().all(|x| x.found == true) {
if !cpu_info_table.iter().all(|x| x.found) {
return Err(CpuInfoError::NotAllProcEntriesFound);
}
// cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo,

Просмотреть файл

@ -178,6 +178,16 @@ pub enum SectionThreadListError {
IOError(#[from] std::io::Error),
}
#[derive(Debug, Error)]
pub enum SectionThreadNamesError {
#[error("Failed integer conversion")]
TryFromIntError(#[from] std::num::TryFromIntError),
#[error("Failed to write to memory")]
MemoryWriterError(#[from] MemoryWriterError),
#[error("Failed to write to memory buffer")]
IOError(#[from] std::io::Error),
}
#[derive(Debug, Error)]
pub enum SectionDsoDebugError {
#[error("Failed to write to memory")]
@ -216,6 +226,8 @@ pub enum WriterError {
SectionSystemInfoError(#[from] SectionSystemInfoError),
#[error("Failed when writing section ThreadList")]
SectionThreadListError(#[from] SectionThreadListError),
#[error("Failed when writing section ThreadNameList")]
SectionThreadNamesError(#[from] SectionThreadNamesError),
#[error("Failed when writing section DsoDebug")]
SectionDsoDebugError(#[from] SectionDsoDebugError),
#[error("Failed to write to memory")]

Просмотреть файл

@ -17,11 +17,17 @@ use std::io::{BufRead, BufReader};
use std::path;
use std::result::Result;
#[derive(Debug, Clone)]
pub struct Thread {
pub tid: Pid,
pub name: Option<String>,
}
#[derive(Debug)]
pub struct LinuxPtraceDumper {
pub pid: Pid,
threads_suspended: bool,
pub threads: Vec<Pid>,
pub threads: Vec<Thread>,
pub auxv: HashMap<AuxvType, AuxvType>,
pub mappings: Vec<MappingInfo>,
}
@ -152,7 +158,7 @@ impl LinuxPtraceDumper {
// If the thread either disappeared before we could attach to it, or if
// it was part of the seccomp sandbox's trusted code, it is OK to
// silently drop it from the minidump.
self.threads.retain(|&x| Self::suspend_thread(x).is_ok());
self.threads.retain(|x| Self::suspend_thread(x.tid).is_ok());
if self.threads.is_empty() {
Err(DumperError::SuspendNoThreadsLeft)
@ -166,7 +172,7 @@ impl LinuxPtraceDumper {
let mut result = Ok(());
if self.threads_suspended {
for thread in &self.threads {
match Self::resume_thread(*thread) {
match Self::resume_thread(thread.tid) {
Ok(_) => {}
x => {
result = x;
@ -175,13 +181,14 @@ impl LinuxPtraceDumper {
}
}
self.threads_suspended = false;
return result;
result
}
/// Parse /proc/$pid/task to list all the threads of the process identified by
/// pid.
fn enumerate_threads(&mut self) -> Result<(), InitError> {
let filename = format!("/proc/{}/task", self.pid);
let pid = self.pid;
let filename = format!("/proc/{}/task", pid);
let task_path = path::PathBuf::from(&filename);
if task_path.is_dir() {
std::fs::read_dir(task_path)
@ -194,8 +201,15 @@ impl LinuxPtraceDumper {
.to_str()
.and_then(|name| name.parse::<Pid>().ok())
})
.map(|tid| self.threads.push(tid)) // Push the resulting Pids
.count(); // Execute iterator
.map(|tid| {
// Read the thread-name (if there is any)
let name = std::fs::read_to_string(format!("/proc/{}/task/{}/comm", pid, tid))
// NOTE: This is a bit wasteful as it does two allocations in order to trim, but leaving it for now
.map(|s| s.trim_end().to_string())
.ok();
(tid, name)
})
.for_each(|(tid, name)| self.threads.push(Thread { tid, name }))
}
Ok(())
}
@ -289,8 +303,7 @@ impl LinuxPtraceDumper {
return Err(ThreadInfoError::IndexOutOfBounds(index, self.threads.len()));
}
let tid = self.threads[index];
ThreadInfo::create(self.pid, tid)
ThreadInfo::create(self.pid, self.threads[index].tid)
}
// Get information about the stack, given the stack pointer. We don't try to
@ -427,7 +440,7 @@ impl LinuxPtraceDumper {
}
// Find the mapping which the given memory address falls in.
pub fn find_mapping<'a>(&'a self, address: usize) -> Option<&'a MappingInfo> {
pub fn find_mapping(&self, address: usize) -> Option<&MappingInfo> {
for map in &self.mappings {
if address >= map.start_address && address - map.start_address < map.size {
return Some(&map);
@ -439,7 +452,7 @@ impl LinuxPtraceDumper {
// Find the mapping which the given memory address falls in. Uses the
// unadjusted mapping address range from the kernel, rather than the
// biased range.
pub fn find_mapping_no_bias<'a>(&'a self, address: usize) -> Option<&'a MappingInfo> {
pub fn find_mapping_no_bias(&self, address: usize) -> Option<&MappingInfo> {
for map in &self.mappings {
if address >= map.system_mapping_info.start_address
&& address < map.system_mapping_info.end_address
@ -475,9 +488,7 @@ impl LinuxPtraceDumper {
let elf_obj = elf::Elf::parse(mem_slice)?;
match Self::parse_build_id(&elf_obj, mem_slice) {
// Look for a build id note first.
Some(build_id) => {
return Ok(build_id.to_vec());
}
Some(build_id) => Ok(build_id.to_vec()),
// Fall back on hashing the first page of the text section.
None => {
// Attempt to locate the .text section of an ELF binary and generate
@ -516,7 +527,7 @@ impl LinuxPtraceDumper {
pub fn elf_identifier_for_mapping_index(&mut self, idx: usize) -> Result<Vec<u8>, DumperError> {
assert!(idx < self.mappings.len());
return Self::elf_identifier_for_mapping(&mut self.mappings[idx], self.pid);
Self::elf_identifier_for_mapping(&mut self.mappings[idx], self.pid)
}
pub fn elf_identifier_for_mapping(
@ -566,6 +577,6 @@ impl LinuxPtraceDumper {
);
}
}
return Ok(build_id);
Ok(build_id)
}
}

Просмотреть файл

@ -9,9 +9,9 @@ use std::fs::File;
use std::mem::size_of;
use std::path::PathBuf;
pub const LINUX_GATE_LIBRARY_NAME: &'static str = "linux-gate.so";
pub const DELETED_SUFFIX: &'static str = " (deleted)";
pub const RESERVED_FLAGS: &'static str = "---p";
pub const LINUX_GATE_LIBRARY_NAME: &str = "linux-gate.so";
pub const DELETED_SUFFIX: &str = " (deleted)";
pub const RESERVED_FLAGS: &str = "---p";
type Result<T> = std::result::Result<T, MapsReaderError>;
@ -60,7 +60,7 @@ pub enum MappingInfoParsingResult {
fn is_mapping_a_path(pathname: Option<&str>) -> bool {
match pathname {
Some(x) => x.contains("/"),
Some(x) => x.contains('/'),
None => false,
}
}
@ -123,7 +123,7 @@ impl MappingInfo {
let start_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
let end_address = usize::from_str_radix(addresses.next().unwrap(), 16)?;
let executable = perms.contains("x");
let executable = perms.contains('x');
// Only copy name if the name is a valid path name, or if
// it's the VDSO image.
@ -193,7 +193,7 @@ impl MappingInfo {
// Not doing this as root_prefix is always "" at the moment
// if (!dumper.GetMappingAbsolutePath(mapping, filename))
let filename = name.clone().unwrap_or(String::new());
let filename = name.clone().unwrap_or_default();
let mapped_file = unsafe {
MmapOptions::new()
.offset(offset.try_into()?) // try_into() to work for both 32 and 64 bit
@ -236,7 +236,7 @@ impl MappingInfo {
// return Err("".into());
// }
// }
return Ok(exe_link);
Ok(exe_link)
}
pub fn stack_has_pointer_to_mapping(&self, stack_copy: &[u8], sp_offset: usize) -> bool {
@ -293,14 +293,14 @@ impl MappingInfo {
let elf_obj = elf::Elf::parse(&mapped_file)?;
let soname = elf_obj.soname.ok_or(MapsReaderError::NoSoName(
self.name.clone().unwrap_or("None".to_string()),
))?;
let soname = elf_obj.soname.ok_or_else(|| {
MapsReaderError::NoSoName(self.name.clone().unwrap_or_else(|| "None".to_string()))
})?;
Ok(soname.to_string())
}
pub fn get_mapping_effective_name_and_path(&self) -> Result<(String, String)> {
let mut file_path = self.name.clone().unwrap_or(String::new());
let mut file_path = self.name.clone().unwrap_or_default();
let file_name;
// Tools such as minidump_stackwalk use the name of the module to look up
@ -314,7 +314,7 @@ impl MappingInfo {
Err(_) => {
// file_path := /path/to/libname.so
// file_name := libname.so
let split: Vec<_> = file_path.rsplitn(2, "/").collect();
let split: Vec<_> = file_path.rsplitn(2, '/').collect();
file_name = split.first().unwrap().to_string();
return Ok((file_path, file_name));
}
@ -609,7 +609,7 @@ mod tests {
match MappingInfo::parse_from_line(&line, linux_gate_loc, mappings.last_mut()) {
Ok(MappingInfoParsingResult::Success(map)) => mappings.push(map),
Ok(MappingInfoParsingResult::SkipLine) => continue,
Err(x) => panic!(format!("{:?}", x)),
Err(x) => panic!("{:?}", x),
}
}
assert_eq!(mappings.len(), 1);

Просмотреть файл

@ -67,6 +67,16 @@ pub const MD_HEADER_SIGNATURE: u32 = 0x504d444d; /* 'PMDM' */
pub const MD_HEADER_VERSION: u32 = 0x0000a793; /* 42899 */
/* MINIDUMP_VERSION */
/// The name of a thread, found in the ThreadNamesStream.
#[repr(C, packed)]
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct MDRawThreadName {
/// The id of the thread.
pub thread_id: u32,
/// Where the name of the thread is stored (yes, the legendary RVA64 is real!!).
pub thread_name_rva: u64,
}
#[repr(C)]
#[derive(Debug, Default, PartialEq)]
pub struct MDRawThread {
@ -403,6 +413,8 @@ pub enum MDStreamType {
JavascriptDataStream = 20,
SystemMemoryInfoStream = 21,
ProcessVmCountersStream = 22,
IptTraceStream = 23,
ThreadNamesStream = 24,
LastReservedStream = 0x0000ffff,
/* Breakpad extension types. 0x4767 = "Gg" */

Просмотреть файл

@ -270,7 +270,7 @@ impl MinidumpWriter {
) -> Result<()> {
// A minidump file contains a number of tagged streams. This is the number
// of stream which we write.
let num_writers = 13u32;
let num_writers = 14u32;
let mut header_section = MemoryWriter::<MDRawHeader>::alloc(buffer)?;
@ -400,6 +400,10 @@ impl MinidumpWriter {
// Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
let dirent = thread_names_stream::write(buffer, dumper)?;
// Write section to file
dir_section.write_to_file(buffer, Some(dirent))?;
// If you add more directory entries, don't forget to update kNumWriters,
// above.
Ok(())

Просмотреть файл

@ -32,7 +32,7 @@ pub fn write(
// Note: elf_identifier_for_mapping_index() can manipulate the |mapping.name|.
let identifier = dumper
.elf_identifier_for_mapping_index(map_idx)
.unwrap_or(Default::default());
.unwrap_or_default();
// If the identifier is all 0, its an uninteresting mapping (bmc#1676109)
if identifier.is_empty() || identifier.iter().all(|&x| x == 0) {

Просмотреть файл

@ -4,6 +4,7 @@ pub mod mappings;
pub mod memory_list_stream;
pub mod systeminfo_stream;
pub mod thread_list_stream;
pub mod thread_names_stream;
use crate::errors::MemoryWriterError;
use crate::minidump_format::*;

Просмотреть файл

@ -66,8 +66,10 @@ pub fn write(
}
for (idx, item) in dumper.threads.clone().iter().enumerate() {
let mut thread = MDRawThread::default();
thread.thread_id = (*item).try_into()?;
let mut thread = MDRawThread {
thread_id: item.tid.try_into()?,
..Default::default()
};
// We have a different source of information for the crashing thread. If
// we used the actual state of the thread we would find it running in the
@ -97,14 +99,16 @@ pub fn write(
{
continue;
}
let mut ip_memory_d: MDMemoryDescriptor = Default::default();
// Try to get 128 bytes before and after the IP, but
// settle for whatever's available.
ip_memory_d.start_of_memory_range =
std::cmp::max(mapping.start_address, instruction_ptr - ip_memory_size / 2)
as u64;
let mut ip_memory_d = MDMemoryDescriptor {
start_of_memory_range: std::cmp::max(
mapping.start_address,
instruction_ptr - ip_memory_size / 2,
) as u64,
..Default::default()
};
let end_of_range = std::cmp::min(
mapping.start_address + mapping.size,
instruction_ptr + ip_memory_size / 2,
@ -156,7 +160,7 @@ pub fn write(
info.fill_cpu_context(&mut cpu);
let cpu_section = MemoryWriter::<RawContextCPU>::alloc_with_val(buffer, cpu)?;
thread.thread_context = cpu_section.location();
if item == &config.blamed_thread {
if item.tid == config.blamed_thread {
// This is the crashing thread of a live process, but
// no context was provided, so set the crash address
// while the instruction pointer is already here.

Просмотреть файл

@ -0,0 +1,38 @@
use crate::errors::SectionThreadNamesError;
use crate::linux_ptrace_dumper::LinuxPtraceDumper;
use crate::minidump_format::*;
use crate::minidump_writer::DumpBuf;
use crate::sections::write_string_to_location;
use crate::sections::{MemoryArrayWriter, MemoryWriter};
use std::convert::TryInto;
type Result<T> = std::result::Result<T, SectionThreadNamesError>;
pub fn write(buffer: &mut DumpBuf, dumper: &LinuxPtraceDumper) -> Result<MDRawDirectory> {
// Only count threads that have a name
let num_threads = dumper.threads.iter().filter(|t| t.name.is_some()).count();
// Memory looks like this:
// <num_threads><thread_1><thread_2>...
let list_header = MemoryWriter::<u32>::alloc_with_val(buffer, num_threads as u32)?;
let mut dirent = MDRawDirectory {
stream_type: MDStreamType::ThreadNamesStream as u32,
location: list_header.location(),
};
let mut thread_list = MemoryArrayWriter::<MDRawThreadName>::alloc_array(buffer, num_threads)?;
dirent.location.data_size += thread_list.location().data_size;
for (idx, item) in dumper.threads.iter().enumerate() {
if let Some(name) = &item.name {
let pos = write_string_to_location(buffer, &name)?;
let thread = MDRawThreadName {
thread_id: item.tid.try_into()?,
thread_name_rva: pos.rva.into(),
};
thread_list.set_value_at(buffer, thread, idx)?;
}
}
Ok(dirent)
}

Просмотреть файл

@ -72,18 +72,18 @@ trait CommonThreadInfo {
let l = line?;
let start = l
.get(0..6)
.ok_or(ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?;
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?;
match start {
"Tgid:\t" => {
tgid = l
.get(6..)
.ok_or(ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?
}
"PPid:\t" => {
ppid = l
.get(6..)
.ok_or(ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.ok_or_else(|| ThreadInfoError::InvalidProcStatusFile(tid, l.clone()))?
.parse::<Pid>()?
}
_ => continue,

Просмотреть файл

@ -30,8 +30,7 @@ pub fn spawn_child(command: &str, args: &[&str]) {
assert_eq!(child.status.code().expect("No return value"), 0);
}
#[allow(unused)]
pub fn start_child_and_wait_for_threads(num: usize) -> Child {
fn start_child_and_wait_for_threads_helper(cmd: &str, num: usize) -> Child {
let mut child = Command::new("cargo")
.env("RUST_BACKTRACE", "1")
.arg("run")
@ -39,7 +38,7 @@ pub fn start_child_and_wait_for_threads(num: usize) -> Child {
.arg("--bin")
.arg("test")
.arg("--")
.arg("spawn_and_wait")
.arg(cmd)
.arg(format!("{}", num))
.stdout(Stdio::piped())
.spawn()
@ -49,6 +48,16 @@ pub fn start_child_and_wait_for_threads(num: usize) -> Child {
child
}
#[allow(unused)]
pub fn start_child_and_wait_for_threads(num: usize) -> Child {
start_child_and_wait_for_threads_helper("spawn_and_wait", num)
}
#[allow(unused)]
pub fn start_child_and_wait_for_named_threads(num: usize) -> Child {
start_child_and_wait_for_threads_helper("spawn_name_wait", num)
}
#[allow(unused)]
pub fn wait_for_threads(child: &mut Child, num: usize) {
let mut f = BufReader::new(child.stdout.as_mut().expect("Can't open stdout"));
@ -62,7 +71,7 @@ pub fn wait_for_threads(child: &mut Child, num: usize) {
}
}
Err(e) => {
panic!(e);
std::panic::panic_any(e);
}
}
}

Просмотреть файл

@ -11,6 +11,7 @@ use minidump_writer_linux::minidump_writer::MinidumpWriter;
use minidump_writer_linux::thread_info::Pid;
use nix::errno::Errno;
use nix::sys::signal::Signal;
use std::collections::HashSet;
use std::convert::TryInto;
use std::io::{BufRead, BufReader};
use std::os::unix::process::ExitStatusExt;
@ -674,3 +675,60 @@ fn test_write_early_abort() {
fn test_write_early_abort_with_context() {
test_write_early_abort_helper(Context::With)
}
fn test_named_threads_helper(context: Context) {
let num_of_threads = 5;
let mut child = start_child_and_wait_for_named_threads(num_of_threads);
let pid = child.id() as i32;
let mut tmpfile = tempfile::Builder::new()
.prefix("named_threads")
.tempfile()
.unwrap();
let mut tmp = MinidumpWriter::new(pid, pid);
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
if context == Context::With {
let crash_context = get_crash_context(pid);
tmp.set_crash_context(crash_context);
}
let _ = tmp.dump(&mut tmpfile).expect("Could not write minidump");
child.kill().expect("Failed to kill process");
// Reap child
let waitres = child.wait().expect("Failed to wait for child");
let status = waitres.signal().expect("Child did not die due to signal");
assert_eq!(waitres.code(), None);
assert_eq!(status, Signal::SIGKILL as i32);
// Read dump file and check its contents. There should be a truncated minidump available
let dump = Minidump::read_path(tmpfile.path()).expect("Failed to read minidump");
let threads: MinidumpThreadList = dump.get_stream().expect("Couldn't find MinidumpThreadList");
let thread_names: MinidumpThreadNames = dump
.get_stream()
.expect("Couldn't find MinidumpThreadNames");
let thread_ids: Vec<_> = threads.threads.iter().map(|t| t.raw.thread_id).collect();
let names: HashSet<_> = thread_ids
.iter()
.map(|id| thread_names.get_name(*id).unwrap_or_default())
.map(|cow| cow.into_owned())
.collect();
let mut expected = HashSet::new();
expected.insert("test".to_string());
for id in 1..num_of_threads {
expected.insert(format!("thread_{}", id));
}
assert_eq!(expected, names);
}
#[test]
fn test_named_threads() {
test_named_threads_helper(Context::Without)
}
#[cfg(not(any(target_arch = "mips", target_arch = "arm")))]
#[test]
fn test_named_threads_with_context() {
test_named_threads_helper(Context::With)
}

Просмотреть файл

@ -33,7 +33,7 @@ fn test_thread_list_from_parent() {
// let mut matching_threads = 0;
for (idx, curr_thread) in dumper.threads.iter().enumerate() {
println!("curr_thread: {}", curr_thread);
println!("curr_thread: {:?}", curr_thread);
let info = dumper
.get_thread_info_by_index(idx)
.expect("Could not get thread info by index");