Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[UI] UProbes & Symbols Search #157

Merged
merged 10 commits into from
Dec 11, 2024
2 changes: 1 addition & 1 deletion frontend/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
signingConfig = signingConfigs.getByName("debug")
signingConfig = signingConfigs.getByName("debug") // change for production
}
}
buildFeatures {
Expand Down
38 changes: 23 additions & 15 deletions frontend/app/src/main/java/de/amosproj3/ziofa/ZiofaApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,22 @@ package de.amosproj3.ziofa
import android.app.Application
import android.content.Context
import android.content.pm.PackageManager
import de.amosproj3.ziofa.api.BackendConfigurationAccess
import de.amosproj3.ziofa.api.DataStreamProvider
import de.amosproj3.ziofa.api.LocalConfigurationAccess
import de.amosproj3.ziofa.api.RunningComponentsAccess
import de.amosproj3.ziofa.bl.ConfigurationManager
import de.amosproj3.ziofa.bl.DataStreamManager
import de.amosproj3.ziofa.bl.PackageInformationProvider
import de.amosproj3.ziofa.bl.RunningComponentsProvider
import de.amosproj3.ziofa.api.configuration.BackendConfigurationAccess
import de.amosproj3.ziofa.api.configuration.LocalConfigurationAccess
import de.amosproj3.ziofa.api.configuration.SymbolsAccess
import de.amosproj3.ziofa.api.events.DataStreamProvider
import de.amosproj3.ziofa.api.processes.RunningComponentsAccess
import de.amosproj3.ziofa.bl.configuration.ConfigurationManager
import de.amosproj3.ziofa.bl.configuration.UProbeManager
import de.amosproj3.ziofa.bl.events.DataStreamManager
import de.amosproj3.ziofa.bl.processes.PackageInformationProvider
import de.amosproj3.ziofa.bl.processes.RunningComponentsProvider
import de.amosproj3.ziofa.client.ClientFactory
import de.amosproj3.ziofa.client.RustClientFactory
import de.amosproj3.ziofa.ui.configuration.ConfigurationViewModel
import de.amosproj3.ziofa.ui.processes.ProcessesViewModel
import de.amosproj3.ziofa.ui.reset.ResetViewModel
import de.amosproj3.ziofa.ui.symbols.SymbolsViewModel
import de.amosproj3.ziofa.ui.visualization.VisualizationViewModel
import kotlinx.coroutines.CoroutineScope
import org.koin.android.ext.koin.androidContext
Expand All @@ -33,20 +37,20 @@ import timber.log.Timber

class ZiofaApplication : Application() {

val appModule: Module = module {
createExternalDependencies()
createBLModules()
createViewModelFactories()
}

override fun onCreate() {
super.onCreate()
Timber.plant(Timber.DebugTree()) // start Timber logging

startKoin {
androidLogger()
androidContext(this@ZiofaApplication)
modules(appModule)
modules(
module {
createExternalDependencies()
createBLModules()
createViewModelFactories()
}
)
}
}

Expand All @@ -63,6 +67,7 @@ class ZiofaApplication : Application() {
single { ConfigurationManager(clientFactory = get()) } binds
arrayOf(BackendConfigurationAccess::class, LocalConfigurationAccess::class)
factory<DataStreamProvider> { (scope: CoroutineScope) -> DataStreamManager(get(), scope) }
single<SymbolsAccess> { UProbeManager(get()) }
}

private fun Module.createViewModelFactories() {
Expand All @@ -73,6 +78,7 @@ class ZiofaApplication : Application() {
pids = pids,
)
}
viewModel { ResetViewModel(get()) }
viewModel { ProcessesViewModel(runningComponentsProvider = get()) }
viewModel {
VisualizationViewModel(
Expand All @@ -81,5 +87,7 @@ class ZiofaApplication : Application() {
runningComponentsAccess = get(),
)
}

viewModel { (pids: List<UInt>) -> SymbolsViewModel(get(), get(), pids) }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.configuration

import kotlinx.coroutines.flow.StateFlow

interface BackendConfigurationAccess {

/** Only emits updates from the backend that are actually confirmed to be active */
val backendConfiguration: StateFlow<ConfigurationUpdate>

/** Clear the backend configuration. */
fun reset()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.configuration

import de.amosproj3.ziofa.client.Configuration

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api.configuration

import de.amosproj3.ziofa.ui.symbols.data.SymbolsEntry

sealed class GetSymbolsRequestState {
data class Response(val symbols: List<SymbolsEntry>) : GetSymbolsRequestState()

data class Error(val errorMessage: String) : GetSymbolsRequestState()

data object Loading : GetSymbolsRequestState()
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.configuration

import de.amosproj3.ziofa.client.JniReferencesConfig
import de.amosproj3.ziofa.client.SysSendmsgConfig
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api.configuration

import kotlinx.coroutines.flow.Flow

interface SymbolsAccess {

/**
* Search all symbols of the given [pids] for a string that **contains** the search query. The
* search is case-insensitive.
*
* @param pids the PID whose binaries should be searched
* @param searchQuery the string to search for using
* @return a flow that describes the state of the request
*/
fun searchSymbols(pids: List<UInt>, searchQuery: String): Flow<GetSymbolsRequestState>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.events

sealed class BackendEvent(
val fileDescriptor: ULong,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.events

import kotlinx.coroutines.flow.Flow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.processes

import android.graphics.drawable.Drawable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.processes

import de.amosproj3.ziofa.client.Process

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.api
package de.amosproj3.ziofa.api.processes

import kotlinx.coroutines.flow.Flow

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.bl
package de.amosproj3.ziofa.bl.configuration

import de.amosproj3.ziofa.api.BackendConfigurationAccess
import de.amosproj3.ziofa.api.ConfigurationUpdate
import de.amosproj3.ziofa.api.LocalConfigurationAccess
import de.amosproj3.ziofa.api.configuration.BackendConfigurationAccess
import de.amosproj3.ziofa.api.configuration.ConfigurationUpdate
import de.amosproj3.ziofa.api.configuration.LocalConfigurationAccess
import de.amosproj3.ziofa.client.Client
import de.amosproj3.ziofa.client.ClientFactory
import de.amosproj3.ziofa.client.Configuration
Expand All @@ -17,13 +17,15 @@ import de.amosproj3.ziofa.client.SysSendmsgConfig
import de.amosproj3.ziofa.client.UprobeConfig
import de.amosproj3.ziofa.client.VfsWriteConfig
import de.amosproj3.ziofa.ui.shared.updatePIDs
import de.amosproj3.ziofa.ui.shared.updateUProbes
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import timber.log.Timber

class ConfigurationManager(val clientFactory: ClientFactory) :
Expand Down Expand Up @@ -62,7 +64,9 @@ class ConfigurationManager(val clientFactory: ClientFactory) :
) {
_localConfiguration.update { prev ->
Timber.e("changeFeatureConfigurationForPIDs.prev $prev")
Timber.e("changeFeatureConfigurationForPIDs() $vfsWriteFeature, $sendMessageFeature")
Timber.e(
"changeFeatureConfigurationForPIDs() $vfsWriteFeature, $sendMessageFeature, $uprobesFeature, $jniReferencesFeature"
)
// the configuration shall not be changed from the UI if there is none received from
// backend
if (prev != null && prev is ConfigurationUpdate.Valid) {
Expand All @@ -87,9 +91,22 @@ class ConfigurationManager(val clientFactory: ClientFactory) :
if (!enable) requestedChanges.entries.entries else setOf(),
)
} ?: previousConfiguration.sysSendmsg,
uprobes = uprobesFeature ?: prev.configuration.uprobes, // TODO
uprobes =
uprobesFeature.let { requestedChanges ->
if (requestedChanges == null)
return@let previousConfiguration.uprobes
previousConfiguration.uprobes.updateUProbes(
pidsToAdd = if (enable) requestedChanges else listOf(),
pidsToRemove = if (!enable) requestedChanges else listOf(),
)
},
jniReferences =
jniReferencesFeature ?: prev.configuration.jniReferences, // TODO
jniReferencesFeature?.let { requestedChanges ->
previousConfiguration.jniReferences.updatePIDs(
pidsToAdd = if (enable) requestedChanges.pids else listOf(),
pidsToRemove = if (!enable) requestedChanges.pids else listOf(),
)
} ?: previousConfiguration.jniReferences,
)
.also { Timber.i("new local configuration = $it") }
.let { ConfigurationUpdate.Valid(it) }
Expand All @@ -106,6 +123,13 @@ class ConfigurationManager(val clientFactory: ClientFactory) :
}
}

override fun reset() {
runBlocking {
client?.setConfiguration(Configuration(null, null, listOf(), null))
updateBothConfigurations(getFromBackend())
}
}

private suspend fun initializeConfigurations() {
val initializedConfiguration =
try {
Expand All @@ -125,7 +149,7 @@ class ConfigurationManager(val clientFactory: ClientFactory) :
vfsWrite = null,
sysSendmsg = null,
uprobes = listOf(),
jniReferences = JniReferencesConfig(pids = listOf()),
jniReferences = null,
)
)
ConfigurationUpdate.Valid(client!!.getConfiguration())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2024 Luca Bretting <[email protected]>
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.bl.configuration

import de.amosproj3.ziofa.api.configuration.GetSymbolsRequestState
import de.amosproj3.ziofa.api.configuration.SymbolsAccess
import de.amosproj3.ziofa.client.ClientFactory
import de.amosproj3.ziofa.ui.symbols.data.SymbolsEntry
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.flatMapMerge
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.toList
import timber.log.Timber

class UProbeManager(private val clientFactory: ClientFactory) : SymbolsAccess {

/** We should do this on the backend in the future. */
@OptIn(ExperimentalCoroutinesApi::class)
override fun searchSymbols(
pids: List<UInt>,
searchQuery: String,
): Flow<GetSymbolsRequestState> =
flow {
emit(GetSymbolsRequestState.Loading)
try {
val client = clientFactory.connect()
val symbols =
pids
.map { pid ->
client
.getOdexFiles(pid)
.onEach { Timber.i("Requesting symbols for odex file $it") }
.flatMapMerge { odexFile ->
client
.getSymbols(odexFilePath = odexFile)
.filter {
it.method
.lowercase()
.contains(searchQuery.lowercase())
}
.map { symbol ->
SymbolsEntry(symbol.method, odexFile, symbol.offset)
}
}
}
.merge()
.toList()
emit(GetSymbolsRequestState.Response(symbols))
} catch (e: Exception) {
emit(GetSymbolsRequestState.Error(e.stackTraceToString()))
}
}
.onStart { Timber.i("searchSymbols pids=$pids searchQuery=$searchQuery") }
.flowOn(Dispatchers.IO)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.bl
package de.amosproj3.ziofa.bl.events

import de.amosproj3.ziofa.api.BackendEvent
import de.amosproj3.ziofa.api.DataStreamProvider
import de.amosproj3.ziofa.api.events.BackendEvent
import de.amosproj3.ziofa.api.events.DataStreamProvider
import de.amosproj3.ziofa.client.ClientFactory
import de.amosproj3.ziofa.client.Event
import kotlinx.coroutines.CoroutineScope
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
//
// SPDX-License-Identifier: MIT

package de.amosproj3.ziofa.bl
package de.amosproj3.ziofa.bl.processes

import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.drawable.Drawable
import de.amosproj3.ziofa.api.InstalledPackageInfo
import de.amosproj3.ziofa.api.processes.InstalledPackageInfo

class PackageInformationProvider(private val packageManager: PackageManager) {

Expand Down
Loading