summaryrefslogtreecommitdiffstats
path: root/testing/talos/talos/mainthreadio.py
diff options
context:
space:
mode:
Diffstat (limited to 'testing/talos/talos/mainthreadio.py')
-rw-r--r--testing/talos/talos/mainthreadio.py189
1 files changed, 189 insertions, 0 deletions
diff --git a/testing/talos/talos/mainthreadio.py b/testing/talos/talos/mainthreadio.py
new file mode 100644
index 0000000000..4c3ac19913
--- /dev/null
+++ b/testing/talos/talos/mainthreadio.py
@@ -0,0 +1,189 @@
+# -*- Mode: python; tab-width: 8; indent-tabs-mode: nil -*-
+# vim: set ts=8 sts=4 et sw=4 tw=80:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+import os
+import re
+from collections import OrderedDict
+
+from talos import allowlist
+
+SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(__file__)))
+
+STAGE_STARTUP = 0
+STAGE_STRINGS = ("startup", "normal", "shutdown")
+
+LENGTH_IO_ENTRY = 5
+LENGTH_NEXT_STAGE_ENTRY = 2
+TOKEN_NEXT_STAGE = "NEXT-STAGE"
+
+INDEX_OPERATION = 1
+INDEX_DURATION = 2
+INDEX_EVENT_SOURCE = 3
+INDEX_FILENAME = 4
+
+KEY_COUNT = "Count"
+KEY_DURATION = "Duration"
+KEY_NO_FILENAME = "(not available)"
+KEY_RUN_COUNT = "RunCount"
+
+LEAKED_SYMLINK_PREFIX = "::\\{"
+
+PATH_SUBSTITUTIONS = OrderedDict(
+ [
+ ("profile", "{profile}"),
+ ("firefox", "{xre}"),
+ ("desktop", "{desktop}"),
+ ("fonts", "{fonts}"),
+ ("appdata", " {appdata}"),
+ ]
+)
+NAME_SUBSTITUTIONS = OrderedDict(
+ [
+ ("installtime", "{time}"),
+ ("prefetch", "{prefetch}"),
+ ("thumbnails", "{thumbnails}"),
+ # {appdata}\locallow\mozilla\temp-{*}
+ ("temp-{", "{temp}"),
+ ("cltbld.", "{cltbld}"),
+ ("windows media player", "{media_player}"),
+ # regex order matters
+ (re.compile(r"{\w{8}-\w{4}-\w{4}-\w{4}-\w{12}}"), "{uuid}"),
+ (re.compile(r"{uuid}\.\d+\.ver\w+\.db"), "{uuid-db}"),
+ ]
+)
+
+TUPLE_EVENT_SOURCE_INDEX = 1
+TUPLE_FILENAME_INDEX = 2
+ALLOWLIST_FILENAME = os.path.join(SCRIPT_DIR, "mtio-allowlist.json")
+
+
+def parse(logfilename, data):
+ try:
+ with open(logfilename, "r") as logfile:
+ if not logfile:
+ return False
+ stage = STAGE_STARTUP
+ for line in logfile:
+ prev_filename = str()
+ entries = line.strip().split(",")
+ if len(entries) == LENGTH_IO_ENTRY:
+ if stage == STAGE_STARTUP:
+ continue
+ if entries[INDEX_FILENAME] == KEY_NO_FILENAME:
+ continue
+ # Format 1: I/O entry
+
+ # Temporary hack: logs are leaking Windows NT symlinks.
+ # We need to ignore those.
+ if entries[INDEX_FILENAME].startswith(LEAKED_SYMLINK_PREFIX):
+ continue
+
+ # We'll key each entry on (stage, event source,
+ # filename, operation)
+ key_tuple = (
+ STAGE_STRINGS[stage],
+ entries[INDEX_EVENT_SOURCE],
+ entries[INDEX_FILENAME],
+ entries[INDEX_OPERATION],
+ )
+ if key_tuple not in data:
+ data[key_tuple] = {
+ KEY_COUNT: 1,
+ KEY_RUN_COUNT: 1,
+ KEY_DURATION: float(entries[INDEX_DURATION]),
+ }
+ else:
+ if prev_filename != entries[INDEX_FILENAME]:
+ data[key_tuple][KEY_RUN_COUNT] += 1
+ data[key_tuple][KEY_COUNT] += 1
+ data[key_tuple][KEY_DURATION] += float(entries[INDEX_DURATION])
+ prev_filename = entries[INDEX_FILENAME]
+ elif (
+ len(entries) == LENGTH_NEXT_STAGE_ENTRY
+ and entries[1] == TOKEN_NEXT_STAGE
+ ):
+ # Format 2: next stage
+ stage = stage + 1
+ return True
+ except IOError as e:
+ print("%s: %s" % (e.filename, e.strerror))
+ return False
+
+
+def write_output(outfilename, data):
+ # Write the data out so that we can track it
+ try:
+ with open(outfilename, "w") as outfile:
+ outfile.write("[\n")
+ for idx, (key, value) in enumerate(data.items()):
+ output = ' ["%s", "%s", "%s", "%s", %d, %d, %f]' % (
+ key[0],
+ key[1],
+ key[2],
+ key[3],
+ value[KEY_COUNT],
+ value[KEY_RUN_COUNT],
+ value[KEY_DURATION],
+ )
+ outfile.write(output)
+ if idx >= 0:
+ outfile.write(",\n")
+ else:
+ outfile.write("\n")
+ outfile.write("]\n")
+ return True
+ except IOError as e:
+ print("%s: %s" % (e.filename, e.strerror))
+ return False
+
+
+def main(argv):
+ if len(argv) < 4:
+ print("Usage: %s <main_thread_io_log_file> <output_file> <xre_path>" % argv[0])
+ return 1
+ if not os.path.exists(argv[3]):
+ print('XRE Path "%s" does not exist' % argv[3])
+ return 1
+ data = {}
+ if not parse(argv[1], data):
+ print("Log parsing failed")
+ return 1
+
+ wl = allowlist.Allowlist(
+ test_name="mainthreadio",
+ paths={"{xre}": argv[3]},
+ path_substitutions=PATH_SUBSTITUTIONS,
+ name_substitutions=NAME_SUBSTITUTIONS,
+ event_sources=["PoisonIOInterposer"],
+ )
+ if not wl.load(ALLOWLIST_FILENAME):
+ print("Failed to load allowlist")
+ return 1
+
+ wl.filter(data, TUPLE_FILENAME_INDEX)
+
+ if not write_output(argv[2], data):
+ return 1
+
+ # Disabled until we enable TBPL oranges
+ # search for unknown filenames
+ errors = wl.check(data, TUPLE_FILENAME_INDEX, TUPLE_EVENT_SOURCE_INDEX)
+ if errors:
+ strs = wl.get_error_strings(errors)
+ wl.print_errors(strs)
+
+ # search for duration > 1.0
+ errors = wl.checkDuration(data, TUPLE_FILENAME_INDEX, "Duration")
+ if errors:
+ strs = wl.get_error_strings(errors)
+ wl.print_errors(strs)
+
+ return 0
+
+
+if __name__ == "__main__":
+ import sys
+
+ sys.exit(main(sys.argv))