Add a tracing API and instrument key functions in order to profile aapt2 bottleneck. The API allows to generate systrace fragment files. Impact on performance is neglibible with each Trace requiring less than 1us and the final Flush operation at the end of a command requiring around 40us. Bug: None Test: None Change-Id: I51b564d3694e9384679f43b878b32295527dddf6
430 lines
13 KiB
C++
430 lines
13 KiB
C++
/*
|
|
* Copyright (C) 2015 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "process/SymbolTable.h"
|
|
|
|
#include <iostream>
|
|
|
|
#include "android-base/logging.h"
|
|
#include "android-base/stringprintf.h"
|
|
#include "androidfw/Asset.h"
|
|
#include "androidfw/AssetManager2.h"
|
|
#include "androidfw/ConfigDescription.h"
|
|
#include "androidfw/ResourceTypes.h"
|
|
#include "androidfw/ResourceUtils.h"
|
|
|
|
#include "NameMangler.h"
|
|
#include "Resource.h"
|
|
#include "ResourceUtils.h"
|
|
#include "ValueVisitor.h"
|
|
#include "trace/TraceBuffer.h"
|
|
#include "util/Util.h"
|
|
|
|
using ::android::ApkAssets;
|
|
using ::android::ConfigDescription;
|
|
using ::android::StringPiece;
|
|
using ::android::StringPiece16;
|
|
|
|
namespace aapt {
|
|
|
|
SymbolTable::SymbolTable(NameMangler* mangler)
|
|
: mangler_(mangler),
|
|
delegate_(util::make_unique<DefaultSymbolTableDelegate>()),
|
|
cache_(200),
|
|
id_cache_(200) {
|
|
}
|
|
|
|
void SymbolTable::SetDelegate(std::unique_ptr<ISymbolTableDelegate> delegate) {
|
|
CHECK(delegate != nullptr) << "can't set a nullptr delegate";
|
|
delegate_ = std::move(delegate);
|
|
|
|
// Clear the cache in case this delegate changes the order of lookup.
|
|
cache_.clear();
|
|
}
|
|
|
|
void SymbolTable::AppendSource(std::unique_ptr<ISymbolSource> source) {
|
|
sources_.push_back(std::move(source));
|
|
|
|
// We do not clear the cache, because sources earlier in the list take
|
|
// precedent.
|
|
}
|
|
|
|
void SymbolTable::PrependSource(std::unique_ptr<ISymbolSource> source) {
|
|
sources_.insert(sources_.begin(), std::move(source));
|
|
|
|
// We must clear the cache in case we did a lookup before adding this
|
|
// resource.
|
|
cache_.clear();
|
|
}
|
|
|
|
const SymbolTable::Symbol* SymbolTable::FindByName(const ResourceName& name) {
|
|
const ResourceName* name_with_package = &name;
|
|
|
|
// Fill in the package name if necessary.
|
|
// If there is no package in `name`, we will need to copy the ResourceName
|
|
// and store it somewhere; we use the Maybe<> class to reserve storage.
|
|
Maybe<ResourceName> name_with_package_impl;
|
|
if (name.package.empty()) {
|
|
name_with_package_impl = ResourceName(mangler_->GetTargetPackageName(), name.type, name.entry);
|
|
name_with_package = &name_with_package_impl.value();
|
|
}
|
|
|
|
// We store the name unmangled in the cache, so look it up as-is.
|
|
if (const std::shared_ptr<Symbol>& s = cache_.get(*name_with_package)) {
|
|
return s.get();
|
|
}
|
|
|
|
// The name was not found in the cache. Mangle it (if necessary) and find it in our sources.
|
|
// Again, here we use a Maybe<> object to reserve storage if we need to mangle.
|
|
const ResourceName* mangled_name = name_with_package;
|
|
Maybe<ResourceName> mangled_name_impl;
|
|
if (mangler_->ShouldMangle(name_with_package->package)) {
|
|
mangled_name_impl = mangler_->MangleName(*name_with_package);
|
|
mangled_name = &mangled_name_impl.value();
|
|
}
|
|
|
|
std::unique_ptr<Symbol> symbol = delegate_->FindByName(*mangled_name, sources_);
|
|
if (symbol == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Take ownership of the symbol into a shared_ptr. We do this because
|
|
// LruCache doesn't support unique_ptr.
|
|
std::shared_ptr<Symbol> shared_symbol(std::move(symbol));
|
|
|
|
// Since we look in the cache with the unmangled, but package prefixed
|
|
// name, we must put the same name into the cache.
|
|
cache_.put(*name_with_package, shared_symbol);
|
|
|
|
if (shared_symbol->id) {
|
|
// The symbol has an ID, so we can also cache this!
|
|
id_cache_.put(shared_symbol->id.value(), shared_symbol);
|
|
}
|
|
|
|
// Returns the raw pointer. Callers are not expected to hold on to this
|
|
// between calls to Find*.
|
|
return shared_symbol.get();
|
|
}
|
|
|
|
const SymbolTable::Symbol* SymbolTable::FindById(const ResourceId& id) {
|
|
if (const std::shared_ptr<Symbol>& s = id_cache_.get(id)) {
|
|
return s.get();
|
|
}
|
|
|
|
// We did not find it in the cache, so look through the sources.
|
|
std::unique_ptr<Symbol> symbol = delegate_->FindById(id, sources_);
|
|
if (symbol == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Take ownership of the symbol into a shared_ptr. We do this because LruCache
|
|
// doesn't support unique_ptr.
|
|
std::shared_ptr<Symbol> shared_symbol(std::move(symbol));
|
|
id_cache_.put(id, shared_symbol);
|
|
|
|
// Returns the raw pointer. Callers are not expected to hold on to this
|
|
// between calls to Find*.
|
|
return shared_symbol.get();
|
|
}
|
|
|
|
const SymbolTable::Symbol* SymbolTable::FindByReference(const Reference& ref) {
|
|
// First try the ID. This is because when we lookup by ID, we only fill in the ID cache.
|
|
// Looking up by name fills in the name and ID cache. So a cache miss will cause a failed
|
|
// ID lookup, then a successful name lookup. Subsequent look ups will hit immediately
|
|
// because the ID is cached too.
|
|
//
|
|
// If we looked up by name first, a cache miss would mean we failed to lookup by name, then
|
|
// succeeded to lookup by ID. Subsequent lookups will miss then hit.
|
|
const SymbolTable::Symbol* symbol = nullptr;
|
|
if (ref.id) {
|
|
symbol = FindById(ref.id.value());
|
|
}
|
|
|
|
if (ref.name && !symbol) {
|
|
symbol = FindByName(ref.name.value());
|
|
}
|
|
return symbol;
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindByName(
|
|
const ResourceName& name, const std::vector<std::unique_ptr<ISymbolSource>>& sources) {
|
|
for (auto& source : sources) {
|
|
std::unique_ptr<SymbolTable::Symbol> symbol = source->FindByName(name);
|
|
if (symbol) {
|
|
return symbol;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> DefaultSymbolTableDelegate::FindById(
|
|
ResourceId id, const std::vector<std::unique_ptr<ISymbolSource>>& sources) {
|
|
for (auto& source : sources) {
|
|
std::unique_ptr<SymbolTable::Symbol> symbol = source->FindById(id);
|
|
if (symbol) {
|
|
return symbol;
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> ResourceTableSymbolSource::FindByName(
|
|
const ResourceName& name) {
|
|
Maybe<ResourceTable::SearchResult> result = table_->FindResource(name);
|
|
if (!result) {
|
|
if (name.type == ResourceType::kAttr) {
|
|
// Recurse and try looking up a private attribute.
|
|
return FindByName(ResourceName(name.package, ResourceType::kAttrPrivate, name.entry));
|
|
}
|
|
return {};
|
|
}
|
|
|
|
ResourceTable::SearchResult sr = result.value();
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>();
|
|
symbol->is_public = (sr.entry->visibility.level == Visibility::Level::kPublic);
|
|
|
|
if (sr.package->id && sr.type->id && sr.entry->id) {
|
|
symbol->id = ResourceId(sr.package->id.value(), sr.type->id.value(), sr.entry->id.value());
|
|
symbol->is_dynamic = (sr.package->id.value() == 0);
|
|
}
|
|
|
|
if (name.type == ResourceType::kAttr || name.type == ResourceType::kAttrPrivate) {
|
|
const ConfigDescription kDefaultConfig;
|
|
ResourceConfigValue* config_value = sr.entry->FindValue(kDefaultConfig);
|
|
if (config_value) {
|
|
// This resource has an Attribute.
|
|
if (Attribute* attr = ValueCast<Attribute>(config_value->value.get())) {
|
|
symbol->attribute = std::make_shared<Attribute>(*attr);
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
}
|
|
return symbol;
|
|
}
|
|
|
|
bool AssetManagerSymbolSource::AddAssetPath(const StringPiece& path) {
|
|
TRACE_CALL();
|
|
if (std::unique_ptr<const ApkAssets> apk = ApkAssets::Load(path.data())) {
|
|
apk_assets_.push_back(std::move(apk));
|
|
|
|
std::vector<const ApkAssets*> apk_assets;
|
|
for (const std::unique_ptr<const ApkAssets>& apk_asset : apk_assets_) {
|
|
apk_assets.push_back(apk_asset.get());
|
|
}
|
|
|
|
asset_manager_.SetApkAssets(apk_assets, true /* invalidate_caches */,
|
|
false /* filter_incompatible_configs */);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
std::map<size_t, std::string> AssetManagerSymbolSource::GetAssignedPackageIds() const {
|
|
TRACE_CALL();
|
|
std::map<size_t, std::string> package_map;
|
|
asset_manager_.ForEachPackage([&package_map](const std::string& name, uint8_t id) -> bool {
|
|
package_map.insert(std::make_pair(id, name));
|
|
return true;
|
|
});
|
|
|
|
return package_map;
|
|
}
|
|
|
|
bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
|
|
if (packageId == 0) {
|
|
return true;
|
|
}
|
|
|
|
for (const std::unique_ptr<const ApkAssets>& assets : apk_assets_) {
|
|
for (const std::unique_ptr<const android::LoadedPackage>& loaded_package
|
|
: assets->GetLoadedArsc()->GetPackages()) {
|
|
if (packageId == loaded_package->GetPackageId() && loaded_package->IsDynamic()) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
|
|
android::AssetManager2& am, ResourceId id) {
|
|
if (am.GetApkAssets().empty()) {
|
|
return {};
|
|
}
|
|
|
|
const android::ResolvedBag* bag = am.GetBag(id.id);
|
|
if (bag == nullptr) {
|
|
return nullptr;
|
|
}
|
|
|
|
// We found a resource.
|
|
std::unique_ptr<SymbolTable::Symbol> s = util::make_unique<SymbolTable::Symbol>(id);
|
|
|
|
const size_t count = bag->entry_count;
|
|
for (uint32_t i = 0; i < count; i++) {
|
|
if (bag->entries[i].key == android::ResTable_map::ATTR_TYPE) {
|
|
s->attribute = std::make_shared<Attribute>(bag->entries[i].value.data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (s->attribute) {
|
|
for (size_t i = 0; i < count; i++) {
|
|
const android::ResolvedBag::Entry& map_entry = bag->entries[i];
|
|
if (Res_INTERNALID(map_entry.key)) {
|
|
switch (map_entry.key) {
|
|
case android::ResTable_map::ATTR_MIN:
|
|
s->attribute->min_int = static_cast<int32_t>(map_entry.value.data);
|
|
break;
|
|
case android::ResTable_map::ATTR_MAX:
|
|
s->attribute->max_int = static_cast<int32_t>(map_entry.value.data);
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
android::AssetManager2::ResourceName name;
|
|
if (!am.GetResourceName(map_entry.key, &name)) {
|
|
return nullptr;
|
|
}
|
|
|
|
Maybe<ResourceName> parsed_name = ResourceUtils::ToResourceName(name);
|
|
if (!parsed_name) {
|
|
return nullptr;
|
|
}
|
|
|
|
Attribute::Symbol symbol;
|
|
symbol.symbol.name = parsed_name.value();
|
|
symbol.symbol.id = ResourceId(map_entry.key);
|
|
symbol.value = map_entry.value.data;
|
|
s->attribute->symbols.push_back(std::move(symbol));
|
|
}
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByName(
|
|
const ResourceName& name) {
|
|
const std::string mangled_entry = NameMangler::MangleEntry(name.package, name.entry);
|
|
|
|
bool found = false;
|
|
ResourceId res_id = 0;
|
|
uint32_t type_spec_flags;
|
|
|
|
// There can be mangled resources embedded within other packages. Here we will
|
|
// look into each package and look-up the mangled name until we find the resource.
|
|
asset_manager_.ForEachPackage([&](const std::string& package_name, uint8_t id) -> bool {
|
|
ResourceName real_name(name.package, name.type, name.entry);
|
|
|
|
if (package_name != name.package) {
|
|
real_name.entry = mangled_entry;
|
|
real_name.package = package_name;
|
|
}
|
|
|
|
res_id = asset_manager_.GetResourceId(real_name.to_string());
|
|
if (res_id.is_valid() && asset_manager_.GetResourceFlags(res_id.id, &type_spec_flags)) {
|
|
found = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
});
|
|
|
|
if (!found) {
|
|
return {};
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> s;
|
|
if (name.type == ResourceType::kAttr) {
|
|
s = LookupAttributeInTable(asset_manager_, res_id);
|
|
} else {
|
|
s = util::make_unique<SymbolTable::Symbol>();
|
|
s->id = res_id;
|
|
s->is_dynamic = IsPackageDynamic(ResourceId(res_id).package_id());
|
|
}
|
|
|
|
if (s) {
|
|
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
|
return s;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
static Maybe<ResourceName> GetResourceName(android::AssetManager2& am,
|
|
ResourceId id) {
|
|
android::AssetManager2::ResourceName name;
|
|
if (!am.GetResourceName(id.id, &name)) {
|
|
return {};
|
|
}
|
|
return ResourceUtils::ToResourceName(name);
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindById(
|
|
ResourceId id) {
|
|
if (!id.is_valid()) {
|
|
// Exit early and avoid the error logs from AssetManager.
|
|
return {};
|
|
}
|
|
|
|
if (apk_assets_.empty()) {
|
|
return {};
|
|
}
|
|
|
|
Maybe<ResourceName> maybe_name = GetResourceName(asset_manager_, id);
|
|
if (!maybe_name) {
|
|
return {};
|
|
}
|
|
|
|
|
|
uint32_t type_spec_flags = 0;
|
|
if (!asset_manager_.GetResourceFlags(id.id, &type_spec_flags)) {
|
|
return {};
|
|
}
|
|
|
|
ResourceName& name = maybe_name.value();
|
|
std::unique_ptr<SymbolTable::Symbol> s;
|
|
if (name.type == ResourceType::kAttr) {
|
|
s = LookupAttributeInTable(asset_manager_, id);
|
|
} else {
|
|
s = util::make_unique<SymbolTable::Symbol>();
|
|
s->id = id;
|
|
s->is_dynamic = IsPackageDynamic(ResourceId(id).package_id());
|
|
}
|
|
|
|
if (s) {
|
|
s->is_public = (type_spec_flags & android::ResTable_typeSpec::SPEC_PUBLIC) != 0;
|
|
return s;
|
|
}
|
|
return {};
|
|
}
|
|
|
|
std::unique_ptr<SymbolTable::Symbol> AssetManagerSymbolSource::FindByReference(
|
|
const Reference& ref) {
|
|
// AssetManager always prefers IDs.
|
|
if (ref.id) {
|
|
return FindById(ref.id.value());
|
|
} else if (ref.name) {
|
|
return FindByName(ref.name.value());
|
|
}
|
|
return {};
|
|
}
|
|
|
|
} // namespace aapt
|