chore: minor fixes on boot and lazybox

master
cfig 2 days ago
parent c55f584f2e
commit 43317d4f92
No known key found for this signature in database
GPG Key ID: B104C307F0FDABB7

@ -41,7 +41,7 @@ interface IPackable {
"adb root".check_call()
val abUpdateProp = "adb shell getprop ro.build.ab_update".check_output()
log.info("ro.build.ab_update=$abUpdateProp")
val slotSuffix = if (abUpdateProp == "true" && !fileName.startsWith("misc.img")) {
val slotSuffix = if (abUpdateProp == "true" && !fileName.contains("misc.img")) {
"adb shell getprop ro.boot.slot_suffix".check_output()
} else {
""
@ -56,7 +56,7 @@ interface IPackable {
"adb root".check_call()
val abUpdateProp = "adb shell getprop ro.build.ab_update".check_output()
log.info("ro.build.ab_update=$abUpdateProp")
val slotSuffix = if (abUpdateProp == "true" && !fileName.startsWith("misc.img")) {
val slotSuffix = if (abUpdateProp == "true" && !fileName.endsWith("misc.img")) {
"adb shell getprop ro.boot.slot_suffix".check_output()
} else {
""

@ -65,8 +65,7 @@ class MiscImgParser : IPackable {
}
fun flash(fileName: String) {
val stem = fileName.substring(0, fileName.indexOf("."))
super.flash("$fileName.new", stem)
super.flash("$fileName.new", File(fileName).nameWithoutExtension)
}
override fun `@verify`(fileName: String) {

@ -1,5 +1,6 @@
package cfig.lazybox
import cfig.lazybox.staging.DiffCI
import cfig.lazybox.sysinfo.BootChart
import cfig.lazybox.sysinfo.CpuInfo
import cfig.lazybox.sysinfo.Pidstat
@ -26,6 +27,8 @@ fun main(args: Array<String>) {
println("sysinfo : get overall system info from Android")
println("\nIncubating usage:")
println("apps : get apk file list from Android")
println("dmainfo : parse /d/dma_buf/bufinfo")
println("diffci : find changelist files from CI server based on date and time ranges")
exitProcess(0)
}
if (args[0] == "cpuinfo") {
@ -83,4 +86,20 @@ fun main(args: Array<String>) {
//BootingParser.run()
BootingParser.run2()
}
if (args[0] == "dmainfo") {
if (args.size != 2) {
log.error("Usage: dmainfo <dmainfo_file>")
return
}
val dmainfoFile = args[1]
if (File(dmainfoFile).exists()) {
val dmaInfoParser = DmaInfoParser()
val dmaInfo = dmaInfoParser.parse(args.drop(1).toTypedArray())
} else {
log.error("File not found: $dmainfoFile")
}
}
if (args[0] == "diffci") {
DiffCI().run(args.drop(1).toTypedArray())
}
}

@ -0,0 +1,155 @@
package cfig.lazybox
import com.fasterxml.jackson.databind.ObjectMapper
import org.slf4j.LoggerFactory
import java.io.File
import java.io.FileNotFoundException
data class DmaBufInfo(
val size: Long,
val flags: String,
val mode: String,
val count: Int,
val exp_name: String,
val ino: String,
val pid: Int?,
val tids: List<Int>,
val processName: String?,
val attachedDevices: List<String>
)
class DmaInfoParser {
companion object {
private val log = LoggerFactory.getLogger(DmaInfoParser::class.java)
}
fun parse(args: Array<String>) {
if (args.isEmpty()) {
log.error("Usage: Provide the path to the dmainfo file as an argument.")
return
}
val filePath = args[0]
log.info("Parsing file: {}", filePath)
try {
val dmaInfoList = parseFile(filePath)
if (dmaInfoList.isNotEmpty()) {
val mapper = ObjectMapper()
val writer = mapper.writerWithDefaultPrettyPrinter()
dmaInfoList.forEach { info ->
log.info("Parsed object:\n{}", writer.writeValueAsString(info))
}
log.info("--------------------------------------------------")
log.info("Successfully parsed {} DMA buffer objects.", dmaInfoList.size)
} else {
log.warn("No valid DMA buffer objects were found in the file.")
}
} catch (e: FileNotFoundException) {
log.error("File operation failed: {}", e.message)
} catch (e: Exception) {
log.error("An unexpected error occurred during parsing.", e)
}
}
/**
* Reads and parses a dmainfo file from the given path.
*
* @param filePath The path to the dmainfo file.
* @return A list of [DmaBufInfo] objects, one for each entry in the file.
* @throws FileNotFoundException if the file does not exist.
*/
private fun parseFile(filePath: String): List<DmaBufInfo> {
val file = File(filePath)
if (!file.exists()) {
throw FileNotFoundException("Error: File not found at '$filePath'")
}
val allLines = file.readLines()
val firstDataLineIndex = allLines.indexOfFirst { line ->
line.trim().matches(Regex("""^[0-9a-fA-F]{8}\s+.*"""))
}
if (firstDataLineIndex == -1) {
log.warn("No data lines found in the file.")
return emptyList()
}
val content = allLines.subList(firstDataLineIndex, allLines.size).joinToString("\n")
val blocks = content.split(Regex("(\\r?\\n){2,}"))
.map { it.trim() }
.filter { it.isNotEmpty() }
return blocks.mapNotNull { parseBlock(it) }
}
/**
* Parses a single block of text representing one DMA buffer object.
*/
private fun parseBlock(block: String): DmaBufInfo? {
val lines = block.lines().filter { it.isNotBlank() }
if (lines.isEmpty()) return null
val mainLine = lines.first()
val mainLineRegex = Regex("""^(\w+)\s+(\w+)\s+(\w+)\s+(\d+)\s+([\w-]+)\s+(\w+)\s*(.*)$""")
val match = mainLineRegex.find(mainLine)
if (match == null) {
log.warn("Skipping malformed line that doesn't match expected format: \"{}\"", mainLine)
return null
}
val (sizeStr, flagsStr, modeStr, countStr, expName, ino, processStr) = match.destructured
var pid: Int? = null
val tids = mutableListOf<Int>()
var processName: String? = null
if (processStr.isNotBlank()) {
val processParts = processStr.trim().split(Regex("\\s+"))
val nameParts = mutableListOf<String>()
var pidFound = false
processParts.forEach { part ->
val num = part.toIntOrNull()
if (num != null) {
if (!pidFound) {
pid = num
pidFound = true
} else {
tids.add(num)
}
} else {
nameParts.add(part)
}
}
if (nameParts.isNotEmpty()) {
processName = nameParts.joinToString(" ")
}
}
val attachedDevices = lines.drop(1)
.dropWhile { !it.trim().equals("Attached Devices:", ignoreCase = true) }
.drop(1)
.map { it.trim() }
.takeWhile { !it.trim().startsWith("Total", ignoreCase = true) }
.filter { it.isNotEmpty() }
return DmaBufInfo(
size = sizeStr.toLongOrNull() ?: 0L,
flags = "0x$flagsStr",
mode = "0x$modeStr",
count = countStr.toIntOrNull() ?: 0,
exp_name = expName,
ino = ino,
pid = pid,
tids = tids,
processName = processName,
attachedDevices = attachedDevices
)
}
}

@ -0,0 +1,236 @@
package cfig.lazybox.staging
import org.slf4j.LoggerFactory
import java.io.IOException
import java.net.URI
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.Duration
import java.time.format.DateTimeParseException
import kotlin.system.exitProcess
class DiffCI {
private val FAKE_URL = "FAKE_URL"
private val baseUrl = System.getenv("diffCIbaseUrl") ?: FAKE_URL
private val finder = ChangelistFinder(baseUrl)
private val dateFormat = DateTimeFormatter.ofPattern("yyyyMMdd")
private val dateTimeFormat = DateTimeFormatter.ofPattern("yyyyMMddHHmm")
private val buildIdRegex = ".*/(\\d{12})/changelist".toRegex()
private val log = LoggerFactory.getLogger(DiffCI::class.java)
fun run(args: Array<String>) {
if (baseUrl == FAKE_URL) {
log.info("Error: Environment variable 'diffCIbaseUrl' is not set.")
log.info("Please set it to the base URL of the changelist directory.")
exitProcess(1)
}
when (args.size) {
1 -> handleSingleDate(args)
2 -> handleRange(args)
else -> printUsageAndExit()
}
}
private fun handleSingleDate(args: Array<String>) {
if (args[0].length != 8) {
log.info("Error: For a single argument, please provide a date in yyyyMMdd format.")
exitProcess(1)
}
val targetDate = parseDate(args[0]) ?: exitProcess(1)
log.info("=============================================")
log.info("Searching for changelists on: ${args[0]}")
log.info("=============================================")
val urls = finder.findChangelistUrlsForDate(targetDate)
printResults(urls)
}
private fun handleRange(args: Array<String>) {
val arg1 = args[0]
val arg2 = args[1]
if (arg1.length != arg2.length) {
log.info("Error: Start and end arguments must be of the same format (both dates or both date-times).")
exitProcess(1)
}
when (arg1.length) {
8 -> handleDateRange(arg1, arg2)
12 -> handleDateTimeRange(arg1, arg2)
else -> {
log.info("Error: Invalid argument format. Please use yyyyMMdd or yyyyMMddHHmm.")
exitProcess(1)
}
}
}
private fun handleDateRange(startDateStr: String, endDateStr: String) {
val startDate = parseDate(startDateStr) ?: exitProcess(1)
val endDate = parseDate(endDateStr) ?: exitProcess(1)
if (startDate.isAfter(endDate)) {
log.info("Error: The start date ($startDateStr) must be before or the same as the end date ($endDateStr).")
exitProcess(1)
}
log.info("=============================================")
log.info("Searching for changelists from $startDateStr to $endDateStr")
log.info("=============================================")
val allUrls = mutableListOf<String>()
var currentDate = startDate
while (currentDate <= endDate) {
log.info("\n----- Processing Date: $currentDate -----")
val dailyUrls = finder.findChangelistUrlsForDate(currentDate)
allUrls.addAll(dailyUrls)
currentDate = currentDate.plusDays(1)
}
printResults(allUrls)
}
private fun handleDateTimeRange(startDateTimeStr: String, endDateTimeStr: String) {
val startDateTime = parseDateTime(startDateTimeStr) ?: exitProcess(1)
val endDateTime = parseDateTime(endDateTimeStr) ?: exitProcess(1)
if (startDateTime.isAfter(endDateTime)) {
log.info("Error: The start time ($startDateTimeStr) must be before or the same as the end time ($endDateTimeStr).")
exitProcess(1)
}
log.info("=============================================")
log.info("Searching for changelists between $startDateTimeStr and $endDateTimeStr")
log.info("=============================================")
val allUrls = mutableListOf<String>()
var currentDate = startDateTime.toLocalDate()
while (currentDate <= endDateTime.toLocalDate()) {
log.info("\n----- Processing Date: $currentDate -----")
val dailyUrls = finder.findChangelistUrlsForDate(currentDate)
val filteredUrls = dailyUrls.filter { url ->
val matchResult = buildIdRegex.find(url)
if (matchResult != null) {
val buildId = matchResult.groupValues[1]
val buildDateTime = parseDateTime(buildId)
buildDateTime != null && !buildDateTime.isBefore(startDateTime) && !buildDateTime.isAfter(endDateTime)
} else {
false
}
}
allUrls.addAll(filteredUrls)
currentDate = currentDate.plusDays(1)
}
printResults(allUrls, isTimeRange = true)
}
private fun printUsageAndExit() {
log.info("Error: Invalid number of arguments.")
log.info("\nUsage:")
log.info(" Single Date: --args='diffci <yyyymmdd>'")
log.info(" Date Range: --args='diffci <start_date> <end_date>'")
log.info(" Time Range: --args='diffci <start_datetime> <end_datetime>'")
log.info("\nExamples:")
log.info(" --args='diffci 20250628'")
log.info(" --args='diffci 20250627 20250628'")
log.info(" --args='diffci 202506281000 202506281430'")
exitProcess(1)
}
private fun printResults(urls: List<String>, isTimeRange: Boolean = false) {
log.info("\n--- Results ---")
if (urls.isNotEmpty()) {
log.info("Successfully found ${urls.size} changelist files:")
urls.forEach { log.info(it) }
} else {
val rangeType = if (isTimeRange) "time range" else "date(s)"
log.info("No changelist files were found for the specified $rangeType.")
}
log.info("---------------")
}
private fun parseDate(dateStr: String): LocalDate? {
return try {
LocalDate.parse(dateStr, dateFormat)
} catch (e: DateTimeParseException) {
log.info("Error: Invalid date format for '$dateStr'.")
log.info("Please use the yyyyMMdd format (e.g., 20250628).")
null
}
}
private fun parseDateTime(dateTimeStr: String): LocalDateTime? {
return try {
LocalDateTime.parse(dateTimeStr, dateTimeFormat)
} catch (e: DateTimeParseException) {
log.info("Error: Invalid date-time format for '$dateTimeStr'.")
log.info("Please use the yyyyMMddHHmm format (e.g., 202506281027).")
null
}
}
/**
* Inner class to handle the web scraping logic.
*/
private inner class ChangelistFinder(private val baseUrl: String) {
private val client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build()
fun findChangelistUrlsForDate(date: LocalDate): List<String> {
val yearMonthPattern = DateTimeFormatter.ofPattern("yyyyMM")
val yearMonthDayPattern = DateTimeFormatter.ofPattern("yyyyMMdd")
val yearMonthStr = date.format(yearMonthPattern)
val yearMonthDayStr = date.format(yearMonthDayPattern)
val directoryUrl = "$baseUrl/$yearMonthStr/$yearMonthDayStr/"
return try {
val htmlContent = fetchUrlContent(directoryUrl)
if (htmlContent != null) {
parseDirectoryHtmlWithRegex(htmlContent, directoryUrl, yearMonthDayStr)
} else {
emptyList()
}
} catch (e: IOException) {
log.info("An error occurred during the network request: ${e.message}")
emptyList()
} catch (e: InterruptedException) {
Thread.currentThread().interrupt()
log.info("The network request was interrupted: ${e.message}")
emptyList()
}
}
private fun fetchUrlContent(url: String): String? {
try {
val request = HttpRequest.newBuilder().uri(URI.create(url)).GET().build()
val response = client.send(request, HttpResponse.BodyHandlers.ofString())
if (response.statusCode() != 200) {
if (response.statusCode() != 404) {
log.info("Request for $url failed with code: ${response.statusCode()}")
}
return null
}
return response.body()
} catch (e: Exception) {
log.info("Exception while fetching URL '$url': ${e.message}")
return null
}
}
private fun parseDirectoryHtmlWithRegex(html: String, directoryUrl: String, datePrefix: String): List<String> {
val regex = "href=\"($datePrefix\\d+)/\"".toRegex()
return regex.findAll(html)
.map { matchResult ->
val dirName = matchResult.groupValues[1]
"$directoryUrl$dirName/changelist"
}
.toList()
}
}
}
Loading…
Cancel
Save