generate_breakpad_symbols.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #!/usr/bin/env python
  2. # Copyright (c) 2013 GitHub, Inc.
  3. # Copyright (c) 2013 The Chromium Authors. All rights reserved.
  4. # Use of this source code is governed by a BSD-style license that can be
  5. # found in the LICENSE file.
  6. """Convert pdb to sym for given directories"""
  7. import errno
  8. import glob
  9. import optparse
  10. import os
  11. import Queue
  12. import re
  13. import subprocess
  14. import sys
  15. import threading
  16. CONCURRENT_TASKS=4
  17. SOURCE_ROOT=os.path.abspath(os.path.dirname(os.path.dirname(os.path.dirname(__file__))))
  18. DUMP_SYMS=os.path.join(SOURCE_ROOT, 'vendor', 'breakpad', 'dump_syms.exe')
  19. def GetCommandOutput(command):
  20. """Runs the command list, returning its output.
  21. Prints the given command (which should be a list of one or more strings),
  22. then runs it and returns its output (stdout) as a string.
  23. From chromium_utils.
  24. """
  25. devnull = open(os.devnull, 'w')
  26. proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=devnull,
  27. bufsize=1)
  28. output = proc.communicate()[0]
  29. return output
  30. def mkdir_p(path):
  31. """Simulates mkdir -p."""
  32. try:
  33. os.makedirs(path)
  34. except OSError as e:
  35. if e.errno == errno.EEXIST and os.path.isdir(path):
  36. pass
  37. else: raise
  38. def GenerateSymbols(options, binaries):
  39. """Dumps the symbols of binary and places them in the given directory."""
  40. queue = Queue.Queue()
  41. print_lock = threading.Lock()
  42. def _Worker():
  43. while True:
  44. binary = queue.get()
  45. if options.verbose:
  46. with print_lock:
  47. print "Generating symbols for %s" % binary
  48. syms = GetCommandOutput([DUMP_SYMS, binary])
  49. module_line = re.match("MODULE [^ ]+ [^ ]+ ([0-9A-Fa-f]+) (.*)\r\n", syms)
  50. if module_line == None:
  51. with print_lock:
  52. print "Failed to get symbols for %s" % binary
  53. queue.task_done()
  54. continue
  55. output_path = os.path.join(options.symbols_dir, module_line.group(2),
  56. module_line.group(1))
  57. mkdir_p(output_path)
  58. symbol_file = "%s.sym" % module_line.group(2)[:-4] # strip .pdb
  59. f = open(os.path.join(output_path, symbol_file), 'w')
  60. f.write(syms)
  61. f.close()
  62. queue.task_done()
  63. for binary in binaries:
  64. queue.put(binary)
  65. for _ in range(options.jobs):
  66. t = threading.Thread(target=_Worker)
  67. t.daemon = True
  68. t.start()
  69. queue.join()
  70. def main():
  71. parser = optparse.OptionParser()
  72. parser.add_option('', '--symbols-dir', default='',
  73. help='The directory where to write the symbols file.')
  74. parser.add_option('', '--clear', default=False, action='store_true',
  75. help='Clear the symbols directory before writing new '
  76. 'symbols.')
  77. parser.add_option('-j', '--jobs', default=CONCURRENT_TASKS, action='store',
  78. type='int', help='Number of parallel tasks to run.')
  79. parser.add_option('-v', '--verbose', action='store_true',
  80. help='Print verbose status output.')
  81. (options, directories) = parser.parse_args()
  82. if not options.symbols_dir:
  83. print "Required option --symbols-dir missing."
  84. return 1
  85. if options.clear:
  86. try:
  87. shutil.rmtree(options.symbols_dir)
  88. except:
  89. pass
  90. pdbs = []
  91. for directory in directories:
  92. pdbs += glob.glob(os.path.join(directory, '*.exe.pdb'))
  93. pdbs += glob.glob(os.path.join(directory, '*.dll.pdb'))
  94. GenerateSymbols(options, pdbs)
  95. return 0
  96. if '__main__' == __name__:
  97. sys.exit(main())