├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── after-fix.txt ├── bot.py ├── detect ├── detect_assertions.py ├── detect_leaks.py ├── detect_malloc_errors.py ├── detectors.py └── findIgnoreLists.py ├── dom ├── .jshintrc ├── README.md ├── automation │ ├── bool-prefs.txt │ ├── bot.py │ ├── buildBrowser.py │ ├── constant-prefs.js │ ├── domInteresting.py │ ├── echoServer.py │ ├── how-to-use.txt │ ├── list_reftests.py │ ├── loopdomfuzz.py │ ├── mac-close-terminal.applescript.txt │ ├── macbot.command │ ├── no-e10s.prefs.js │ ├── old-dom-retest.py │ ├── randomPrefs.py │ ├── retestLocalW.py │ ├── runbrowser.py │ ├── shell_compiles_browser_dies.py │ ├── status.py │ └── urls-reftests ├── empties │ ├── a.html │ ├── a.svg │ ├── a.xhtml │ └── a.xul ├── fuzzer │ ├── a-driver-finish-auto.js │ ├── a-driver-start.js │ ├── a-driver.js │ ├── a-reduction.js │ ├── files-to-link.txt │ ├── jshint.txt │ ├── main.js │ ├── misc.js │ ├── modules │ │ ├── README.md │ │ ├── WebIDL.js │ │ ├── accessibility.js │ │ ├── call-testing-functions.js │ │ ├── canvas.js │ │ ├── canvas2d.js │ │ ├── change-root.js │ │ ├── clone-node.js │ │ ├── compare-pixels.js │ │ ├── document-fragments.js │ │ ├── editor.js │ │ ├── event-loop.js │ │ ├── generate-nodes-html.js │ │ ├── generate-nodes-mathml.js │ │ ├── generate-nodes-svg.js │ │ ├── generate-nodes.js │ │ ├── innerhtml.js │ │ ├── keyboard-events.js │ │ ├── misc-privileged.js │ │ ├── modify-attributes.js │ │ ├── range-and-selection.js │ │ ├── repeat.js │ │ ├── script-nodes.js │ │ ├── set-image-element.js │ │ ├── slurp-frame.js │ │ ├── stir-attributes.js │ │ ├── stir-dom.js │ │ ├── style-objects.js │ │ ├── style-properties.js │ │ ├── style-sheet-objects.js │ │ ├── style-sheets.js │ │ ├── tables.js │ │ ├── test-iteration.js │ │ ├── textboxes.js │ │ ├── undo-manager.js │ │ ├── unicode-characters.js │ │ ├── url-objects.js │ │ └── weak-maps.js │ ├── platform.js │ ├── properties-to-avoid.js │ ├── serialize-dom-as-html.js │ ├── serialize-dom-as-script.js │ ├── serialize-fuzz-commands.js │ ├── serialize-string.js │ ├── things.js │ ├── undefined.js │ ├── values.js │ ├── values │ │ └── generate-html.js │ ├── values2.js │ ├── veto.js │ └── visit-nodes.js ├── mozconfig │ └── mozconfig-asan ├── reducing-testcases.md ├── test-pages │ ├── audio.html │ ├── auth-script.html │ ├── echo-server │ │ ├── bad-requests.html │ │ ├── basic-auth.html │ │ ├── correctness.html │ │ ├── redirect.html │ │ └── stress.html │ ├── enableAccessibility.html │ ├── justmath.xml │ ├── list-fonts.html │ ├── quit.html │ ├── svg │ │ ├── masking-mask-01-b.svg │ │ ├── masking-path-01-b.svg │ │ └── masking-path-05-f.svg │ ├── test_comparePixels.html │ ├── test_serializeDOMAsScript.html │ ├── tp-math.xhtml │ ├── tp-mixed-html.xhtml │ ├── tp-mixed-svg.svg │ ├── tp-nested-lists.html │ ├── tp-quotes.xhtml │ ├── tp-table-basic.xhtml │ ├── tp-table-span.html │ ├── tp-trees.xul │ ├── trustedKeyEvent.html │ └── zoom.html └── webidl │ ├── mozilla-ize-webidl2.diff │ ├── parse-all-with-webidl2.js │ └── webidl.json ├── js └── shared │ ├── mersenne-twister.js │ ├── random.js │ └── testing-functions.js ├── known └── mozilla-central │ ├── assertions.txt │ ├── crashes.txt │ └── rleak.txt ├── loopBot.py ├── requirements.txt └── util ├── LockDir.py ├── cdbCmds.txt ├── crashesat.py ├── createCollector.py ├── downloadBuild.py ├── exportForFuzzManager.py ├── fileManipulation.py ├── forkJoin.py ├── gdb-quick.txt ├── hgCmds.py ├── linkJS.py ├── lithOps.py ├── multi.py ├── oomReaper.py ├── reposUpdate.py ├── s3cache.py ├── subprocesses.py └── tooltool ├── README ├── linux32.manifest ├── linux64.manifest ├── macosx64.manifest ├── tooltool.py └── win32.manifest /.gitignore: -------------------------------------------------------------------------------- 1 | # .gitignore - List of filenames git should ignore 2 | 3 | 4 | ############################### 5 | # Generated by other programs # 6 | ############################### 7 | 8 | *~ 9 | *.pyc 10 | .DS_Store 11 | .gdb_history 12 | 13 | 14 | ######################## 15 | # Generated by funfuzz # 16 | ######################## 17 | 18 | wlog* 19 | wtmp* 20 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dom/fuzzpriv"] 2 | path = dom/extension 3 | url = git://github.com/MozillaSecurity/fuzzpriv.git 4 | branch = legacy 5 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Participation Guidelines 2 | 3 | This repository is governed by Mozilla's code of conduct and etiquette guidelines. 4 | For more details, please read the 5 | [Mozilla Community Participation Guidelines](https://www.mozilla.org/about/governance/policies/participation/). 6 | 7 | ## How to Report 8 | For more information on how to report violations of the Community Participation Guidelines, please read our '[How to Report](https://www.mozilla.org/about/governance/policies/participation/reporting/)' page. 9 | 10 | 16 | -------------------------------------------------------------------------------- /after-fix.txt: -------------------------------------------------------------------------------- 1 | # https://github.com/jruderman/after-fix 2 | 3 | contents bot.py 4 | contents util/*.py 5 | contents detect/*.py 6 | contents known/mozilla-central/*.txt 7 | contents js/*.py 8 | contents js/shared/*.js 9 | contents js/jsfunfuzz/*.js 10 | contents dom/automation/*.py 11 | contents dom/automation/*.txt 12 | contents dom/extension/components/*.js 13 | contents dom/extension/content/*.js 14 | contents dom/fuzzer/*.js 15 | contents dom/fuzzer/modules/*.js 16 | contents dom/fuzzer/values/*.js 17 | -------------------------------------------------------------------------------- /detect/detect_assertions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Recognizes non-fatal assertions: 4 | # * NS_ASSERTIONs, based on condition, text, and filename (ignoring irrelevant parts of the path) 5 | # * Obj-C exceptions caught by Mozilla code 6 | # (FuzzManager's AssertionHelper.py handles fatal assertions of all flavors.) 7 | 8 | from __future__ import absolute_import 9 | 10 | import findIgnoreLists 11 | import re 12 | 13 | simpleIgnoreList = [] 14 | twoPartIgnoreList = [] 15 | ready = False 16 | 17 | 18 | # Called directly by domInteresting.py 19 | def scanLine(knownPath, line): 20 | global ignoreList 21 | if not ready: 22 | readIgnoreLists(knownPath) 23 | 24 | line = line.strip("\x07").rstrip("\n") 25 | 26 | if "###!!! ASSERT" in line: 27 | line = re.sub("^\\[\\d+\\]\\s+", "", line, count=1) # Strip leading [PID], if present 28 | if assertionIsNew(line): 29 | return line 30 | elif "Mozilla has caught an Obj-C exception" in line: 31 | if assertionIsNew(line): 32 | return line 33 | 34 | return None 35 | 36 | 37 | def readIgnoreLists(knownPath): 38 | global ready 39 | for filename in findIgnoreLists.findIgnoreLists(knownPath, "assertions.txt"): 40 | readIgnoreList(filename) 41 | ready = True 42 | print "detect_assertions is ready (ignoring %d strings without filenames and %d strings with filenames)" % (len(simpleIgnoreList), len(twoPartIgnoreList)) 43 | 44 | 45 | def readIgnoreList(filename): 46 | global ready 47 | with open(filename) as ignoreFile: 48 | for line in ignoreFile: 49 | line = line.rstrip() 50 | if (len(line) > 0) and not line.startswith("#"): 51 | mpi = line.find(", file ") 52 | if mpi == -1: 53 | simpleIgnoreList.append(line) 54 | else: 55 | twoPartIgnoreList.append((line[:mpi+7], line[mpi+7:])) 56 | ready = True 57 | 58 | 59 | def assertionIsNew(assertion): 60 | global simpleIgnoreList 61 | for ig in simpleIgnoreList: 62 | if assertion.find(ig) != -1: 63 | return False 64 | for (part1, part2) in twoPartIgnoreList: 65 | if assertion.find(part1) != -1 and assertion.replace('\\', '/').find(part2) != -1: 66 | return False 67 | return True 68 | -------------------------------------------------------------------------------- /detect/detect_leaks.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import sys 7 | import findIgnoreLists 8 | 9 | ready = False 10 | knownObjects = dict() 11 | sizes = 0 12 | 13 | def readKnownLeakList(knownPath): 14 | global ready, knownObjects, sizes 15 | 16 | knownLeaksFn = os.path.join(findIgnoreLists.THIS_REPO_PATH, "known", knownPath, "rleak.txt") 17 | with open(knownLeaksFn) as f: 18 | for line in f: 19 | line = line.split("#")[0] 20 | line = line.strip() 21 | parts = line.split(" ") 22 | if parts[0] == "": 23 | continue 24 | elif parts[0] == "SIZE": 25 | sizes += 1 26 | elif parts[0] == "LEAK" and len(parts) == 2: 27 | objname = parts[1] 28 | knownObjects[objname] = {'size': 10-sizes, 'knownToLeak': True} 29 | elif len(parts) == 1: 30 | objname = parts[0] 31 | knownObjects[objname] = {'size': 10-sizes, 'knownToLeak': False} 32 | else: 33 | raise Exception("What? " + repr(parts)) 34 | 35 | #print "detect_leaks is ready" 36 | #print repr(knownObjects) 37 | 38 | ready = True 39 | 40 | 41 | def amiss(knownPath, leakLogFn, verbose=False): 42 | if not ready: 43 | readKnownLeakList(knownPath) 44 | sawLeakStats = False 45 | 46 | with open(leakLogFn) as leakLog: 47 | 48 | for line in leakLog: 49 | line = line.rstrip() 50 | if line.startswith("nsTraceRefcntImpl::DumpStatistics"): 51 | continue 52 | if (line.startswith("== BloatView: ALL (cumulative) LEAK STATISTICS")): 53 | sawLeakStats = True 54 | # This line appears only if there are leaks with XPCOM_MEM_LEAK_LOG (but always shows with XPCOM_MEM_BLOAT_LOG, oops) 55 | if (line.endswith("Total Rem|")): 56 | break 57 | else: 58 | if verbose: 59 | if sawLeakStats: 60 | print "detect_leaks: PASS with no leaks at all :)" 61 | else: 62 | print "detect_leaks: PASS missing leak stats, don't care enough to fail" 63 | return False 64 | 65 | largestA = -1 # largest object known to leak 66 | largestB = -2 # largest object not known to leak 67 | 68 | for line in leakLog: 69 | line = line.strip("\x07").rstrip("\n").lstrip(" ") 70 | if (line == ""): 71 | break 72 | if line.startswith("nsTraceRefcntImpl::DumpStatistics"): 73 | continue 74 | parts = line.split("|") 75 | if len(parts) < 2: 76 | print "detect_leaks: failed to parse: " + line 77 | return True 78 | objname = parts[1].rstrip(" ") 79 | if objname == "TOTAL": 80 | continue 81 | info = knownObjects.get(objname, {'size': 10-sizes, 'knownToLeak': False}) 82 | if verbose: 83 | print "detect_leaks: Leaked " + repr(info) + " " + repr(objname) 84 | if info.get("knownToLeak"): 85 | largestA = max(largestA, info.get("size")) 86 | else: 87 | largestB = max(largestB, info.get("size")) 88 | 89 | if largestB >= largestA: 90 | if verbose: 91 | print "detect_leaks: FAIL " + str(largestB) + " " + str(largestA) 92 | return True 93 | else: 94 | if verbose: 95 | print "detect_leaks: PASS " + str(largestB) + " " + str(largestA) 96 | return False 97 | 98 | # For standalone use 99 | if __name__ == "__main__": 100 | knownPath = sys.argv[1] 101 | leakLogFn = sys.argv[2] 102 | print amiss(knownPath, leakLogFn, verbose=True) 103 | -------------------------------------------------------------------------------- /detect/detect_malloc_errors.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | # Look for "szone_error" (Tiger), "malloc_error_break" (Leopard), "MallocHelp" (?) 6 | # which are signs of malloc being unhappy (double free, out-of-memory, etc). 7 | 8 | pline = "" 9 | ppline = "" 10 | 11 | def amiss(logPrefix): 12 | foundSomething = False 13 | global pline, ppline 14 | 15 | pline = "" 16 | ppline = "" 17 | 18 | with open(logPrefix + "-err.txt") as f: 19 | for line in f: 20 | if scanLine(line): 21 | foundSomething = True 22 | break # Don't flood the log with repeated malloc failures 23 | 24 | return foundSomething 25 | 26 | 27 | def scanLine(line): 28 | global ppline, pline 29 | 30 | line = line.strip("\x07").rstrip("\n") 31 | 32 | if (-1 != line.find("szone_error") 33 | or -1 != line.find("malloc_error_break") 34 | or -1 != line.find("MallocHelp")): 35 | if (-1 == pline.find("can't allocate region")): 36 | print "" 37 | print ppline 38 | print pline 39 | print line 40 | return True 41 | 42 | ppline = pline 43 | pline = line 44 | -------------------------------------------------------------------------------- /detect/findIgnoreLists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | 7 | THIS_SCRIPT_DIRECTORY = os.path.dirname(os.path.abspath(__file__)) 8 | THIS_REPO_PATH = os.path.abspath(os.path.join(THIS_SCRIPT_DIRECTORY, os.pardir)) 9 | REPO_PARENT_PATH = os.path.abspath(os.path.join(THIS_SCRIPT_DIRECTORY, os.pardir, os.pardir)) 10 | 11 | # Lets us combine ignore lists: 12 | # from private&public fuzzing repos 13 | # for project branches and for their base branches (e.g. mozilla-central) 14 | # 15 | # Given a targetRepo "mozilla-central/ionmonkey" and a name "crashes.txt", returns a list of 2N absolute paths like: 16 | # ???/domfuzz*/known/mozilla-central/ionmonkey/crashes.txt 17 | # ???/domfuzz*/known/mozilla-central/crashes.txt 18 | 19 | 20 | def findIgnoreLists(targetRepo, needle): 21 | r = [] 22 | assert not targetRepo.startswith("/") 23 | for name in sorted(os.listdir(REPO_PARENT_PATH)): 24 | if name.startswith("domfuzz"): 25 | knownPath = os.path.join(REPO_PARENT_PATH, name, "known", targetRepo) 26 | if os.path.isdir(knownPath): 27 | while os.path.basename(knownPath) != "known": 28 | filename = os.path.join(knownPath, needle) 29 | if os.path.exists(filename): 30 | r.append(filename) 31 | knownPath = os.path.dirname(knownPath) 32 | assert len(r) > 0 33 | return r 34 | -------------------------------------------------------------------------------- /dom/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr": 10000, 3 | 4 | // Relax the kinds of things fuzzers do 5 | 6 | "moz": true, 7 | "evil": true, // Allow eval 8 | "scripturl": true, 9 | 10 | // Relax style 11 | 12 | "sub": true, // allow x["a"] 13 | "eqnull": true, // i don't feel like examining all these spots and determining whether i mean "null" or "null or undefined" 14 | "laxbreak": true, // i really like my operators lined up. i wish this warning would go away automatically when parenthesized (to defeat ASI) 15 | "-W008": true, // .5 is fine by me 16 | "-W097": true, // i understand the risks of using "use strict" at top level 17 | "-W086": true, // false positives, e.g. { case 1: if (x) { return true; } else { return false; } default: } is incorrectly treated as missing a break 18 | 19 | // Enforce style 20 | 21 | // "latedef": "nofunc", // i only want this inside functions, not at top level 22 | // "noempty": true, // i wish i could turn this off for empty 'else' blocks that contain comments 23 | // "unused": "strict", // i'd have to get it to work across files 24 | "noarg": true, 25 | "nonbsp": true, 26 | "nonew": true, 27 | "trailing": true 28 | } 29 | -------------------------------------------------------------------------------- /dom/README.md: -------------------------------------------------------------------------------- 1 | DOMFuzz tests layout and other parts of browser engines through DOM API calls. Some [modules](fuzzer/modules/) lean more toward "mutation" and others lean more toward "generation", but all act on the DOM of a web page. 2 | 3 | For each instance of Firefox, up to 4 modules will be chosen and enabled. Modules include: 4 | * [Stir DOM](fuzzer/modules/stir-dom.js), which moves nodes around the document tree using appendChild and insertBefore. 5 | * [Random Styles](fuzzer/modules/style-properties.js), which adds inline style properties. 6 | 7 | 8 | ## Setup 9 | 10 | ### Mac 11 | 12 | When fuzzing a browser, you may want to disable GUI crash dialogs. You'll still get crash reports in `/Library/Logs/DiagnosticReports/` and `~/Library/Logs/DiagnosticReports/`, but they won't be shown on-screen or submitted to Apple. 13 | 14 | ``` 15 | defaults write com.apple.CrashReporter DialogType server 16 | ``` 17 | 18 | To allow `atos` to work: 19 | 20 | ``` 21 | sudo /usr/sbin/DevToolsSecurity --enable 22 | ``` 23 | 24 | ## Running 25 | 26 | Running `./loopdomfuzz.py build` will: 27 | * Figure out which version of Firefox you are testing and use appropriate ignore lists. 28 | * Create temporary Firefox profiles with the [DOMFuzz Helper extension](extension/) installed, [appropriate settings](automation/constant-prefs.js), and some [random settings](automation/randomPrefs.py) as well. 29 | * In a loop, open Firefox to a random file from the reftest suite, and load random DOMFuzz modules into it. (If a bug is found, it will place a file in a wtmp*/ directory, and try to reduce it with Lithium.) 30 | 31 | |build| must be a directory containing a build of Firefox: 32 | * A Firefox object directory, built locally with --enable-tests 33 | * A Treeherder build that was downloaded using funfuzz/util/downloadBuild.py --want-tests 34 | 35 | Quick start: 36 | ``` 37 | funfuzz/util/downloadBuild.py --want-tests && funfuzz/util/multi.py 8 funfuzz/dom/automation/loopdomfuzz.py build 38 | ``` 39 | 40 | 41 | ## Contributors 42 | 43 | * Paul Nickerson contributed a module for testing CanvasRenderingContext2D and prototyped serializeDOMAsScript 44 | * Christoph Diehl contributed a bunch of modules 45 | * Mats Palmgren contributed [Stir Tables](fuzzer/modules/tables.js) 46 | -------------------------------------------------------------------------------- /dom/automation/bot.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import sys 7 | 8 | path0 = os.path.dirname(__file__) 9 | path1 = os.path.abspath(os.path.join(path0, os.pardir, os.pardir)) 10 | sys.path.insert(0, path1) # We must use this because this file is also called bot.py. 11 | #sys.path.append(path1) 12 | import bot 13 | 14 | # RelEng uses bot.py at the original location, this file is a stub to call bot.py at the new place. 15 | 16 | if __name__ == '__main__': 17 | bot.main() 18 | -------------------------------------------------------------------------------- /dom/automation/buildBrowser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import sys 7 | import optparse 8 | import platform 9 | import time 10 | import copy 11 | import subprocess 12 | import shutil 13 | import tempfile 14 | 15 | path0 = os.path.dirname(os.path.abspath(__file__)) 16 | path3 = os.path.abspath(os.path.join(path0, os.pardir, os.pardir, 'util')) 17 | sys.path.append(path3) 18 | import subprocesses as sps 19 | 20 | 21 | def parseOptions(inputArgs): 22 | """Returns a 'buildOptions' object, which is intended to be immutable.""" 23 | 24 | parser = optparse.OptionParser() 25 | # http://docs.python.org/library/optparse.html#optparse.OptionParser.disable_interspersed_args 26 | parser.disable_interspersed_args() 27 | 28 | parser.set_defaults( 29 | repoDir = sps.normExpUserPath(os.path.join('~', 'trees', 'mozilla-central')), 30 | objDir = None, 31 | mozconfig = None 32 | ) 33 | 34 | parser.add_option('-R', '--repoDir', dest='repoDir', 35 | help='Sets the source repository. Defaults to "%default".') 36 | parser.add_option('-o', '--objDir', dest='objDir', 37 | help='The obj dir that will be created by the given mozconfig. May get clobbered.') 38 | parser.add_option('-c', '--mozconfig', dest='mozconfig', 39 | help='A mozconfig file.') 40 | 41 | (options, args) = parser.parse_args(inputArgs) 42 | 43 | if len(args) > 0: 44 | parser.print_help() 45 | raise Exception("buildBrowser.py: extra arguments") 46 | 47 | # All are required for now 48 | if not (options.objDir and options.repoDir and options.mozconfig): 49 | print "buildBrowser requires you to specify a repoDir, objDir, and mozconfig" 50 | parser.print_help() 51 | raise Exception("buildBrowser.py: usage") 52 | 53 | options.objDir = os.path.expanduser(options.objDir) 54 | options.repoDir = os.path.expanduser(options.repoDir) 55 | options.mozconfig = os.path.expanduser(options.mozconfig) 56 | 57 | assert os.path.exists(options.repoDir) 58 | assert os.path.exists(options.mozconfig) 59 | 60 | return options 61 | 62 | 63 | def tryCompiling(options): 64 | env = copy.deepcopy(os.environ) 65 | env['MOZCONFIG'] = options.mozconfig 66 | 67 | compileOutput = tempfile.NamedTemporaryFile(delete=False) 68 | compileOutputFn = compileOutput.name 69 | print "Compiling (details in " + compileOutputFn + ")..." 70 | rv = subprocess.call(['make', '-C', options.repoDir, '-f', 'client.mk'], env=env, stdout=compileOutput.file, stderr=subprocess.STDOUT) 71 | compileOutput.close() 72 | if rv != 0: 73 | print "Compilation failed" 74 | time.sleep(8) 75 | if os.path.exists(options.objDir): 76 | shutil.rmtree(options.objDir) 77 | os.remove(compileOutputFn) 78 | return False 79 | else: 80 | assert os.path.exists(options.objDir) 81 | os.remove(compileOutputFn) 82 | return True 83 | 84 | 85 | # For autoBisect 86 | def makeTestRev(options): 87 | srcDir = options.browserOptions.repoDir 88 | objDir = options.browserOptions.objDir 89 | 90 | def testRev(rev): 91 | print "Updating to " + rev + "..." 92 | subprocess.check_call(['hg', '-R', srcDir, 'update', '-r', rev]) 93 | sps.destroyPyc(srcDir) 94 | 95 | if os.path.exists(objDir): 96 | print "Clobbering..." 97 | # We don't trust the clobberer while bisecting 98 | shutil.rmtree(objDir) 99 | 100 | if not tryCompiling(options.browserOptions): 101 | return (options.compilationFailedLabel, "compilation failed") 102 | 103 | print "Testing..." 104 | ans = options.testAndLabel(objDir, rev) 105 | time.sleep(8) 106 | return ans 107 | 108 | return testRev 109 | 110 | if __name__ == "__main__": 111 | print tryCompiling(parseOptions(sys.argv[1:])) 112 | -------------------------------------------------------------------------------- /dom/automation/echoServer.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Note: this module abuses strings in a way that is specific to Python 2. 4 | # Will need changes for Python 3. 5 | # http://stackoverflow.com/questions/2411864/python-socket-send-buffer-vs-str 6 | 7 | """ 8 | An HTTP echo server that uses threads to handle multiple clients at a time. 9 | Entering any line of input at the terminal will exit the server. 10 | """ 11 | 12 | from __future__ import absolute_import 13 | from __future__ import division 14 | 15 | import base64 16 | import os 17 | import select 18 | import socket 19 | import sys 20 | import threading 21 | import time 22 | 23 | 24 | class Server: 25 | # Based on http://ilab.cs.byu.edu/python/threadingmodule.html 26 | # which is CC (by-nc-nd) 27 | 28 | def __init__(self): 29 | self.host = '' 30 | self.port = int(sys.argv[1]) if len(sys.argv) > 1 else 9606 31 | self.backlog = 5 32 | self.size = 1024 33 | self.server = None 34 | self.threads = [] 35 | 36 | def open_socket(self): 37 | try: 38 | self.server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 39 | self.server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 40 | self.server.bind((self.host, self.port)) 41 | self.server.listen(5) 42 | except socket.error, (value, message): 43 | if self.server: 44 | self.server.close() 45 | print "Could not open socket: " + message 46 | sys.exit(1) 47 | 48 | def run(self): 49 | self.open_socket() 50 | input = [self.server, sys.stdin] 51 | 52 | while True: 53 | inputready, outputready, exceptready = select.select(input, [], []) 54 | 55 | for s in inputready: 56 | 57 | if s == self.server: 58 | # handle the server socket 59 | print int(time.time()) 60 | c = Client(self.server.accept()) 61 | c.start() 62 | self.threads.append(c) 63 | 64 | elif s == sys.stdin: 65 | # handle standard input (including pipe closure) 66 | junk = sys.stdin.readline() # prevent fallthrough if I press Ctrl+D in a Terminal window 67 | self.server.close() 68 | os._exit(0) # immediately kill all threads. do not join, do not clean up, do not pass Go. 69 | 70 | 71 | class Client(threading.Thread): 72 | def __init__(self, (client, address)): 73 | threading.Thread.__init__(self) 74 | self.client = client 75 | self.address = address 76 | self.size = 1024 77 | self.httpRequest = "" 78 | 79 | def respond(self, path): 80 | if not path[0:2] == "/?": 81 | # e.g. requests for /favicon.ico 82 | return 83 | queryParams = path[2:].split("&") 84 | response = "oops" 85 | for param in queryParams: 86 | e = param.find("=") # not using param.split because base64 can include "=" 87 | if e == -1: 88 | print "Error: url param without =: " + param[:20] 89 | return 90 | name = param[:e] 91 | value = param[e+1:] 92 | if name == 'response': 93 | try: 94 | response = base64.standard_b64decode(value) 95 | except TypeError as e: 96 | # e.g. "Incorrect padding" 97 | print "Error: standard_b64decode threw " + str(e) 98 | elif name == 'delay': 99 | specifiedSeconds = parseInt(value) / 1000 100 | time.sleep(minmax(0, specifiedSeconds, 10)) 101 | else: 102 | print "Error: unexpected url param: " + name[:20] 103 | return 104 | self.client.sendall(response) 105 | 106 | def run(self): 107 | running = True 108 | while running: 109 | data = self.client.recv(self.size) 110 | if data: 111 | self.httpRequest += data 112 | if len(self.httpRequest) > 100000: 113 | print "Error: request URL too long, even for me" 114 | running = False 115 | elif self.httpRequest.endswith("\r\n\r\n"): 116 | path = self.httpRequest.split(" ")[1] 117 | self.respond(path) 118 | running = False 119 | else: 120 | running = False 121 | self.client.close() 122 | 123 | 124 | def minmax(low, input, high): 125 | return min(high, max(low, input)) 126 | 127 | 128 | def parseInt(s): 129 | try: 130 | return int(s) 131 | except ValueError: 132 | return 0 133 | 134 | 135 | if __name__ == "__main__": 136 | Server().run() 137 | -------------------------------------------------------------------------------- /dom/automation/how-to-use.txt: -------------------------------------------------------------------------------- 1 | Just run: 2 | ./loopdomfuzz.py build 3 | 4 | It will: 5 | * Figure out which version of Firefox you are testing and use appropriate ignore lists. 6 | * Create temporary Firefox profiles. 7 | * Keep fuzzing new instances of Firefox until it finds a new bug. 8 | * If it finds a bug, it will place a file in a wtmp*/ directory, and try to reduce it with Lithium. 9 | 10 | |build| must be a directory, either: 11 | * A Firefox object directory, built locally with --enable-tests 12 | * A Tinderbox build that was downloaded using funfuzz/util/downloadBuild.py --want-tests 13 | 14 | 15 | Crashes: 16 | * It's probably best if you compiled Firefox with --enable-breakpad and you have minidump_stackwalk installed, but the breakpad integration isn't fully working yet. 17 | * On Mac, if you don't have breakpad et al set up, you'll want to turn off crash dialogs: defaults write com.apple.CrashReporter DialogType server 18 | 19 | To reproduce a bug with the extension in place (usually not needed): 20 | ./rundomfuzz.py build url 21 | 22 | 23 | 24 | For dedicated fuzzing machines (Mac only): 25 | 26 | Add to /etc/sudoers (using "sudo visudo") to allow the script to "sudo reboot" without entering a password: 27 | %admin ALL=(ALL)NOPASSWD: /sbin/reboot 28 | 29 | In "General" System Preferences, ensure "Close windows when quitting an application" is checked OR change which line is commented out in mac-close-terminal.applescript.txt. (This makes sure Terminal windows don't pile up, one per reboot.) 30 | 31 | In "Users & Groups", enable "automatic login" and add macbot.command to your startup commands. This is essentially a shell script that opens a Terminal window first. 32 | 33 | And that's it! The script will update the fuzzing repository and download new builds from Tinderbox automatically. It will reboot the machine every 12 hours or so, and store its results in ~/domfuzzjobs/. 34 | -------------------------------------------------------------------------------- /dom/automation/list_reftests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Based on js/src/tests/manifest.py 4 | 5 | from __future__ import absolute_import, print_function 6 | 7 | import argparse 8 | import os 9 | import re 10 | import sys 11 | 12 | 13 | def parse(filename, add_result_callback): 14 | comment_re = re.compile(r'#.*') # this is wrong, because URLs are allowed to have #, but that works just fine for the fuzzer 15 | reldir = os.path.dirname(filename) 16 | 17 | with open(filename) as f: 18 | for line in f: 19 | sline = comment_re.sub('', line) 20 | parts = sline.split() 21 | if len(parts) == 0: 22 | # line is empty or just a comment, skip 23 | pass 24 | elif parts[0] == 'url-prefix': 25 | pass 26 | else: 27 | pos = 0 28 | while pos < len(parts): 29 | # https://hg.mozilla.org/mozilla-central/file/866777368827/layout/tools/reftest/print-manifest-dirs.py#l42 30 | K = ['fails', 'needs-focus', 'random', 'skip', 'asserts', 'slow', 'require-or', 'silentfail', 'pref', 'test-pref', 'ref-pref', 'fuzzy', 'default-preferences', 'chaos-mode'] 31 | part = parts[pos] 32 | if any([part.startswith(k) for k in K]): 33 | # fails, fails-if(...), asserts(3) 34 | #print "Skipping: " + part 35 | pos += 1 36 | pass 37 | elif part == 'include': 38 | pos += 1 39 | parse(os.path.normpath(os.path.join(reldir, parts[pos])), add_result_callback) 40 | break 41 | elif part.startswith("HTTP"): 42 | pos += 1 43 | pass 44 | elif part in ["==", "!=", "load"]: 45 | pos += 1 46 | while pos < len(parts): 47 | part = parts[pos] 48 | if not part.startswith("data:") and not part.startswith("javascript:") and not part.startswith("about:") and not part.startswith("view-source:"): 49 | add_result_callback(os.path.join(reldir, parts[pos])) 50 | pos += 1 51 | break 52 | elif part == "script": 53 | break 54 | else: 55 | print('warning: in %s unrecognized manifest line element "%s"' % (filename, parts[pos]), file=sys.stderr) 56 | pos += 1 57 | 58 | 59 | def main(): 60 | 61 | testfiles = set() 62 | 63 | def add_result(r): 64 | if not ("pngsuite" in r or "351236" in r or "432561" in r or "wrapper.html" in r or "xul" in r or "xbl" in r or r.endswith(".sjs")): 65 | if r not in testfiles: 66 | assert r.startswith(sourcetree) 67 | if not os.path.exists(r.split("?")[0]): 68 | raise Exception("Missing test: " + r.split("?")[0]) 69 | testfiles.add(r.split("?")[0]) 70 | 71 | parser = argparse.ArgumentParser() 72 | parser.add_argument("mozilla_topsrcdir") 73 | args = parser.parse_args() 74 | 75 | sourcetree = args.mozilla_topsrcdir 76 | 77 | if not os.path.isfile(os.path.join(sourcetree, os.path.join("mozilla-config.h.in"))): 78 | parser.error("Not a Mozilla source tree: " + sourcetree) 79 | 80 | parse(os.path.join(sourcetree, "layout/reftests/reftest.list"), add_result) 81 | parse(os.path.join(sourcetree, "testing/crashtest/crashtests.list"), add_result) 82 | 83 | for testfile in testfiles: 84 | print(testfile[len(sourcetree):].lstrip("\\/")) 85 | 86 | 87 | if __name__ == "__main__": 88 | main() 89 | -------------------------------------------------------------------------------- /dom/automation/mac-close-terminal.applescript.txt: -------------------------------------------------------------------------------- 1 | tell application "Terminal" to activate 2 | 3 | tell application "System Events" 4 | tell process "Terminal" 5 | # If you have [[System Preferences > General > Close windows when quitting an application]] unchecked 6 | #keystroke "q" using {command down, option down} 7 | 8 | # If you have [[System Preferences > General > Close windows when quitting an application]] checked 9 | keystroke "q" using {command down} 10 | end tell 11 | end tell 12 | -------------------------------------------------------------------------------- /dom/automation/macbot.command: -------------------------------------------------------------------------------- 1 | date 2 | 3 | sleep 10 && ping -c 1 -o www.mozilla.org && hg -R ~/funfuzz pull -u 4 | 5 | python funfuzz/dom/automation/bot.py --test-type=dom --target-time=43200 6 | 7 | # Reboot in a way that ensures the terminal window won't be saved 8 | echo REBOOT COMING 9 | sleep 8 10 | bash -c "sleep 4 && /usr/bin/osascript funfuzz/dom/automation/mac-close-terminal.applescript.txt && sleep 4 && sudo reboot" & 11 | -------------------------------------------------------------------------------- /dom/automation/no-e10s.prefs.js: -------------------------------------------------------------------------------- 1 | user_pref("browser.tabs.remote.autostart", false); 2 | user_pref("browser.tabs.remote.autostart.1", false); 3 | user_pref("browser.tabs.remote.autostart.2", false); 4 | -------------------------------------------------------------------------------- /dom/automation/old-dom-retest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import shutil 7 | import tempfile 8 | 9 | import domInteresting 10 | import loopdomfuzz 11 | import randomPrefs 12 | 13 | 14 | retestRoot = "/Users/jruderman/fuzz-results/" 15 | skips = "/Users/jruderman/retest-skips.txt" 16 | buildDir = "build" 17 | 18 | 19 | def readSkips(filename): 20 | skips = {} 21 | if filename: 22 | with open(filename) as f: 23 | for line in f: 24 | jobname = line.split(" ")[0] 25 | skips[jobname] = True 26 | return skips 27 | 28 | 29 | def retestAll(): 30 | ''' 31 | Retest all testcases in retestRoot, starting with the newest, 32 | without modifying that subtree (because it might be rsync'ed). 33 | ''' 34 | 35 | testcases = [] 36 | retestSkips = readSkips(skips) 37 | 38 | # Find testcases to retest 39 | for jobTypeDir in (os.path.join(retestRoot, x) for x in os.listdir(retestRoot) if x.startswith("dom" + "-")): 40 | if "mac" not in jobTypeDir: 41 | continue # XXX just for now 42 | for j in os.listdir(jobTypeDir): 43 | if "-asan" in buildDir and "-asan" not in jobTypeDir: 44 | pass # what's going on here??? 45 | elif j.split("_")[0] in retestSkips: 46 | print "Skipping " + j + " for " + j.split("_")[0] 47 | elif "_0_lines" in j: 48 | print "Skipping a 0-line testcase" 49 | elif "_reduced" in j: 50 | job = os.path.join(jobTypeDir, j) 51 | testcase_leafs = filter(lambda s: s.find("reduced") != -1, os.listdir(job)) 52 | if len(testcase_leafs) == 1: 53 | testcase = os.path.join(job, testcase_leafs[0]) 54 | mtime = os.stat(testcase).st_mtime 55 | testcases.append({'testcase': testcase, 'mtime': mtime}) 56 | 57 | # Sort so the newest testcases are first 58 | print "Retesting " + str(len(testcases)) + " testcases..." 59 | testcases.sort(key=lambda t: t['mtime'], reverse=True) 60 | 61 | i = 0 62 | bc = domInteresting.BrowserConfig(["--background", buildDir], domInteresting.createCollector.createCollector("DOMFuzz")) 63 | tempDir = tempfile.mkdtemp("retesting") 64 | 65 | # Retest all the things! 66 | for t in testcases: 67 | testcase = t['testcase'] 68 | print testcase 69 | i += 1 70 | logPrefix = os.path.join(tempDir, str(i)) 71 | extraPrefs = randomPrefs.grabExtraPrefs(testcase) 72 | testcaseURL = loopdomfuzz.asFileURL(testcase) 73 | domresult = domInteresting.BrowserResult(bc, testcaseURL, logPrefix, extraPrefs=extraPrefs, quiet=True) 74 | 75 | #if level > domInteresting.DOM_FINE: 76 | # print "Reproduced: " + testcase 77 | # with open(logPrefix + "-summary.txt") as f: 78 | # for line in f: 79 | # print line, 80 | 81 | # Would it be easier to do it this way? 82 | 83 | #with open(os.devnull, "w") as devnull: 84 | # p = subprocess.Popen([loopdomfuzz.domInterestingpy, "mozilla-central/obj-firefox-asan-debug/", testcase], stdout=devnull, stderr=subprocess.STDOUT) 85 | # if p.wait() > 0: 86 | # print "Still reproduces: " + testcase 87 | 88 | # Ideally we'd use something like "lithium-command.txt" to get the right --valgrind args, etc... 89 | # (but we don't want the --min-level option) 90 | 91 | # Or this way? 92 | 93 | #lithArgs = ["--strategy=check-only", loopdomfuzz.domInterestingpy, buildInfo.buildDir, testcase] 94 | # 95 | #(lithResult, lithDetails) = lithOps.runLithium(lithArgs, logPrefix, options.targetTime) 96 | #if lithResult == lithOps.LITH_RETESTED_STILL_INTERESTING: 97 | # print "Reproduced: " + testcase 98 | 99 | shutil.rmtree(tempDir) 100 | 101 | 102 | if __name__ == "__main__": 103 | retestAll() 104 | -------------------------------------------------------------------------------- /dom/automation/retestLocalW.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import re 7 | import subprocess 8 | import sys 9 | import time 10 | 11 | build = sys.argv[1] 12 | fullu = os.path.expanduser("~") 13 | dominteresting_py = os.path.join(fullu, "funfuzz", "dom", "automation", "domInteresting.py") 14 | 15 | 16 | def timestamp(): 17 | return str(int(time.time())) 18 | 19 | 20 | for wn in os.listdir(fullu): 21 | if wn.startswith("wtmp"): 22 | fullwn = os.path.join(fullu, wn) 23 | for filename in os.listdir(fullwn): 24 | match = re.match(r"(q\d+)-splice-reduced\..*", filename) 25 | if match: 26 | fullFilename = os.path.join(fullwn, filename) 27 | dicall = [sys.executable, dominteresting_py, "--background", "--submit", build, fullFilename] 28 | outfn = os.path.join(fullwn, match.group(1) + "-retest-out-" + timestamp() + ".txt") 29 | with open(outfn, "w") as out: 30 | if subprocess.call(dicall, stdout=out, stderr=subprocess.STDOUT): 31 | print fullFilename 32 | print outfn 33 | 34 | 35 | # pbpaste | xargs open -a "Sublime Text" 36 | -------------------------------------------------------------------------------- /dom/automation/runbrowser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import sys 7 | import shutil 8 | from optparse import OptionParser 9 | 10 | oldcwd = os.getcwd() 11 | 12 | 13 | def removeDirIfExists(d): 14 | if os.path.exists(d): 15 | shutil.rmtree(d, ignore_errors=True) 16 | 17 | 18 | def runBrowser(): 19 | parser = OptionParser() 20 | # we want to pass down everything from automation.__all__ 21 | parser.add_option("--valgrind", 22 | action="store_true", dest="valgrind", 23 | default=False, 24 | help="use valgrind with the options given in --vgargs") 25 | parser.add_option("--vgargs", 26 | action="store", dest="vgargs", 27 | default=None, 28 | help="space-separated arguments to give to valgrind") 29 | parser.add_option("--symbols-dir", 30 | action="store", dest="symbolsDir", 31 | default=None) 32 | parser.add_option("--leak-log-file", 33 | action="store", dest="leakLogFile", 34 | default=None) 35 | parser.add_option("--background", 36 | action="store_true", dest="background", 37 | default=False) 38 | options, args = parser.parse_args(sys.argv) 39 | 40 | reftestScriptDir = args[1] 41 | utilityDir = args[2] 42 | profileDir = args[3] 43 | url = args[4] 44 | 45 | sys.path.append(reftestScriptDir) 46 | sys.path.append(os.path.join(reftestScriptDir, "..", "mozbase", "mozinfo")) 47 | sys.path.append(os.path.join(reftestScriptDir, "..", "mozbase", "mozfile")) 48 | sys.path.append(os.path.join(reftestScriptDir, "..", "tools", "mozterm")) 49 | try: 50 | from automation import Automation 51 | except ImportError: 52 | # The first time running from a local objdir, I get "ImportError: No module named mozcrash". 53 | # The correct fix is to use virtualenv: https://bugzilla.mozilla.org/show_bug.cgi?id=903616#c12 54 | # For now, just try again. 55 | from automation import Automation 56 | 57 | automation = Automation() 58 | 59 | theapp = os.path.join(reftestScriptDir, automation.DEFAULT_APP) 60 | if not os.path.exists(theapp): 61 | print "RUNBROWSER ERROR | runbrowser.py | Application %s doesn't exist." % theapp 62 | sys.exit(1) 63 | print "theapp: " + theapp 64 | 65 | xrePath = os.path.dirname(theapp) 66 | 67 | if options.valgrind: 68 | raise Exception("runbrowser isn't working with Valgrind at the moment.") 69 | #print "About to use valgrind" 70 | #debuggerInfoVG = automationutils.getDebuggerInfo(oldcwd, "valgrind", "", False) 71 | #debuggerInfoVG["args"] = options.vgargs.split(" ") 72 | #if automation.IS_MAC: 73 | # debuggerInfoVG["args"].append("--dsymutil=yes") 74 | #slowness = 3.0 75 | else: 76 | debuggerInfoVG = None 77 | slowness = 1.0 78 | 79 | # browser environment 80 | browserEnv = automation.environment(xrePath=xrePath) 81 | gatherAssertionStacks = False # windows output entangling (bug 573306); mac symbolizing slowness and output bloat 82 | if gatherAssertionStacks: 83 | browserEnv["XPCOM_DEBUG_BREAK"] = "stack" 84 | browserEnv["MOZ_GDB_SLEEP"] = "2" # seconds 85 | if not options.valgrind and "-asan" not in theapp: 86 | browserEnv["MallocScribble"] = "1" 87 | browserEnv["MallocPreScribble"] = "1" 88 | if options.valgrind and automation.IS_LINUX: 89 | browserEnv["G_SLICE"] = "always-malloc" 90 | if automation.IS_DEBUG_BUILD and not options.valgrind and options.leakLogFile: 91 | browserEnv["XPCOM_MEM_LEAK_LOG"] = options.leakLogFile 92 | browserEnv["MOZ_DISABLE_SAFE_MODE_KEY"] = "1" 93 | browserEnv["RUST_BACKTRACE"] = "full" 94 | 95 | # Defeat Lion's misguided attempt to stop Firefox from crashing repeatedly. 96 | # (I suspect "restorecount.txt" is the most important file to remove.) 97 | removeDirIfExists(os.path.expanduser("~/Library/Saved Application State/org.mozilla.nightly.savedState")) 98 | removeDirIfExists(os.path.expanduser("~/Library/Saved Application State/org.mozilla.nightlydebug.savedState")) 99 | 100 | cmdLineArgs = [] 101 | if "#fuzz=" in url: 102 | cmdLineArgs.append("-fuzzinject") 103 | cmdLineArgs.append(url) 104 | 105 | print "RUNBROWSER INFO | runbrowser.py | runApp: start." 106 | print "RUNBROWSER INFO | runbrowser.py | " + url 107 | 108 | if options.background: 109 | automation.buildCommandLine = stripForegroundArg(automation.buildCommandLine) 110 | 111 | status = automation.runApp(None, browserEnv, theapp, profileDir, cmdLineArgs, 112 | utilityPath=utilityDir, 113 | xrePath=xrePath, 114 | symbolsPath=options.symbolsDir, 115 | debuggerInfo=debuggerInfoVG, 116 | maxTime=400.0 * slowness, 117 | timeout=200.0 * slowness) 118 | try: 119 | print "RUNBROWSER INFO | runbrowser.py | runApp: exited with status %d" % status[0] 120 | except TypeError: 121 | print "RUNBROWSER INFO | runbrowser.py | runApp: exited with status %d" % status 122 | 123 | 124 | 125 | def stripForegroundArg(buildCommandLine): 126 | def intercept(*args): 127 | cmd, args = buildCommandLine(*args) 128 | args = filter((lambda a: a != "-foreground"), args) 129 | return cmd, args 130 | return intercept 131 | 132 | 133 | if __name__ == "__main__": 134 | runBrowser() 135 | -------------------------------------------------------------------------------- /dom/automation/shell_compiles_browser_dies.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from __future__ import absolute_import 4 | 5 | import os 6 | import subprocess 7 | 8 | from lithium.interestingness import timed_run 9 | 10 | # usage: put the js in a separate file from html. give the js filename to lithium as --testcase *and* the second parameter to this shell_compiles_browser_dies. 11 | # for example: 12 | # ./lithium.py --testcase=c.js shell_compiles_browser_dies.py 120 c.js ~/central/debug-obj/dist/MinefieldDebug.app/Contents/MacOS/firefox-bin uses-c.html 13 | 14 | jsshell = os.path.expanduser("~/tracemonkey/js/src/debug/js") 15 | 16 | 17 | def interesting(args, tempPrefix): 18 | timeout = int(args[0]) 19 | returncode = subprocess.call([jsshell, "-c", args[1]], stdout=subprocess.PIPE, stderr=subprocess.PIPE) 20 | if returncode != 0: 21 | print "JS didn't compile, skipping browser test" 22 | return False 23 | wantStack = False # We do not care about the stack when using this interestingness test. 24 | runinfo = timed_run.timed_run(args[2:], timeout, tempPrefix, wantStack) 25 | print "Exit status: %s (%.3f seconds)" % (runinfo.msg, runinfo.elapsedtime) 26 | return runinfo.sta == timed_run.CRASHED or runinfo.sta == timed_run.ABNORMAL 27 | -------------------------------------------------------------------------------- /dom/automation/status.py: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | from __future__ import print_function 3 | import collections 4 | import json 5 | import os 6 | import re 7 | import time 8 | import psutil 9 | 10 | 11 | CPU_CHECK_INTERVAL = 1 12 | 13 | 14 | def get_tmp_status(): 15 | for subdir in os.listdir("."): 16 | if re.match(r"^wtmp\d+$", subdir) is not None: 17 | if os.path.isfile(os.path.join(subdir, "stats.txt")): 18 | with open(os.path.join(subdir, "stats.txt")) as f: 19 | status = json.load(f) 20 | status["alive"] = psutil.pid_exists(status["pid"]) 21 | del status["pid"] 22 | yield status 23 | 24 | 25 | def merge_status(): 26 | status = {"iterations": [], "results": {}, "errors": 0} 27 | for substat in get_tmp_status(): 28 | status["iterations"].append(substat["iterations"]) 29 | for q, v in substat["results"].items(): 30 | status["results"].setdefault(q, 0) 31 | status["results"][q] += v 32 | if not substat["alive"]: 33 | status["errors"] += 1 34 | status["results"] = collections.OrderedDict(sorted(status["results"].items(), key=lambda t: t[0])) 35 | return status 36 | 37 | 38 | def output_status(status): 39 | print("Iterations: %d (%s)" % (sum(status["iterations"]), 40 | ", ".join("%d" % v for v in status["iterations"]))) 41 | print("Results: %d {%s}" % (sum(status["results"].values()), 42 | ", ".join("q%d: %d" % (q, v) for (q, v) in status["results"].items()))) 43 | print("Errors: %d" % status["errors"]) 44 | print("CPU & Load: %0.1f%% %s" % (psutil.cpu_percent(interval=CPU_CHECK_INTERVAL), os.getloadavg())) 45 | print("Memory: %dMB available" % (psutil.virtual_memory().available / 1048576)) 46 | print("Disk: %dMB available" % (psutil.disk_usage("/").free / 1048576)) 47 | print("Timestamp: %s" % time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())) 48 | 49 | 50 | def main(): 51 | output_status(merge_status()) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /dom/empties/a.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /dom/empties/a.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | -------------------------------------------------------------------------------- /dom/empties/a.xhtml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /dom/empties/a.xul: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |