lazybox: update gki subcommand

master
cfig 7 months ago
parent 52e600dc0d
commit 33224b09ea
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -20,11 +20,14 @@ dependencies {
implementation("org.apache.commons:commons-exec:1.3")
implementation("com.fasterxml.jackson.core:jackson-annotations:2.14.0")
implementation("com.fasterxml.jackson.core:jackson-databind:2.14.0")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.14.0")
implementation(project(mapOf("path" to ":helper")))
implementation("org.slf4j:slf4j-api:2.0.9")
implementation("org.apache.commons:commons-compress:1.26.0")
implementation("com.github.freva:ascii-table:1.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
implementation("com.squareup.okio:okio:3.9.0")
// Use the Kotlin JUnit 5 integration.
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
// Use the JUnit 5 integration.
@ -57,3 +60,7 @@ tasks.named<Test>("test") {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
tasks.named<JavaExec>("run") {
standardInput = System.`in`
}

@ -99,17 +99,15 @@ class AMS {
log.info(text)
val lines = text.trim().split("\n") // Split lines
val regex = Regex("""^ +Proc #\d+: (.*?) +oom: max=\d+ curRaw=(-?\d+) setRaw=(-?\d+) cur=(-?\d+) set=(-?\d+)""") // Match relevant parts
val results = lines.mapNotNull { line ->
val matchResult = regex.matchEntire(line) ?: return@mapNotNull null
val groups = matchResult.groups
// Extract data from groups
val packageName = groups[1]?.value ?: ""
val oomCurValue = groups[2]?.value?.toIntOrNull() ?: 0
val status = groups[3]?.value ?: ""
// Create the Any array
arrayOf(packageName, oomCurValue, status)
log.info("$packageName -> $oomCurValue -> $status")
lines.forEach { line ->
regex.matchEntire(line)?.let { matchResult ->
val groups = matchResult.groups
// Extract data from groups
val packageName = groups[1]?.value ?: ""
val oomCurValue = groups[2]?.value?.toIntOrNull() ?: 0
val status = groups[3]?.value ?: ""
log.info("$packageName -> $oomCurValue -> $status")
}
}
}

@ -22,9 +22,10 @@ fun main(args: Array<String>) {
println("Usage: args: (Array<String>) ...")
println(" or: function [arguments]...")
println("\nCurrently defined functions:")
println("\tcpuinfo sysinfo sysstat pidstat bootchart thermal_info compiledb")
println("\tcpuinfo gki sysinfo sysstat pidstat bootchart thermal_info compiledb")
println("\nCommand Usage:")
println("bootchart: generate Android bootchart")
println("gki : interactive GKI JSON downloader/parser, or process GKI modules from <dir>")
println("pidstat : given a pid, profile its CPU usage")
println("tracecmd : analyze trace-cmd report")
println("cpuinfo : get cpu info from /sys/devices/system/cpu/")
@ -49,6 +50,9 @@ fun main(args: Array<String>) {
)
log.info("cpuinfo.json is ready")
}
if (args[0] == "gki") {
Gki.run(args.drop(1).toTypedArray())
}
if (args[0] == "sysinfo") {
SysInfo().run()
}

@ -0,0 +1,324 @@
package cfig.lazybox
import com.fasterxml.jackson.annotation.JsonIgnoreProperties
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import java.io.File
import okhttp3.OkHttpClient
import okhttp3.Request
import java.util.concurrent.TimeUnit
import java.security.MessageDigest
import kotlin.io.path.createTempDirectory
import kotlin.system.exitProcess
@JsonIgnoreProperties(ignoreUnknown = true)
data class GkiBuild(
val name: String,
val branches: List<Branch>
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class Branch(
val name: String,
val kernel_version: String,
val releases: List<Release>
)
@JsonIgnoreProperties(ignoreUnknown = true)
data class Release(
val tag: String,
val date: String,
val sha1: String,
val kernel_bid: String
)
fun printHelp() {
println("""
Usage:
kotlinc -script gki.kts # Run interactive GKI JSON downloader/parser
kotlinc -script gki.kts <dir> # Process GKI modules from input directory
kotlinc -script gki.kts -h # Show this help message
""".trimIndent())
}
fun runGkiJsonLogic() {
println("Running GKI JSON logic...")
val todoFile = File("to-do.txt")
val branchesMap = listOf(
"android14-5_15" to "https://source.android.com/static/docs/core/architecture/kernel/gki-android14-5_15-release-builds.json",
"android14-6_1" to "https://source.android.com/static/docs/core/architecture/kernel/gki-android14-6_1-release-builds.json",
"android15-6_6" to "https://source.android.com/static/docs/core/architecture/kernel/gki-android15-6_6-release-builds.json",
"android16-6_12" to "https://source.android.com/static/docs/core/architecture/kernel/gki-android16-6_12-release-builds.json"
)
println("Select a GKI branch:")
branchesMap.forEachIndexed { index, (name, _) ->
println("${index + 1}. $name")
}
print("Enter choice (1-${branchesMap.size}) [Default: 1]: ")
val input = readlnOrNull()
val choice = if (input.isNullOrBlank()) 1 else input.toIntOrNull()
if (choice == null || choice !in 1..branchesMap.size) {
println("Invalid choice.")
return
}
val (branchName, url) = branchesMap[choice - 1]
val jsonFileName = "gki-$branchName-release-builds.json"
val file = File(jsonFileName)
if (!file.exists()) {
println("File $jsonFileName not found, downloading from $url.")
try {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw Exception("Request failed: $response")
file.writeText(response.body!!.string())
}
println("Download complete.")
} catch (e: Exception) {
println("Download failed: ${e.message}")
return
}
}
val content = file.readText()
val jsonString: String
val jsonStartMarker = "<code translate=\"no\" dir=\"ltr\">"
val startIndex = content.indexOf(jsonStartMarker)
if (startIndex != -1) {
val fromStart = content.substring(startIndex + jsonStartMarker.length)
jsonString = fromStart.substringBefore("</code>").trim()
} else if (content.trim().startsWith("{")) {
jsonString = content.trim()
} else {
println("Error: JSON data not found in file.")
return
}
val mapper = jacksonObjectMapper()
val gkiBuild: GkiBuild
try {
gkiBuild = mapper.readValue(jsonString)
} catch (e: Exception) {
println("JSON parsing failed: ${e.message}")
return
}
val expectedNameFromFile = jsonFileName.substringAfter("gki-").substringBefore("-release-builds.json")
if (gkiBuild.name.replace('_', '.') != expectedNameFromFile.replace('_', '.')) {
println("Error: JSON name ('${gkiBuild.name}') does not match expected name from filename ('$expectedNameFromFile').")
return
}
println("\nFound ${gkiBuild.branches.size} branches. Please select one:")
gkiBuild.branches.forEachIndexed { index, branch ->
println("${index + 1}. ${branch.name}")
}
print("Enter choice (1-${gkiBuild.branches.size}): ")
val branchChoice = readlnOrNull()?.toIntOrNull()
if (branchChoice == null || branchChoice !in 1..gkiBuild.branches.size) {
println("Invalid choice.")
return
}
val selectedBranch = gkiBuild.branches[branchChoice - 1]
println("\n--- Branch Info ---\n${mapper.writerWithDefaultPrettyPrinter().writeValueAsString(selectedBranch)}")
if (selectedBranch.releases.isEmpty()) {
println("\nNo releases available for this branch.")
return
}
println("\nFound ${selectedBranch.releases.size} releases. Please select one:")
selectedBranch.releases.forEachIndexed { index, release ->
println("${index + 1}. ${release.tag}")
}
print("Enter choice (1-${selectedBranch.releases.size}): ")
val releaseChoice = readlnOrNull()?.toIntOrNull()
if (releaseChoice == null || releaseChoice !in 1..selectedBranch.releases.size) {
println("Invalid choice.")
return
}
val selectedRelease = selectedBranch.releases[releaseChoice - 1]
val tempVal = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(selectedRelease)
println("\n--- Release Info ---\n$tempVal")
// --- URL Generation Logic ---
println("\nGenerating URL list...")
val kernelBid = selectedRelease.kernel_bid
val urlsToGenerate = listOf(
// Debug
"https://ci.android.com/builds/submitted/$kernelBid/kernel_debug_aarch64/latest/boot.img",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_debug_aarch64/latest/system_dlkm_staging_archive.tar.gz",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_debug_aarch64/latest/vmlinux.symvers",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_debug_aarch64/latest/view/BUILD_INFO",
// Release
"https://ci.android.com/builds/submitted/$kernelBid/kernel_aarch64/latest/signed/certified-boot-img-$kernelBid.tar.gz",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_aarch64/latest/system_dlkm_staging_archive.tar.gz",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_aarch64/latest/vmlinux.symvers",
"https://ci.android.com/builds/submitted/$kernelBid/kernel_aarch64/latest/view/BUILD_INFO"
)
todoFile.writeText(urlsToGenerate.joinToString("\n"))
println("URL list successfully written to ${todoFile.path}")
println("\nScript execution complete.")
File("bid_$kernelBid").mkdir()
}
// --- Logic from gki2.kts ---
fun runGki2Logic(inputPath: String) {
println("Running GKI Modules Processing logic...")
val inputDir = File(inputPath)
if (!inputDir.exists() || !inputDir.isDirectory) {
System.err.println("Error: Input directory '$inputPath' does not exist or is not a directory.")
exitProcess(1)
}
println("Input directory: ${inputDir.absolutePath}")
val outputDir = File("gki_modules")
println("Cleaning up and creating output directory: ${outputDir.absolutePath}")
if (outputDir.exists()) {
outputDir.deleteRecursively()
}
outputDir.mkdir()
// --- Boot Image Handling ---
val certifiedBootImg = inputDir.walk().firstOrNull { it.isFile && it.name.startsWith("certified-boot-img-") && it.name.endsWith(".tar.gz") }
if (certifiedBootImg != null) {
println("Found certified boot image: ${certifiedBootImg.name}")
val tempBootDir = createTempDirectory("boot_img_extraction").toFile()
println("Extracting ${certifiedBootImg.name} to ${tempBootDir.absolutePath}...")
"tar -xzf ${certifiedBootImg.absolutePath} -C ${tempBootDir.absolutePath}".runCommand()
val bootImgInTar = tempBootDir.resolve("boot.img")
if (!bootImgInTar.exists()) {
System.err.println("Error: boot.img not found inside ${certifiedBootImg.name}")
tempBootDir.deleteRecursively()
exitProcess(1)
}
val newBootImg = outputDir.resolve("boot-5.15.img")
println("Copying and renaming boot.img to ${newBootImg.absolutePath}")
bootImgInTar.copyTo(newBootImg, overwrite = true)
tempBootDir.deleteRecursively()
} else {
val bootImg = inputDir.resolve("boot.img")
if (!bootImg.exists()) {
System.err.println("Error: Neither certified-boot-img-*.tar.gz nor boot.img found in '${inputDir.absolutePath}'")
exitProcess(1)
}
println("Found boot.img directly in input directory.")
val newBootImg = outputDir.resolve("boot-5.15.img")
println("Copying boot.img to ${newBootImg.absolutePath}")
bootImg.copyTo(newBootImg, overwrite = true)
}
// --- Kernel Modules Handling ---
val systemDlkm = inputDir.resolve("system_dlkm_staging_archive.tar.gz")
if (!systemDlkm.exists()) {
System.err.println("Error: system_dlkm_staging_archive.tar.gz not found in '${inputDir.absolutePath}'")
exitProcess(1)
}
println("Found kernel modules archive: ${systemDlkm.name}")
val tempModulesDir = createTempDirectory("modules_extraction").toFile()
println("Extracting ${systemDlkm.name} to ${tempModulesDir.absolutePath}...")
"tar -xzf ${systemDlkm.absolutePath} -C ${tempModulesDir.absolutePath}".runCommand()
val modulesDir = tempModulesDir.resolve("flatten/lib/modules")
if (!modulesDir.exists() || !modulesDir.isDirectory) {
System.err.println("Error: 'flatten/lib/modules' directory not found inside the extracted archive.")
tempModulesDir.deleteRecursively()
exitProcess(1)
}
println("Copying .ko files to ${outputDir.absolutePath}...")
modulesDir.walk().forEach {
if (it.isFile && it.extension == "ko") {
it.copyTo(outputDir.resolve(it.name), overwrite = true)
}
}
tempModulesDir.deleteRecursively()
println("Finished copying kernel modules.")
// --- MD5 Sum Generation ---
println("Generating md5sum.txt...")
val md5sumFile = outputDir.resolve("md5sum.txt")
val md5sums = mutableListOf<String>()
outputDir.walk().sortedBy { it.name }.forEach {
if (it.isFile && it.name != "md5sum.txt") {
val md5 = it.md5()
md5sums.add("$md5 ${it.name}")
println(" - Calculated MD5 for ${it.name}")
}
}
md5sumFile.writeText(md5sums.joinToString("\n"))
println("md5sum.txt created successfully.")
println("\nDone! All files are in '${outputDir.absolutePath}'.")
}
// --- Helpers ---
fun String.runCommand(workingDir: File = File(".")) {
val process = ProcessBuilder(*split(" ").toTypedArray())
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.INHERIT)
.redirectError(ProcessBuilder.Redirect.INHERIT)
.start()
if (process.waitFor() != 0) {
throw RuntimeException("Failed to run command: $this")
}
}
fun File.md5(): String {
val md = MessageDigest.getInstance("MD5")
this.inputStream().use {
val buffer = ByteArray(8192)
var bytesRead = it.read(buffer)
while (bytesRead != -1) {
md.update(buffer, 0, bytesRead)
bytesRead = it.read(buffer)
}
}
return md.digest().joinToString("") { "%02x".format(it) }
}
class Gki {
companion object {
fun run(args: Array<String>) {
// --- Main Entry Point ---
if (args.isNotEmpty() && (args[0] == "-h" || args[0] == "--help")) {
printHelp()
exitProcess(0)
}
if (args.isEmpty()) {
runGkiJsonLogic()
} else if (args.size == 1) {
runGki2Logic(args[0])
} else {
println("Invalid arguments.")
printHelp()
exitProcess(1)
}
}
}
}
Loading…
Cancel
Save