Reporting back native stacktrace to dart side for crash reporting. (flutter/engine#20280)

* Add native stacktrace on iOS

* Add native stacktrace on Android

* format and changing naming to errorWithCode on iOS

* reformat

* Remove stacktrace from decodeEnvelope, not needed.

* Separate encodeErrorEnvelopeWithStacktrace with original encode function

* Add unit tests

* re-format

* change comments for stacktrace

* Remove changes for iOS

Co-authored-by: Ben Li <libe@google.com>
This commit is contained in:
LI DONGZE 2020-08-21 19:59:36 -07:00 committed by GitHub
parent 3c1d67e975
commit 38ad113515
5 changed files with 79 additions and 1 deletions

View File

@ -70,6 +70,17 @@ public final class JSONMethodCodec implements MethodCodec {
.put(JSONUtil.wrap(errorDetails)));
}
@Override
public ByteBuffer encodeErrorEnvelopeWithStacktrace(
String errorCode, String errorMessage, Object errorDetails, String errorStacktrace) {
return JSONMessageCodec.INSTANCE.encodeMessage(
new JSONArray()
.put(errorCode)
.put(JSONUtil.wrap(errorMessage))
.put(JSONUtil.wrap(errorDetails))
.put(JSONUtil.wrap(errorStacktrace)));
}
@Override
public Object decodeEnvelope(ByteBuffer envelope) {
try {

View File

@ -11,6 +11,9 @@ import androidx.annotation.UiThread;
import io.flutter.BuildConfig;
import io.flutter.plugin.common.BinaryMessenger.BinaryMessageHandler;
import io.flutter.plugin.common.BinaryMessenger.BinaryReply;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.nio.ByteBuffer;
/**
@ -247,8 +250,16 @@ public class MethodChannel {
});
} catch (RuntimeException e) {
Log.e(TAG + name, "Failed to handle method call", e);
reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
reply.reply(
codec.encodeErrorEnvelopeWithStacktrace(
"error", e.getMessage(), null, getStackTrace(e)));
}
}
private String getStackTrace(Exception e) {
Writer result = new StringWriter();
e.printStackTrace(new PrintWriter(result));
return result.toString();
}
}
}

View File

@ -55,6 +55,20 @@ public interface MethodCodec {
*/
ByteBuffer encodeErrorEnvelope(String errorCode, String errorMessage, Object errorDetails);
/**
* Encodes an error result into a binary envelope message with the native stacktrace.
*
* @param errorCode An error code String.
* @param errorMessage An error message String, possibly null.
* @param errorDetails Error details, possibly null. Consider supporting {@link Throwable} in your
* codec. This is the most common value passed to this field.
* @param errorStacktrace Platform stacktrace for the error. possibly null.
* @return a {@link ByteBuffer} containing the encoding between position 0 and the current
* position.
*/
ByteBuffer encodeErrorEnvelopeWithStacktrace(
String errorCode, String errorMessage, Object errorDetails, String errorStacktrace);
/**
* Decodes a result envelope from binary.
*

View File

@ -79,6 +79,24 @@ public final class StandardMethodCodec implements MethodCodec {
return buffer;
}
@Override
public ByteBuffer encodeErrorEnvelopeWithStacktrace(
String errorCode, String errorMessage, Object errorDetails, String errorStacktrace) {
final ExposedByteArrayOutputStream stream = new ExposedByteArrayOutputStream();
stream.write(1);
messageCodec.writeValue(stream, errorCode);
messageCodec.writeValue(stream, errorMessage);
if (errorDetails instanceof Throwable) {
messageCodec.writeValue(stream, getStackTrace((Throwable) errorDetails));
} else {
messageCodec.writeValue(stream, errorDetails);
}
messageCodec.writeValue(stream, errorStacktrace);
final ByteBuffer buffer = ByteBuffer.allocateDirect(stream.size());
buffer.put(stream.buffer(), 0, stream.size());
return buffer;
}
@Override
public Object decodeEnvelope(ByteBuffer envelope) {
envelope.order(ByteOrder.nativeOrder());

View File

@ -7,6 +7,7 @@ import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
@ -89,4 +90,27 @@ public class StandardMethodCodecTest {
"at io.flutter.plugin.common.StandardMethodCodecTest.encodeErrorEnvelopeWithThrowableTest(StandardMethodCodecTest.java:"));
}
}
@Test
public void encodeErrorEnvelopeWithStacktraceTest() {
final Exception e = new IllegalArgumentException("foo");
final ByteBuffer buffer =
StandardMethodCodec.INSTANCE.encodeErrorEnvelopeWithStacktrace(
"code", e.getMessage(), e, "error stacktrace");
assertNotNull(buffer);
buffer.flip();
buffer.order(ByteOrder.nativeOrder());
final byte flag = buffer.get();
final Object code = StandardMessageCodec.INSTANCE.readValue(buffer);
final Object message = StandardMessageCodec.INSTANCE.readValue(buffer);
final Object details = StandardMessageCodec.INSTANCE.readValue(buffer);
final Object stacktrace = StandardMessageCodec.INSTANCE.readValue(buffer);
assertEquals("code", (String) code);
assertEquals("foo", (String) message);
String stack = (String) details;
assertTrue(
stack.contains(
"at io.flutter.plugin.common.StandardMethodCodecTest.encodeErrorEnvelopeWithStacktraceTest(StandardMethodCodecTest.java:"));
assertEquals("error stacktrace", (String) stacktrace);
}
}