Update from https://crrev.com/327068
This rolls in //base, //build and //sandbox/linux and updates other
things to match, in particular:
*) Update build_v8.patch
*) Add junit, mockito and roboelectric to DEPS for android test rules
*) Update DEPS for grit
*) Fix up various GN files for os->target_os rename
*) Fix up a few places that were using //base/float_util to use std::isnan
*) Fix up a few places using ApiCompatibilityUtil to use Android SDK directly
as well as a few miscellaneous fixes.
Many portions based on ncbray's work in
https://codereview.chromium.org/1108173002/
R=ncbray@chromium.org
TBR=ncbray@chromium.org
Review URL: https://codereview.chromium.org/1124763003
diff --git a/base/trace_event/BUILD.gn b/base/trace_event/BUILD.gn
index 479a918..4f3819d 100644
--- a/base/trace_event/BUILD.gn
+++ b/base/trace_event/BUILD.gn
@@ -4,13 +4,19 @@
source_set("trace_event") {
sources = [
- "memory_allocator_attributes.h",
+ "java_heap_dump_provider_android.cc",
+ "java_heap_dump_provider_android.h",
+ "memory_allocator_attributes_type_info.cc",
+ "memory_allocator_attributes_type_info.h",
"memory_allocator_dump.cc",
"memory_allocator_dump.h",
"memory_dump_manager.cc",
"memory_dump_manager.h",
"memory_dump_provider.cc",
"memory_dump_provider.h",
+ "memory_dump_request_args.h",
+ "memory_dump_session_state.cc",
+ "memory_dump_session_state.h",
"process_memory_dump.cc",
"process_memory_dump.h",
"process_memory_maps.cc",
@@ -25,6 +31,8 @@
"trace_event_android.cc",
"trace_event_argument.cc",
"trace_event_argument.h",
+ "trace_event_etw_export_win.cc",
+ "trace_event_etw_export_win.h",
"trace_event_impl.cc",
"trace_event_impl.h",
"trace_event_impl_constants.cc",
@@ -36,6 +44,8 @@
"trace_event_system_stats_monitor.h",
"trace_event_win.cc",
"trace_event_win.h",
+ "winheap_dump_provider_win.cc",
+ "winheap_dump_provider_win.h",
]
if (is_nacl) {
@@ -45,6 +55,13 @@
]
}
+ if (is_linux || is_android) {
+ sources += [
+ "malloc_dump_provider.cc",
+ "malloc_dump_provider.h",
+ ]
+ }
+
configs += [ "//base:base_implementation" ]
deps = [
@@ -55,6 +72,10 @@
"//base/third_party/dynamic_annotations",
]
+ if (is_win) {
+ deps += [ "//base/trace_event/etw_manifest:chrome_events_win" ]
+ }
+
allow_circular_includes_from = [
"//base/debug",
"//base/memory",
@@ -67,6 +88,7 @@
source_set("trace_event_unittests") {
testonly = true
sources = [
+ "memory_allocator_attributes_type_info_unittest.cc",
"memory_allocator_dump_unittest.cc",
"memory_dump_manager_unittest.cc",
"process_memory_maps_dump_provider_unittest.cc",
@@ -77,6 +99,7 @@
"trace_event_system_stats_monitor_unittest.cc",
"trace_event_unittest.cc",
"trace_event_win_unittest.cc",
+ "winheap_dump_provider_win_unittest.cc",
]
deps = [
diff --git a/base/trace_event/OWNERS b/base/trace_event/OWNERS
index 3932776..aa1d675 100644
--- a/base/trace_event/OWNERS
+++ b/base/trace_event/OWNERS
@@ -1,3 +1,4 @@
nduca@chromium.org
dsinclair@chromium.org
+primiano@chromium.org
per-file trace_event_android.cc=wangxianzhu@chromium.org
diff --git a/base/trace_event/etw_manifest/BUILD.gn b/base/trace_event/etw_manifest/BUILD.gn
new file mode 100644
index 0000000..07cf80e
--- /dev/null
+++ b/base/trace_event/etw_manifest/BUILD.gn
@@ -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.
+
+assert(is_win, "This only runs on Windows.")
+
+# Makes the .h/.rc files from the .man file.
+action("chrome_events_win") {
+ visibility = [
+ "//base/trace_event/*",
+ "//chrome:main_dll",
+ ]
+ script = "build/message_compiler.py"
+
+ sources = [
+ "chrome_events_win.man",
+ ]
+
+ outputs = [
+ "$target_gen_dir/chrome_events_win.h",
+ "$target_gen_dir/chrome_events_win.rc",
+ ]
+
+ args = [
+ # Where to put the header.
+ "-h",
+ rebase_path("$target_gen_dir", root_build_dir),
+
+ # Where to put the .rc file.
+ "-r",
+ rebase_path("$target_gen_dir", root_build_dir),
+
+ # Generate the user-mode code.
+ "-um",
+ rebase_path("chrome_events_win.man", root_build_dir),
+ ]
+}
diff --git a/base/trace_event/etw_manifest/BUILD/message_compiler.py b/base/trace_event/etw_manifest/BUILD/message_compiler.py
new file mode 100644
index 0000000..be5927d
--- /dev/null
+++ b/base/trace_event/etw_manifest/BUILD/message_compiler.py
@@ -0,0 +1,16 @@
+# 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.
+
+# Runs the Microsoft Message Compiler (mc.exe). This Python adapter is for the
+# GN build, which can only run Python and not native binaries.
+
+import subprocess
+import sys
+
+# mc writes to stderr, so this explicily redirects to stdout and eats it.
+try:
+ subprocess.check_output(["mc.exe"] + sys.argv[1:], stderr=subprocess.STDOUT)
+except subprocess.CalledProcessError as e:
+ print e.output
+ sys.exit(e.returncode)
diff --git a/base/trace_event/etw_manifest/chrome_events_win.man b/base/trace_event/etw_manifest/chrome_events_win.man
new file mode 100644
index 0000000..10a8ddf
--- /dev/null
+++ b/base/trace_event/etw_manifest/chrome_events_win.man
@@ -0,0 +1,84 @@
+<?xml version='1.0' encoding='utf-8' standalone='yes'?>
+<instrumentationManifest
+ xmlns="http://schemas.microsoft.com/win/2004/08/events"
+ xmlns:win="http://manifests.microsoft.com/win/2004/08/windows/events"
+ xmlns:xs="http://www.w3.org/2001/XMLSchema"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://schemas.microsoft.com/win/2004/08/events eventman.xsd"
+ >
+ <instrumentation>
+ <events>
+ <provider
+ guid="{D2D578D9-2936-45B6-A09f-30E32715F42D}"
+ messageFileName="chrome.dll"
+ name="Chrome"
+ resourceFileName="chrome.dll"
+ symbol="CHROME"
+ >
+ <channels>
+ <importChannel
+ chid="SYSTEM"
+ name="System"
+ />
+ </channels>
+ <templates>
+ <template tid="tid_chrome_event">
+ <data
+ inType="win:AnsiString"
+ name="Name"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Phase"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Name 1"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Value 1"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Name 2"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Value 2"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Name 3"
+ />
+ <data
+ inType="win:AnsiString"
+ name="Arg Value 3"
+ />
+ </template>
+ </templates>
+ <events>
+ <event
+ channel="SYSTEM"
+ level="win:Informational"
+ message="$(string.ChromeEvent.EventMessage)"
+ opcode="win:Info"
+ symbol="ChromeEvent"
+ template="tid_chrome_event"
+ value="1"
+ />
+ </events>
+ </provider>
+ </events>
+ </instrumentation>
+ <localization xmlns="http://schemas.microsoft.com/win/2004/08/events">
+ <resources culture="en-US">
+ <stringTable>
+ <string
+ id="ChromeEvent.EventMessage"
+ value="Chrome Event: %1 (%2)"
+ />
+ </stringTable>
+ </resources>
+ </localization>
+</instrumentationManifest>
diff --git a/base/trace_event/etw_manifest/etw_manifest.gyp b/base/trace_event/etw_manifest/etw_manifest.gyp
new file mode 100644
index 0000000..b0a8712
--- /dev/null
+++ b/base/trace_event/etw_manifest/etw_manifest.gyp
@@ -0,0 +1,39 @@
+# 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.
+{
+ 'targets': [
+ {
+ # GN version: //base/trace_event/etw_manifest/BUILD.gn
+ 'target_name': 'etw_manifest',
+ 'type': 'static_library',
+ 'conditions': [
+ ['OS=="win"', {
+ 'sources': [
+ 'chrome_events_win.man',
+ ],
+ 'variables': {
+ 'man_output_dir': '<(SHARED_INTERMEDIATE_DIR)/base/trace_event/etw_manifest',
+ },
+ 'rules': [{
+ # Rule to run the message compiler.
+ 'rule_name': 'message_compiler',
+ 'extension': 'man',
+ 'outputs': [
+ '<(man_output_dir)/chrome_events_win.h',
+ '<(man_output_dir)/chrome_events_win.rc',
+ ],
+ 'action': [
+ 'mc.exe',
+ '-h', '<(man_output_dir)',
+ '-r', '<(man_output_dir)/.',
+ '-um',
+ '<(RULE_INPUT_PATH)',
+ ],
+ 'message': 'Running message compiler on <(RULE_INPUT_PATH)',
+ }],
+ }],
+ ],
+ }
+ ]
+}
diff --git a/base/trace_event/java_heap_dump_provider_android.cc b/base/trace_event/java_heap_dump_provider_android.cc
new file mode 100644
index 0000000..aa193ab
--- /dev/null
+++ b/base/trace_event/java_heap_dump_provider_android.cc
@@ -0,0 +1,53 @@
+// 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/java_heap_dump_provider_android.h"
+
+#include "base/android/java_runtime.h"
+#include "base/trace_event/process_memory_dump.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char kDumperFriendlyName[] = "JavaHeap";
+const char kDumperName[] = "java_heap";
+
+} // namespace
+
+// static
+JavaHeapDumpProvider* JavaHeapDumpProvider::GetInstance() {
+ return Singleton<JavaHeapDumpProvider,
+ LeakySingletonTraits<JavaHeapDumpProvider>>::get();
+}
+
+JavaHeapDumpProvider::JavaHeapDumpProvider() {
+}
+
+JavaHeapDumpProvider::~JavaHeapDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot with the memory counters
+// for the current process.
+bool JavaHeapDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+ MemoryAllocatorDump* dump =
+ pmd->CreateAllocatorDump(kDumperName, MemoryAllocatorDump::kRootHeap);
+
+ // These numbers come from java.lang.Runtime stats.
+ long total_heap_size = 0;
+ long free_heap_size = 0;
+ android::JavaRuntime::GetMemoryUsage(&total_heap_size, &free_heap_size);
+ dump->set_physical_size_in_bytes(total_heap_size);
+ dump->set_allocated_objects_count(0);
+ dump->set_allocated_objects_size_in_bytes(total_heap_size - free_heap_size);
+ return true;
+}
+
+const char* JavaHeapDumpProvider::GetFriendlyName() const {
+ return kDumperFriendlyName;
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/java_heap_dump_provider_android.h b/base/trace_event/java_heap_dump_provider_android.h
new file mode 100644
index 0000000..8280751
--- /dev/null
+++ b/base/trace_event/java_heap_dump_provider_android.h
@@ -0,0 +1,35 @@
+// 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_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
+#define BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
+
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class JavaHeapDumpProvider : public MemoryDumpProvider {
+ public:
+ static JavaHeapDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ bool DumpInto(ProcessMemoryDump* pmd) override;
+ const char* GetFriendlyName() const override;
+
+ private:
+ friend struct DefaultSingletonTraits<JavaHeapDumpProvider>;
+
+ JavaHeapDumpProvider();
+ ~JavaHeapDumpProvider() override;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaHeapDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_JAVA_HEAP_DUMP_PROVIDER_ANDROID_H_
diff --git a/base/trace_event/malloc_dump_provider.cc b/base/trace_event/malloc_dump_provider.cc
new file mode 100644
index 0000000..7d9931c
--- /dev/null
+++ b/base/trace_event/malloc_dump_provider.cc
@@ -0,0 +1,64 @@
+// 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/malloc_dump_provider.h"
+
+#include <malloc.h>
+
+#include "base/trace_event/process_memory_dump.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char kDumperFriendlyName[] = "Malloc";
+
+} // namespace
+
+// static
+MallocDumpProvider* MallocDumpProvider::GetInstance() {
+ return Singleton<MallocDumpProvider,
+ LeakySingletonTraits<MallocDumpProvider>>::get();
+}
+
+MallocDumpProvider::MallocDumpProvider() {
+}
+
+MallocDumpProvider::~MallocDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+bool MallocDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+ struct mallinfo info = mallinfo();
+ DCHECK_GE(info.arena + info.hblkhd, info.uordblks);
+
+ MemoryAllocatorDump* dump =
+ pmd->CreateAllocatorDump("malloc", MemoryAllocatorDump::kRootHeap);
+ if (!dump)
+ return false;
+
+ // When the system allocator is implemented by tcmalloc, the total physical
+ // size is given by |arena| and |hblkhd| is 0. In case of Android's jemalloc
+ // |arena| is 0 and the outer pages size is reported by |hblkhd|. In case of
+ // dlmalloc the total is given by |arena| + |hblkhd|.
+ // For more details see link: http://goo.gl/fMR8lF.
+ dump->set_physical_size_in_bytes(info.arena + info.hblkhd);
+
+ // mallinfo doesn't support any allocated object count.
+ dump->set_allocated_objects_count(0);
+
+ // Total allocated space is given by |uordblks|.
+ dump->set_allocated_objects_size_in_bytes(info.uordblks);
+
+ return true;
+}
+
+const char* MallocDumpProvider::GetFriendlyName() const {
+ return kDumperFriendlyName;
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/malloc_dump_provider.h b/base/trace_event/malloc_dump_provider.h
new file mode 100644
index 0000000..b6f6973
--- /dev/null
+++ b/base/trace_event/malloc_dump_provider.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_MALLOC_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
+
+#include <istream>
+
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT MallocDumpProvider : public MemoryDumpProvider {
+ public:
+ static MallocDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ bool DumpInto(ProcessMemoryDump* pmd) override;
+ const char* GetFriendlyName() const override;
+
+ private:
+ friend struct DefaultSingletonTraits<MallocDumpProvider>;
+
+ MallocDumpProvider();
+ ~MallocDumpProvider() override;
+
+ DISALLOW_COPY_AND_ASSIGN(MallocDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_MALLOC_DUMP_PROVIDER_H_
diff --git a/base/trace_event/memory_allocator_attributes.h b/base/trace_event/memory_allocator_attributes.h
deleted file mode 100644
index efae9de..0000000
--- a/base/trace_event/memory_allocator_attributes.h
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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_ALLOCATOR_ATTRIBUTES_H_
-#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_H_
-
-#include <string>
-
-#include "base/base_export.h"
-#include "base/containers/hash_tables.h"
-
-namespace base {
-namespace trace_event {
-
-struct BASE_EXPORT MemoryAllocatorDeclaredAttribute {
- std::string name;
-
- // Refer to src/tools/perf/unit-info.json for the semantic of the type.
- std::string type;
-};
-
-using MemoryAllocatorDeclaredAttributes =
- hash_map<std::string, MemoryAllocatorDeclaredAttribute>;
-
-} // namespace trace_event
-} // namespace base
-
-#endif // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_H_
diff --git a/base/trace_event/memory_allocator_attributes_type_info.cc b/base/trace_event/memory_allocator_attributes_type_info.cc
new file mode 100644
index 0000000..7d1e61c
--- /dev/null
+++ b/base/trace_event/memory_allocator_attributes_type_info.cc
@@ -0,0 +1,61 @@
+// 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_allocator_attributes_type_info.h"
+
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+base::LazyInstance<const std::string> kNotFound = LAZY_INSTANCE_INITIALIZER;
+
+std::string GetKey(const std::string& allocator_name,
+ const std::string& attribute_name) {
+ return allocator_name + "/" + attribute_name;
+}
+} // namespace
+
+MemoryAllocatorAttributesTypeInfo::MemoryAllocatorAttributesTypeInfo() {
+}
+
+MemoryAllocatorAttributesTypeInfo::~MemoryAllocatorAttributesTypeInfo() {
+}
+
+const std::string& MemoryAllocatorAttributesTypeInfo::Get(
+ const std::string& allocator_name,
+ const std::string& attribute_name) const {
+ auto it = type_info_map_.find(GetKey(allocator_name, attribute_name));
+ if (it == type_info_map_.end())
+ return kNotFound.Get();
+ return it->second;
+}
+
+void MemoryAllocatorAttributesTypeInfo::Set(const std::string& allocator_name,
+ const std::string& attribute_name,
+ const std::string& attribute_type) {
+ std::string key = GetKey(allocator_name, attribute_name);
+ DCHECK_EQ(0u, type_info_map_.count(key));
+ type_info_map_[key] = attribute_type;
+}
+
+bool MemoryAllocatorAttributesTypeInfo::Exists(
+ const std::string& allocator_name,
+ const std::string& attribute_name) const {
+ return type_info_map_.count(GetKey(allocator_name, attribute_name)) == 1;
+}
+
+void MemoryAllocatorAttributesTypeInfo::Update(
+ const MemoryAllocatorAttributesTypeInfo& other) {
+ for (auto it = other.type_info_map_.begin();
+ it != other.type_info_map_.end(); ++it) {
+ bool no_duplicates = type_info_map_.insert(*it).second;
+ DCHECK(no_duplicates) << "Duplicated allocator attribute " << it->first;
+ }
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/memory_allocator_attributes_type_info.h b/base/trace_event/memory_allocator_attributes_type_info.h
new file mode 100644
index 0000000..c3986ae
--- /dev/null
+++ b/base/trace_event/memory_allocator_attributes_type_info.h
@@ -0,0 +1,49 @@
+// 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_ALLOCATOR_ATTRIBUTES_TYPE_INFO_H_
+#define BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_TYPE_INFO_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/containers/hash_tables.h"
+
+namespace base {
+namespace trace_event {
+
+// A dictionary of "allocator_name/attribute_name" -> "attribute_type" which
+// supports merging and enforces duplicate checking.
+class BASE_EXPORT MemoryAllocatorAttributesTypeInfo {
+ public:
+ MemoryAllocatorAttributesTypeInfo();
+ ~MemoryAllocatorAttributesTypeInfo();
+
+ // Returns the attribute type, or an empty string if not found.
+ const std::string& Get(const std::string& allocator_name,
+ const std::string& attribute_name) const;
+
+ // Refer to tools/perf/unit-info.json for the semantics of |attribute_type|.
+ void Set(const std::string& allocator_name,
+ const std::string& attribute_name,
+ const std::string& attribute_type);
+
+ // Checks whether a given {allocator_name, attribute_name} declaration exists.
+ bool Exists(const std::string& allocator_name,
+ const std::string& attribute_name) const;
+
+ // Merges the attribute types declared in |other| into this.
+ void Update(const MemoryAllocatorAttributesTypeInfo& other);
+
+ private:
+ // "allocator_name/attribute_name" -> attribute_type.
+ hash_map<std::string, std::string> type_info_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorAttributesTypeInfo);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_MEMORY_ALLOCATOR_ATTRIBUTES_TYPE_INFO_H_
diff --git a/base/trace_event/memory_allocator_attributes_type_info_unittest.cc b/base/trace_event/memory_allocator_attributes_type_info_unittest.cc
new file mode 100644
index 0000000..5f69fae
--- /dev/null
+++ b/base/trace_event/memory_allocator_attributes_type_info_unittest.cc
@@ -0,0 +1,59 @@
+// 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_allocator_attributes_type_info.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(MemoryAllocatorAttributesTypeInfoTest, BasicTest) {
+ MemoryAllocatorAttributesTypeInfo attrs;
+ EXPECT_EQ("", attrs.Get("non_existing_alloc", "non_existing_attr"));
+
+ attrs.Set("alloc", "attr", "type");
+ EXPECT_TRUE(attrs.Exists("alloc", "attr"));
+ EXPECT_FALSE(attrs.Exists("alloc", "foo"));
+ EXPECT_FALSE(attrs.Exists("foo", "attr"));
+ EXPECT_EQ("type", attrs.Get("alloc", "attr"));
+
+ attrs.Set("alloc2", "attr", "type2");
+ EXPECT_TRUE(attrs.Exists("alloc2", "attr"));
+ EXPECT_FALSE(attrs.Exists("alloc2", "foo"));
+ EXPECT_EQ("type", attrs.Get("alloc", "attr"));
+ EXPECT_EQ("type2", attrs.Get("alloc2", "attr"));
+
+ MemoryAllocatorAttributesTypeInfo other_attrs;
+ other_attrs.Set("other_alloc", "other_attr", "other_type");
+ other_attrs.Set("other_alloc", "attr", "other_type2");
+ other_attrs.Set("other_alloc_2", "other_attr", "other_type");
+ other_attrs.Set("other_alloc_2", "attr", "other_type3");
+
+ // Check the merging logic.
+ attrs.Update(other_attrs);
+ EXPECT_EQ("other_type", attrs.Get("other_alloc", "other_attr"));
+ EXPECT_EQ("other_type2", attrs.Get("other_alloc", "attr"));
+ EXPECT_EQ("other_type", attrs.Get("other_alloc_2", "other_attr"));
+ EXPECT_EQ("other_type3", attrs.Get("other_alloc_2", "attr"));
+ EXPECT_EQ("type", attrs.Get("alloc", "attr"));
+ EXPECT_EQ("type2", attrs.Get("alloc2", "attr"));
+ EXPECT_FALSE(other_attrs.Exists("alloc", "attr"));
+ EXPECT_FALSE(other_attrs.Exists("alloc2", "attr"));
+}
+
+// DEATH tests are not supported in Android / iOS.
+#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
+TEST(MemoryAllocatorAttributesTypeInfoTest, DuplicatesDeathTest) {
+ MemoryAllocatorAttributesTypeInfo attrs;
+ attrs.Set("alloc", "attr", "type");
+ MemoryAllocatorAttributesTypeInfo conflicting_attrs;
+ conflicting_attrs.Set("alloc", "attr", "type2");
+ ASSERT_DEATH(attrs.Set("alloc", "attr", "other_type"), "");
+ ASSERT_DEATH(attrs.Update(conflicting_attrs), "");
+}
+#endif
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/memory_allocator_dump.cc b/base/trace_event/memory_allocator_dump.cc
index 604af7a..1d2fb3f 100644
--- a/base/trace_event/memory_allocator_dump.cc
+++ b/base/trace_event/memory_allocator_dump.cc
@@ -6,85 +6,122 @@
#include "base/format_macros.h"
#include "base/strings/stringprintf.h"
-#include "base/trace_event/memory_allocator_attributes.h"
#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 "base/trace_event/trace_event_argument.h"
#include "base/values.h"
namespace base {
namespace trace_event {
-MemoryAllocatorDump::MemoryAllocatorDump(const std::string& name,
- MemoryAllocatorDump* parent)
- : name_(name),
- parent_(parent),
+// static
+const char MemoryAllocatorDump::kRootHeap[] = "";
+
+// static
+std::string MemoryAllocatorDump::GetAbsoluteName(
+ const std::string& allocator_name,
+ const std::string& heap_name) {
+ return allocator_name + (heap_name == kRootHeap ? "" : "/" + heap_name);
+}
+
+MemoryAllocatorDump::MemoryAllocatorDump(const std::string& allocator_name,
+ const std::string& heap_name,
+ ProcessMemoryDump* process_memory_dump)
+ : allocator_name_(allocator_name),
+ heap_name_(heap_name),
+ process_memory_dump_(process_memory_dump),
physical_size_in_bytes_(0),
allocated_objects_count_(0),
allocated_objects_size_in_bytes_(0) {
- // Dots are not allowed in the name as the underlying base::DictionaryValue
+ // The allocator name cannot be empty or contain slash separators.
+ DCHECK(!allocator_name.empty());
+ DCHECK_EQ(std::string::npos, allocator_name.find_first_of('/'));
+
+ // The heap_name can be empty and contain slash separator, but not
+ // leading or trailing ones.
+ DCHECK(heap_name.empty() ||
+ (heap_name[0] != '/' && *heap_name.rbegin() != '/'));
+
+ // Dots are not allowed anywhere as the underlying base::DictionaryValue
// would treat them magically and split in sub-nodes, which is not intended.
- DCHECK_EQ(std::string::npos, name.find_first_of('.'));
+ DCHECK_EQ(std::string::npos, allocator_name.find_first_of('.'));
+ DCHECK_EQ(std::string::npos, heap_name.find_first_of('.'));
}
MemoryAllocatorDump::~MemoryAllocatorDump() {
}
-void MemoryAllocatorDump::SetExtraAttribute(const std::string& name,
- int value) {
- extra_attributes_.SetInteger(name, value);
+void MemoryAllocatorDump::SetAttribute(const std::string& name, int value) {
+ DCHECK(GetAttributesTypeInfo().Exists(allocator_name_, name))
+ << "attribute '" << name << "' not declared."
+ << "See MemoryDumpProvider.DeclareAllocatorAttribute()";
+ attributes_values_.SetInteger(name, value);
}
-int MemoryAllocatorDump::GetExtraIntegerAttribute(
- const std::string& name) const {
- bool res;
+std::string MemoryAllocatorDump::GetAbsoluteName() const {
+ return GetAbsoluteName(allocator_name_, heap_name_);
+}
+
+int MemoryAllocatorDump::GetIntegerAttribute(const std::string& name) const {
int value = -1;
- res = extra_attributes_.GetInteger(name, &value);
- DCHECK(res) << "Allocator attribute '" << name << "' not found";
+ bool res = attributes_values_.GetInteger(name, &value);
+ DCHECK(res) << "Attribute '" << name << "' not found";
return value;
}
void MemoryAllocatorDump::AsValueInto(TracedValue* value) const {
static const char kHexFmt[] = "%" PRIx64;
- value->BeginDictionary(name_.c_str());
+ value->BeginDictionary(GetAbsoluteName().c_str());
+ value->BeginDictionary("attrs");
- value->SetString("parent", parent_ ? parent_->name_ : "");
- value->SetString("physical_size_in_bytes",
- StringPrintf(kHexFmt, physical_size_in_bytes_));
- value->SetString("allocated_objects_count",
- StringPrintf(kHexFmt, allocated_objects_count_));
- value->SetString("allocated_objects_size_in_bytes",
+ // TODO(primiano): these hard-coded types are temporary to transition to the
+ // new generalized attribute format. This code will be refactored by the end
+ // of May 2015.
+ value->BeginDictionary("outer_size");
+ value->SetString("type", "scalar");
+ value->SetString("units", "bytes");
+ value->SetString("value", StringPrintf(kHexFmt, physical_size_in_bytes_));
+ value->EndDictionary();
+
+ value->BeginDictionary("inner_size");
+ value->SetString("type", "scalar");
+ value->SetString("units", "bytes");
+ value->SetString("value",
StringPrintf(kHexFmt, allocated_objects_size_in_bytes_));
+ value->EndDictionary();
+
+ value->BeginDictionary("objects_count");
+ value->SetString("type", "scalar");
+ value->SetString("units", "objects");
+ value->SetString("value", StringPrintf(kHexFmt, allocated_objects_count_));
+ value->EndDictionary();
// Copy all the extra attributes.
- const MemoryDumpProvider* mdp =
- MemoryDumpManager::GetInstance()->dump_provider_currently_active();
- const MemoryAllocatorDeclaredAttributes& extra_attributes_types =
- mdp->allocator_attributes();
-
- value->BeginDictionary("args");
- for (DictionaryValue::Iterator it(extra_attributes_); !it.IsAtEnd();
+ for (DictionaryValue::Iterator it(attributes_values_); !it.IsAtEnd();
it.Advance()) {
const std::string& attr_name = it.key();
const Value& attr_value = it.value();
value->BeginDictionary(attr_name.c_str());
value->SetValue("value", attr_value.DeepCopy());
- auto attr_it = extra_attributes_types.find(attr_name);
- DCHECK(attr_it != extra_attributes_types.end())
- << "Allocator attribute " << attr_name
- << " not declared for the dumper " << mdp->GetFriendlyName();
-
- // TODO(primiano): the "type" should be dumped just once, not repeated on
- // on every event. The ability of doing so depends on crbug.com/466121.
- value->SetString("type", attr_it->second.type);
+ const std::string& attr_type =
+ GetAttributesTypeInfo().Get(allocator_name_, attr_name);
+ DCHECK(!attr_type.empty());
+ value->SetString("type", "scalar");
+ value->SetString("units", attr_type);
value->EndDictionary(); // "arg_name": { "type": "...", "value": "..." }
}
- value->EndDictionary(); // "args": {}
- value->EndDictionary(); // "allocator name": {}
+ value->EndDictionary(); // "attrs": { ... }
+ value->EndDictionary(); // "allocator_name/heap_subheap": { ... }
+}
+
+const MemoryAllocatorAttributesTypeInfo&
+MemoryAllocatorDump::GetAttributesTypeInfo() const {
+ return process_memory_dump_->session_state()->allocators_attributes_type_info;
}
} // namespace trace_event
diff --git a/base/trace_event/memory_allocator_dump.h b/base/trace_event/memory_allocator_dump.h
index 4d7293e..1c786ab 100644
--- a/base/trace_event/memory_allocator_dump.h
+++ b/base/trace_event/memory_allocator_dump.h
@@ -8,34 +8,45 @@
#include "base/base_export.h"
#include "base/basictypes.h"
#include "base/logging.h"
-#include "base/trace_event/memory_allocator_attributes.h"
+#include "base/trace_event/memory_allocator_attributes_type_info.h"
#include "base/values.h"
namespace base {
namespace trace_event {
class MemoryDumpManager;
+class ProcessMemoryDump;
class TracedValue;
// Data model for user-land memory allocator dumps.
class BASE_EXPORT MemoryAllocatorDump {
public:
- MemoryAllocatorDump(const std::string& name, MemoryAllocatorDump* parent);
+ // Returns the absolute name for a given (|allocator_name|,|heap_name|) tuple.
+ static std::string GetAbsoluteName(const std::string& allocator_name,
+ const std::string& heap_name);
+
+ // Use as argument for |heap_name| when the allocator has only one root heap.
+ static const char kRootHeap[];
+
+ // MemoryAllocatorDump is owned by ProcessMemoryDump.
+ MemoryAllocatorDump(const std::string& allocator_name,
+ const std::string& heap_name,
+ ProcessMemoryDump* process_memory_dump);
~MemoryAllocatorDump();
- const std::string& name() const { return name_; }
- const MemoryAllocatorDump* parent() const { return parent_; }
+ // Name of the allocator, a plain string with no separators (e.g, "malloc").
+ const std::string& allocator_name() const { return allocator_name_; }
- void set_physical_size_in_bytes(uint64 value) {
- physical_size_in_bytes_ = value;
- }
- uint64 physical_size_in_bytes() const { return physical_size_in_bytes_; }
+ // Name of the heap being dumped, either: "heap", "heap/subheap" or kRootHeap
+ // if the allocator has just one root heap.
+ const std::string& heap_name() const { return heap_name_; }
- void set_allocated_objects_count(uint64 value) {
- allocated_objects_count_ = value;
- }
- uint64 allocated_objects_count() const { return allocated_objects_count_; }
+ // Absolute name, unique within the scope of an entire ProcessMemoryDump.
+ // In practice this is "allocator_name/heap/subheap".
+ std::string GetAbsoluteName() const;
+ // Inner size: Bytes requested by clients of the allocator, without accounting
+ // for any metadata or allocator-specific bookeeping structs.
void set_allocated_objects_size_in_bytes(uint64 value) {
allocated_objects_size_in_bytes_ = value;
}
@@ -43,19 +54,46 @@
return allocated_objects_size_in_bytes_;
}
- void SetExtraAttribute(const std::string& name, int value);
- int GetExtraIntegerAttribute(const std::string& name) const;
+ // Outer size: bytes requested to the system to handle all the allocations,
+ // including any allocator-internal metadata / bookeeping structs. For
+ // instance, in the case of an allocator which gets pages to the system via
+ // mmap() or similar, this is the number of requested pages * 4k.
+ void set_physical_size_in_bytes(uint64 value) {
+ physical_size_in_bytes_ = value;
+ }
+ uint64 physical_size_in_bytes() const { return physical_size_in_bytes_; }
+
+ // Number of objects allocated, if known, or 0 if not available.
+ void set_allocated_objects_count(uint64 value) {
+ allocated_objects_count_ = value;
+ }
+ uint64 allocated_objects_count() const { return allocated_objects_count_; }
+
+ // Get/Set extra attributes. The attributes name must have been previously
+ // declared through MemoryDumpProvider.DeclareAllocatorAttribute().
+ void SetAttribute(const std::string& name, int value);
+ int GetIntegerAttribute(const std::string& name) const;
// Called at trace generation time to populate the TracedValue.
void AsValueInto(TracedValue* value) const;
+ // Get the ProcessMemoryDump instance that owns this.
+ ProcessMemoryDump* process_memory_dump() const {
+ return process_memory_dump_;
+ }
+
+ // Retrieves the map of allocator attributes types, which is shared by all
+ // MemoryAllocatorDump(s) across all ProcessMemoryDump(s) per tracing session.
+ const MemoryAllocatorAttributesTypeInfo& GetAttributesTypeInfo() const;
+
private:
- const std::string name_;
- MemoryAllocatorDump* const parent_; // Not owned.
+ const std::string allocator_name_;
+ const std::string heap_name_;
+ ProcessMemoryDump* const process_memory_dump_; // Not owned (PMD owns this).
uint64 physical_size_in_bytes_;
uint64 allocated_objects_count_;
uint64 allocated_objects_size_in_bytes_;
- DictionaryValue extra_attributes_;
+ DictionaryValue attributes_values_;
DISALLOW_COPY_AND_ASSIGN(MemoryAllocatorDump);
};
diff --git a/base/trace_event/memory_allocator_dump_unittest.cc b/base/trace_event/memory_allocator_dump_unittest.cc
index 8c14560..110a25d 100644
--- a/base/trace_event/memory_allocator_dump_unittest.cc
+++ b/base/trace_event/memory_allocator_dump_unittest.cc
@@ -5,89 +5,110 @@
#include "base/trace_event/memory_allocator_dump.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/trace_event_argument.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace base {
namespace trace_event {
namespace {
+
class FakeMemoryAllocatorDumpProvider : public MemoryDumpProvider {
public:
FakeMemoryAllocatorDumpProvider() {
- DeclareAllocatorAttribute({"attr1", "count"});
- DeclareAllocatorAttribute({"attr2", "bytes"});
+ DeclareAllocatorAttribute("foobar_allocator", "attr1", "count");
+ DeclareAllocatorAttribute("foobar_allocator", "attr2", "bytes");
}
bool DumpInto(ProcessMemoryDump* pmd) override {
- MemoryAllocatorDump* mad_foo = pmd->CreateAllocatorDump("foo");
- mad_foo->set_physical_size_in_bytes(4096);
- mad_foo->set_allocated_objects_count(42);
- mad_foo->set_allocated_objects_size_in_bytes(1000);
- mad_foo->SetExtraAttribute("attr1", 1234);
- mad_foo->SetExtraAttribute("attr2", 99);
+ MemoryAllocatorDump* root_heap = pmd->CreateAllocatorDump(
+ "foobar_allocator", MemoryAllocatorDump::kRootHeap);
+ root_heap->set_physical_size_in_bytes(4096);
+ root_heap->set_allocated_objects_count(42);
+ root_heap->set_allocated_objects_size_in_bytes(1000);
+ root_heap->SetAttribute("attr1", 1234);
+ root_heap->SetAttribute("attr2", 99);
- MemoryAllocatorDump* mad_bar = pmd->CreateAllocatorDump("foo/bar", mad_foo);
- mad_bar->set_physical_size_in_bytes(1);
- mad_bar->set_allocated_objects_count(2);
- mad_bar->set_allocated_objects_size_in_bytes(3);
+ MemoryAllocatorDump* sub_heap =
+ pmd->CreateAllocatorDump("foobar_allocator", "sub_heap");
+ sub_heap->set_physical_size_in_bytes(1);
+ sub_heap->set_allocated_objects_count(2);
+ sub_heap->set_allocated_objects_size_in_bytes(3);
- pmd->CreateAllocatorDump("baz");
- // Leave the rest of |baz| deliberately uninitialized, to check that
+ pmd->CreateAllocatorDump("foobar_allocator", "sub_heap/empty");
+ // Leave the rest of sub heap deliberately uninitialized, to check that
// CreateAllocatorDump returns a properly zero-initialized object.
return true;
}
- const char* GetFriendlyName() const override { return "mock_allocator"; }
+ const char* GetFriendlyName() const override { return "FooBar Allocator"; }
};
} // namespace
TEST(MemoryAllocatorDumpTest, DumpIntoProcessMemoryDump) {
FakeMemoryAllocatorDumpProvider fmadp;
- ProcessMemoryDump pmd;
+ ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState()));
+ pmd.session_state()->allocators_attributes_type_info.Update(
+ fmadp.allocator_attributes_type_info());
fmadp.DumpInto(&pmd);
ASSERT_EQ(3u, pmd.allocator_dumps().size());
- const MemoryAllocatorDump* mad_foo = pmd.GetAllocatorDump("foo");
- ASSERT_NE(nullptr, mad_foo);
- EXPECT_EQ("foo", mad_foo->name());
- ASSERT_EQ(nullptr, mad_foo->parent());
- EXPECT_EQ(4096u, mad_foo->physical_size_in_bytes());
- EXPECT_EQ(42u, mad_foo->allocated_objects_count());
- EXPECT_EQ(1000u, mad_foo->allocated_objects_size_in_bytes());
+ const MemoryAllocatorDump* root_heap =
+ pmd.GetAllocatorDump("foobar_allocator", MemoryAllocatorDump::kRootHeap);
+ ASSERT_NE(nullptr, root_heap);
+ EXPECT_EQ("foobar_allocator", root_heap->allocator_name());
+ EXPECT_EQ("", root_heap->heap_name());
+ EXPECT_NE("", root_heap->GetAbsoluteName());
+ EXPECT_EQ(4096u, root_heap->physical_size_in_bytes());
+ EXPECT_EQ(42u, root_heap->allocated_objects_count());
+ EXPECT_EQ(1000u, root_heap->allocated_objects_size_in_bytes());
- // Check the extra attributes of |mad_foo|.
- EXPECT_EQ(1234, mad_foo->GetExtraIntegerAttribute("attr1"));
- EXPECT_EQ(99, mad_foo->GetExtraIntegerAttribute("attr2"));
+ // Check the extra attributes of |root_heap|.
+ EXPECT_EQ(1234, root_heap->GetIntegerAttribute("attr1"));
+ EXPECT_EQ(99, root_heap->GetIntegerAttribute("attr2"));
- const MemoryAllocatorDump* mad_bar = pmd.GetAllocatorDump("foo/bar");
- ASSERT_NE(nullptr, mad_bar);
- EXPECT_EQ("foo/bar", mad_bar->name());
- ASSERT_EQ(mad_foo, mad_bar->parent());
- EXPECT_EQ(1u, mad_bar->physical_size_in_bytes());
- EXPECT_EQ(2u, mad_bar->allocated_objects_count());
- EXPECT_EQ(3u, mad_bar->allocated_objects_size_in_bytes());
+ const MemoryAllocatorDump* sub_heap =
+ pmd.GetAllocatorDump("foobar_allocator", "sub_heap");
+ ASSERT_NE(nullptr, sub_heap);
+ EXPECT_EQ("foobar_allocator", sub_heap->allocator_name());
+ EXPECT_EQ("sub_heap", sub_heap->heap_name());
+ EXPECT_NE("", sub_heap->GetAbsoluteName());
+ EXPECT_EQ(1u, sub_heap->physical_size_in_bytes());
+ EXPECT_EQ(2u, sub_heap->allocated_objects_count());
+ EXPECT_EQ(3u, sub_heap->allocated_objects_size_in_bytes());
- const MemoryAllocatorDump* mad_baz = pmd.GetAllocatorDump("baz");
- ASSERT_NE(nullptr, mad_baz);
- EXPECT_EQ("baz", mad_baz->name());
- ASSERT_EQ(nullptr, mad_baz->parent());
- EXPECT_EQ(0u, mad_baz->physical_size_in_bytes());
- EXPECT_EQ(0u, mad_baz->allocated_objects_count());
- EXPECT_EQ(0u, mad_baz->allocated_objects_size_in_bytes());
+ const MemoryAllocatorDump* empty_sub_heap =
+ pmd.GetAllocatorDump("foobar_allocator", "sub_heap/empty");
+ ASSERT_NE(nullptr, empty_sub_heap);
+ EXPECT_EQ("foobar_allocator", empty_sub_heap->allocator_name());
+ EXPECT_EQ("sub_heap/empty", empty_sub_heap->heap_name());
+ EXPECT_NE("", sub_heap->GetAbsoluteName());
+ EXPECT_EQ(0u, empty_sub_heap->physical_size_in_bytes());
+ EXPECT_EQ(0u, empty_sub_heap->allocated_objects_count());
+ EXPECT_EQ(0u, empty_sub_heap->allocated_objects_size_in_bytes());
+
+ // Check that the AsValueInfo doesn't hit any DCHECK.
+ scoped_refptr<TracedValue> traced_value(new TracedValue());
+ pmd.AsValueInto(traced_value.get());
}
// DEATH tests are not supported in Android / iOS.
#if !defined(NDEBUG) && !defined(OS_ANDROID) && !defined(OS_IOS)
TEST(MemoryAllocatorDumpTest, ForbidDuplicatesDeathTest) {
FakeMemoryAllocatorDumpProvider fmadp;
- ProcessMemoryDump pmd;
- pmd.CreateAllocatorDump("dump_1");
- pmd.CreateAllocatorDump("dump_2");
- ASSERT_DEATH(pmd.CreateAllocatorDump("dump_1"), "");
+ ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState()));
+ pmd.CreateAllocatorDump("foo_allocator", MemoryAllocatorDump::kRootHeap);
+ pmd.CreateAllocatorDump("bar_allocator", "heap");
+ ASSERT_DEATH(
+ pmd.CreateAllocatorDump("foo_allocator", MemoryAllocatorDump::kRootHeap),
+ "");
+ ASSERT_DEATH(pmd.CreateAllocatorDump("bar_allocator", "heap"), "");
+ ASSERT_DEATH(pmd.CreateAllocatorDump("", "must_have_allocator_name"), "");
}
#endif
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index 0ec5d19..859e8e0 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -9,41 +9,123 @@
#include "base/atomic_sequence_num.h"
#include "base/compiler_specific.h"
#include "base/trace_event/memory_dump_provider.h"
+#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_dump.h"
#include "base/trace_event/trace_event_argument.h"
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include "base/trace_event/malloc_dump_provider.h"
+#include "base/trace_event/process_memory_maps_dump_provider.h"
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+#elif defined(OS_WIN)
+#include "base/trace_event/winheap_dump_provider_win.h"
+#endif
+
namespace base {
namespace trace_event {
namespace {
+// TODO(primiano): this should be smarter and should do something similar to
+// trace event synthetic delays.
+const char kTraceCategory[] = TRACE_DISABLED_BY_DEFAULT("memory-infra");
+
MemoryDumpManager* g_instance_for_testing = nullptr;
+const int kDumpIntervalSeconds = 2;
const int kTraceEventNumArgs = 1;
const char* kTraceEventArgNames[] = {"dumps"};
const unsigned char kTraceEventArgTypes[] = {TRACE_VALUE_TYPE_CONVERTABLE};
StaticAtomicSequenceNumber g_next_guid;
-const char* DumpPointTypeToString(const DumpPointType& dump_point_type) {
- switch (dump_point_type) {
- case DumpPointType::TASK_BEGIN:
+const char* MemoryDumpTypeToString(const MemoryDumpType& dump_type) {
+ switch (dump_type) {
+ case MemoryDumpType::TASK_BEGIN:
return "TASK_BEGIN";
- case DumpPointType::TASK_END:
+ case MemoryDumpType::TASK_END:
return "TASK_END";
- case DumpPointType::PERIODIC_INTERVAL:
+ case MemoryDumpType::PERIODIC_INTERVAL:
return "PERIODIC_INTERVAL";
- case DumpPointType::EXPLICITLY_TRIGGERED:
+ case MemoryDumpType::EXPLICITLY_TRIGGERED:
return "EXPLICITLY_TRIGGERED";
}
NOTREACHED();
return "UNKNOWN";
}
+// Internal class used to hold details about ProcessMemoryDump requests for the
+// current process.
+// TODO(primiano): In the upcoming CLs, ProcessMemoryDump will become async.
+// and this class will be used to convey more details across PostTask()s.
+class ProcessMemoryDumpHolder
+ : public RefCountedThreadSafe<ProcessMemoryDumpHolder> {
+ public:
+ ProcessMemoryDumpHolder(
+ MemoryDumpRequestArgs req_args,
+ const scoped_refptr<MemoryDumpSessionState>& session_state,
+ MemoryDumpCallback callback)
+ : process_memory_dump(session_state),
+ req_args(req_args),
+ callback(callback),
+ task_runner(MessageLoop::current()->task_runner()),
+ num_pending_async_requests(0) {}
+
+ ProcessMemoryDump process_memory_dump;
+ const MemoryDumpRequestArgs req_args;
+
+ // Callback passed to the initial call to CreateProcessDump().
+ MemoryDumpCallback callback;
+
+ // Thread on which FinalizeDumpAndAddToTrace() should be called, which is the
+ // same that invoked the initial CreateProcessDump().
+ const scoped_refptr<SingleThreadTaskRunner> task_runner;
+
+ // Number of pending ContinueAsyncProcessDump() calls.
+ int num_pending_async_requests;
+
+ private:
+ friend class RefCountedThreadSafe<ProcessMemoryDumpHolder>;
+ virtual ~ProcessMemoryDumpHolder() {}
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDumpHolder);
+};
+
+void FinalizeDumpAndAddToTrace(
+ const scoped_refptr<ProcessMemoryDumpHolder>& pmd_holder) {
+ DCHECK_EQ(0, pmd_holder->num_pending_async_requests);
+
+ if (!pmd_holder->task_runner->BelongsToCurrentThread()) {
+ pmd_holder->task_runner->PostTask(
+ FROM_HERE, Bind(&FinalizeDumpAndAddToTrace, pmd_holder));
+ return;
+ }
+
+ scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
+ pmd_holder->process_memory_dump.AsValueInto(
+ static_cast<TracedValue*>(event_value.get()));
+ const char* const event_name =
+ MemoryDumpTypeToString(pmd_holder->req_args.dump_type);
+
+ TRACE_EVENT_API_ADD_TRACE_EVENT(
+ TRACE_EVENT_PHASE_MEMORY_DUMP,
+ TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name,
+ pmd_holder->req_args.dump_guid, kTraceEventNumArgs, kTraceEventArgNames,
+ kTraceEventArgTypes, nullptr /* arg_values */, &event_value,
+ TRACE_EVENT_FLAG_HAS_ID);
+
+ if (!pmd_holder->callback.is_null()) {
+ pmd_holder->callback.Run(pmd_holder->req_args.dump_guid, true);
+ pmd_holder->callback.Reset();
+ }
+}
+
+void RequestPeriodicGlobalDump() {
+ MemoryDumpManager::GetInstance()->RequestGlobalDump(
+ MemoryDumpType::PERIODIC_INTERVAL);
+}
+
} // namespace
-// 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
+const char* const MemoryDumpManager::kTraceCategoryForTesting = kTraceCategory;
// static
MemoryDumpManager* MemoryDumpManager::GetInstance() {
@@ -56,11 +138,17 @@
// static
void MemoryDumpManager::SetInstanceForTesting(MemoryDumpManager* instance) {
+ if (instance)
+ instance->skip_core_dumpers_auto_registration_for_testing_ = true;
g_instance_for_testing = instance;
}
MemoryDumpManager::MemoryDumpManager()
- : dump_provider_currently_active_(nullptr), memory_tracing_enabled_(0) {
+ : dump_provider_currently_active_(nullptr),
+ delegate_(nullptr),
+ memory_tracing_enabled_(0),
+ skip_core_dumpers_auto_registration_for_testing_(false) {
+ g_next_guid.GetNext(); // Make sure that first guid is not zero.
}
MemoryDumpManager::~MemoryDumpManager() {
@@ -70,94 +158,176 @@
void MemoryDumpManager::Initialize() {
TRACE_EVENT0(kTraceCategory, "init"); // Add to trace-viewer category list.
trace_event::TraceLog::GetInstance()->AddEnabledStateObserver(this);
+
+ if (skip_core_dumpers_auto_registration_for_testing_)
+ return;
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ // Enable the core dump providers.
+ RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance());
+ RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance());
+ RegisterDumpProvider(MallocDumpProvider::GetInstance());
+#elif defined(OS_WIN)
+ RegisterDumpProvider(WinHeapDumpProvider::GetInstance());
+#endif
+}
+
+void MemoryDumpManager::SetDelegate(MemoryDumpManagerDelegate* delegate) {
+ AutoLock lock(lock_);
+ DCHECK_EQ(static_cast<MemoryDumpManagerDelegate*>(nullptr), delegate_);
+ delegate_ = delegate;
}
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);
+ dump_providers_registered_.insert(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);
+ // Unregistration of a MemoryDumpProvider while tracing is ongoing is safe
+ // only if the MDP has specified a thread affinity (via task_runner()) AND
+ // the unregistration happens on the same thread (so the MDP cannot unregister
+ // and DumpInto() at the same time).
+ // Otherwise, it is not possible to guarantee that its unregistration is
+ // race-free. If you hit this DCHECK, your MDP has a bug.
+ DCHECK_IMPLIES(
+ subtle::NoBarrier_Load(&memory_tracing_enabled_),
+ mdp->task_runner() && mdp->task_runner()->BelongsToCurrentThread())
+ << "The MemoryDumpProvider " << mdp->GetFriendlyName() << " attempted to "
+ << "unregister itself in a racy way. Please file a crbug.";
// 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);
+ dump_providers_enabled_.erase(mdp);
+ dump_providers_registered_.erase(mdp);
}
-void MemoryDumpManager::RequestDumpPoint(DumpPointType dump_point_type) {
- // TODO(primiano): this will have more logic to coordinate dump points across
- // multiple processes via IPC. See crbug.com/462930.
-
+void MemoryDumpManager::RequestGlobalDump(
+ MemoryDumpType dump_type,
+ const MemoryDumpCallback& callback) {
// Bail out immediately if tracing is not enabled at all.
if (!UNLIKELY(subtle::NoBarrier_Load(&memory_tracing_enabled_)))
return;
- // TODO(primiano): Make guid actually unique (cross-process) by hashing it
- // with the PID. See crbug.com/462931 for details.
- const uint64 guid = g_next_guid.GetNext();
- CreateLocalDumpPoint(dump_point_type, guid);
-}
+ const uint64 guid =
+ TraceLog::GetInstance()->MangleEventId(g_next_guid.GetNext());
-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(DumpPointType dump_point_type,
- uint64 guid) {
- bool did_any_provider_dump = false;
- scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump());
-
- // Serialize dump point generation so that memory dump providers don't have to
- // deal with thread safety.
+ // The delegate_ is supposed to be thread safe, immutable and long lived.
+ // No need to keep the lock after we ensure that a delegate has been set.
+ MemoryDumpManagerDelegate* delegate;
{
AutoLock lock(lock_);
- for (auto it = dump_providers_enabled_.begin();
- it != dump_providers_enabled_.end();) {
- dump_provider_currently_active_ = *it;
- if (dump_provider_currently_active_->DumpInto(pmd.get())) {
- did_any_provider_dump = true;
- ++it;
- } else {
- LOG(ERROR) << "The memory dumper "
- << dump_provider_currently_active_->GetFriendlyName()
- << " failed, possibly due to sandboxing (crbug.com/461788), "
- "disabling it for current process. Try restarting chrome "
- "with the --no-sandbox switch.";
- it = dump_providers_enabled_.erase(it);
- }
- dump_provider_currently_active_ = nullptr;
- }
+ delegate = delegate_;
}
- // Don't create a dump point if all the dumpers failed.
- if (!did_any_provider_dump)
- return;
+ if (delegate) {
+ // The delegate is in charge to coordinate the request among all the
+ // processes and call the CreateLocalDumpPoint on the local process.
+ MemoryDumpRequestArgs args = {guid, dump_type};
+ delegate->RequestGlobalMemoryDump(args, callback);
+ } else if (!callback.is_null()) {
+ callback.Run(guid, false /* success */);
+ }
+}
- scoped_refptr<ConvertableToTraceFormat> event_value(new TracedValue());
- pmd->AsValueInto(static_cast<TracedValue*>(event_value.get()));
- const char* const event_name = DumpPointTypeToString(dump_point_type);
+void MemoryDumpManager::RequestGlobalDump(MemoryDumpType dump_type) {
+ RequestGlobalDump(dump_type, MemoryDumpCallback());
+}
- TRACE_EVENT_API_ADD_TRACE_EVENT(
- TRACE_EVENT_PHASE_MEMORY_DUMP,
- TraceLog::GetCategoryGroupEnabled(kTraceCategory), event_name, guid,
- kTraceEventNumArgs, kTraceEventArgNames, kTraceEventArgTypes,
- NULL /* arg_values */, &event_value, TRACE_EVENT_FLAG_HAS_ID);
+// Creates a memory dump for the current process and appends it to the trace.
+void MemoryDumpManager::CreateProcessDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) {
+ scoped_refptr<ProcessMemoryDumpHolder> pmd_holder(
+ new ProcessMemoryDumpHolder(args, session_state_, callback));
+ ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
+ bool did_any_provider_dump = false;
+
+ // Iterate over the active dump providers and invoke DumpInto(pmd).
+ // The MDM guarantees linearity (at most one MDP is active within one
+ // process) and thread-safety (MDM enforces the right locking when entering /
+ // leaving the MDP.DumpInto() call). This is to simplify the clients' design
+ // and not let the MDPs worry about locking.
+ // As regards thread affinity, depending on the MDP configuration (see
+ // memory_dump_provider.h), the DumpInto() invocation can happen:
+ // - Synchronousy on the MDM thread, when MDP.task_runner() is not set.
+ // - Posted on MDP.task_runner(), when MDP.task_runner() is set.
+ {
+ AutoLock lock(lock_);
+ for (auto dump_provider_iter = dump_providers_enabled_.begin();
+ dump_provider_iter != dump_providers_enabled_.end();) {
+ // InvokeDumpProviderLocked will remove the MDP from the set if it fails.
+ MemoryDumpProvider* mdp = *dump_provider_iter;
+ ++dump_provider_iter;
+ if (mdp->task_runner()) {
+ // The DumpInto() call must be posted.
+ bool did_post_async_task = mdp->task_runner()->PostTask(
+ FROM_HERE, Bind(&MemoryDumpManager::ContinueAsyncProcessDump,
+ Unretained(this), Unretained(mdp), pmd_holder));
+ // The thread underlying the TaskRunner might have gone away.
+ if (did_post_async_task)
+ ++pmd_holder->num_pending_async_requests;
+ } else {
+ // Invoke the dump provider synchronously.
+ did_any_provider_dump |= InvokeDumpProviderLocked(mdp, pmd);
+ }
+ }
+ } // AutoLock
+
+ // If at least one synchronous provider did dump and there are no pending
+ // asynchronous requests, add the dump to the trace and invoke the callback
+ // straight away (FinalizeDumpAndAddToTrace() takes care of the callback).
+ if (did_any_provider_dump && pmd_holder->num_pending_async_requests == 0)
+ FinalizeDumpAndAddToTrace(pmd_holder);
+}
+
+// Invokes the MemoryDumpProvider.DumpInto(), taking care of the failsafe logic
+// which disables the dumper when failing (crbug.com/461788).
+bool MemoryDumpManager::InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
+ ProcessMemoryDump* pmd) {
+ lock_.AssertAcquired();
+ dump_provider_currently_active_ = mdp;
+ bool dump_successful = mdp->DumpInto(pmd);
+ dump_provider_currently_active_ = nullptr;
+ if (!dump_successful) {
+ LOG(ERROR) << "The memory dumper " << mdp->GetFriendlyName()
+ << " failed, possibly due to sandboxing (crbug.com/461788), "
+ "disabling it for current process. Try restarting chrome "
+ "with the --no-sandbox switch.";
+ dump_providers_enabled_.erase(mdp);
+ }
+ return dump_successful;
+}
+
+// This is posted to arbitrary threads as a continuation of CreateProcessDump(),
+// when one or more MemoryDumpProvider(s) require the DumpInto() call to happen
+// on a different thread.
+void MemoryDumpManager::ContinueAsyncProcessDump(
+ MemoryDumpProvider* mdp,
+ scoped_refptr<ProcessMemoryDumpHolder> pmd_holder) {
+ bool should_finalize_dump = false;
+ {
+ // The lock here is to guarantee that different asynchronous dumps on
+ // different threads are still serialized, so that the MemoryDumpProvider
+ // has a consistent view of the |pmd| argument passed.
+ AutoLock lock(lock_);
+ ProcessMemoryDump* pmd = &pmd_holder->process_memory_dump;
+
+ // Check if the MemoryDumpProvider is still there. It might have been
+ // destroyed and unregistered while hopping threads.
+ if (dump_providers_enabled_.count(mdp))
+ InvokeDumpProviderLocked(mdp, pmd);
+
+ // Finalize the dump appending it to the trace if this was the last
+ // asynchronous request pending.
+ --pmd_holder->num_pending_async_requests;
+ if (pmd_holder->num_pending_async_requests == 0)
+ should_finalize_dump = true;
+ } // AutoLock(lock_)
+
+ if (should_finalize_dump)
+ FinalizeDumpAndAddToTrace(pmd_holder);
}
void MemoryDumpManager::OnTraceLogEnabled() {
@@ -168,19 +338,36 @@
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 {
+
+ // There is no point starting the tracing without a delegate.
+ if (!enabled || !delegate_) {
dump_providers_enabled_.clear();
+ return;
}
+
+ // Merge the dictionary of allocator attributes from all dump providers
+ // into the session state.
+ session_state_ = new MemoryDumpSessionState();
+ for (const MemoryDumpProvider* mdp : dump_providers_registered_) {
+ session_state_->allocators_attributes_type_info.Update(
+ mdp->allocator_attributes_type_info());
+ }
+ dump_providers_enabled_ = dump_providers_registered_;
subtle::NoBarrier_Store(&memory_tracing_enabled_, 1);
+
+ if (delegate_->IsCoordinatorProcess()) {
+ periodic_dump_timer_.Start(FROM_HERE,
+ TimeDelta::FromSeconds(kDumpIntervalSeconds),
+ base::Bind(&RequestPeriodicGlobalDump));
+ }
}
void MemoryDumpManager::OnTraceLogDisabled() {
AutoLock lock(lock_);
+ periodic_dump_timer_.Stop();
dump_providers_enabled_.clear();
subtle::NoBarrier_Store(&memory_tracing_enabled_, 0);
+ session_state_ = nullptr;
}
} // namespace trace_event
diff --git a/base/trace_event/memory_dump_manager.h b/base/trace_event/memory_dump_manager.h
index 04d0135..371a47a 100644
--- a/base/trace_event/memory_dump_manager.h
+++ b/base/trace_event/memory_dump_manager.h
@@ -8,42 +8,58 @@
#include <vector>
#include "base/atomicops.h"
+#include "base/containers/hash_tables.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/singleton.h"
#include "base/synchronization/lock.h"
+#include "base/timer/timer.h"
+#include "base/trace_event/memory_dump_request_args.h"
#include "base/trace_event/trace_event.h"
namespace base {
namespace trace_event {
-class MemoryDumpProvider;
+namespace {
+class ProcessMemoryDumpHolder;
+}
-// 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.
-};
+class MemoryDumpManagerDelegate;
+class MemoryDumpProvider;
+class ProcessMemoryDump;
+class MemoryDumpSessionState;
// 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 const char* const kTraceCategoryForTesting;
+
static MemoryDumpManager* GetInstance();
// Invoked once per process to register the TraceLog observer.
void Initialize();
+ // See the lifetime and thread-safety requirements on the delegate below in
+ // the |MemoryDumpManagerDelegate| docstring.
+ void SetDelegate(MemoryDumpManagerDelegate* delegate);
+
// MemoryDumpManager does NOT take memory ownership of |mdp|, which is
- // expected to be a singleton.
+ // expected to either be a singleton or unregister itself.
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 dump_point_type);
+ // The optional |callback| is executed asynchronously, on an arbitrary thread,
+ // to notify about the completion of the global dump (i.e. after all the
+ // processes have dumped) and its success (true iff all the dumps were
+ // successful).
+ void RequestGlobalDump(MemoryDumpType dump_type,
+ const MemoryDumpCallback& callback);
+
+ // Same as above (still asynchronous), but without callback.
+ void RequestGlobalDump(MemoryDumpType dump_type);
// TraceLog::EnabledStateObserver implementation.
void OnTraceLogEnabled() override;
@@ -55,41 +71,89 @@
return dump_provider_currently_active_;
}
+ // Returns the MemoryDumpSessionState object, which is shared by all the
+ // ProcessMemoryDump and MemoryAllocatorDump instances through all the tracing
+ // session lifetime.
+ const scoped_refptr<MemoryDumpSessionState>& session_state() const {
+ return session_state_;
+ }
+
private:
friend struct DefaultDeleter<MemoryDumpManager>; // For the testing instance.
friend struct DefaultSingletonTraits<MemoryDumpManager>;
+ friend class MemoryDumpManagerDelegate;
friend class MemoryDumpManagerTest;
- static const char kTraceCategory[];
-
static void SetInstanceForTesting(MemoryDumpManager* instance);
MemoryDumpManager();
virtual ~MemoryDumpManager();
- // Broadcasts the dump requests to the other processes.
- void BroadcastDumpRequest();
+ // Internal, used only by MemoryDumpManagerDelegate.
+ // Creates a memory dump for the current process and appends it to the trace.
+ // |callback| will be invoked asynchronously upon completion on the same
+ // thread on which CreateProcessDump() was called.
+ void CreateProcessDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback);
- // Creates a dump point for the current process and appends it to the trace.
- void CreateLocalDumpPoint(DumpPointType dump_point_type, uint64 guid);
+ bool InvokeDumpProviderLocked(MemoryDumpProvider* mdp,
+ ProcessMemoryDump* pmd);
+ void ContinueAsyncProcessDump(
+ MemoryDumpProvider* mdp,
+ scoped_refptr<ProcessMemoryDumpHolder> pmd_holder);
- std::vector<MemoryDumpProvider*> dump_providers_registered_; // Not owned.
- std::vector<MemoryDumpProvider*> dump_providers_enabled_; // Not owned.
+ hash_set<MemoryDumpProvider*> dump_providers_registered_; // Not owned.
+ hash_set<MemoryDumpProvider*> dump_providers_enabled_; // Not owned.
// TODO(primiano): this is required only until crbug.com/466121 gets fixed.
- MemoryDumpProvider* dump_provider_currently_active_; // Now owned.
+ MemoryDumpProvider* dump_provider_currently_active_; // Not owned.
- // Protects from concurrent accesses to the |dump_providers_*|, e.g., tearing
- // down logging while creating a dump point on another thread.
+ // Shared among all the PMDs to keep state scoped to the tracing session.
+ scoped_refptr<MemoryDumpSessionState> session_state_;
+
+ MemoryDumpManagerDelegate* delegate_; // Not owned.
+
+ // Protects from concurrent accesses to the |dump_providers_*| and |delegate_|
+ // to guard against disabling logging while dumping on another thread.
Lock lock_;
- // Optimization to avoid attempting any dump point (i.e. to not walk an empty
+ // Optimization to avoid attempting any memory dump (i.e. to not walk an empty
// dump_providers_enabled_ list) when tracing is not enabled.
subtle::AtomicWord memory_tracing_enabled_;
+ // For time-triggered periodic dumps.
+ RepeatingTimer<MemoryDumpManager> periodic_dump_timer_;
+
+ // Skips the auto-registration of the core dumpers during Initialize().
+ bool skip_core_dumpers_auto_registration_for_testing_;
+
DISALLOW_COPY_AND_ASSIGN(MemoryDumpManager);
};
+// The delegate is supposed to be long lived (read: a Singleton) and thread
+// safe (i.e. should expect calls from any thread and handle thread hopping).
+class BASE_EXPORT MemoryDumpManagerDelegate {
+ public:
+ virtual void RequestGlobalMemoryDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) = 0;
+
+ // Determines whether the MemoryDumpManager instance should be the master
+ // (the ones which initiates and coordinates the multiprocess dumps) or not.
+ virtual bool IsCoordinatorProcess() const = 0;
+
+ protected:
+ MemoryDumpManagerDelegate() {}
+ virtual ~MemoryDumpManagerDelegate() {}
+
+ void CreateProcessDump(const MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) {
+ MemoryDumpManager::GetInstance()->CreateProcessDump(args, callback);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MemoryDumpManagerDelegate);
+};
+
} // namespace trace_event
} // namespace base
diff --git a/base/trace_event/memory_dump_manager_unittest.cc b/base/trace_event/memory_dump_manager_unittest.cc
index 65e719f..589c406 100644
--- a/base/trace_event/memory_dump_manager_unittest.cc
+++ b/base/trace_event/memory_dump_manager_unittest.cc
@@ -4,6 +4,11 @@
#include "base/trace_event/memory_dump_manager.h"
+#include "base/bind_helpers.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/threading/thread.h"
#include "base/trace_event/memory_dump_provider.h"
#include "base/trace_event/process_memory_dump.h"
#include "testing/gmock/include/gmock/gmock.h"
@@ -16,23 +21,46 @@
namespace base {
namespace trace_event {
+// Testing MemoryDumpManagerDelegate which short-circuits dump requests locally
+// instead of performing IPC dances.
+class MemoryDumpManagerDelegateForTesting : public MemoryDumpManagerDelegate {
+ public:
+ void RequestGlobalMemoryDump(
+ const base::trace_event::MemoryDumpRequestArgs& args,
+ const MemoryDumpCallback& callback) override {
+ CreateProcessDump(args, callback);
+ }
+
+ bool IsCoordinatorProcess() const override { return false; }
+};
+
class MemoryDumpManagerTest : public testing::Test {
public:
void SetUp() override {
+ message_loop_.reset(new MessageLoop());
mdm_.reset(new MemoryDumpManager());
MemoryDumpManager::SetInstanceForTesting(mdm_.get());
ASSERT_EQ(mdm_, MemoryDumpManager::GetInstance());
MemoryDumpManager::GetInstance()->Initialize();
+ MemoryDumpManager::GetInstance()->SetDelegate(&delegate_);
}
void TearDown() override {
MemoryDumpManager::SetInstanceForTesting(nullptr);
mdm_.reset();
+ message_loop_.reset();
TraceLog::DeleteForTesting();
}
+ void DumpCallbackAdapter(scoped_refptr<SingleThreadTaskRunner> task_runner,
+ Closure closure,
+ uint64 dump_guid,
+ bool success) {
+ task_runner->PostTask(FROM_HERE, closure);
+ }
+
protected:
- const char* const kTraceCategory = MemoryDumpManager::kTraceCategory;
+ const char* kTraceCategory = MemoryDumpManager::kTraceCategoryForTesting;
void EnableTracing(const char* category) {
TraceLog::GetInstance()->SetEnabled(
@@ -44,15 +72,29 @@
scoped_ptr<MemoryDumpManager> mdm_;
private:
+ scoped_ptr<MessageLoop> message_loop_;
+ MemoryDumpManagerDelegateForTesting delegate_;
+
// We want our singleton torn down after each test.
ShadowingAtExitManager at_exit_manager_;
};
class MockDumpProvider : public MemoryDumpProvider {
public:
+ MockDumpProvider() {}
+
+ explicit MockDumpProvider(
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner)
+ : MemoryDumpProvider(task_runner) {}
+
+ // Ctor for the SharedSessionState test.
+ explicit MockDumpProvider(const std::string& id) {
+ DeclareAllocatorAttribute("allocator" + id, "attr" + id, "type" + id);
+ }
+
MOCK_METHOD1(DumpInto, bool(ProcessMemoryDump* pmd));
- // DumpInto() override for the ActiveDumpProviderConsistency test,
+ // DumpInto() override for the ActiveDumpProviderConsistency test.
bool DumpIntoAndCheckDumpProviderCurrentlyActive(ProcessMemoryDump* pmd) {
EXPECT_EQ(
this,
@@ -60,6 +102,22 @@
return true;
}
+ // DumpInto() override for the RespectTaskRunnerAffinity test.
+ bool DumpIntoAndCheckTaskRunner(ProcessMemoryDump* pmd) {
+ EXPECT_TRUE(task_runner()->RunsTasksOnCurrentThread());
+ return true;
+ }
+
+ // DumpInto() override for the SharedSessionState test.
+ bool DumpIntoAndCheckSessionState(ProcessMemoryDump* pmd) {
+ EXPECT_TRUE(pmd->session_state());
+ const auto& attrs_type_info =
+ pmd->session_state()->allocators_attributes_type_info;
+ EXPECT_TRUE(attrs_type_info.Exists("allocator1", "attr1"));
+ EXPECT_TRUE(attrs_type_info.Exists("allocator2", "attr2"));
+ return true;
+ }
+
const char* GetFriendlyName() const override { return "MockDumpProvider"; }
};
@@ -70,7 +128,7 @@
// 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);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
// Now repeat enabling the memory category and check that the dumper is
@@ -78,7 +136,7 @@
EnableTracing(kTraceCategory);
EXPECT_CALL(mdp, DumpInto(_)).Times(3).WillRepeatedly(Return(true));
for (int i = 0; i < 3; ++i)
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
mdm_->UnregisterDumpProvider(&mdp);
@@ -86,21 +144,24 @@
// 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);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
TraceLog::GetInstance()->SetDisabled();
}
-TEST_F(MemoryDumpManagerTest, UnregisterDumperWhileTracing) {
- MockDumpProvider mdp;
- mdm_->RegisterDumpProvider(&mdp);
+TEST_F(MemoryDumpManagerTest, SharedSessionState) {
+ MockDumpProvider mdp1("1"); // Will declare an allocator property "attr1".
+ MockDumpProvider mdp2("2"); // Will declare an allocator property "attr2".
+ mdm_->RegisterDumpProvider(&mdp1);
+ mdm_->RegisterDumpProvider(&mdp2);
EnableTracing(kTraceCategory);
- EXPECT_CALL(mdp, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ EXPECT_CALL(mdp1, DumpInto(_)).Times(2).WillRepeatedly(
+ Invoke(&mdp1, &MockDumpProvider::DumpIntoAndCheckSessionState));
+ EXPECT_CALL(mdp2, DumpInto(_)).Times(2).WillRepeatedly(
+ Invoke(&mdp2, &MockDumpProvider::DumpIntoAndCheckSessionState));
- mdm_->UnregisterDumpProvider(&mdp);
- EXPECT_CALL(mdp, DumpInto(_)).Times(0);
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ for (int i = 0; i < 2; ++i)
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
}
@@ -114,7 +175,7 @@
EnableTracing(kTraceCategory);
EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
EXPECT_CALL(mdp2, DumpInto(_)).Times(0);
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
// Invert: enable mdp1 and disable mdp2.
@@ -123,7 +184,7 @@
EnableTracing(kTraceCategory);
EXPECT_CALL(mdp1, DumpInto(_)).Times(0);
EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
// Enable both mdp1 and mdp2.
@@ -131,7 +192,66 @@
EnableTracing(kTraceCategory);
EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ DisableTracing();
+}
+
+// Checks that the MemoryDumpManager respects the thread affinity when a
+// MemoryDumpProvider specifies a task_runner(). The test starts creating 8
+// threads and registering a MemoryDumpProvider on each of them. At each
+// iteration, one thread is removed, to check the live unregistration logic.
+TEST_F(MemoryDumpManagerTest, RespectTaskRunnerAffinity) {
+ const uint32 kNumInitialThreads = 8;
+
+ ScopedVector<Thread> threads;
+ ScopedVector<MockDumpProvider> mdps;
+
+ // Create the threads and setup the expectations. Given that at each iteration
+ // we will pop out one thread/MemoryDumpProvider, each MDP is supposed to be
+ // invoked a number of times equal to its index.
+ for (uint32 i = kNumInitialThreads; i > 0; --i) {
+ threads.push_back(new Thread("test thread"));
+ threads.back()->Start();
+ mdps.push_back(new MockDumpProvider(threads.back()->task_runner()));
+ MockDumpProvider* mdp = mdps.back();
+ mdm_->RegisterDumpProvider(mdp);
+ EXPECT_CALL(*mdp, DumpInto(_))
+ .Times(i)
+ .WillRepeatedly(
+ Invoke(mdp, &MockDumpProvider::DumpIntoAndCheckTaskRunner));
+ }
+
+ EnableTracing(kTraceCategory);
+
+ while (!threads.empty()) {
+ {
+ RunLoop run_loop;
+ MemoryDumpCallback callback =
+ Bind(&MemoryDumpManagerTest::DumpCallbackAdapter, Unretained(this),
+ MessageLoop::current()->task_runner(), run_loop.QuitClosure());
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED, callback);
+ // This nested message loop (|run_loop|) will be quit if and only if
+ // the RequestGlobalDump callback is invoked.
+ run_loop.Run();
+ }
+
+ // Unregister a MDP and destroy one thread at each iteration to check the
+ // live unregistration logic. The unregistration needs to happen on the same
+ // thread the MDP belongs to.
+ {
+ RunLoop run_loop;
+ Closure unregistration =
+ Bind(&MemoryDumpManager::UnregisterDumpProvider,
+ Unretained(mdm_.get()), Unretained(mdps.back()));
+ threads.back()->task_runner()->PostTaskAndReply(FROM_HERE, unregistration,
+ run_loop.QuitClosure());
+ run_loop.Run();
+ }
+ mdps.pop_back();
+ threads.back()->Stop();
+ threads.pop_back();
+ }
+
DisableTracing();
}
@@ -148,11 +268,11 @@
EXPECT_CALL(mdp1, DumpInto(_)).Times(1).WillRepeatedly(Return(false));
EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(true));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
EXPECT_CALL(mdp1, DumpInto(_)).Times(0);
EXPECT_CALL(mdp2, DumpInto(_)).Times(1).WillRepeatedly(Return(false));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
}
@@ -177,8 +297,8 @@
.WillRepeatedly(Invoke(
&mdp2,
&MockDumpProvider::DumpIntoAndCheckDumpProviderCurrentlyActive));
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
- mdm_->RequestDumpPoint(DumpPointType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
+ mdm_->RequestGlobalDump(MemoryDumpType::EXPLICITLY_TRIGGERED);
DisableTracing();
}
diff --git a/base/trace_event/memory_dump_provider.cc b/base/trace_event/memory_dump_provider.cc
index 9518461..a2d3889 100644
--- a/base/trace_event/memory_dump_provider.cc
+++ b/base/trace_event/memory_dump_provider.cc
@@ -4,7 +4,7 @@
#include "base/trace_event/memory_dump_provider.h"
-#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
namespace base {
namespace trace_event {
@@ -12,15 +12,20 @@
MemoryDumpProvider::MemoryDumpProvider() {
}
+MemoryDumpProvider::MemoryDumpProvider(
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner)
+ : task_runner_(task_runner) {
+}
+
MemoryDumpProvider::~MemoryDumpProvider() {
}
void MemoryDumpProvider::DeclareAllocatorAttribute(
- const MemoryAllocatorDeclaredAttribute& attr) {
- DCHECK_EQ(0u, allocator_attributes_.count(attr.name))
- << "Allocator attribute " << attr.name << " already declared for dumper "
- << GetFriendlyName();
- allocator_attributes_[attr.name] = attr;
+ const std::string& allocator_name,
+ const std::string& attribute_name,
+ const std::string& attribute_type) {
+ allocator_attributes_type_info_.Set(
+ allocator_name, attribute_name, attribute_type);
}
} // namespace trace_event
diff --git a/base/trace_event/memory_dump_provider.h b/base/trace_event/memory_dump_provider.h
index 5cc3a6e..9ec7ad9 100644
--- a/base/trace_event/memory_dump_provider.h
+++ b/base/trace_event/memory_dump_provider.h
@@ -6,9 +6,13 @@
#define BASE_TRACE_EVENT_MEMORY_DUMP_PROVIDER_H_
#include "base/base_export.h"
-#include "base/trace_event/memory_allocator_attributes.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/memory_allocator_attributes_type_info.h"
namespace base {
+
+class SingleThreadTaskRunner;
+
namespace trace_event {
class ProcessMemoryDump;
@@ -16,27 +20,46 @@
// The contract interface that memory dump providers must implement.
class BASE_EXPORT MemoryDumpProvider {
public:
- // Called by the MemoryDumpManager when generating dump points.
+ // Called by the MemoryDumpManager when generating memory dumps.
// Returns: true if the |pmd| was successfully populated, false otherwise.
virtual bool DumpInto(ProcessMemoryDump* pmd) = 0;
virtual const char* GetFriendlyName() const = 0;
- const MemoryAllocatorDeclaredAttributes& allocator_attributes() const {
- return allocator_attributes_;
+ const MemoryAllocatorAttributesTypeInfo& allocator_attributes_type_info()
+ const {
+ return allocator_attributes_type_info_;
+ }
+
+ // The dump provider can specify an optional thread affinity (in its
+ // base constructor call). If |task_runner| is non empty, all the calls to
+ // DumpInto are guaranteed to be posted to that TaskRunner.
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner() const {
+ return task_runner_;
}
protected:
+ // Default ctor: the MDP is not bound to any thread (must be a singleton).
MemoryDumpProvider();
+
+ // Use this ctor to ensure that DumpInto() is called always on the same thread
+ // specified by |task_runner|.
+ explicit MemoryDumpProvider(
+ const scoped_refptr<SingleThreadTaskRunner>& task_runner);
+
virtual ~MemoryDumpProvider();
- void DeclareAllocatorAttribute(const MemoryAllocatorDeclaredAttribute& attr);
+ void DeclareAllocatorAttribute(const std::string& allocator_name,
+ const std::string& attribute_name,
+ const std::string& attribute_type);
private:
- // The map (attribute name -> type) that specifies the semantic of the
- // extra attributes that the MemoryAllocatorDump(s) produced by this
- // MemoryDumpProvider will have.
- MemoryAllocatorDeclaredAttributes allocator_attributes_;
+ // A map of attributes types (declared through DeclareAllocatorAttribute())
+ // emitted by this allocator dumper.
+ MemoryAllocatorAttributesTypeInfo allocator_attributes_type_info_;
+
+ // (Optional) TaskRunner on which the DumpInfo call should be posted.
+ scoped_refptr<SingleThreadTaskRunner> task_runner_;
DISALLOW_COPY_AND_ASSIGN(MemoryDumpProvider);
};
diff --git a/base/trace_event/memory_dump_request_args.h b/base/trace_event/memory_dump_request_args.h
new file mode 100644
index 0000000..4fb0335
--- /dev/null
+++ b/base/trace_event/memory_dump_request_args.h
@@ -0,0 +1,41 @@
+// 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_REQUEST_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_H_
+
+// This file defines the types and structs used to issue memory dump requests.
+// These are also used in the IPCs for coordinating inter-process memory dumps.
+
+#include "base/base_export.h"
+#include "base/callback.h"
+
+namespace base {
+namespace trace_event {
+
+// Captures the reason why a memory dump is being requested. This is to allow
+// selective enabling of dumps, filtering and post-processing.
+enum class MemoryDumpType {
+ 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.
+ LAST = EXPLICITLY_TRIGGERED // For IPC macros.
+};
+
+using MemoryDumpCallback = Callback<void(uint64 dump_guid, bool success)>;
+
+struct BASE_EXPORT MemoryDumpRequestArgs {
+ // Globally unique identifier. In multi-process dumps, all processes issue a
+ // local dump with the same guid. This allows the trace importers to
+ // reconstruct the global dump.
+ uint64 dump_guid;
+
+ MemoryDumpType dump_type;
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_MEMORY_DUMP_REQUEST_H_
diff --git a/base/trace_event/memory_dump_session_state.cc b/base/trace_event/memory_dump_session_state.cc
new file mode 100644
index 0000000..433ac14
--- /dev/null
+++ b/base/trace_event/memory_dump_session_state.cc
@@ -0,0 +1,17 @@
+// 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_session_state.h"
+
+namespace base {
+namespace trace_event {
+
+MemoryDumpSessionState::MemoryDumpSessionState() {
+}
+
+MemoryDumpSessionState::~MemoryDumpSessionState() {
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/memory_dump_session_state.h b/base/trace_event/memory_dump_session_state.h
new file mode 100644
index 0000000..38a6fe2
--- /dev/null
+++ b/base/trace_event/memory_dump_session_state.h
@@ -0,0 +1,31 @@
+// 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_SESSION_STATE_H_
+#define BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/memory/ref_counted.h"
+#include "base/trace_event/memory_allocator_attributes_type_info.h"
+
+namespace base {
+namespace trace_event {
+
+class BASE_EXPORT MemoryDumpSessionState
+ : public RefCountedThreadSafe<MemoryDumpSessionState> {
+ public:
+ MemoryDumpSessionState();
+ MemoryAllocatorAttributesTypeInfo allocators_attributes_type_info;
+
+ private:
+ friend class RefCountedThreadSafe<MemoryDumpSessionState>;
+ ~MemoryDumpSessionState();
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_MEMORY_DUMP_SESSION_STATE_H_
diff --git a/base/trace_event/process_memory_dump.cc b/base/trace_event/process_memory_dump.cc
index bbca36c..836d6ae 100644
--- a/base/trace_event/process_memory_dump.cc
+++ b/base/trace_event/process_memory_dump.cc
@@ -10,31 +10,32 @@
namespace base {
namespace trace_event {
-ProcessMemoryDump::ProcessMemoryDump()
- : has_process_totals_(false), has_process_mmaps_(false) {
+ProcessMemoryDump::ProcessMemoryDump(
+ const scoped_refptr<MemoryDumpSessionState>& session_state)
+ : has_process_totals_(false),
+ has_process_mmaps_(false),
+ session_state_(session_state) {
}
ProcessMemoryDump::~ProcessMemoryDump() {
}
MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
- const std::string& name) {
- return CreateAllocatorDump(name, nullptr);
-}
-
-MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
- const std::string& name,
- MemoryAllocatorDump* parent) {
- DCHECK_EQ(0ul, allocator_dumps_.count(name));
- MemoryAllocatorDump* mad = new MemoryAllocatorDump(name, parent);
+ const std::string& allocator_name,
+ const std::string& heap_name) {
+ MemoryAllocatorDump* mad =
+ new MemoryAllocatorDump(allocator_name, heap_name, this);
+ DCHECK_EQ(0ul, allocator_dumps_.count(mad->GetAbsoluteName()));
allocator_dumps_storage_.push_back(mad);
- allocator_dumps_[name] = mad;
+ allocator_dumps_[mad->GetAbsoluteName()] = mad;
return mad;
}
MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
- const std::string& name) const {
- auto it = allocator_dumps_.find(name);
+ const std::string& allocator_name,
+ const std::string& heap_name) const {
+ auto it = allocator_dumps_.find(
+ MemoryAllocatorDump::GetAbsoluteName(allocator_name, heap_name));
return it == allocator_dumps_.end() ? nullptr : it->second;
}
diff --git a/base/trace_event/process_memory_dump.h b/base/trace_event/process_memory_dump.h
index df6cc82..bd9c543 100644
--- a/base/trace_event/process_memory_dump.h
+++ b/base/trace_event/process_memory_dump.h
@@ -8,8 +8,10 @@
#include "base/base_export.h"
#include "base/containers/hash_tables.h"
#include "base/containers/small_map.h"
+#include "base/memory/ref_counted.h"
#include "base/memory/scoped_vector.h"
#include "base/trace_event/memory_allocator_dump.h"
+#include "base/trace_event/memory_dump_session_state.h"
#include "base/trace_event/process_memory_maps.h"
#include "base/trace_event/process_memory_totals.h"
@@ -18,19 +20,22 @@
class ConvertableToTraceFormat;
class MemoryDumpManager;
+class MemoryDumpSessionState;
// ProcessMemoryDump is as a strongly typed container which enforces the data
-// model for each memory dump point and holds the dumps produced by the
+// model for each memory dump and holds the dumps produced by the
// MemoryDumpProvider(s) for a specific process.
// At trace generation time (i.e. when AsValue() is called), ProcessMemoryDump
// will compose a key-value dictionary of the various dumps obtained at trace
// dump point time.
class BASE_EXPORT ProcessMemoryDump {
public:
+ // Maps allocator dumps absolute names (allocator_name/heap/subheap) to
+ // MemoryAllocatorDump instances.
using AllocatorDumpsMap =
SmallMap<hash_map<std::string, MemoryAllocatorDump*>>;
- ProcessMemoryDump();
+ ProcessMemoryDump(const scoped_refptr<MemoryDumpSessionState>& session_state);
~ProcessMemoryDump();
// Called at trace generation time to populate the TracedValue.
@@ -45,20 +50,34 @@
void set_has_process_mmaps() { has_process_mmaps_ = true; }
// Creates a new MemoryAllocatorDump with the given name and returns the
- // empty object back to the caller. The |name| must be unique in the dump.
- // ProcessMemoryDump handles the memory ownership of the created object.
- // |parent| can be used to specify a hierarchical relationship of the
- // allocator dumps.
- MemoryAllocatorDump* CreateAllocatorDump(const std::string& name);
- MemoryAllocatorDump* CreateAllocatorDump(const std::string& name,
- MemoryAllocatorDump* parent);
+ // empty object back to the caller.
+ // Arguments:
+ // allocator_name: a name that univocally identifies allocator dumps
+ // produced by this provider. It acts as a type w.r.t. the allocator
+ // attributes, in the sense that all the MAD with the same allocator_name
+ // are expected to have the same attributes.
+ // heap_name, either:
+ // - kRootHeap: if the allocator has only one default heap.
+ // - a string identifing a heap name (e.g., isolate1, isolate2 ...). It is
+ // possible to specify nesting by using a path-like string (e.g.,
+ // isolate1/heap_spaceX, isolate1/heap_spaceY, isolate2/heap_spaceX).
+ // The tuple (|allocator_name|, |heap_name|) is unique inside a PMD.
+ // ProcessMemoryDump handles the memory ownership of its MemoryAllocatorDumps.
+ MemoryAllocatorDump* CreateAllocatorDump(const std::string& allocator_name,
+ const std::string& heap_name);
- // Returns a MemoryAllocatorDump given its name or nullptr if not found.
- MemoryAllocatorDump* GetAllocatorDump(const std::string& name) const;
+ // Looks up a MemoryAllocatorDump given its allocator and heap names, or
+ // nullptr if not found.
+ MemoryAllocatorDump* GetAllocatorDump(const std::string& allocator_name,
+ const std::string& heap_name) const;
// Returns the map of the MemoryAllocatorDumps added to this dump.
const AllocatorDumpsMap& allocator_dumps() const { return allocator_dumps_; }
+ const scoped_refptr<MemoryDumpSessionState>& session_state() const {
+ return session_state_;
+ }
+
private:
ProcessMemoryTotals process_totals_;
bool has_process_totals_;
@@ -66,13 +85,14 @@
ProcessMemoryMaps process_mmaps_;
bool has_process_mmaps_;
- // A maps of "allocator_name" -> MemoryAllocatorDump populated by
- // allocator dump providers.
AllocatorDumpsMap allocator_dumps_;
// ProcessMemoryDump handles the memory ownership of all its belongings.
ScopedVector<MemoryAllocatorDump> allocator_dumps_storage_;
+ // State shared among all PMDs instances created in a given trace session.
+ scoped_refptr<MemoryDumpSessionState> session_state_;
+
DISALLOW_COPY_AND_ASSIGN(ProcessMemoryDump);
};
diff --git a/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
index 0bf81ac..2ce2504 100644
--- a/base/trace_event/process_memory_maps_dump_provider_unittest.cc
+++ b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
@@ -114,7 +114,7 @@
auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance();
// Emulate a non-existent /proc/self/smaps.
- ProcessMemoryDump pmd_invalid;
+ ProcessMemoryDump pmd_invalid(nullptr /* session_state */);
std::ifstream non_existent_file("/tmp/does-not-exist");
ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &non_existent_file;
CHECK_EQ(false, non_existent_file.good());
@@ -129,7 +129,7 @@
ASSERT_FALSE(pmd_invalid.has_process_mmaps());
// Parse the 1st smaps file.
- ProcessMemoryDump pmd_1;
+ ProcessMemoryDump pmd_1(nullptr /* session_state */);
std::istringstream test_smaps_1(kTestSmaps1);
ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_1;
pmmdp->DumpInto(&pmd_1);
@@ -154,7 +154,7 @@
EXPECT_EQ((60 + 8) * 1024UL, regions_1[1].byte_stats_private_resident);
// Parse the 2nd smaps file.
- ProcessMemoryDump pmd_2;
+ ProcessMemoryDump pmd_2(nullptr /* session_state */);
std::istringstream test_smaps_2(kTestSmaps2);
ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_2;
pmmdp->DumpInto(&pmd_2);
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
index 372db63..ffaf177 100644
--- a/base/trace_event/process_memory_totals_dump_provider_unittest.cc
+++ b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -13,8 +13,8 @@
TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
auto pmtdp = ProcessMemoryTotalsDumpProvider::GetInstance();
- scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump());
- scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump());
+ scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump(nullptr));
+ scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump(nullptr));
ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
pmtdp->DumpInto(pmd_before.get());
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
index ca6c076..9bbd172 100644
--- a/base/trace_event/trace_event.gypi
+++ b/base/trace_event/trace_event.gypi
@@ -4,13 +4,19 @@
{
'variables': {
'trace_event_sources' : [
- 'trace_event/memory_allocator_attributes.h',
+ 'trace_event/java_heap_dump_provider_android.cc',
+ 'trace_event/java_heap_dump_provider_android.h',
+ 'trace_event/memory_allocator_attributes_type_info.cc',
+ 'trace_event/memory_allocator_attributes_type_info.h',
'trace_event/memory_allocator_dump.cc',
'trace_event/memory_allocator_dump.h',
'trace_event/memory_dump_manager.cc',
'trace_event/memory_dump_manager.h',
'trace_event/memory_dump_provider.cc',
'trace_event/memory_dump_provider.h',
+ 'trace_event/memory_dump_request_args.h',
+ 'trace_event/memory_dump_session_state.cc',
+ 'trace_event/memory_dump_session_state.h',
'trace_event/process_memory_dump.cc',
'trace_event/process_memory_dump.h',
'trace_event/process_memory_maps.cc',
@@ -25,6 +31,8 @@
'trace_event/trace_event_android.cc',
'trace_event/trace_event_argument.cc',
'trace_event/trace_event_argument.h',
+ 'trace_event/trace_event_etw_export_win.cc',
+ 'trace_event/trace_event_etw_export_win.h',
'trace_event/trace_event_impl.cc',
'trace_event/trace_event_impl.h',
'trace_event/trace_event_impl_constants.cc',
@@ -36,8 +44,19 @@
'trace_event/trace_event_system_stats_monitor.h',
'trace_event/trace_event_win.cc',
'trace_event/trace_event_win.h',
+ 'trace_event/winheap_dump_provider_win.cc',
+ 'trace_event/winheap_dump_provider_win.h',
+ ],
+ 'conditions': [
+ ['OS == "linux" or OS == "android"', {
+ 'trace_event_sources': [
+ 'trace_event/malloc_dump_provider.cc',
+ 'trace_event/malloc_dump_provider.h',
+ ],
+ }],
],
'trace_event_test_sources' : [
+ 'trace_event/memory_allocator_attributes_type_info_unittest.cc',
'trace_event/memory_allocator_dump_unittest.cc',
'trace_event/memory_dump_manager_unittest.cc',
'trace_event/process_memory_maps_dump_provider_unittest.cc',
@@ -48,6 +67,7 @@
'trace_event/trace_event_system_stats_monitor_unittest.cc',
'trace_event/trace_event_unittest.cc',
'trace_event/trace_event_win_unittest.cc',
+ 'trace_event/winheap_dump_provider_win_unittest.cc',
],
},
}
diff --git a/base/trace_event/trace_event.h b/base/trace_event/trace_event.h
index 1bf9429..e0249f5 100644
--- a/base/trace_event/trace_event.h
+++ b/base/trace_event/trace_event.h
@@ -144,11 +144,11 @@
// class MyData : public base::trace_event::ConvertableToTraceFormat {
// public:
// MyData() {}
-// virtual void AppendAsTraceFormat(std::string* out) const override {
+// void AppendAsTraceFormat(std::string* out) const override {
// out->append("{\"foo\":1}");
// }
// private:
-// virtual ~MyData() {}
+// ~MyData() override {}
// DISALLOW_COPY_AND_ASSIGN(MyData);
// };
//
@@ -601,6 +601,12 @@
TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \
static_cast<int>(base::PlatformThread::CurrentId()), \
timestamp, TRACE_EVENT_FLAG_NONE)
+#define TRACE_EVENT_COPY_ASYNC_BEGIN_WITH_TIMESTAMP0(category_group, \
+ name, id, timestamp) \
+ INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
+ TRACE_EVENT_PHASE_ASYNC_BEGIN, category_group, name, id, \
+ static_cast<int>(base::PlatformThread::CurrentId()), \
+ timestamp, TRACE_EVENT_FLAG_COPY)
// Records a single ASYNC_STEP_INTO event for |step| immediately. If the
// category is not enabled, then this does nothing. The |name| and |id| must
@@ -828,9 +834,10 @@
category_group, name, TRACE_ID_DONT_MANGLE(id), TRACE_EVENT_FLAG_NONE)
#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \
- UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
- (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \
- base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK))
+ UNLIKELY(*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
+ (base::trace_event::TraceLog::ENABLED_FOR_RECORDING | \
+ base::trace_event::TraceLog::ENABLED_FOR_EVENT_CALLBACK | \
+ base::trace_event::TraceLog::ENABLED_FOR_ETW_EXPORT))
// Macro to efficiently determine if a given category group is enabled.
#define TRACE_EVENT_CATEGORY_GROUP_ENABLED(category_group, ret) \
diff --git a/base/trace_event/trace_event_etw_export_win.cc b/base/trace_event/trace_event_etw_export_win.cc
new file mode 100644
index 0000000..f7f9ecc
--- /dev/null
+++ b/base/trace_event/trace_event_etw_export_win.cc
@@ -0,0 +1,239 @@
+// 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/trace_event_etw_export_win.h"
+
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/memory/singleton.h"
+#include "base/strings/utf_string_conversions.h"
+#include "base/trace_event/trace_event.h"
+#include "base/trace_event/trace_event_impl.h"
+
+// The GetProcAddress technique is borrowed from
+// https://github.com/randomascii/main/tree/master/xperf/ETWProviders
+//
+// EVNTAPI is used in evntprov.h which is included by chrome_events_win.h.
+// We define EVNTAPI without the DECLSPEC_IMPORT specifier so that we can
+// implement these functions locally instead of using the import library, and
+// can therefore still run on Windows XP.
+#define EVNTAPI __stdcall
+// Include the event register/write/unregister macros compiled from the manifest
+// file. Note that this includes evntprov.h which requires a Vista+ Windows SDK.
+//
+// In SHARED_INTERMEDIATE_DIR.
+#include "base/trace_event/etw_manifest/chrome_events_win.h" // NOLINT
+
+namespace {
+// Typedefs for use with GetProcAddress
+typedef ULONG(__stdcall* tEventRegister)(LPCGUID ProviderId,
+ PENABLECALLBACK EnableCallback,
+ PVOID CallbackContext,
+ PREGHANDLE RegHandle);
+typedef ULONG(__stdcall* tEventWrite)(REGHANDLE RegHandle,
+ PCEVENT_DESCRIPTOR EventDescriptor,
+ ULONG UserDataCount,
+ PEVENT_DATA_DESCRIPTOR UserData);
+typedef ULONG(__stdcall* tEventUnregister)(REGHANDLE RegHandle);
+
+tEventRegister EventRegisterProc = nullptr;
+tEventWrite EventWriteProc = nullptr;
+tEventUnregister EventUnregisterProc = nullptr;
+} // namespace
+
+// Redirector function for EventRegister. Called by macros in
+// chrome_events_win.h
+ULONG EVNTAPI EventRegister(LPCGUID ProviderId,
+ PENABLECALLBACK EnableCallback,
+ PVOID CallbackContext,
+ PREGHANDLE RegHandle) {
+ if (EventRegisterProc)
+ return EventRegisterProc(ProviderId, EnableCallback, CallbackContext,
+ RegHandle);
+ return 0;
+}
+
+// Redirector function for EventWrite. Called by macros in
+// chrome_events_win.h
+ULONG EVNTAPI EventWrite(REGHANDLE RegHandle,
+ PCEVENT_DESCRIPTOR EventDescriptor,
+ ULONG UserDataCount,
+ PEVENT_DATA_DESCRIPTOR UserData) {
+ if (EventWriteProc)
+ return EventWriteProc(RegHandle, EventDescriptor, UserDataCount, UserData);
+ return 0;
+}
+
+// Redirector function for EventUnregister. Called by macros in
+// chrome_events_win.h
+ULONG EVNTAPI EventUnregister(REGHANDLE RegHandle) {
+ if (EventUnregisterProc)
+ return EventUnregisterProc(RegHandle);
+ return 0;
+}
+
+namespace base {
+namespace trace_event {
+
+TraceEventETWExport::TraceEventETWExport() : ETWExportEnabled_(false) {
+ // Find Advapi32.dll. This should always succeed.
+ HMODULE AdvapiDLL = ::LoadLibraryW(L"Advapi32.dll");
+ if (AdvapiDLL) {
+ // Try to find the ETW functions. This will fail on XP.
+ EventRegisterProc = reinterpret_cast<tEventRegister>(
+ ::GetProcAddress(AdvapiDLL, "EventRegister"));
+ EventWriteProc = reinterpret_cast<tEventWrite>(
+ ::GetProcAddress(AdvapiDLL, "EventWrite"));
+ EventUnregisterProc = reinterpret_cast<tEventUnregister>(
+ ::GetProcAddress(AdvapiDLL, "EventUnregister"));
+
+ // Register the ETW provider. If registration fails then the event logging
+ // calls will fail (on XP this call will do nothing).
+ EventRegisterChrome();
+ }
+}
+
+TraceEventETWExport::~TraceEventETWExport() {
+ EventUnregisterChrome();
+}
+
+// static
+TraceEventETWExport* TraceEventETWExport::GetInstance() {
+ return Singleton<TraceEventETWExport,
+ StaticMemorySingletonTraits<TraceEventETWExport>>::get();
+}
+
+// static
+void TraceEventETWExport::EnableETWExport() {
+ GetInstance()->ETWExportEnabled_ = true;
+}
+
+// static
+void TraceEventETWExport::DisableETWExport() {
+ GetInstance()->ETWExportEnabled_ = false;
+}
+
+// static
+void TraceEventETWExport::AddEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values) {
+ // We bail early in case exporting is disabled or no consumer is listening.
+ if (!GetInstance()->ETWExportEnabled_ || !EventEnabledChromeEvent())
+ return;
+
+ std::string phase_string;
+ switch (phase) {
+ case TRACE_EVENT_PHASE_BEGIN:
+ phase_string = "Begin";
+ break;
+ case TRACE_EVENT_PHASE_END:
+ phase_string = "End";
+ break;
+ case TRACE_EVENT_PHASE_COMPLETE:
+ phase_string = "Complete";
+ break;
+ case TRACE_EVENT_PHASE_INSTANT:
+ phase_string = "Instant";
+ break;
+ case TRACE_EVENT_PHASE_ASYNC_BEGIN:
+ phase_string = "Async Begin";
+ break;
+ case TRACE_EVENT_PHASE_ASYNC_STEP_INTO:
+ phase_string = "Async Step Into";
+ break;
+ case TRACE_EVENT_PHASE_ASYNC_STEP_PAST:
+ phase_string = "Async Step Past";
+ break;
+ case TRACE_EVENT_PHASE_ASYNC_END:
+ phase_string = "Async End";
+ break;
+ case TRACE_EVENT_PHASE_NESTABLE_ASYNC_BEGIN:
+ phase_string = "Nestable Async Begin";
+ break;
+ case TRACE_EVENT_PHASE_NESTABLE_ASYNC_END:
+ phase_string = "Nestable Async End";
+ break;
+ case TRACE_EVENT_PHASE_NESTABLE_ASYNC_INSTANT:
+ phase_string = "Nestable Async Instant";
+ break;
+ case TRACE_EVENT_PHASE_FLOW_BEGIN:
+ phase_string = "Phase Flow Begin";
+ break;
+ case TRACE_EVENT_PHASE_FLOW_STEP:
+ phase_string = "Phase Flow Step";
+ break;
+ case TRACE_EVENT_PHASE_FLOW_END:
+ phase_string = "Phase Flow End";
+ break;
+ case TRACE_EVENT_PHASE_METADATA:
+ phase_string = "Phase Metadata";
+ break;
+ case TRACE_EVENT_PHASE_COUNTER:
+ phase_string = "Phase Counter";
+ break;
+ case TRACE_EVENT_PHASE_SAMPLE:
+ phase_string = "Phase Sample";
+ break;
+ case TRACE_EVENT_PHASE_CREATE_OBJECT:
+ phase_string = "Phase Create Object";
+ break;
+ case TRACE_EVENT_PHASE_SNAPSHOT_OBJECT:
+ phase_string = "Phase Snapshot Object";
+ break;
+ case TRACE_EVENT_PHASE_DELETE_OBJECT:
+ phase_string = "Phase Delete Object";
+ break;
+ default:
+ phase_string.push_back(phase);
+ break;
+ }
+
+ std::string arg_values_string[3];
+ for (int i = 0; i < num_args; i++) {
+ if (arg_types[i] == TRACE_VALUE_TYPE_CONVERTABLE) {
+ convertable_values[i]->AppendAsTraceFormat(arg_values_string + i);
+ } else {
+ TraceEvent::TraceValue trace_event;
+ trace_event.as_uint = arg_values[i];
+ TraceEvent::AppendValueAsJSON(arg_types[i], trace_event,
+ arg_values_string + i);
+ }
+ }
+
+ EventWriteChromeEvent(
+ name, phase_string.c_str(), num_args > 0 ? arg_names[0] : "",
+ arg_values_string[0].c_str(), num_args > 1 ? arg_names[1] : "",
+ arg_values_string[1].c_str(), num_args > 2 ? arg_names[2] : "",
+ arg_values_string[2].c_str());
+}
+
+// static
+void TraceEventETWExport::AddCustomEvent(const char* name,
+ char const* phase,
+ const char* arg_name_1,
+ const char* arg_value_1,
+ const char* arg_name_2,
+ const char* arg_value_2,
+ const char* arg_name_3,
+ const char* arg_value_3) {
+ if (!GetInstance()->ETWExportEnabled_ || !EventEnabledChromeEvent())
+ return;
+
+ EventWriteChromeEvent(name, phase, arg_name_1, arg_value_1, arg_name_2,
+ arg_value_2, arg_name_3, arg_value_3);
+}
+
+void TraceEventETWExport::Resurrect() {
+ StaticMemorySingletonTraits<TraceEventETWExport>::Resurrect();
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/trace_event_etw_export_win.h b/base/trace_event/trace_event_etw_export_win.h
new file mode 100644
index 0000000..0a551c3
--- /dev/null
+++ b/base/trace_event/trace_event_etw_export_win.h
@@ -0,0 +1,73 @@
+// 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.
+
+// This file contains the Windows-specific exporting to ETW.
+#ifndef BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_
+#define BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_
+
+#include "base/base_export.h"
+#include "base/trace_event/trace_event_impl.h"
+
+// Fwd.
+template <typename Type>
+struct StaticMemorySingletonTraits;
+
+namespace base {
+namespace trace_event {
+
+class BASE_EXPORT TraceEventETWExport {
+ public:
+ ~TraceEventETWExport();
+
+ // Retrieves the singleton.
+ // Note that this may return NULL post-AtExit processing.
+ static TraceEventETWExport* GetInstance();
+
+ // Enables/disables exporting of events to ETW. If disabled,
+ // AddEvent and AddCustomEvent will simply return when called.
+ static void EnableETWExport();
+ static void DisableETWExport();
+
+ static bool isETWExportEnabled() { return GetInstance()->ETWExportEnabled_; }
+
+ // Exports an event to ETW. This is mainly used in
+ // TraceLog::AddTraceEventWithThreadIdAndTimestamp to export internal events.
+ static void AddEvent(
+ char phase,
+ const unsigned char* category_group_enabled,
+ const char* name,
+ unsigned long long id,
+ int num_args,
+ const char** arg_names,
+ const unsigned char* arg_types,
+ const unsigned long long* arg_values,
+ const scoped_refptr<ConvertableToTraceFormat>* convertable_values);
+
+ // Exports an event to ETW. This should be used when exporting an event only
+ // to ETW. Supports three arguments to be passed to ETW.
+ // TODO(georgesak): Allow different providers.
+ static void AddCustomEvent(const char* name,
+ char const* phase,
+ const char* arg_name_1,
+ const char* arg_value_1,
+ const char* arg_name_2,
+ const char* arg_value_2,
+ const char* arg_name_3,
+ const char* arg_value_3);
+
+ void Resurrect();
+
+ private:
+ bool ETWExportEnabled_;
+ // Ensure only the provider can construct us.
+ friend struct StaticMemorySingletonTraits<TraceEventETWExport>;
+ TraceEventETWExport();
+
+ DISALLOW_COPY_AND_ASSIGN(TraceEventETWExport);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_TRACE_ETW_EXPORT_H_
diff --git a/base/trace_event/trace_event_impl.cc b/base/trace_event/trace_event_impl.cc
index 72b46f9..cbeeeab 100644
--- a/base/trace_event/trace_event_impl.cc
+++ b/base/trace_event/trace_event_impl.cc
@@ -5,12 +5,12 @@
#include "base/trace_event/trace_event_impl.h"
#include <algorithm>
+#include <cmath>
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/leak_annotations.h"
-#include "base/float_util.h"
#include "base/format_macros.h"
#include "base/json/string_escape.h"
#include "base/lazy_instance.h"
@@ -30,11 +30,13 @@
#include "base/third_party/dynamic_annotations/dynamic_annotations.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread_id_name_manager.h"
+#include "base/threading/worker_pool.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/trace_event/trace_event_synthetic_delay.h"
#if defined(OS_WIN)
+#include "base/trace_event/trace_event_etw_export_win.h"
#include "base/trace_event/trace_event_win.h"
#endif
@@ -73,7 +75,7 @@
512000000 / kTraceBufferChunkSize;
const size_t kTraceEventVectorBufferChunks = 256000 / kTraceBufferChunkSize;
const size_t kTraceEventRingBufferChunks = kTraceEventVectorBufferChunks / 4;
-const size_t kTraceEventBatchChunks = 1000 / kTraceBufferChunkSize;
+const size_t kTraceEventBufferSizeInBytes = 100 * 1024;
// Can store results for 30 seconds with 1 ms sampling interval.
const size_t kMonitorTraceEventBufferChunks = 30000 / kTraceBufferChunkSize;
// ECHO_TO_CONSOLE needs a small buffer to hold the unfinished COMPLETE events.
@@ -645,7 +647,7 @@
// should be made into a common method.
std::string real;
double val = value.as_double;
- if (IsFinite(val)) {
+ if (std::isfinite(val)) {
real = DoubleToString(val);
// Ensure that the number has a .0 if there's no decimal or 'e'. This
// makes sure that when we read the JSON back, it's interpreted as a
@@ -663,7 +665,7 @@
// "-.1" bad "-0.1" good
real.insert(1, "0");
}
- } else if (IsNaN(val)){
+ } else if (std::isnan(val)){
// The JSON spec doesn't allow NaN and Infinity (since these are
// objects in EcmaScript). Use strings instead.
real = "\"NaN\"";
@@ -1208,7 +1210,8 @@
event_callback_category_filter_(
CategoryFilter::kDefaultCategoryFilterString),
thread_shared_chunk_index_(0),
- generation_(0) {
+ generation_(0),
+ use_worker_thread_(false) {
// Trace is enabled or disabled on one thread while other threads are
// accessing the enabled flag. We don't care whether edge-case events are
// traced or not, so we allow races on the enabled flag to keep the trace
@@ -1290,6 +1293,11 @@
if (event_callback_ &&
event_callback_category_filter_.IsCategoryGroupEnabled(category_group))
enabled_flag |= ENABLED_FOR_EVENT_CALLBACK;
+#if defined(OS_WIN)
+ if (base::trace_event::TraceEventETWExport::isETWExportEnabled())
+ enabled_flag |= ENABLED_FOR_ETW_EXPORT;
+#endif
+
g_category_group_enabled[category_index] = enabled_flag;
}
@@ -1681,7 +1689,9 @@
// - The message loop will be removed from thread_message_loops_;
// If this is the last message loop, finish the flush;
// 4. If any thread hasn't finish its flush in time, finish the flush.
-void TraceLog::Flush(const TraceLog::OutputCallback& cb) {
+void TraceLog::Flush(const TraceLog::OutputCallback& cb,
+ bool use_worker_thread) {
+ use_worker_thread_ = use_worker_thread;
if (IsEnabled()) {
// Can't flush when tracing is enabled because otherwise PostTask would
// - generate more trace events;
@@ -1735,6 +1745,7 @@
FinishFlush(generation);
}
+// Usually it runs on a different thread.
void TraceLog::ConvertTraceEventsToTraceFormat(
scoped_ptr<TraceBuffer> logged_events,
const TraceLog::OutputCallback& flush_output_callback) {
@@ -1749,19 +1760,17 @@
scoped_refptr<RefCountedString> json_events_str_ptr =
new RefCountedString();
- for (size_t i = 0; i < kTraceEventBatchChunks; ++i) {
+ while (json_events_str_ptr->size() < kTraceEventBufferSizeInBytes) {
const TraceBufferChunk* chunk = logged_events->NextChunk();
- if (!chunk) {
- has_more_events = false;
+ has_more_events = chunk != NULL;
+ if (!chunk)
break;
- }
for (size_t j = 0; j < chunk->size(); ++j) {
- if (i > 0 || j > 0)
+ if (json_events_str_ptr->size())
json_events_str_ptr->data().append(",\n");
chunk->GetEventAt(j)->AppendAsJSON(&(json_events_str_ptr->data()));
}
}
-
flush_output_callback.Run(json_events_str_ptr, has_more_events);
} while (has_more_events);
}
@@ -1785,6 +1794,16 @@
flush_output_callback_.Reset();
}
+ if (use_worker_thread_ &&
+ WorkerPool::PostTask(
+ FROM_HERE,
+ Bind(&TraceLog::ConvertTraceEventsToTraceFormat,
+ Passed(&previous_logged_events),
+ flush_output_callback),
+ true)) {
+ return;
+ }
+
ConvertTraceEventsToTraceFormat(previous_logged_events.Pass(),
flush_output_callback);
}
@@ -1908,7 +1927,7 @@
DCHECK(!timestamp.is_null());
if (flags & TRACE_EVENT_FLAG_MANGLE_ID)
- id ^= process_id_hash_;
+ id = MangleEventId(id);
TimeTicks offset_event_timestamp = OffsetTimestamp(timestamp);
TimeTicks now = flags & TRACE_EVENT_FLAG_EXPLICIT_TIMESTAMP ?
@@ -1971,6 +1990,15 @@
}
}
+#if defined(OS_WIN)
+ // This is done sooner rather than later, to avoid creating the event and
+ // acquiring the lock, which is not needed for ETW as it's already threadsafe.
+ if (*category_group_enabled & ENABLED_FOR_ETW_EXPORT)
+ TraceEventETWExport::AddEvent(phase, category_group_enabled, name, id,
+ num_args, arg_names, arg_types, arg_values,
+ convertable_values);
+#endif // OS_WIN
+
std::string console_message;
if (*category_group_enabled &
(ENABLED_FOR_RECORDING | ENABLED_FOR_MONITORING)) {
@@ -2177,6 +2205,10 @@
watch_event_callback_.Reset();
}
+uint64 TraceLog::MangleEventId(uint64 id) {
+ return id ^ process_id_hash_;
+}
+
void TraceLog::AddMetadataEventsWhileLocked() {
lock_.AssertAcquired();
diff --git a/base/trace_event/trace_event_impl.h b/base/trace_event/trace_event_impl.h
index efa20c4..33a85c9 100644
--- a/base/trace_event/trace_event_impl.h
+++ b/base/trace_event/trace_event_impl.h
@@ -26,6 +26,7 @@
// Older style trace macros with explicit id and extra data
// Only these macros result in publishing data to ETW as currently implemented.
+// TODO(georgesak): Update/replace these with new ETW macros.
#define TRACE_EVENT_BEGIN_ETW(name, id, extra) \
base::trace_event::TraceLog::AddTraceEventEtw( \
TRACE_EVENT_PHASE_BEGIN, \
@@ -446,6 +447,8 @@
ENABLED_FOR_MONITORING = 1 << 1,
// Category group enabled by SetEventCallbackEnabled().
ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
+ // Category group enabled to export events to ETW.
+ ENABLED_FOR_ETW_EXPORT = 1 << 3
};
static TraceLog* GetInstance();
@@ -538,10 +541,11 @@
// Due to the implementation of thread-local buffers, flush can't be
// done when tracing is enabled. If called when tracing is enabled, the
// callback will be called directly with (empty_string, false) to indicate
- // the end of this unsuccessful flush.
+ // the end of this unsuccessful flush. Flush does the serialization
+ // on the same thread if the caller doesn't set use_worker_thread explicitly.
typedef base::Callback<void(const scoped_refptr<base::RefCountedString>&,
bool has_more_events)> OutputCallback;
- void Flush(const OutputCallback& cb);
+ void Flush(const OutputCallback& cb, bool use_worker_thread = false);
void FlushButLeaveBufferIntact(const OutputCallback& flush_output_callback);
// Called by TRACE_EVENT* macros, don't call this directly.
@@ -602,6 +606,8 @@
int process_id() const { return process_id_; }
+ uint64 MangleEventId(uint64 id);
+
// Exposed for unittesting:
void WaitSamplingEventForTesting();
@@ -710,7 +716,9 @@
// |generation| is used in the following callbacks to check if the callback
// is called for the flush of the current |logged_events_|.
void FlushCurrentThread(int generation);
- void ConvertTraceEventsToTraceFormat(scoped_ptr<TraceBuffer> logged_events,
+ // Usually it runs on a different thread.
+ static void ConvertTraceEventsToTraceFormat(
+ scoped_ptr<TraceBuffer> logged_events,
const TraceLog::OutputCallback& flush_output_callback);
void FinishFlush(int generation);
void OnFlushTimeout(int generation);
@@ -803,6 +811,7 @@
OutputCallback flush_output_callback_;
scoped_refptr<MessageLoopProxy> flush_message_loop_proxy_;
subtle::AtomicWord generation_;
+ bool use_worker_thread_;
DISALLOW_COPY_AND_ASSIGN(TraceLog);
};
diff --git a/base/trace_event/trace_event_win_unittest.cc b/base/trace_event/trace_event_win_unittest.cc
index 7f1004e..d4dc854 100644
--- a/base/trace_event/trace_event_win_unittest.cc
+++ b/base/trace_event/trace_event_win_unittest.cc
@@ -92,7 +92,7 @@
TraceEventWinTest() {
}
- void SetUp() {
+ void SetUp() override {
bool is_xp = win::GetVersion() < base::win::VERSION_VISTA;
if (is_xp) {
@@ -151,7 +151,7 @@
EXPECT_TRUE(tracelog->IsTracing());
}
- void TearDown() {
+ void TearDown() override {
EtwTraceProperties prop;
if (controller_.session() != 0)
EXPECT_HRESULT_SUCCEEDED(controller_.Stop(&prop));
diff --git a/base/trace_event/winheap_dump_provider_win.cc b/base/trace_event/winheap_dump_provider_win.cc
new file mode 100644
index 0000000..3a4fd87
--- /dev/null
+++ b/base/trace_event/winheap_dump_provider_win.cc
@@ -0,0 +1,103 @@
+// 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/winheap_dump_provider_win.h"
+
+#include <windows.h>
+
+#include "base/trace_event/process_memory_dump.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+
+const char kDumperFriendlyName[] = "winheap";
+
+// Report a heap dump to a process memory dump. The |heap_info| structure
+// contains the information about this heap, and |heap_name| will be used to
+// represent it in the report.
+bool ReportHeapDump(ProcessMemoryDump* pmd,
+ const WinHeapInfo& heap_info,
+ const std::string& heap_name) {
+ MemoryAllocatorDump* dump =
+ pmd->CreateAllocatorDump(kDumperFriendlyName, heap_name);
+ if (!dump)
+ return false;
+ dump->set_physical_size_in_bytes(heap_info.committed_size);
+ dump->set_allocated_objects_count(heap_info.block_count);
+ dump->set_allocated_objects_size_in_bytes(heap_info.allocated_size);
+ return true;
+}
+
+} // namespace
+
+WinHeapDumpProvider* WinHeapDumpProvider::GetInstance() {
+ return Singleton<WinHeapDumpProvider,
+ LeakySingletonTraits<WinHeapDumpProvider>>::get();
+}
+
+bool WinHeapDumpProvider::DumpInto(ProcessMemoryDump* pmd) {
+ // Retrieves the number of heaps in the current process.
+ DWORD number_of_heaps = ::GetProcessHeaps(0, NULL);
+ WinHeapInfo all_heap_info = {0};
+
+ // Try to retrieve a handle to all the heaps owned by this process. Returns
+ // false if the number of heaps has changed.
+ //
+ // This is inherently racy as is, but it's not something that we observe a lot
+ // in Chrome, the heaps tend to be created at startup only.
+ scoped_ptr<HANDLE[]> all_heaps(new HANDLE[number_of_heaps]);
+ if (::GetProcessHeaps(number_of_heaps, all_heaps.get()) != number_of_heaps)
+ return false;
+
+ // Skip the pointer to the heap array to avoid accounting the memory used by
+ // this dump provider.
+ std::set<void*> block_to_skip;
+ block_to_skip.insert(all_heaps.get());
+
+ // Retrieves some metrics about each heap.
+ for (size_t i = 0; i < number_of_heaps; ++i) {
+ WinHeapInfo heap_info = {0};
+ heap_info.heap_id = all_heaps[i];
+ GetHeapInformation(&heap_info, block_to_skip);
+
+ all_heap_info.allocated_size += heap_info.allocated_size;
+ all_heap_info.committed_size += heap_info.committed_size;
+ all_heap_info.block_count += heap_info.block_count;
+ }
+ // Report the heap dump.
+ if (!ReportHeapDump(pmd, all_heap_info, MemoryAllocatorDump::kRootHeap))
+ return false;
+
+ return true;
+}
+
+const char* WinHeapDumpProvider::GetFriendlyName() const {
+ return kDumperFriendlyName;
+}
+
+bool WinHeapDumpProvider::GetHeapInformation(
+ WinHeapInfo* heap_info,
+ const std::set<void*>& block_to_skip) {
+ CHECK(::HeapLock(heap_info->heap_id) == TRUE);
+ PROCESS_HEAP_ENTRY heap_entry;
+ heap_entry.lpData = nullptr;
+ // Walk over all the entries in this heap.
+ while (::HeapWalk(heap_info->heap_id, &heap_entry) != FALSE) {
+ if (block_to_skip.count(heap_entry.lpData) == 1)
+ continue;
+ if ((heap_entry.wFlags & PROCESS_HEAP_ENTRY_BUSY) != 0) {
+ heap_info->allocated_size += heap_entry.cbData;
+ heap_info->block_count++;
+ } else if ((heap_entry.wFlags & PROCESS_HEAP_REGION) != 0) {
+ heap_info->committed_size += heap_entry.Region.dwCommittedSize;
+ }
+ }
+ CHECK(::HeapUnlock(heap_info->heap_id) == TRUE);
+ return true;
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/winheap_dump_provider_win.h b/base/trace_event/winheap_dump_provider_win.h
new file mode 100644
index 0000000..8abac47
--- /dev/null
+++ b/base/trace_event/winheap_dump_provider_win.h
@@ -0,0 +1,53 @@
+// 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_WINHEAP_DUMP_PROVIDER_WIN_H_
+#define BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_
+
+#include <set>
+
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+namespace trace_event {
+
+// A structure containing some information about a given heap.
+struct WinHeapInfo {
+ HANDLE heap_id;
+ size_t committed_size;
+ size_t allocated_size;
+ size_t block_count;
+};
+
+// Dump provider which collects process-wide heap memory stats. This provider
+// iterates over all the heaps of the current process to gather some metrics
+// about them.
+class BASE_EXPORT WinHeapDumpProvider : public MemoryDumpProvider {
+ public:
+ static WinHeapDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ bool DumpInto(ProcessMemoryDump* pmd) override;
+ const char* GetFriendlyName() const override;
+
+ private:
+ friend struct DefaultSingletonTraits<WinHeapDumpProvider>;
+
+ // Retrieves the information about given heap. The |heap_info| should contain
+ // a valid handle to an existing heap. The blocks contained in the
+ // |block_to_skip| set will be ignored.
+ bool GetHeapInformation(WinHeapInfo* heap_info,
+ const std::set<void*>& block_to_skip);
+
+ WinHeapDumpProvider() {}
+ ~WinHeapDumpProvider() override {}
+
+ DISALLOW_COPY_AND_ASSIGN(WinHeapDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_WINHEAP_DUMP_PROVIDER_WIN_H_
diff --git a/base/trace_event/winheap_dump_provider_win_unittest.cc b/base/trace_event/winheap_dump_provider_win_unittest.cc
new file mode 100644
index 0000000..99da18a
--- /dev/null
+++ b/base/trace_event/winheap_dump_provider_win_unittest.cc
@@ -0,0 +1,27 @@
+// 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/winheap_dump_provider_win.h"
+
+#include <windows.h>
+
+#include "base/trace_event/memory_dump_session_state.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(WinHeapDumpProviderTest, DumpInto) {
+ ProcessMemoryDump pmd(make_scoped_refptr(new MemoryDumpSessionState()));
+
+ WinHeapDumpProvider* winheap_dump_provider =
+ WinHeapDumpProvider::GetInstance();
+ ASSERT_NE(static_cast<WinHeapDumpProvider*>(nullptr), winheap_dump_provider);
+
+ ASSERT_TRUE(winheap_dump_provider->DumpInto(&pmd));
+}
+
+} // namespace trace_event
+} // namespace base