Skip to content

Commit

Permalink
Log Java stack trace for uncaught JniExceptions
Browse files Browse the repository at this point in the history
Summary: `JniException::what` is used as the abort message when a `JniException` is uncaught. It is currently missing the stack trace information you'd need to debug the underlying Java `Exception`.

Reviewed By: mhorowitz

Differential Revision: D55830419

fbshipit-source-id: 82992db5e60871cf78a779d56aa3528c2c8b8e68
  • Loading branch information
Edward Pastuszenski authored and facebook-github-bot committed Apr 11, 2024
1 parent 17d30bc commit cd6e61a
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 4 deletions.
8 changes: 7 additions & 1 deletion cxx/fbjni/detail/Exceptions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,13 @@ local_ref<JThrowable> JniException::getThrowable() const noexcept {
void JniException::populateWhat() const noexcept {
try {
ThreadScope ts;
what_ = throwable_->toString();
static auto exceptionHelperClass =
findClassStatic("com/facebook/jni/ExceptionHelper");
static auto getErrorDescriptionMethod =
exceptionHelperClass->getStaticMethod<std::string(jthrowable)>(
"getErrorDescription");
what_ = getErrorDescriptionMethod(exceptionHelperClass, throwable_.get())
->toStdString();
isMessageExtracted_ = true;
} catch (...) {
what_ = kExceptionMessageFailure;
Expand Down
31 changes: 31 additions & 0 deletions java/com/facebook/jni/ExceptionHelper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.facebook.jni;

import com.facebook.jni.annotations.DoNotStrip;
import java.io.PrintWriter;
import java.io.StringWriter;

@DoNotStrip
class ExceptionHelper {
@DoNotStrip
private static String getErrorDescription(Throwable throwable) {
final StringWriter stringWriter = new StringWriter();
throwable.printStackTrace(new PrintWriter(stringWriter));
return stringWriter.toString();
}
}
26 changes: 23 additions & 3 deletions test/FBJniTests.java
Original file line number Diff line number Diff line change
Expand Up @@ -639,23 +639,43 @@ public void testHandleNoRttiException() {
@Test
public void testCopyConstructor() {
assertThat(nativeTestCopyConstructor())
.isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
.startsWith(
"com.facebook.jni.FBJniTests$CustomException: getMessages: 1\n"
+ "\tat com.facebook.jni.FBJniTests.customExceptionThrower(FBJniTests.java:")
.contains(
")\n"
+ "\tat com.facebook.jni.FBJniTests.nativeTestCopyConstructor(Native Method)\n"
+ "\tat com.facebook.jni.FBJniTests.testCopyConstructor(FBJniTests.java:");
}

private native String nativeTestCopyConstructor();

@Test
public void testMoveConstructorWithEmptyWhat() {
assertThat(nativeTestMoveConstructorWithEmptyWhat())
.isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
.startsWith(
"com.facebook.jni.FBJniTests$CustomException: getMessages: 1\n"
+ "\tat com.facebook.jni.FBJniTests.customExceptionThrower(FBJniTests.java:")
.contains(
")\n"
+ "\tat com.facebook.jni.FBJniTests.nativeTestMoveConstructorWithEmptyWhat(Native"
+ " Method)\n"
+ "\tat com.facebook.jni.FBJniTests.testMoveConstructorWithEmptyWhat(FBJniTests.java:");
}

private native String nativeTestMoveConstructorWithEmptyWhat();

@Test
public void testMoveConstructorWithPopulatedWhat() {
assertThat(nativeTestMoveConstructorWithPopulatedWhat())
.isEqualTo("com.facebook.jni.FBJniTests$CustomException: getMessages: 1");
.startsWith(
"com.facebook.jni.FBJniTests$CustomException: getMessages: 1\n"
+ "\tat com.facebook.jni.FBJniTests.customExceptionThrower(FBJniTests.java:")
.contains(
")\n"
+ "\tat com.facebook.jni.FBJniTests.nativeTestMoveConstructorWithPopulatedWhat(Native"
+ " Method)\n"
+ "\tat com.facebook.jni.FBJniTests.testMoveConstructorWithPopulatedWhat(FBJniTests.java:");
}

private native String nativeTestMoveConstructorWithPopulatedWhat();
Expand Down

0 comments on commit cd6e61a

Please sign in to comment.