Skip to content

Commit 4abb66c

Browse files
authored
Migrate over to using pathlib and subprocess.run() (#198) r=truber
* Refactor code involving resource import to not rely on os.name * Replace various os/os.path usages with pathlib2/pathlib. * Ignore pylint "no-member" false positive error for now. * Remove getAbsPathForAdjacentFile and normExpUserPath. * Fix #23 - Remove captureStdout and use subprocess32/subprocess from PyPI/stdlib throughout. * Remove unused imports. * subprocess-related follow-up fix. * pathlib-related follow-up fix. * Ignore more pylint "no-member" false positives. * Add comment related to is_win_dumping_to_default raising WindowsError in some circumstances. * In Python 3, winreg functions usually throw OSError instead of WindowsError. * Oops, WindowsError is a subclass of OSError. * Revert "Ignore more pylint "no-member" false positives." This reverts commit 7bf932f. * Revert "Ignore pylint "no-member" false positive error for now." This reverts commit 42865b6. * Remove last no-member ignore line false-positive. * Continue to ignore no-member pylint errors until newer linter libraries get released. * Inline scanLine function, then move last detect_malloc_errors function "amiss" into util/file_manipulation.py * Replace logPrefix file-renaming and suffix-changing with the proper pathlib way of doing so. * Deal with .fuzzmanagerconf files using pathlib. * Convert Paths to make them Lithium-friendly. * Check file sizes in truncateFile using the pathlib way. * More pathlib fixes. * We should assert the existence of the concatenated jsfunfuzz file a little later. * Rename the autoBisectLogFilename and autoBisectLog variables. * Make handle_rm_readonly Windows-only for simplification since we only use it on Windows. * Remove superfluous __name__/__main__ line in bot.py * Python 3 only allows binary mode when using open() with buffering=0 set. * Use the parent/stem/suffix model of pathlib in more places. * Make runthis retain its Path components and only convert to string when calling Lithium functions. * Make quote usage first convert items to strings. * Replace the filenameToReduce variable name with reduced_log. * Force shutil usages to operate on strings. * Rename _ to branch. * Follow-up fix. * Whitespace fixes. * Fix/remove invalid comments as indicated during review. * Remove more comments. * Stop using range and use enumerate instead as suggested in review. * More pathlib fixes. * Remove bad line. * Fix pathlib issue related to get_shell_cache_js_bin_path. * Yet more pathlib fixes. * Comment and whitespace changes. * Refactor getCfgCmdExclEnv to get_cfg_cmd_excl_env and setCfgCmdExclEnv to set_cfg_cmd_excl_env. * self.cfg should be instantiated as a list in compile_shell. * Add a docstring for CompiledShell class. * Inline updateRepo and remove the function. * fork_join tests should become actual tests for pytest, commenting them out for now. * Make a tempfile import top-level and use the backports.tempfile version from PyPI for Python 2. * Remove unneeded 'if __name__ == "__main__":' lines. * Remove unneeded imports from loop.py * Consolidate subprocess32 import with others in compare_jit.py * Rename printMachineInfo to print_machine_info and refactor it a little. * Defend against logPrefix not being a Path, e.g. when Lithium uses it, it is likely a string. * Rename tempPrefix to cwd_prefix. * Make cwd_prefix explicit in using Path.cwd() when it is used. * Convert remaining subprocess calls to use subprocess.run() instead. * Refactor getEnvAdded to get_env_added and setEnvAdded to set_env_added. * Fix potential issue in get_shell_compiled_runlibs_path. * We must call Path on a path, not potentially a symlink. * Add tests for autoconf_run and ensure_cache_dir. * More documentation for functions in the CompiledShell class. * Add more documentation for compare_jit function in compare_jit. * Fix missing imports. * Remove print_function from files that do not have any print functions. * Add unicode_literals to the future import line of all files. * Refactor ignoreSomeOfStderr to ignore_some_stderr and fix pylint issues. * Re-add some pylint ignores for the compare_jit function. * Refactor getRepoName to get_repo_name. * Refactor getS3TarballWithExt to get_s3_tar_name_with_ext. * Remove obsolete pylint ignore line. * Refactor getEnvFull to get_env_full and setEnvFull to set_env_full. * getMajorVersion and setMajorVersion seem unnecessary since they are just used/fixated off set/getVersion and both are set at the same time. * Refactor getVersion to get_version and setVersion to set_version. * Fix str/unicode confusion in repos_update. * WIP: Add some FIXME comments, they really should be fixed. * Fix pylint errors in test_autoconf_run. * Fix the test_ensure_cache_dir test. * Remove extra whitespace. * Split off some helper functions from compile_shell.py into sm_compile_helpers.py * Inline code involving version in compile_shell. * Mostly stop catching generic Exceptions in compile_shell. * Refactor compileJs to sm_compile and fix previous WIP/FIXME items. * Use io.open instead of open throughout. * Fix more issues involving the move of functions to sm_compile_helpers. * Fix encoding issues with io.open * Remove unneeded parenthesis. * Fix more encoding issues with io.open in tests. * Binary mode of io.open does not take an encoding argument. * Fix another encoding issue. * Lithium only takes in strings, not Paths nor bytes. * SyntaxError fix. * Revert "Remove unneeded 'if __name__ == "__main__":' lines." This reverts commit 1eddcfc. * Cast more paths to strings before decoding them. * Most repo_dirs are now Paths. * Fix missing imports. * Improve dual Python 2/3 code. * knownPath is still present so account for it for now. * Yet another pathlib issue. * Ensure that cwd_prefix is a Path in js_interesting. * Collector takes in strings, not Paths. * Expand the repo_dir path first, in build_options. * Add an unrelated pylint ignore line. * Run Paths in the parsing function of compare_jit through .expanduser() first. * Raise an OSError if a file does not exist in the parsing function of compare_jit instead of a generic Exception. * Ensure cwd_prefix is a Path in compare_jit. * When running compare_jit standalone, ensure the temp folder that is created returns its result in the form of a Path. * Path does not care that it is already working on a Path instance. * Make sure also to run pathToBinary through .expanduser() * Add more pylint ignore lines to suit PyPy3.
1 parent 35b5e09 commit 4abb66c

36 files changed

Lines changed: 2087 additions & 1389 deletions

src/funfuzz/autobisectjs/autobisectjs.py

Lines changed: 101 additions & 58 deletions
Large diffs are not rendered by default.

src/funfuzz/autobisectjs/known_broken_earliest_working.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"""Known broken changeset ranges of SpiderMonkey are specified in this file.
88
"""
99

10-
from __future__ import absolute_import, print_function # isort:skip
10+
from __future__ import absolute_import, unicode_literals # isort:skip
1111

1212
import os
1313
import platform
@@ -129,7 +129,8 @@ def earliest_known_working_rev(options, flags, skip_revs): # pylint: disable=mi
129129
# Note that the sed version check only works with GNU sed, not BSD sed found in macOS.
130130
if (platform.system() == "Linux" and
131131
parse_version(subprocess.run(["sed", "--version"],
132-
stdout=subprocess.PIPE).stdout.split()[3]) >= parse_version("4.3")):
132+
stdout=subprocess.PIPE).stdout.decode("utf-8", errors="replace").split()[3])
133+
>= parse_version("4.3")):
133134
required.append("ebcbf47a83e7") # m-c 328765 Fx53, 1st w/ working builds using sed 4.3+ found on Ubuntu 17.04+
134135
if options.disableProfiling:
135136
required.append("800a887c705e") # m-c 324836 Fx53, 1st w/ --disable-profiling, see bug 1321065

