tools/rb/fix_macosx_stack.py

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rwxr-xr-x

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))

mercurial