generate-asm-lcov.py
Go to the documentation of this file.
1 #!/usr/bin/python
2 # Copyright (c) 2016, Google Inc.
3 #
4 # Permission to use, copy, modify, and/or distribute this software for any
5 # purpose with or without fee is hereby granted, provided that the above
6 # copyright notice and this permission notice appear in all copies.
7 #
8 # THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 import os
16 import os.path
17 import subprocess
18 import sys
19 
20 # The LCOV output format for each source file is:
21 #
22 # SF:<filename>
23 # DA:<line>,<execution count>
24 # ...
25 # end_of_record
26 #
27 # The <execution count> can either be 0 for an unexecuted instruction or a
28 # value representing the number of executions. The DA line should be omitted
29 # for lines not representing an instruction.
30 
31 SECTION_SEPERATOR = '-' * 80
32 
33 def is_asm(l):
34  """Returns whether a line should be considered to be an instruction."""
35  l = l.strip()
36  # Empty lines
37  if l == '':
38  return False
39  # Comments
40  if l.startswith('#'):
41  return False
42  # Assembly Macros
43  if l.startswith('.'):
44  return False
45  # Label
46  if l.endswith(':'):
47  return False
48  return True
49 
50 def merge(callgrind_files, srcs):
51  """Calls callgrind_annotate over the set of callgrind output
52  |callgrind_files| using the sources |srcs| and merges the results
53  together."""
54  out = ''
55  for file in callgrind_files:
56  data = subprocess.check_output(['callgrind_annotate', file] + srcs)
57  out += '%s\n%s\n' % (data, SECTION_SEPERATOR)
58  return out
59 
60 def parse(filename, data, current):
61  """Parses an annotated execution flow |data| from callgrind_annotate for
62  source |filename| and updates the current execution counts from |current|."""
63  with open(filename) as f:
64  source = f.read().split('\n')
65 
66  out = current
67  if out == None:
68  out = [0 if is_asm(l) else None for l in source]
69 
70  # Lines are of the following formats:
71  # -- line: Indicates that analysis continues from a different place.
72  # Ir : Indicates the start of a file.
73  # => : Indicates a call/jump in the control flow.
74  # <Count> <Code>: Indicates that the line has been executed that many times.
75  line = None
76  for l in data:
77  l = l.strip() + ' '
78  if l.startswith('-- line'):
79  line = int(l.split(' ')[2]) - 1
80  elif l.strip() == 'Ir':
81  line = 0
82  elif line != None and l.strip() and '=>' not in l and 'unidentified lines' not in l:
83  count = l.split(' ')[0].replace(',', '').replace('.', '0')
84  instruction = l.split(' ', 1)[1].strip()
85  if count != '0' or is_asm(instruction):
86  if out[line] == None:
87  out[line] = 0
88  out[line] += int(count)
89  line += 1
90 
91  return out
92 
93 
94 def generate(data):
95  """Parses the merged callgrind_annotate output |data| and generates execution
96  counts for all annotated files."""
97  out = {}
98  data = [p.strip() for p in data.split(SECTION_SEPERATOR)]
99 
100 
101  # Most sections are ignored, but a section with:
102  # User-annotated source: <file>
103  # precedes a listing of execution count for that <file>.
104  for i in range(len(data)):
105  if 'User-annotated source' in data[i] and i < len(data) - 1:
106  filename = data[i].split(':', 1)[1].strip()
107  res = data[i + 1]
108  if filename not in out:
109  out[filename] = None
110  if 'No information' in res:
111  res = []
112  else:
113  res = res.split('\n')
114  out[filename] = parse(filename, res, out[filename])
115  return out
116 
117 def output(data):
118  """Takes a dictionary |data| of filenames and execution counts and generates
119  a LCOV coverage output."""
120  out = ''
121  for filename, counts in data.iteritems():
122  out += 'SF:%s\n' % (os.path.abspath(filename))
123  for line, count in enumerate(counts):
124  if count != None:
125  out += 'DA:%d,%s\n' % (line + 1, count)
126  out += 'end_of_record\n'
127  return out
128 
129 if __name__ == '__main__':
130  if len(sys.argv) != 3:
131  print '%s <Callgrind Folder> <Build Folder>' % (__file__)
132  sys.exit()
133 
134  cg_folder = sys.argv[1]
135  build_folder = sys.argv[2]
136 
137  cg_files = []
138  for (cwd, _, files) in os.walk(cg_folder):
139  for f in files:
140  if f.startswith('callgrind.out'):
141  cg_files.append(os.path.abspath(os.path.join(cwd, f)))
142 
143  srcs = []
144  for (cwd, _, files) in os.walk(build_folder):
145  for f in files:
146  fn = os.path.join(cwd, f)
147  if fn.endswith('.S'):
148  srcs.append(fn)
149 
150  annotated = merge(cg_files, srcs)
151  lcov = generate(annotated)
152  print output(lcov)
generate-asm-lcov.parse
def parse(filename, data, current)
Definition: generate-asm-lcov.py:60
capstone.range
range
Definition: third_party/bloaty/third_party/capstone/bindings/python/capstone/__init__.py:6
xds_interop_client.int
int
Definition: xds_interop_client.py:113
generate-asm-lcov.generate
def generate(data)
Definition: generate-asm-lcov.py:94
generate-asm-lcov.is_asm
def is_asm(l)
Definition: generate-asm-lcov.py:33
generate
Definition: generate.py:1
generate-asm-lcov.output
def output(data)
Definition: generate-asm-lcov.py:117
open
#define open
Definition: test-fs.c:46
len
int len
Definition: abseil-cpp/absl/base/internal/low_level_alloc_test.cc:46
generate-asm-lcov.merge
def merge(callgrind_files, srcs)
Definition: generate-asm-lcov.py:50
split
static void split(const char *s, char ***ss, size_t *ns)
Definition: debug/trace.cc:111


grpc
Author(s):
autogenerated on Thu Mar 13 2025 02:59:23