| #!/usr/bin/env python3 |
| |
| # Copyright 2020 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| """This script is used to run the error_calculator binary. |
| |
| ./error_calculator.py 1 657579885 14 1 |
| |
| This script assumes it is being run from a third_party embedding in a fuchsia |
| checkout. If you are running from the cobalt standalone repository, run |
| 'cobaltb.py calculate_error' instead. If the build configuration doesn't match |
| the assumed fuchsia or cobalt checkout, use the --bin_dir flag to specify the |
| path to the error_calculator and config_parser binaries. |
| |
| A new version of the registry is generated every time. |
| """ |
| |
| import subprocess |
| import os |
| import argparse |
| import sys |
| |
| THIS_DIR = os.path.abspath(os.path.dirname(__file__)) |
| COBALT_ROOT_DIR = os.path.abspath(os.path.join(THIS_DIR, '..')) |
| PRIVACY_PARAMS = os.path.join(COBALT_ROOT_DIR, 'src', 'algorithms', 'privacy', |
| 'data', 'privacy_encoding_params') |
| REGISTRY_PROTO_DEFAULT = os.path.join(COBALT_ROOT_DIR, 'out', 'registry.pb') |
| POPULATION_DEFAULT = 100000 |
| |
| |
| def estimate_from_args(error_binary, args): |
| """Unpacks and validates command line arguments before calling error calculator. |
| |
| Args: |
| error_binary: The filepath of the error calculator binary. |
| args: Python arguments defined below. |
| """ |
| _validate_args(args) |
| estimate(error_binary, args.registry_proto, args.customer_id, args.project_id, |
| args.metric_id, args.report_id, args.population, args.epsilon, |
| args.min_denominator, args.min_value, args.max_value, args.max_count, |
| args.simple) |
| |
| |
| def estimate(error_binary, |
| registry_proto, |
| customer_id, |
| project_id, |
| metric_id, |
| report_id, |
| population, |
| epsilon=None, |
| min_denominator=None, |
| min_value=None, |
| max_value=None, |
| max_count=None, |
| simple=False): |
| """Calls the error_calculator binary with the specified arguments. |
| |
| Args: |
| error_binary: The string filepath to the error calculator binary. |
| registry_proto: The string filepath to the registry formatted as a |
| serialized proto. |
| customer_id: The integer customer id. |
| project_id: The integer project id. |
| metric_id: The integer metric id. |
| report_id: The integer report id. |
| population: Integer value estimating the number of reporting devices. |
| epsilon: Optional; Epsilon value for which to estimate error. |
| min_denominator: Optional; estimated minimum number of unique contributing |
| devices per day. |
| min_value: Optional; override the report's min_value. |
| max_value: Optional; override the report's max_value. |
| max_count: Optional; override the report's max_count. |
| simple: Outputs a single estimate. |
| """ |
| error_args = [ |
| '-registry_proto', registry_proto, '--privacy_params', PRIVACY_PARAMS, |
| '--population', |
| str(population) |
| ] |
| if epsilon: |
| error_args = error_args + ['--epsilon', str(epsilon)] |
| if min_denominator: |
| error_args = error_args + ['--min_denominator', str(min_denominator)] |
| if simple: |
| error_args = error_args + ['--simple'] |
| if min_value: |
| error_args = error_args + ['--min_value', str(min_value)] |
| if max_value: |
| error_args = error_args + ['--max_value', str(max_value)] |
| if max_count: |
| error_args = error_args + ['--max_count', str(max_count)] |
| error_args = error_args + [ |
| str(customer_id), |
| str(project_id), |
| str(metric_id), |
| str(report_id) |
| ] |
| subprocess.check_call([error_binary] + error_args) |
| |
| |
| # TODO(b/228513646): Use the build system to generate the registry only when |
| # necessary. |
| def generate_registry(registry_proto, config_dir, config_parser): |
| """Generates a binary encoding of the cobalt registry. |
| |
| Args: |
| registry_proto: The filepath of the error calculator binary. This is the |
| output of the config_parser and the input for the error_calculator |
| config_dir: Location of the cobalt config. |
| config_parser: Location of the binary used to generate the registry_proto. |
| """ |
| if not config_parser: |
| sys.exit('Run \'config_parser --out_filename %s\' and try again.' % |
| registry_proto) |
| subprocess.check_call([ |
| config_parser, '--output_file', registry_proto, '--config_dir', |
| config_dir, '--privacy_encoding_params_file', PRIVACY_PARAMS |
| ]) |
| print('Wrote binary encoding of registry to %s.\n' % registry_proto) |
| |
| |
| def add_parse_args(parser): |
| """Adds the standard arguments required for the error_calculator. |
| |
| This is used to set arguments for this script and the cobaltb.py script. |
| |
| Args: |
| parser: An ArgumentParser to be augmented with error calculator arguments. |
| """ |
| parser.add_argument( |
| '--registry_proto', |
| help='Set a specific filepath for the binary encoding of the Cobalt Registry. Default: %s' |
| % REGISTRY_PROTO_DEFAULT, |
| default=REGISTRY_PROTO_DEFAULT) |
| parser.add_argument( |
| '--population', |
| help='Expected number of devices contributing to the report. Default: %s' |
| % POPULATION_DEFAULT, |
| default=POPULATION_DEFAULT) |
| parser.add_argument( |
| '--bin_dir', |
| help='Directory containing the error_calculator and config_parser binaries. ' |
| 'If unset, the script attempts to find binaries based on the default ' |
| 'build configuration for Fuchsia and Cobalt.', |
| default=None) |
| parser.add_argument( |
| '--epsilon', |
| help='If set, estimates the error using the specified epsilon value.') |
| parser.add_argument( |
| '--min_denominator', |
| help='Estimated minimum number of unique contributing devices per day.') |
| parser.add_argument( |
| '--min_value', help='Optionally overrides the report\'s MinValue field.') |
| parser.add_argument( |
| '--max_value', help='Optionally overrides the report\'s MaxValue field.') |
| parser.add_argument( |
| '--max_count', help='Optionally overrides the report\'s MaxCount field.') |
| parser.add_argument( |
| '--simple', |
| default=False, |
| action='store_true', |
| help='Output a single error estimate.') |
| parser.add_argument( |
| 'customer_id', help='a report\'s parent customer id', type=int) |
| parser.add_argument( |
| 'project_id', help='a report\'s parent project id.', type=int) |
| parser.add_argument( |
| 'metric_id', help='a report\'s parent metric id.', type=int) |
| parser.add_argument( |
| 'report_id', help='a report\'s parent report id.', type=int) |
| |
| |
| def _validate_args(args): |
| if args.registry_proto == None: |
| sys.exit('--registry_proto is required') |
| if args.population == None: |
| sys.exit('--population flag is required') |
| if not os.path.exists(args.registry_proto): |
| sys.exit( |
| 'No serialized proto found. Try running \'config_parser --out_filename %s\' and try again.' |
| % args.registry_proto) |
| |
| |
| if __name__ == '__main__': |
| # Assumes the user is running from a Fuchsia checkout. |
| # If you're running this script directly from a cobalt checkout, use |
| # `cobaltb.py calculate_error` instead. |
| parser = argparse.ArgumentParser() |
| add_parse_args(parser) |
| args = parser.parse_args() |
| |
| if args.bin_dir: |
| bin_dir = args.bin_dir |
| else: |
| out = subprocess.check_output(['fx', 'get-build-dir']) |
| out_dir = out.decode('utf-8').rstrip() |
| bin_dir = os.path.join(out_dir, 'host_x64') |
| |
| error_binary = os.path.join(bin_dir, 'error_calculator') |
| config_parser = os.path.join(bin_dir, 'config_parser') |
| config_dir = os.path.join(COBALT_ROOT_DIR, '..', 'cobalt_config') |
| config_dir = os.path.abspath(config_dir) |
| if not os.path.exists(error_binary): |
| sys.exit( |
| 'Error Calculator binary not found: %s.\nRun \'fx build\' and try again.' |
| % error_binary) |
| |
| generate_registry(args.registry_proto, config_dir, config_parser) |
| estimate_from_args(error_binary, args) |