Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b993f517 authored by Makoto Onuki's avatar Makoto Onuki
Browse files

Update stats CSV for the new dashboard

Created a new dashboard (with the same go link, go/ravenwood-stats)
which is a pivot table that supports drill-down into methods.

Updated the CSV to support it.

Bug: 402797626
Test: Manual test with importing data and browsing go/ravenwood-stats
Flag: EXEMPT host tool change only

Change-Id: Ia23c0fae7fc6a096565e9902fafab0a4a3ea3067
parent 364d006c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -114,7 +114,7 @@ collect_apis() {


collect_stats $stats " (import it as 'ravenwood_stats')"
collect_apis $apis " (import it as 'ravenwood_supported_apis')"
collect_apis $apis " (import it as 'ravenwood_supported_apis2')"

cp *keep_all.txt $keep_all_dir
echo "Keep all files created at:"
+3 −7
Original line number Diff line number Diff line
@@ -24,13 +24,9 @@ import org.objectweb.asm.Opcodes
import java.io.PrintWriter

/**
 * TODO This is for the legacy API coverage stats CSV that shows how many APIs are "supported"
 * in each class with some heuristics. We created [ApiDumper] later, which dumpps all methods
 * with the "supported" status. We should update the coverage dashboard to use the [ApiDumper]
 * output and remove this class, once we port all the heuristics to [ApiDumper] as well.
 * (For example, this class ignores non-public and/or abstract methods, but [ApiDumper] shows
 * all of them in the same way. We should probably mark them as "Boring" or maybe "Ignore"
 * for [ApiDumper])
 * This class is no longer used. It was used for the old ravenwood dashboard. (b/402797626)
 *
 * TODO: Delete the class.
 */
open class HostStubGenStats(val classes: ClassNodes) {
    data class Stats(
+4 −0
Original line number Diff line number Diff line
@@ -377,6 +377,10 @@ fun MethodNode.isPublic(): Boolean {
    return (this.access and Opcodes.ACC_PUBLIC) != 0
}

fun MethodNode.isAbstract(): Boolean {
    return (this.access and Opcodes.ACC_ABSTRACT) != 0
}

fun MethodNode.isNative(): Boolean {
    return (this.access and Opcodes.ACC_NATIVE) != 0
}
+56 −73
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import com.android.hoststubgen.asm.CTOR_NAME
import com.android.hoststubgen.asm.ClassNodes
import com.android.hoststubgen.asm.getClassNameFromFullClassName
import com.android.hoststubgen.asm.getPackageNameFromFullClassName
import com.android.hoststubgen.asm.isAbstract
import com.android.hoststubgen.asm.isPublic
import com.android.hoststubgen.asm.toHumanReadableClassName
import com.android.hoststubgen.csvEscape
import com.android.hoststubgen.filters.FilterPolicy
@@ -27,8 +29,8 @@ import com.android.hoststubgen.filters.FilterPolicyWithReason
import com.android.hoststubgen.filters.OutputFilter
import com.android.hoststubgen.filters.StatsLabel
import com.android.hoststubgen.log
import org.objectweb.asm.Type
import org.objectweb.asm.tree.ClassNode
import org.objectweb.asm.tree.MethodNode
import java.io.PrintWriter

/**
@@ -45,19 +47,14 @@ class ApiDumper(
        val descriptor: String,
    )

    private val javaStandardApiPolicy = FilterPolicy.Keep.withReason(
        "Java standard API",
        StatsLabel.Supported,
    )

    private val shownMethods = mutableSetOf<MethodKey>()

    /**
     * Do the dump.
     */
    fun dump() {
        pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" +
                ",Supported,Policy,Reason,SupportedLabel\n")
        pw.printf("PackageName,ClassName,Inherited,DeclareClass,MethodName,MethodDesc" +
                ",Supported,Policy,Reason,Boring\n")

        classes.forEach { classNode ->
            shownMethods.clear()
@@ -72,32 +69,21 @@ class ApiDumper(
        methodClassName: String,
        methodName: String,
        methodDesc: String,
        classPolicy: FilterPolicyWithReason,
        computedMethodLabel: StatsLabel,
        methodPolicy: FilterPolicyWithReason,
    ) {
        if (methodPolicy.statsLabel == StatsLabel.Ignored) {
            return
        }
        // Label hack -- if the method is supported, but the class is boring, then the
        // method is boring too.
        var methodLabel = methodPolicy.statsLabel
        if (methodLabel == StatsLabel.SupportedButBoring
            && classPolicy.statsLabel == StatsLabel.SupportedButBoring) {
            methodLabel = classPolicy.statsLabel
        }

        pw.printf(
            "%s,%s,%d,%s,%s,%s,%d,%s,%s,%s\n",
            "%s,%s,%d,%s,%s,%s,%d,%s,%s,%d\n",
            csvEscape(classPackage),
            csvEscape(className),
            if (isSuperClass) { 1 } else { 0 },
            csvEscape(methodClassName),
            csvEscape(methodName),
            csvEscape(methodDesc),
            methodLabel.statValue,
            csvEscape(methodName + methodDesc),
            if (computedMethodLabel.isSupported) { 1 } else { 0 },
            methodPolicy.policy,
            csvEscape(methodPolicy.reason),
            methodLabel,
            if (computedMethodLabel == StatsLabel.SupportedButBoring) { 1 } else { 0 },
        )
    }

@@ -111,6 +97,42 @@ class ApiDumper(
        return false
    }

    private fun getClassLabel(cn: ClassNode, classPolicy: FilterPolicyWithReason): StatsLabel {
        if (!classPolicy.statsLabel.isSupported) {
            return classPolicy.statsLabel
        }
        if (cn.name.endsWith("Proto")
            || cn.name.endsWith("ProtoEnums")
            || cn.name.endsWith("LogTags")
            || cn.name.endsWith("StatsLog")) {
            return StatsLabel.SupportedButBoring
        }

        return classPolicy.statsLabel
    }

    private fun resolveMethodLabel(
        mn: MethodNode,
        methodPolicy: FilterPolicyWithReason,
        classLabel: StatsLabel,
    ): StatsLabel {
        // Class label will override the method label
        if (!classLabel.isSupported) {
            return classLabel
        }
        // If method isn't supported, just use it as-is.
        if (!methodPolicy.statsLabel.isSupported) {
            return methodPolicy.statsLabel
        }

        // Use heuristics to override the label.
        if (!mn.isPublic() || mn.isAbstract()) {
            return StatsLabel.SupportedButBoring
        }

        return methodPolicy.statsLabel
    }

    private fun dump(
        dumpClass: ClassNode,
        methodClass: ClassNode,
@@ -120,9 +142,11 @@ class ApiDumper(
            return
        }
        log.d("Class ${dumpClass.name} -- policy $classPolicy")
        val classLabel = getClassLabel(dumpClass, classPolicy)

        val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
        val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
        val humanReadableClassName = dumpClass.name.toHumanReadableClassName()
        val pkg = getPackageNameFromFullClassName(humanReadableClassName)
        val cls = getClassNameFromFullClassName(humanReadableClassName)

        val isSuperClass = dumpClass != methodClass

@@ -150,8 +174,12 @@ class ApiDumper(

            val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc)

            val methodLabel = resolveMethodLabel(method, methodPolicy, classLabel)

            if (methodLabel != StatsLabel.Ignored) {
                dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(),
                renameTo ?: method.name, method.desc, classPolicy, methodPolicy)
                    renameTo ?: method.name, method.desc, methodLabel, methodPolicy)
            }
       }

        // Dump super class methods.
@@ -178,51 +206,6 @@ class ApiDumper(
            dump(dumpClass, methodClass)
            return
        }

        // Dump overriding methods from Java standard classes, except for the Object methods,
        // which are obvious.
        if (methodClassName.startsWith("java/") || methodClassName.startsWith("javax/")) {
            if (methodClassName != "java/lang/Object") {
               dumpStandardClass(dumpClass, methodClassName)
            }
            return
        }
        log.w("Super class or interface $methodClassName (used by ${dumpClass.name}) not found.")
    }

    /**
     * Dump methods from Java standard classes.
     */
    private fun dumpStandardClass(
        dumpClass: ClassNode,
        methodClassName: String,
    ) {
        val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName()
        val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName()

        val methodClassName = methodClassName.toHumanReadableClassName()

        try {
            val clazz = Class.forName(methodClassName)

            // Method.getMethods() returns only public methods, but with inherited ones.
            // Method.getDeclaredMethods() returns private methods too, but no inherited methods.
            //
            // Since we're only interested in public ones, just use getMethods().
            clazz.methods.forEach { method ->
                val methodName = method.name
                val methodDesc = Type.getMethodDescriptor(method)

                // If we already printed the method from a subclass, don't print it.
                if (shownAlready(methodName, methodDesc)) {
                    return@forEach
                }

                dumpMethod(pkg, cls, true, methodClassName,
                    methodName, methodDesc, javaStandardApiPolicy, javaStandardApiPolicy)
            }
        } catch (e: ClassNotFoundException) {
            log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.")
        }
    }
}
+9 −1
Original line number Diff line number Diff line
@@ -32,7 +32,15 @@ enum class StatsLabel(val statValue: Int, val label: String) {
    SupportedButBoring(1, "Boring"),

    /** Entry should be shown as "supported" */
    Supported(2, "Supported"),
    Supported(2, "Supported");

    val isSupported: Boolean
        get() {
        return when (this) {
            SupportedButBoring, Supported -> true
            else -> false
        }
    }
}

/**