OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 # memcheck_analyze.py | 6 # memcheck_analyze.py |
7 | 7 |
8 ''' Given a valgrind XML file, parses errors and uniques them.''' | 8 ''' Given a valgrind XML file, parses errors and uniques them.''' |
9 | 9 |
10 import gdb_helper | 10 import gdb_helper |
11 | 11 |
| 12 from collections import defaultdict |
12 import hashlib | 13 import hashlib |
13 import logging | 14 import logging |
14 import optparse | 15 import optparse |
15 import os | 16 import os |
16 import re | 17 import re |
17 import subprocess | 18 import subprocess |
18 import sys | 19 import sys |
19 import time | 20 import time |
20 from xml.dom.minidom import parse | 21 from xml.dom.minidom import parse |
21 from xml.parsers.expat import ExpatError | 22 from xml.parsers.expat import ExpatError |
(...skipping 406 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
428 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> | 429 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> |
429 # giving the filename and load address of each binary that was mapped | 430 # giving the filename and load address of each binary that was mapped |
430 # into the process. | 431 # into the process. |
431 | 432 |
432 global TheAddressTable | 433 global TheAddressTable |
433 if self._use_gdb: | 434 if self._use_gdb: |
434 TheAddressTable = gdb_helper.AddressTable() | 435 TheAddressTable = gdb_helper.AddressTable() |
435 else: | 436 else: |
436 TheAddressTable = None | 437 TheAddressTable = None |
437 cur_report_errors = set() | 438 cur_report_errors = set() |
438 suppcounts = {} | 439 suppcounts = defaultdict(int) |
439 badfiles = set() | 440 badfiles = set() |
440 | 441 |
441 if self._analyze_start_time == None: | 442 if self._analyze_start_time == None: |
442 self._analyze_start_time = time.time() | 443 self._analyze_start_time = time.time() |
443 start_time = self._analyze_start_time | 444 start_time = self._analyze_start_time |
444 | 445 |
445 parse_failed = False | 446 parse_failed = False |
446 for file in files: | 447 for file in files: |
447 # Wait up to three minutes for valgrind to finish writing all files, | 448 # Wait up to three minutes for valgrind to finish writing all files, |
448 # but after that, just skip incomplete files and warn. | 449 # but after that, just skip incomplete files and warn. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
541 # ... and we haven't seen it in other tests as well | 542 # ... and we haven't seen it in other tests as well |
542 self._errors.add(error) | 543 self._errors.add(error) |
543 cur_report_errors.add(error) | 544 cur_report_errors.add(error) |
544 | 545 |
545 suppcountlist = parsed_file.getElementsByTagName("suppcounts") | 546 suppcountlist = parsed_file.getElementsByTagName("suppcounts") |
546 if len(suppcountlist) > 0: | 547 if len(suppcountlist) > 0: |
547 suppcountlist = suppcountlist[0] | 548 suppcountlist = suppcountlist[0] |
548 for node in suppcountlist.getElementsByTagName("pair"): | 549 for node in suppcountlist.getElementsByTagName("pair"): |
549 count = getTextOf(node, "count"); | 550 count = getTextOf(node, "count"); |
550 name = getTextOf(node, "name"); | 551 name = getTextOf(node, "name"); |
551 if name in suppcounts: | 552 suppcounts[name] += int(count) |
552 suppcounts[name] += int(count) | |
553 else: | |
554 suppcounts[name] = int(count) | |
555 | 553 |
556 if len(badfiles) > 0: | 554 if len(badfiles) > 0: |
557 logging.warn("valgrind didn't finish writing %d files?!" % len(badfiles)) | 555 logging.warn("valgrind didn't finish writing %d files?!" % len(badfiles)) |
558 for file in badfiles: | 556 for file in badfiles: |
559 logging.warn("Last 20 lines of %s :" % file) | 557 logging.warn("Last 20 lines of %s :" % file) |
560 os.system("tail -n 20 '%s' 1>&2" % file) | 558 os.system("tail -n 20 '%s' 1>&2" % file) |
561 | 559 |
562 if parse_failed: | 560 if parse_failed: |
563 logging.error("FAIL! Couldn't parse Valgrind output file") | 561 logging.error("FAIL! Couldn't parse Valgrind output file") |
564 return -2 | 562 return -2 |
565 | 563 |
566 is_sane = False | 564 common.PrintUsedSuppressionsList(suppcounts) |
567 print "-----------------------------------------------------" | |
568 print "Suppressions used:" | |
569 print " count name" | |
570 | |
571 remaining_sanity_supp = MemcheckAnalyzer.SANITY_TEST_SUPPRESSIONS | |
572 for (name, count) in sorted(suppcounts.items(), | |
573 key=lambda (k,v): (v,k)): | |
574 print "%7d %s" % (count, name) | |
575 if name in remaining_sanity_supp and remaining_sanity_supp[name] == count: | |
576 del remaining_sanity_supp[name] | |
577 if len(remaining_sanity_supp) == 0: | |
578 is_sane = True | |
579 print "-----------------------------------------------------" | |
580 sys.stdout.flush() | |
581 | 565 |
582 retcode = 0 | 566 retcode = 0 |
583 if cur_report_errors: | 567 if cur_report_errors: |
584 logging.error("FAIL! There were %s errors: " % len(cur_report_errors)) | 568 logging.error("FAIL! There were %s errors: " % len(cur_report_errors)) |
585 | 569 |
586 if TheAddressTable != None: | 570 if TheAddressTable != None: |
587 TheAddressTable.ResolveAll() | 571 TheAddressTable.ResolveAll() |
588 | 572 |
589 for error in cur_report_errors: | 573 for error in cur_report_errors: |
590 logging.error(error) | 574 logging.error(error) |
591 | 575 |
592 retcode = -1 | 576 retcode = -1 |
593 | 577 |
594 # Report tool's insanity even if there were errors. | 578 # Report tool's insanity even if there were errors. |
595 if check_sanity and not is_sane: | 579 if check_sanity: |
596 logging.error("FAIL! Sanity check failed!") | 580 remaining_sanity_supp = MemcheckAnalyzer.SANITY_TEST_SUPPRESSIONS |
597 logging.info("The following test errors were not handled: ") | 581 for (name, count) in suppcounts.iteritems(): |
598 for (name, count) in sorted(remaining_sanity_supp.items(), | 582 if (name in remaining_sanity_supp and |
599 key=lambda (k,v): (v,k)): | 583 remaining_sanity_supp[name] == count): |
600 logging.info("%7d %s" % (count, name)) | 584 del remaining_sanity_supp[name] |
| 585 if remaining_sanity_supp: |
| 586 logging.error("FAIL! Sanity check failed!") |
| 587 logging.info("The following test errors were not handled: ") |
| 588 for (name, count) in remaining_sanity_supp.iteritems(): |
| 589 logging.info(" * %dx %s" % (count, name)) |
601 retcode = -3 | 590 retcode = -3 |
602 | 591 |
603 if retcode != 0: | 592 if retcode != 0: |
604 return retcode | 593 return retcode |
605 | 594 |
606 logging.info("PASS! No errors found!") | 595 logging.info("PASS! No errors found!") |
607 return 0 | 596 return 0 |
608 | 597 |
609 def _main(): | 598 def _main(): |
610 '''For testing only. The MemcheckAnalyzer class should be imported instead.''' | 599 '''For testing only. The MemcheckAnalyzer class should be imported instead.''' |
611 retcode = 0 | 600 retcode = 0 |
612 parser = optparse.OptionParser("usage: %prog [options] <files to analyze>") | 601 parser = optparse.OptionParser("usage: %prog [options] <files to analyze>") |
613 parser.add_option("", "--source_dir", | 602 parser.add_option("", "--source_dir", |
614 help="path to top of source tree for this build" | 603 help="path to top of source tree for this build" |
615 "(used to normalize source paths in baseline)") | 604 "(used to normalize source paths in baseline)") |
616 | 605 |
617 (options, args) = parser.parse_args() | 606 (options, args) = parser.parse_args() |
618 if len(args) == 0: | 607 if len(args) == 0: |
619 parser.error("no filename specified") | 608 parser.error("no filename specified") |
620 filenames = args | 609 filenames = args |
621 | 610 |
622 analyzer = MemcheckAnalyzer(options.source_dir, use_gdb=True) | 611 analyzer = MemcheckAnalyzer(options.source_dir, use_gdb=True) |
623 retcode = analyzer.Report(filenames, None) | 612 retcode = analyzer.Report(filenames, None) |
624 | 613 |
625 sys.exit(retcode) | 614 sys.exit(retcode) |
626 | 615 |
627 if __name__ == "__main__": | 616 if __name__ == "__main__": |
628 _main() | 617 _main() |
OLD | NEW |