1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
|
# Copyright 2022 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Logic needed by multiple archive-related modules."""
import logging
import os
import re
import models
def ExtendSectionRange(section_range_by_name, section_name, delta_size):
"""Adds |delta_size| to |section_name|'s size in |section_range_by_name|."""
prev_address, prev_size = section_range_by_name.get(section_name, (0, 0))
section_range_by_name[section_name] = (prev_address, prev_size + delta_size)
def _NormalizeObjectPath(path, obj_prefixes):
"""Normalizes object paths.
Prefixes are removed: obj/, ../../
Archive names made more pathy: foo/bar.a(baz.o) -> foo/bar.a/baz.o
"""
if path.startswith('../../'):
# Convert ../../third_party/... -> third_party/...
path = path[6:]
elif path.startswith('/'):
# Convert absolute paths to $SYSTEM/basename.o.
path = os.path.join(models.SYSTEM_PREFIX_PATH, os.path.basename(path))
else:
# Convert obj/third_party/... -> third_party/...
for prefix in obj_prefixes:
if path.startswith(prefix):
path = path[len(prefix):]
if path.endswith(')'):
# Convert foo/bar.a(baz.o) -> foo/bar.a/baz.o so that hierarchical
# breakdowns consider the .o part to be a separate node.
start_idx = path.rindex('(')
path = os.path.join(path[:start_idx], path[start_idx + 1:-1])
return path
def _NormalizeSourcePath(path, gen_prefixes, gen_dir_pattern):
"""Returns (is_generated, normalized_path)"""
# Don't change $APK/, or $NATIVE/ paths.
if path.startswith('$'):
return False, path
if gen_dir_pattern:
# Non-chromium gen_dir logic.
m = gen_dir_pattern.match(path)
if m:
return True, path[m.end():]
return False, path
if path.startswith('../../'):
# Convert ../../third_party/... -> third_party/...
return False, path[6:]
if path.startswith('/'):
# Convert absolute paths to $SYSTEM/basename.cpp.
# E.g.: /buildbot/src/android/ndk-release-r23/toolchain/llvm-project/
# libcxx/src/vector.cpp
path = os.path.join(models.SYSTEM_PREFIX_PATH, os.path.basename(path))
# Convert gen/third_party/... -> third_party/...
for prefix in gen_prefixes:
if path.startswith(prefix):
return True, path[len(prefix):]
return True, path
def NormalizePaths(raw_symbols, gen_dir_regex=None, toolchain_subdirs=None):
"""Fills in the |source_path| attribute and normalizes |object_path|."""
logging.info('Normalizing source and object paths')
gen_dir_pattern = re.compile(gen_dir_regex) if gen_dir_regex else None
obj_prefixes = ['obj/']
gen_prefixes = ['gen/']
if toolchain_subdirs != None:
obj_prefixes.extend(f'{t}/obj/' for t in toolchain_subdirs)
gen_prefixes.extend(f'{t}/gen/' for t in toolchain_subdirs)
for symbol in raw_symbols:
if symbol.object_path:
symbol.object_path = _NormalizeObjectPath(symbol.object_path,
obj_prefixes)
if symbol.source_path:
symbol.generated_source, symbol.source_path = _NormalizeSourcePath(
symbol.source_path, gen_prefixes, gen_dir_pattern)
def _ComputeAncestorPath(path_list, symbol_count):
"""Returns the common ancestor of the given paths."""
if not path_list:
return ''
prefix = os.path.commonprefix(path_list)
# Check if all paths were the same.
if prefix == path_list[0]:
return prefix
# Put in buckets to cut down on the number of unique paths.
if symbol_count >= 100:
symbol_count_str = '100+'
elif symbol_count >= 50:
symbol_count_str = '50-99'
elif symbol_count >= 20:
symbol_count_str = '20-49'
elif symbol_count >= 10:
symbol_count_str = '10-19'
else:
symbol_count_str = str(symbol_count)
# Put the path count as a subdirectory so that grouping by path will show
# "{shared}" as a bucket, and the symbol counts as leafs.
if not prefix:
return os.path.join('{shared}', symbol_count_str)
return os.path.join(os.path.dirname(prefix), '{shared}', symbol_count_str)
def CompactLargeAliasesIntoSharedSymbols(raw_symbols, max_count):
"""Converts symbols with large number of aliases into single symbols.
The merged symbol's path fields are changed to common-ancestor paths in
the form: common/dir/{shared}/$SYMBOL_COUNT
Assumes aliases differ only by path (not by name).
"""
num_raw_symbols = len(raw_symbols)
num_shared_symbols = 0
src_cursor = 0
dst_cursor = 0
while src_cursor < num_raw_symbols:
symbol = raw_symbols[src_cursor]
raw_symbols[dst_cursor] = symbol
dst_cursor += 1
aliases = symbol.aliases
if aliases and len(aliases) > max_count:
symbol.source_path = _ComputeAncestorPath(
[s.source_path for s in aliases if s.source_path], len(aliases))
symbol.object_path = _ComputeAncestorPath(
[s.object_path for s in aliases if s.object_path], len(aliases))
symbol.generated_source = all(s.generated_source for s in aliases)
symbol.aliases = None
num_shared_symbols += 1
src_cursor += len(aliases)
else:
src_cursor += 1
raw_symbols[dst_cursor:] = []
num_removed = src_cursor - dst_cursor
logging.debug('Converted %d aliases into %d shared-path symbols', num_removed,
num_shared_symbols)
def RemoveAssetSuffix(path):
"""Undo asset path suffixing. https://crbug.com/357131361"""
# E.g.: "assets/foo.pak+org.foo.bar+"
if path.endswith('+'):
suffix_idx = path.rfind('+', 0, len(path) - 1)
if suffix_idx != -1:
path = path[:suffix_idx]
return path
|