Retrofit: Android debugger crash at async callback

Created on 17 Oct 2017  Â·  6Comments  Â·  Source: square/retrofit

I was trying to debug my retrofit call but it crashes every single time I enter the async callback, specifically the below line:
mApiService.getReviews(mMovie.getId(), PrivateApiKey.YOUR_API_KEY).enqueue(new Callback<ReviewRootResponse>() { @Override public void onResponse(){} @Override public void onFailure(){} });
Running the app w/o debugger from Android Studio works fine. Crashes only in debug mode.
Logcat is as follows:
10-16 18:37:23.158 6690-6697/com.example.android.designpattern A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x14 in tid 6697 (JDWP)

Needs Info

Most helpful comment

Hello all,

I ran into this bug today, and since this is the only report I could find about this from Google, I thought I'd leave a message for posterity (even though this has absolutely nothing to do with Retrofit as far as I know).

As far as I understand this is a bug in AOSP, in the Art debugger support specifically.
It's triggered when a breakpoint is set on code called under a proxy class.

The backtrace shows a SIGSEV at address 0x14 in GetSourceDebugExtension, and indeed if you look at the source code:

const char* GetSourceDebugExtension(Handle<mirror::Class> klass) {
  if (klass->GetDexCache() == nullptr) {
    DCHECK(klass->IsPrimitive() || klass->IsArrayClass());
    return nullptr;
  }

  ClassData data(klass); /// <----- This calls klass->GetClassDef(), which sometimes returns a nullptr
  const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
    // ... <rest of function snipped>
}

const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  const DexFile& dex_file = klass.GetDexFile();
  const DexFile::AnnotationsDirectoryItem* annotations_dir =
      dex_file.GetAnnotationsDirectory(*klass.GetClassDef()); /// <-------- This happily takes a potentially null pointer and dereferences it
  // ... <rest of function snipped>
}

The good news is this has already been found and fixed in AOSP master, and after a quick git blame there's the commit that fixes it and here's the bug report mentioned in that commit.

The bad news is the fix didn't make it in time for Android 8.1, so we're stuck with workarounds for now.

Cheers, and my apologies for reviving a dead issue!

All 6 comments

If the only thing that changes is how you deploy from Android Studio, then
that sounds like a bug in Android Studio or the compilation toolchain. What
action do you expect us to take for this? Also you haven't really provided
enough information to even discern the cause of the crash.

On Mon, Oct 16, 2017 at 10:47 PM k3rn3l16 notifications@github.com wrote:

I was trying to debug my retrofit call but it crashes every single time I
enter the async callback, specifically the below line:
mApiService.getReviews(mMovie.getId(),
PrivateApiKey.YOUR_API_KEY).enqueue(new Callback() {
@Override public void onResponse(){} @Override public void onFailure(){} });
Running the app w/o debugger from Android Studio works fine. Crashes only
in debug mode.
Logcat is as follows:
10-16 18:37:23.158 6690-6697/com.example.android.designpattern A/libc:
Fatal signal 11 (SIGSEGV), code 1, fault addr 0x14 in tid 6697 (JDWP)

—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/square/retrofit/issues/2526, or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEULxDEHEZBArSbm_gEPsY5cBWtwJks5stBUlgaJpZM4P7gSQ
.

Ok let me give you all the information I have.
Below is my setup.
Client

public class TmdbApiClient {
    private static Retrofit retrofit = null;

    public static Retrofit getClient() {
        // Check if already initialized
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(TmdbApiService.BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();
        }

        // Return retrofit object
        return retrofit;
    }

Endpoint

public interface TmdbApiService {

    String BASE_URL = "https://api.themoviedb.org/3/";

    @GET("movie/{id}/reviews")
    Call<ReviewRootResponse> getReviews(
            @Path("id") int movieId,
            @Query("api_key") String apiKey
    );
}

Method

private void loadReviews(Movie movie) {
        TmdbApiService mApiService = TmdbApiClient.getClient().create(TmdbApiService.class);
        mApiService.getReviews(movie.getId(), PrivateApiKey.YOUR_API_KEY).enqueue(new Callback<ReviewRootResponse>() {
            @Override
            public void onResponse(Call<ReviewRootResponse> call, Response<ReviewRootResponse> response) {
                String url = call.request().url().toString();
                Log.d(LOG_TAG, "URL: " + url);
                if (response.isSuccessful()) {
                    if (response != null) {
                        mReviewAdapter.setReviews(response.body().getReviews());
                    }
                }
            }

            @Override
            public void onFailure(Call<ReviewRootResponse> call, Throwable t) {
                Log.d(LOG_TAG, t.toString());
            }
        });
    }

Call

        @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View detailMovieView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
        ButterKnife.bind(this, detailMovieView);

        // Get passed-in movie object
        mIntent = getActivity().getIntent();
        Movie movie = mIntent.getParcelableExtra("Movie");

        loadReviews(movie);

        // Return view
        return detailMovieView;
    }

