Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 """The deep heap profiler script for Chrome.""" | 6 """The deep heap profiler script for Chrome.""" |
| 7 | 7 |
| 8 from datetime import datetime | 8 from datetime import datetime |
| 9 import json | 9 import json |
| 10 import os | 10 import os |
| 11 import re | 11 import re |
| 12 import shutil | |
| 12 import subprocess | 13 import subprocess |
| 13 import sys | 14 import sys |
| 14 import tempfile | 15 import tempfile |
| 15 | 16 |
| 17 FIND_RUNTIME_SYMBOLS_PATH = os.path.join( | |
| 18 os.path.dirname(os.path.abspath(__file__)), | |
| 19 os.pardir, | |
| 20 'find_runtime_symbols') | |
| 21 sys.path.append(FIND_RUNTIME_SYMBOLS_PATH) | |
| 22 | |
| 23 from prepare_symbol_info import prepare_symbol_info | |
| 24 from find_runtime_symbols import find_runtime_symbols_list | |
| 25 | |
| 16 BUCKET_ID = 5 | 26 BUCKET_ID = 5 |
| 17 VIRTUAL = 0 | 27 VIRTUAL = 0 |
| 18 COMMITTED = 1 | 28 COMMITTED = 1 |
| 19 ALLOC_COUNT = 2 | 29 ALLOC_COUNT = 2 |
| 20 FREE_COUNT = 3 | 30 FREE_COUNT = 3 |
| 21 NULL_REGEX = re.compile('') | 31 NULL_REGEX = re.compile('') |
| 22 | 32 |
| 23 # If an executable pprof script is in the directory of deep_memory_profiler, | |
| 24 # use it. Otherwise, use tcmalloc's pprof. | |
| 25 # | |
| 26 # A pprof script in deep_memory_profiler/ is prioritized to allow packaging | |
| 27 # deep_memory_profiler files with a pprof script. The packaged | |
| 28 # deep_memory_profiler is downloaded with pprof by using download.sh. | |
| 29 PPROF_PATH = os.path.join(os.path.dirname(__file__), 'pprof') | |
| 30 if not (os.path.isfile(PPROF_PATH) and os.access(PPROF_PATH, os.X_OK)): | |
| 31 PPROF_PATH = os.path.join(os.path.dirname(__file__), | |
| 32 os.pardir, | |
| 33 os.pardir, | |
| 34 'third_party', | |
| 35 'tcmalloc', | |
| 36 'chromium', | |
| 37 'src', | |
| 38 'pprof') | |
| 39 | |
| 40 # Heap Profile Dump versions | 33 # Heap Profile Dump versions |
| 41 | 34 |
| 42 # DUMP_DEEP_1 is OBSOLETE. | 35 # DUMP_DEEP_1 is OBSOLETE. |
| 43 # DUMP_DEEP_1 DOES NOT distinct mmap regions and malloc chunks. | 36 # DUMP_DEEP_1 DOES NOT distinct mmap regions and malloc chunks. |
| 44 # Their stacktraces DO contain mmap* or tc-* at their tops. | 37 # Their stacktraces DO contain mmap* or tc-* at their tops. |
| 45 # They should be processed by POLICY_DEEP_1. | 38 # They should be processed by POLICY_DEEP_1. |
| 46 DUMP_DEEP_1 = 'DUMP_DEEP_1' | 39 DUMP_DEEP_1 = 'DUMP_DEEP_1' |
| 47 | 40 |
| 48 # DUMP_DEEP_2 is OBSOLETE. | 41 # DUMP_DEEP_2 is OBSOLETE. |
| 49 # DUMP_DEEP_2 DOES distinct mmap regions and malloc chunks. | 42 # DUMP_DEEP_2 DOES distinct mmap regions and malloc chunks. |
| (...skipping 514 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 564 | 557 |
| 565 sorted_sizes_list = sorted( | 558 sorted_sizes_list = sorted( |
| 566 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) | 559 sizes.iteritems(), key=(lambda x: x[1]), reverse=True) |
| 567 total = 0 | 560 total = 0 |
| 568 for size_pair in sorted_sizes_list: | 561 for size_pair in sorted_sizes_list: |
| 569 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) | 562 sys.stdout.write('%10d %s\n' % (size_pair[1], size_pair[0])) |
| 570 total += size_pair[1] | 563 total += size_pair[1] |
| 571 sys.stderr.write('total: %d\n' % (total)) | 564 sys.stderr.write('total: %d\n' % (total)) |
| 572 | 565 |
| 573 | 566 |
| 574 def update_symbols(symbol_path, mapping_lines, chrome_path): | 567 def update_symbols(symbol_path, mapping_lines, maps_path): |
| 575 """Updates address/symbol mapping on memory and in a .symbol cache file. | 568 """Updates address/symbol mapping on memory and in a .symbol cache file. |
| 576 | 569 |
| 577 It reads cached address/symbol mapping from a .symbol file if it exists. | 570 It reads cached address/symbol mapping from a .symbol file if it exists. |
| 578 Then, it resolves unresolved addresses from a Chrome binary with pprof. | 571 Then, it resolves unresolved addresses from a Chrome binary with pprof. |
| 579 Both mappings on memory and in a .symbol cache file are updated. | 572 Both mappings on memory and in a .symbol cache file are updated. |
| 580 | 573 |
| 581 Symbol files are formatted as follows: | 574 Symbol files are formatted as follows: |
| 582 <Address> <Symbol> | 575 <Address> <Symbol> |
| 583 <Address> <Symbol> | 576 <Address> <Symbol> |
| 584 <Address> <Symbol> | 577 <Address> <Symbol> |
| 585 ... | 578 ... |
| 586 | 579 |
| 587 Args: | 580 Args: |
| 588 symbol_path: A string representing a path for a .symbol file. | 581 symbol_path: A string representing a path for a .symbol file. |
| 589 mapping_lines: A list of strings containing /proc/.../maps. | 582 mapping_lines: A list of strings containing /proc/.../maps. |
| 590 chrome_path: A string representing a path for a Chrome binary. | 583 maps_path: A string of the path of /proc/.../maps. |
| 591 """ | 584 """ |
| 592 with open(symbol_path, mode='a+') as symbol_f: | 585 with open(symbol_path, mode='a+') as symbol_f: |
| 593 symbol_lines = symbol_f.readlines() | 586 symbol_lines = symbol_f.readlines() |
| 594 if symbol_lines: | 587 if symbol_lines: |
| 595 for line in symbol_lines: | 588 for line in symbol_lines: |
| 596 items = line.split(None, 1) | 589 items = line.split(None, 1) |
| 597 address_symbol_dict[items[0]] = items[1].rstrip() | 590 address_symbol_dict[items[0]] = items[1].rstrip() |
| 598 | 591 |
| 599 unresolved_addresses = sorted( | 592 unresolved_addresses = sorted( |
| 600 a for a in appeared_addresses if a not in address_symbol_dict) | 593 a for a in appeared_addresses if a not in address_symbol_dict) |
| 601 | 594 |
| 602 if unresolved_addresses: | 595 if unresolved_addresses: |
| 603 with tempfile.NamedTemporaryFile( | 596 prepared_data_dir = tempfile.mkdtemp() |
| 604 suffix='maps', prefix="dmprof", mode='w+') as pprof_in: | 597 prepare_symbol_info(maps_path, prepared_data_dir) |
|
M-A Ruel
2012/07/23 12:01:20
wrap lines 597-606 into try: and line 607 inside f
| |
| 605 with tempfile.NamedTemporaryFile( | |
| 606 suffix='symbols', prefix="dmprof", mode='w+') as pprof_out: | |
| 607 for line in mapping_lines: | |
| 608 pprof_in.write(line) | |
| 609 | 598 |
| 610 for address in unresolved_addresses: | 599 symbols = find_runtime_symbols_list( |
| 611 pprof_in.write(address + '\n') | 600 prepared_data_dir, unresolved_addresses) |
| 612 | 601 |
| 613 pprof_in.seek(0) | 602 for address, symbol in zip(unresolved_addresses, symbols): |
| 603 stripped_symbol = symbol.strip() | |
| 604 address_symbol_dict[address] = stripped_symbol | |
| 605 symbol_f.write('%s %s\n' % (address, stripped_symbol)) | |
| 614 | 606 |
| 615 p = subprocess.Popen( | 607 shutil.rmtree(prepared_data_dir) |
| 616 '%s --symbols %s' % (PPROF_PATH, chrome_path), | |
| 617 shell=True, stdin=pprof_in, stdout=pprof_out) | |
| 618 p.wait() | |
| 619 | |
| 620 pprof_out.seek(0) | |
| 621 symbols = pprof_out.readlines() | |
| 622 symbol_f.seek(0, 2) | |
| 623 for address, symbol in zip(unresolved_addresses, symbols): | |
| 624 stripped_symbol = symbol.strip() | |
| 625 address_symbol_dict[address] = stripped_symbol | |
| 626 symbol_f.write('%s %s\n' % (address, symbol.strip())) | |
| 627 | 608 |
| 628 | 609 |
| 629 def parse_policy(policy_path): | 610 def parse_policy(policy_path): |
| 630 """Parses policy file. | 611 """Parses policy file. |
| 631 | 612 |
| 632 A policy file contains component's names and their | 613 A policy file contains component's names and their |
| 633 stacktrace pattern written in regular expression. | 614 stacktrace pattern written in regular expression. |
| 634 Those patterns are matched against each symbols of | 615 Those patterns are matched against each symbols of |
| 635 each stacktraces in the order written in the policy file | 616 each stacktraces in the order written in the policy file |
| 636 | 617 |
| (...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 763 new_log.parse_log(buckets) | 744 new_log.parse_log(buckets) |
| 764 except EmptyDumpException: | 745 except EmptyDumpException: |
| 765 sys.stderr.write(' WARNING: ignored an empty dump: %s\n' % path) | 746 sys.stderr.write(' WARNING: ignored an empty dump: %s\n' % path) |
| 766 except ParsingException, e: | 747 except ParsingException, e: |
| 767 sys.stderr.write(' Error in parsing heap profile dump: %s\n' % e) | 748 sys.stderr.write(' Error in parsing heap profile dump: %s\n' % e) |
| 768 sys.exit(1) | 749 sys.exit(1) |
| 769 else: | 750 else: |
| 770 logs.append(new_log) | 751 logs.append(new_log) |
| 771 | 752 |
| 772 sys.stderr.write('getting symbols\n') | 753 sys.stderr.write('getting symbols\n') |
| 773 update_symbols(symbol_path, maps_lines, chrome_path) | 754 update_symbols(symbol_path, maps_lines, maps_path) |
| 774 | 755 |
| 775 # TODO(dmikurube): Many modes now. Split them into separete functions. | 756 # TODO(dmikurube): Many modes now. Split them into separete functions. |
| 776 if action == '--stacktrace': | 757 if action == '--stacktrace': |
| 777 logs[0].dump_stacktrace(buckets) | 758 logs[0].dump_stacktrace(buckets) |
| 778 | 759 |
| 779 elif action == '--csv': | 760 elif action == '--csv': |
| 780 sys.stdout.write(','.join(components)) | 761 sys.stdout.write(','.join(components)) |
| 781 sys.stdout.write('\n') | 762 sys.stdout.write('\n') |
| 782 | 763 |
| 783 for log in logs: | 764 for log in logs: |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 822 | 803 |
| 823 elif action == '--pprof': | 804 elif action == '--pprof': |
| 824 if len(sys.argv) > 5: | 805 if len(sys.argv) > 5: |
| 825 logs[0].dump_for_pprof(policy_list, buckets, maps_lines, sys.argv[5]) | 806 logs[0].dump_for_pprof(policy_list, buckets, maps_lines, sys.argv[5]) |
| 826 else: | 807 else: |
| 827 logs[0].dump_for_pprof(policy_list, buckets, maps_lines, None) | 808 logs[0].dump_for_pprof(policy_list, buckets, maps_lines, None) |
| 828 | 809 |
| 829 | 810 |
| 830 if __name__ == '__main__': | 811 if __name__ == '__main__': |
| 831 sys.exit(main()) | 812 sys.exit(main()) |
| OLD | NEW |