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

refactor: Refactor data scratch-performance metrics #1840

Merged
merged 1 commit into from
Apr 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ftl.adapter

import ftl.adapter.google.toApiModel
import ftl.adapter.google.toClientModel
import ftl.api.PerfMetrics
import ftl.gc.GcToolResults

object GooglePerformanceMetricsFetch :
PerfMetrics.Fetch,
(PerfMetrics.Identity) -> PerfMetrics.Summary by { identity ->
GcToolResults.getPerformanceMetric(identity.toClientModel()).toApiModel()
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package ftl.adapter.google

import com.google.api.services.toolresults.model.AppStartTime
import com.google.api.services.toolresults.model.CPUInfo
import com.google.api.services.toolresults.model.GraphicsStats
import com.google.api.services.toolresults.model.GraphicsStatsBucket
import com.google.api.services.toolresults.model.MemoryInfo
import com.google.api.services.toolresults.model.PerfEnvironment
import com.google.api.services.toolresults.model.PerfMetricsSummary
import com.google.testing.model.ToolResultsStep
import ftl.api.PerfMetrics
import ftl.util.Duration
import com.google.api.services.toolresults.model.Duration as GoogleApiDuration

internal fun PerfMetrics.Identity.toClientModel(): ToolResultsStep =
ToolResultsStep()
.setHistoryId(historyId)
.setExecutionId(executionId)
.setStepId(stepId)
.setProjectId(projectId)

internal fun PerfMetricsSummary.toApiModel(): PerfMetrics.Summary = PerfMetrics.Summary(
appStartTime = appStartTime?.toApiModel(),
graphicsStats = graphicsStats?.toApiModel(),
perfEnvironment = perfEnvironment?.toApiModel(),
perfMetrics = perfMetrics.orEmpty(),
executionId = executionId.orEmpty(),
historyId = historyId.orEmpty(),
projectId = projectId.orEmpty(),
stepId = stepId.orEmpty()
)

private fun PerfEnvironment.toApiModel() = PerfMetrics.PerfEnvironment(
cpuInfo = cpuInfo?.toApiModel(),
memoryInfo = memoryInfo?.toApiModel()
)

private fun CPUInfo.toApiModel() = PerfMetrics.CPUInfo(cpuProcessor, cpuSpeedInGhz, numberOfCores)

private fun MemoryInfo.toApiModel() = PerfMetrics.MemoryInfo(memoryCapInKibibyte, memoryTotalInKibibyte)

private fun GraphicsStats.toApiModel() = PerfMetrics.GraphicsStats(
buckets = buckets?.mapNotNull { bucket -> bucket?.toApiModel() },
highInputLatencyCount = highInputLatencyCount,
jankyFrames = jankyFrames,
missedVsyncCount = missedVsyncCount,
p50Millis = p50Millis,
p90Millis = p90Millis,
p95Millis = p95Millis,
p99Millis = p99Millis,
slowBitmapUploadCount = slowBitmapUploadCount,
slowDrawCount = slowDrawCount,
slowUiThreadCount = slowUiThreadCount,
totalFrames = totalFrames
)

private fun GraphicsStatsBucket.toApiModel() = PerfMetrics.GraphicsStats.Bucket(frameCount, renderMillis)

private fun AppStartTime.toApiModel() = PerfMetrics.AppStartTime(
fullyDrawnTime = fullyDrawnTime?.toApiModel(),
initialDisplayTime = initialDisplayTime?.toApiModel()
)

private fun GoogleApiDuration.toApiModel() = Duration(seconds ?: 0, nanos)
70 changes: 70 additions & 0 deletions test_runner/src/main/kotlin/ftl/api/PerfMetrics.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package ftl.api

import ftl.adapter.GooglePerformanceMetricsFetch
import ftl.util.Duration

val fetchPerformanceMetrics: PerfMetrics.Fetch get() = GooglePerformanceMetricsFetch

object PerfMetrics {

data class Summary(
val appStartTime: AppStartTime? = null,
val graphicsStats: GraphicsStats? = null,
val perfEnvironment: PerfEnvironment? = null,
val perfMetrics: List<String>? = null,
val executionId: String? = null,
val historyId: String? = null,
val projectId: String? = null,
val stepId: String? = null,
)

data class GraphicsStats(
val buckets: List<Bucket>? = null,
val highInputLatencyCount: Long? = null,
val jankyFrames: Long? = null,
val missedVsyncCount: Long? = null,
val p50Millis: Long? = null,
val p90Millis: Long? = null,
val p95Millis: Long? = null,
val p99Millis: Long? = null,
val slowBitmapUploadCount: Long? = null,
val slowDrawCount: Long? = null,
val slowUiThreadCount: Long? = null,
val totalFrames: Long? = null,
) {
data class Bucket(
val frameCount: Long?,
val renderMillis: Long?,
)
}

data class AppStartTime(
val fullyDrawnTime: Duration? = null,
val initialDisplayTime: Duration? = null,
)

data class PerfEnvironment(
val cpuInfo: CPUInfo? = null,
val memoryInfo: MemoryInfo? = null,
)

data class CPUInfo(
val cpuProcessor: String? = null,
val cpuSpeedInGhz: Float? = null,
val numberOfCores: Int? = null,
)

data class MemoryInfo(
val memoryCapInKibibyte: Long? = null,
val memoryTotalInKibibyte: Long? = null,
)

data class Identity(
val executionId: String,
val historyId: String,
val projectId: String,
val stepId: String,
)

interface Fetch : (Identity) -> Summary
}
3 changes: 2 additions & 1 deletion test_runner/src/main/kotlin/ftl/gc/GcToolResults.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import com.google.api.services.toolresults.model.History
import com.google.api.services.toolresults.model.ListEnvironmentsResponse
import com.google.api.services.toolresults.model.ListStepsResponse
import com.google.api.services.toolresults.model.ListTestCasesResponse
import com.google.api.services.toolresults.model.PerfMetricsSummary
import com.google.api.services.toolresults.model.Step
import com.google.api.services.toolresults.model.TestCase
import com.google.testing.model.TestExecution
Expand Down Expand Up @@ -111,7 +112,7 @@ object GcToolResults {

fun getPerformanceMetric(
toolResultsStep: ToolResultsStep
) = service
): PerfMetricsSummary = service
.projects()
.histories()
.executions()
Expand Down
30 changes: 22 additions & 8 deletions test_runner/src/main/kotlin/ftl/reports/api/PerformanceMetrics.kt
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package ftl.reports.api

import com.google.api.services.toolresults.model.PerfMetricsSummary
import com.google.testing.model.TestExecution
import flank.common.logLn
import ftl.api.PerfMetrics
import ftl.api.RemoteStorage
import ftl.api.fetchPerformanceMetrics
import ftl.api.uploadToRemoteStorage
import ftl.args.IArgs
import ftl.client.google.AndroidCatalog
import ftl.gc.GcToolResults
import ftl.run.common.prettyPrint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
Expand All @@ -31,31 +32,44 @@ internal fun List<Pair<TestExecution, String>>.getAndUploadPerformanceMetrics(
.awaitAll()
}

private fun PerfMetricsSummary.save(resultsDir: String, args: IArgs) {
private fun PerfMetrics.Summary.save(resultsDir: String, args: IArgs) {
val configFilePath =
if (args.useLocalResultDir()) Paths.get(args.localResultDir, "performanceMetrics.json")
else Paths.get(args.localResultDir, resultsDir, "performanceMetrics.json")

configFilePath.parent.toFile().mkdirs()
Files.write(configFilePath, toPrettyString().toByteArray())

Files.write(configFilePath, prettyPrint.toJson(this).toByteArray())
}

private fun List<Pair<TestExecution, String>>.filterAndroidPhysicalDevicesRuns() = filterNot { (testExecution, _) ->
AndroidCatalog.isVirtualDevice(testExecution.environment.androidDevice, testExecution.projectId)
}

private fun TestExecution.getPerformanceMetric() = GcToolResults.getPerformanceMetric(toolResultsStep)
// TODO probably this mapping and test execution will be removed in #1756
private fun TestExecution.getPerformanceMetric(): PerfMetrics.Summary = fetchPerformanceMetrics(
PerfMetrics.Identity(
executionId = toolResultsStep.executionId,
historyId = toolResultsStep.historyId,
projectId = toolResultsStep.projectId,
stepId = toolResultsStep.stepId
)
)

private fun PerfMetricsSummary.upload(
private fun PerfMetrics.Summary.upload(
resultBucket: String,
resultDir: String
) = uploadPerformanceMetrics(this, resultBucket, resultDir)

internal fun uploadPerformanceMetrics(perfMetricsSummary: PerfMetricsSummary, resultsBucket: String, resultDir: String) =
internal fun uploadPerformanceMetrics(
perfMetricsSummary: PerfMetrics.Summary,
resultsBucket: String,
resultDir: String
) =
runCatching {
uploadToRemoteStorage(
RemoteStorage.Dir(resultsBucket, resultDir),
RemoteStorage.Data("performanceMetrics.json", perfMetricsSummary.toPrettyString().toByteArray())
RemoteStorage.Data("performanceMetrics.json", prettyPrint.toJson(perfMetricsSummary).toByteArray())
)
}.onFailure {
logLn("Cannot upload performance metrics ${it.message}")
Expand Down
5 changes: 4 additions & 1 deletion test_runner/src/main/kotlin/ftl/util/StopWatch.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ suspend fun measureTime(block: suspend () -> Unit) = StopWatch().run {
check()
}

data class Duration(val seconds: Long)
data class Duration(
val seconds: Long,
val nanos: Int? = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If seconds is in Long, should not nanos be Long too? It potentially can be a larger number.

Copy link
Contributor Author

@piotradamczyk5 piotradamczyk5 Apr 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is copy-pasted from google api 🙉

)

fun Duration.formatted(alignSeconds: Boolean = false): String {
val minutes = TimeUnit.SECONDS.toMinutes(seconds)
Expand Down
Loading