In debug mode, when I step into the below line my app crashes and the logcat is already attached to this issue.
mApiService.getReviews(movie.getId(), PrivateApiKey.YOUR_API_KEY).enqueue(new Callback<ReviewRootResponse>() {

I hope I have given you all the information. Please let me know If the issue is purely IDE related, I'll talk to the appropriate team.

Can you provide the logs of the error? The message you posted doesn't tell
us anything about the cause.

On Tue, Oct 17, 2017 at 1:52 PM k3rn3l16 notifications@github.com wrote:

Ok let me give you all the information I have.
Below is my setup.
Client

public class TmdbApiClient {
private static Retrofit retrofit = null;

public static Retrofit getClient() {
// Check if already initialized
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(TmdbApiService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}

  // Return retrofit object
  return retrofit;

}

Endpoint

public interface TmdbApiService {

String BASE_URL = "https://api.themoviedb.org/3/";

@GET("movie/{id}/reviews")
Call getReviews(
@Path("id") int movieId,
@Query("api_key") String apiKey
);
}

Method

private void loadReviews(Movie movie) {
TmdbApiService mApiService = TmdbApiClient.getClient().create(TmdbApiService.class);
mApiService.getReviews(movie.getId(), PrivateApiKey.YOUR_API_KEY).enqueue(new Callback() {
@Override
public void onResponse(Call call, Response response) {
String url = call.request().url().toString();
Log.d(LOG_TAG, "URL: " + url);
if (response.isSuccessful()) {
if (response != null) {
mReviewAdapter.setReviews(response.body().getReviews());
}
}
}

      @Override
      public void onFailure(Call<ReviewRootResponse> call, Throwable t) {
          Log.d(LOG_TAG, t.toString());
      }
  });

}

Call

    @Override

public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View detailMovieView = inflater.inflate(R.layout.fragment_movie_detail, container, false);
ButterKnife.bind(this, detailMovieView);

  // Get passed-in movie object
  mIntent = getActivity().getIntent();
  Movie movie = mIntent.getParcelableExtra("Movie");

  loadReviews(movie);

  // Return view
  return detailMovieView;

}

In debug mode, when I step into the below line my app crashes and the
logcat is already attached to this issue.
mApiService.getReviews(movie.getId(),
PrivateApiKey.YOUR_API_KEY).enqueue(new Callback() {

I hope I have given you all the information. Please let me know If the
issue is purely IDE related, I'll talk to the appropriate team.

—
You are receiving this because you commented.

Reply to this email directly, view it on GitHub
https://github.com/square/retrofit/issues/2526#issuecomment-337307102,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AAEEEZ6NVv0o18spufQdcNXKW28zJGQrks5stOiygaJpZM4P7gSQ
.

@JakeWharton
Below is all I have in my logcat window.

10-18 10:34:10.640 31854-31864/com.example.android.designpattern A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x14 in tid 31864 (JDWP)

Please advise me on how to get the logs that you want to see.

There should be a native or Java stacktrace, or both, above that in the logs. Without it, there's no way to know what's going on, and I'm not sure how you deduced that Retrofit was the cause either.

Hello all,

I ran into this bug today, and since this is the only report I could find about this from Google, I thought I'd leave a message for posterity (even though this has absolutely nothing to do with Retrofit as far as I know).

As far as I understand this is a bug in AOSP, in the Art debugger support specifically.
It's triggered when a breakpoint is set on code called under a proxy class.

The backtrace shows a SIGSEV at address 0x14 in GetSourceDebugExtension, and indeed if you look at the source code:

const char* GetSourceDebugExtension(Handle<mirror::Class> klass) {
  if (klass->GetDexCache() == nullptr) {
    DCHECK(klass->IsPrimitive() || klass->IsArrayClass());
    return nullptr;
  }

  ClassData data(klass); /// <----- This calls klass->GetClassDef(), which sometimes returns a nullptr
  const DexFile::AnnotationSetItem* annotation_set = FindAnnotationSetForClass(data);
    // ... <rest of function snipped>
}

const DexFile::AnnotationSetItem* FindAnnotationSetForClass(const ClassData& klass)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  const DexFile& dex_file = klass.GetDexFile();
  const DexFile::AnnotationsDirectoryItem* annotations_dir =
      dex_file.GetAnnotationsDirectory(*klass.GetClassDef()); /// <-------- This happily takes a potentially null pointer and dereferences it
  // ... <rest of function snipped>
}

The good news is this has already been found and fixed in AOSP master, and after a quick git blame there's the commit that fixes it and here's the bug report mentioned in that commit.

The bad news is the fix didn't make it in time for Android 8.1, so we're stuck with workarounds for now.

Cheers, and my apologies for reviving a dead issue!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bhagyasri picture bhagyasri  Â·  3Comments

attaullahpro picture attaullahpro  Â·  3Comments

chriskessel picture chriskessel  Â·  3Comments

starktonys picture starktonys  Â·  3Comments

MetaiR picture MetaiR  Â·  3Comments