diff --git a/Cargo.lock b/Cargo.lock index 8869cfdc7609..6958f1f7877d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3882,6 +3882,14 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "minidump-analyzer-export" +version = "0.1.0" +dependencies = [ + "minidump-analyzer", + "mozilla-central-workspace-hack", +] + [[package]] name = "minidump-common" version = "0.22.1" @@ -4068,6 +4076,7 @@ dependencies = [ "futures", "futures-channel", "futures-core", + "futures-executor", "futures-sink", "futures-util", "getrandom", diff --git a/Cargo.toml b/Cargo.toml index 49f911b302f6..b2c323ba9fdc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "testing/geckodriver", "toolkit/components/uniffi-bindgen-gecko-js", "toolkit/crashreporter/client/app", + "toolkit/crashreporter/minidump-analyzer/android/export", "toolkit/crashreporter/mozwer-rust", "toolkit/crashreporter/rust_minidump_writer_linux", "toolkit/library/gtest/rust", diff --git a/build/workspace-hack/Cargo.toml b/build/workspace-hack/Cargo.toml index db1d0730e75f..727220a05948 100644 --- a/build/workspace-hack/Cargo.toml +++ b/build/workspace-hack/Cargo.toml @@ -242,6 +242,7 @@ http3server = ["dep:arrayvec", "dep:bindgen", "dep:bitflags", "dep:bytes", "dep: ipcclientcerts-static = ["dep:bindgen", "dep:bitflags", "dep:itertools", "dep:memchr", "dep:nom", "dep:regex"] jsrust = ["dep:arrayvec", "dep:cc", "dep:env_logger", "dep:getrandom", "dep:hashbrown", "dep:icu_locid", "dep:icu_properties", "dep:indexmap", "dep:log", "dep:memchr", "dep:num-traits", "dep:once_cell", "dep:semver", "dep:smallvec", "dep:stable_deref_trait", "dep:tinystr", "dep:unicode-bidi", "dep:url", "dep:yoke", "dep:zerofrom", "dep:zerovec"] minidump-analyzer = ["dep:clap", "dep:env_logger", "dep:futures-executor", "dep:futures-util", "dep:log", "dep:serde_json", "dep:windows-sys"] +minidump-analyzer-export = ["minidump-analyzer"] mozwer_s = ["dep:getrandom", "dep:hashbrown", "dep:indexmap", "dep:log", "dep:once_cell", "dep:scroll", "dep:serde_json", "dep:uuid", "dep:windows-sys"] nmhproxy = ["dep:bitflags", "dep:hashbrown", "dep:icu_locid", "dep:icu_properties", "dep:indexmap", "dep:once_cell", "dep:serde_json", "dep:smallvec", "dep:stable_deref_trait", "dep:tinystr", "dep:unicode-bidi", "dep:url", "dep:windows-sys", "dep:yoke", "dep:zerofrom", "dep:zerovec"] osclientcerts-static = ["dep:bindgen", "dep:bitflags", "dep:core-foundation-sys", "dep:env_logger", "dep:itertools", "dep:log", "dep:memchr", "dep:nom", "dep:regex"] diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in index d330007d5612..e4914eb9f4a0 100644 --- a/mobile/android/installer/package-manifest.in +++ b/mobile/android/installer/package-manifest.in @@ -79,6 +79,10 @@ @BINPATH@/@DLL_PREFIX@gvr@DLL_SUFFIX@ #endif +#ifdef MOZ_CRASHREPORTER +@BINPATH@/@DLL_PREFIX@minidump_analyzer@DLL_SUFFIX@ +#endif + [xpcom] @BINPATH@/package-name.txt diff --git a/toolkit/crashreporter/minidump-analyzer/android/export/Cargo.toml b/toolkit/crashreporter/minidump-analyzer/android/export/Cargo.toml new file mode 100644 index 000000000000..6221bebb5a7a --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/android/export/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "minidump-analyzer-export" +version = "0.1.0" +edition = "2021" + +[lib] +name = "minidump_analyzer_export" +crate-type = ["staticlib"] +path = "lib.rs" + +[dependencies] +minidump-analyzer = { path = "../../" } +mozilla-central-workspace-hack = { version = "0.1", features = ["minidump-analyzer-export"], optional = true } diff --git a/toolkit/crashreporter/minidump-analyzer/android/export/lib.rs b/toolkit/crashreporter/minidump-analyzer/android/export/lib.rs new file mode 100644 index 000000000000..47c6b3b163ed --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/android/export/lib.rs @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +use minidump_analyzer::MinidumpAnalyzer; + +#[derive(Debug)] +#[repr(C)] +pub struct Utf16String { + chars: *mut u16, + len: usize, +} + +impl Utf16String { + pub fn empty() -> Self { + Utf16String { + chars: std::ptr::null_mut(), + len: 0, + } + } + + pub fn as_string_lossy(&self) -> String { + if self.chars.is_null() { + String::default() + } else { + String::from_utf16_lossy(unsafe { std::slice::from_raw_parts(self.chars, self.len) }) + } + } +} + +impl Drop for Utf16String { + fn drop(&mut self) { + if !self.chars.is_null() { + // # Safety + // We only own Utf16Strings which are created in Rust code, so when dropping, the + // memory is that which we allocated (and thus is safe to deallocate). + unsafe { + std::alloc::dealloc( + self.chars as *mut u8, + // unwrap() because the memory must have been a size that doesn't overflow when + // it was originally allocated. + std::alloc::Layout::array::(self.len).unwrap(), + ) + }; + } + } +} + +impl Default for Utf16String { + fn default() -> Self { + Self::empty() + } +} + +impl From for Utf16String { + fn from(value: String) -> Self { + let utf16: Box<[u16]> = value.encode_utf16().collect(); + let utf16 = Box::leak(utf16); + Utf16String { + chars: utf16.as_mut_ptr(), + len: utf16.len(), + } + } +} + +#[no_mangle] +pub extern "C" fn minidump_analyzer_analyze( + minidump_path: &Utf16String, + extras_path: &Utf16String, + all_threads: bool, +) -> Utf16String { + let minidump_path = minidump_path.as_string_lossy(); + let extras_path = extras_path.as_string_lossy(); + MinidumpAnalyzer::new(minidump_path.as_ref()) + .extras_file(extras_path.as_ref()) + .all_threads(all_threads) + .analyze() + .err() + .map(|e| e.to_string().into()) + .unwrap_or_default() +} + +#[no_mangle] +pub extern "C" fn minidump_analyzer_free_result(result: Utf16String) { + // This will happen anyway, but we're explicit about it for clarity. + drop(result); +} diff --git a/toolkit/crashreporter/minidump-analyzer/android/export/moz.build b/toolkit/crashreporter/minidump-analyzer/android/export/moz.build new file mode 100644 index 000000000000..268e15cfdfe8 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/android/export/moz.build @@ -0,0 +1 @@ +RustLibrary("minidump-analyzer-export") diff --git a/toolkit/crashreporter/minidump-analyzer/android/lib.cpp b/toolkit/crashreporter/minidump-analyzer/android/lib.cpp new file mode 100644 index 000000000000..9b5fb7a01197 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/android/lib.cpp @@ -0,0 +1,65 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include +#include +#include + +namespace { + +struct Utf16String { + const uint16_t* chars; + size_t len; +}; + +extern "C" { +Utf16String minidump_analyzer_analyze(const Utf16String& minidump_path, + const Utf16String& extras_path, + bool all_threads); +void minidump_analyzer_free_result(Utf16String result); +} + +struct LocalString : Utf16String { + LocalString(JNIEnv* env, jstring str) : env(env), str(str) { + chars = env->GetStringChars(str, nullptr); + len = env->GetStringLength(str); + } + + ~LocalString() { env->ReleaseStringChars(str, chars); } + + LocalString(const LocalString&) = delete; + LocalString& operator=(const LocalString&) = delete; + + JNIEnv* env; + jstring str; +}; + +struct ForeignString : Utf16String { + ForeignString(Utf16String s) : Utf16String(s) {} + ~ForeignString() { minidump_analyzer_free_result(*this); } + + ForeignString(const ForeignString&) = delete; + ForeignString& operator=(const ForeignString&) = delete; + + explicit operator bool() const { return chars != nullptr; } + + jstring to_jstring(JNIEnv* env) const { return env->NewString(chars, len); } +}; + +} // namespace + +extern "C" { +JNIEXPORT jstring JNICALL +Java_mozilla_components_lib_crash_MinidumpAnalyzer_analyze( + JNIEnv* env, jobject obj, jstring minidump_path, jstring extras_path, + jboolean all_threads) { + LocalString minidump_str(env, minidump_path); + LocalString extras_str(env, extras_path); + if (auto error = ForeignString( + minidump_analyzer_analyze(minidump_str, extras_str, all_threads))) { + return error.to_jstring(env); + } + return nullptr; +} +} diff --git a/toolkit/crashreporter/minidump-analyzer/android/moz.build b/toolkit/crashreporter/minidump-analyzer/android/moz.build new file mode 100644 index 000000000000..c51d0f5348a4 --- /dev/null +++ b/toolkit/crashreporter/minidump-analyzer/android/moz.build @@ -0,0 +1,7 @@ +DIRS += ["export"] +USE_LIBS += ["minidump-analyzer-export"] +# libm log/logf are required by the rust static library +OS_LIBS += ["m"] + +SOURCES += ["lib.cpp"] +SharedLibrary("minidump_analyzer") diff --git a/toolkit/crashreporter/moz.build b/toolkit/crashreporter/moz.build index 4b2f7f8f9d8e..340c90ff804c 100644 --- a/toolkit/crashreporter/moz.build +++ b/toolkit/crashreporter/moz.build @@ -66,6 +66,9 @@ if CONFIG["MOZ_CRASHREPORTER"]: if CONFIG["OS_TARGET"] != "Android": DIRS += ["client/app"] + if CONFIG["OS_TARGET"] == "Android": + DIRS += ["minidump-analyzer/android"] + DIRS += [ "mozannotation_client", "mozannotation_server",