Skip to content

Commit

Permalink
Merge pull request #2526 from square/py/treemap
Browse files Browse the repository at this point in the history
Treemap
  • Loading branch information
pyricau committed May 31, 2023
2 parents b463445 + f028180 commit 540545e
Show file tree
Hide file tree
Showing 28 changed files with 753 additions and 176 deletions.
10 changes: 5 additions & 5 deletions config/detekt-config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ complexity:
# #LeakCanary - increased from 60 to 90
# active: true
# threshold: 90
LongParameterList:
#LeakCanary - enabled ignore
active: true
threshold: 6
ignoreDefaultParameters: true
# LongParameterList:
# #LeakCanary - enabled ignore
# active: true
# threshold: 6
# ignoreDefaultParameters: true
MethodOverloading:
active: false
threshold: 6
Expand Down
17 changes: 17 additions & 0 deletions leakcanary/leakcanary-android-core/api/leakcanary-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -167,3 +167,20 @@ public final class leakcanary/WorkManagerHeapAnalyzer : leakcanary/EventListener
public fun onEvent (Lleakcanary/EventListener$Event;)V
}

public final class leakcanary/internal/LeakCanaryFileProvider : android/content/ContentProvider {
public static final field Companion Lleakcanary/internal/LeakCanaryFileProvider$Companion;
public fun <init> ()V
public fun attachInfo (Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V
public fun delete (Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I
public fun getType (Landroid/net/Uri;)Ljava/lang/String;
public fun insert (Landroid/net/Uri;Landroid/content/ContentValues;)Landroid/net/Uri;
public fun onCreate ()Z
public fun openFile (Landroid/net/Uri;Ljava/lang/String;)Landroid/os/ParcelFileDescriptor;
public fun query (Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;
public fun update (Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I
}

public final class leakcanary/internal/LeakCanaryFileProvider$Companion {
public final fun getUriForFile (Landroid/content/Context;Ljava/lang/String;Ljava/io/File;)Landroid/net/Uri;
}

Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ import org.xmlpull.v1.XmlPullParserException

/**
* Copy of androidx.core.content.FileProvider, converted to Kotlin.
* This is an internal class, only public to be usable in another module.
* TODO Consider building a public API for exposing files with the right permissions to
* be shared.
*/
internal class LeakCanaryFileProvider : ContentProvider() {
class LeakCanaryFileProvider : ContentProvider() {

private lateinit var mStrategy: PathStrategy

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ import android.graphics.Paint.Style.FILL
import android.graphics.Paint.Style.STROKE
import android.graphics.Rect
import com.squareup.leakcanary.core.R
import java.io.File
import kotlin.math.ceil
import kotlin.math.max
import leakcanary.internal.navigation.getColorCompat
import shark.HprofRecord
import shark.HprofRecord.HeapDumpEndRecord
Expand All @@ -32,9 +35,6 @@ import shark.HprofRecord.StackTraceRecord
import shark.HprofRecord.StringRecord
import shark.StreamingHprofReader
import shark.StreamingRecordReaderAdapter.Companion.asStreamingRecordReader
import java.io.File
import kotlin.math.ceil
import kotlin.math.max

internal object HeapDumpRenderer {

Expand All @@ -48,7 +48,6 @@ internal object HeapDumpRenderer {
get() = this * density
}

@Suppress("LongMethod")
fun render(
context: Context,
heapDumpFile: File,
Expand Down
23 changes: 2 additions & 21 deletions leakcanary/leakcanary-app-aidl/api/leakcanary-app-aidl.api
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
public abstract interface class org/leakcanary/internal/HeapDataRepository : android/os/IInterface {
public abstract fun sayHi ()V
}

public class org/leakcanary/internal/HeapDataRepository$Default : org/leakcanary/internal/HeapDataRepository {
public fun <init> ()V
public fun asBinder ()Landroid/os/IBinder;
public fun sayHi ()V
}

public abstract class org/leakcanary/internal/HeapDataRepository$Stub : android/os/Binder, org/leakcanary/internal/HeapDataRepository {
public fun <init> ()V
public fun asBinder ()Landroid/os/IBinder;
public static fun asInterface (Landroid/os/IBinder;)Lorg/leakcanary/internal/HeapDataRepository;
public static fun getDefaultImpl ()Lorg/leakcanary/internal/HeapDataRepository;
public fun onTransact (ILandroid/os/Parcel;Landroid/os/Parcel;I)Z
public static fun setDefaultImpl (Lorg/leakcanary/internal/HeapDataRepository;)Z
}

public abstract interface class org/leakcanary/internal/LeakUiApp : android/os/IInterface {
public abstract fun sendHeapAnalysis (Lorg/leakcanary/internal/ParcelableHeapAnalysis;)V
public abstract fun sendHeapAnalysis (Lorg/leakcanary/internal/ParcelableHeapAnalysis;Landroid/net/Uri;)V
}

public class org/leakcanary/internal/LeakUiApp$Default : org/leakcanary/internal/LeakUiApp {
public fun <init> ()V
public fun asBinder ()Landroid/os/IBinder;
public fun sendHeapAnalysis (Lorg/leakcanary/internal/ParcelableHeapAnalysis;)V
public fun sendHeapAnalysis (Lorg/leakcanary/internal/ParcelableHeapAnalysis;Landroid/net/Uri;)V
}

public abstract class org/leakcanary/internal/LeakUiApp$Stub : android/os/Binder, org/leakcanary/internal/LeakUiApp {
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package org.leakcanary.internal;

import android.net.Uri;

parcelable ParcelableHeapAnalysis;

interface LeakUiApp {
void sendHeapAnalysis(in ParcelableHeapAnalysis heapAnalysis);
void sendHeapAnalysis(in ParcelableHeapAnalysis heapAnalysis, in Uri heapDumpUri);
}
2 changes: 2 additions & 0 deletions leakcanary/leakcanary-app-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,7 @@ android {

dependencies {
implementation projects.leakcanary.leakcanaryAppAidl
implementation projects.leakcanary.leakcanaryAndroidCore
implementation projects.shark.shark
implementation projects.shark.sharkAndroid
}
13 changes: 0 additions & 13 deletions leakcanary/leakcanary-app-service/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,19 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application>
<service
android:name="org.leakcanary.internal.HeapDataRepositoryService"
android:exported="true">
<!-- TODO Double check this is the right setup.-->
<!-- TODO Probs use $applicationId in package name instead.-->
<intent-filter>
<action android:name="org.leakcanary.internal.HeapDataRepositoryService.BIND" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>

<queries>
<package android:name="org.leakcanary" />
</queries>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ package org.leakcanary.internal
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION
import android.content.ServiceConnection
import android.os.IBinder
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import leakcanary.internal.LeakCanaryFileProvider
import org.leakcanary.internal.ParcelableHeapAnalysis.Companion.asParcelable
import shark.HeapAnalysis
import shark.SharkLog
Expand All @@ -23,10 +25,9 @@ import shark.SharkLog
* We can ensure apps have the LeakCanary app in their manifest, however the other way round
* isn't possible, we don't know in advance which apps we'll talk to.
*
* One of the automatic cases is "Any app that starts or binds to a service in your app".
*
* So we'll have apps poke the LeakCanary app by binding, which then gives it permission to bind
* a service back.
* One of the automatic cases is "Any app that starts or binds to a service in your app", so if we
* ever need the LeakCanary app to talk back we could have apps first poke the LeakCanary app by
* binding, which then gives it permission to bind a service back.
*
* On AIDL backward compatibility: HeapAnalysis is Serializable so we need to ensure compatibility
* via Serializable.
Expand Down Expand Up @@ -60,7 +61,21 @@ class LeakUiAppClient(
SharkLog.d { "LeakUiAppService up=$bringingServiceUp" }
val serviceConnected = sendLatch.await(20, TimeUnit.SECONDS)
if (serviceConnected) {
leakUiApp.sendHeapAnalysis(heapAnalysis.asParcelable())
val heapDumpContentUri = LeakCanaryFileProvider.getUriForFile(
appContext,
"com.squareup.leakcanary.fileprovider.${appContext.packageName}",
heapAnalysis.heapDumpFile
)
appContext.grantUriPermission("org.leakcanary", heapDumpContentUri, FLAG_GRANT_READ_URI_PERMISSION)
try {
leakUiApp.sendHeapAnalysis(heapAnalysis.asParcelable(), heapDumpContentUri)
} finally {
appContext.revokeUriPermission(
"org.leakcanary", heapDumpContentUri, FLAG_GRANT_READ_URI_PERMISSION
)
}

// TODO Revoke permission
} else {
// TODO Handle service connection error
}
Expand Down
1 change: 1 addition & 0 deletions leakcanary/leakcanary-app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ dependencies {
// TODO Split out what's included in debug vs the subset for release
implementation projects.leakcanary.leakcanaryAndroid
implementation 'com.google.dagger:hilt-android:2.43.2'
implementation libs.okio2
kapt 'com.google.dagger:hilt-compiler:2.43.2'
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,70 +1,28 @@
package org.leakcanary

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.ViewModelProvider
import dagger.hilt.android.AndroidEntryPoint
import org.leakcanary.internal.HeapDataRepository
import org.leakcanary.screens.BackStackViewModel
import org.leakcanary.screens.ScreenHost
import org.leakcanary.ui.theme.MyApplicationTheme
import shark.HeapAnalysis
import shark.SharkLog

@AndroidEntryPoint
class MainActivity : ComponentActivity() {

private var heapDataRepository: HeapDataRepository? = null

private var connected by mutableStateOf(false)

private val serviceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName, service: IBinder) {
heapDataRepository = HeapDataRepository.Stub.asInterface(service)
connected = true
}

override fun onServiceDisconnected(name: ComponentName) {
heapDataRepository = null
connected = false
}
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// Dumb hack to ensure the backstack is created early enough in the activity
// graph so that BackStackHolder will always have a ref.
ViewModelProvider(this)[BackStackViewModel::class.java]

val intent = Intent("org.leakcanary.internal.HeapDataRepositoryService.BIND")
.apply {
setPackage("com.example.leakcanary")
}

val bringingServiceUp = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
SharkLog.d { "HeapDataRepositoryService up=$bringingServiceUp" }

setContent {
MyApplicationTheme {
Surface(modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {
Expand All @@ -73,9 +31,4 @@ class MainActivity : ComponentActivity() {
}
}
}

override fun onDestroy() {
super.onDestroy()
unbindService(serviceConnection)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class HeapRepository @Inject constructor(
""".trimMargin(),
parameters = 1,
binders = {
bindString(1, signature)
bindString(0, signature)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,12 @@ import org.leakcanary.data.HeapRepository
import org.leakcanary.screens.ClientAppAnalysisState.Loading
import org.leakcanary.screens.ClientAppAnalysisState.Success
import org.leakcanary.screens.Destination.ClientAppAnalysisDestination
import org.leakcanary.screens.Destination.TreeMapDestination
import org.leakcanary.screens.HeaderCardLink.EXPLORE_HPROF
import org.leakcanary.screens.HeaderCardLink.PRINT
import org.leakcanary.screens.HeaderCardLink.SHARE_ANALYSIS
import org.leakcanary.screens.HeaderCardLink.SHARE_HPROF
import org.leakcanary.screens.HeaderCardLink.SHOW_TREE_MAP
import org.leakcanary.util.LeakTraceWrapper
import org.leakcanary.util.Sharer
import shark.HeapAnalysisSuccess
Expand Down Expand Up @@ -91,6 +93,9 @@ class ClientAppAnalysisViewModel @Inject constructor(
SharkLog.d { "\u200B\n" + LeakTraceWrapper.wrap(heapAnalysis.toString(), 120) }
}
SHARE_HPROF -> TODO()
SHOW_TREE_MAP -> {
navigator.goTo(TreeMapDestination(heapAnalysis.heapDumpFile))
}
}
}

Expand All @@ -105,7 +110,8 @@ enum class HeaderCardLink {
EXPLORE_HPROF,
SHARE_ANALYSIS,
PRINT,
SHARE_HPROF
SHARE_HPROF,
SHOW_TREE_MAP
}

@Composable fun ClientAppAnalysisScreen(viewModel: ClientAppAnalysisViewModel = viewModel()) {
Expand Down Expand Up @@ -145,6 +151,10 @@ enum class HeaderCardLink {
appendLink("Heap Dump file", SHARE_HPROF)
append("\n\n")
}
// TODO check we can connect to app
append("Show ")
appendLink("Tree Map", SHOW_TREE_MAP)
append("\n\n")
// TODO this should be an expendable item row instead.
/*
val dumpDurationMillis =
Expand All @@ -170,11 +180,12 @@ enum class HeaderCardLink {
ClickableText(text = annotatedString,
style = MaterialTheme.typography.bodySmall,
onClick = { offset ->
val link = HeaderCardLink.valueOf(
annotatedString.getStringAnnotations(tag = "link", start = offset, end = offset)
.single().item
)
viewModel.onHeaderCardLinkClicked(heapAnalysis, link)

val annotations = annotatedString.getStringAnnotations(tag = "link", start = offset, end = offset)
if (annotations.size == 1) {
val link = HeaderCardLink.valueOf(annotations.single().item)
viewModel.onHeaderCardLinkClicked(heapAnalysis, link)
}
})
}
}
Expand Down
Loading

0 comments on commit 540545e

Please sign in to comment.