Commit f63c9a84 authored by Alexei Filippov's avatar Alexei Filippov Committed by Commit Bot

[sampling heap profiler] Introduce ModuleCache

The class provides support for module lookups for
memory addresses.

BUG=803276
TBR=dcheng@chromium.org

Change-Id: Iaae0d70c6e7091ced6276e321321b74a6496255a
Reviewed-on: https://chromium-review.googlesource.com/1132545
Commit-Queue: Alexei Filippov <alph@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#577352}
parent cb6a7105
......@@ -721,6 +721,8 @@ jumbo_component("base") {
"run_loop.h",
"sampling_heap_profiler/lock_free_address_hash_set.cc",
"sampling_heap_profiler/lock_free_address_hash_set.h",
"sampling_heap_profiler/module_cache.cc",
"sampling_heap_profiler/module_cache.h",
"sampling_heap_profiler/sampling_heap_profiler.cc",
"sampling_heap_profiler/sampling_heap_profiler.h",
"scoped_clear_errno.h",
......@@ -2345,6 +2347,7 @@ test("base_unittests") {
"run_loop_unittest.cc",
"safe_numerics_unittest.cc",
"sampling_heap_profiler/lock_free_address_hash_set_unittest.cc",
"sampling_heap_profiler/module_cache_unittest.cc",
"scoped_clear_errno_unittest.cc",
"scoped_generic_unittest.cc",
"scoped_native_library_unittest.cc",
......
......@@ -56,6 +56,13 @@ class NativeStackSampler {
// any NativeStackSampler object.
static std::unique_ptr<StackBuffer> CreateStackBuffer();
// Creates a Module object for the specified memory address. If the address
// does not belong to a module returns an invalid module.
// TODO(alph): Move platform-specific module lookup code into
// base::ModuleCache and remove this function.
static StackSamplingProfiler::InternalModule GetModuleForAddress(
uintptr_t address);
// The following functions are all called on the SamplingThread (not the
// thread being sampled).
......
......@@ -511,20 +511,10 @@ InternalModule NativeStackSamplerMac::GetInternalModule(
if (loc != module_cache_entry_.end())
return loc->internal_module;
Dl_info inf;
if (!dladdr(reinterpret_cast<const void*>(instruction_pointer), &inf))
return InternalModule();
auto base_module_address = reinterpret_cast<uintptr_t>(inf.dli_fbase);
InternalModule internal_module(
base_module_address, GetUniqueId(inf.dli_fbase), FilePath(inf.dli_fname));
module_cache_entry_.emplace_back(
base_module_address,
base_module_address + GetModuleTextSize(inf.dli_fbase), internal_module);
return internal_module;
InternalModule module = GetModuleForAddress(instruction_pointer);
module_cache_entry_.emplace_back(module.base_address,
module.base_address + module.size, module);
return module;
}
template <typename StackFrameCallback, typename ContinueUnwindPredicate>
......@@ -620,12 +610,28 @@ void NativeStackSamplerMac::WalkStack(
} // namespace
// NativeStackSampler ---------------------------------------------------------
// static
std::unique_ptr<NativeStackSampler> NativeStackSampler::Create(
PlatformThreadId thread_id,
NativeStackSamplerTestDelegate* test_delegate) {
return std::make_unique<NativeStackSamplerMac>(thread_id, test_delegate);
}
// static
StackSamplingProfiler::InternalModule NativeStackSampler::GetModuleForAddress(
uintptr_t address) {
Dl_info inf;
if (!dladdr(reinterpret_cast<const void*>(address), &inf))
return StackSamplingProfiler::InternalModule();
auto base_module_address = reinterpret_cast<uintptr_t>(inf.dli_fbase);
return StackSamplingProfiler::InternalModule(
base_module_address, GetUniqueId(inf.dli_fbase), FilePath(inf.dli_fname),
GetModuleTextSize(inf.dli_fbase));
}
// static
size_t NativeStackSampler::GetStackBufferSize() {
// In platform_thread_mac's GetDefaultThreadStackSize(), RLIMIT_STACK is used
// for all stacks, not just the main thread's, so it is good for use here.
......
......@@ -12,6 +12,13 @@ std::unique_ptr<NativeStackSampler> NativeStackSampler::Create(
return std::unique_ptr<NativeStackSampler>();
}
// static
StackSamplingProfiler::InternalModule NativeStackSampler::GetModuleForAddress(
uintptr_t address) {
// TODO(alph): Implement it.
return StackSamplingProfiler::InternalModule();
}
size_t NativeStackSampler::GetStackBufferSize() {
return 0;
}
......
......@@ -495,6 +495,9 @@ std::vector<InternalFrame> NativeStackSamplerWin::CreateInternalFrames(
} // namespace
// NativeStackSampler ---------------------------------------------------------
// static
std::unique_ptr<NativeStackSampler> NativeStackSampler::Create(
PlatformThreadId thread_id,
NativeStackSamplerTestDelegate* test_delegate) {
......@@ -512,6 +515,14 @@ std::unique_ptr<NativeStackSampler> NativeStackSampler::Create(
return std::unique_ptr<NativeStackSampler>();
}
// static
StackSamplingProfiler::InternalModule NativeStackSampler::GetModuleForAddress(
uintptr_t address) {
// TODO(alph): Implement it.
return StackSamplingProfiler::InternalModule();
}
// static
size_t NativeStackSampler::GetStackBufferSize() {
// The default Win32 reserved stack size is 1 MB and Chrome Windows threads
// currently always use the default, but this allows for expansion if it
......
......@@ -59,7 +59,17 @@ StackSamplingProfiler::InternalModule::InternalModule() : is_valid(false) {}
StackSamplingProfiler::InternalModule::InternalModule(uintptr_t base_address,
const std::string& id,
const FilePath& filename)
: base_address(base_address), id(id), filename(filename), is_valid(true) {}
: InternalModule(base_address, id, filename, 0) {}
StackSamplingProfiler::InternalModule::InternalModule(uintptr_t base_address,
const std::string& id,
const FilePath& filename,
size_t size)
: base_address(base_address),
id(id),
filename(filename),
is_valid(true),
size(size) {}
StackSamplingProfiler::InternalModule::~InternalModule() = default;
......
......@@ -90,6 +90,10 @@ class BASE_EXPORT StackSamplingProfiler {
InternalModule(uintptr_t base_address,
const std::string& id,
const FilePath& filename);
InternalModule(uintptr_t base_address,
const std::string& id,
const FilePath& filename,
size_t size);
~InternalModule();
// Points to the base address of the module.
......@@ -109,6 +113,9 @@ class BASE_EXPORT StackSamplingProfiler {
// The validness of the module.
bool is_valid;
// Size of the module.
size_t size;
};
// Frame represents an individual sampled stack frame with module information.
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/sampling_heap_profiler/module_cache.h"
#include "base/no_destructor.h"
#include "base/profiler/native_stack_sampler.h"
namespace base {
ModuleCache::ModuleCache() = default;
ModuleCache::~ModuleCache() = default;
const ModuleCache::Module& ModuleCache::GetModuleForAddress(uintptr_t address) {
static NoDestructor<Module> invalid_module;
auto it = modules_cache_map_.upper_bound(address);
if (it != modules_cache_map_.begin()) {
DCHECK(!modules_cache_map_.empty());
--it;
Module& module = it->second;
if (address < module.base_address + module.size)
return module;
}
auto module = NativeStackSampler::GetModuleForAddress(address);
if (!module.is_valid)
return *invalid_module;
return modules_cache_map_.emplace(module.base_address, std::move(module))
.first->second;
}
} // namespace base
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_SAMPLING_HEAP_PROFILER_MODULE_CACHE_H_
#define BASE_SAMPLING_HEAP_PROFILER_MODULE_CACHE_H_
#include <map>
#include "base/profiler/stack_sampling_profiler.h"
namespace base {
class BASE_EXPORT ModuleCache {
public:
using Module = StackSamplingProfiler::InternalModule;
ModuleCache();
~ModuleCache();
const Module& GetModuleForAddress(uintptr_t address);
private:
std::map<uintptr_t, Module> modules_cache_map_;
};
} // namespace base
#endif // BASE_SAMPLING_HEAP_PROFILER_MODULE_CACHE_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/sampling_heap_profiler/module_cache.h"
#include "build/build_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace {
class ModuleCacheTest : public ::testing::Test {};
int AFunctionForTest() {
return 42;
}
// Checks that ModuleCache returns the same module instance for
// addresses within the module.
#if defined(OS_MACOSX) && !defined(OS_IOS)
#define MAYBE_ModuleCache ModuleCache
#else
#define MAYBE_ModuleCache DISABLED_ModuleCache
#endif
TEST_F(ModuleCacheTest, MAYBE_ModuleCache) {
uintptr_t ptr1 = reinterpret_cast<uintptr_t>(&AFunctionForTest);
uintptr_t ptr2 = ptr1 + 1;
ModuleCache cache;
const ModuleCache::Module& module1 = cache.GetModuleForAddress(ptr1);
const ModuleCache::Module& module2 = cache.GetModuleForAddress(ptr2);
EXPECT_EQ(&module1, &module2);
EXPECT_TRUE(module1.is_valid);
EXPECT_LT(module1.base_address, ptr1);
EXPECT_GT(module1.base_address + module1.size, ptr2);
}
TEST_F(ModuleCacheTest, InvalidModule) {
ModuleCache cache;
const ModuleCache::Module& invalid_module = cache.GetModuleForAddress(1);
EXPECT_FALSE(invalid_module.is_valid);
}
} // namespace
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment