Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | #!/usr/bin/python |
michael@0 | 2 | # vim:sw=4:ts=4:et: |
michael@0 | 3 | # This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 4 | # License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 5 | # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
michael@0 | 6 | |
michael@0 | 7 | # This script uses atos to process the output of nsTraceRefcnt's Mac OS |
michael@0 | 8 | # X stack walking code. This is useful for two things: |
michael@0 | 9 | # (1) Getting line number information out of |
michael@0 | 10 | # |nsTraceRefcnt::WalkTheStack|'s output in debug builds. |
michael@0 | 11 | # (2) Getting function names out of |nsTraceRefcnt::WalkTheStack|'s |
michael@0 | 12 | # output on all builds (where it mostly prints UNKNOWN because only |
michael@0 | 13 | # a handful of symbols are exported from component libraries). |
michael@0 | 14 | # |
michael@0 | 15 | # Use the script by piping output containing stacks (such as raw stacks |
michael@0 | 16 | # or make-tree.pl balance trees) through this script. |
michael@0 | 17 | |
michael@0 | 18 | import subprocess |
michael@0 | 19 | import sys |
michael@0 | 20 | import re |
michael@0 | 21 | import os |
michael@0 | 22 | import pty |
michael@0 | 23 | import termios |
michael@0 | 24 | |
michael@0 | 25 | class unbufferedLineConverter: |
michael@0 | 26 | """ |
michael@0 | 27 | Wrap a child process that responds to each line of input with one line of |
michael@0 | 28 | output. Uses pty to trick the child into providing unbuffered output. |
michael@0 | 29 | """ |
michael@0 | 30 | def __init__(self, command, args = []): |
michael@0 | 31 | pid, fd = pty.fork() |
michael@0 | 32 | if pid == 0: |
michael@0 | 33 | # We're the child. Transfer control to command. |
michael@0 | 34 | os.execvp(command, [command] + args) |
michael@0 | 35 | else: |
michael@0 | 36 | # Disable echoing. |
michael@0 | 37 | attr = termios.tcgetattr(fd) |
michael@0 | 38 | attr[3] = attr[3] & ~termios.ECHO |
michael@0 | 39 | termios.tcsetattr(fd, termios.TCSANOW, attr) |
michael@0 | 40 | # Set up a file()-like interface to the child process |
michael@0 | 41 | self.r = os.fdopen(fd, "r", 1) |
michael@0 | 42 | self.w = os.fdopen(os.dup(fd), "w", 1) |
michael@0 | 43 | def convert(self, line): |
michael@0 | 44 | self.w.write(line + "\n") |
michael@0 | 45 | return self.r.readline().rstrip("\r\n") |
michael@0 | 46 | @staticmethod |
michael@0 | 47 | def test(): |
michael@0 | 48 | assert unbufferedLineConverter("rev").convert("123") == "321" |
michael@0 | 49 | assert unbufferedLineConverter("cut", ["-c3"]).convert("abcde") == "c" |
michael@0 | 50 | print "Pass" |
michael@0 | 51 | |
michael@0 | 52 | def separate_debug_file_for(file): |
michael@0 | 53 | return None |
michael@0 | 54 | |
michael@0 | 55 | address_adjustments = {} |
michael@0 | 56 | def address_adjustment(file): |
michael@0 | 57 | if not file in address_adjustments: |
michael@0 | 58 | result = None |
michael@0 | 59 | otool = subprocess.Popen(["otool", "-l", file], stdout=subprocess.PIPE) |
michael@0 | 60 | while True: |
michael@0 | 61 | line = otool.stdout.readline() |
michael@0 | 62 | if line == "": |
michael@0 | 63 | break |
michael@0 | 64 | if line == " segname __TEXT\n": |
michael@0 | 65 | line = otool.stdout.readline() |
michael@0 | 66 | if not line.startswith(" vmaddr "): |
michael@0 | 67 | raise StandardError("unexpected otool output") |
michael@0 | 68 | result = int(line[10:], 16) |
michael@0 | 69 | break |
michael@0 | 70 | otool.stdout.close() |
michael@0 | 71 | |
michael@0 | 72 | if result is None: |
michael@0 | 73 | raise StandardError("unexpected otool output") |
michael@0 | 74 | |
michael@0 | 75 | address_adjustments[file] = result |
michael@0 | 76 | |
michael@0 | 77 | return address_adjustments[file] |
michael@0 | 78 | |
michael@0 | 79 | atoses = {} |
michael@0 | 80 | def addressToSymbol(file, address): |
michael@0 | 81 | converter = None |
michael@0 | 82 | if not file in atoses: |
michael@0 | 83 | debug_file = separate_debug_file_for(file) or file |
michael@0 | 84 | converter = unbufferedLineConverter('/usr/bin/xcrun', ['atos', '-arch', 'x86_64', '-o', debug_file]) |
michael@0 | 85 | atoses[file] = converter |
michael@0 | 86 | else: |
michael@0 | 87 | converter = atoses[file] |
michael@0 | 88 | return converter.convert("0x%X" % address) |
michael@0 | 89 | |
michael@0 | 90 | cxxfilt_proc = None |
michael@0 | 91 | def cxxfilt(sym): |
michael@0 | 92 | if cxxfilt_proc is None: |
michael@0 | 93 | # --no-strip-underscores because atos already stripped the underscore |
michael@0 | 94 | globals()["cxxfilt_proc"] = subprocess.Popen(['c++filt', |
michael@0 | 95 | '--no-strip-underscores', |
michael@0 | 96 | '--format', 'gnu-v3'], |
michael@0 | 97 | stdin=subprocess.PIPE, |
michael@0 | 98 | stdout=subprocess.PIPE) |
michael@0 | 99 | cxxfilt_proc.stdin.write(sym + "\n") |
michael@0 | 100 | return cxxfilt_proc.stdout.readline().rstrip("\n") |
michael@0 | 101 | |
michael@0 | 102 | line_re = re.compile("^(.*) ?\[([^ ]*) \+(0x[0-9a-fA-F]{1,8})\](.*)$") |
michael@0 | 103 | balance_tree_re = re.compile("^([ \|0-9-]*)") |
michael@0 | 104 | atos_name_re = re.compile("^(.+) \(in ([^)]+)\) \((.+)\)$") |
michael@0 | 105 | |
michael@0 | 106 | def fixSymbols(line): |
michael@0 | 107 | result = line_re.match(line) |
michael@0 | 108 | if result is not None: |
michael@0 | 109 | # before allows preservation of balance trees |
michael@0 | 110 | # after allows preservation of counts |
michael@0 | 111 | (before, file, address, after) = result.groups() |
michael@0 | 112 | address = int(address, 16) |
michael@0 | 113 | |
michael@0 | 114 | if os.path.exists(file) and os.path.isfile(file): |
michael@0 | 115 | address += address_adjustment(file) |
michael@0 | 116 | info = addressToSymbol(file, address) |
michael@0 | 117 | |
michael@0 | 118 | # atos output seems to have three forms: |
michael@0 | 119 | # address |
michael@0 | 120 | # address (in foo.dylib) |
michael@0 | 121 | # symbol (in foo.dylib) (file:line) |
michael@0 | 122 | name_result = atos_name_re.match(info) |
michael@0 | 123 | if name_result is not None: |
michael@0 | 124 | # Print the first two forms as-is, and transform the third |
michael@0 | 125 | (name, library, fileline) = name_result.groups() |
michael@0 | 126 | # atos demangles, but occasionally it fails. cxxfilt can mop |
michael@0 | 127 | # up the remaining cases(!), which will begin with '_Z'. |
michael@0 | 128 | if (name.startswith("_Z")): |
michael@0 | 129 | name = cxxfilt(name) |
michael@0 | 130 | info = "%s (%s, in %s)" % (name, fileline, library) |
michael@0 | 131 | |
michael@0 | 132 | # throw away the bad symbol, but keep balance tree structure |
michael@0 | 133 | before = balance_tree_re.match(before).groups()[0] |
michael@0 | 134 | |
michael@0 | 135 | return before + info + after + "\n" |
michael@0 | 136 | else: |
michael@0 | 137 | sys.stderr.write("Warning: File \"" + file + "\" does not exist.\n") |
michael@0 | 138 | return line |
michael@0 | 139 | else: |
michael@0 | 140 | return line |
michael@0 | 141 | |
michael@0 | 142 | if __name__ == "__main__": |
michael@0 | 143 | for line in sys.stdin: |
michael@0 | 144 | sys.stdout.write(fixSymbols(line)) |