Skip to content

Commit

Permalink
feat: Enable GPU acceleration for new instances (#1991)
Browse files Browse the repository at this point in the history
Fixes #1983 

## Test Plan
> How do we know the code works?

1. Remove previously created virtual devices in Corellium project.
2. Run `flank corellium test android run -c="./flank_corellium.yml"`
3. Check the newly created devices have enabled GPU acceleration
    * Debug `RunTestAndroidCorelliumExample` and check `flank.corellium.adapter.getCreatedInstances` are returning instances with properly configured `BootOptions`|
    * Or find another way for checking if GPU acceleration is enabled.

## Checklist

- [x] Documented
- [x] Unit tested
  • Loading branch information
jan-goral authored Jun 1, 2021
1 parent 33d8220 commit 6e76e61
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import flank.corellium.client.core.getProjectInstancesList
import flank.corellium.client.core.startInstance
import flank.corellium.client.core.waitUntilInstanceIsReady
import flank.corellium.client.data.Instance
import flank.corellium.client.data.Instance.BootOptions.AdditionalTags.GPU
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.channelFlow
Expand All @@ -20,23 +21,24 @@ private const val SCREEN = "720x1280:280"

fun invokeAndroidDevices(
projectName: String,
) = AndroidInstance.Invoke { (amount) ->
) = AndroidInstance.Invoke { config ->
channelFlow<String> {
val projectId = getProjectId(projectName)
val instances = getCreatedInstances(projectId, amount)
val instances = getCreatedInstances(projectId, config.amount)
startNotRunningInstances(instances)

val ids = instances.map(Instance::id) + let {
// When existing instances size match required amount
// there is not needs for creating more instances.
if (instances.size == amount) emptyList()
if (instances.size == config.amount) emptyList()
// Otherwise is required to create some additional instances
else createInstances(
projectId = projectId,
gpuAcceleration = config.gpuAcceleration,
indexes = calculateAdditionalIndexes(
current = instances,
requiredAmount = amount
)
requiredAmount = config.amount
),
)
}

Expand Down Expand Up @@ -87,7 +89,8 @@ private suspend fun startNotRunningInstances(
*/
private suspend fun createInstances(
projectId: String,
indexes: List<Int>
indexes: List<Int>,
gpuAcceleration: Boolean,
) = indexes
.apply { println("Creating additional ${indexes.size} instances. Connecting to the agents may take longer.") }
.map { index ->
Expand All @@ -98,7 +101,10 @@ private suspend fun createInstances(
flavor = FLAVOUR,
os = OS,
bootOptions = Instance.BootOptions(
screen = SCREEN
screen = SCREEN,
additionalTags = listOfNotNull(
GPU.takeIf { gpuAcceleration }
)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ object AndroidInstance {
/**
* Configuration for devices to invoke.
*
* @property amount the amount of devices to invoke.
* @property amount The amount of devices to invoke.
* @property gpuAcceleration Enables gpu acceleration for virtual devices.
*/
data class Config(
val amount: Int
val amount: Int,
val gpuAcceleration: Boolean
)

/**
* Invoke the android corellium devices.
*
* After successful invoke, the devices specified in th [Config] should be running and ready for use.
*
* @return list of invoked device ids.
* @return List of invoked device ids.
*/
fun interface Invoke : (Config) -> Flow<String>
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,17 @@ class RunTestCorelliumAndroidCommand :
]
)
var obfuscate: Boolean? by data

@set:CommandLine.Option(
names = ["--gpu-acceleration"],
description = [
"Enable cloud GPU acceleration (Extra costs incurred)." +
"Currently this option only works for newly devices created." +
"To create new device pool with gpu-acceleration, remove old devices manually and let Flank recreate the pool."
]
)
@set:JsonProperty("gpu-acceleration")
var gpuAcceleration: Boolean? by data
}

@CommandLine.Mixin
Expand Down Expand Up @@ -119,6 +130,7 @@ private fun defaultConfig() = Config().apply {
maxTestShards = 1
localResultsDir = null
obfuscate = false
gpuAcceleration = true
}

private fun RunTestCorelliumAndroidCommand.yamlConfig(): Config =
Expand All @@ -130,4 +142,5 @@ private fun RunTestCorelliumAndroidCommand.createArgs() = Args(
maxShardsCount = config.maxTestShards!!,
outputDir = config.localResultsDir ?: Args.DefaultOutputDir.new,
obfuscateDumpShards = config.obfuscate!!,
gpuAcceleration = config.gpuAcceleration!!
)
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class RunTestCorelliumAndroidCommandTest {
maxTestShards = Int.MAX_VALUE
localResultsDir = "test_result_dir"
obfuscate = true
gpuAcceleration = false
}

/**
Expand All @@ -57,6 +58,7 @@ class RunTestCorelliumAndroidCommandTest {
"--max-test-shards=$maxTestShards",
"--local-result-dir=$localResultsDir",
"--obfuscate=$obfuscate",
"--gpu-acceleration=$gpuAcceleration",
)
}

Expand All @@ -75,6 +77,7 @@ apks:
max-test-shards: $maxTestShards
local-result-dir: $localResultsDir
obfuscate: $obfuscate
gpu-acceleration: $gpuAcceleration
""".trimIndent()
}

Expand Down Expand Up @@ -136,7 +139,8 @@ obfuscate: $obfuscate
apks = apks!!,
maxShardsCount = maxTestShards!!,
outputDir = localResultsDir!!,
obfuscateDumpShards = obfuscate!!
obfuscateDumpShards = obfuscate!!,
gpuAcceleration = gpuAcceleration!!,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,29 @@ data class Instance(
val info: String = ""
)

/**
* @param udid Predefined Unique Device ID (UDID) for iOS device
* @param screen Change the screen metrics for Ranchu devices `XxY[:DPI]`, e.g. `720x1280:280`
* @param additionalTags features to utilize for the device, valid options include. Check [AdditionalTags]
*/
@Serializable
data class BootOptions(
val bootArgs: String = "",
val restoreBootArgs: String = "",
val udid: String = "",
val ecid: String = "",
val screen: String = ""
)
val screen: String = "",
val additionalTags: List<String> = emptyList(),
) {
/**
* @property GPU Enable cloud GPU acceleration (Extra costs incurred, cloud only).
* @property KALLOC Enable kalloc/kfree trace access via GDB (Enterprise only).
*/
object AdditionalTags {
const val GPU = "gpu"
const val KALLOC = "kalloc"
}
}
}

@Serializable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@ object RunTestCorelliumAndroid {
* @param maxShardsCount Maximum amount of shards to create. For each shard Flank is invoking dedicated device instance, so do not use values grater than maximum number available instances in the Corellium account.
* @param obfuscateDumpShards Obfuscate the test names in shards before dumping to file.
* @param outputDir Set output dir. Default value is [DefaultOutputDir.new]
* @param gpuAcceleration Enable gpu acceleration for newly created virtual devices.
*/
data class Args(
val credentials: Authorization.Credentials,
val apks: List<Apk.App>,
val maxShardsCount: Int,
val obfuscateDumpShards: Boolean = false,
val outputDir: String = DefaultOutputDir.new,
val gpuAcceleration: Boolean = true,
) {
/**
* Default output directory scheme.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,9 @@ import kotlinx.coroutines.flow.toList
*/
internal fun RunTestCorelliumAndroid.Context.invokeDevices() = RunTestCorelliumAndroid.step {
println("* Invoking devices")
copy(ids = api.invokeAndroidDevices(AndroidInstance.Config(shards.size)).toList())
val config = AndroidInstance.Config(
amount = shards.size,
gpuAcceleration = args.gpuAcceleration
)
copy(ids = api.invokeAndroidDevices(config).toList())
}

0 comments on commit 6e76e61

Please sign in to comment.