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

Commit 44a09259 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Update stats CSV for the new dashboard" into main

parents 3421d00f b993f517
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
        }
    }
}

/**