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

Side by Side Diff: tools/drmemory/scripts/valgrind_test.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/pdfium_tests.py ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
4
5 """Runs an exe through Valgrind and puts the intermediate files in a
6 directory.
7 """
8
9 import datetime
10 import glob
11 import logging
12 import optparse
13 import os
14 import re
15 import shutil
16 import stat
17 import subprocess
18 import sys
19 import tempfile
20
21 import common
22
23 import drmemory_analyze
24
25 class BaseTool(object):
26 """Abstract class for running dynamic error detection tools.
27
28 Always subclass this and implement ToolCommand with framework- and
29 tool-specific stuff.
30 """
31
32 def __init__(self):
33 temp_parent_dir = None
34 self.log_parent_dir = ""
35 if common.IsWindows():
36 # gpu process on Windows Vista+ runs at Low Integrity and can only
37 # write to certain directories (http://crbug.com/119131)
38 #
39 # TODO(bruening): if scripts die in middle and don't clean up temp
40 # dir, we'll accumulate files in profile dir. should remove
41 # really old files automatically.
42 profile = os.getenv("USERPROFILE")
43 if profile:
44 self.log_parent_dir = profile + "\\AppData\\LocalLow\\"
45 if os.path.exists(self.log_parent_dir):
46 self.log_parent_dir = common.NormalizeWindowsPath(self.log_parent_dir)
47 temp_parent_dir = self.log_parent_dir
48 # Generated every time (even when overridden)
49 self.temp_dir = tempfile.mkdtemp(prefix="vg_logs_", dir=temp_parent_dir)
50 self.log_dir = self.temp_dir # overridable by --keep_logs
51 self.option_parser_hooks = []
52 # TODO(glider): we may not need some of the env vars on some of the
53 # platforms.
54 self._env = {
55 "G_SLICE" : "always-malloc",
56 "NSS_DISABLE_UNLOAD" : "1",
57 "NSS_DISABLE_ARENA_FREE_LIST" : "1",
58 "GTEST_DEATH_TEST_USE_FORK": "1",
59 }
60
61 def ToolName(self):
62 raise NotImplementedError, "This method should be implemented " \
63 "in the tool-specific subclass"
64
65 def Analyze(self, check_sanity=False):
66 raise NotImplementedError, "This method should be implemented " \
67 "in the tool-specific subclass"
68
69 def RegisterOptionParserHook(self, hook):
70 # Frameworks and tools can add their own flags to the parser.
71 self.option_parser_hooks.append(hook)
72
73 def CreateOptionParser(self):
74 # Defines Chromium-specific flags.
75 self._parser = optparse.OptionParser("usage: %prog [options] <program to "
76 "test>")
77 self._parser.disable_interspersed_args()
78 self._parser.add_option("-t", "--timeout",
79 dest="timeout", metavar="TIMEOUT", default=10000,
80 help="timeout in seconds for the run (default 10000)")
81 self._parser.add_option("", "--build-dir",
82 help="the location of the compiler output")
83 self._parser.add_option("", "--source-dir",
84 help="path to top of source tree for this build"
85 "(used to normalize source paths in baseline)")
86 self._parser.add_option("", "--gtest_filter", default="",
87 help="which test case to run")
88 self._parser.add_option("", "--gtest_repeat",
89 help="how many times to run each test")
90 self._parser.add_option("", "--gtest_print_time", action="store_true",
91 default=False,
92 help="show how long each test takes")
93 self._parser.add_option("", "--ignore_exit_code", action="store_true",
94 default=False,
95 help="ignore exit code of the test "
96 "(e.g. test failures)")
97 self._parser.add_option("", "--keep_logs", action="store_true",
98 default=False,
99 help="store memory tool logs in the <tool>.logs "
100 "directory instead of /tmp.\nThis can be "
101 "useful for tool developers/maintainers.\n"
102 "Please note that the <tool>.logs directory "
103 "will be clobbered on tool startup.")
104
105 # To add framework- or tool-specific flags, please add a hook using
106 # RegisterOptionParserHook in the corresponding subclass.
107 # See ValgrindTool for an example.
108 for hook in self.option_parser_hooks:
109 hook(self, self._parser)
110
111 def ParseArgv(self, args):
112 self.CreateOptionParser()
113
114 # self._tool_flags will store those tool flags which we don't parse
115 # manually in this script.
116 self._tool_flags = []
117 known_args = []
118
119 """ We assume that the first argument not starting with "-" is a program
120 name and all the following flags should be passed to the program.
121 TODO(timurrrr): customize optparse instead
122 """
123 while len(args) > 0 and args[0][:1] == "-":
124 arg = args[0]
125 if (arg == "--"):
126 break
127 if self._parser.has_option(arg.split("=")[0]):
128 known_args += [arg]
129 else:
130 self._tool_flags += [arg]
131 args = args[1:]
132
133 if len(args) > 0:
134 known_args += args
135
136 self._options, self._args = self._parser.parse_args(known_args)
137
138 self._timeout = int(self._options.timeout)
139 self._source_dir = self._options.source_dir
140 if self._options.keep_logs:
141 # log_parent_dir has trailing slash if non-empty
142 self.log_dir = self.log_parent_dir + "%s.logs" % self.ToolName()
143 if os.path.exists(self.log_dir):
144 shutil.rmtree(self.log_dir)
145 os.mkdir(self.log_dir)
146 logging.info("Logs are in " + self.log_dir)
147
148 self._ignore_exit_code = self._options.ignore_exit_code
149 if self._options.gtest_filter != "":
150 self._args.append("--gtest_filter=%s" % self._options.gtest_filter)
151 if self._options.gtest_repeat:
152 self._args.append("--gtest_repeat=%s" % self._options.gtest_repeat)
153 if self._options.gtest_print_time:
154 self._args.append("--gtest_print_time")
155
156 return True
157
158 def Setup(self, args):
159 return self.ParseArgv(args)
160
161 def ToolCommand(self):
162 raise NotImplementedError, "This method should be implemented " \
163 "in the tool-specific subclass"
164
165 def Cleanup(self):
166 # You may override it in the tool-specific subclass
167 pass
168
169 def Execute(self):
170 """ Execute the app to be tested after successful instrumentation.
171 Full execution command-line provided by subclassers via proc."""
172 logging.info("starting execution...")
173 proc = self.ToolCommand()
174 for var in self._env:
175 common.PutEnvAndLog(var, self._env[var])
176 return common.RunSubprocess(proc, self._timeout)
177
178 def RunTestsAndAnalyze(self, check_sanity):
179 exec_retcode = self.Execute()
180 analyze_retcode = self.Analyze(check_sanity)
181
182 if analyze_retcode:
183 logging.error("Analyze failed.")
184 logging.info("Search the log for '[ERROR]' to see the error reports.")
185 return analyze_retcode
186
187 if exec_retcode:
188 if self._ignore_exit_code:
189 logging.info("Test execution failed, but the exit code is ignored.")
190 else:
191 logging.error("Test execution failed.")
192 return exec_retcode
193 else:
194 logging.info("Test execution completed successfully.")
195
196 if not analyze_retcode:
197 logging.info("Analysis completed successfully.")
198
199 return 0
200
201 def Main(self, args, check_sanity, min_runtime_in_seconds):
202 """Call this to run through the whole process: Setup, Execute, Analyze"""
203 start_time = datetime.datetime.now()
204 retcode = -1
205 if self.Setup(args):
206 retcode = self.RunTestsAndAnalyze(check_sanity)
207 shutil.rmtree(self.temp_dir, ignore_errors=True)
208 self.Cleanup()
209 else:
210 logging.error("Setup failed")
211 end_time = datetime.datetime.now()
212 runtime_in_seconds = (end_time - start_time).seconds
213 hours = runtime_in_seconds / 3600
214 seconds = runtime_in_seconds % 3600
215 minutes = seconds / 60
216 seconds = seconds % 60
217 logging.info("elapsed time: %02d:%02d:%02d" % (hours, minutes, seconds))
218 if (min_runtime_in_seconds > 0 and
219 runtime_in_seconds < min_runtime_in_seconds):
220 logging.error("Layout tests finished too quickly. "
221 "It should have taken at least %d seconds. "
222 "Something went wrong?" % min_runtime_in_seconds)
223 retcode = -1
224 return retcode
225
226 def Run(self, args, module, min_runtime_in_seconds=0):
227 MODULES_TO_SANITY_CHECK = ["base"]
228
229 check_sanity = module in MODULES_TO_SANITY_CHECK
230 return self.Main(args, check_sanity, min_runtime_in_seconds)
231
232
233 class ValgrindTool(BaseTool):
234 """Abstract class for running Valgrind tools.
235
236 Always subclass this and implement ToolSpecificFlags() and
237 ExtendOptionParser() for tool-specific stuff.
238 """
239 def __init__(self):
240 super(ValgrindTool, self).__init__()
241 self.RegisterOptionParserHook(ValgrindTool.ExtendOptionParser)
242
243 def UseXML(self):
244 # Override if tool prefers nonxml output
245 return True
246
247 def ExtendOptionParser(self, parser):
248 parser.add_option("", "--suppressions", default=[],
249 action="append",
250 help="path to a valgrind suppression file")
251 parser.add_option("", "--indirect", action="store_true",
252 default=False,
253 help="set BROWSER_WRAPPER rather than "
254 "running valgrind directly")
255 parser.add_option("", "--indirect_webkit_layout", action="store_true",
256 default=False,
257 help="set --wrapper rather than running Dr. Memory "
258 "directly.")
259 parser.add_option("", "--trace_children", action="store_true",
260 default=False,
261 help="also trace child processes")
262 parser.add_option("", "--num-callers",
263 dest="num_callers", default=30,
264 help="number of callers to show in stack traces")
265 parser.add_option("", "--generate_dsym", action="store_true",
266 default=False,
267 help="Generate .dSYM file on Mac if needed. Slow!")
268
269 def Setup(self, args):
270 if not BaseTool.Setup(self, args):
271 return False
272 if common.IsMac():
273 self.PrepareForTestMac()
274 return True
275
276 def PrepareForTestMac(self):
277 """Runs dsymutil if needed.
278
279 Valgrind for Mac OS X requires that debugging information be in a .dSYM
280 bundle generated by dsymutil. It is not currently able to chase DWARF
281 data into .o files like gdb does, so executables without .dSYM bundles or
282 with the Chromium-specific "fake_dsym" bundles generated by
283 build/mac/strip_save_dsym won't give source file and line number
284 information in valgrind.
285
286 This function will run dsymutil if the .dSYM bundle is missing or if
287 it looks like a fake_dsym. A non-fake dsym that already exists is assumed
288 to be up-to-date.
289 """
290 test_command = self._args[0]
291 dsym_bundle = self._args[0] + '.dSYM'
292 dsym_file = os.path.join(dsym_bundle, 'Contents', 'Resources', 'DWARF',
293 os.path.basename(test_command))
294 dsym_info_plist = os.path.join(dsym_bundle, 'Contents', 'Info.plist')
295
296 needs_dsymutil = True
297 saved_test_command = None
298
299 if os.path.exists(dsym_file) and os.path.exists(dsym_info_plist):
300 # Look for the special fake_dsym tag in dsym_info_plist.
301 dsym_info_plist_contents = open(dsym_info_plist).read()
302
303 if not re.search('^\s*<key>fake_dsym</key>$', dsym_info_plist_contents,
304 re.MULTILINE):
305 # fake_dsym is not set, this is a real .dSYM bundle produced by
306 # dsymutil. dsymutil does not need to be run again.
307 needs_dsymutil = False
308 else:
309 # fake_dsym is set. dsym_file is a copy of the original test_command
310 # before it was stripped. Copy it back to test_command so that
311 # dsymutil has unstripped input to work with. Move the stripped
312 # test_command out of the way, it will be restored when this is
313 # done.
314 saved_test_command = test_command + '.stripped'
315 os.rename(test_command, saved_test_command)
316 shutil.copyfile(dsym_file, test_command)
317 shutil.copymode(saved_test_command, test_command)
318
319 if needs_dsymutil:
320 if self._options.generate_dsym:
321 # Remove the .dSYM bundle if it exists.
322 shutil.rmtree(dsym_bundle, True)
323
324 dsymutil_command = ['dsymutil', test_command]
325
326 # dsymutil is crazy slow. Ideally we'd have a timeout here,
327 # but common.RunSubprocess' timeout is only checked
328 # after each line of output; dsymutil is silent
329 # until the end, and is then killed, which is silly.
330 common.RunSubprocess(dsymutil_command)
331
332 if saved_test_command:
333 os.rename(saved_test_command, test_command)
334 else:
335 logging.info("No real .dSYM for test_command. Line numbers will "
336 "not be shown. Either tell xcode to generate .dSYM "
337 "file, or use --generate_dsym option to this tool.")
338
339 def ToolCommand(self):
340 """Get the valgrind command to run."""
341 # Note that self._args begins with the exe to be run.
342 tool_name = self.ToolName()
343
344 # Construct the valgrind command.
345 if 'CHROME_VALGRIND' in os.environ:
346 path = os.path.join(os.environ['CHROME_VALGRIND'], "bin", "valgrind")
347 else:
348 path = "valgrind"
349 proc = [path, "--tool=%s" % tool_name]
350
351 proc += ["--num-callers=%i" % int(self._options.num_callers)]
352
353 if self._options.trace_children:
354 proc += ["--trace-children=yes"]
355 proc += ["--trace-children-skip='*dbus-daemon*'"]
356 proc += ["--trace-children-skip='*dbus-launch*'"]
357 proc += ["--trace-children-skip='*perl*'"]
358 proc += ["--trace-children-skip='*python*'"]
359 # This is really Python, but for some reason Valgrind follows it.
360 proc += ["--trace-children-skip='*lsb_release*'"]
361
362 proc += self.ToolSpecificFlags()
363 proc += self._tool_flags
364
365 suppression_count = 0
366 for suppression_file in self._options.suppressions:
367 if os.path.exists(suppression_file):
368 suppression_count += 1
369 proc += ["--suppressions=%s" % suppression_file]
370
371 if not suppression_count:
372 logging.warning("WARNING: NOT USING SUPPRESSIONS!")
373
374 logfilename = self.log_dir + ("/%s." % tool_name) + "%p"
375 if self.UseXML():
376 proc += ["--xml=yes", "--xml-file=" + logfilename]
377 else:
378 proc += ["--log-file=" + logfilename]
379
380 # The Valgrind command is constructed.
381
382 # Handle --indirect_webkit_layout separately.
383 if self._options.indirect_webkit_layout:
384 # Need to create the wrapper before modifying |proc|.
385 wrapper = self.CreateBrowserWrapper(proc, webkit=True)
386 proc = self._args
387 proc.append("--wrapper")
388 proc.append(wrapper)
389 return proc
390
391 if self._options.indirect:
392 wrapper = self.CreateBrowserWrapper(proc)
393 os.environ["BROWSER_WRAPPER"] = wrapper
394 logging.info('export BROWSER_WRAPPER=' + wrapper)
395 proc = []
396 proc += self._args
397 return proc
398
399 def ToolSpecificFlags(self):
400 raise NotImplementedError, "This method should be implemented " \
401 "in the tool-specific subclass"
402
403 def CreateBrowserWrapper(self, proc, webkit=False):
404 """The program being run invokes Python or something else that can't stand
405 to be valgrinded, and also invokes the Chrome browser. In this case, use a
406 magic wrapper to only valgrind the Chrome browser. Build the wrapper here.
407 Returns the path to the wrapper. It's up to the caller to use the wrapper
408 appropriately.
409 """
410 command = " ".join(proc)
411 # Add the PID of the browser wrapper to the logfile names so we can
412 # separate log files for different UI tests at the analyze stage.
413 command = command.replace("%p", "$$.%p")
414
415 (fd, indirect_fname) = tempfile.mkstemp(dir=self.log_dir,
416 prefix="browser_wrapper.",
417 text=True)
418 f = os.fdopen(fd, "w")
419 f.write('#!/bin/bash\n'
420 'echo "Started Valgrind wrapper for this test, PID=$$" >&2\n')
421
422 f.write('DIR=`dirname $0`\n'
423 'TESTNAME_FILE=$DIR/testcase.$$.name\n\n')
424
425 if webkit:
426 # Webkit layout_tests pass the URL as the first line of stdin.
427 f.write('tee $TESTNAME_FILE | %s "$@"\n' % command)
428 else:
429 # Try to get the test case name by looking at the program arguments.
430 # i.e. Chromium ui_tests used --test-name arg.
431 # TODO(timurrrr): This doesn't handle "--test-name Test.Name"
432 # TODO(timurrrr): ui_tests are dead. Where do we use the non-webkit
433 # wrapper now? browser_tests? What do they do?
434 f.write('for arg in $@\ndo\n'
435 ' if [[ "$arg" =~ --test-name=(.*) ]]\n then\n'
436 ' echo ${BASH_REMATCH[1]} >$TESTNAME_FILE\n'
437 ' fi\n'
438 'done\n\n'
439 '%s "$@"\n' % command)
440
441 f.close()
442 os.chmod(indirect_fname, stat.S_IRUSR|stat.S_IXUSR)
443 return indirect_fname
444
445 def CreateAnalyzer(self):
446 raise NotImplementedError, "This method should be implemented " \
447 "in the tool-specific subclass"
448
449 def GetAnalyzeResults(self, check_sanity=False):
450 # Glob all the files in the log directory
451 filenames = glob.glob(self.log_dir + "/" + self.ToolName() + ".*")
452
453 # If we have browser wrapper, the logfiles are named as
454 # "toolname.wrapper_PID.valgrind_PID".
455 # Let's extract the list of wrapper_PIDs and name it ppids
456 ppids = set([int(f.split(".")[-2]) \
457 for f in filenames if re.search("\.[0-9]+\.[0-9]+$", f)])
458
459 analyzer = self.CreateAnalyzer()
460 if len(ppids) == 0:
461 # Fast path - no browser wrapper was set.
462 return analyzer.Report(filenames, None, check_sanity)
463
464 ret = 0
465 for ppid in ppids:
466 testcase_name = None
467 try:
468 f = open(self.log_dir + ("/testcase.%d.name" % ppid))
469 testcase_name = f.read().strip()
470 f.close()
471 wk_layout_prefix="third_party/WebKit/LayoutTests/"
472 wk_prefix_at = testcase_name.rfind(wk_layout_prefix)
473 if wk_prefix_at != -1:
474 testcase_name = testcase_name[wk_prefix_at + len(wk_layout_prefix):]
475 except IOError:
476 pass
477 print "====================================================="
478 print " Below is the report for valgrind wrapper PID=%d." % ppid
479 if testcase_name:
480 print " It was used while running the `%s` test." % testcase_name
481 else:
482 print " You can find the corresponding test"
483 print " by searching the above log for 'PID=%d'" % ppid
484 sys.stdout.flush()
485
486 ppid_filenames = [f for f in filenames \
487 if re.search("\.%d\.[0-9]+$" % ppid, f)]
488 # check_sanity won't work with browser wrappers
489 assert check_sanity == False
490 ret |= analyzer.Report(ppid_filenames, testcase_name)
491 print "====================================================="
492 sys.stdout.flush()
493
494 if ret != 0:
495 print ""
496 print "The Valgrind reports are grouped by test names."
497 print "Each test has its PID printed in the log when the test was run"
498 print "and at the beginning of its Valgrind report."
499 print "Hint: you can search for the reports by Ctrl+F -> `=#`"
500 sys.stdout.flush()
501
502 return ret
503
504 class DrMemory(BaseTool):
505 """Dr.Memory
506 Dynamic memory error detector for Windows.
507
508 http://dev.chromium.org/developers/how-tos/using-drmemory
509 It is not very mature at the moment, some things might not work properly.
510 """
511
512 def __init__(self, full_mode, pattern_mode):
513 super(DrMemory, self).__init__()
514 self.full_mode = full_mode
515 self.pattern_mode = pattern_mode
516 self.RegisterOptionParserHook(DrMemory.ExtendOptionParser)
517
518 def ToolName(self):
519 return "drmemory"
520
521 def ExtendOptionParser(self, parser):
522 parser.add_option("", "--suppressions", default=[],
523 action="append",
524 help="path to a drmemory suppression file")
525 parser.add_option("", "--follow_python", action="store_true",
526 default=False, dest="follow_python",
527 help="Monitor python child processes. If off, neither "
528 "python children nor any children of python children "
529 "will be monitored.")
530 parser.add_option("", "--indirect", action="store_true",
531 default=False,
532 help="set BROWSER_WRAPPER rather than "
533 "running Dr. Memory directly on the harness")
534 parser.add_option("", "--indirect_webkit_layout", action="store_true",
535 default=False,
536 help="set --wrapper rather than running valgrind "
537 "directly.")
538 parser.add_option("", "--use_debug", action="store_true",
539 default=False, dest="use_debug",
540 help="Run Dr. Memory debug build")
541 parser.add_option("", "--trace_children", action="store_true",
542 default=True,
543 help="TODO: default value differs from Valgrind")
544
545 def ToolCommand(self):
546 """Get the tool command to run."""
547 # WINHEAP is what Dr. Memory supports as there are issues w/ both
548 # jemalloc (https://github.com/DynamoRIO/drmemory/issues/320) and
549 # tcmalloc (https://github.com/DynamoRIO/drmemory/issues/314)
550 add_env = {
551 "CHROME_ALLOCATOR" : "WINHEAP",
552 "JSIMD_FORCEMMX" : "1", # https://github.com/DynamoRIO/drmemory/issues/ 540
553 }
554 for k,v in add_env.iteritems():
555 logging.info("export %s=%s", k, v)
556 os.putenv(k, v)
557
558 drmem_cmd = os.getenv("DRMEMORY_COMMAND")
559 if not drmem_cmd:
560 raise RuntimeError, "Please set DRMEMORY_COMMAND environment variable " \
561 "with the path to drmemory.exe"
562 proc = drmem_cmd.split(" ")
563
564 # By default, don't run python (this will exclude python's children as well)
565 # to reduce runtime. We're not really interested in spending time finding
566 # bugs in the python implementation.
567 # With file-based config we must update the file every time, and
568 # it will affect simultaneous drmem uses by this user. While file-based
569 # config has many advantages, here we may want this-instance-only
570 # (https://github.com/DynamoRIO/drmemory/issues/334).
571 drconfig_cmd = [ proc[0].replace("drmemory.exe", "drconfig.exe") ]
572 drconfig_cmd += ["-quiet"] # suppress errors about no 64-bit libs
573 run_drconfig = True
574 if self._options.follow_python:
575 logging.info("Following python children")
576 # -unreg fails if not already registered so query for that first
577 query_cmd = drconfig_cmd + ["-isreg", "python.exe"]
578 query_proc = subprocess.Popen(query_cmd, stdout=subprocess.PIPE,
579 shell=True)
580 (query_out, query_err) = query_proc.communicate()
581 if re.search("exe not registered", query_out):
582 run_drconfig = False # all set
583 else:
584 drconfig_cmd += ["-unreg", "python.exe"]
585 else:
586 logging.info("Excluding python children")
587 drconfig_cmd += ["-reg", "python.exe", "-norun"]
588 if run_drconfig:
589 drconfig_retcode = common.RunSubprocess(drconfig_cmd, self._timeout)
590 if drconfig_retcode:
591 logging.error("Configuring whether to follow python children failed " \
592 "with %d.", drconfig_retcode)
593 raise RuntimeError, "Configuring python children failed "
594
595 suppression_count = 0
596 supp_files = self._options.suppressions
597 if self.full_mode:
598 supp_files += [s.replace(".txt", "_full.txt") for s in supp_files]
599 for suppression_file in supp_files:
600 if os.path.exists(suppression_file):
601 suppression_count += 1
602 proc += ["-suppress", common.NormalizeWindowsPath(suppression_file)]
603
604 if not suppression_count:
605 logging.warning("WARNING: NOT USING SUPPRESSIONS!")
606
607 # Un-comment to dump Dr.Memory events on error
608 #proc += ["-dr_ops", "-dumpcore_mask", "-dr_ops", "0x8bff"]
609
610 # Un-comment and comment next line to debug Dr.Memory
611 #proc += ["-dr_ops", "-no_hide"]
612 #proc += ["-dr_ops", "-msgbox_mask", "-dr_ops", "15"]
613 #Proc += ["-dr_ops", "-stderr_mask", "-dr_ops", "15"]
614 # Ensure we see messages about Dr. Memory crashing!
615 proc += ["-dr_ops", "-stderr_mask", "-dr_ops", "12"]
616
617 if self._options.use_debug:
618 proc += ["-debug"]
619
620 proc += ["-logdir", common.NormalizeWindowsPath(self.log_dir)]
621
622 if self.log_parent_dir:
623 # gpu process on Windows Vista+ runs at Low Integrity and can only
624 # write to certain directories (http://crbug.com/119131)
625 symcache_dir = os.path.join(self.log_parent_dir, "drmemory.symcache")
626 elif self._options.build_dir:
627 # The other case is only possible with -t cmdline.
628 # Anyways, if we omit -symcache_dir the -logdir's value is used which
629 # should be fine.
630 symcache_dir = os.path.join(self._options.build_dir, "drmemory.symcache")
631 if symcache_dir:
632 if not os.path.exists(symcache_dir):
633 try:
634 os.mkdir(symcache_dir)
635 except OSError:
636 logging.warning("Can't create symcache dir?")
637 if os.path.exists(symcache_dir):
638 proc += ["-symcache_dir", common.NormalizeWindowsPath(symcache_dir)]
639
640 # Use -no_summary to suppress DrMemory's summary and init-time
641 # notifications. We generate our own with drmemory_analyze.py.
642 proc += ["-batch", "-no_summary"]
643
644 # Un-comment to disable interleaved output. Will also suppress error
645 # messages normally printed to stderr.
646 #proc += ["-quiet", "-no_results_to_stderr"]
647
648 proc += ["-callstack_max_frames", "40"]
649
650 # disable leak scan for now
651 proc += ["-no_count_leaks", "-no_leak_scan"]
652
653 # disable warnings about unaddressable prefetches
654 proc += ["-no_check_prefetch"]
655
656 # crbug.com/413215, no heap mismatch check for Windows release build binary
657 if common.IsWindows() and "Release" in self._options.build_dir:
658 proc += ["-no_check_delete_mismatch"]
659
660 # make callstacks easier to read
661 proc += ["-callstack_srcfile_prefix",
662 "build\\src,chromium\\src,crt_build\\self_x86"]
663 proc += ["-callstack_modname_hide",
664 "*drmemory*,chrome.dll"]
665
666 boring_callers = common.BoringCallers(mangled=False, use_re_wildcards=False)
667 # TODO(timurrrr): In fact, we want "starting from .." instead of "below .."
668 proc += ["-callstack_truncate_below", ",".join(boring_callers)]
669
670 if self.pattern_mode:
671 proc += ["-pattern", "0xf1fd", "-no_count_leaks", "-redzone_size", "0x20"]
672 elif not self.full_mode:
673 proc += ["-light"]
674
675 proc += self._tool_flags
676
677 # Dr.Memory requires -- to separate tool flags from the executable name.
678 proc += ["--"]
679
680 if self._options.indirect or self._options.indirect_webkit_layout:
681 wrapper_path = os.path.join(self._source_dir,
682 "tools", "valgrind", "browser_wrapper_win.py")
683 wrapper = " ".join(["python", wrapper_path] + proc)
684 self.CreateBrowserWrapper(wrapper)
685 logging.info("browser wrapper = " + " ".join(proc))
686 if self._options.indirect_webkit_layout:
687 proc = self._args
688 # Layout tests want forward slashes.
689 wrapper = wrapper.replace('\\', '/')
690 proc += ["--wrapper", wrapper]
691 return proc
692 else:
693 proc = []
694
695 # Note that self._args begins with the name of the exe to be run.
696 self._args[0] = common.NormalizeWindowsPath(self._args[0])
697 proc += self._args
698 return proc
699
700 def CreateBrowserWrapper(self, command):
701 os.putenv("BROWSER_WRAPPER", command)
702
703 def Analyze(self, check_sanity=False):
704 # Use one analyzer for all the log files to avoid printing duplicate reports
705 #
706 # TODO(timurrrr): unify this with Valgrind and other tools when we have
707 # https://github.com/DynamoRIO/drmemory/issues/684
708 analyzer = drmemory_analyze.DrMemoryAnalyzer()
709
710 ret = 0
711 if not self._options.indirect and not self._options.indirect_webkit_layout:
712 filenames = glob.glob(self.log_dir + "/*/results.txt")
713
714 ret = analyzer.Report(filenames, None, check_sanity)
715 else:
716 testcases = glob.glob(self.log_dir + "/testcase.*.logs")
717 # If we have browser wrapper, the per-test logdirs are named as
718 # "testcase.wrapper_PID.name".
719 # Let's extract the list of wrapper_PIDs and name it ppids.
720 # NOTE: ppids may contain '_', i.e. they are not ints!
721 ppids = set([f.split(".")[-2] for f in testcases])
722
723 for ppid in ppids:
724 testcase_name = None
725 try:
726 f = open("%s/testcase.%s.name" % (self.log_dir, ppid))
727 testcase_name = f.read().strip()
728 f.close()
729 except IOError:
730 pass
731 print "====================================================="
732 print " Below is the report for drmemory wrapper PID=%s." % ppid
733 if testcase_name:
734 print " It was used while running the `%s` test." % testcase_name
735 else:
736 # TODO(timurrrr): hm, the PID line is suppressed on Windows...
737 print " You can find the corresponding test"
738 print " by searching the above log for 'PID=%s'" % ppid
739 sys.stdout.flush()
740 ppid_filenames = glob.glob("%s/testcase.%s.logs/*/results.txt" %
741 (self.log_dir, ppid))
742 ret |= analyzer.Report(ppid_filenames, testcase_name, False)
743 print "====================================================="
744 sys.stdout.flush()
745
746 logging.info("Please see http://dev.chromium.org/developers/how-tos/"
747 "using-drmemory for the info on Dr. Memory")
748 return ret
749
750
751 class ToolFactory:
752 def Create(self, tool_name):
753 if tool_name == "drmemory" or tool_name == "drmemory_light":
754 # TODO(timurrrr): remove support for "drmemory" when buildbots are
755 # switched to drmemory_light OR make drmemory==drmemory_full the default
756 # mode when the tool is mature enough.
757 return DrMemory(False, False)
758 if tool_name == "drmemory_full":
759 return DrMemory(True, False)
760 if tool_name == "drmemory_pattern":
761 return DrMemory(False, True)
762 try:
763 platform_name = common.PlatformNames()[0]
764 except common.NotImplementedError:
765 platform_name = sys.platform + "(Unknown)"
766 raise RuntimeError, "Unknown tool (tool=%s, platform=%s)" % (tool_name,
767 platform_name)
768
769 def CreateTool(tool):
770 return ToolFactory().Create(tool)
OLDNEW
« no previous file with comments | « tools/drmemory/scripts/pdfium_tests.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698