Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(22)

Side by Side Diff: components/crash/tools/generate_breakpad_symbols.py

Issue 1315303004: Turn components/crash into a layered component. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/crash/tools/dmp2minidump.py ('k') | content/shell/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/env python
2 # Copyright 2013 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """A tool to generate symbols for a binary suitable for breakpad.
7
8 Currently, the tool only supports Linux, Android, and Mac. Support for other
9 platforms is planned.
10 """
11
12 import errno
13 import optparse
14 import os
15 import Queue
16 import re
17 import shutil
18 import subprocess
19 import sys
20 import threading
21
22
23 CONCURRENT_TASKS=4
24
25
26 def GetCommandOutput(command):
27 """Runs the command list, returning its output.
28
29 Prints the given command (which should be a list of one or more strings),
30 then runs it and returns its output (stdout) as a string.
31
32 From chromium_utils.
33 """
34 devnull = open(os.devnull, 'w')
35 proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
36 bufsize=1)
37 output = proc.communicate()[0]
38 return output
39
40
41 def GetDumpSymsBinary(build_dir=None):
42 """Returns the path to the dump_syms binary."""
43 DUMP_SYMS = 'dump_syms'
44 dump_syms_bin = os.path.join(os.path.expanduser(build_dir), DUMP_SYMS)
45 if not os.access(dump_syms_bin, os.X_OK):
46 print 'Cannot find %s.' % dump_syms_bin
47 return None
48
49 return dump_syms_bin
50
51
52 def Resolve(path, exe_path, loader_path, rpaths):
53 """Resolve a dyld path.
54
55 @executable_path is replaced with |exe_path|
56 @loader_path is replaced with |loader_path|
57 @rpath is replaced with the first path in |rpaths| where the referenced file
58 is found
59 """
60 path = path.replace('@loader_path', loader_path)
61 path = path.replace('@executable_path', exe_path)
62 if path.find('@rpath') != -1:
63 for rpath in rpaths:
64 new_path = Resolve(path.replace('@rpath', rpath), exe_path, loader_path,
65 [])
66 if os.access(new_path, os.X_OK):
67 return new_path
68 return ''
69 return path
70
71
72 def GetSharedLibraryDependenciesLinux(binary):
73 """Return absolute paths to all shared library dependecies of the binary.
74
75 This implementation assumes that we're running on a Linux system."""
76 ldd = GetCommandOutput(['ldd', binary])
77 lib_re = re.compile('\t.* => (.+) \(.*\)$')
78 result = []
79 for line in ldd.splitlines():
80 m = lib_re.match(line)
81 if m:
82 result.append(m.group(1))
83 return result
84
85
86 def GetSharedLibraryDependenciesMac(binary, exe_path):
87 """Return absolute paths to all shared library dependecies of the binary.
88
89 This implementation assumes that we're running on a Mac system."""
90 loader_path = os.path.dirname(binary)
91 otool = GetCommandOutput(['otool', '-l', binary]).splitlines()
92 rpaths = []
93 for idx, line in enumerate(otool):
94 if line.find('cmd LC_RPATH') != -1:
95 m = re.match(' *path (.*) \(offset .*\)$', otool[idx+2])
96 rpaths.append(m.group(1))
97
98 otool = GetCommandOutput(['otool', '-L', binary]).splitlines()
99 lib_re = re.compile('\t(.*) \(compatibility .*\)$')
100 deps = []
101 for line in otool:
102 m = lib_re.match(line)
103 if m:
104 dep = Resolve(m.group(1), exe_path, loader_path, rpaths)
105 if dep:
106 deps.append(os.path.normpath(dep))
107 return deps
108
109
110 def GetSharedLibraryDependencies(options, binary, exe_path):
111 """Return absolute paths to all shared library dependecies of the binary."""
112 deps = []
113 if sys.platform.startswith('linux'):
114 deps = GetSharedLibraryDependenciesLinux(binary)
115 elif sys.platform == 'darwin':
116 deps = GetSharedLibraryDependenciesMac(binary, exe_path)
117 else:
118 print "Platform not supported."
119 sys.exit(1)
120
121 result = []
122 build_dir = os.path.abspath(options.build_dir)
123 for dep in deps:
124 if (os.access(dep, os.X_OK) and
125 os.path.abspath(os.path.dirname(dep)).startswith(build_dir)):
126 result.append(dep)
127 return result
128
129
130 def mkdir_p(path):
131 """Simulates mkdir -p."""
132 try:
133 os.makedirs(path)
134 except OSError as e:
135 if e.errno == errno.EEXIST and os.path.isdir(path):
136 pass
137 else: raise
138
139
140 def GenerateSymbols(options, binaries):
141 """Dumps the symbols of binary and places them in the given directory."""
142
143 queue = Queue.Queue()
144 print_lock = threading.Lock()
145
146 def _Worker():
147 while True:
148 binary = queue.get()
149
150 should_dump_syms = True
151 reason = "no reason"
152
153 output_path = os.path.join(
154 options.symbols_dir, os.path.basename(binary))
155 if os.path.isdir(output_path):
156 if os.path.getmtime(binary) < os.path.getmtime(output_path):
157 should_dump_syms = False
158 reason = "symbols are more current than binary"
159
160 if not should_dump_syms:
161 if options.verbose:
162 with print_lock:
163 print "Skipping %s (%s)" % (binary, reason)
164 queue.task_done()
165 continue
166
167 if options.verbose:
168 with print_lock:
169 print "Generating symbols for %s" % binary
170
171 if os.path.isdir(output_path):
172 os.utime(output_path, None)
173
174 syms = GetCommandOutput([GetDumpSymsBinary(options.build_dir), '-r',
175 binary])
176 module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-F]+) (.*)\n", syms)
177 output_path = os.path.join(options.symbols_dir, module_line.group(2),
178 module_line.group(1))
179 mkdir_p(output_path)
180 symbol_file = "%s.sym" % module_line.group(2)
181 try:
182 f = open(os.path.join(output_path, symbol_file), 'w')
183 f.write(syms)
184 f.close()
185 except Exception, e:
186 # Not much we can do about this.
187 with print_lock:
188 print e
189
190 queue.task_done()
191
192 for binary in binaries:
193 queue.put(binary)
194
195 for _ in range(options.jobs):
196 t = threading.Thread(target=_Worker)
197 t.daemon = True
198 t.start()
199
200 queue.join()
201
202
203 def main():
204 parser = optparse.OptionParser()
205 parser.add_option('', '--build-dir', default='',
206 help='The build output directory.')
207 parser.add_option('', '--symbols-dir', default='',
208 help='The directory where to write the symbols file.')
209 parser.add_option('', '--binary', default='',
210 help='The path of the binary to generate symbols for.')
211 parser.add_option('', '--clear', default=False, action='store_true',
212 help='Clear the symbols directory before writing new '
213 'symbols.')
214 parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
215 type='int', help='Number of parallel tasks to run.')
216 parser.add_option('-v', '--verbose', action='store_true',
217 help='Print verbose status output.')
218
219 (options, _) = parser.parse_args()
220
221 if not options.symbols_dir:
222 print "Required option --symbols-dir missing."
223 return 1
224
225 if not options.build_dir:
226 print "Required option --build-dir missing."
227 return 1
228
229 if not options.binary:
230 print "Required option --binary missing."
231 return 1
232
233 if not os.access(options.binary, os.X_OK):
234 print "Cannot find %s." % options.binary
235 return 1
236
237 if options.clear:
238 try:
239 shutil.rmtree(options.symbols_dir)
240 except:
241 pass
242
243 if not GetDumpSymsBinary(options.build_dir):
244 return 1
245
246 # Build the transitive closure of all dependencies.
247 binaries = set([options.binary])
248 queue = [options.binary]
249 exe_path = os.path.dirname(options.binary)
250 while queue:
251 deps = GetSharedLibraryDependencies(options, queue.pop(0), exe_path)
252 new_deps = set(deps) - binaries
253 binaries |= new_deps
254 queue.extend(list(new_deps))
255
256 GenerateSymbols(options, binaries)
257
258 return 0
259
260
261 if '__main__' == __name__:
262 sys.exit(main())
OLDNEW
« no previous file with comments | « components/crash/tools/dmp2minidump.py ('k') | content/shell/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698