We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Show time executed / cpu hours instead of number of runs in fuzzer stats.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/local/butler/scripts/setup.py`
Content:
```
1 # Copyright 2019 Google LLC
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #      http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 """Initial datastore setup."""
15 from __future__ import print_function
16 
17 from builtins import object
18 import six
19 
20 from google.cloud import monitoring_v3
21 
22 from base import utils
23 from datastore import data_types
24 from metrics import monitor
25 from metrics import monitoring_metrics
26 
27 LIBFUZZER_TEMPLATE = """APP_NAME = launcher.py
28 MAX_FUZZ_THREADS = 1
29 MAX_TESTCASES = 4
30 FUZZ_TEST_TIMEOUT = 4800
31 TEST_TIMEOUT = 30
32 WARMUP_TIMEOUT = 30
33 BAD_BUILD_CHECK = False
34 THREAD_ALIVE_CHECK_INTERVAL = 1
35 REPORT_OOMS_AND_HANGS = True
36 CORPUS_FUZZER_NAME_OVERRIDE = libFuzzer
37 ENABLE_GESTURES = False
38 THREAD_DELAY = 30.0
39 """
40 
41 AFL_TEMPLATE = """APP_NAME = launcher.py
42 MAX_FUZZ_THREADS = 1
43 MAX_TESTCASES = 4
44 FUZZ_TEST_TIMEOUT = 4800
45 TEST_TIMEOUT = 30
46 WARMUP_TIMEOUT = 30
47 BAD_BUILD_CHECK = False
48 THREAD_ALIVE_CHECK_INTERVAL = 1
49 CORPUS_FUZZER_NAME_OVERRIDE = libFuzzer
50 ADDITIONAL_PROCESSES_TO_KILL = afl-fuzz afl-showmap
51 ENABLE_GESTURES = False
52 THREAD_DELAY  = 30.0
53 """
54 
55 ENGINE_ASAN_TEMPLATE = """LSAN = True
56 ADDITIONAL_ASAN_OPTIONS = quarantine_size_mb=64:strict_memcmp=1:symbolize=0:fast_unwind_on_fatal=0:allocator_release_to_os_interval_ms=500
57 """
58 
59 ENGINE_MSAN_TEMPLATE = ('ADDITIONAL_MSAN_OPTIONS = symbolize=0:print_stats=1:'
60                         'allocator_release_to_os_interval_ms=500:'
61                         'halt_on_error=1')
62 
63 ENGINE_UBSAN_TEMPLATE = """LSAN = False
64 ADDITIONAL_UBSAN_OPTIONS = symbolize=0:allocator_release_to_os_interval_ms=500
65 """
66 
67 PRUNE_TEMPLATE = 'CORPUS_PRUNE = True'
68 
69 TEMPLATES = {
70     'afl': AFL_TEMPLATE,
71     'engine_asan': ENGINE_ASAN_TEMPLATE,
72     'engine_msan': ENGINE_MSAN_TEMPLATE,
73     'engine_ubsan': ENGINE_UBSAN_TEMPLATE,
74     'libfuzzer': LIBFUZZER_TEMPLATE,
75     'prune': PRUNE_TEMPLATE,
76 }
77 
78 
79 class BaseBuiltinFuzzerDefaults(object):
80   """Default values for a builtin Fuzzer data_type. Note this class should be
81   inherited and should not be used directly."""
82 
83   def __init__(self):
84     # Set defaults for any builtin fuzzer.
85     self.revision = 1
86     self.file_size = 'builtin'
87     self.source = 'builtin'
88     self.builtin = True
89 
90     # Create attributes that must be set by child classes.
91     self.name = None
92     self.stats_column_descriptions = None
93     self.stats_columns = None
94     self.key_id = None
95 
96   def create_fuzzer(self):
97     """Create a Fuzzer data_type with columns set to the defaults specified by
98     this object."""
99     assert self.name is not None
100     return data_types.Fuzzer(
101         id=self.key_id,
102         revision=self.revision,
103         file_size=self.file_size,
104         source=self.source,
105         name=self.name,
106         builtin=self.builtin,
107         stats_column_descriptions=self.stats_column_descriptions,
108         stats_columns=self.stats_columns)
109 
110 
111 class LibFuzzerDefaults(BaseBuiltinFuzzerDefaults):
112   """Default values for libFuzzer."""
113 
114   def __init__(self):
115     super(LibFuzzerDefaults, self).__init__()
116     # Override empty values from parent.
117     self.name = 'libFuzzer'
118     self.key_id = 1337
119     # Use single quotes since the string ends in a double quote.
120     # pylint: disable=line-too-long
121     self.stats_column_descriptions = '''fuzzer: "Fuzz target"
122 perf_report: "Link to performance analysis report"
123 tests_executed: "Number of testcases executed during this time period"
124 new_crashes: "Number of new unique crashes observed during this time period"
125 edge_coverage: "Coverage for this fuzz target (number of edges/total)"
126 cov_report: "Link to coverage report"
127 corpus_size: "Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)"
128 avg_exec_per_sec: "Average number of testcases executed per second"
129 fuzzing_time_percent: "Percent of expected fuzzing time that is actually spent fuzzing."
130 new_tests_added: "New testcases added to the corpus during fuzzing based on code coverage"
131 new_features: "New coverage features based on new tests added to corpus."
132 regular_crash_percent: "Percent of fuzzing runs that had regular crashes (other than ooms, leaks, timeouts, startup and bad instrumentation crashes)"
133 oom_percent: "Percent of fuzzing runs that crashed on OOMs (should be 0)"
134 leak_percent: "Percent of fuzzing runs that crashed on memory leaks (should be 0)"
135 timeout_percent: "Percent of fuzzing runs that had testcases timeout (should be 0)"
136 startup_crash_percent: "Percent of fuzzing runs that crashed on startup (should be 0)"
137 avg_unwanted_log_lines: "Average number of unwanted log lines in fuzzing runs (should be 0)"
138 runs_count: "Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)"
139 logs: "Link to fuzzing logs"
140 corpus_backup: "Backup copy of the minimized corpus generated based on code coverage"'''
141 
142     self.stats_columns = """_PERFORMANCE_REPORT as perf_report,
143 sum(t.number_of_executed_units) as tests_executed,
144 custom(j.new_crashes) as new_crashes,
145 _EDGE_COV as edge_coverage,
146 _COV_REPORT as cov_report,
147 _CORPUS_SIZE as corpus_size,
148 avg(t.average_exec_per_sec) as avg_exec_per_sec,
149 avg(t.fuzzing_time_percent) as fuzzing_time_percent,
150 sum(t.new_units_added) as new_tests_added,
151 sum(t.new_features) as new_features,
152 avg(t.crash_count*100) as regular_crash_percent,
153 avg(t.oom_count*100) as oom_percent,
154 avg(t.leak_count*100) as leak_percent,
155 avg(t.timeout_count*100) as timeout_percent,
156 avg(t.startup_crash_count*100) as startup_crash_percent,
157 avg(t.log_lines_unwanted) as avg_unwanted_log_lines,
158 COUNT(t.bad_instrumentation) as runs_count,
159 _FUZZER_RUN_LOGS as logs,
160 _CORPUS_BACKUP as corpus_backup,"""
161 
162 
163 class AflDefaults(BaseBuiltinFuzzerDefaults):
164   """Default values for AFL."""
165 
166   def __init__(self):
167     super(AflDefaults, self).__init__()
168     # Override empty values from parent.
169     self.name = 'afl'
170     self.key_id = 1338
171     # Use single quotes since the string ends in a double quote.
172     # pylint: disable=line-too-long
173     self.stats_column_descriptions = '''fuzzer: "Fuzz target"
174 new_crashes: "Number of new unique crashes observed during this time period"
175 edge_coverage: "Edge coverage for this fuzz target (number of edges / total)"
176 cov_report: "Link to coverage report"
177 corpus_size: "Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)"
178 avg_exec_per_sec: "Average number of testcases executed per second"
179 stability: "Percentage of edges that behave deterministically"
180 new_tests_added: "New testcases added to the corpus during fuzzing based on code coverage"
181 regular_crash_percent: "Percent of fuzzing runs that had regular crashes (other than startup and bad instrumentation crashes)"
182 timeout_percent: "Percent of fuzzing runs that had testcases timeout (should be 0)"
183 startup_crash_percent: "Percent of fuzzing runs that crashed on startup (should be 0)"
184 avg_unwanted_log_lines: "Average number of unwanted log lines in fuzzing runs (should be 0)"
185 runs_count: "Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)"
186 logs: "Link to fuzzing logs"
187 corpus_backup: "Backup copy of the minimized corpus generated based on code coverage"'''
188 
189     self.stats_columns = """custom(j.new_crashes) as new_crashes,
190 _EDGE_COV as edge_coverage,
191 _COV_REPORT as cov_report,
192 _CORPUS_SIZE as corpus_size,
193 avg(t.average_exec_per_sec) as avg_exec_per_sec,
194 avg(t.stability) as stability,
195 sum(t.new_units_added) as new_tests_added,
196 avg(t.crash_count*100) as regular_crash_percent,
197 avg(t.timeout_count*100) as timeout_percent,
198 avg(t.startup_crash_count*100) as startup_crash_percent,
199 avg(t.log_lines_unwanted) as avg_unwanted_log_lines,
200 COUNT(t.bad_instrumentation) as runs_count,
201 _FUZZER_RUN_LOGS as logs,
202 _CORPUS_BACKUP as corpus_backup,"""
203 
204 
205 def setup_config(non_dry_run):
206   """Set up configuration."""
207   config = data_types.Config.query().get()
208   if not config:
209     config = data_types.Config()
210 
211     if non_dry_run:
212       print('Creating config')
213       config.put()
214     else:
215       print('Skip creating config (dry-run mode)')
216 
217 
218 def setup_fuzzers(non_dry_run):
219   """Set up fuzzers."""
220   for fuzzer_defaults in [AflDefaults(), LibFuzzerDefaults()]:
221     fuzzer = data_types.Fuzzer.query(
222         data_types.Fuzzer.name == fuzzer_defaults.name).get()
223     if fuzzer:
224       print(fuzzer_defaults.name, 'fuzzer already exists')
225       if non_dry_run:
226         print('Updating stats metrics.')
227         fuzzer.stats_columns = fuzzer_defaults.stats_columns
228         fuzzer.stats_column_descriptions = (
229             fuzzer_defaults.stats_column_descriptions)
230         fuzzer.put()
231 
232       continue
233 
234     if non_dry_run:
235       print('Creating fuzzer', fuzzer_defaults.name)
236       fuzzer_defaults.create_fuzzer().put()
237     else:
238       print('Skip creating fuzzer', fuzzer_defaults.name, '(dry-run mode)')
239 
240 
241 def setup_templates(non_dry_run):
242   """Set up templates."""
243   for name, template in six.iteritems(TEMPLATES):
244     job = data_types.JobTemplate.query(
245         data_types.JobTemplate.name == name).get()
246     if job:
247       print('Template with name', name, 'already exists.')
248       continue
249 
250     if non_dry_run:
251       print('Creating template', name)
252       data_types.JobTemplate(name=name, environment_string=template).put()
253     else:
254       print('Skip creating template', name, '(dry-run mode)')
255 
256 
257 def setup_metrics(non_dry_run):
258   """Set up metrics."""
259   client = monitoring_v3.MetricServiceClient()
260   project_name = utils.get_application_id()
261   project_path = client.project_path(project_name)
262 
263   for name in dir(monitoring_metrics):
264     metric = getattr(monitoring_metrics, name)
265     if not isinstance(metric, monitor.Metric):
266       continue
267 
268     descriptor = monitoring_v3.types.MetricDescriptor()
269     metric.monitoring_v3_metric_descriptor(descriptor)
270 
271     if non_dry_run:
272       print('Creating metric', descriptor)
273       client.create_metric_descriptor(project_path, descriptor)
274     else:
275       print('Skip creating metric', descriptor, '(dry-run mode)')
276 
277 
278 def execute(args):
279   """Set up initial Datastore models."""
280   setup_config(args.non_dry_run)
281   setup_fuzzers(args.non_dry_run)
282   setup_templates(args.non_dry_run)
283 
284   if not args.local:
285     setup_metrics(args.non_dry_run)
286 
287   print('Done')
288
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/local/butler/scripts/setup.py b/src/local/butler/scripts/setup.py
--- a/src/local/butler/scripts/setup.py
+++ b/src/local/butler/scripts/setup.py
@@ -135,7 +135,7 @@
 timeout_percent: "Percent of fuzzing runs that had testcases timeout (should be 0)"
 startup_crash_percent: "Percent of fuzzing runs that crashed on startup (should be 0)"
 avg_unwanted_log_lines: "Average number of unwanted log lines in fuzzing runs (should be 0)"
