mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2025-02-21 17:33:19 +03:00
193 lines
7.6 KiB
C++
193 lines
7.6 KiB
C++
// Copyright 2018 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "base/profiler/module_cache.h"
|
|
|
|
#include <iterator>
|
|
#include <utility>
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/ranges/algorithm.h"
|
|
#include "base/strings/strcat.h"
|
|
|
|
namespace base {
|
|
|
|
namespace {
|
|
|
|
// Supports heterogeneous comparisons on modules and addresses, for use in
|
|
// binary searching modules sorted by range for a contained address.
|
|
struct ModuleAddressCompare {
|
|
bool operator()(const std::unique_ptr<const ModuleCache::Module>& module,
|
|
uintptr_t address) const {
|
|
return module->GetBaseAddress() + module->GetSize() <= address;
|
|
}
|
|
|
|
bool operator()(
|
|
uintptr_t address,
|
|
const std::unique_ptr<const ModuleCache::Module>& module) const {
|
|
return address < module->GetBaseAddress();
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
std::string TransformModuleIDToSymbolServerFormat(StringPiece module_id) {
|
|
std::string mangled_id(module_id);
|
|
// Android and Linux Chrome builds use the "breakpad" format to index their
|
|
// build id, so we transform the build id for these platforms. All other
|
|
// platforms keep their symbols indexed by the original build ID.
|
|
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX)
|
|
// Linux ELF module IDs are 160bit integers, which we need to mangle
|
|
// down to 128bit integers to match the id that Breakpad outputs.
|
|
// Example on version '66.0.3359.170' x64:
|
|
// Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
|
|
// Debug-ID "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
|
|
|
|
if (mangled_id.size() < 32) {
|
|
mangled_id.resize(32, '0');
|
|
}
|
|
|
|
mangled_id = base::StrCat({mangled_id.substr(6, 2), mangled_id.substr(4, 2),
|
|
mangled_id.substr(2, 2), mangled_id.substr(0, 2),
|
|
mangled_id.substr(10, 2), mangled_id.substr(8, 2),
|
|
mangled_id.substr(14, 2), mangled_id.substr(12, 2),
|
|
mangled_id.substr(16, 16), "0"});
|
|
#endif
|
|
return mangled_id;
|
|
}
|
|
|
|
ModuleCache::ModuleCache() = default;
|
|
|
|
ModuleCache::~ModuleCache() {
|
|
DCHECK_EQ(auxiliary_module_provider_, nullptr);
|
|
}
|
|
|
|
const ModuleCache::Module* ModuleCache::GetModuleForAddress(uintptr_t address) {
|
|
if (const ModuleCache::Module* module = GetExistingModuleForAddress(address))
|
|
return module;
|
|
|
|
std::unique_ptr<const Module> new_module = CreateModuleForAddress(address);
|
|
if (!new_module && auxiliary_module_provider_)
|
|
new_module = auxiliary_module_provider_->TryCreateModuleForAddress(address);
|
|
if (!new_module)
|
|
return nullptr;
|
|
|
|
const auto result = native_modules_.insert(std::move(new_module));
|
|
// TODO(https://crbug.com/1131769): Reintroduce DCHECK(result.second) after
|
|
// fixing the issue that is causing it to fail.
|
|
return result.first->get();
|
|
}
|
|
|
|
std::vector<const ModuleCache::Module*> ModuleCache::GetModules() const {
|
|
std::vector<const Module*> result;
|
|
result.reserve(native_modules_.size());
|
|
for (const std::unique_ptr<const Module>& module : native_modules_)
|
|
result.push_back(module.get());
|
|
for (const std::unique_ptr<const Module>& module : non_native_modules_)
|
|
result.push_back(module.get());
|
|
return result;
|
|
}
|
|
|
|
void ModuleCache::UpdateNonNativeModules(
|
|
const std::vector<const Module*>& defunct_modules,
|
|
std::vector<std::unique_ptr<const Module>> new_modules) {
|
|
// Insert the modules to remove into a set to support O(log(n)) lookup below.
|
|
flat_set<const Module*> defunct_modules_set(defunct_modules.begin(),
|
|
defunct_modules.end());
|
|
|
|
// Reorder the modules to be removed to the last slots in the set, then move
|
|
// them to the inactive modules, then erase the moved-from modules from the
|
|
// set. This is a variation on the standard erase-remove idiom, which is
|
|
// explicitly endorsed for implementing erase behavior on flat_sets.
|
|
//
|
|
// stable_partition is O(m*log(r)) where m is the number of current modules
|
|
// and r is the number of modules to remove. insert and erase are both O(r).
|
|
auto first_module_defunct_modules = ranges::stable_partition(
|
|
non_native_modules_,
|
|
[&defunct_modules_set](const std::unique_ptr<const Module>& module) {
|
|
return defunct_modules_set.find(module.get()) ==
|
|
defunct_modules_set.end();
|
|
});
|
|
// All modules requested to be removed should have been found.
|
|
DCHECK_EQ(
|
|
static_cast<ptrdiff_t>(defunct_modules.size()),
|
|
std::distance(first_module_defunct_modules, non_native_modules_.end()));
|
|
inactive_non_native_modules_.insert(
|
|
inactive_non_native_modules_.end(),
|
|
std::make_move_iterator(first_module_defunct_modules),
|
|
std::make_move_iterator(non_native_modules_.end()));
|
|
non_native_modules_.erase(first_module_defunct_modules,
|
|
non_native_modules_.end());
|
|
|
|
// Insert the modules to be added. This operation is O((m + a) + a*log(a))
|
|
// where m is the number of current modules and a is the number of modules to
|
|
// be added.
|
|
const size_t prior_non_native_modules_size = non_native_modules_.size();
|
|
non_native_modules_.insert(std::make_move_iterator(new_modules.begin()),
|
|
std::make_move_iterator(new_modules.end()));
|
|
// Every module in |new_modules| should have been moved into
|
|
// |non_native_modules_|. This guards against use-after-frees if |new_modules|
|
|
// were to contain any modules equivalent to what's already in
|
|
// |non_native_modules_|, in which case the module would remain in
|
|
// |new_modules| and be deleted on return from the function. While this
|
|
// scenario would be a violation of the API contract, it would present a
|
|
// difficult-to-track-down crash scenario.
|
|
CHECK_EQ(prior_non_native_modules_size + new_modules.size(),
|
|
non_native_modules_.size());
|
|
}
|
|
|
|
void ModuleCache::AddCustomNativeModule(std::unique_ptr<const Module> module) {
|
|
const bool was_inserted = native_modules_.insert(std::move(module)).second;
|
|
// |module| should have been inserted into |native_modules_|, indicating that
|
|
// there was no equivalent module already present. While this scenario would
|
|
// be a violation of the API contract, it would present a
|
|
// difficult-to-track-down crash scenario.
|
|
CHECK(was_inserted);
|
|
}
|
|
|
|
const ModuleCache::Module* ModuleCache::GetExistingModuleForAddress(
|
|
uintptr_t address) const {
|
|
const auto non_native_module_loc = non_native_modules_.find(address);
|
|
if (non_native_module_loc != non_native_modules_.end())
|
|
return non_native_module_loc->get();
|
|
|
|
const auto native_module_loc = native_modules_.find(address);
|
|
if (native_module_loc != native_modules_.end())
|
|
return native_module_loc->get();
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
void ModuleCache::RegisterAuxiliaryModuleProvider(
|
|
AuxiliaryModuleProvider* auxiliary_module_provider) {
|
|
DCHECK(!auxiliary_module_provider_);
|
|
auxiliary_module_provider_ = auxiliary_module_provider;
|
|
}
|
|
|
|
void ModuleCache::UnregisterAuxiliaryModuleProvider(
|
|
AuxiliaryModuleProvider* auxiliary_module_provider) {
|
|
DCHECK_EQ(auxiliary_module_provider_, auxiliary_module_provider);
|
|
auxiliary_module_provider_ = nullptr;
|
|
}
|
|
|
|
bool ModuleCache::ModuleAndAddressCompare::operator()(
|
|
const std::unique_ptr<const Module>& m1,
|
|
const std::unique_ptr<const Module>& m2) const {
|
|
return m1->GetBaseAddress() < m2->GetBaseAddress();
|
|
}
|
|
|
|
bool ModuleCache::ModuleAndAddressCompare::operator()(
|
|
const std::unique_ptr<const Module>& m1,
|
|
uintptr_t address) const {
|
|
return m1->GetBaseAddress() + m1->GetSize() <= address;
|
|
}
|
|
|
|
bool ModuleCache::ModuleAndAddressCompare::operator()(
|
|
uintptr_t address,
|
|
const std::unique_ptr<const Module>& m2) const {
|
|
return address < m2->GetBaseAddress();
|
|
}
|
|
|
|
} // namespace base
|