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 |