diff options
author | primiano <primiano@chromium.org> | 2015-02-17 15:21:19 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-17 23:22:26 +0000 |
commit | f11ccf580631ee65a5143baff55f704b1a41e591 (patch) | |
tree | 789d8ac8385ee023cead811b7ace98a3cdcd147c /base/trace_event | |
parent | 48ed6c69108ac710f778a151bfc35b3bfb77fb09 (diff) | |
download | chromium_src-f11ccf580631ee65a5143baff55f704b1a41e591.zip chromium_src-f11ccf580631ee65a5143baff55f704b1a41e591.tar.gz chromium_src-f11ccf580631ee65a5143baff55f704b1a41e591.tar.bz2 |
[tracing] Introduce memory tracing skeleton.
This is the first step towards the introduction of memory as a
metric in chromium tracing. This change introduces the core memory
dump manager which will coordinate the memory dumps.
Specifically, the following concepts are introduced by this CL:
- memory_dump_manager: the front-end interface exposed to the rest of
the codebase to create memory dump points.
- memory_dump_provider: the contract interface that memory dumpers need
to expose in order to coordinate with the manager.
This CL does NOT expose any user-visible tracing functionality.
BUG=458295
Review URL: https://codereview.chromium.org/933613002
Cr-Commit-Position: refs/heads/master@{#316700}
Diffstat (limited to 'base/trace_event')
-rw-r--r-- | base/trace_event/memory_dump_manager.cc | 135 | ||||
-rw-r--r-- | base/trace_event/memory_dump_manager.h | 87 | ||||
-rw-r--r-- | base/trace_event/memory_dump_manager_unittest.cc | 125 | ||||
-rw-r--r-- | base/trace_event/memory_dump_provider.h | 33 | ||||
-rw-r--r-- | base/trace_event/process_memory_dump.cc | 29 | ||||
-rw-r--r-- | base/trace_event/process_memory_dump.h | 37 |
6 files changed, 446 insertions, 0 deletions
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc new file mode 100644 index 0000000..bf631b3 --- /dev/null +++ b/base/trace_event/memory_dump_manager.cc @@ -0,0 +1,135 @@ +// Copyright 2015 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/trace_event/memory_dump_manager.h" + +#include <algorithm> + +#include "base/compiler_specific.h" +#include "base/trace_event/memory_dump_provider.h" +#include "base/trace_event/process_memory_dump.h" + +// TODO(primiano): in a separate CL rename DeleteTraceLogForTesting into +// something like base::internal::TeardownSingletonForTesting so we don't have +// to add a new friend to singleton each time. +class DeleteTraceLogForTesting { + public: + static void Delete() { + Singleton< + base::trace_event::MemoryDumpManager, + LeakySingletonTraits<base::trace_event::MemoryDumpManager>>::OnExit(0); + } +}; + +namespace base { +namespace trace_event { + +// TODO(primiano): this should be smarter and should do something similar to +// trace event synthetic delays. +const char MemoryDumpManager::kTraceCategory[] = + TRACE_DISABLED_BY_DEFAULT("memory-dumps"); + +// static +MemoryDumpManager* MemoryDumpManager::GetInstance() { + return Singleton<MemoryDumpManager, + LeakySingletonTraits<MemoryDumpManager>>::get(); +} + +// static +void MemoryDumpManager::DeleteForTesting() { + DeleteTraceLogForTesting::Delete(); +} + +MemoryDumpManager::MemoryDumpManager() : memory_tracing_enabled_(0) { +} + +MemoryDumpManager::~MemoryDumpManager() { + base::trace_event::TraceLog::GetInstance()->RemoveEnabledStateObserver(this); +} + +void MemoryDumpManager::Initialize() { + TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list. + trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this); +} + +void MemoryDumpManager::RegisterDumpProvider(MemoryDumpProvider* mdp) { + AutoLock lock(lock_); + if (std::find(dump_providers_registered_.begin(), + dump_providers_registered_.end(), + mdp) != dump_providers_registered_.end()) { + return; + } + dump_providers_registered_.push_back(mdp); +} + +void MemoryDumpManager::UnregisterDumpProvider(MemoryDumpProvider* mdp) { + AutoLock lock(lock_); + + // Remove from the registered providers list. + auto it = std::find(dump_providers_registered_.begin(), + dump_providers_registered_.end(), mdp); + if (it != dump_providers_registered_.end()) + dump_providers_registered_.erase(it); + + // Remove from the enabled providers list. This is to deal with the case that + // UnregisterDumpProvider is called while the trace is enabled. + it = std::find(dump_providers_enabled_.begin(), dump_providers_enabled_.end(), + mdp); + if (it != dump_providers_enabled_.end()) + dump_providers_enabled_.erase(it); +} + +void MemoryDumpManager::RequestDumpPoint(DumpPointType type) { + // TODO(primiano): this will have more logic, IPC broadcast & co. + // Bail out immediately if tracing is not enabled at all. + if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_))) + return; + + CreateLocalDumpPoint(); +} + +void MemoryDumpManager::BroadcastDumpRequest() { + NOTREACHED(); // TODO(primiano): implement IPC synchronization. +} + +// Creates a dump point for the current process and appends it to the trace. +void MemoryDumpManager::CreateLocalDumpPoint() { + AutoLock lock(lock_); + // TRACE_EVENT_* macros don't induce scoped_refptr type inference, hence we + // need the base ConvertableToTraceFormat and the upcast below. The + // alternative would be unnecessarily expensive (double Acquire/Release). + scoped_refptr<ConvertableToTraceFormat> pmd(new ProcessMemoryDump()); + + for (MemoryDumpProvider* dump_provider : dump_providers_enabled_) { + dump_provider->DumpInto(static_cast<ProcessMemoryDump*>(pmd.get())); + } + + // TODO(primiano): add the dump point to the trace at this point. +} + +void MemoryDumpManager::OnTraceLogEnabled() { + // TODO(primiano): at this point we query TraceLog::GetCurrentCategoryFilter + // to figure out (and cache) which dumpers should be enabled or not. + // For the moment piggy back everything on the generic "memory" category. + bool enabled; + TRACE_EVENT_CATEGORY_GROUP_ENABLED(kTraceCategory, &enabled); + + AutoLock lock(lock_); + if (enabled) { + dump_providers_enabled_.assign(dump_providers_registered_.begin(), + dump_providers_registered_.end()); + } else { + dump_providers_enabled_.clear(); + } + subtle::NoBarrier_Store(&memory_tracing_enabled_, 1); +} + +void MemoryDumpManager::OnTraceLogDisabled() { + AutoLock lock(lock_); + dump_providers_enabled_.clear(); + subtle::NoBarrier_Store(&memory_tracing_enabled_, 0); +} + +} // namespace trace_event +} // namespace base diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h new file mode 100644 index 0000000..fbc71d5 --- /dev/null +++ b/base/trace_event/memory_dump_manager.h @@ -0,0 +1,87 @@ +// Copyright 2015 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_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ + +#include <vector> + +#include "base/atomicops.h" +#include "base/memory/singleton.h" +#include "base/synchronization/lock.h" +#include "base/trace_event/trace_event.h" + +namespace base { +namespace trace_event { + +class MemoryDumpProvider; + +// Captures the reason why a dump point is being requested. This is to allow +// selective enabling of dump points, filtering and post-processing. +enum class DumpPointType { + TASK_BEGIN, // Dumping memory at the beginning of a message-loop task. + TASK_END, // Dumping memory at the ending of a message-loop task. + PERIODIC_INTERVAL, // Dumping memory at periodic intervals. + EXPLICITLY_TRIGGERED, // Non maskable dump request. +}; + +// This is the interface exposed to the rest of the codebase to deal with +// memory tracing. The main entry point for clients is represented by +// RequestDumpPoint(). The extension by Un(RegisterDumpProvider). +class BASE_EXPORT MemoryDumpManager : public TraceLog::EnabledStateObserver { + public: + static MemoryDumpManager* GetInstance(); + + // Invoked once per process to register the TraceLog observer. + void Initialize(); + + // MemoryDumpManager does NOT take memory ownership of |mdp|, which is + // expected to be a singleton. + void RegisterDumpProvider(MemoryDumpProvider* mdp); + void UnregisterDumpProvider(MemoryDumpProvider* mdp); + + // Requests a memory dump. The dump might happen or not depending on the + // filters and categories specified when enabling tracing. + void RequestDumpPoint(DumpPointType type); + + // TraceLog::EnabledStateObserver implementation. + void OnTraceLogEnabled() override; + void OnTraceLogDisabled() override; + + private: + friend struct DefaultSingletonTraits<MemoryDumpManager>; + friend class MemoryDumpManagerTest; + + static const char kTraceCategory[]; + + MemoryDumpManager(); + virtual ~MemoryDumpManager(); + + // Tears down the singleton instance. + static void DeleteForTesting(); + + // Broadcasts the dump requests to the other processes. + void BroadcastDumpRequest(); + + // Creates a dump point for the current process and appends it to the trace. + void CreateLocalDumpPoint(); + + std::vector<MemoryDumpProvider*> dump_providers_registered_; // Not owned. + std::vector<MemoryDumpProvider*> dump_providers_enabled_; // Not owned. + + // Protects from concurrent accesses to the |dump_providers_*|, e.g., tearing + // down logging while creating a dump point on another thread. + Lock lock_; + + // Optimization to avoid attempting any dump point (i.e. to not walk an empty + // dump_providers_enabled_ list) when tracing is not enabled. + subtle::AtomicWord memory_tracing_enabled_; + + DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_MANAGER_H_ diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc new file mode 100644 index 0000000..b5337e9 --- /dev/null +++ b/base/trace_event/memory_dump_manager_unittest.cc @@ -0,0 +1,125 @@ +// Copyright 2015 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/trace_event/memory_dump_manager.h" + +#include "base/trace_event/memory_dump_provider.h" +#include "base/trace_event/process_memory_dump.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::_; + +namespace base { +namespace trace_event { + +class MemoryDumpManagerTest : public testing::Test { + public: + void SetUp() override { + MemoryDumpManager::GetInstance()->Initialize(); + mdm_ = MemoryDumpManager::GetInstance(); + } + + void TearDown() override { + MemoryDumpManager::DeleteForTesting(); + TraceLog::DeleteForTesting(); + mdm_ = NULL; + } + + protected: + const char* const kTraceCategory = MemoryDumpManager::kTraceCategory; + + void EnableTracing(const char* category) { + TraceLog::GetInstance()->SetEnabled( + CategoryFilter(category), TraceLog::RECORDING_MODE, TraceOptions()); + } + + void DisableTracing() { TraceLog::GetInstance()->SetDisabled(); } + + MemoryDumpManager* mdm_; + + private: + // We want our singleton torn down after each test. + ShadowingAtExitManager at_exit_manager_; +}; + +class MockDumpProvider : public MemoryDumpProvider { + public: + MOCK_METHOD1(DumpInto, void(ProcessMemoryDump* pmd)); +}; + +TEST_F(MemoryDumpManagerTest, SingleDumper) { + MockDumpProvider mdp; + mdm_->RegisterDumpProvider(&mdp); + + // Check that the dumper is not called if the memory category is not enabled. + EnableTracing("foo-and-bar-but-not-memory"); + EXPECT_CALL(mdp, DumpInto(_)).Times(0); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Now repeat enabling the memory category and check that the dumper is + // invoked this time. + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp, DumpInto(_)).Times(3); + for (int i = 0; i < 3; ++i) + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + mdm_->UnregisterDumpProvider(&mdp); + + // Finally check the unregister logic (no calls to the mdp after unregister). + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp, DumpInto(_)).Times(0); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + TraceLog::GetInstance()->SetDisabled(); +} + +TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileTracing) { + MockDumpProvider mdp; + mdm_->RegisterDumpProvider(&mdp); + + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp, DumpInto(_)).Times(1); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + + mdm_->UnregisterDumpProvider(&mdp); + EXPECT_CALL(mdp, DumpInto(_)).Times(0); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + + DisableTracing(); +} + +TEST_F(MemoryDumpManagerTest, MultipleDumpers) { + MockDumpProvider mdp1; + MockDumpProvider mdp2; + + // Enable only mdp1. + mdm_->RegisterDumpProvider(&mdp1); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, DumpInto(_)).Times(1); + EXPECT_CALL(mdp2, DumpInto(_)).Times(0); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Invert: enable mdp1 and disable mdp2. + mdm_->UnregisterDumpProvider(&mdp1); + mdm_->RegisterDumpProvider(&mdp2); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, DumpInto(_)).Times(0); + EXPECT_CALL(mdp2, DumpInto(_)).Times(1); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + DisableTracing(); + + // Enable both mdp1 and mdp2. + mdm_->RegisterDumpProvider(&mdp1); + EnableTracing(kTraceCategory); + EXPECT_CALL(mdp1, DumpInto(_)).Times(1); + EXPECT_CALL(mdp2, DumpInto(_)).Times(1); + mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED); + DisableTracing(); +} + +} // namespace trace_Event +} // namespace base diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h new file mode 100644 index 0000000..18363c5 --- /dev/null +++ b/base/trace_event/memory_dump_provider.h @@ -0,0 +1,33 @@ +// Copyright 2015 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_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ +#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ + +#include "base/base_export.h" +#include "base/macros.h" + +namespace base { +namespace trace_event { + +class ProcessMemoryDump; + +// The contract interface that memory dump providers must implement. +class BASE_EXPORT MemoryDumpProvider { + public: + // Called by the MemoryDumpManager when generating dump points. + virtual void DumpInto(ProcessMemoryDump* pmd) = 0; + + protected: + MemoryDumpProvider() {} + virtual ~MemoryDumpProvider() {} + + private: + DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_ diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc new file mode 100644 index 0000000..0a3e096 --- /dev/null +++ b/base/trace_event/process_memory_dump.cc @@ -0,0 +1,29 @@ +// Copyright 2015 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/trace_event/process_memory_dump.h" + +#include "base/json/json_writer.h" +#include "base/values.h" + +namespace base { +namespace trace_event { + +ProcessMemoryDump::ProcessMemoryDump() { +} + +ProcessMemoryDump::~ProcessMemoryDump() { +} + +void ProcessMemoryDump::AppendAsTraceFormat(std::string* out) const { + // Build up the [dumper name] -> [serialized snapshot] JSON dictionary. + DictionaryValue dict; + std::string json_dict; + // TODO(primiano): this will append here the actual dumps from the dumpers. + base::JSONWriter::Write(&dict, &json_dict); + *out += json_dict; +} + +} // namespace trace_event +} // namespace base diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h new file mode 100644 index 0000000..ae42987 --- /dev/null +++ b/base/trace_event/process_memory_dump.h @@ -0,0 +1,37 @@ +// Copyright 2015 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_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ +#define BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ + +#include "base/base_export.h" +#include "base/basictypes.h" +#include "base/trace_event/trace_event_impl.h" + +namespace base { +namespace trace_event { + +// A container which holds the dumps produced by the MemoryDumpProvider(s) +// for a specific process. ProcessMemoryDump is as a strongly typed container +// which enforces the data model for each memory dump point. +// At trace generation time (i.e. when AppendAsTraceFormat is called) the +// ProcessMemoryDump will compose a key-value dictionary of the various dumps +// obtained during at trace dump point time. +class BASE_EXPORT ProcessMemoryDump : public ConvertableToTraceFormat { + public: + ProcessMemoryDump(); + + // ConvertableToTraceFormat implementation. + void AppendAsTraceFormat(std::string* out) const override; + + private: + ~ProcessMemoryDump() override; + + DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump); +}; + +} // namespace trace_event +} // namespace base + +#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_DUMP_H_ |