Port bloat from nm/linux to ld64 -map/mac.
diff --git a/README.markdown b/README.markdown
index aab7cbe..8508f82 100644
--- a/README.markdown
+++ b/README.markdown
@@ -12,14 +12,14 @@
 
 2) Build your binary with the `-g` flag to get symbols.
 
-3) Run `./bloat.py --help` and generate `nm.out` as instructed there.
+3) Run `./bloat.py --help` and generate `a.out.map` as instructed there.
 
 4) Example command line:
 
-        ./bloat.py --strip-prefix=/path/to/src syms > bloat.json
+    ./bloat.py syms --map-output ~/src/llvm-svn//tools/clang/cf.map  > bloat.json
 
 ## Misc other feature
 
 Dump large symbols:
 
-    $ ./bloat.py dump | head -20
+    ./bloat.py dump | head -20
diff --git a/bloat.py b/bloat.py
index 6abf55c..afc214b 100755
--- a/bloat.py
+++ b/bloat.py
@@ -48,54 +48,70 @@
         }[type]
 
 
-def parse_nm(input):
-    """Parse nm output.
+def parse_map(input):
+    """Parse ld64 -map output.
 
-    Argument: an iterable over lines of nm output.
+    Argument: an iterable over lines of ld64's -map output.
 
     Yields: (symbol name, symbol type, symbol size, source file path).
-    Path may be None if nm couldn't figure out the source file.
+    Path may be None if we couldn't figure out the source file.
     """
 
-    # Match lines with size + symbol + optional filename.
-    sym_re = re.compile(r'^[0-9a-f]+ ([0-9a-f]+) (.) ([^\t]+)(?:\t(.*):\d+)?$')
+    START, OBJFILES, SECTIONS, SYMBOLS = range(4)
+    mode = START
 
-    # Match lines with addr but no size.
-    addr_re = re.compile(r'^[0-9a-f]+ (.) ([^\t]+)(?:\t.*)?$')
-    # Match lines that don't have an address at all -- typically external symbols.
-    noaddr_re = re.compile(r'^ + (.) (.*)$')
+    objfiles = []
+    # matches:
+    #   [  1] /Volumes/MacintoshHD2/../ang-format/Release+Asserts/ClangFormat.o
+    obj_re = re.compile(r'^\[\s*(\d+)\] (.+)$')
+
+    # matches:
+    #   0x100000EA0	0x000015B7	[  1] __ZN5cl....N4llvm9StringRefE
+    sym_re = re.compile(r'^(0x[0-9A-F]+)\t(0x[0-9A-F]+)\t\[\s*(\d+)\] (.+)$')
 
     for line in input:
         line = line.rstrip()
-        match = sym_re.match(line)
-        if match:
-            size, type, sym = match.groups()[0:3]
+        if line == '# Object files:':
+          mode = OBJFILES
+          continue
+        if line == '# Sections:':
+          mode = SECTIONS
+          continue
+        if line == '# Symbols:':
+          mode = SYMBOLS
+          continue
+        if line.startswith('#'):
+          if line.endswith(':'):
+            raise Exception('Unknown section "%s"' % line)
+          continue  # Ignore comments.
+
+        if mode == OBJFILES:
+          match = obj_re.match(line)
+          if match:
+            index, path = match.groups()
+            index = int(index)
+            assert index == len(objfiles), '%d vs %d' % (index, len(objfiles))
+            objfiles.append(path)
+            continue
+        elif mode == SECTIONS:
+          continue
+        elif mode == SYMBOLS:
+          match = sym_re.match(line)
+          if match:
+            start, size, file_index, sym = match.groups()
+            start = int(start, 16)
             size = int(size, 16)
-            type = type.lower()
-            if type in ['u', 'v']:
-                type = 'w'  # just call them all weak
-            if type == 'b':
-                continue  # skip all BSS for now
-            path = match.group(4)
-            yield sym, type, size, path
+            file_index = int(file_index)
+            path = objfiles[file_index]
+            yield sym, 't', size, path
             continue
-        match = addr_re.match(line)
-        if match:
-            type, sym = match.groups()[0:2]
-            # No size == we don't care.
-            continue
-        match = noaddr_re.match(line)
-        if match:
-            type, sym = match.groups()
-            if type in ('U', 'w'):
-                # external or weak symbol
-                continue
 
         print >>sys.stderr, 'unparsed:', repr(line)
 
+
 def demangle(ident, cppfilt):
-    if cppfilt and ident.startswith('_Z'):
-        # Demangle names when possible. Mangled names all start with _Z.
+    if cppfilt and ident.startswith('__Z'):
+        # Demangle names when possible. Mangled names all start with __Z.
         ident = subprocess.check_output([cppfilt, ident]).strip()
     return ident
 
@@ -215,7 +231,12 @@
                 path = path[1:]
             path = ['[path]'] + path.split('/')
 
-        parts = parse_cpp_name(sym, cppfilt)
+        if sym.startswith('literal string: '):
+          parts = sym.split(':', 1)
+          parts[1] = repr(parts[1])
+        else:
+          sym = demangle(sym, cppfilt)
+          parts = parse_cpp_name(sym, cppfilt)
         if len(parts) == 1:
           if path:
             # No namespaces, group with path.
@@ -301,85 +322,24 @@
         }
 
 
-def dump_nm(nmfile, strip_prefix, cppfilt):
-    dirs = treeify_syms(parse_nm(nmfile), strip_prefix, cppfilt)
+def dump_map(mapfile, strip_prefix, cppfilt):
+    dirs = treeify_syms(parse_map(mapfile), strip_prefix, cppfilt)
     print ('var kTree = ' +
            json.dumps(jsonify_tree(dirs, '[everything]'), indent=2))
 
 
-def parse_objdump(input):
-    """Parse objdump -h output."""
-    sec_re = re.compile('^\d+ (\S+) +([0-9a-z]+)')
-    sections = []
-    debug_sections = []
-
-    for line in input:
-        line = line.strip()
-        match = sec_re.match(line)
-        if match:
-            name, size = match.groups()
-            if name.startswith('.'):
-                name = name[1:]
-            if name.startswith('debug_'):
-                name = name[len('debug_'):]
-                debug_sections.append((name, int(size, 16)))
-            else:
-                sections.append((name, int(size, 16)))
-            continue
-    return sections, debug_sections
-
-
-def jsonify_sections(name, sections):
-    children = []
-    total = 0
-    for section, size in sections:
-        children.append({
-                'name': section + ' ' + format_bytes(size),
-                'data': { '$area': size }
-                })
-        total += size
-
-    children.sort(key=lambda child: -child['data']['$area'])
-
-    return {
-        'name': name + ' ' + format_bytes(total),
-        'data': { '$area': total },
-        'children': children
-        }
-
-
-def dump_sections(objdump):
-    sections, debug_sections = parse_objdump(objdump)
-    sections = jsonify_sections('sections', sections)
-    debug_sections = jsonify_sections('debug', debug_sections)
-    size = sections['data']['$area'] + debug_sections['data']['$area']
-    print 'var kTree = ' + json.dumps({
-            'name': 'top ' + format_bytes(size),
-            'data': { '$area': size },
-            'children': [ debug_sections, sections ]})
-
-
 usage="""%prog [options] MODE
 
 Modes are:
   syms: output symbols json suitable for a treemap
   dump: print symbols sorted by size (pipe to head for best output)