-runs_count: "Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)"
+total_fuzzing_time_hrs: "Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently."
 logs: "Link to fuzzing logs"
 corpus_backup: "Backup copy of the minimized corpus generated based on code coverage"'''
 
@@ -155,7 +155,7 @@
 avg(t.timeout_count*100) as timeout_percent,
 avg(t.startup_crash_count*100) as startup_crash_percent,
 avg(t.log_lines_unwanted) as avg_unwanted_log_lines,
-COUNT(t.bad_instrumentation) as runs_count,
+sum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,
 _FUZZER_RUN_LOGS as logs,
 _CORPUS_BACKUP as corpus_backup,"""
 
@@ -182,7 +182,7 @@
 timeout_percent: "Percent of fuzzing runs that had testcases timeout (should be 0)"
 startup_crash_percent: "Percent of fuzzing runs that crashed on startup (should be 0)"
 avg_unwanted_log_lines: "Average number of unwanted log lines in fuzzing runs (should be 0)"
-runs_count: "Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)"
+total_fuzzing_time_hrs: "Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently."
 logs: "Link to fuzzing logs"
 corpus_backup: "Backup copy of the minimized corpus generated based on code coverage"'''
 
@@ -197,7 +197,7 @@
 avg(t.timeout_count*100) as timeout_percent,
 avg(t.startup_crash_count*100) as startup_crash_percent,
 avg(t.log_lines_unwanted) as avg_unwanted_log_lines,
