File: llvm_symbolizer.py

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (134 lines) | stat: -rw-r--r-- 4,117 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import functools
import logging
import os
import subprocess
import threading

_CHROME_SRC = os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)
_LLVM_SYMBOLIZER_PATH = os.path.join(
    _CHROME_SRC, 'third_party', 'llvm-build', 'Release+Asserts', 'bin',
    'llvm-symbolizer')

_UNKNOWN = '<UNKNOWN>'

_ELF_MAGIC_HEADER_BYTES = b'\x7f\x45\x4c\x46'

@functools.lru_cache
def IsValidLLVMSymbolizerTarget(file_path):
  """ Verify the passed file is a valid target for llvm-symbolization

  Args:
    file_path: Path to a file to be checked

  Return:
    True if the file exists and has the correct ELF header, False otherwise
  """
  try:
    with open(file_path, 'rb') as f:
      header_bytes = f.read(4)
      return header_bytes == _ELF_MAGIC_HEADER_BYTES
  except IOError:
    return False


class LLVMSymbolizer(object):
  def __init__(self):
    """Create a LLVMSymbolizer instance that interacts with the llvm symbolizer.

    The purpose of the LLVMSymbolizer is to get function names and line
    numbers of an address from the symbols library.
    """
    self._llvm_symbolizer_subprocess = None
    self._llvm_symbolizer_parameters = [
        '--functions',
        '--demangle',
        '--inlines',
    ]

    # Allow only one thread to call GetSymbolInformation at a time.
    self._lock = threading.Lock()

  def Start(self):
    """Start the llvm symbolizer subprocess.

    Create a subprocess of the llvm symbolizer executable, which will be used
    to retrieve function names etc.
    """
    if os.path.isfile(_LLVM_SYMBOLIZER_PATH):
      self._llvm_symbolizer_subprocess = subprocess.Popen(
          [_LLVM_SYMBOLIZER_PATH] + self._llvm_symbolizer_parameters,
          stdout=subprocess.PIPE,
          stdin=subprocess.PIPE,
          universal_newlines=True)
    else:
      logging.error('Cannot find llvm_symbolizer here: %s.' %
                    _LLVM_SYMBOLIZER_PATH)
      self._llvm_symbolizer_subprocess = None

  def Close(self):
    """Close the llvm symbolizer subprocess.

    Close the subprocess by closing stdin, stdout and killing the subprocess.
    """
    with self._lock:
      if self._llvm_symbolizer_subprocess:
        self._llvm_symbolizer_subprocess.kill()
        self._llvm_symbolizer_subprocess = None

  def __enter__(self):
    """Start the llvm symbolizer subprocess."""
    self.Start()
    return self

  def __exit__(self, exc_type, exc_val, exc_tb):
    """Close the llvm symbolizer subprocess."""
    self.Close()

  def GetSymbolInformation(self, lib, addr):
    """Return the corresponding function names and line numbers.

    Args:
      lib: library to search for info.
      addr: address to look for info.

    Returns:
      A triplet of address, module-name and list of symbols
    """
    if (self._llvm_symbolizer_subprocess is None):
      logging.error('Can\'t run llvm-symbolizer! ' +
                    'Subprocess for llvm-symbolizer has not been started!')
      return [(_UNKNOWN, lib)]

    if not lib:
      logging.error('Can\'t run llvm-symbolizer! No target is given!')
      return [(_UNKNOWN, lib)]

    if not IsValidLLVMSymbolizerTarget(lib):
      logging.error(
          'Can\'t run llvm-symbolizer! ' +
          'Given binary is not a valid target. path=%s', lib)
      return [(_UNKNOWN, lib)]

    proc = self._llvm_symbolizer_subprocess
    with self._lock:
      proc.stdin.write('%s %s\n' % (lib, hex(addr)))
      proc.stdin.flush()
      result = []
      # Read until an empty line is observed, which indicates the end of the
      # output. Each line with a function name is always followed by one line
      # with the corresponding line number.
      while True:
        line = proc.stdout.readline()
        if line != '\n':
          line_numbers = proc.stdout.readline()
          result.append((line[:-1], line_numbers[:-1]))
        else:
          return result

  @staticmethod
  def IsValidTarget(path):
    return IsValidLLVMSymbolizerTarget(path)