-  sections: output binary sections json suitable for a treemap
 
-nm output passed to --nm-output should from running a command
-like the following (note, can take a long time -- 30 minutes):
-  nm -C -S -l /path/to/binary > nm.out
-
-objdump output passed to --objdump-output should be from a command
-like:
-  objdump -h /path/to/binary > objdump.out"""
+ld64 -map output passed to the linker:
+  ld64 -o /path/to/binary *.o -Wl,-map,a.out.map"""
 parser = optparse.OptionParser(usage=usage)
-parser.add_option('--nm-output', action='store', dest='nmpath',
-                  metavar='PATH', default='nm.out',
-                  help='path to nm output [default=nm.out]')
-parser.add_option('--objdump-output', action='store', dest='objdumppath',
-                  metavar='PATH', default='objdump.out',
-                  help='path to objdump output [default=objdump.out]')
+parser.add_option('--map-output', action='store', dest='mappath',
+                  metavar='PATH', default='a.out.map',
+                  help='path to ld64 -map output [default=a.out.map]')
 parser.add_option('--strip-prefix', metavar='PATH', action='store',
                   help='strip PATH prefix from paths; e.g. /path/to/src/root')
 parser.add_option('--filter', action='store',
@@ -396,7 +356,7 @@
 
 mode = args[0]
 if mode == 'syms':
-    nmfile = open(opts.nmpath, 'r')
+    mapfile = open(opts.mappath, 'r')
     try:
         res = subprocess.check_output([opts.cppfilt, 'main'])
         if res.strip() != 'main':
@@ -407,13 +367,10 @@
         print >>sys.stderr, ("Could not find c++filt at %s, "
                              "output won't be demangled." % opt.cppfilt)
         opts.cppfilt = None
-    dump_nm(nmfile, strip_prefix=opts.strip_prefix, cppfilt=opts.cppfilt)
-elif mode == 'sections':
-    objdumpfile = open(opts.objdumppath, 'r')
-    dump_sections(objdumpfile)
+    dump_map(mapfile, strip_prefix=opts.strip_prefix, cppfilt=opts.cppfilt)
 elif mode == 'dump':
-    nmfile = open(opts.nmpath, 'r')
-    syms = list(parse_nm(nmfile))
+    mapfile = open(opts.mappath, 'r')
+    syms = list(parse_map(mapfile))
     # a list of (sym, type, size, path); sort by size.
     syms.sort(key=lambda x: -x[2])
     total = 0