src/funfuzz/bot.py

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@
88
99
"""
1010

11-
from __future__ import absolute_import, division, print_function # isort:skip
11+
from __future__ import absolute_import, division, print_function, unicode_literals # isort:skip
1212

1313
from builtins import object # pylint: disable=redefined-builtin
14+
import io
1415
import multiprocessing
1516
from optparse import OptionParser # pylint: disable=deprecated-module
1617
import os
@@ -20,19 +21,26 @@
2021
import tempfile
2122
import time
2223

24+
from whichcraft import which
25+
2326
from .js import build_options
2427
from .js import compile_shell
2528
from .js import loop
2629
from .util import create_collector
2730
from .util import fork_join
2831
from .util import hg_helpers
29-
from .util import subprocesses as sps
32+
from .util import sm_compile_helpers
3033
from .util.lock_dir import LockDir
3134

3235
if sys.version_info.major == 2:
36+
from pathlib2 import Path
3337
import psutil
38+
if os.name == "posix":
39+
import subprocess32 as subprocess # pylint: disable=import-error
40+
else:
41+
import subprocess
42+
from pathlib import Path # pylint: disable=import-error
3443

35-
path0 = os.path.dirname(os.path.abspath(__file__)) # pylint: disable=invalid-name
3644
JS_SHELL_DEFAULT_TIMEOUT = 24 # see comments in loop for tradeoffs
3745

3846

@@ -84,7 +92,8 @@ def parseOpts(): # pylint: disable=invalid-name,missing-docstring,missing-retur
8492
if args:
8593
print("Warning: bot does not use positional arguments")
8694

87-
if not options.useTreeherderBuilds and not os.path.isdir(build_options.DEFAULT_TREES_LOCATION):
95+
# pylint: disable=no-member
96+
if not options.useTreeherderBuilds and not build_options.DEFAULT_TREES_LOCATION.is_dir():
8897
# We don't have trees, so we must use treeherder builds.
8998
options.useTreeherderBuilds = True
9099
print()
@@ -102,11 +111,11 @@ def parseOpts(): # pylint: disable=invalid-name,missing-docstring,missing-retur
102111

103112

104113
def main(): # pylint: disable=missing-docstring
105-
printMachineInfo()
114+
print_machine_info()
106115

107116
options = parseOpts()
108117

109-
collector = create_collector.createCollector("jsfunfuzz")
118+
collector = create_collector.make_collector()
110119
try:
111120
collector.refresh()
112121
except RuntimeError as ex:
@@ -118,10 +127,10 @@ def main(): # pylint: disable=missing-docstring
118127
print(options.tempDir)
119128

120129
build_info = ensureBuild(options)
121-
assert os.path.isdir(build_info.buildDir)
130+
assert build_info.buildDir.is_dir()
122131

123132
number_of_processes = multiprocessing.cpu_count()
124-
if "-asan" in build_info.buildDir:
133+
if "-asan" in str(build_info.buildDir):
125134
# This should really be based on the amount of RAM available, but I don't know how to compute that in Python.
126135
# I could guess 1 GB RAM per core, but that wanders into sketchyville.
127136
number_of_processes = max(number_of_processes // 2, 1)
@@ -131,41 +140,41 @@ def main(): # pylint: disable=missing-docstring
131140
shutil.rmtree(options.tempDir)
132141

133142

134-
def printMachineInfo(): # pylint: disable=invalid-name
143+
def print_machine_info():
135144
"""Log information about the machine."""
136145
print("Platform details: %s" % " ".join(platform.uname()))
137-
print("hg version: %s" % sps.captureStdout(["hg", "-q", "version"])[0])
138146

139-
# In here temporarily to see if mock Linux slaves on TBPL have gdb installed
140-
try:
141-
print("gdb version: %s" % sps.captureStdout(["gdb", "--version"], combineStderr=True,
142-
ignoreStderr=True, ignoreExitCode=True)[0])
143-
except (KeyboardInterrupt, Exception) as ex: # pylint: disable=broad-except
144-
print("Error involving gdb is: %r" % (ex,))
145-
146-
# FIXME: Should have if os.path.exists(path to git) or something # pylint: disable=fixme
147-
# print("git version: %s" % sps.captureStdout(["git", "--version"], combineStderr=True,
148-
# ignoreStderr=True, ignoreExitCode=True)[0])
147+
print("hg info: %s" % subprocess.run(["hg", "-q", "version"], check=True, stdout=subprocess.PIPE).stdout.rstrip())
148+
if which("gdb"):
149+
gdb_version = subprocess.run(["gdb", "--version"],
150+
stdout=subprocess.PIPE).stdout.decode("utf-8", errors="replace")
151+
print("gdb info: %s" % gdb_version.split("\n")[0])
152+
if which("git"):
153+
print("git info: %s" % subprocess.run(["git", "version"], check=True, stdout=subprocess.PIPE).stdout.rstrip())
149154
print("Python version: %s" % sys.version.split()[0])
155+
150156
print("Number of cores visible to OS: %d" % multiprocessing.cpu_count())
151157
if sys.version_info.major == 2:
152158
rootdir_free_space = psutil.disk_usage("/").free / (1024 ** 3)
153159
else:
154160
rootdir_free_space = shutil.disk_usage("/").free / (1024 ** 3) # pylint: disable=no-member
155161
print("Free space (GB): %.2f" % rootdir_free_space)
156162

157-
hgrc_path = os.path.join(path0, ".hg", "hgrc")
158-
if os.path.isfile(hgrc_path):
163+
hgrc_path = Path("~/.hg/hgrc").expanduser()
164+
if hgrc_path.is_file():
159165
print("The hgrc of this repository is:")
160-
with open(hgrc_path, "r") as f:
166+
with io.open(str(hgrc_path), "r", encoding="utf-8", errors="replace") as f:
161167
hgrc_contents = f.readlines()
162168
for line in hgrc_contents:
163169
print(line.rstrip())
164170

165-
if os.name == "posix":
171+
try:
166172
# resource library is only applicable to Linux or Mac platforms.
167173
import resource # pylint: disable=import-error
174+
# pylint: disable=no-member
168175
print("Corefile size (soft limit, hard limit) is: %r" % (resource.getrlimit(resource.RLIMIT_CORE),))
176+
except ImportError:
177+
print("Not checking corefile size as resource module is unavailable")
169178

170179

171180
def ensureBuild(options): # pylint: disable=invalid-name,missing-docstring,missing-return-doc,missing-return-type-doc
@@ -177,16 +186,16 @@ def ensureBuild(options): # pylint: disable=invalid-name,missing-docstring,miss
177186
bRev = "" # pylint: disable=invalid-name
178187
manyTimedRunArgs = [] # pylint: disable=invalid-name
179188
elif not options.useTreeherderBuilds:
180-
options.build_options = build_options.parseShellOptions(options.build_options)
189+
options.build_options = build_options.parse_shell_opts(options.build_options)
181190
options.timeout = options.timeout or (300 if options.build_options.runWithVg else JS_SHELL_DEFAULT_TIMEOUT)
182191

183-
with LockDir(compile_shell.getLockDirPath(options.build_options.repoDir)):
184-
bRev = hg_helpers.getRepoHashAndId(options.build_options.repoDir)[0] # pylint: disable=invalid-name
192+
with LockDir(sm_compile_helpers.get_lock_dir_path(Path.home(), options.build_options.repo_dir)):
193+
bRev = hg_helpers.get_repo_hash_and_id(options.build_options.repo_dir)[0] # pylint: disable=invalid-name
185194
cshell = compile_shell.CompiledShell(options.build_options, bRev)
186-
updateLatestTxt = (options.build_options.repoDir == "mozilla-central") # pylint: disable=invalid-name
195+
updateLatestTxt = (options.build_options.repo_dir == "mozilla-central") # pylint: disable=invalid-name
187196
compile_shell.obtainShell(cshell, updateLatestTxt=updateLatestTxt)
188197

189-
bDir = cshell.getShellCacheDir() # pylint: disable=invalid-name
198+
bDir = cshell.get_shell_cache_dir() # pylint: disable=invalid-name
190199
# Strip out first 3 chars or else the dir name in fuzzing jobs becomes:
191200
# js-js-dbg-opt-64-dm-linux
192201
bType = build_options.computeShellType(options.build_options)[3:] # pylint: disable=invalid-name
@@ -199,9 +208,9 @@ def ensureBuild(options): # pylint: disable=invalid-name,missing-docstring,miss
199208
"==============================================\n\n" % (
200209
"funfuzz.js.compile_shell",
201210
options.build_options.build_options_str,
202-
options.build_options.repoDir,
211+
options.build_options.repo_dir,
203212
bRev,
204-
cshell.getRepoName(),
213+
cshell.get_repo_name(),
205214
time.asctime()
206215
))
207216

@@ -216,15 +225,15 @@ def ensureBuild(options): # pylint: disable=invalid-name,missing-docstring,miss
216225

217226

218227
def loopFuzzingAndReduction(options, buildInfo, collector, i): # pylint: disable=invalid-name,missing-docstring
219-
tempDir = tempfile.mkdtemp("loop" + str(i)) # pylint: disable=invalid-name
228+
tempDir = Path(tempfile.mkdtemp("loop" + str(i))) # pylint: disable=invalid-name
220229
loop.many_timed_runs(options.targetTime, tempDir, buildInfo.mtrArgs, collector)
221230

222231

223232
def mtrArgsCreation(options, cshell): # pylint: disable=invalid-name,missing-param-doc,missing-return-doc
224233
# pylint: disable=missing-return-type-doc,missing-type-doc
225234
"""Create many_timed_run arguments for compiled builds."""
226235
manyTimedRunArgs = [] # pylint: disable=invalid-name
227-
manyTimedRunArgs.append("--repo=" + sps.normExpUserPath(options.build_options.repoDir))
236+
manyTimedRunArgs.append("--repo=%s" % options.build_options.repo_dir)
228237
manyTimedRunArgs.append("--build=" + options.build_options.build_options_str)
229238
if options.build_options.runWithVg:
230239
manyTimedRunArgs.append("--valgrind")
@@ -236,10 +245,6 @@ def mtrArgsCreation(options, cshell): # pylint: disable=invalid-name,missing-pa
236245

237246
# Ordering of elements in manyTimedRunArgs is important.
238247
manyTimedRunArgs.append(str(options.timeout))
239-
manyTimedRunArgs.append(cshell.getRepoName()) # known bugs' directory
240-
manyTimedRunArgs.append(cshell.getShellCacheFullPath())
248+
manyTimedRunArgs.append(cshell.get_repo_name()) # known bugs' directory
249+
manyTimedRunArgs.append(cshell.get_shell_cache_js_bin_path())
241250
return manyTimedRunArgs
242-
243-
244-
if __name__ == "__main__":
245-
main()

src/funfuzz/ccoverage/get_build.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,17 @@
1111

1212
import io
1313
import logging
14-
import os
14+
import sys
1515
import tarfile
1616
import zipfile
1717

1818
import requests
1919

20+
if sys.version_info.major == 2:
21+
from pathlib2 import Path
22+
else:
23+
from pathlib import Path # pylint: disable=import-error
24+
2025
RUN_COV_LOG = logging.getLogger("funfuzz")
2126

2227

@@ -41,7 +46,8 @@ def get_coverage_build(dirpath, args):
4146

4247
js_cov_bin_name = "js"
4348
js_cov_bin = extract_folder / "dist" / "bin" / js_cov_bin_name
44-
os.chmod(str(js_cov_bin), os.stat(str(js_cov_bin)).st_mode | 0o111) # Ensure the js binary is executable
49+
50+
Path.chmod(js_cov_bin, Path.stat(js_cov_bin).st_mode | 0o111) # Ensure the js binary is executable
4551
assert js_cov_bin.is_file()
4652

4753
# Check that the binary is non-debug.

src/funfuzz/js/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,6 @@
1313
from . import compile_shell
1414
from . import inspect_shell
1515
from . import js_interesting
16+
from . import link_fuzzer
1617
from . import loop
1718
from . import shell_flags

0 commit comments

Comments
 (0)