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

Side by Side Diff: tools/purify/purify_analyze.py

Issue 20097: Allow purify scripts to take a --report_dir argument which specifies where th... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 10 months 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 | Annotate | Revision Log
« no previous file with comments | « tools/purify/chrome_tests.py ('k') | tools/purify/purify_test.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/bin/env python 1 #!/bin/env python
2 # Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 2 # Copyright (c) 2006-2008 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 # purify_analyze.py 6 # purify_analyze.py
7 7
8 ''' Given a Purify text file, parses messages, normalizes and uniques them. 8 ''' Given a Purify text file, parses messages, normalizes and uniques them.
9 If there's an existing baseline of this data, it can compare against that 9 If there's an existing baseline of this data, it can compare against that
10 baseline and return an error code if there are any new errors not in the 10 baseline and return an error code if there are any new errors not in the
(...skipping 145 matching lines...) Expand 10 before | Expand all | Expand 10 after
156 pat_ignore = (re.compile('^(Start|Exit)ing'), 156 pat_ignore = (re.compile('^(Start|Exit)ing'),
157 re.compile('^Program terminated'), 157 re.compile('^Program terminated'),
158 re.compile('^Terminating thread'), 158 re.compile('^Terminating thread'),
159 re.compile('^Message: CryptVerifySignature')) 159 re.compile('^Message: CryptVerifySignature'))
160 # message types that aren't analyzed 160 # message types that aren't analyzed
161 # handled, ignored and continued exceptions will likely never be interesting 161 # handled, ignored and continued exceptions will likely never be interesting
162 # TODO(erikkay): MPK ("potential" memory leaks) may be worth turning on 162 # TODO(erikkay): MPK ("potential" memory leaks) may be worth turning on
163 types_excluded = ("EXH", "EXI", "EXC", "MPK") 163 types_excluded = ("EXH", "EXI", "EXC", "MPK")
164 164
165 165
166 def __init__(self, files, echo, name=None, source_dir=None, data_dir=None): 166 def __init__(self, files, echo, name=None, source_dir=None, data_dir=None,
167 report_dir=None):
167 # The input file we're analyzing. 168 # The input file we're analyzing.
168 self._files = files 169 self._files = files
170
169 # Whether the input file contents should be echoed to stdout. 171 # Whether the input file contents should be echoed to stdout.
170 self._echo = echo 172 self._echo = echo
173
171 # A symbolic name for the run being analyzed, often the name of the 174 # A symbolic name for the run being analyzed, often the name of the
172 # exe which was purified. 175 # exe which was purified.
173 self._name = name 176 self._name = name
177
174 # The top of the source code tree of the code we're analyzing. 178 # The top of the source code tree of the code we're analyzing.
175 # This prefix is stripped from all filenames in stacks for normalization. 179 # This prefix is stripped from all filenames in stacks for normalization.
176 if source_dir: 180 if source_dir:
177 purify_message.Stack.SetSourceDir(source_dir) 181 purify_message.Stack.SetSourceDir(source_dir)
182
183 script_dir = google.path_utils.ScriptDir()
184
178 if data_dir: 185 if data_dir:
179 self._data_dir = data_dir 186 self._data_dir = data_dir
187 self._global_data_dir = os.path.join(script_dir, "data")
180 else: 188 else:
181 self._data_dir = os.path.join(google.path_utils.ScriptDir(), "data") 189 self._data_dir = os.path.join(script_dir, "data")
190 self._global_data_dir = None
191
192 if report_dir:
193 self._report_dir = report_dir
194 else:
195 self._report_dir = os.path.join(script_dir, "latest")
196
182 # A map of message_type to a MessageList of that type. 197 # A map of message_type to a MessageList of that type.
183 self._message_lists = {} 198 self._message_lists = {}
184 self._ReadIgnoreFile() 199 self._ReadIgnoreFile()
185 200
186 def _ReadIgnoreFile(self): 201 def _ReadIgnoreFile(self):
187 '''Read a file which is a list of regexps for either the title or the 202 '''Read a file which is a list of regexps for either the title or the
188 top-most visible stack line. 203 top-most visible stack line.
189 ''' 204 '''
190 self._pat_ignore = [] 205 self._pat_ignore = []
191 filenames = [os.path.join(self._data_dir, "ignore.txt"), 206 filenames = [os.path.join(self._data_dir, "ignore.txt")]
192 os.path.join(google.path_utils.ScriptDir(), "data", "ignore.txt")] 207 if self._global_data_dir:
208 filenames.append(os.path.join(self._global_data_dir, "ignore.txt"))
193 for filename in filenames: 209 for filename in filenames:
194 if os.path.exists(filename): 210 if os.path.exists(filename):
195 f = open(filename, 'r') 211 f = open(filename, 'r')
196 for line in f.readlines(): 212 for line in f.readlines():
197 if line.startswith("#") or line.startswith("//") or line.isspace(): 213 if line.startswith("#") or line.startswith("//") or line.isspace():
198 continue 214 continue
199 line = line.rstrip() 215 line = line.rstrip()
200 pat = re.compile(line) 216 pat = re.compile(line)
201 if pat: 217 if pat:
202 self._pat_ignore.append(pat) 218 self._pat_ignore.append(pat)
(...skipping 385 matching lines...) Expand 10 before | Expand all | Expand 10 after
588 print "test error counts" 604 print "test error counts"
589 print "========================" 605 print "========================"
590 tests = test_counts.keys() 606 tests = test_counts.keys()
591 tests.sort() 607 tests.sort()
592 for test in tests: 608 for test in tests:
593 print "%s: %d" % (test, test_counts[test]) 609 print "%s: %d" % (test, test_counts[test])
594 # make sure stdout is flushed to avoid weird overlaps with logging 610 # make sure stdout is flushed to avoid weird overlaps with logging
595 print 611 print
596 sys.stdout.flush() 612 sys.stdout.flush()
597 613
598 def SaveLatestStrings(self, string_list, key, fname_extra=""): 614 def SaveStrings(self, string_list, key, fname_extra=""):
599 '''Output a list of strings to a file in the "latest" dir. 615 '''Output a list of strings to a file in the report dir.
600 ''' 616 '''
601 script_dir = google.path_utils.ScriptDir() 617 out = os.path.join(self._report_dir,
602 path = os.path.join(script_dir, "latest") 618 "%s_%s%s.txt" % (self._name, key, fname_extra))
603 out = os.path.join(path, "%s_%s%s.txt" % (self._name, key, fname_extra))
604 logging.info("saving %s" % (out)) 619 logging.info("saving %s" % (out))
605 try: 620 try:
606 f = open(out, "w+") 621 f = open(out, "w+")
607 f.write('\n'.join(string_list)) 622 f.write('\n'.join(string_list))
608 except IOError, (errno, strerror): 623 except IOError, (errno, strerror):
609 logging.error("error writing to file %s (%d, %s)" % out, errno, strerror) 624 logging.error("error writing to file %s (%d, %s)" % out, errno, strerror)
610 if f: 625 if f:
611 f.close() 626 f.close()
612 return True 627 return True
613 628
614 def SaveResults(self, path=None, verbose=False): 629 def SaveResults(self, path=None, verbose=False):
615 ''' Output normalized data to baseline files for future comparison runs. 630 ''' Output normalized data to baseline files for future comparison runs.
616 Messages are saved in sorted order into a separate file for each message 631 Messages are saved in sorted order into a separate file for each message
617 type. See Message.NormalizedStr() for details of what's written. 632 type. See Message.NormalizedStr() for details of what's written.
618 ''' 633 '''
619 if not path: 634 if not path:
620 path = self._data_dir 635 path = self._report_dir
621 for key in self._message_lists: 636 for key in self._message_lists:
622 out = os.path.join(path, "%s_%s.txt" % (self._name, key)) 637 out = os.path.join(path, "%s_%s.txt" % (self._name, key))
623 logging.info("saving %s" % (out)) 638 logging.info("saving %s" % (out))
624 f = open(out, "w+") 639 f = open(out, "w+")
625 list = self._message_lists[key].UniqueMessages() 640 list = self._message_lists[key].UniqueMessages()
626 # TODO(erikkay): should the count of each message be a diff factor? 641 # TODO(erikkay): should the count of each message be a diff factor?
627 # (i.e. the same error shows up, but more frequently) 642 # (i.e. the same error shows up, but more frequently)
628 for message in list: 643 for message in list:
629 f.write(message.NormalizedStr(verbose=verbose)) 644 f.write(message.NormalizedStr(verbose=verbose))
630 f.write("\n") 645 f.write("\n")
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after
691 msg += line 706 msg += line
692 else: 707 else:
693 # note that the hash doesn't include the title, see Message.__hash__ 708 # note that the hash doesn't include the title, see Message.__hash__
694 h = hash(msg) 709 h = hash(msg)
695 msgs[h] = title + msg 710 msgs[h] = title + msg
696 title = None 711 title = None
697 msg = "" 712 msg = ""
698 logging.info("%s: %d msgs" % (filename, len(msgs))) 713 logging.info("%s: %d msgs" % (filename, len(msgs)))
699 return msgs 714 return msgs
700 715
701 def _SaveLatestGroupSummary(self, message_list): 716 def _SaveGroupSummary(self, message_list):
702 '''Save a summary of message groups and their counts to a file in "latest" 717 '''Save a summary of message groups and their counts to a file in report_dir
703 ''' 718 '''
704 string_list = [] 719 string_list = []
705 groups = message_list.UniqueMessageGroups() 720 groups = message_list.UniqueMessageGroups()
706 group_keys = groups.keys() 721 group_keys = groups.keys()
707 722
708 group_keys.sort(cmp=lambda x,y: len(groups[y]) - len(groups[x])) 723 group_keys.sort(cmp=lambda x,y: len(groups[y]) - len(groups[x]))
709 for group in group_keys: 724 for group in group_keys:
710 string_list.append("%s: %d" % (group, len(groups[group]))) 725 string_list.append("%s: %d" % (group, len(groups[group])))
711 726
712 self.SaveLatestStrings(string_list, message_list.GetType(), "_GROUPS") 727 self.SaveStrings(string_list, message_list.GetType(), "_GROUPS")
713 728
714 def CompareResults(self): 729 def CompareResults(self):
715 ''' Compares the results from the current run with the baseline data 730 ''' Compares the results from the current run with the baseline data
716 stored in data/<name>_<key>.txt returning False if it finds new errors 731 stored in data/<name>_<key>.txt returning False if it finds new errors
717 that are not in the baseline. See ReadFile() and SaveResults() for 732 that are not in the baseline. See ReadFile() and SaveResults() for
718 details of what's in the original file and what's in the baseline. 733 details of what's in the original file and what's in the baseline.
719 Errors that show up in the baseline but not the current run are not 734 Errors that show up in the baseline but not the current run are not
720 considered errors (they're considered "fixed"), but they do suggest 735 considered errors (they're considered "fixed"), but they do suggest
721 that the baseline file could be re-generated.''' 736 that the baseline file could be re-generated.'''
722 errors = 0 737 errors = 0
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
780 purify_message.GetMessageType(type), type, 795 purify_message.GetMessageType(type), type,
781 len(type_errors), len(type_fixes))) 796 len(type_errors), len(type_fixes)))
782 797
783 if len(type_errors): 798 if len(type_errors):
784 strs = [current_hashes[x].NormalizedStr(verbose=True) 799 strs = [current_hashes[x].NormalizedStr(verbose=True)
785 for x in type_errors] 800 for x in type_errors]
786 logging.error("%d new '%s(%s)' errors found\n%s" % (len(type_errors), 801 logging.error("%d new '%s(%s)' errors found\n%s" % (len(type_errors),
787 purify_message.GetMessageType(type), type, 802 purify_message.GetMessageType(type), type,
788 '\n'.join(strs))) 803 '\n'.join(strs)))
789 strs = [current_hashes[x].NormalizedStr() for x in type_errors] 804 strs = [current_hashes[x].NormalizedStr() for x in type_errors]
790 self.SaveLatestStrings(strs, type, "_NEW") 805 self.SaveStrings(strs, type, "_NEW")
791 errors += len(type_errors) 806 errors += len(type_errors)
792 807
793 if len(type_fixes): 808 if len(type_fixes):
794 # we don't have access to the original message, so all we can do is log 809 # we don't have access to the original message, so all we can do is log
795 # the non-verbose normalized text 810 # the non-verbose normalized text
796 logging.warning("%d new '%s(%s)' unexpected fixes found\n%s" % ( 811 logging.warning("%d new '%s(%s)' unexpected fixes found\n%s" % (
797 len(type_fixes), purify_message.GetMessageType(type), 812 len(type_fixes), purify_message.GetMessageType(type),
798 type, '\n'.join(type_fixes))) 813 type, '\n'.join(type_fixes)))
799 self.SaveLatestStrings(type_fixes, type, "_FIXED") 814 self.SaveStrings(type_fixes, type, "_FIXED")
800 fixes += len(type_fixes) 815 fixes += len(type_fixes)
801 if len(current_messages) == 0: 816 if len(current_messages) == 0:
802 logging.warning("all errors fixed in %s" % baseline_file) 817 logging.warning("all errors fixed in %s" % baseline_file)
803 818
804 if len(type_fixes) or len(type_errors): 819 if len(type_fixes) or len(type_errors):
805 strs = [baseline_hashes[x] for x in new_baseline] 820 strs = [baseline_hashes[x] for x in new_baseline]
806 self.SaveLatestStrings(strs, type, "_BASELINE") 821 self.SaveStrings(strs, type, "_BASELINE")
807 822
808 if current_list: 823 if current_list:
809 self._SaveLatestGroupSummary(current_list) 824 self._SaveGroupSummary(current_list)
810 825
811 if errors: 826 if errors:
812 logging.error("%d total new errors found" % errors) 827 logging.error("%d total new errors found" % errors)
813 return -1 828 return -1
814 else: 829 else:
815 logging.info("no new errors found - yay!") 830 logging.info("no new errors found - yay!")
816 if fixes: 831 if fixes:
817 logging.warning("%d total errors unexpectedly fixed" % fixes) 832 logging.warning("%d total errors unexpectedly fixed" % fixes)
818 # magic return code to turn the builder orange (via ReturnCodeCommand) 833 # magic return code to turn the builder orange (via ReturnCodeCommand)
819 return -88 834 return -88
(...skipping 25 matching lines...) Expand all
845 parser.add_option("-n", "--name", 860 parser.add_option("-n", "--name",
846 help="name of the test being run " 861 help="name of the test being run "
847 "(used for output filenames)") 862 "(used for output filenames)")
848 parser.add_option("", "--data_dir", 863 parser.add_option("", "--data_dir",
849 help="path to where purify data files live") 864 help="path to where purify data files live")
850 parser.add_option("", "--bug_report", default=False, 865 parser.add_option("", "--bug_report", default=False,
851 action="store_true", 866 action="store_true",
852 help="print output as an attempted summary of bugs") 867 help="print output as an attempted summary of bugs")
853 parser.add_option("-v", "--verbose", action="store_true", default=False, 868 parser.add_option("-v", "--verbose", action="store_true", default=False,
854 help="verbose output - enable debug log messages") 869 help="verbose output - enable debug log messages")
870 parser.add_option("", "--report_dir",
871 help="path where report files are saved")
855 872
856 (options, args) = parser.parse_args() 873 (options, args) = parser.parse_args()
857 if not len(args) >= 1: 874 if not len(args) >= 1:
858 parser.error("no filename specified") 875 parser.error("no filename specified")
859 filenames = args 876 filenames = args
860 877
861 if options.verbose: 878 if options.verbose:
862 google.logging_utils.config_root(level=logging.DEBUG) 879 google.logging_utils.config_root(level=logging.DEBUG)
863 else: 880 else:
864 google.logging_utils.config_root(level=logging.INFO) 881 google.logging_utils.config_root(level=logging.INFO)
865 pa = PurifyAnalyze(filenames, options.echo_to_stdout, options.name, 882 pa = PurifyAnalyze(filenames, options.echo_to_stdout, options.name,
866 options.source_dir, options.data_dir) 883 options.source_dir, options.data_dir, options.report_dir)
867 execute_crash = not pa.ReadFile() 884 execute_crash = not pa.ReadFile()
868 if options.bug_report: 885 if options.bug_report:
869 pa.PrintBugReport() 886 pa.PrintBugReport()
870 pa.PrintSummary(False) 887 pa.PrintSummary(False)
871 elif options.memory_in_use: 888 elif options.memory_in_use:
872 pa.PrintMemoryInUse(int(options.byte_filter)) 889 pa.PrintMemoryInUse(int(options.byte_filter))
873 elif execute_crash: 890 elif execute_crash:
874 retcode = -1 891 retcode = -1
875 logging.error("Fatal error during test execution. Analysis skipped.") 892 logging.error("Fatal error during test execution. Analysis skipped.")
876 elif options.validate: 893 elif options.validate:
877 if pa.CompareResults() != 0: 894 if pa.CompareResults() != 0:
878 retcode = -1 895 retcode = -1
879 script_dir = google.path_utils.ScriptDir() 896 pa.SaveResults()
880 latest_dir = os.path.join(script_dir, "latest")
881 pa.SaveResults(latest_dir)
882 pa.PrintSummary() 897 pa.PrintSummary()
883 elif options.baseline: 898 elif options.baseline:
884 if not pa.SaveResults(verbose=True): 899 if not pa.SaveResults(verbose=True):
885 retcode = -1 900 retcode = -1
886 pa.PrintSummary(False) 901 pa.PrintSummary(False)
887 else: 902 else:
888 pa.PrintSummary(False) 903 pa.PrintSummary(False)
889 904
890 sys.exit(retcode) 905 sys.exit(retcode)
891 906
892 if __name__ == "__main__": 907 if __name__ == "__main__":
893 _main() 908 _main()
894 909
895 910
OLDNEW
« no previous file with comments | « tools/purify/chrome_tests.py ('k') | tools/purify/purify_test.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698