diff --git a/Cargo.lock b/Cargo.lock index 2f8847eda..34c85b962 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -988,7 +988,7 @@ dependencies = [ "minidump-common", "nom", "range-map", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -2241,7 +2241,7 @@ dependencies = [ "once_cell", "rand 0.9.2", "ring", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -2264,7 +2264,7 @@ dependencies = [ "rand 0.9.2", "resolv-conf", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -2856,7 +2856,7 @@ dependencies = [ "swc_common", "swc_ecma_parser", "swc_ecma_visit", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -3095,7 +3095,7 @@ dependencies = [ "prost", "range-map", "scroll 0.12.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", "tracing", "uuid", @@ -3132,7 +3132,7 @@ dependencies = [ "scroll 0.12.0", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "yaxpeax-x86", ] @@ -3529,7 +3529,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323" dependencies = [ "memchr", - "thiserror 2.0.16", + "thiserror 2.0.17", "ucd-trie", ] @@ -3768,14 +3768,14 @@ dependencies = [ [[package]] name = "proguard" -version = "5.8.0" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260b8aaeeb4bc5175bb90a599f51d74a560f19de428c2d058c67eac92b5085d3" +checksum = "485ce6a0eaff8ca5566dde882ee2ef65dc25ba9f3ef1dba1129a8dd78a181952" dependencies = [ "serde", "serde_json", - "thiserror 1.0.69", - "watto", + "thiserror 2.0.17", + "watto 0.2.0", ] [[package]] @@ -3955,7 +3955,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -4472,7 +4472,7 @@ dependencies = [ "rand 0.9.2", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", "url", "uuid", @@ -4671,7 +4671,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -4788,7 +4788,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85cd3e3828fb4dd5ba0e7091777edb6c3db3cd2d6fc10547b29b40f6949a29be" dependencies = [ "memchr", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -5103,7 +5103,7 @@ dependencies = [ "symbolic-common", "thiserror 1.0.69", "uuid", - "watto", + "watto 0.1.0", ] [[package]] @@ -5118,7 +5118,7 @@ dependencies = [ "symbolic-common", "thiserror 1.0.69", "tracing", - "watto", + "watto 0.1.0", ] [[package]] @@ -5133,7 +5133,7 @@ dependencies = [ "symbolic-il2cpp", "thiserror 1.0.69", "tracing", - "watto", + "watto 0.1.0", ] [[package]] @@ -5164,7 +5164,7 @@ dependencies = [ "symbolicator-sources", "symbolicator-test", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-metrics", "tokio-util", @@ -5235,7 +5235,7 @@ dependencies = [ "symbolicator-test", "tempfile", "test-assembler", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", "tracing", @@ -5301,7 +5301,7 @@ dependencies = [ "symbolicator-sources", "symbolicator-test", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "thread_local", "tokio", "tokio-util", @@ -5521,11 +5521,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -5541,9 +5541,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -6274,6 +6274,17 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "watto" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfbc1480663d640f8c9f7a1ac70922eea60ac16fea79df883177df3bc7bb8b49" +dependencies = [ + "hashbrown 0.15.5", + "leb128", + "thiserror 2.0.17", +] + [[package]] name = "web-sys" version = "0.3.78" @@ -6874,7 +6885,7 @@ dependencies = [ "flate2", "indexmap", "memchr", - "thiserror 2.0.16", + "thiserror 2.0.17", "zopfli", ] diff --git a/Cargo.toml b/Cargo.toml index e61839a78..4226095d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -95,7 +95,7 @@ minidump-processor = "0.26.1" minidump-unwind = "0.26.1" moka = { version = "0.12.8", features = ["future", "sync"] } prettytable-rs = "0.10.0" -proguard = "5.8.0" +proguard = "5.9.0" rand = "0.9.0" rayon = "1.10.0" regex = "1.5.5" diff --git a/crates/symbolicator-proguard/src/interface.rs b/crates/symbolicator-proguard/src/interface.rs index f9f1b0309..28945dfa9 100644 --- a/crates/symbolicator-proguard/src/interface.rs +++ b/crates/symbolicator-proguard/src/interface.rs @@ -128,6 +128,8 @@ pub struct JvmException { /// A JVM stacktrace. #[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq)] pub struct JvmStacktrace { + /// The exception that caused the stacktrace. + pub exception: Option, /// The stacktrace's frames. pub frames: Vec, } diff --git a/crates/symbolicator-proguard/src/symbolication.rs b/crates/symbolicator-proguard/src/symbolication.rs index 3c45b2074..e3dd77efb 100644 --- a/crates/symbolicator-proguard/src/symbolication.rs +++ b/crates/symbolicator-proguard/src/symbolication.rs @@ -14,6 +14,18 @@ use symbolicator_service::caching::CacheError; use symbolicator_service::source_context::get_context_lines; use symbolicator_service::types::FrameOrder; +/// Result of attempting a full frame remap with rewrite rules. +enum FullRemapResult { + /// Outline frame - skip this frame but preserve rewrite eligibility for next frame. + OutlineFrame, + /// Rewrite rules cleared all frames - skip this frame and disable rewrite for subsequent frames. + RewriteCleared, + /// Successfully remapped to these frames. + Frames(Vec), + /// No mappings found - try fallback methods. + NoFrames, +} + impl ProguardService { /// Symbolicates a JVM event. /// @@ -145,10 +157,16 @@ impl ProguardService { let mut remapped_stacktraces: Vec<_> = stacktraces .into_iter() .map(|raw_stacktrace| { + let exception = raw_stacktrace + .exception + .as_ref() + .map(|exc| Self::map_exception(&mappers, exc).unwrap_or_else(|| exc.clone())); + let mut frames = Self::map_stacktrace( &mappers, &raw_stacktrace.frames, release_package.as_deref(), + exception.as_ref(), &mut stats, ); @@ -157,7 +175,7 @@ impl ProguardService { frames.reverse(); } - JvmStacktrace { frames } + JvmStacktrace { exception, frames } }) .collect(); @@ -202,11 +220,22 @@ impl ProguardService { mappers: &[&proguard::ProguardCache], stacktrace: &[JvmFrame], release_package: Option<&str>, + exception: Option<&JvmException>, stats: &mut SymbolicationStats, ) -> Vec { let mut carried_outline_pos = vec![None; mappers.len()]; let mut remapped_frames = Vec::new(); + // Compute exception descriptor for rewrite rules + let exception_descriptor = exception.map(|exc| { + let full_class = format!("{}.{}", exc.module, exc.ty); + proguard::class_name_to_descriptor(&full_class) + }); + + // Track whether the next frame can have rewrite rules applied + // (only the first non-outline frame after an exception) + let mut next_frame_can_rewrite = exception_descriptor.is_some(); + 'frames: for frame in stacktrace { let deobfuscated_signature = frame.signature.as_ref().and_then(|signature| { mappers @@ -253,22 +282,32 @@ impl ProguardService { let mut remap_buffer = Vec::new(); for (mapper_idx, mapper) in mappers.iter().enumerate() { - if mapper.is_outline_frame(proguard_frame.class(), proguard_frame.method()) { - carried_outline_pos[mapper_idx] = Some(proguard_frame.line()); - continue 'frames; - } - - let effective = mapper.prepare_frame_for_mapping( + // Try full remap with rewrite rules + match Self::map_full_frame( + mapper, &proguard_frame, + frame, + exception_descriptor.as_deref(), + next_frame_can_rewrite, &mut carried_outline_pos[mapper_idx], - ); - - // First, try to remap the whole frame. - if let Some(frames_out) = - Self::map_full_frame(mapper, frame, &effective, &mut remap_buffer) - { - mapped_result = Some(frames_out); - break; + &mut remap_buffer, + ) { + FullRemapResult::OutlineFrame => { + // Outline frame - skip but preserve rewrite eligibility for next frame + continue 'frames; + } + FullRemapResult::RewriteCleared => { + // Rewrite rules cleared all frames - skip and disable rewrite + next_frame_can_rewrite = false; + continue 'frames; + } + FullRemapResult::Frames(frames) => { + mapped_result = Some(frames); + break; + } + FullRemapResult::NoFrames => { + // Try fallbacks + } } // Second, try to remap the frame's method. @@ -284,6 +323,9 @@ impl ProguardService { } } + // After processing the first non-outline frame, disable rewrite rules for subsequent frames + next_frame_can_rewrite = false; + // Fix up the frames' in-app fields only if they were actually mapped if let Some(frames) = mapped_result.as_mut() { for mapped_frame in frames { @@ -365,22 +407,48 @@ impl ProguardService { /// constructed from it. /// /// The `buf` parameter is used as a buffer for the frames returned - /// by `remap_frame`. + /// by `remap_frame_with_context`. + /// + /// The `exception_descriptor` parameter is used to apply rewrite rules + /// to the frame. + /// + /// The `apply_rewrite` parameter is used to determine whether to apply rewrite rules. + /// + /// The `carried_outline_pos` parameter is used to track the position of the + /// next frame in the outline. /// /// This function returns a list of frames because one frame may be expanded into /// a series of inlined frames. The returned list is sorted so that inlinees come before their callers. - #[tracing::instrument(skip_all)] fn map_full_frame<'a>( mapper: &'a proguard::ProguardCache<'a>, - original_frame: &JvmFrame, proguard_frame: &proguard::StackFrame<'a>, + original_frame: &JvmFrame, + exception_descriptor: Option<&str>, + apply_rewrite: bool, + carried_outline_pos: &mut Option, buf: &mut Vec>, - ) -> Option> { + ) -> FullRemapResult { buf.clear(); - buf.extend(mapper.remap_frame(proguard_frame)); + + let Some(iter) = mapper.remap_frame_with_context( + proguard_frame, + exception_descriptor, + apply_rewrite, + carried_outline_pos, + ) else { + // Outline frame - preserve rewrite eligibility for next frame + return FullRemapResult::OutlineFrame; + }; + + let had_mappings = iter.had_mappings(); + buf.extend(iter); + if had_mappings && buf.is_empty() { + // Rewrite rules cleared all frames + return FullRemapResult::RewriteCleared; + } if buf.is_empty() { - return None; + return FullRemapResult::NoFrames; } let res = buf @@ -401,7 +469,8 @@ impl ProguardService { ..original_frame.clone() }) .collect(); - Some(res) + + FullRemapResult::Frames(res) } /// Tries to remap a frame's class and method. @@ -511,6 +580,15 @@ mod tests { proguard_source: &[u8], release_package: Option<&str>, frames: &mut [JvmFrame], + ) -> Vec { + remap_stacktrace_caller_first_with_exception(proguard_source, release_package, None, frames) + } + + fn remap_stacktrace_caller_first_with_exception( + proguard_source: &[u8], + release_package: Option<&str>, + exception: Option<&JvmException>, + frames: &mut [JvmFrame], ) -> Vec { frames.reverse(); let mapping = ProguardMapping::new(proguard_source); @@ -523,6 +601,7 @@ mod tests { &[&cache], frames, release_package, + exception, &mut SymbolicationStats::default(), ); @@ -790,13 +869,13 @@ org.slf4j.helpers.Util$ClassContext -> org.a.b.g$b: filename: App.java module: com.example.App abs_path: App.java - lineno: 0 + lineno: 42 index: 0 - function: barInternalInject filename: App.java module: com.example.App abs_path: App.java - lineno: 0 + lineno: 47 index: 0 "###); } diff --git a/crates/symbolicator-proguard/tests/integration/proguard.rs b/crates/symbolicator-proguard/tests/integration/proguard.rs index 266ab9f1b..b57f137f9 100644 --- a/crates/symbolicator-proguard/tests/integration/proguard.rs +++ b/crates/symbolicator-proguard/tests/integration/proguard.rs @@ -22,7 +22,10 @@ fn make_jvm_request( let exceptions = vec![serde_json::from_str(exception).unwrap()]; let frames: Vec = serde_json::from_str(frames).unwrap(); let modules: Vec = serde_json::from_str(modules).unwrap(); - let stacktraces = vec![JvmStacktrace { frames }]; + let stacktraces = vec![JvmStacktrace { + frames, + exception: serde_json::from_str(exception).unwrap(), + }]; SymbolicateJvmStacktraces { platform: None, @@ -478,3 +481,67 @@ async fn test_source_lookup_with_proguard() { assert_snapshot!(response); } + +#[tokio::test] +async fn test_rewrite_frames() { + symbolicator_test::setup(); + let (symbolication, _cache_dir) = setup_service(|_| ()); + let (_srv, source) = proguard_server("rewrite_frames", |_url, _query| { + json!([{ + "id":"proguard.txt", + "uuid":"550e8400-e29b-41d4-a716-446655440000", + "debugId":"550e8400-e29b-41d4-a716-446655440000", + "codeId":null, + "cpuName":"any", + "objectName":"proguard-mapping", + "symbolType":"proguard", + "headers": { + "Content-Type":"text/x-proguard+plain" + }, + "size":1000, + "sha1":"0000000000000000000000000000000000000000", + "dateCreated":"2024-02-14T10:49:38.770116Z", + "data":{ + "features":["mapping"] + } + }]) + }); + + let source = SourceConfig::Sentry(Arc::new(source)); + + // Test with NullPointerException thrown at a.start + // In CallerFirst order: [outermost_caller, ..., exception_location] + // So we have: draw (outermost) -> dispatch -> start (where exception thrown) + // + // After rewrite: + // - c.draw(20) -> UiBridge.render(200) + // - b.dispatch(5) -> StreamRouter.dispatch(12) + StreamRouter$Inline.internalDispatch(30) + // - a.start(10) -> Initializer.start(42) only (2 inlined frames removed by rewrite rule) + let frames_npe = r#"[{ + "function": "draw", + "module": "c", + "lineno": 20, + "index": 0 + }, { + "function": "dispatch", + "module": "b", + "lineno": 5, + "index": 1 + }, { + "function": "start", + "module": "a", + "lineno": 10, + "index": 2 + }]"#; + + let request_npe = make_jvm_request( + source.clone(), + r#"{"type": "NullPointerException", "module": "java.lang"}"#, + frames_npe, + r#"[{"uuid": "550e8400-e29b-41d4-a716-446655440000", "type": "proguard"}]"#, + None, + ); + + let response_npe = symbolication.symbolicate_jvm(request_npe).await; + assert_snapshot!(response_npe); +} diff --git a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__basic_source_lookup.snap b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__basic_source_lookup.snap index 8cfd0b992..db152e893 100644 --- a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__basic_source_lookup.snap +++ b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__basic_source_lookup.snap @@ -6,7 +6,10 @@ exceptions: - type: RuntimeException module: io.sentry.samples stacktraces: - - frames: + - exception: + type: RuntimeException + module: io.sentry.samples + frames: - function: otherMethod filename: OtherActivity.java module: OtherActivity diff --git a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__remap_exception.snap b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__remap_exception.snap index 111e153dc..8d7de9b70 100644 --- a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__remap_exception.snap +++ b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__remap_exception.snap @@ -6,6 +6,9 @@ exceptions: - type: Util$ClassContextSecurityManager module: org.slf4j.helpers stacktraces: - - frames: [] + - exception: + type: Util$ClassContextSecurityManager + module: org.slf4j.helpers + frames: [] classes: {} errors: [] diff --git a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__resolving_inline.snap b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__resolving_inline.snap index 6d76ef6a2..ebd84f46c 100644 --- a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__resolving_inline.snap +++ b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__resolving_inline.snap @@ -6,7 +6,10 @@ exceptions: - type: g$a module: org.a.b stacktraces: - - frames: + - exception: + type: g$a + module: org.a.b + frames: - function: onClick module: io.sentry.sample.-$$Lambda$r3Avcbztes2hicEObh02jjhQqd4 lineno: 2 diff --git a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__rewrite_frames.snap b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__rewrite_frames.snap new file mode 100644 index 000000000..e136676e7 --- /dev/null +++ b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__rewrite_frames.snap @@ -0,0 +1,31 @@ +--- +source: crates/symbolicator-proguard/tests/integration/proguard.rs +assertion_line: 546 +expression: response_npe +--- +exceptions: + - type: NullPointerException + module: java.lang +stacktraces: + - exception: + type: NullPointerException + module: java.lang + frames: + - function: render + module: com.example.flow.UiBridge + lineno: 200 + index: 0 + - function: dispatch + module: com.example.flow.StreamRouter + lineno: 12 + index: 1 + - function: internalDispatch + module: com.example.flow.StreamRouter$Inline + lineno: 30 + index: 1 + - function: start + module: com.example.flow.Initializer + lineno: 42 + index: 2 +classes: {} +errors: [] diff --git a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__source_lookup_with_proguard.snap b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__source_lookup_with_proguard.snap index b9a41e562..f1e908701 100644 --- a/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__source_lookup_with_proguard.snap +++ b/crates/symbolicator-proguard/tests/integration/snapshots/integration__proguard__source_lookup_with_proguard.snap @@ -6,7 +6,10 @@ exceptions: - type: RuntimeException module: java.lang stacktraces: - - frames: + - exception: + type: RuntimeException + module: java.lang + frames: - function: main filename: ZygoteInit.java module: com.android.internal.os.ZygoteInit diff --git a/crates/symbolicator-service/src/caches/versions.rs b/crates/symbolicator-service/src/caches/versions.rs index 48dca4374..df581158a 100644 --- a/crates/symbolicator-service/src/caches/versions.rs +++ b/crates/symbolicator-service/src/caches/versions.rs @@ -273,6 +273,9 @@ pub const BUNDLE_INDEX_CACHE_VERSIONS: CacheVersions = CacheVersions { /// Proguard Cache, with the following versions: /// +/// - `6`: Information about whether a method has rewrite rules is now part +/// of the cache format. +/// /// - `5`: Information about whether a method is an outline/outlineCallsite is now part /// of the cache format. /// @@ -285,16 +288,17 @@ pub const BUNDLE_INDEX_CACHE_VERSIONS: CacheVersions = CacheVersions { /// /// - `1`: Initial version. pub const PROGUARD_CACHE_VERSIONS: CacheVersions = CacheVersions { - current: CacheVersion::new(5, CachePathFormat::V2), + current: CacheVersion::new(6, CachePathFormat::V2), fallbacks: &[], previous: &[ CacheVersion::new(1, CachePathFormat::V1), CacheVersion::new(2, CachePathFormat::V1), CacheVersion::new(3, CachePathFormat::V2), CacheVersion::new(4, CachePathFormat::V2), + CacheVersion::new(5, CachePathFormat::V2), ], }; -static_assert!(proguard::PRGCACHE_VERSION == 3); +static_assert!(proguard::PRGCACHE_VERSION == 4); /// Symstore index cache, with the following versions: /// diff --git a/docs/api/response.md b/docs/api/response.md index 1212af5ce..30041fbc4 100644 --- a/docs/api/response.md +++ b/docs/api/response.md @@ -201,6 +201,10 @@ a list of processed stack traces, exceptions and classes as well as an optional ], "stacktraces": [ { + "exception": { + "type": "RuntimeException", + "module": "java.lang" + }, "frames": [ { "function": "onMenuItemClick", diff --git a/docs/api/symbolicate-jvm.md b/docs/api/symbolicate-jvm.md index 83401508a..c1134dddd 100644 --- a/docs/api/symbolicate-jvm.md +++ b/docs/api/symbolicate-jvm.md @@ -27,6 +27,10 @@ Content-Type: application/json ], "stacktraces": [ { + "exception": { + "type": "RuntimeException", + "module": "io.sentry.samples" + }, "frames": [ { "function": "otherMethod", @@ -75,6 +79,7 @@ Content-Type: application/json - `modules`: A list of source code files with a corresponding debug id that were loaded during JVM code execution. The list is handled by the Sentry source. - `stacktrace`: A list of stacktraces to symbolicate. + - `exception`: (_optional_) The stacktrace exception which will have its module and type fields remapped. Necessary for applying [rewrite rules](https://r8.googlesource.com/r8/+/refs/heads/main/doc/retrace.md#rewriteframe-introduced-at-version-2_0). - `frames`: A list of frames with corresponding `abs_path`, `lineno`, and other optional fields like `colno` or minified `function` name. This list is assumed to be ordered according to the `frame_order` option (see below). - `release_package`: Name of Sentry `release` for the processed request. diff --git a/tests/fixtures/proguard/rewrite_frames/proguard.txt b/tests/fixtures/proguard/rewrite_frames/proguard.txt new file mode 100644 index 000000000..43b45351c --- /dev/null +++ b/tests/fixtures/proguard/rewrite_frames/proguard.txt @@ -0,0 +1,12 @@ +com.example.flow.Initializer -> a: + 10:10:void com.example.flow.Inliner.firstStep(com.example.flow.Step):60:60 -> start + 10:10:void com.example.flow.Inliner.secondStep(com.example.flow.Step):61:61 -> start + 10:10:void start(com.example.flow.Step):42 -> start + # {"id":"com.android.tools.r8.rewriteFrame","conditions":["throws(Ljava/lang/NullPointerException;)"],"actions":["removeInnerFrames(2)"]} + 15:15:void resume():55 -> start +com.example.flow.StreamRouter -> b: + 5:5:void com.example.flow.StreamRouter$Inline.internalDispatch(java.lang.String):30:30 -> dispatch + 5:5:void dispatch(java.lang.String):12 -> dispatch + # {"id":"com.android.tools.r8.rewriteFrame","conditions":["throws(Ljava/lang/IllegalStateException;)"],"actions":["removeInnerFrames(1)"]} +com.example.flow.UiBridge -> c: + 20:20:void render():200 -> draw