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