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

Side by Side Diff: tools/drmemory/scripts/drmemory_analyze.py

Issue 1452293002: Add Dr. Memory tool (Closed) Base URL: https://pdfium.googlesource.com/pdfium.git@master
Patch Set: remove more unused code Created 5 years, 1 month 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 | « tools/drmemory/scripts/common.py ('k') | tools/drmemory/scripts/logging_utils.py » ('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 (c) 2011 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 # drmemory_analyze.py
7
8 ''' Given a Dr. Memory output file, parses errors and uniques them.'''
9
10 from collections import defaultdict
11 import common
12 import hashlib
13 import logging
14 import optparse
15 import os
16 import re
17 import subprocess
18 import sys
19 import time
20
21 class DrMemoryError:
22 def __init__(self, report, suppression, testcase):
23 self._report = report
24 self._testcase = testcase
25
26 # Chromium-specific transformations of the suppressions:
27 # Replace 'any_test.exe' and 'chrome.dll' with '*', then remove the
28 # Dr.Memory-generated error ids from the name= lines as they don't
29 # make sense in a multiprocess report.
30 supp_lines = suppression.split("\n")
31 for l in xrange(len(supp_lines)):
32 if supp_lines[l].startswith("name="):
33 supp_lines[l] = "name=<insert_a_suppression_name_here>"
34 if supp_lines[l].startswith("chrome.dll!"):
35 supp_lines[l] = supp_lines[l].replace("chrome.dll!", "*!")
36 bang_index = supp_lines[l].find("!")
37 d_exe_index = supp_lines[l].find(".exe!")
38 if bang_index >= 4 and d_exe_index + 4 == bang_index:
39 supp_lines[l] = "*" + supp_lines[l][bang_index:]
40 self._suppression = "\n".join(supp_lines)
41
42 def __str__(self):
43 output = ""
44 output += "### BEGIN MEMORY TOOL REPORT (error hash=#%016X#)\n" % \
45 self.ErrorHash()
46 output += self._report + "\n"
47 if self._testcase:
48 output += "The report came from the `%s` test.\n" % self._testcase
49 output += "Suppression (error hash=#%016X#):\n" % self.ErrorHash()
50 output += (" For more info on using suppressions see "
51 "http://dev.chromium.org/developers/how-tos/using-drmemory#TOC-Suppressi ng-error-reports-from-the-\n")
52 output += "{\n%s\n}\n" % self._suppression
53 output += "### END MEMORY TOOL REPORT (error hash=#%016X#)\n" % \
54 self.ErrorHash()
55 return output
56
57 # This is a device-independent hash identifying the suppression.
58 # By printing out this hash we can find duplicate reports between tests and
59 # different shards running on multiple buildbots
60 def ErrorHash(self):
61 return int(hashlib.md5(self._suppression).hexdigest()[:16], 16)
62
63 def __hash__(self):
64 return hash(self._suppression)
65
66 def __eq__(self, rhs):
67 return self._suppression == rhs
68
69
70 class DrMemoryAnalyzer:
71 ''' Given a set of Dr.Memory output files, parse all the errors out of
72 them, unique them and output the results.'''
73
74 def __init__(self):
75 self.known_errors = set()
76 self.error_count = 0;
77
78 def ReadLine(self):
79 self.line_ = self.cur_fd_.readline()
80
81 def ReadSection(self):
82 result = [self.line_]
83 self.ReadLine()
84 while len(self.line_.strip()) > 0:
85 result.append(self.line_)
86 self.ReadLine()
87 return result
88
89 def ParseReportFile(self, filename, testcase):
90 ret = []
91
92 # First, read the generated suppressions file so we can easily lookup a
93 # suppression for a given error.
94 supp_fd = open(filename.replace("results", "suppress"), 'r')
95 generated_suppressions = {} # Key -> Error #, Value -> Suppression text.
96 for line in supp_fd:
97 # NOTE: this regexp looks fragile. Might break if the generated
98 # suppression format slightly changes.
99 m = re.search("# Suppression for Error #([0-9]+)", line.strip())
100 if not m:
101 continue
102 error_id = int(m.groups()[0])
103 assert error_id not in generated_suppressions
104 # OK, now read the next suppression:
105 cur_supp = ""
106 for supp_line in supp_fd:
107 if supp_line.startswith("#") or supp_line.strip() == "":
108 break
109 cur_supp += supp_line
110 generated_suppressions[error_id] = cur_supp.strip()
111 supp_fd.close()
112
113 self.cur_fd_ = open(filename, 'r')
114 while True:
115 self.ReadLine()
116 if (self.line_ == ''): break
117
118 match = re.search("^Error #([0-9]+): (.*)", self.line_)
119 if match:
120 error_id = int(match.groups()[0])
121 self.line_ = match.groups()[1].strip() + "\n"
122 report = "".join(self.ReadSection()).strip()
123 suppression = generated_suppressions[error_id]
124 ret.append(DrMemoryError(report, suppression, testcase))
125
126 if re.search("SUPPRESSIONS USED:", self.line_):
127 self.ReadLine()
128 while self.line_.strip() != "":
129 line = self.line_.strip()
130 (count, name) = re.match(" *([0-9\?]+)x(?: \(.*?\))?: (.*)",
131 line).groups()
132 if (count == "?"):
133 # Whole-module have no count available: assume 1
134 count = 1
135 else:
136 count = int(count)
137 self.used_suppressions[name] += count
138 self.ReadLine()
139
140 if self.line_.startswith("ASSERT FAILURE"):
141 ret.append(self.line_.strip())
142
143 self.cur_fd_.close()
144 return ret
145
146 def Report(self, filenames, testcase, check_sanity):
147 sys.stdout.flush()
148 # TODO(timurrrr): support positive tests / check_sanity==True
149 self.used_suppressions = defaultdict(int)
150
151 to_report = []
152 reports_for_this_test = set()
153 for f in filenames:
154 cur_reports = self.ParseReportFile(f, testcase)
155
156 # Filter out the reports that were there in previous tests.
157 for r in cur_reports:
158 if r in reports_for_this_test:
159 # A similar report is about to be printed for this test.
160 pass
161 elif r in self.known_errors:
162 # A similar report has already been printed in one of the prev tests.
163 to_report.append("This error was already printed in some "
164 "other test, see 'hash=#%016X#'" % r.ErrorHash())
165 reports_for_this_test.add(r)
166 else:
167 self.known_errors.add(r)
168 reports_for_this_test.add(r)
169 to_report.append(r)
170
171 common.PrintUsedSuppressionsList(self.used_suppressions)
172
173 if not to_report:
174 logging.info("PASS: No error reports found")
175 return 0
176
177 sys.stdout.flush()
178 sys.stderr.flush()
179 logging.info("Found %i error reports" % len(to_report))
180 for report in to_report:
181 self.error_count += 1
182 logging.info("Report #%d\n%s" % (self.error_count, report))
183 logging.info("Total: %i error reports" % len(to_report))
184 sys.stdout.flush()
185 return -1
186
187
188 def main():
189 '''For testing only. The DrMemoryAnalyze class should be imported instead.'''
190 parser = optparse.OptionParser("usage: %prog <files to analyze>")
191
192 (options, args) = parser.parse_args()
193 if len(args) == 0:
194 parser.error("no filename specified")
195 filenames = args
196
197 logging.getLogger().setLevel(logging.INFO)
198 return DrMemoryAnalyzer().Report(filenames, None, False)
199
200
201 if __name__ == '__main__':
202 sys.exit(main())
OLDNEW
« no previous file with comments | « tools/drmemory/scripts/common.py ('k') | tools/drmemory/scripts/logging_utils.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698