Deployment fixes for performance monitoring system (#4356)

This commit is contained in:
LepilkinaElena
2020-09-07 14:16:34 +03:00
committed by GitHub
parent 257590971c
commit 9b727bdd9d
13 changed files with 244 additions and 172 deletions
@@ -159,7 +159,9 @@ fun Array<String>.runCommand(workingDir: File = File("."),
waitFor(timeoutAmount, timeoutUnit)
}.inputStream.bufferedReader().readText()
} catch (e: Exception) {
error("Couldn't run command $this")
println("Couldn't run command ${this.joinToString(" ")}")
println(e.stackTrace.joinToString("\n"))
error(e.message!!)
}
}
+27 -4
View File
@@ -81,16 +81,27 @@ task slackSummary(type: RegressionsSummaryReporter) {
'Windows': "${rootBuildDirectory}/targetsResults/Windows 10.txt".toString()]
}
private def uploadBenchmarkResultToArtifactory(String fileName) {
private String getUploadedFile(String fileName) {
def teamcityConfig = System.getenv("TEAMCITY_BUILD_PROPERTIES_FILE")
if (teamcityConfig) {
def buildProperties = new Properties()
buildProperties.load(new FileInputStream(teamcityConfig))
def password = buildProperties.getProperty("artifactory.apikey")
def buildNumber = buildProperties.getProperty("build.number")
def target = System.getProperty("os.name").replaceAll("\\s", "")
return "${target}/${buildNumber}/${fileName}"
}
return null
}
private def uploadBenchmarkResultToArtifactory(String fileName) {
def teamcityConfig = System.getenv("TEAMCITY_BUILD_PROPERTIES_FILE")
if (teamcityConfig) {
def uploadedFile = getUploadedFile(fileName)
def buildProperties = new Properties()
buildProperties.load(new FileInputStream(teamcityConfig))
def password = buildProperties.getProperty("artifactory.apikey")
MPPTools.uploadFileToArtifactory("${artifactoryUrl}", "${artifactoryRepo}",
"${target}/${buildNumber}/${fileName}", "${buildDir.absolutePath}/${fileName}", password)
uploadedFile, "${buildDir.absolutePath}/${fileName}", password)
}
}
@@ -161,6 +172,11 @@ task registerExternalBenchmarks {
task registerBuild(type: BuildRegister) {
onlyBranch = project.findProperty('kotlin.register.branch')
def uploadedFile = getUploadedFile(nativeJson)
if (uploadedFile != null) {
println("Use file from Artifacrory $uploadedFile")
fileWithResult = uploadedFile
}
// Get bundle size.
bundleSize = null
if (project.findProperty('kotlin.bundleBuild') != null) {
@@ -172,7 +188,14 @@ task registerBuild(type: BuildRegister) {
task registerExternalBuild(type: BuildRegister) {
onlyBranch = project.findProperty('kotlin.register.branch')
fileWithResult = externalBenchmarksReport
def uploadedFile = getUploadedFile(externalBenchmarksReport)
if (uploadedFile != null) {
println("Use file from Artifacrory $uploadedFile")
fileWithResult = uploadedFile
} else {
fileWithResult = externalBenchmarksReport
}
}
registerExternalBenchmarks.finalizedBy registerExternalBuild
+11
View File
@@ -154,6 +154,17 @@ kotlin {
}
}
}
js {
browser {
distribution {
directory = new File("$projectDir/web/")
}
dceTask {
keep 'benchmarksAnalyzer.main_kand9s$'
}
}
}
}
def getMingwPath() {
+3 -3
View File
@@ -292,9 +292,9 @@
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc="
},
"kotlin": {
"version": "1.4.0-M3",
"resolved": "https://registry.npmjs.org/kotlin/-/kotlin-1.4.0-M3.tgz",
"integrity": "sha512-y6dl1t36tI1hAJlJTxlOB/fj/0iOga+XC8+eg9hUSTu5BDJ9MIUR7XnA8Ld+tQA9IakAPaUmgZZfX2lsHaAdIw=="
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/kotlin/-/kotlin-1.4.0.tgz",
"integrity": "sha512-q+Ts9Xr72eT3QkmLjHDObPAHbsKbZ/Vn0ozqrNNr7UbtpLxa26+Hn8ILORPLz1UIUTespZyfXztXJ7AO5Xl/Gg=="
},
"media-typer": {
"version": "0.3.0",
+1 -1
View File
@@ -11,7 +11,7 @@
"debug": "~4.1.1",
"ejs": "~2.6.1",
"express": "~4.16.4",
"kotlin": "~1.4.0-M3",
"kotlin": "~1.4.0",
"node-fetch": "~2.6.0"
}
}
@@ -125,14 +125,11 @@ enum class ElasticSearchType {
}
abstract class ElasticSearchIndex(val indexName: String, val connector: ElasticSearchConnector) {
var nextId = 0L
// Insert data.
fun insert(data: JsonSerializable): Promise<String> {
val description = data.toJson()
val writePath = "$indexName/_doc/$nextId?pretty"
nextId++
return connector.request(RequestMethod.PUT, writePath, body = description)
val writePath = "$indexName/_doc/"
return connector.request(RequestMethod.POST, writePath, body = description)
}
// Delete data.
@@ -148,25 +145,6 @@ abstract class ElasticSearchIndex(val indexName: String, val connector: ElasticS
return connector.request(RequestMethod.POST, path, body = requestJson)
}
init {
// Get latest id for index.
val queryBody = """{
"_source": ["_id"],
"size": 1,
"query": {
"match_all": {}
}
}"""
search(queryBody, listOf("hits.total.value")).then { responseString ->
val response = JsonTreeParser.parse(responseString).jsonObject
val value = response.getObjectOrNull("hits")?.getObjectOrNull("total")?.getPrimitiveOrNull("value")?.content
?: error("Error response from ElasticSearch:\n$responseString")
nextId = value.toLong()
}.catch { errorMessage ->
error(errorMessage.message ?: "Failed getting next id for index $indexName")
}
}
abstract val mapping: Map<String, ElasticSearchType>
val mappingDescription: String
@@ -35,6 +35,7 @@ class UrlNetworkConnector(private val host: String, private val port: Int? = nul
)
).then { response ->
if (!response.ok) {
println(JSON.stringify(response))
errorHandler(fullUrl, response)
} else {
response.text()
@@ -97,13 +97,13 @@ class BenchmarksIndexesDispatcher(connector: ElasticSearchConnector, val feature
}
// Get benchmarks values of needed metric for choosen build number.
fun getSamples(metricName: String, featureValue: String = "", samples: List<String>,
fun getSamples(metricName: String, featureValue: String = "", samples: List<String>, buildsCountToShow: Int,
buildNumbers: Iterable<String>? = null,
normalize: Boolean = false): Promise<List<Pair<String, Array<Double?>>>> {
val queryDescription = """
{
"_source": ["buildNumber"],
"size": 1000,
"size": ${samples.size * buildsCountToShow},
"query": {
"bool": {
"must": [
@@ -255,7 +255,7 @@ class BenchmarksIndexesDispatcher(connector: ElasticSearchConnector, val feature
// Get geometric mean for benchmarks values of needed metric.
fun getGeometricMean(metricName: String, featureValue: String = "",
buildNumbers: Iterable<String>? = null, normalize: Boolean = false,
excludeNames: List<String> = emptyList()): Promise<List<Pair<String, List<Double>>>> {
excludeNames: List<String> = emptyList()): Promise<List<Pair<String, List<Double?>>>> {
// Filter only with metric or also with names.
val filterBenchmarks = if (excludeNames.isEmpty())
"""
@@ -345,9 +345,9 @@ class BenchmarksIndexesDispatcher(connector: ElasticSearchConnector, val feature
.getObject("metric_samples")
.getObject("buckets")
.getObject("samples")
.getObject("geom_mean")
.getPrimitive("value")
.double
.getObjectOrNull("geom_mean")
?.getPrimitive("value")
?.double
)
}
} ?: listOf("golden" to listOf(aggregations
@@ -355,9 +355,9 @@ class BenchmarksIndexesDispatcher(connector: ElasticSearchConnector, val feature
.getObject("metric_samples")
.getObject("buckets")
.getObject("samples")
.getObject("geom_mean")
.getPrimitive("value")
.double
.getObjectOrNull("geom_mean")
?.getPrimitive("value")
?.double
)
)
}
@@ -35,11 +35,12 @@ internal fun deleteBuildInfo(agentInfo: String, buildInfoIndex: ElasticSearchInd
// Get infromation about builds details from database.
internal fun getBuildsDescription(type: String?, branch: String?, agentInfo: String, buildInfoIndex: ElasticSearchIndex,
buildsCountToShow: Int, beforeDate: String?, afterDate: String?,
onlyNumbers: Boolean = false): Promise<JsonArray> {
val queryDescription = """
{ "size": 10000,
{ "size": $buildsCountToShow,
${if (onlyNumbers) """"_source": ["buildNumber"],""" else ""}
"sort": {"_id": "desc" },
"sort": {"startTime": "desc" },
"query": {
"bool": {
"must": [
@@ -51,6 +52,16 @@ internal fun getBuildsDescription(type: String?, branch: String?, agentInfo: Str
}
"""
} ?: ""}
${beforeDate?.let {
""",
{ "range": { "startTime": { "lt": "$it" } } }
"""
} ?: ""}
${afterDate?.let {
""",
{ "range": { "startTime": { "gt": "$it" } } }
"""
} ?: ""}
${branch?.let {
""",
{"match": { "branch": "$it" }}
@@ -61,6 +72,7 @@ internal fun getBuildsDescription(type: String?, branch: String?, agentInfo: Str
}
}
""".trimIndent()
return buildInfoIndex.search(queryDescription, listOf("hits.hits._source")).then { responseString ->
val dbResponse = JsonTreeParser.parse(responseString).jsonObject
dbResponse.getObjectOrNull("hits")?.getArrayOrNull("hits") ?: error("Wrong response:\n$responseString")
@@ -93,14 +105,18 @@ suspend fun buildExists(buildInfo: BuildInfo, buildInfoIndex: ElasticSearchIndex
}
// Get builds numbers corresponding to machine and branch.
fun getBuildsNumbers(type: String?, branch: String?, agentInfo: String, buildInfoIndex: ElasticSearchIndex) =
getBuildsDescription(type, branch, agentInfo, buildInfoIndex, true).then { responseArray ->
fun getBuildsNumbers(type: String?, branch: String?, agentInfo: String, buildsCountToShow: Int,
buildInfoIndex: ElasticSearchIndex, beforeDate: String? = null, afterDate: String? = null) =
getBuildsDescription(type, branch, agentInfo, buildInfoIndex, buildsCountToShow, beforeDate, afterDate, true)
.then { responseArray ->
responseArray.map { (it as JsonObject).getObject("_source").getPrimitive("buildNumber").content }
}
// Get full builds information corresponding to machine and branch.
fun getBuildsInfo(type: String?, branch: String?, agentInfo: String, buildInfoIndex: ElasticSearchIndex) =
getBuildsDescription(type, branch, agentInfo, buildInfoIndex).then { responseArray ->
fun getBuildsInfo(type: String?, branch: String?, agentInfo: String, buildsCountToShow: Int,
buildInfoIndex: ElasticSearchIndex, beforeDate: String? = null,
afterDate: String? = null) =
getBuildsDescription(type, branch, agentInfo, buildInfoIndex, buildsCountToShow, beforeDate, afterDate).then { responseArray ->
responseArray.map { BuildInfo.create((it as JsonObject).getObject("_source")) }
}
@@ -17,6 +17,8 @@ object CachableResponseDispatcher {
// Storage of cached responses.
private val cachedResponses = mutableMapOf<String, CachedResponse>()
private val cacheMaxSize = 200
// Get response. If response isn't cached, use provided action to get response.
fun getResponse(request: dynamic, response: dynamic,
action: (success: (result: Any) -> Unit, reject: () -> Unit) -> Unit) {
@@ -31,7 +33,9 @@ object CachableResponseDispatcher {
response.json(it.cachedResult)
} ?: run {
action({ result: Any ->
cachedResponses[request.url] = CachedResponse(result, TimeSource.Monotonic.markNow())
if (cachedResponses.size >= cacheMaxSize) {
cachedResponses[request.url] = CachedResponse(result, TimeSource.Monotonic.markNow())
}
response.json(result)
}, { response.sendStatus(400) })
}
@@ -39,13 +39,19 @@ external object AWSInstance {
val handleRequest: dynamic
}
class SharedIniFileCredentials(options: Map<String, String>) {
interface Credentials
class SharedIniFileCredentials(options: Map<String, String>): Credentials {
val accessKeyId: String
}
class EnvironmentCredentials(envPrefix: String): Credentials {
val accessKeyId: String
}
class Signers() {
class V4(request: HttpRequest, subsystem: String) {
fun addAuthorization(credentials: SharedIniFileCredentials, date: Date)
fun addAuthorization(credentials: Credentials, date: Date)
}
}
}
@@ -58,6 +64,7 @@ class AWSNetworkConnector : NetworkConnector() {
override fun <T : String?> sendBaseRequest(method: RequestMethod, path: String, user: String?, password: String?,
acceptJsonContentType: Boolean, body: String?,
errorHandler: (url: String, response: dynamic) -> Nothing?): Promise<T> {
val useEnvironmentCredentials = true // For easy test on localhost change to false.
val AWSEndpoint = AWSInstance.Endpoint(AWSDomain)
var request = AWSInstance.HttpRequest(AWSEndpoint, AWSRegion)
request.method = method.toString()
@@ -71,7 +78,8 @@ class AWSNetworkConnector : NetworkConnector() {
request.headers["Content-Length"] = js("Buffer").byteLength(request.body)
}
val credentials = AWSInstance.SharedIniFileCredentials(mapOf<String, String>())
val credentials = if (useEnvironmentCredentials) AWSInstance.EnvironmentCredentials("AWS")
else AWSInstance.SharedIniFileCredentials(mapOf<String, String>())
val signer = AWSInstance.Signers.V4(request, "es")
signer.addAuthorization(credentials, Date())
@@ -25,6 +25,8 @@ operator fun <K, V> Map<K, V>?.get(key: K) = this?.get(key)
fun getArtifactoryHeader(artifactoryApiKey: String) = Pair("X-JFrog-Art-Api", artifactoryApiKey)
external fun decodeURIComponent(url: String): String
// Convert saved old report to expected new format.
internal fun convertToNewFormat(data: JsonObject): List<Any> {
val env = Environment.create(data.getRequiredField("env"))
@@ -183,7 +185,7 @@ fun router() {
val goldenIndex = GoldenResultsIndex(connector)
val buildInfoIndex = BuildInfoIndex(connector)
router.get("/createMapping") { request, response ->
router.get("/createMapping") { _, response ->
buildInfoIndex.createMapping().then { _ ->
response.sendStatus(200)
}.catch { _ ->
@@ -220,7 +222,10 @@ fun router() {
register.sendTeamCityRequest(register.changesListUrl, true).then { changes ->
val commitsList = CommitsList(JsonTreeParser.parse(changes))
// Get artifact.
register.sendTeamCityRequest(register.teamCityArtifactsUrl).then { resultsContent ->
val content = if(register.fileWithResult.contains("/"))
UrlNetworkConnector(artifactoryUrl).sendRequest(RequestMethod.GET, register.fileWithResult)
else register.sendTeamCityRequest(register.teamCityArtifactsUrl)
content.then { resultsContent ->
launch {
val reportData = JsonTreeParser.parse(resultsContent)
val reports = if (reportData is JsonArray) {
@@ -292,6 +297,9 @@ fun router() {
var branch: String? = null
var type: String? = null
var buildsCountToShow = 200
var beforeDate: String? = null
var afterDate: String? = null
if (request.query != undefined) {
if (request.query.branch != undefined) {
branch = request.query.branch
@@ -299,9 +307,19 @@ fun router() {
if (request.query.type != undefined) {
type = request.query.type
}
if (request.query.count != undefined) {
buildsCountToShow = request.query.count.toString().toInt()
}
if (request.query.before != undefined) {
beforeDate = decodeURIComponent(request.query.before)
}
if (request.query.after != undefined) {
afterDate = decodeURIComponent(request.query.after)
}
}
getBuildsInfo(type, branch, target, buildInfoIndex).then { buildsInfo ->
getBuildsInfo(type, branch, target, buildsCountToShow, buildInfoIndex, beforeDate, afterDate)
.then { buildsInfo ->
val buildNumbers = buildsInfo.map { it.buildNumber }
// Get number of failed benchmarks for each build.
benchmarksDispatcher.getFailuresNumber(target, buildNumbers).then { failures ->
@@ -331,6 +349,9 @@ fun router() {
var branch: String? = null
var type: String? = null
var excludeNames: List<String> = emptyList()
var buildsCountToShow = 200
var beforeDate: String? = null
var afterDate: String? = null
// Parse parameters from request if it exists.
if (request.query != undefined) {
@@ -352,9 +373,18 @@ fun router() {
if (request.query.exclude != undefined) {
excludeNames = request.query.exclude.toString().split(",").map { it.trim() }
}
if (request.query.count != undefined) {
buildsCountToShow = request.query.count.toString().toInt()
}
if (request.query.before != undefined) {
beforeDate = decodeURIComponent(request.query.before)
}
if (request.query.after != undefined) {
afterDate = decodeURIComponent(request.query.after)
}
}
getBuildsNumbers(type, branch, target, buildInfoIndex).then { buildNumbers ->
getBuildsNumbers(type, branch, target, buildsCountToShow, buildInfoIndex, beforeDate, afterDate).then { buildNumbers ->
if (aggregation == "geomean") {
// Get geometric mean for samples.
benchmarksDispatcher.getGeometricMean(metric, target, buildNumbers, normalize,
@@ -366,7 +396,8 @@ fun router() {
reject()
}
} else {
benchmarksDispatcher.getSamples(metric, target, samples, buildNumbers, normalize).then { geoMeansValues ->
benchmarksDispatcher.getSamples(metric, target, samples, buildsCountToShow, buildNumbers, normalize)
.then { geoMeansValues ->
success(orderedValues(geoMeansValues, { it -> it.first }, branch == "master"))
}.catch {
println("Error during getting samples")
@@ -412,6 +443,7 @@ fun router() {
if (request.query != undefined) {
if (request.query.buildNumber != undefined) {
buildNumber = request.query.buildNumber
buildNumber = request.query.buildNumber
}
}
getBuildsInfoFromArtifactory(targetPathName).then { buildInfo ->
@@ -448,10 +480,13 @@ fun router() {
val buildInfoRecord = BuildInfo(currentBuildNumber, infoParts[1], infoParts[2],
CommitsList.parse(infoParts[4]), infoParts[3], target)
val externalJsonReport = artifactoryUrlConnector.sendOptionalRequest(RequestMethod.GET, accessExternalFileUrl).await()
val externalJsonReport = artifactoryUrlConnector.sendOptionalRequest(RequestMethod.GET, accessExternalFileUrl)
.await()
buildInfoIndex.insert(buildInfoRecord).then { _ ->
println("[BUILD INFO] Success insert build number ${buildInfoRecord.buildNumber}")
externalJsonReport?.let {
var externalReports = convert(externalJsonReport, currentBuildNumber, target)
var externalReports = convert(externalJsonReport.replace("circlet_iosX64", "SpaceFramework_iosX64"),
currentBuildNumber, target)
externalReports.forEach { externalReport ->
val extrenalAdditionalReport = SummaryBenchmarksReport(externalReport)
.getBenchmarksReport().normalizeBenchmarksSet(goldenResults)
@@ -35,36 +35,12 @@ fun sendGetRequest(url: String) = window.fetch(url, RequestInit("GET")).then { r
response.text()
}.then { text -> text }
// Get groups of builds for different zoom values.
fun getBuildsGroup(builds: List<Build?>) = buildsNumberToShow?.let {
val buildsGroups = builds.chunked(buildsNumberToShow!!)
val expectedGroup = buildsGroups.size - 1 + stageToShow
val index = when {
expectedGroup < 0 -> 0
expectedGroup >= buildsGroups.size -> buildsGroups.size - 1
else -> expectedGroup
}
buildsGroups[index]
} ?: builds
// Get data for chart in needed format.
fun getChartData(labels: List<String>, valuesList: Collection<List<*>>, stageToShow: Int = 0,
buildsNumber: Int? = null, classNames: Array<String>? = null): dynamic {
fun getChartData(labels: List<String>, valuesList: Collection<List<*>>,
classNames: Array<String>? = null): dynamic {
val chartData: dynamic = object {}
// Show only some part of data.
val (labelsData, valuesData) = buildsNumber?.let {
val labelsGroups = labels.chunked(buildsNumber)
val valuesListGroups = valuesList.map { it.chunked(buildsNumber) }
val expectedGroup = labelsGroups.size - 1 + stageToShow
val index = when {
expectedGroup < 0 -> 0
expectedGroup >= labelsGroups.size -> labelsGroups.size - 1
else -> expectedGroup
}
Pair(labelsGroups[index], valuesListGroups.map { it[index] })
} ?: Pair(labels, valuesList)
chartData["labels"] = labelsData.toTypedArray()
chartData["series"] = valuesData.mapIndexed { index, it ->
chartData["labels"] = labels.toTypedArray()
chartData["series"] = valuesList.mapIndexed { index, it ->
val series: dynamic = object {}
series["data"] = it.toTypedArray()
classNames?.let { series["className"] = classNames[index] }
@@ -83,7 +59,7 @@ fun getChartOptions(samples: Array<String>, yTitle: String, classNames: Array<St
val axisXObject: dynamic = object {}
axisXObject["offset"] = 40
axisXObject["labelInterpolationFnc"] = { value, index, labels ->
val labelsCount = 30
val labelsCount = 20
val skipNumber = ceil((labels.length as Int).toDouble() / labelsCount).toInt()
if (skipNumber > 1) {
if (index % skipNumber == 0) value else null
@@ -126,9 +102,8 @@ fun customizeChart(chart: dynamic, chartContainer: String, jquerySelector: dynam
chart.on("draw", { data ->
var element = data.element
if (data.type == "point") {
val buildsGroup = getBuildsGroup(builds)
val pointSize = 12
val currentBuild = buildsGroup.get(data.index)
val currentBuild = builds.get(data.index)
currentBuild?.let { currentBuild ->
// Higlight builds with failures.
if (currentBuild.failuresNumber > 0) {
@@ -154,7 +129,7 @@ fun customizeChart(chart: dynamic, chartContainer: String, jquerySelector: dynam
var shift = 1
var previousBuild: Build? = null
while (previousBuild == null && data.index - shift >= 0) {
previousBuild = buildsGroup.get(data.index - shift)
previousBuild = builds.get(data.index - shift)
shift++
}
val linkToDetailedInfo = "https://kotlin-native-performance.labs.jb.gg/?report=" +
@@ -204,21 +179,19 @@ fun customizeChart(chart: dynamic, chartContainer: String, jquerySelector: dynam
})
}
var stageToShow = 0
var buildsNumberToShow: Int = 200
var beforeDate: String? = null
var afterDate: String? = null
var buildsNumberToShow: Int? = null
external fun decodeURIComponent(url: String): String
external fun encodeURIComponent(url: String): String
fun waitForElements(elements: Iterable<Any?>, action: () -> Unit) {
if (elements.filterNotNull().size == elements.count())
action()
else window.setTimeout(waitForElements(elements, action), 500)
}
fun getDatesComponents() = "${beforeDate?.let {"&before=${encodeURIComponent(it)}"} ?: ""}" +
"${afterDate?.let {"&after=${encodeURIComponent(it)}"} ?: ""}"
fun main(args: Array<String>) {
val serverUrl = "https://kotlin-native-perf-summary.labs.jb.gg"
buildsNumberToShow = null
stageToShow = 0
val zoomRatio = 3
val zoomRatio = 2
// Get parameters from request.
val url = window.location.href
@@ -232,6 +205,10 @@ fun main(args: Array<String>) {
}
}
buildsNumberToShow = parameters["count"]?.toInt() ?: buildsNumberToShow
beforeDate = parameters["before"]?.let{ decodeURIComponent(it)}
afterDate = parameters["after"]?.let{ decodeURIComponent(it)}
// Get branches.
val branchesUrl = "$serverUrl/branches"
sendGetRequest(branchesUrl).then { response ->
@@ -256,7 +233,8 @@ fun main(args: Array<String>) {
autocompleteParameters["onSelect"] = { suggestion ->
if (suggestion.value != parameters["build"]) {
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}" +
"${if ((suggestion.value as String).isEmpty()) "" else "&build=${suggestion.value}"}"
"${if ((suggestion.value as String).isEmpty()) "" else "&build=${suggestion.value}"}&count=$buildsNumberToShow" +
getDatesComponents()
window.location.href = newLink
}
}
@@ -265,7 +243,8 @@ fun main(args: Array<String>) {
val newValue = js("$(this).val()").toString()
if (newValue.isEmpty() || newValue in buildsNumbers) {
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}" +
"${if (newValue.isEmpty()) "" else "&build=$newValue"}"
"${if (newValue.isEmpty()) "" else "&build=$newValue"}&count=$buildsNumberToShow" +
getDatesComponents()
window.location.href = newLink
}
})
@@ -282,7 +261,7 @@ fun main(args: Array<String>) {
val newValue = js("$(this).val()")
if (newValue != parameters["target"]) {
val newLink = "http://${window.location.host}/?target=$newValue&type=${parameters["type"]}&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}"
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow"
window.location.href = newLink
}
})
@@ -290,7 +269,7 @@ fun main(args: Array<String>) {
val newValue = js("$(this).val()")
if (newValue != parameters["type"]) {
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=$newValue&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}"
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow"
window.location.href = newLink
}
})
@@ -298,35 +277,35 @@ fun main(args: Array<String>) {
val newValue = js("$(this).val()")
if (newValue != parameters["branch"]) {
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}&branch=$newValue" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}"
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow"
window.location.href = newLink
}
})
val platformSpecificBenchs = if (parameters["target"] == "Mac_OS_X") ",FrameworkBenchmarksAnalyzer,circlet_iosX64" else
val platformSpecificBenchs = if (parameters["target"] == "Mac_OS_X") ",FrameworkBenchmarksAnalyzer,SpaceFramework_iosX64" else
if (parameters["target"] == "Linux") ",kotlinx.coroutines" else ""
// Collect information for charts library.
val valuesToShow = mapOf("EXECUTION_TIME" to arrayOf(mapOf(
val valuesToShow = mapOf("EXECUTION_TIME" to listOf(mapOf(
"normalize" to "true"
)),
"COMPILE_TIME" to arrayOf(mapOf(
"COMPILE_TIME" to listOf(mapOf(
"samples" to "HelloWorld,Videoplayer$platformSpecificBenchs",
"agr" to "samples"
)),
"CODE_SIZE" to arrayOf(mapOf(
"CODE_SIZE" to listOf(mapOf(
"normalize" to "true",
"exclude" to if (parameters["target"] == "Linux")
"kotlinx.coroutines"
else if (parameters["target"] == "Mac_OS_X")
"circlet_iosX64"
"SpaceFramework_iosX64"
else ""
), mapOf(
), if (platformSpecificBenchs.isNotEmpty()) mapOf(
"normalize" to "true",
"agr" to "samples",
"samples" to platformSpecificBenchs.removePrefix(",")
)),
"BUNDLE_SIZE" to arrayOf(mapOf("samples" to "KotlinNative",
) else null).filterNotNull(),
"BUNDLE_SIZE" to listOf(mapOf("samples" to "KotlinNative",
"agr" to "samples"))
)
@@ -344,13 +323,27 @@ fun main(args: Array<String>) {
var bundleSizeChart: dynamic = null
val descriptionUrl = "$serverUrl/buildsDesc/${parameters["target"]}?type=${parameters["type"]}" +
"${if (parameters["branch"] != "all") "&branch=${parameters["branch"]}" else ""}"
"${if (parameters["branch"] != "all") "&branch=${parameters["branch"]}" else ""}&count=$buildsNumberToShow" +
getDatesComponents()
val metricUrl = "$serverUrl/metricValue/${parameters["target"]}/"
// Get builds description.
val buildsInfoPromise = sendGetRequest(descriptionUrl).then { response ->
val buildsInfo = response as String
val data = JsonTreeParser.parse(buildsInfo)
if (data !is JsonArray) {
error("Response is expected to be an array.")
}
data.jsonArray.map {
val element = it as JsonElement
if (element.isNull) null else Build.create(element as JsonObject)
}
}
// Send requests to get all needed metric values.
valuesToShow.map { (metric, arrayOfSettings) ->
val resultValues = arrayOfSettings.map { settings ->
valuesToShow.map { (metric, listOfSettings) ->
val resultValues = listOfSettings.map { settings ->
val getParameters = with(StringBuilder()) {
if (settings.isNotEmpty()) {
append("?")
@@ -372,7 +365,7 @@ fun main(args: Array<String>) {
if (parameters["type"] != "all")
(if (getParameters.isEmpty() && branchParameter.isEmpty()) "?" else "&") + "type=${parameters["type"]}"
else ""
}"
}&count=$buildsNumberToShow${getDatesComponents()}"
sendGetRequest(url)
}.toTypedArray()
@@ -397,33 +390,47 @@ fun main(args: Array<String>) {
"COMPILE_TIME" -> {
compileData = labels to values.map { it.map { it?.let { it / 1000 } } }
compileChart = Chartist.Line("#compile_chart",
getChartData(labels, compileData.second, stageToShow, buildsNumberToShow),
getChartData(labels, compileData.second),
getChartOptions(valuesToShow["COMPILE_TIME"]!![0]!!["samples"]!!.split(',').toTypedArray(),
"Time, milliseconds"))
buildsInfoPromise.then { builds ->
customizeChart(compileChart, "compile_chart", js("$(\"#compile_chart\")"), builds, parameters)
compileChart.update(getChartData(compileData.first, compileData.second))
}
}
"EXECUTION_TIME" -> {
execData = labels to values
execChart = Chartist.Line("#exec_chart",
getChartData(labels, execData.second, stageToShow, buildsNumberToShow),
getChartData(labels, execData.second),
getChartOptions(arrayOf("Geometric Mean"), "Normalized time"))
buildsInfoPromise.then { builds ->
customizeChart(execChart, "exec_chart", js("$(\"#exec_chart\")"), builds, parameters)
execChart.update(getChartData(execData.first, execData.second))
}
}
"CODE_SIZE" -> {
codeSizeData = labels to values
codeSizeChart = Chartist.Line("#codesize_chart",
getChartData(labels, codeSizeData.second,
stageToShow, buildsNumberToShow, sizeClassNames),
getChartData(labels, codeSizeData.second),
getChartOptions(arrayOf("Geometric Mean") + platformSpecificBenchs.split(',')
.filter { it.isNotEmpty() },
"Normalized size",
arrayOf("ct-series-4", "ct-series-5", "ct-series-6")))
buildsInfoPromise.then { builds ->
customizeChart(codeSizeChart, "codesize_chart", js("$(\"#codesize_chart\")"), builds, parameters)
codeSizeChart.update(getChartData(codeSizeData.first, codeSizeData.second, sizeClassNames))
}
}
"BUNDLE_SIZE" -> {
bundleSizeData = labels to values.map { it.map { it?.let { it.toInt() / 1024 / 1024 } } }
bundleSizeChart = Chartist.Line("#bundlesize_chart",
getChartData(labels,
bundleSizeData.second, stageToShow,
buildsNumberToShow, sizeClassNames),
bundleSizeData.second, sizeClassNames),
getChartOptions(arrayOf("Bundle size"), "Size, MB", arrayOf("ct-series-4")))
buildsInfoPromise.then { builds ->
customizeChart(bundleSizeChart, "bundlesize_chart", js("$(\"#bundlesize_chart\")"), builds, parameters)
bundleSizeChart.update(getChartData(bundleSizeData.first, bundleSizeData.second, sizeClassNames))
}
}
else -> error("No chart for metric $metric")
}
@@ -433,69 +440,56 @@ fun main(args: Array<String>) {
// Update all charts with using same data.
val updateAllCharts: () -> Unit = {
execChart.update(getChartData(execData.first, execData.second, stageToShow, buildsNumberToShow))
compileChart.update(getChartData(compileData.first, compileData.second, stageToShow, buildsNumberToShow))
codeSizeChart.update(getChartData(codeSizeData.first, codeSizeData.second, stageToShow, buildsNumberToShow, sizeClassNames))
bundleSizeChart.update(getChartData(bundleSizeData.first, bundleSizeData.second, stageToShow, buildsNumberToShow, sizeClassNames))
}
// Get builds description.
sendGetRequest(descriptionUrl).then { response ->
val buildsInfo = response as String
val data = JsonTreeParser.parse(buildsInfo)
if (data !is JsonArray) {
error("Response is expected to be an array.")
}
val builds = data.jsonArray.map {
val element = it as JsonElement
if (element.isNull) null else Build.create(element as JsonObject)
}
waitForElements(listOf(execChart, compileChart, codeSizeChart, bundleSizeChart)) {
customizeChart(execChart, "exec_chart", js("$(\"#exec_chart\")"), builds, parameters)
customizeChart(compileChart, "compile_chart", js("$(\"#compile_chart\")"), builds, parameters)
customizeChart(codeSizeChart, "codesize_chart", js("$(\"#codesize_chart\")"), builds, parameters)
customizeChart(bundleSizeChart, "bundlesize_chart", js("$(\"#bundlesize_chart\")"), builds, parameters)
updateAllCharts()
}
execChart.update(getChartData(execData.first, execData.second))
compileChart.update(getChartData(compileData.first, compileData.second))
codeSizeChart.update(getChartData(codeSizeData.first, codeSizeData.second, sizeClassNames))
bundleSizeChart.update(getChartData(bundleSizeData.first, bundleSizeData.second, sizeClassNames))
}
js("$('#plusBtn')").click({
buildsNumberToShow = buildsNumberToShow?.let {
if (it / zoomRatio > zoomRatio) {
it / zoomRatio
buildsNumberToShow =
if (buildsNumberToShow / zoomRatio > zoomRatio) {
buildsNumberToShow / zoomRatio
} else {
it
buildsNumberToShow
}
} ?: execData.first.size / zoomRatio
updateAllCharts()
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow" +
getDatesComponents()
window.location.href = newLink
Unit
})
js("$('#minusBtn')").click({
buildsNumberToShow = buildsNumberToShow?.let {
if (it * zoomRatio <= execData.first.size) {
it * zoomRatio
} else {
null
}
}
updateAllCharts()
buildsNumberToShow = buildsNumberToShow * zoomRatio
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow" +
getDatesComponents()
window.location.href = newLink
Unit
})
js("$('#prevBtn')").click({
buildsNumberToShow?.let {
val bottomBorder = -execData.first.size / (buildsNumberToShow as Int)
if (stageToShow - 1 > bottomBorder) {
stageToShow--
}
} ?: run { stageToShow = 0 }
updateAllCharts()
buildsInfoPromise.then { builds ->
beforeDate = builds.firstOrNull()?.startTime
afterDate = null
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow" +
"${beforeDate?.let {"&before=${encodeURIComponent(it)}"} ?: ""}"
window.location.href = newLink
}
})
js("$('#nextBtn')").click({
if (stageToShow + 1 <= 0) {
stageToShow++
buildsInfoPromise.then { builds ->
beforeDate = null
afterDate = builds.lastOrNull()?.startTime
val newLink = "http://${window.location.host}/?target=${parameters["target"]}&type=${parameters["type"]}&branch=${parameters["branch"]}" +
"${if (parameters["build"]!!.isEmpty()) "" else "&build=${parameters["build"]}"}&count=$buildsNumberToShow" +
"${afterDate?.let {"&after=${encodeURIComponent(it)}"} ?: ""}"
window.location.href = newLink
}
updateAllCharts()
})
// Auto reload.