Loading ravenwood/scripts/csv-flattener 0 → 100755 +57 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 # # Copyright (C) 2025 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Reads lines from CSV files and print each colum in a separate line, which helps taking a per-field diff of CSV files. Columns that are not the first in each line are indented. Example: $ (echo "a,b,c"; echo "d,e,f") | csv-flattener a b c d e f """ import csv import sys from signal import signal, SIGPIPE, SIG_DFL def flatten(infile): rd = csv.reader(infile, delimiter=',', quotechar='"') for cols in rd: prefix = "" for col in cols: print(prefix + col) prefix = " " def main(files): signal(SIGPIPE, SIG_DFL) # Don't show stacktrace on SIGPIPE if len(files) == 0: flatten(sys.stdin) return for f in files: with open(f, newline='') as csvfile: flatten(csvfile) if __name__ == "__main__": main(sys.argv[1:]) ravenwood/scripts/ravenwood-stats-collector.sh +127 −42 Original line number Diff line number Diff line Loading @@ -17,6 +17,25 @@ set -e # This script's directory. SCRIPT_DIR="$(realpath ${0%/*})" # Parse the options. diff_base="" while getopts "d:" opt; do case "$opt" in d) # If this flag is provided, diff the newly generated CSV against this file. diff_base="$OPTARG" ;; '?') exit 1 ;; esac done shift $(($OPTIND - 1)) # Output files out_dir=/tmp/ravenwood apis=$out_dir/ravenwood-apis-all.csv Loading @@ -28,78 +47,144 @@ mkdir -p $out_dir mkdir -p $keep_all_dir mkdir -p $dump_dir stats_checker_module="ravenwood-stats-checker" minfo=$OUT/module-info.json # Current time. timestamp="$(date --iso-8601=seconds)" # First, use jq to get the output files from the checker module. This will be something like this: # The input CSV files are generated by different build modules, so collecting # them would be painful. # # So instead, we use a dummy sh_test_host "ravenwood-stats-checker" with # all the input files as its data files. So if we build this module, soong # will copy this script to its test output directory, along with all the data files, # so we can now just copy all the *.csv, *.txt files from this single directory. # # So first, we need to find this output directory, which is in the module-info.json # file. So we use JQ to find it. # # The json contains something like the following, so we need to find the script path. # # --- # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt # "ravenwood-stats-checker" : { # : # out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker # "installed": [ # "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/ravenwood-stats-checker" <-- need to find this. # ], # : #}, # --- # 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$' checker=ravenwood-stats-checker minfo=$OUT/module-info.json checker_path="$( jq -r ".\"$checker\".installed.[] | select(endswith(\"/$checker\"))" \ $minfo )" if [[ "$script_path" == "" ]] ; then echo "Error: $stats_checker_module script not found from $minfo" if [[ "$checker_path" == "" ]] ; then echo "Error: '$checker' script not found in $minfo" exit 1 fi # This is the directory where our input files are. script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")" files_dir="$ANDROID_BUILD_TOP/$(dirname "$checker_path")/" # Clear it before (re-)buildign the script, to make sure we won't have stale files. rm -fr "$script_dir" # Clear the directory before (re-)building the script, to make sure we won't have stale files. rm -fr "$files_dir" # Then build it, which will also collect the input files in the same dir. echo "Collecting the input files..." m "$stats_checker_module" # Then build it, which will also collect the input files in $files_dir. echo "Files directory is: $files_dir" # Start... echo "Collecting the input files..." m $checker echo "Files directory is: $script_dir" cd "$script_dir" # Run a command after printing it. run() { echo "Running: $*" 1>&2 "$@" } # Just cat a CSV file, but with two extra columns prepended: # - Jar module name (first argument) # - Timestamp dump() { local jar=$1 local file=$2 local jar="$1" local file="$2" # Remove the header row, and prepend the columns. sed -e '1d' -e "s/^/$jar,$timestamp,/" $file sed -e '1d' -e "s/^/$jar,$timestamp,/" "$file" } collect_apis() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. # Copy the header, with the first two columns appended. echo -n "Jar,Generated Date," head -n 1 hoststubgen_framework-minus-apex_apis.csv head -n 1 "$files_dir"/hoststubgen_framework-minus-apex_apis.csv dump "framework-minus-apex" hoststubgen_framework-minus-apex_apis.csv dump "service.core" hoststubgen_services.core_apis.csv dump "framework-configinfrastructure" framework-configinfrastructure_apis.csv dump "framework-statsd" framework-statsd_apis.csv # Dump each module. dump "framework-minus-apex" "$files_dir"/hoststubgen_framework-minus-apex_apis.csv dump "service.core" "$files_dir"/hoststubgen_services.core_apis.csv dump "framework-configinfrastructure" "$files_dir"/framework-configinfrastructure_apis.csv dump "framework-statsd" "$files_dir"/framework-statsd_apis.csv } > "$out" echo "API CVS created at $out$desc" echo "API CVS created at $out (import it as 'ravenwood_supported_apis3')" } collect_apis $apis " (import it as 'ravenwood_supported_apis3')" do_main() { collect_apis $apis cp *keep_all.txt $keep_all_dir # Collect keep_all files. cp "$files_dir"/*keep_all.txt $keep_all_dir echo "Keep all files created at:" find $keep_all_dir -type f cp *dump.txt $dump_dir # Collect dump files. cp "$files_dir"/*dump.txt $dump_dir echo "Dump files created at:" find $dump_dir -type f } # Run the main routine. do_main # Main routine done. #--------------------------------------------------------------------- # Next, if -d (diff base) is provided, take a diff against that file. if [[ "$diff_base" == "" ]] ; then exit 0 fi echo echo "Taking a diff against $diff_base ..." # Flatten a CSV (expand each column in a separate line) for a better diff. # (Also remove the timestamp.) flatten() { local csv="$1" local to="$2" echo "Flattening $csv to $to ..." # "grep -v" to remove timestamps. "$SCRIPT_DIR"/csv-flattener "$csv" | grep -v "^ 20[0-9][0-9]-[0-9][0-9]" > "$to" } do_diff() { local f1="$1" local f2="$2" # Generate flattened text files from the old and new CSVs. flatten "$f1" "$f1.txt" flatten "$f2" "$f2.txt" # Then take a diff. # ("|| true" for ignoring the status code) run ${DIFF_CMD:-diff} -U 10 "$f1.txt" "$f2.txt" || true } do_diff "$diff_base" "$apis" No newline at end of file ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/stats/ApiDumper.kt +15 −1 Original line number Diff line number Diff line Loading @@ -127,7 +127,21 @@ class ApiDumper( } // Use heuristics to override the label. if (!mn.isPublic() || mn.isAbstract()) { if (!mn.isPublic()) { return StatsLabel.SupportedButBoring } // If the policy is "not supported" but the statsLabel explicitly says "supported" // then that's "throw-but-supported". In that case, let's not mark it as "boring" // even if it's abstract, and show it. (if the statsLabel doesn't say "supported", // we'd be returning earlier already.) if (!methodPolicy.policy.isSupported) { return StatsLabel.Supported } if (mn.isAbstract()) { // An abstract method is normally boring -- but if it's a throw-but-interesting, // that's actually not boring. return StatsLabel.SupportedButBoring } Loading Loading
ravenwood/scripts/csv-flattener 0 → 100755 +57 −0 Original line number Diff line number Diff line #!/usr/bin/env python3 # # Copyright (C) 2025 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Reads lines from CSV files and print each colum in a separate line, which helps taking a per-field diff of CSV files. Columns that are not the first in each line are indented. Example: $ (echo "a,b,c"; echo "d,e,f") | csv-flattener a b c d e f """ import csv import sys from signal import signal, SIGPIPE, SIG_DFL def flatten(infile): rd = csv.reader(infile, delimiter=',', quotechar='"') for cols in rd: prefix = "" for col in cols: print(prefix + col) prefix = " " def main(files): signal(SIGPIPE, SIG_DFL) # Don't show stacktrace on SIGPIPE if len(files) == 0: flatten(sys.stdin) return for f in files: with open(f, newline='') as csvfile: flatten(csvfile) if __name__ == "__main__": main(sys.argv[1:])
ravenwood/scripts/ravenwood-stats-collector.sh +127 −42 Original line number Diff line number Diff line Loading @@ -17,6 +17,25 @@ set -e # This script's directory. SCRIPT_DIR="$(realpath ${0%/*})" # Parse the options. diff_base="" while getopts "d:" opt; do case "$opt" in d) # If this flag is provided, diff the newly generated CSV against this file. diff_base="$OPTARG" ;; '?') exit 1 ;; esac done shift $(($OPTIND - 1)) # Output files out_dir=/tmp/ravenwood apis=$out_dir/ravenwood-apis-all.csv Loading @@ -28,78 +47,144 @@ mkdir -p $out_dir mkdir -p $keep_all_dir mkdir -p $dump_dir stats_checker_module="ravenwood-stats-checker" minfo=$OUT/module-info.json # Current time. timestamp="$(date --iso-8601=seconds)" # First, use jq to get the output files from the checker module. This will be something like this: # The input CSV files are generated by different build modules, so collecting # them would be painful. # # So instead, we use a dummy sh_test_host "ravenwood-stats-checker" with # all the input files as its data files. So if we build this module, soong # will copy this script to its test output directory, along with all the data files, # so we can now just copy all the *.csv, *.txt files from this single directory. # # So first, we need to find this output directory, which is in the module-info.json # file. So we use JQ to find it. # # The json contains something like the following, so we need to find the script path. # # --- # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt # "ravenwood-stats-checker" : { # : # out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv # out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker # "installed": [ # "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/ravenwood-stats-checker" <-- need to find this. # ], # : #}, # --- # 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$' checker=ravenwood-stats-checker minfo=$OUT/module-info.json checker_path="$( jq -r ".\"$checker\".installed.[] | select(endswith(\"/$checker\"))" \ $minfo )" if [[ "$script_path" == "" ]] ; then echo "Error: $stats_checker_module script not found from $minfo" if [[ "$checker_path" == "" ]] ; then echo "Error: '$checker' script not found in $minfo" exit 1 fi # This is the directory where our input files are. script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")" files_dir="$ANDROID_BUILD_TOP/$(dirname "$checker_path")/" # Clear it before (re-)buildign the script, to make sure we won't have stale files. rm -fr "$script_dir" # Clear the directory before (re-)building the script, to make sure we won't have stale files. rm -fr "$files_dir" # Then build it, which will also collect the input files in the same dir. echo "Collecting the input files..." m "$stats_checker_module" # Then build it, which will also collect the input files in $files_dir. echo "Files directory is: $files_dir" # Start... echo "Collecting the input files..." m $checker echo "Files directory is: $script_dir" cd "$script_dir" # Run a command after printing it. run() { echo "Running: $*" 1>&2 "$@" } # Just cat a CSV file, but with two extra columns prepended: # - Jar module name (first argument) # - Timestamp dump() { local jar=$1 local file=$2 local jar="$1" local file="$2" # Remove the header row, and prepend the columns. sed -e '1d' -e "s/^/$jar,$timestamp,/" $file sed -e '1d' -e "s/^/$jar,$timestamp,/" "$file" } collect_apis() { local out="$1" local desc="$2" { # Copy the header, with the first column appended. # Copy the header, with the first two columns appended. echo -n "Jar,Generated Date," head -n 1 hoststubgen_framework-minus-apex_apis.csv head -n 1 "$files_dir"/hoststubgen_framework-minus-apex_apis.csv dump "framework-minus-apex" hoststubgen_framework-minus-apex_apis.csv dump "service.core" hoststubgen_services.core_apis.csv dump "framework-configinfrastructure" framework-configinfrastructure_apis.csv dump "framework-statsd" framework-statsd_apis.csv # Dump each module. dump "framework-minus-apex" "$files_dir"/hoststubgen_framework-minus-apex_apis.csv dump "service.core" "$files_dir"/hoststubgen_services.core_apis.csv dump "framework-configinfrastructure" "$files_dir"/framework-configinfrastructure_apis.csv dump "framework-statsd" "$files_dir"/framework-statsd_apis.csv } > "$out" echo "API CVS created at $out$desc" echo "API CVS created at $out (import it as 'ravenwood_supported_apis3')" } collect_apis $apis " (import it as 'ravenwood_supported_apis3')" do_main() { collect_apis $apis cp *keep_all.txt $keep_all_dir # Collect keep_all files. cp "$files_dir"/*keep_all.txt $keep_all_dir echo "Keep all files created at:" find $keep_all_dir -type f cp *dump.txt $dump_dir # Collect dump files. cp "$files_dir"/*dump.txt $dump_dir echo "Dump files created at:" find $dump_dir -type f } # Run the main routine. do_main # Main routine done. #--------------------------------------------------------------------- # Next, if -d (diff base) is provided, take a diff against that file. if [[ "$diff_base" == "" ]] ; then exit 0 fi echo echo "Taking a diff against $diff_base ..." # Flatten a CSV (expand each column in a separate line) for a better diff. # (Also remove the timestamp.) flatten() { local csv="$1" local to="$2" echo "Flattening $csv to $to ..." # "grep -v" to remove timestamps. "$SCRIPT_DIR"/csv-flattener "$csv" | grep -v "^ 20[0-9][0-9]-[0-9][0-9]" > "$to" } do_diff() { local f1="$1" local f2="$2" # Generate flattened text files from the old and new CSVs. flatten "$f1" "$f1.txt" flatten "$f2" "$f2.txt" # Then take a diff. # ("|| true" for ignoring the status code) run ${DIFF_CMD:-diff} -U 10 "$f1.txt" "$f2.txt" || true } do_diff "$diff_base" "$apis" No newline at end of file
ravenwood/tools/ravenhelper/src/com/android/platform/test/ravenwood/ravenhelper/stats/ApiDumper.kt +15 −1 Original line number Diff line number Diff line Loading @@ -127,7 +127,21 @@ class ApiDumper( } // Use heuristics to override the label. if (!mn.isPublic() || mn.isAbstract()) { if (!mn.isPublic()) { return StatsLabel.SupportedButBoring } // If the policy is "not supported" but the statsLabel explicitly says "supported" // then that's "throw-but-supported". In that case, let's not mark it as "boring" // even if it's abstract, and show it. (if the statsLabel doesn't say "supported", // we'd be returning earlier already.) if (!methodPolicy.policy.isSupported) { return StatsLabel.Supported } if (mn.isAbstract()) { // An abstract method is normally boring -- but if it's a throw-but-interesting, // that's actually not boring. return StatsLabel.SupportedButBoring } Loading