-COUNT(t.bad_instrumentation) as runs_count,
+sum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,
 _FUZZER_RUN_LOGS as logs,
 _CORPUS_BACKUP as corpus_backup,"""

{"golden_diff": "diff --git a/src/local/butler/scripts/setup.py b/src/local/butler/scripts/setup.py\n--- a/src/local/butler/scripts/setup.py\n+++ b/src/local/butler/scripts/setup.py\n@@ -135,7 +135,7 @@\n timeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\n startup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\n avg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\n-runs_count: \"Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)\"\n+total_fuzzing_time_hrs: \"Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently.\"\n logs: \"Link to fuzzing logs\"\n corpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n \n@@ -155,7 +155,7 @@\n avg(t.timeout_count*100) as timeout_percent,\n avg(t.startup_crash_count*100) as startup_crash_percent,\n avg(t.log_lines_unwanted) as avg_unwanted_log_lines,\n-COUNT(t.bad_instrumentation) as runs_count,\n+sum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,\n _FUZZER_RUN_LOGS as logs,\n _CORPUS_BACKUP as corpus_backup,\"\"\"\n \n@@ -182,7 +182,7 @@\n timeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\n startup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\n avg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\n-runs_count: \"Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)\"\n+total_fuzzing_time_hrs: \"Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently.\"\n logs: \"Link to fuzzing logs\"\n corpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n \n@@ -197,7 +197,7 @@\n avg(t.timeout_count*100) as timeout_percent,\n avg(t.startup_crash_count*100) as startup_crash_percent,\n avg(t.log_lines_unwanted) as avg_unwanted_log_lines,\n-COUNT(t.bad_instrumentation) as runs_count,\n+sum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,\n _FUZZER_RUN_LOGS as logs,\n _CORPUS_BACKUP as corpus_backup,\"\"\"\n", "issue": "Show time executed / cpu hours instead of number of runs in fuzzer stats.\n\n", "before_files": [{"content": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Initial datastore setup.\"\"\"\nfrom __future__ import print_function\n\nfrom builtins import object\nimport six\n\nfrom google.cloud import monitoring_v3\n\nfrom base import utils\nfrom datastore import data_types\nfrom metrics import monitor\nfrom metrics import monitoring_metrics\n\nLIBFUZZER_TEMPLATE = \"\"\"APP_NAME = launcher.py\nMAX_FUZZ_THREADS = 1\nMAX_TESTCASES = 4\nFUZZ_TEST_TIMEOUT = 4800\nTEST_TIMEOUT = 30\nWARMUP_TIMEOUT = 30\nBAD_BUILD_CHECK = False\nTHREAD_ALIVE_CHECK_INTERVAL = 1\nREPORT_OOMS_AND_HANGS = True\nCORPUS_FUZZER_NAME_OVERRIDE = libFuzzer\nENABLE_GESTURES = False\nTHREAD_DELAY = 30.0\n\"\"\"\n\nAFL_TEMPLATE = \"\"\"APP_NAME = launcher.py\nMAX_FUZZ_THREADS = 1\nMAX_TESTCASES = 4\nFUZZ_TEST_TIMEOUT = 4800\nTEST_TIMEOUT = 30\nWARMUP_TIMEOUT = 30\nBAD_BUILD_CHECK = False\nTHREAD_ALIVE_CHECK_INTERVAL = 1\nCORPUS_FUZZER_NAME_OVERRIDE = libFuzzer\nADDITIONAL_PROCESSES_TO_KILL = afl-fuzz afl-showmap\nENABLE_GESTURES = False\nTHREAD_DELAY  = 30.0\n\"\"\"\n\nENGINE_ASAN_TEMPLATE = \"\"\"LSAN = True\nADDITIONAL_ASAN_OPTIONS = quarantine_size_mb=64:strict_memcmp=1:symbolize=0:fast_unwind_on_fatal=0:allocator_release_to_os_interval_ms=500\n\"\"\"\n\nENGINE_MSAN_TEMPLATE = ('ADDITIONAL_MSAN_OPTIONS = symbolize=0:print_stats=1:'\n                        'allocator_release_to_os_interval_ms=500:'\n                        'halt_on_error=1')\n\nENGINE_UBSAN_TEMPLATE = \"\"\"LSAN = False\nADDITIONAL_UBSAN_OPTIONS = symbolize=0:allocator_release_to_os_interval_ms=500\n\"\"\"\n\nPRUNE_TEMPLATE = 'CORPUS_PRUNE = True'\n\nTEMPLATES = {\n    'afl': AFL_TEMPLATE,\n    'engine_asan': ENGINE_ASAN_TEMPLATE,\n    'engine_msan': ENGINE_MSAN_TEMPLATE,\n    'engine_ubsan': ENGINE_UBSAN_TEMPLATE,\n    'libfuzzer': LIBFUZZER_TEMPLATE,\n    'prune': PRUNE_TEMPLATE,\n}\n\n\nclass BaseBuiltinFuzzerDefaults(object):\n  \"\"\"Default values for a builtin Fuzzer data_type. Note this class should be\n  inherited and should not be used directly.\"\"\"\n\n  def __init__(self):\n    # Set defaults for any builtin fuzzer.\n    self.revision = 1\n    self.file_size = 'builtin'\n    self.source = 'builtin'\n    self.builtin = True\n\n    # Create attributes that must be set by child classes.\n    self.name = None\n    self.stats_column_descriptions = None\n    self.stats_columns = None\n    self.key_id = None\n\n  def create_fuzzer(self):\n    \"\"\"Create a Fuzzer data_type with columns set to the defaults specified by\n    this object.\"\"\"\n    assert self.name is not None\n    return data_types.Fuzzer(\n        id=self.key_id,\n        revision=self.revision,\n        file_size=self.file_size,\n        source=self.source,\n        name=self.name,\n        builtin=self.builtin,\n        stats_column_descriptions=self.stats_column_descriptions,\n        stats_columns=self.stats_columns)\n\n\nclass LibFuzzerDefaults(BaseBuiltinFuzzerDefaults):\n  \"\"\"Default values for libFuzzer.\"\"\"\n\n  def __init__(self):\n    super(LibFuzzerDefaults, self).__init__()\n    # Override empty values from parent.\n    self.name = 'libFuzzer'\n    self.key_id = 1337\n    # Use single quotes since the string ends in a double quote.\n    # pylint: disable=line-too-long\n    self.stats_column_descriptions = '''fuzzer: \"Fuzz target\"\nperf_report: \"Link to performance analysis report\"\ntests_executed: \"Number of testcases executed during this time period\"\nnew_crashes: \"Number of new unique crashes observed during this time period\"\nedge_coverage: \"Coverage for this fuzz target (number of edges/total)\"\ncov_report: \"Link to coverage report\"\ncorpus_size: \"Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)\"\navg_exec_per_sec: \"Average number of testcases executed per second\"\nfuzzing_time_percent: \"Percent of expected fuzzing time that is actually spent fuzzing.\"\nnew_tests_added: \"New testcases added to the corpus during fuzzing based on code coverage\"\nnew_features: \"New coverage features based on new tests added to corpus.\"\nregular_crash_percent: \"Percent of fuzzing runs that had regular crashes (other than ooms, leaks, timeouts, startup and bad instrumentation crashes)\"\noom_percent: \"Percent of fuzzing runs that crashed on OOMs (should be 0)\"\nleak_percent: \"Percent of fuzzing runs that crashed on memory leaks (should be 0)\"\ntimeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\nstartup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\navg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\nruns_count: \"Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)\"\nlogs: \"Link to fuzzing logs\"\ncorpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n\n    self.stats_columns = \"\"\"_PERFORMANCE_REPORT as perf_report,\nsum(t.number_of_executed_units) as tests_executed,\ncustom(j.new_crashes) as new_crashes,\n_EDGE_COV as edge_coverage,\n_COV_REPORT as cov_report,\n_CORPUS_SIZE as corpus_size,\navg(t.average_exec_per_sec) as avg_exec_per_sec,\navg(t.fuzzing_time_percent) as fuzzing_time_percent,\nsum(t.new_units_added) as new_tests_added,\nsum(t.new_features) as new_features,\navg(t.crash_count*100) as regular_crash_percent,\navg(t.oom_count*100) as oom_percent,\navg(t.leak_count*100) as leak_percent,\navg(t.timeout_count*100) as timeout_percent,\navg(t.startup_crash_count*100) as startup_crash_percent,\navg(t.log_lines_unwanted) as avg_unwanted_log_lines,\nCOUNT(t.bad_instrumentation) as runs_count,\n_FUZZER_RUN_LOGS as logs,\n_CORPUS_BACKUP as corpus_backup,\"\"\"\n\n\nclass AflDefaults(BaseBuiltinFuzzerDefaults):\n  \"\"\"Default values for AFL.\"\"\"\n\n  def __init__(self):\n    super(AflDefaults, self).__init__()\n    # Override empty values from parent.\n    self.name = 'afl'\n    self.key_id = 1338\n    # Use single quotes since the string ends in a double quote.\n    # pylint: disable=line-too-long\n    self.stats_column_descriptions = '''fuzzer: \"Fuzz target\"\nnew_crashes: \"Number of new unique crashes observed during this time period\"\nedge_coverage: \"Edge coverage for this fuzz target (number of edges / total)\"\ncov_report: \"Link to coverage report\"\ncorpus_size: \"Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)\"\navg_exec_per_sec: \"Average number of testcases executed per second\"\nstability: \"Percentage of edges that behave deterministically\"\nnew_tests_added: \"New testcases added to the corpus during fuzzing based on code coverage\"\nregular_crash_percent: \"Percent of fuzzing runs that had regular crashes (other than startup and bad instrumentation crashes)\"\ntimeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\nstartup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\navg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\nruns_count: \"Number of libFuzzer fuzzing runs or sessions (default is ~1 hr)\"\nlogs: \"Link to fuzzing logs\"\ncorpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n\n    self.stats_columns = \"\"\"custom(j.new_crashes) as new_crashes,\n_EDGE_COV as edge_coverage,\n_COV_REPORT as cov_report,\n_CORPUS_SIZE as corpus_size,\navg(t.average_exec_per_sec) as avg_exec_per_sec,\navg(t.stability) as stability,\nsum(t.new_units_added) as new_tests_added,\navg(t.crash_count*100) as regular_crash_percent,\navg(t.timeout_count*100) as timeout_percent,\navg(t.startup_crash_count*100) as startup_crash_percent,\navg(t.log_lines_unwanted) as avg_unwanted_log_lines,\nCOUNT(t.bad_instrumentation) as runs_count,\n_FUZZER_RUN_LOGS as logs,\n_CORPUS_BACKUP as corpus_backup,\"\"\"\n\n\ndef setup_config(non_dry_run):\n  \"\"\"Set up configuration.\"\"\"\n  config = data_types.Config.query().get()\n  if not config:\n    config = data_types.Config()\n\n    if non_dry_run:\n      print('Creating config')\n      config.put()\n    else:\n      print('Skip creating config (dry-run mode)')\n\n\ndef setup_fuzzers(non_dry_run):\n  \"\"\"Set up fuzzers.\"\"\"\n  for fuzzer_defaults in [AflDefaults(), LibFuzzerDefaults()]:\n    fuzzer = data_types.Fuzzer.query(\n        data_types.Fuzzer.name == fuzzer_defaults.name).get()\n    if fuzzer:\n      print(fuzzer_defaults.name, 'fuzzer already exists')\n      if non_dry_run:\n        print('Updating stats metrics.')\n        fuzzer.stats_columns = fuzzer_defaults.stats_columns\n        fuzzer.stats_column_descriptions = (\n            fuzzer_defaults.stats_column_descriptions)\n        fuzzer.put()\n\n      continue\n\n    if non_dry_run:\n      print('Creating fuzzer', fuzzer_defaults.name)\n      fuzzer_defaults.create_fuzzer().put()\n    else:\n      print('Skip creating fuzzer', fuzzer_defaults.name, '(dry-run mode)')\n\n\ndef setup_templates(non_dry_run):\n  \"\"\"Set up templates.\"\"\"\n  for name, template in six.iteritems(TEMPLATES):\n    job = data_types.JobTemplate.query(\n        data_types.JobTemplate.name == name).get()\n    if job:\n      print('Template with name', name, 'already exists.')\n      continue\n\n    if non_dry_run:\n      print('Creating template', name)\n      data_types.JobTemplate(name=name, environment_string=template).put()\n    else:\n      print('Skip creating template', name, '(dry-run mode)')\n\n\ndef setup_metrics(non_dry_run):\n  \"\"\"Set up metrics.\"\"\"\n  client = monitoring_v3.MetricServiceClient()\n  project_name = utils.get_application_id()\n  project_path = client.project_path(project_name)\n\n  for name in dir(monitoring_metrics):\n    metric = getattr(monitoring_metrics, name)\n    if not isinstance(metric, monitor.Metric):\n      continue\n\n    descriptor = monitoring_v3.types.MetricDescriptor()\n    metric.monitoring_v3_metric_descriptor(descriptor)\n\n    if non_dry_run:\n      print('Creating metric', descriptor)\n      client.create_metric_descriptor(project_path, descriptor)\n    else:\n      print('Skip creating metric', descriptor, '(dry-run mode)')\n\n\ndef execute(args):\n  \"\"\"Set up initial Datastore models.\"\"\"\n  setup_config(args.non_dry_run)\n  setup_fuzzers(args.non_dry_run)\n  setup_templates(args.non_dry_run)\n\n  if not args.local:\n    setup_metrics(args.non_dry_run)\n\n  print('Done')\n", "path": "src/local/butler/scripts/setup.py"}], "after_files": [{"content": "# Copyright 2019 Google LLC\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\"\"\"Initial datastore setup.\"\"\"\nfrom __future__ import print_function\n\nfrom builtins import object\nimport six\n\nfrom google.cloud import monitoring_v3\n\nfrom base import utils\nfrom datastore import data_types\nfrom metrics import monitor\nfrom metrics import monitoring_metrics\n\nLIBFUZZER_TEMPLATE = \"\"\"APP_NAME = launcher.py\nMAX_FUZZ_THREADS = 1\nMAX_TESTCASES = 4\nFUZZ_TEST_TIMEOUT = 4800\nTEST_TIMEOUT = 30\nWARMUP_TIMEOUT = 30\nBAD_BUILD_CHECK = False\nTHREAD_ALIVE_CHECK_INTERVAL = 1\nREPORT_OOMS_AND_HANGS = True\nCORPUS_FUZZER_NAME_OVERRIDE = libFuzzer\nENABLE_GESTURES = False\nTHREAD_DELAY = 30.0\n\"\"\"\n\nAFL_TEMPLATE = \"\"\"APP_NAME = launcher.py\nMAX_FUZZ_THREADS = 1\nMAX_TESTCASES = 4\nFUZZ_TEST_TIMEOUT = 4800\nTEST_TIMEOUT = 30\nWARMUP_TIMEOUT = 30\nBAD_BUILD_CHECK = False\nTHREAD_ALIVE_CHECK_INTERVAL = 1\nCORPUS_FUZZER_NAME_OVERRIDE = libFuzzer\nADDITIONAL_PROCESSES_TO_KILL = afl-fuzz afl-showmap\nENABLE_GESTURES = False\nTHREAD_DELAY  = 30.0\n\"\"\"\n\nENGINE_ASAN_TEMPLATE = \"\"\"LSAN = True\nADDITIONAL_ASAN_OPTIONS = quarantine_size_mb=64:strict_memcmp=1:symbolize=0:fast_unwind_on_fatal=0:allocator_release_to_os_interval_ms=500\n\"\"\"\n\nENGINE_MSAN_TEMPLATE = ('ADDITIONAL_MSAN_OPTIONS = symbolize=0:print_stats=1:'\n                        'allocator_release_to_os_interval_ms=500:'\n                        'halt_on_error=1')\n\nENGINE_UBSAN_TEMPLATE = \"\"\"LSAN = False\nADDITIONAL_UBSAN_OPTIONS = symbolize=0:allocator_release_to_os_interval_ms=500\n\"\"\"\n\nPRUNE_TEMPLATE = 'CORPUS_PRUNE = True'\n\nTEMPLATES = {\n    'afl': AFL_TEMPLATE,\n    'engine_asan': ENGINE_ASAN_TEMPLATE,\n    'engine_msan': ENGINE_MSAN_TEMPLATE,\n    'engine_ubsan': ENGINE_UBSAN_TEMPLATE,\n    'libfuzzer': LIBFUZZER_TEMPLATE,\n    'prune': PRUNE_TEMPLATE,\n}\n\n\nclass BaseBuiltinFuzzerDefaults(object):\n  \"\"\"Default values for a builtin Fuzzer data_type. Note this class should be\n  inherited and should not be used directly.\"\"\"\n\n  def __init__(self):\n    # Set defaults for any builtin fuzzer.\n    self.revision = 1\n    self.file_size = 'builtin'\n    self.source = 'builtin'\n    self.builtin = True\n\n    # Create attributes that must be set by child classes.\n    self.name = None\n    self.stats_column_descriptions = None\n    self.stats_columns = None\n    self.key_id = None\n\n  def create_fuzzer(self):\n    \"\"\"Create a Fuzzer data_type with columns set to the defaults specified by\n    this object.\"\"\"\n    assert self.name is not None\n    return data_types.Fuzzer(\n        id=self.key_id,\n        revision=self.revision,\n        file_size=self.file_size,\n        source=self.source,\n        name=self.name,\n        builtin=self.builtin,\n        stats_column_descriptions=self.stats_column_descriptions,\n        stats_columns=self.stats_columns)\n\n\nclass LibFuzzerDefaults(BaseBuiltinFuzzerDefaults):\n  \"\"\"Default values for libFuzzer.\"\"\"\n\n  def __init__(self):\n    super(LibFuzzerDefaults, self).__init__()\n    # Override empty values from parent.\n    self.name = 'libFuzzer'\n    self.key_id = 1337\n    # Use single quotes since the string ends in a double quote.\n    # pylint: disable=line-too-long\n    self.stats_column_descriptions = '''fuzzer: \"Fuzz target\"\nperf_report: \"Link to performance analysis report\"\ntests_executed: \"Number of testcases executed during this time period\"\nnew_crashes: \"Number of new unique crashes observed during this time period\"\nedge_coverage: \"Coverage for this fuzz target (number of edges/total)\"\ncov_report: \"Link to coverage report\"\ncorpus_size: \"Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)\"\navg_exec_per_sec: \"Average number of testcases executed per second\"\nfuzzing_time_percent: \"Percent of expected fuzzing time that is actually spent fuzzing.\"\nnew_tests_added: \"New testcases added to the corpus during fuzzing based on code coverage\"\nnew_features: \"New coverage features based on new tests added to corpus.\"\nregular_crash_percent: \"Percent of fuzzing runs that had regular crashes (other than ooms, leaks, timeouts, startup and bad instrumentation crashes)\"\noom_percent: \"Percent of fuzzing runs that crashed on OOMs (should be 0)\"\nleak_percent: \"Percent of fuzzing runs that crashed on memory leaks (should be 0)\"\ntimeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\nstartup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\navg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\ntotal_fuzzing_time_hrs: \"Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently.\"\nlogs: \"Link to fuzzing logs\"\ncorpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n\n    self.stats_columns = \"\"\"_PERFORMANCE_REPORT as perf_report,\nsum(t.number_of_executed_units) as tests_executed,\ncustom(j.new_crashes) as new_crashes,\n_EDGE_COV as edge_coverage,\n_COV_REPORT as cov_report,\n_CORPUS_SIZE as corpus_size,\navg(t.average_exec_per_sec) as avg_exec_per_sec,\navg(t.fuzzing_time_percent) as fuzzing_time_percent,\nsum(t.new_units_added) as new_tests_added,\nsum(t.new_features) as new_features,\navg(t.crash_count*100) as regular_crash_percent,\navg(t.oom_count*100) as oom_percent,\navg(t.leak_count*100) as leak_percent,\navg(t.timeout_count*100) as timeout_percent,\navg(t.startup_crash_count*100) as startup_crash_percent,\navg(t.log_lines_unwanted) as avg_unwanted_log_lines,\nsum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,\n_FUZZER_RUN_LOGS as logs,\n_CORPUS_BACKUP as corpus_backup,\"\"\"\n\n\nclass AflDefaults(BaseBuiltinFuzzerDefaults):\n  \"\"\"Default values for AFL.\"\"\"\n\n  def __init__(self):\n    super(AflDefaults, self).__init__()\n    # Override empty values from parent.\n    self.name = 'afl'\n    self.key_id = 1338\n    # Use single quotes since the string ends in a double quote.\n    # pylint: disable=line-too-long\n    self.stats_column_descriptions = '''fuzzer: \"Fuzz target\"\nnew_crashes: \"Number of new unique crashes observed during this time period\"\nedge_coverage: \"Edge coverage for this fuzz target (number of edges / total)\"\ncov_report: \"Link to coverage report\"\ncorpus_size: \"Size of the minimized corpus generated based on code coverage (number of testcases and total size on disk)\"\navg_exec_per_sec: \"Average number of testcases executed per second\"\nstability: \"Percentage of edges that behave deterministically\"\nnew_tests_added: \"New testcases added to the corpus during fuzzing based on code coverage\"\nregular_crash_percent: \"Percent of fuzzing runs that had regular crashes (other than startup and bad instrumentation crashes)\"\ntimeout_percent: \"Percent of fuzzing runs that had testcases timeout (should be 0)\"\nstartup_crash_percent: \"Percent of fuzzing runs that crashed on startup (should be 0)\"\navg_unwanted_log_lines: \"Average number of unwanted log lines in fuzzing runs (should be 0)\"\ntotal_fuzzing_time_hrs: \"Total time in hours for which the fuzzer(s) ran. Will be lower if fuzzer hits a crash frequently.\"\nlogs: \"Link to fuzzing logs\"\ncorpus_backup: \"Backup copy of the minimized corpus generated based on code coverage\"'''\n\n    self.stats_columns = \"\"\"custom(j.new_crashes) as new_crashes,\n_EDGE_COV as edge_coverage,\n_COV_REPORT as cov_report,\n_CORPUS_SIZE as corpus_size,\navg(t.average_exec_per_sec) as avg_exec_per_sec,\navg(t.stability) as stability,\nsum(t.new_units_added) as new_tests_added,\navg(t.crash_count*100) as regular_crash_percent,\navg(t.timeout_count*100) as timeout_percent,\navg(t.startup_crash_count*100) as startup_crash_percent,\navg(t.log_lines_unwanted) as avg_unwanted_log_lines,\nsum(t.actual_duration/3600.0) as total_fuzzing_time_hrs,\n_FUZZER_RUN_LOGS as logs,\n_CORPUS_BACKUP as corpus_backup,\"\"\"\n\n\ndef setup_config(non_dry_run):\n  \"\"\"Set up configuration.\"\"\"\n  config = data_types.Config.query().get()\n  if not config:\n    config = data_types.Config()\n\n    if non_dry_run:\n      print('Creating config')\n      config.put()\n    else:\n      print('Skip creating config (dry-run mode)')\n\n\ndef setup_fuzzers(non_dry_run):\n  \"\"\"Set up fuzzers.\"\"\"\n  for fuzzer_defaults in [AflDefaults(), LibFuzzerDefaults()]:\n    fuzzer = data_types.Fuzzer.query(\n        data_types.Fuzzer.name == fuzzer_defaults.name).get()\n    if fuzzer:\n      print(fuzzer_defaults.name, 'fuzzer already exists')\n      if non_dry_run:\n        print('Updating stats metrics.')\n        fuzzer.stats_columns = fuzzer_defaults.stats_columns\n        fuzzer.stats_column_descriptions = (\n            fuzzer_defaults.stats_column_descriptions)\n        fuzzer.put()\n\n      continue\n\n    if non_dry_run:\n      print('Creating fuzzer', fuzzer_defaults.name)\n      fuzzer_defaults.create_fuzzer().put()\n    else:\n      print('Skip creating fuzzer', fuzzer_defaults.name, '(dry-run mode)')\n\n\ndef setup_templates(non_dry_run):\n  \"\"\"Set up templates.\"\"\"\n  for name, template in six.iteritems(TEMPLATES):\n    job = data_types.JobTemplate.query(\n        data_types.JobTemplate.name == name).get()\n    if job:\n      print('Template with name', name, 'already exists.')\n      continue\n\n    if non_dry_run:\n      print('Creating template', name)\n      data_types.JobTemplate(name=name, environment_string=template).put()\n    else:\n      print('Skip creating template', name, '(dry-run mode)')\n\n\ndef setup_metrics(non_dry_run):\n  \"\"\"Set up metrics.\"\"\"\n  client = monitoring_v3.MetricServiceClient()\n  project_name = utils.get_application_id()\n  project_path = client.project_path(project_name)\n\n  for name in dir(monitoring_metrics):\n    metric = getattr(monitoring_metrics, name)\n    if not isinstance(metric, monitor.Metric):\n      continue\n\n    descriptor = monitoring_v3.types.MetricDescriptor()\n    metric.monitoring_v3_metric_descriptor(descriptor)\n\n    if non_dry_run:\n      print('Creating metric', descriptor)\n      client.create_metric_descriptor(project_path, descriptor)\n    else:\n      print('Skip creating metric', descriptor, '(dry-run mode)')\n\n\ndef execute(args):\n  \"\"\"Set up initial Datastore models.\"\"\"\n  setup_config(args.non_dry_run)\n  setup_fuzzers(args.non_dry_run)\n  setup_templates(args.non_dry_run)\n\n  if not args.local:\n    setup_metrics(args.non_dry_run)\n\n  print('Done')\n", "path": "src/local/butler/scripts/setup.py"}]}