Skip to content

Commit 5d22e0a

Browse files
committed
Fix code formatting, make API 21 level compatible, minor improvements
1 parent 1b983d5 commit 5d22e0a

File tree

4 files changed

+106
-79
lines changed

4 files changed

+106
-79
lines changed

sentry-android-core/src/main/java/io/sentry/android/core/AnrV2Integration.java

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
import java.util.ArrayList;
4343
import java.util.Collections;
4444
import java.util.List;
45-
import java.util.Map;
4645
import java.util.concurrent.TimeUnit;
4746
import org.jetbrains.annotations.ApiStatus;
4847
import org.jetbrains.annotations.NotNull;
@@ -270,9 +269,9 @@ private void reportAsSentryEvent(
270269
event.setMessage(sentryMessage);
271270
} else if (result.type == ParseResult.Type.DUMP) {
272271
event.setThreads(result.threads);
273-
if (!result.debugImages.isEmpty()) {
272+
if (result.debugImages != null) {
274273
final DebugMeta debugMeta = new DebugMeta();
275-
debugMeta.setImages(new ArrayList<>(result.debugImages.values()));
274+
debugMeta.setImages(result.debugImages);
276275
event.setDebugMeta(debugMeta);
277276
}
278277
}
@@ -319,15 +318,19 @@ private void reportAsSentryEvent(
319318
final Lines lines = Lines.readLines(reader);
320319

321320
final ThreadDumpParser threadDumpParser = new ThreadDumpParser(options, isBackground);
322-
final List<SentryThread> threads = threadDumpParser.parse(lines);
321+
threadDumpParser.parse(lines);
322+
323+
final @NotNull List<SentryThread> threads = threadDumpParser.getThreads();
324+
final @NotNull List<DebugImage> debugImages = threadDumpParser.getDebugImages();
325+
323326
if (threads.isEmpty()) {
324327
// if the list is empty this means the system failed to capture a proper thread dump of
325328
// the android threads, and only contains kernel-level threads and statuses, those ANRs
326329
// are not actionable and neither they are reported by Google Play Console, so we just
327330
// fall back to not reporting them
328331
return new ParseResult(ParseResult.Type.NO_DUMP);
329332
}
330-
return new ParseResult(ParseResult.Type.DUMP, dump, threads, threadDumpParser.getDebugImages());
333+
return new ParseResult(ParseResult.Type.DUMP, dump, threads, debugImages);
331334
} catch (Throwable e) {
332335
options.getLogger().log(SentryLevel.WARNING, "Failed to parse ANR thread dump", e);
333336
return new ParseResult(ParseResult.Type.ERROR, dump);
@@ -411,7 +414,7 @@ enum Type {
411414
final Type type;
412415
final byte[] dump;
413416
final @Nullable List<SentryThread> threads;
414-
final @Nullable Map<String, DebugImage> debugImages;
417+
final @Nullable List<DebugImage> debugImages;
415418

416419
ParseResult(final @NotNull Type type) {
417420
this.type = type;
@@ -428,7 +431,10 @@ enum Type {
428431
}
429432

430433
ParseResult(
431-
final @NotNull Type type, final byte[] dump, final @Nullable List<SentryThread> threads, final @Nullable Map<String, DebugImage> debugImages) {
434+
final @NotNull Type type,
435+
final byte[] dump,
436+
final @Nullable List<SentryThread> threads,
437+
final @Nullable List<DebugImage> debugImages) {
432438
this.type = type;
433439
this.dump = dump;
434440
this.threads = threads;

sentry-android-core/src/main/java/io/sentry/android/core/internal/threaddump/ThreadDumpParser.java

Lines changed: 73 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,12 @@
2727
import io.sentry.protocol.SentryStackTrace;
2828
import io.sentry.protocol.SentryThread;
2929
import java.math.BigInteger;
30+
import java.nio.BufferUnderflowException;
3031
import java.nio.ByteBuffer;
3132
import java.nio.ByteOrder;
32-
import java.nio.BufferUnderflowException;
3333
import java.util.ArrayList;
34-
import java.util.Collection;
3534
import java.util.Collections;
3635
import java.util.HashMap;
37-
import java.util.LinkedHashMap;
3836
import java.util.List;
3937
import java.util.Map;
4038
import java.util.regex.Matcher;
@@ -50,26 +48,38 @@ public class ThreadDumpParser {
5048
Pattern.compile("\"(.*)\" (.*) ?sysTid=(\\d+)");
5149

5250
// For reference, see native_stack_dump.cc and tombstone_proto_to_text.cpp in Android sources
51+
// Groups
52+
// 0:entire regex
53+
// 1:index
54+
// 2:pc
55+
// 3:mapinfo
56+
// 4:filename
57+
// 5:mapoffset
58+
// 6:function
59+
// 7:fnoffset
60+
// 7:buildid
5361
private static final Pattern NATIVE_RE =
5462
Pattern.compile(
5563
// " native: #12 pc 0xabcd1234"
56-
" *(?:native: )?#(?<index>\\d+) \\S+ (?<pc>[0-9a-fA-F]+)"
57-
// The map info includes a filename and an optional offset into the file
58-
+ ("\\s+(?<mapinfo>"
59-
// "/path/to/file.ext",
60-
+ "(?<filename>.*?)"
61-
// optional " (deleted)" suffix (deleted files) needed here to bias regex correctly
62-
+ "(?:\\s+\\(deleted\\))?"
63-
// " (offset 0xabcd1234)", if the mapping is not into the beginning of the file
64-
+ "(?:\\s+\\(offset (?<mapoffset>.*?)\\))?"
65-
+ ")")
66-
// Optional function
67-
+ ("(?:\\s+\\((?:"
68-
+ "\\?\\?\\?" // " (???) marks a missing function, so don't capture it in a group
69-
+ "|(?<function>.*?)(?:\\+(?<fnoffset>\\d+))?" // " (func+1234)", offset is optional
70-
+ ")\\))?")
71-
// Optional " (BuildId: abcd1234abcd1234abcd1234abcd1234abcd1234)"
72-
+ "(?:\\s+\\(BuildId: (?<buildid>.*?)\\))?");
64+
" *(?:native: )?#(\\d+) \\S+ ([0-9a-fA-F]+)"
65+
// The map info includes a filename and an optional offset into the file
66+
+ ("\\s+("
67+
// "/path/to/file.ext",
68+
+ "(.*?)"
69+
// optional " (deleted)" suffix (deleted files) needed here to bias regex
70+
// correctly
71+
+ "(?:\\s+\\(deleted\\))?"
72+
// " (offset 0xabcd1234)", if the mapping is not into the beginning of the file
73+
+ "(?:\\s+\\(offset (.*?)\\))?"
74+
+ ")")
75+
// Optional function
76+
+ ("(?:\\s+\\((?:"
77+
+ "\\?\\?\\?" // " (???) marks a missing function, so don't capture it in a group
78+
+ "|(.*?)(?:\\+(\\d+))?" // " (func+1234)", offset is
79+
// optional
80+
+ ")\\))?")
81+
// Optional " (BuildId: abcd1234abcd1234abcd1234abcd1234abcd1234)"
82+
+ "(?:\\s+\\(BuildId: (.*?)\\))?");
7383

7484
private static final Pattern JAVA_RE =
7585
Pattern.compile(" *at (?:(.+)\\.)?([^.]+)\\.([^.]+)\\((.*):([\\d-]+)\\)");
@@ -100,16 +110,24 @@ public class ThreadDumpParser {
100110

101111
private final @NotNull Map<String, DebugImage> debugImages;
102112

113+
private final @NotNull List<SentryThread> threads;
114+
103115
public ThreadDumpParser(final @NotNull SentryOptions options, final boolean isBackground) {
104116
this.options = options;
105117
this.isBackground = isBackground;
106118
this.stackTraceFactory = new SentryStackTraceFactory(options);
107-
this.debugImages = new HashMap<String, DebugImage>();
119+
this.debugImages = new HashMap<>();
120+
this.threads = new ArrayList<>();
121+
}
122+
123+
@NotNull
124+
public List<DebugImage> getDebugImages() {
125+
return new ArrayList<>(debugImages.values());
108126
}
109127

110128
@NotNull
111-
public Map<String, DebugImage> getDebugImages() {
112-
return debugImages;
129+
public List<SentryThread> getThreads() {
130+
return threads;
113131
}
114132

115133
@Nullable
@@ -118,21 +136,20 @@ private static String buildIdToDebugId(String buildId) {
118136
// Abuse BigInteger as a hex string parser. Extra byte needed to handle leading zeros.
119137
final ByteBuffer buf = ByteBuffer.wrap(new BigInteger("10" + buildId, 16).toByteArray());
120138
buf.get();
121-
return String.format("%08x-%04x-%04x-%04x-%04x%08x",
122-
buf.order(ByteOrder.LITTLE_ENDIAN).getInt(),
123-
buf.getShort(),
124-
buf.getShort(),
125-
buf.order(ByteOrder.BIG_ENDIAN).getShort(),
126-
buf.getShort(),
127-
buf.getInt());
139+
return String.format(
140+
"%08x-%04x-%04x-%04x-%04x%08x",
141+
buf.order(ByteOrder.LITTLE_ENDIAN).getInt(),
142+
buf.getShort(),
143+
buf.getShort(),
144+
buf.order(ByteOrder.BIG_ENDIAN).getShort(),
145+
buf.getShort(),
146+
buf.getInt());
128147
} catch (NumberFormatException | BufferUnderflowException e) {
129148
return null;
130149
}
131150
}
132151

133-
@NotNull
134-
public List<SentryThread> parse(final @NotNull Lines lines) {
135-
final List<SentryThread> sentryThreads = new ArrayList<>();
152+
public void parse(final @NotNull Lines lines) {
136153

137154
final Matcher beginManagedThreadRe = BEGIN_MANAGED_THREAD_RE.matcher("");
138155
final Matcher beginUnmanagedNativeThreadRe = BEGIN_UNMANAGED_NATIVE_THREAD_RE.matcher("");
@@ -141,7 +158,7 @@ public List<SentryThread> parse(final @NotNull Lines lines) {
141158
final Line line = lines.next();
142159
if (line == null) {
143160
options.getLogger().log(SentryLevel.WARNING, "Internal error while parsing thread dump.");
144-
return sentryThreads;
161+
return;
145162
}
146163
final String text = line.text;
147164
// we only handle managed threads, as unmanaged/not attached do not have the thread id and
@@ -151,11 +168,10 @@ public List<SentryThread> parse(final @NotNull Lines lines) {
151168

152169
final SentryThread thread = parseThread(lines);
153170
if (thread != null) {
154-
sentryThreads.add(thread);
171+
threads.add(thread);
155172
}
156173
}
157174
}
158-
return sentryThreads;
159175
}
160176

161177
private SentryThread parseThread(final @NotNull Lines lines) {
@@ -242,22 +258,34 @@ private SentryStackTrace parseStacktrace(
242258
break;
243259
}
244260
final String text = line.text;
245-
if (matches(nativeRe, text)) {
261+
if (matches(javaRe, text)) {
262+
final SentryStackFrame frame = new SentryStackFrame();
263+
final String packageName = javaRe.group(1);
264+
final String className = javaRe.group(2);
265+
final String module = String.format("%s.%s", packageName, className);
266+
frame.setModule(module);
267+
frame.setFunction(javaRe.group(3));
268+
frame.setFilename(javaRe.group(4));
269+
frame.setLineno(getUInteger(javaRe, 5, null));
270+
frame.setInApp(stackTraceFactory.isInApp(module));
271+
frames.add(frame);
272+
lastJavaFrame = frame;
273+
} else if (matches(nativeRe, text)) {
246274
final SentryStackFrame frame = new SentryStackFrame();
247-
frame.setPackage(nativeRe.group("mapinfo"));
248-
frame.setFunction(nativeRe.group("function"));
249-
frame.setLineno(getInteger(nativeRe, "fnoffset", null));
250-
frame.setInstructionAddr("0x" + nativeRe.group("pc"));
275+
frame.setPackage(nativeRe.group(3));
276+
frame.setFunction(nativeRe.group(6));
277+
frame.setLineno(getInteger(nativeRe, 7, null));
278+
frame.setInstructionAddr("0x" + nativeRe.group(2));
251279
frame.setPlatform("native");
252280

253-
final String buildId = nativeRe.group("buildid");
281+
final String buildId = nativeRe.group(8);
254282
final String debugId = buildId == null ? null : buildIdToDebugId(buildId);
255283
if (debugId != null) {
256284
if (!debugImages.containsKey(debugId)) {
257285
final DebugImage debugImage = new DebugImage();
258286
debugImage.setDebugId(debugId);
259287
debugImage.setType("elf");
260-
debugImage.setCodeFile(nativeRe.group("filename"));
288+
debugImage.setCodeFile(nativeRe.group(4));
261289
debugImage.setCodeId(buildId);
262290
debugImages.put(debugId, debugImage);
263291
}
@@ -267,18 +295,6 @@ private SentryStackTrace parseStacktrace(
267295

268296
frames.add(frame);
269297
lastJavaFrame = null;
270-
} else if (matches(javaRe, text)) {
271-
final SentryStackFrame frame = new SentryStackFrame();
272-
final String packageName = javaRe.group(1);
273-
final String className = javaRe.group(2);
274-
final String module = String.format("%s.%s", packageName, className);
275-
frame.setModule(module);
276-
frame.setFunction(javaRe.group(3));
277-
frame.setFilename(javaRe.group(4));
278-
frame.setLineno(getUInteger(javaRe, 5, null));
279-
frame.setInApp(stackTraceFactory.isInApp(module));
280-
frames.add(frame);
281-
lastJavaFrame = frame;
282298
} else if (matches(jniRe, text)) {
283299
final SentryStackFrame frame = new SentryStackFrame();
284300
final String packageName = jniRe.group(1);
@@ -395,8 +411,8 @@ private Long getLong(
395411

396412
@Nullable
397413
private Integer getInteger(
398-
final @NotNull Matcher matcher, final String group, final @Nullable Integer defaultValue) {
399-
final String str = matcher.group(group);
414+
final @NotNull Matcher matcher, final int groupIndex, final @Nullable Integer defaultValue) {
415+
final String str = matcher.group(groupIndex);
400416
if (str == null || str.length() == 0) {
401417
return defaultValue;
402418
} else {

sentry-android-core/src/test/java/io/sentry/android/core/internal/threaddump/ThreadDumpParserTest.kt

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ class ThreadDumpParserTest {
1818
SentryOptions().apply { addInAppInclude("io.sentry.samples") },
1919
false
2020
)
21-
val threads = parser.parse(lines)
21+
parser.parse(lines)
22+
val threads = parser.threads
2223
// just verifying a few important threads, as there are many
2324
val main = threads.find { it.name == "main" }
2425
assertEquals(1, main!!.id)
@@ -85,8 +86,10 @@ class ThreadDumpParserTest {
8586

8687
val nativeFrame = randomThread.stacktrace!!.frames!!.get(5)
8788
assertEquals("/system/lib64/libandroid_runtime.so", nativeFrame.`package`)
88-
assertEquals("android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)",
89-
nativeFrame.function)
89+
assertEquals(
90+
"android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)",
91+
nativeFrame.function
92+
)
9093
assertEquals(44, nativeFrame.lineno)
9194
assertNull(nativeFrame.isNative) // Confusing, but "isNative" means JVM frame for a JNI method
9295
assertEquals("native", nativeFrame.platform)
@@ -99,7 +102,8 @@ class ThreadDumpParserTest {
99102
SentryOptions().apply { addInAppInclude("io.sentry.samples") },
100103
false
101104
)
102-
val threads = parser.parse(lines)
105+
parser.parse(lines)
106+
val threads = parser.threads
103107
// just verifying a few important threads, as there are many
104108
val thread = threads.find { it.name == "samples.android" }
105109
assertEquals(9955, thread!!.id)
@@ -129,9 +133,10 @@ class ThreadDumpParserTest {
129133

130134
val spaceFrame = frames.get(14)
131135
assertEquals(
132-
"[anon:dalvik-classes16.dex extracted in memory from /data/app/~~izn1xSZpFlzfVmWi_I0xlQ=="
133-
+ "/io.sentry.samples.android-tQSGMNiGA-qdjZm6lPOcNw==/base.apk!classes16.dex]",
134-
spaceFrame.`package`)
136+
"[anon:dalvik-classes16.dex extracted in memory from /data/app/~~izn1xSZpFlzfVmWi_I0xlQ==" +
137+
"/io.sentry.samples.android-tQSGMNiGA-qdjZm6lPOcNw==/base.apk!classes16.dex]",
138+
spaceFrame.`package`
139+
)
135140
assertNull(spaceFrame.function)
136141
assertNull(spaceFrame.lineno)
137142
assertEquals("0x00000000000306f0", spaceFrame.instructionAddr)
@@ -152,7 +157,7 @@ class ThreadDumpParserTest {
152157
assertNull(deletedFrame.addrMode)
153158

154159
val debugImages = parser.debugImages
155-
val image = debugImages["499d48ba-c085-17cf-3209-da67405662f9"]
160+
val image = debugImages.first { image -> image.debugId == "499d48ba-c085-17cf-3209-da67405662f9" }
156161
assertNotNull(image)
157162
assertEquals("499d48ba-c085-17cf-3209-da67405662f9", image.debugId)
158163
assertEquals("/apex/com.android.runtime/lib64/bionic/libc.so", image.codeFile)
@@ -166,7 +171,7 @@ class ThreadDumpParserTest {
166171
SentryOptions().apply { addInAppInclude("io.sentry.samples") },
167172
false
168173
)
169-
val threads = parser.parse(lines)
170-
assertTrue(threads.isEmpty())
174+
parser.parse(lines)
175+
assertTrue(parser.threads.isEmpty())
171176
}
172177
}

sentry/src/main/java/io/sentry/protocol/SentryStackFrame.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,10 @@ public final class SentryStackFrame implements JsonUnknown, JsonSerializable {
100100
*
101101
* <ul>
102102
* <li><code>"abs"</code> (the default): <code>instruction_addr</code> is absolute.
103-
* <li><code>"rel:$idx"</code>: <code>instruction_addr</code> is relative to the
104-
* <code>debug_meta.image</code> identified by its index in the list.
105-
* <li><code>"rel:$uuid"</code>: <code>instruction_addr</code> is relative to the
106-
* <code>debug_meta.image</code> identified by its <code>debug_id</code>.
103+
* <li><code>"rel:$idx"</code>: <code>instruction_addr</code> is relative to the <code>
104+
* debug_meta.image</code> identified by its index in the list.
105+
* <li><code>"rel:$uuid"</code>: <code>instruction_addr</code> is relative to the <code>
106+
* debug_meta.image</code> identified by its <code>debug_id</code>.
107107
* </ul>
108108
*/
109109
private @Nullable String addrMode;
@@ -282,7 +282,7 @@ public void setInstructionAddr(final @Nullable String instructionAddr) {
282282
}
283283

284284
public void setAddrMode(final @Nullable String addrMode) {
285-
this.addrMode = addrMode;
285+
this.addrMode = addrMode;
286286
}
287287

288288
public @Nullable Boolean isNative() {

0 commit comments

Comments
 (0)