| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/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 # 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 logging | 10 import logging |
| (...skipping 230 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 241 # <frame> | 241 # <frame> |
| 242 # <ip>0x83751BC</ip> | 242 # <ip>0x83751BC</ip> |
| 243 # <obj>/data/dkegel/chrome-build/src/out/Release/base_unittests</obj> | 243 # <obj>/data/dkegel/chrome-build/src/out/Release/base_unittests</obj> |
| 244 # <fn>_ZN7testing8internal12TestInfoImpl7RunTestEPNS_8TestInfoE</fn> | 244 # <fn>_ZN7testing8internal12TestInfoImpl7RunTestEPNS_8TestInfoE</fn> |
| 245 # <dir>/data/dkegel/chrome-build/src/testing/gtest/src</dir> | 245 # <dir>/data/dkegel/chrome-build/src/testing/gtest/src</dir> |
| 246 # <file>gtest-internal-inl.h</file> | 246 # <file>gtest-internal-inl.h</file> |
| 247 # <line>655</line> | 247 # <line>655</line> |
| 248 # </frame> | 248 # </frame> |
| 249 # although the dir, file, and line elements are missing if there is | 249 # although the dir, file, and line elements are missing if there is |
| 250 # no debug info. | 250 # no debug info. |
| 251 # | |
| 252 # With our patch for https://bugs.kde.org/show_bug.cgi?id=205000 in, | |
| 253 # the file also includes records of the form | |
| 254 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> | |
| 255 # giving the filename and load address of each binary that was mapped | |
| 256 # into the process. | |
| 257 | 251 |
| 258 self._kind = getTextOf(error_node, "kind") | 252 self._kind = getTextOf(error_node, "kind") |
| 259 self._backtraces = [] | 253 self._backtraces = [] |
| 260 self._suppression = None | 254 self._suppression = None |
| 261 self._commandline = commandline | 255 self._commandline = commandline |
| 262 | 256 |
| 263 # Iterate through the nodes, parsing <what|auxwhat><stack> pairs. | 257 # Iterate through the nodes, parsing <what|auxwhat><stack> pairs. |
| 264 description = None | 258 description = None |
| 265 for node in error_node.childNodes: | 259 for node in error_node.childNodes: |
| 266 if node.localName == "what" or node.localName == "auxwhat": | 260 if node.localName == "what" or node.localName == "auxwhat": |
| (...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 371 | 365 |
| 372 def __init__(self, source_dir, files, show_all_leaks=False, use_gdb=False): | 366 def __init__(self, source_dir, files, show_all_leaks=False, use_gdb=False): |
| 373 '''Reads in a set of files. | 367 '''Reads in a set of files. |
| 374 | 368 |
| 375 Args: | 369 Args: |
| 376 source_dir: Path to top of source tree for this build | 370 source_dir: Path to top of source tree for this build |
| 377 files: A list of filenames. | 371 files: A list of filenames. |
| 378 show_all_leaks: whether to show even less important leaks | 372 show_all_leaks: whether to show even less important leaks |
| 379 ''' | 373 ''' |
| 380 | 374 |
| 375 # Beyond the detailed errors parsed by ValgrindError above, |
| 376 # the xml file contain records describing suppressions that were used: |
| 377 # <suppcounts> |
| 378 # <pair> |
| 379 # <count>28</count> |
| 380 # <name>pango_font_leak_todo</name> |
| 381 # </pair> |
| 382 # <pair> |
| 383 # <count>378</count> |
| 384 # <name>bug_13243</name> |
| 385 # </pair> |
| 386 # </suppcounts |
| 387 # Collect these and print them at the end. |
| 388 # |
| 389 # With our patch for https://bugs.kde.org/show_bug.cgi?id=205000 in, |
| 390 # the file also includes records of the form |
| 391 # <load_obj><obj>/usr/lib/libgcc_s.1.dylib</obj><ip>0x27000</ip></load_obj> |
| 392 # giving the filename and load address of each binary that was mapped |
| 393 # into the process. |
| 394 |
| 381 global TheAddressTable | 395 global TheAddressTable |
| 382 if use_gdb: | 396 if use_gdb: |
| 383 TheAddressTable = _AddressTable() | 397 TheAddressTable = _AddressTable() |
| 384 self._errors = set() | 398 self._errors = set() |
| 399 self._suppcounts = {} |
| 385 badfiles = set() | 400 badfiles = set() |
| 386 start = time.time() | 401 start = time.time() |
| 387 self._parse_failed = False | 402 self._parse_failed = False |
| 388 for file in files: | 403 for file in files: |
| 389 # Wait up to three minutes for valgrind to finish writing all files, | 404 # Wait up to three minutes for valgrind to finish writing all files, |
| 390 # but after that, just skip incomplete files and warn. | 405 # but after that, just skip incomplete files and warn. |
| 391 f = open(file, "r+") | 406 f = open(file, "r+") |
| 392 found = False | 407 found = False |
| 393 firstrun = True | 408 firstrun = True |
| 394 origsize = os.path.getsize(file) | 409 origsize = os.path.getsize(file) |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 442 break | 457 break |
| 443 | 458 |
| 444 raw_errors = parsed_file.getElementsByTagName("error") | 459 raw_errors = parsed_file.getElementsByTagName("error") |
| 445 for raw_error in raw_errors: | 460 for raw_error in raw_errors: |
| 446 # Ignore "possible" leaks for now by default. | 461 # Ignore "possible" leaks for now by default. |
| 447 if (show_all_leaks or | 462 if (show_all_leaks or |
| 448 getTextOf(raw_error, "kind") != "Leak_PossiblyLost"): | 463 getTextOf(raw_error, "kind") != "Leak_PossiblyLost"): |
| 449 error = ValgrindError(source_dir, raw_error, commandline) | 464 error = ValgrindError(source_dir, raw_error, commandline) |
| 450 self._errors.add(error) | 465 self._errors.add(error) |
| 451 | 466 |
| 467 suppcountlist = parsed_file.getElementsByTagName("suppcounts") |
| 468 if len(suppcountlist) > 0: |
| 469 suppcountlist = suppcountlist[0] |
| 470 for node in suppcountlist.getElementsByTagName("pair"): |
| 471 count = getTextOf(node, "count"); |
| 472 name = getTextOf(node, "name"); |
| 473 if name in self._suppcounts: |
| 474 self._suppcounts[name] += int(count) |
| 475 else: |
| 476 self._suppcounts[name] = int(count) |
| 477 |
| 452 if len(badfiles) > 0: | 478 if len(badfiles) > 0: |
| 453 logging.warn("valgrind didn't finish writing %d files?!" % len(badfiles)) | 479 logging.warn("valgrind didn't finish writing %d files?!" % len(badfiles)) |
| 454 for file in badfiles: | 480 for file in badfiles: |
| 455 logging.warn("Last 20 lines of %s :" % file) | 481 logging.warn("Last 20 lines of %s :" % file) |
| 456 os.system("tail -n 20 '%s' 1>&2" % file) | 482 os.system("tail -n 20 '%s' 1>&2" % file) |
| 457 | 483 |
| 458 def Report(self): | 484 def Report(self): |
| 459 if self._parse_failed: | 485 if self._parse_failed: |
| 460 logging.error("FAIL! Couldn't parse Valgrind output file") | 486 logging.error("FAIL! Couldn't parse Valgrind output file") |
| 461 return -2 | 487 return -2 |
| 462 | 488 |
| 489 print "-----------------------------------------------------" |
| 490 print "Suppressions used:" |
| 491 print " count name" |
| 492 for item in sorted(self._suppcounts.items(), key=lambda (k,v): (v,k)): |
| 493 print "%7s %s" % (item[1], item[0]) |
| 494 print "-----------------------------------------------------" |
| 495 |
| 463 if self._errors: | 496 if self._errors: |
| 464 logging.error("FAIL! There were %s errors: " % len(self._errors)) | 497 logging.error("FAIL! There were %s errors: " % len(self._errors)) |
| 465 | 498 |
| 466 global TheAddressTable | 499 global TheAddressTable |
| 467 if TheAddressTable != None: | 500 if TheAddressTable != None: |
| 468 TheAddressTable.ResolveAll() | 501 TheAddressTable.ResolveAll() |
| 469 | 502 |
| 470 for error in self._errors: | 503 for error in self._errors: |
| 471 logging.error(error) | 504 logging.error(error) |
| 472 | 505 |
| (...skipping 15 matching lines...) Expand all Loading... |
| 488 parser.error("no filename specified") | 521 parser.error("no filename specified") |
| 489 filenames = args | 522 filenames = args |
| 490 | 523 |
| 491 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) | 524 analyzer = MemcheckAnalyze(options.source_dir, filenames, use_gdb=True) |
| 492 retcode = analyzer.Report() | 525 retcode = analyzer.Report() |
| 493 | 526 |
| 494 sys.exit(retcode) | 527 sys.exit(retcode) |
| 495 | 528 |
| 496 if __name__ == "__main__": | 529 if __name__ == "__main__": |
| 497 _main() | 530 _main() |
| OLD | NEW |