1+ import com.gette.debugger.Protos
2+ import com.gette.debugger.Protos.ExecutionEnvironmentVariables
3+ import com.gette.debugger.Protos.ExecutionInputs
14import com.google.devtools.build.lib.exec.Protos.SpawnExec
5+ import kotlinx.cli.ArgParser
6+ import kotlinx.cli.ArgType
7+ import kotlinx.cli.required
28import java.io.File
9+ import java.io.FileOutputStream
310import java.io.InputStream
411import java.security.MessageDigest
512
613val sha256 = MessageDigest .getInstance(" SHA-256" )
714
815fun main (args : Array <String >) {
9- mergeSpawnExecs(" D:/exec2.log" , " D:/exec3.log" )
16+ val parser = ArgParser (" debugger" )
17+ val first_exec_log by parser.option(
18+ ArgType .String ,
19+ shortName = " first" ,
20+ description = " Path to first execution log"
21+ ).required()
22+ val second_exec_log by parser.option(
23+ ArgType .String ,
24+ shortName = " second" ,
25+ description = " Path to second execution log"
26+ ).required()
27+ val output_binary_log by parser.option(
28+ ArgType .String ,
29+ shortName = " ob" ,
30+ description = " Path to save output log in binary format"
31+ )
32+ val output_text_log by parser.option(
33+ ArgType .String ,
34+ shortName = " o" ,
35+ description = " Path to save output log in text format"
36+ )
37+ parser.parse(args)
38+ mergeSpawnExecs(first_exec_log, second_exec_log, output_binary_log, output_text_log)
1039}
1140
12- fun mergeSpawnExecs (pathA : String , pathB : String ) {
13- val mergedSpawnExecs: HashMap <String , MergedSpawnExec > = HashMap ()
41+ fun mergeSpawnExecs (pathA : String , pathB : String , outputBinaryLogPath : String? , outputTextLogPath : String? ) {
42+ val aExecCounter: Int
43+ var bExecCounter: Int = 0
44+ var cacheHits: Int = 0
45+ val aSpawnExecs: HashMap <String , SpawnExec > = HashMap ()
1446 var ins = File (pathA).inputStream()
15-
1647 while (ins.available() > 0 ) {
1748 val spawnExec = getNextSpawnExec(ins)
18- val mergedSpawnExec: MergedSpawnExec =
19- MergedSpawnExec (
20- spawnExec.second.listedOutputsList,
21- spawnExec.second.environmentVariablesList.associate { it.name to it.value },
22- spawnExec.second.inputsList.associate { it.path to it.digest }
23- )
24- mergedSpawnExecs[spawnExec.first] = mergedSpawnExec
49+ aSpawnExecs[spawnExec.first] = spawnExec.second
2550 }
51+ aExecCounter = aSpawnExecs.size
52+ val textLogWriter = if (! outputTextLogPath.isNullOrEmpty()) File (outputTextLogPath) else null
53+ val binaryLogFile = if (! outputBinaryLogPath.isNullOrEmpty()) File (outputBinaryLogPath) else null
2654
2755 ins = File (pathB).inputStream()
2856 while (ins.available() > 0 ) {
2957 val spawnExec = getNextSpawnExec(ins)
30- if (mergedSpawnExecs.contains(spawnExec.first)) {
31- mergedSpawnExecs[spawnExec.first]!! .presentInBothExecs = true
32- mergedSpawnExecs[spawnExec.first]!! .bEnvVars =
58+ bExecCounter++
59+ if (! spawnExec.second.remoteCacheHit) {
60+ val aEnvVars: Map <String , String > =
61+ aSpawnExecs[spawnExec.first]!! .environmentVariablesList.associate { it.name to it.value }
62+ val bEnvVars: Map <String , String > =
3363 spawnExec.second.environmentVariablesList.associate { it.name to it.value }
34- mergedSpawnExecs[spawnExec.first]!! .bInputs = spawnExec.second.inputsList.associate { it.path to it.digest }
64+ val mergedEnvVars =
65+ calculateDiff(aEnvVars, bEnvVars).map {
66+ ExecutionEnvironmentVariables .newBuilder().setName(it.key).setAValue(it.value.first)
67+ .setBValue(it.value.second).build()
68+ }
69+ val aInputs = aSpawnExecs[spawnExec.first]!! .inputsList.associate { it.path to it.digest }
70+ val bInputs = spawnExec.second.inputsList.associate { it.path to it.digest }
71+ val mergedInputs = calculateDiff(aInputs, bInputs).map {
72+ ExecutionInputs .newBuilder().setPath(it.key).setAHash(it.value.first.hash)
73+ .setBHash(it.value.second.hash).build()
74+ }
75+ var mergedSpawnExec =
76+ Protos .MergedSpawnExec .newBuilder().setExecutionHash(spawnExec.first)
77+ .addAllListedOutputs(spawnExec.second.listedOutputsList)
78+ .addAllEnvVars(mergedEnvVars.toMutableList())
79+ .addAllInputs(mergedInputs.toMutableList())
80+ .build()
81+ println (mergedSpawnExec.toString())
82+ textLogWriter?.appendText(mergedSpawnExec.toString())
83+ if (binaryLogFile != null ) FileOutputStream (binaryLogFile, true ).use {
84+ mergedSpawnExec.writeDelimitedTo(
85+ it
86+ )
87+ }
3588 } else {
36- mergedSpawnExecs[spawnExec.first] = MergedSpawnExec (
37- spawnExec.second.listedOutputsList,
38- HashMap (),
39- HashMap (),
40- spawnExec.second.environmentVariablesList.associate { it.name to it.value },
41- spawnExec.second.inputsList.associate { it.path to it.digest },
42- false
43- )
89+ cacheHits++
4490 }
4591 }
46- File (" D:/output.txt" ).printWriter().use { out ->
47- mergedSpawnExecs.forEach {
48-
49- out .println (" =============================" )
50- out .println (" Listed Outputs {" )
51- it.value.listedOutputs.forEach { listedOutput -> out .println (" $listedOutput " ) }
52- out .println (" }" )
53- it.value.printEnvVarsDiff(out )
54- it.value.printInputsDiff(out )
55-
56- /* println("=============================")
57- println("Listed Outputs {")
58- it.value.listedOutputs.forEach { listedOutput -> println(" ${listedOutput}") }
59- println("}")
60- it.value.printEnvVarsDiff()
61- it.value.printInputsDiff()*/
62- }
92+ if (aExecCounter != bExecCounter) {
93+ val warning = " WARNING! Number of executions isn't the same across builds so results may be not correct!"
94+ println (warning)
95+ textLogWriter?.appendText(warning)
6396 }
97+ val reportText = """ ====================REPORT====================
98+ Spawned Executions: ${aExecCounter}
99+ Cache Hits: ${cacheHits}
100+ Cache Hit Rate: ${" %.2f" .format(cacheHits.toFloat() / aExecCounter.toFloat() * 100 )} %
101+ ==============================================
102+ """ .trimIndent()
103+ println (reportText)
104+ textLogWriter?.appendText(reportText)
64105}
65106
66107fun getNextSpawnExec (ins : InputStream ): Pair <String , SpawnExec > {
@@ -73,3 +114,9 @@ fun calculateExecHash(input: String): String {
73114 return sha256.digest(input.toByteArray())
74115 .fold(" " ) { str, it -> str + " %02x" .format(it) }
75116}
117+
118+ fun <T > calculateDiff (aMap : Map <String , T >, bMap : Map <String , T >): Map <String , Pair <T , T >> {
119+ return aMap.filterKeys { bMap.containsKey(it) }.filter { (key, _) ->
120+ aMap[key] != bMap[key]
121+ }.mapValues { Pair (aMap[it.key]!! , bMap[it.key]!! ) }
122+ }
0 commit comments