Loading ravenwood/scripts/ravenwood-stats-collector.sh +40 −13 Original line number Diff line number Diff line Loading @@ -29,21 +29,46 @@ mkdir -p $out_dir mkdir -p $keep_all_dir mkdir -p $dump_dir # Where the input files are. path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/ timestamp="$(date --iso-8601=seconds)" stats_checker_module="ravenwood-stats-checker" minfo=$OUT/module-info.json m() { ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" } timestamp="$(date --iso-8601=seconds)" # Building this will generate the files we need. m ravenwood-stats-checker # First, use jq to get the output files from the checker module. This will be something like this: # # --- # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt # : # out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker # --- # Then, use grep to find the script's path (the last line in the above examle) script_path="$( jq -r ".\"$stats_checker_module\".installed | .[]" $minfo | grep '/ravenwood-stats-checker$' )" if [[ "$script_path" == "" ]] ; then echo "Error: $stats_checker_module script not found from $minfo" exit 1 fi # This is the directory where our input files are. script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")" # Clear it before (re-)buildign the script, to make sure we won't have stale files. rm -fr "$script_dir" # Then build it, which will also collect the input files in the same dir. echo "Collecting the input files..." m "$stats_checker_module" # Start... cd $path echo "Files directory is: $script_dir" cd "$script_dir" dump() { local jar=$1 Loading @@ -55,6 +80,7 @@ dump() { collect_stats() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," Loading @@ -66,11 +92,12 @@ collect_stats() { dump "framework-statsd" framework-statsd_stats.csv } > "$out" echo "Stats CVS created at $out" echo "Stats CVS created at $out$desc" } collect_apis() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," Loading @@ -82,12 +109,12 @@ collect_apis() { dump "framework-statsd" framework-statsd_apis.csv } > "$out" echo "API CVS created at $out" echo "API CVS created at $out$desc" } collect_stats $stats collect_apis $apis collect_stats $stats " (import it as 'ravenwood_stats')" collect_apis $apis " (import it as 'ravenwood_supported_apis')" cp *keep_all.txt $keep_all_dir echo "Keep all files created at:" Loading ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt +18 −15 Original line number Diff line number Diff line Loading @@ -15,13 +15,24 @@ */ package com.android.hoststubgen import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName import com.android.hoststubgen.asm.getPackageNameFromFullClassName import com.android.hoststubgen.filters.FilterPolicyWithReason import com.android.hoststubgen.filters.StatsLabel import org.objectweb.asm.Opcodes import java.io.PrintWriter open class HostStubGenStats { /** * 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]) */ open class HostStubGenStats(val classes: ClassNodes) { data class Stats( var supported: Int = 0, var total: Int = 0, Loading @@ -30,14 +41,6 @@ open class HostStubGenStats { private val stats = mutableMapOf<String, Stats>() data class Api( val fullClassName: String, val methodName: String, val methodDesc: String, ) private val apis = mutableListOf<Api>() fun onVisitPolicyForMethod( fullClassName: String, methodName: String, Loading @@ -45,16 +48,16 @@ open class HostStubGenStats { policy: FilterPolicyWithReason, access: Int ) { if (policy.policy.isSupported) { apis.add(Api(fullClassName, methodName, descriptor)) } // Ignore methods that aren't public if ((access and Opcodes.ACC_PUBLIC) == 0) return // Ignore methods that are abstract if ((access and Opcodes.ACC_ABSTRACT) != 0) return // Ignore methods where policy isn't relevant if (policy.isIgnoredForStats) return val statsLabel = policy.statsLabel if (statsLabel == StatsLabel.Ignored) return val cn = classes.findClass(fullClassName) ?: return val packageName = getPackageNameFromFullClassName(fullClassName) val className = getOuterClassNameFromFullClassName(fullClassName) Loading @@ -70,7 +73,7 @@ open class HostStubGenStats { val packageStats = stats.getOrPut(packageName) { Stats() } val classStats = packageStats.children.getOrPut(className) { Stats() } if (policy.policy.isSupported) { if (statsLabel == StatsLabel.Supported) { packageStats.supported += 1 classStats.supported += 1 } Loading ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt +44 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.hoststubgen.csvEscape import com.android.hoststubgen.filters.FilterPolicy 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 Loading @@ -44,7 +45,10 @@ class ApiDumper( val descriptor: String, ) private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API") private val javaStandardApiPolicy = FilterPolicy.Keep.withReason( "Java standard API", StatsLabel.Supported, ) private val shownMethods = mutableSetOf<MethodKey>() Loading @@ -53,7 +57,7 @@ class ApiDumper( */ fun dump() { pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" + ",Supported,Policy,Reason\n") ",Supported,Policy,Reason,SupportedLabel\n") classes.forEach { classNode -> shownMethods.clear() Loading @@ -68,23 +72,36 @@ class ApiDumper( methodClassName: String, methodName: String, methodDesc: String, policy: FilterPolicyWithReason, classPolicy: FilterPolicyWithReason, 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\n", "%s,%s,%d,%s,%s,%s,%d,%s,%s,%s\n", csvEscape(classPackage), csvEscape(className), if (isSuperClass) { 1 } else { 0 }, csvEscape(methodClassName), csvEscape(methodName), csvEscape(methodDesc), if (policy.policy.isSupported) { 1 } else { 0 }, policy.policy, csvEscape(policy.reason), methodLabel.statValue, methodPolicy.policy, csvEscape(methodPolicy.reason), methodLabel, ) } private fun isDuplicate(methodName: String, methodDesc: String): Boolean { private fun shownAlready(methodName: String, methodDesc: String): Boolean { val methodKey = MethodKey(methodName, methodDesc) if (shownMethods.contains(methodKey)) { Loading @@ -98,6 +115,12 @@ class ApiDumper( dumpClass: ClassNode, methodClass: ClassNode, ) { val classPolicy = filter.getPolicyForClass(dumpClass.name) if (classPolicy.statsLabel == StatsLabel.Ignored) { return } log.d("Class ${dumpClass.name} -- policy $classPolicy") val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName() val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName() Loading @@ -112,23 +135,23 @@ class ApiDumper( } } // If we already printed the method from a subclass, don't print it. if (isDuplicate(method.name, method.desc)) { if (shownAlready(method.name, method.desc)) { return@forEach } val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) val methodPolicy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) // Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV // complete, we still need to hide methods substituted (== @RavenwoodReplace) methods // and for now we don't have an easy way to detect it. if (policy.policy == FilterPolicy.Remove) { if (methodPolicy.policy == FilterPolicy.Remove) { return@forEach } val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc) dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(), renameTo ?: method.name, method.desc, policy) renameTo ?: method.name, method.desc, classPolicy, methodPolicy) } // Dump super class methods. Loading @@ -155,10 +178,13 @@ class ApiDumper( dump(dumpClass, methodClass) return } if (methodClassName.startsWith("java/") || methodClassName.startsWith("javax/") ) { // 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.") Loading Loading @@ -188,12 +214,12 @@ class ApiDumper( val methodDesc = Type.getMethodDescriptor(method) // If we already printed the method from a subclass, don't print it. if (isDuplicate(methodName, methodDesc)) { if (shownAlready(methodName, methodDesc)) { return@forEach } dumpMethod(pkg, cls, true, methodClassName, methodName, methodDesc, javaStandardApiPolicy) methodName, methodDesc, javaStandardApiPolicy, javaStandardApiPolicy) } } catch (e: ClassNotFoundException) { log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.") Loading ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt +5 −2 Original line number Diff line number Diff line Loading @@ -155,7 +155,10 @@ enum class FilterPolicy(val policyStringOrPrefix: String) { /** * Create a [FilterPolicyWithReason] with a given reason. */ fun withReason(reason: String): FilterPolicyWithReason { return FilterPolicyWithReason(this, reason) fun withReason( reason: String, statsLabelOverride: StatsLabel? = null, ): FilterPolicyWithReason { return FilterPolicyWithReason(this, reason, statsLabelOverride = statsLabelOverride) } } ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +36 −14 Original line number Diff line number Diff line Loading @@ -15,33 +15,55 @@ */ package com.android.hoststubgen.filters /** * How each entry should be handled on the dashboard. */ enum class StatsLabel(val statValue: Int, val label: String) { /** Entry shouldn't show up in the dashboard. */ Ignored(-1, ""), /** Entry should be shown as "not supported" */ NotSupported(0, "NotSupported"), /** * Entry should be shown as "supported", but are too "boring" to show on the dashboard, * e.g. annotation classes. */ SupportedButBoring(1, "Boring"), /** Entry should be shown as "supported" */ Supported(2, "Supported"), } /** * Captures a [FilterPolicy] with a human-readable reason. */ data class FilterPolicyWithReason ( val policy: FilterPolicy, val reason: String = "", private val statsLabelOverride: StatsLabel? = null ) { /** * Return a new [FilterPolicy] with an updated reason, while keeping the original reason * as an "inner-reason". */ fun wrapReason(reason: String): FilterPolicyWithReason { return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]") fun wrapReason(reason: String, statsLabelOverride: StatsLabel? = null): FilterPolicyWithReason { return FilterPolicyWithReason( policy, "$reason [inner-reason: ${this.reason}]", statsLabelOverride = statsLabelOverride, ) } override fun toString(): String { return "[$policy - reason: $reason]" return "[$policy/$statsLabel - reason: $reason]" } /** Returns whether this policy should be ignored for stats. */ val isIgnoredForStats: Boolean get() { return reason.contains("anonymous-inner-class") || reason.contains("is-annotation") || reason.contains("is-enum") || reason.contains("is-synthetic-method") || reason.contains("special-class") || reason.contains("substitute-to") val statsLabel: StatsLabel get() { statsLabelOverride?.let { return it } if (policy.isSupported) { return StatsLabel.Supported } return StatsLabel.NotSupported } } Loading
ravenwood/scripts/ravenwood-stats-collector.sh +40 −13 Original line number Diff line number Diff line Loading @@ -29,21 +29,46 @@ mkdir -p $out_dir mkdir -p $keep_all_dir mkdir -p $dump_dir # Where the input files are. path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/ timestamp="$(date --iso-8601=seconds)" stats_checker_module="ravenwood-stats-checker" minfo=$OUT/module-info.json m() { ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" } timestamp="$(date --iso-8601=seconds)" # Building this will generate the files we need. m ravenwood-stats-checker # First, use jq to get the output files from the checker module. This will be something like this: # # --- # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt # : # out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker # --- # Then, use grep to find the script's path (the last line in the above examle) script_path="$( jq -r ".\"$stats_checker_module\".installed | .[]" $minfo | grep '/ravenwood-stats-checker$' )" if [[ "$script_path" == "" ]] ; then echo "Error: $stats_checker_module script not found from $minfo" exit 1 fi # This is the directory where our input files are. script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")" # Clear it before (re-)buildign the script, to make sure we won't have stale files. rm -fr "$script_dir" # Then build it, which will also collect the input files in the same dir. echo "Collecting the input files..." m "$stats_checker_module" # Start... cd $path echo "Files directory is: $script_dir" cd "$script_dir" dump() { local jar=$1 Loading @@ -55,6 +80,7 @@ dump() { collect_stats() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," Loading @@ -66,11 +92,12 @@ collect_stats() { dump "framework-statsd" framework-statsd_stats.csv } > "$out" echo "Stats CVS created at $out" echo "Stats CVS created at $out$desc" } collect_apis() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," Loading @@ -82,12 +109,12 @@ collect_apis() { dump "framework-statsd" framework-statsd_apis.csv } > "$out" echo "API CVS created at $out" echo "API CVS created at $out$desc" } collect_stats $stats collect_apis $apis collect_stats $stats " (import it as 'ravenwood_stats')" collect_apis $apis " (import it as 'ravenwood_supported_apis')" cp *keep_all.txt $keep_all_dir echo "Keep all files created at:" Loading
ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt +18 −15 Original line number Diff line number Diff line Loading @@ -15,13 +15,24 @@ */ package com.android.hoststubgen import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName import com.android.hoststubgen.asm.getPackageNameFromFullClassName import com.android.hoststubgen.filters.FilterPolicyWithReason import com.android.hoststubgen.filters.StatsLabel import org.objectweb.asm.Opcodes import java.io.PrintWriter open class HostStubGenStats { /** * 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]) */ open class HostStubGenStats(val classes: ClassNodes) { data class Stats( var supported: Int = 0, var total: Int = 0, Loading @@ -30,14 +41,6 @@ open class HostStubGenStats { private val stats = mutableMapOf<String, Stats>() data class Api( val fullClassName: String, val methodName: String, val methodDesc: String, ) private val apis = mutableListOf<Api>() fun onVisitPolicyForMethod( fullClassName: String, methodName: String, Loading @@ -45,16 +48,16 @@ open class HostStubGenStats { policy: FilterPolicyWithReason, access: Int ) { if (policy.policy.isSupported) { apis.add(Api(fullClassName, methodName, descriptor)) } // Ignore methods that aren't public if ((access and Opcodes.ACC_PUBLIC) == 0) return // Ignore methods that are abstract if ((access and Opcodes.ACC_ABSTRACT) != 0) return // Ignore methods where policy isn't relevant if (policy.isIgnoredForStats) return val statsLabel = policy.statsLabel if (statsLabel == StatsLabel.Ignored) return val cn = classes.findClass(fullClassName) ?: return val packageName = getPackageNameFromFullClassName(fullClassName) val className = getOuterClassNameFromFullClassName(fullClassName) Loading @@ -70,7 +73,7 @@ open class HostStubGenStats { val packageStats = stats.getOrPut(packageName) { Stats() } val classStats = packageStats.children.getOrPut(className) { Stats() } if (policy.policy.isSupported) { if (statsLabel == StatsLabel.Supported) { packageStats.supported += 1 classStats.supported += 1 } Loading
ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt +44 −18 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.hoststubgen.csvEscape import com.android.hoststubgen.filters.FilterPolicy 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 Loading @@ -44,7 +45,10 @@ class ApiDumper( val descriptor: String, ) private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API") private val javaStandardApiPolicy = FilterPolicy.Keep.withReason( "Java standard API", StatsLabel.Supported, ) private val shownMethods = mutableSetOf<MethodKey>() Loading @@ -53,7 +57,7 @@ class ApiDumper( */ fun dump() { pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" + ",Supported,Policy,Reason\n") ",Supported,Policy,Reason,SupportedLabel\n") classes.forEach { classNode -> shownMethods.clear() Loading @@ -68,23 +72,36 @@ class ApiDumper( methodClassName: String, methodName: String, methodDesc: String, policy: FilterPolicyWithReason, classPolicy: FilterPolicyWithReason, 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\n", "%s,%s,%d,%s,%s,%s,%d,%s,%s,%s\n", csvEscape(classPackage), csvEscape(className), if (isSuperClass) { 1 } else { 0 }, csvEscape(methodClassName), csvEscape(methodName), csvEscape(methodDesc), if (policy.policy.isSupported) { 1 } else { 0 }, policy.policy, csvEscape(policy.reason), methodLabel.statValue, methodPolicy.policy, csvEscape(methodPolicy.reason), methodLabel, ) } private fun isDuplicate(methodName: String, methodDesc: String): Boolean { private fun shownAlready(methodName: String, methodDesc: String): Boolean { val methodKey = MethodKey(methodName, methodDesc) if (shownMethods.contains(methodKey)) { Loading @@ -98,6 +115,12 @@ class ApiDumper( dumpClass: ClassNode, methodClass: ClassNode, ) { val classPolicy = filter.getPolicyForClass(dumpClass.name) if (classPolicy.statsLabel == StatsLabel.Ignored) { return } log.d("Class ${dumpClass.name} -- policy $classPolicy") val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName() val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName() Loading @@ -112,23 +135,23 @@ class ApiDumper( } } // If we already printed the method from a subclass, don't print it. if (isDuplicate(method.name, method.desc)) { if (shownAlready(method.name, method.desc)) { return@forEach } val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) val methodPolicy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) // Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV // complete, we still need to hide methods substituted (== @RavenwoodReplace) methods // and for now we don't have an easy way to detect it. if (policy.policy == FilterPolicy.Remove) { if (methodPolicy.policy == FilterPolicy.Remove) { return@forEach } val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc) dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(), renameTo ?: method.name, method.desc, policy) renameTo ?: method.name, method.desc, classPolicy, methodPolicy) } // Dump super class methods. Loading @@ -155,10 +178,13 @@ class ApiDumper( dump(dumpClass, methodClass) return } if (methodClassName.startsWith("java/") || methodClassName.startsWith("javax/") ) { // 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.") Loading Loading @@ -188,12 +214,12 @@ class ApiDumper( val methodDesc = Type.getMethodDescriptor(method) // If we already printed the method from a subclass, don't print it. if (isDuplicate(methodName, methodDesc)) { if (shownAlready(methodName, methodDesc)) { return@forEach } dumpMethod(pkg, cls, true, methodClassName, methodName, methodDesc, javaStandardApiPolicy) methodName, methodDesc, javaStandardApiPolicy, javaStandardApiPolicy) } } catch (e: ClassNotFoundException) { log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.") Loading
ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt +5 −2 Original line number Diff line number Diff line Loading @@ -155,7 +155,10 @@ enum class FilterPolicy(val policyStringOrPrefix: String) { /** * Create a [FilterPolicyWithReason] with a given reason. */ fun withReason(reason: String): FilterPolicyWithReason { return FilterPolicyWithReason(this, reason) fun withReason( reason: String, statsLabelOverride: StatsLabel? = null, ): FilterPolicyWithReason { return FilterPolicyWithReason(this, reason, statsLabelOverride = statsLabelOverride) } }
ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +36 −14 Original line number Diff line number Diff line Loading @@ -15,33 +15,55 @@ */ package com.android.hoststubgen.filters /** * How each entry should be handled on the dashboard. */ enum class StatsLabel(val statValue: Int, val label: String) { /** Entry shouldn't show up in the dashboard. */ Ignored(-1, ""), /** Entry should be shown as "not supported" */ NotSupported(0, "NotSupported"), /** * Entry should be shown as "supported", but are too "boring" to show on the dashboard, * e.g. annotation classes. */ SupportedButBoring(1, "Boring"), /** Entry should be shown as "supported" */ Supported(2, "Supported"), } /** * Captures a [FilterPolicy] with a human-readable reason. */ data class FilterPolicyWithReason ( val policy: FilterPolicy, val reason: String = "", private val statsLabelOverride: StatsLabel? = null ) { /** * Return a new [FilterPolicy] with an updated reason, while keeping the original reason * as an "inner-reason". */ fun wrapReason(reason: String): FilterPolicyWithReason { return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]") fun wrapReason(reason: String, statsLabelOverride: StatsLabel? = null): FilterPolicyWithReason { return FilterPolicyWithReason( policy, "$reason [inner-reason: ${this.reason}]", statsLabelOverride = statsLabelOverride, ) } override fun toString(): String { return "[$policy - reason: $reason]" return "[$policy/$statsLabel - reason: $reason]" } /** Returns whether this policy should be ignored for stats. */ val isIgnoredForStats: Boolean get() { return reason.contains("anonymous-inner-class") || reason.contains("is-annotation") || reason.contains("is-enum") || reason.contains("is-synthetic-method") || reason.contains("special-class") || reason.contains("substitute-to") val statsLabel: StatsLabel get() { statsLabelOverride?.let { return it } if (policy.isSupported) { return StatsLabel.Supported } return StatsLabel.NotSupported } }