summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authoroshima <oshima@chromium.org>2016-01-21 15:52:21 -0800
committerCommit bot <commit-bot@chromium.org>2016-01-21 23:53:56 +0000
commit94bef8d8bf199e23102783a55a15c9f10bc88631 (patch)
treea819b7e93665c1d196cc27ab507ef875786a11b5 /base
parent96eeb2b4f996606caa94f5e4baee43cc6d22b1c7 (diff)
downloadchromium_src-94bef8d8bf199e23102783a55a15c9f10bc88631.zip
chromium_src-94bef8d8bf199e23102783a55a15c9f10bc88631.tar.gz
chromium_src-94bef8d8bf199e23102783a55a15c9f10bc88631.tar.bz2
Revert of [tracing] Dump child processes' memory metrics in browser (patchset #1 id:1 of https://codereview.chromium.org/1591553002/ )
Reason for revert: TracingBrowserTest.TestMemoryInfra is failing on DrMemory bot with the following error: UNADDRESSABLE ACCESS: reading 0x00000048-0x0000004c 4 byte(s) # 0 base.dll!base::Thread::task_runner [base\threading\thread.h:168] # 1 base.dll!base::trace_event::MemoryDumpManager::CreateProcessDump [base\trace_event\memory_dump_manager.cc:313] # 2 content.dll!content::TracingControllerImpl::RequestGlobalMemoryDump [content\browser\tracing\tracing_controller_impl.cc:1039] # 3 content.dll!base::internal::Invoker<>::Run [base\bind_internal.h:350] bug: crbug.com/580295 Original issue's description: > Reland of [tracing] Dump child processes' memory metrics in browser (patchset #1 id:1 of https://codereview.chromium.org/1586893003/ ) > > The failure was due to flakiness fixed by crrev.com/1606983002. > See crbug.com/578128. > > Original issue's description: > > Revert of [tracing] Dump child processes' memory metrics in browser (patchset #15 id:400001 of https://codereview.chromium.org/1417003003/ ) > > > > Reason for revert: > > This is suspected by Findit to cause failure in TracingBrowserTest.TestMemoryInfra under Mac ASan 64 Tests (1). > > > > Original issue's description: > > > [tracing] Dump child processes' memory metrics in browser > > > > > > The sandbox in linux prevents the process from reading the process > > > metrics file. To work around this issue, the browser process will now > > > dump statistics of the child processes too. This requires the > > > composable dumps support in trace viewer and telemetry. > > > > > > This CL makes following changes. > > > 1. Move the process totals and memory maps dump provider into a single > > > header as process_metrics_dump_provider in components/tracing. This is > > > because it is not necessary to have this in base and now the provider > > > knows/manages for different processes. Also the dump method are made to > > > handle error better, since process can vanish while dumping. > > > > > > 2. Make the dump provider non-singleton and have a register / unregister > > > that manages the lifetime of the dump providers. > > > > > > 3. The dump providers unregister using the new > > > UnregisterAndDeleteDumpProviderAsync api added by crrev.com/1430073002. > > > > > > 4. On linux the browser process dumps metrics for all processes and on > > > android the child processes can dump since seccomp sandbox is not > > > enabled in android yet. > > > > > > 5. The proc/status file is human readable stats and is not guaranteed to > > > have a field that is asked for. So, the NOTREACHED is removed in > > > ReadProcStatusAndGetFieldAsSizeT. > > > > > > 6. Since we introduce other process dumps from browser process there > > > could be races while unregistering and dumping. To test this, the > > > browser test is updated. > > > > > > BUG=461788 > > > > > > Committed: https://crrev.com/4d77d76a42425282b1a3c5b7309db9b98e777f60 > > > Cr-Commit-Position: refs/heads/master@{#369482} > > > > TBR=primiano@chromium.org,thakis@chromium.org,simonhatch@chromium.org,sievers@chromium.org,ssid@chromium.org > > # Skipping CQ checks because original CL landed less than 1 days ago. > > NOPRESUBMIT=true > > NOTREECHECKS=true > > NOTRY=true > > BUG=461788 > > > > Committed: https://crrev.com/93aa967cfcb3e933000f169b9a4f7ac84dbd96da > > Cr-Commit-Position: refs/heads/master@{#369535} > > TBR=primiano@chromium.org,thakis@chromium.org,simonhatch@chromium.org,sievers@chromium.org,huangs@chromium.org > BUG=461788 > CQ_INCLUDE_TRYBOTS=tryserver.blink:linux_blink_rel > > Committed: https://crrev.com/df4156349708e21844209290a2d3638b550e18b2 > Cr-Commit-Position: refs/heads/master@{#370195} TBR=primiano@chromium.org,thakis@chromium.org,simonhatch@chromium.org,sievers@chromium.org,huangs@chromium.org,ssid@chromium.org # Not skipping CQ checks because original CL landed more than 1 days ago. BUG=461788 Review URL: https://codereview.chromium.org/1617263002 Cr-Commit-Position: refs/heads/master@{#370836}
Diffstat (limited to 'base')
-rw-r--r--base/BUILD.gn11
-rw-r--r--base/process/process_metrics_linux.cc3
-rw-r--r--base/trace_event/memory_dump_manager.cc18
-rw-r--r--base/trace_event/process_memory_maps_dump_provider.cc176
-rw-r--r--base/trace_event/process_memory_maps_dump_provider.h43
-rw-r--r--base/trace_event/process_memory_maps_dump_provider_unittest.cc190
-rw-r--r--base/trace_event/process_memory_totals_dump_provider.cc92
-rw-r--r--base/trace_event/process_memory_totals_dump_provider.h48
-rw-r--r--base/trace_event/process_memory_totals_dump_provider_unittest.cc48
-rw-r--r--base/trace_event/trace_event.gypi12
10 files changed, 639 insertions, 2 deletions
diff --git a/base/BUILD.gn b/base/BUILD.gn
index 3f8380c..5e94b37 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -849,8 +849,11 @@ component("base") {
"trace_event/process_memory_dump.h",
"trace_event/process_memory_maps.cc",
"trace_event/process_memory_maps.h",
+ "trace_event/process_memory_maps_dump_provider.h",
"trace_event/process_memory_totals.cc",
"trace_event/process_memory_totals.h",
+ "trace_event/process_memory_totals_dump_provider.cc",
+ "trace_event/process_memory_totals_dump_provider.h",
"trace_event/trace_buffer.cc",
"trace_event/trace_buffer.h",
"trace_event/trace_config.cc",
@@ -991,6 +994,7 @@ component("base") {
"sys_info_linux.cc",
"trace_event/malloc_dump_provider.cc",
"trace_event/malloc_dump_provider.h",
+ "trace_event/process_memory_maps_dump_provider.cc",
]
set_sources_assignment_filter(sources_assignment_filter)
@@ -1057,6 +1061,7 @@ component("base") {
"sync_socket_posix.cc",
"sys_info.cc",
"sys_info_posix.cc",
+ "trace_event/process_memory_totals_dump_provider.cc",
"trace_event/trace_event_system_stats_monitor.cc",
]
@@ -1185,6 +1190,7 @@ component("base") {
sources += [
"trace_event/malloc_dump_provider.cc",
"trace_event/malloc_dump_provider.h",
+ "trace_event/process_memory_maps_dump_provider.cc",
]
if (is_asan || is_lsan || is_msan || is_tsan) {
@@ -1822,6 +1828,7 @@ test("base_unittests") {
"trace_event/memory_allocator_dump_unittest.cc",
"trace_event/memory_dump_manager_unittest.cc",
"trace_event/process_memory_dump_unittest.cc",
+ "trace_event/process_memory_totals_dump_provider_unittest.cc",
"trace_event/trace_config_memory_test_util.h",
"trace_event/trace_config_unittest.cc",
"trace_event/trace_event_argument_unittest.cc",
@@ -1938,6 +1945,10 @@ test("base_unittests") {
}
}
+ if (is_linux || is_android) {
+ sources += [ "trace_event/process_memory_maps_dump_provider_unittest.cc" ]
+ }
+
if (!is_linux || use_ozone) {
sources -= [ "message_loop/message_pump_glib_unittest.cc" ]
}
diff --git a/base/process/process_metrics_linux.cc b/base/process/process_metrics_linux.cc
index 7a731bb..bcebcf5 100644
--- a/base/process/process_metrics_linux.cc
+++ b/base/process/process_metrics_linux.cc
@@ -86,8 +86,7 @@ size_t ReadProcStatusAndGetFieldAsSizeT(pid_t pid, const std::string& field) {
return value;
}
}
- // This can be reached if the process dies when proc is read -- in that case,
- // the kernel can return missing fields.
+ NOTREACHED();
return 0;
}
diff --git a/base/trace_event/memory_dump_manager.cc b/base/trace_event/memory_dump_manager.cc
index eb82ac2..1f311b2 100644
--- a/base/trace_event/memory_dump_manager.cc
+++ b/base/trace_event/memory_dump_manager.cc
@@ -23,6 +23,14 @@
#include "base/trace_event/trace_event_argument.h"
#include "build/build_config.h"
+#if !defined(OS_NACL)
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+#endif
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include "base/trace_event/process_memory_maps_dump_provider.h"
+#endif
+
#if defined(OS_ANDROID)
#include "base/trace_event/java_heap_dump_provider_android.h"
#endif
@@ -145,10 +153,20 @@ void MemoryDumpManager::Initialize(MemoryDumpManagerDelegate* delegate,
}
// Enable the core dump providers.
+#if !defined(OS_NACL)
+ RegisterDumpProvider(ProcessMemoryTotalsDumpProvider::GetInstance(),
+ "ProcessMemoryTotals", nullptr);
+#endif
+
#if defined(MALLOC_MEMORY_TRACING_SUPPORTED)
RegisterDumpProvider(MallocDumpProvider::GetInstance(), "Malloc", nullptr);
#endif
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ RegisterDumpProvider(ProcessMemoryMapsDumpProvider::GetInstance(),
+ "ProcessMemoryMaps", nullptr);
+#endif
+
#if defined(OS_ANDROID)
RegisterDumpProvider(JavaHeapDumpProvider::GetInstance(), "JavaHeap",
nullptr);
diff --git a/base/trace_event/process_memory_maps_dump_provider.cc b/base/trace_event/process_memory_maps_dump_provider.cc
new file mode 100644
index 0000000..4c3959f
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider.cc
@@ -0,0 +1,176 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_maps_dump_provider.h"
+
+#include <stdint.h>
+
+#include "base/files/scoped_file.h"
+#include "base/format_macros.h"
+#include "base/logging.h"
+#include "base/strings/string_util.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_maps.h"
+
+namespace base {
+namespace trace_event {
+
+// static
+FILE* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr;
+
+namespace {
+
+const uint32_t kMaxLineSize = 4096;
+
+bool ParseSmapsHeader(const char* header_line,
+ ProcessMemoryMaps::VMRegion* region) {
+ // e.g., "00400000-00421000 r-xp 00000000 fc:01 1234 /foo.so\n"
+ bool res = true; // Whether this region should be appended or skipped.
+ uint64_t end_addr = 0;
+ char protection_flags[5] = {0};
+ char mapped_file[kMaxLineSize];
+
+ if (sscanf(header_line, "%" SCNx64 "-%" SCNx64 " %4c %*s %*s %*s%4095[^\n]\n",
+ &region->start_address, &end_addr, protection_flags,
+ mapped_file) != 4)
+ return false;
+
+ if (end_addr > region->start_address) {
+ region->size_in_bytes = end_addr - region->start_address;
+ } else {
+ // This is not just paranoia, it can actually happen (See crbug.com/461237).
+ region->size_in_bytes = 0;
+ res = false;
+ }
+
+ region->protection_flags = 0;
+ if (protection_flags[0] == 'r') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+ }
+ if (protection_flags[1] == 'w') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+ }
+ if (protection_flags[2] == 'x') {
+ region->protection_flags |=
+ ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+ }
+
+ region->mapped_file = mapped_file;
+ TrimWhitespaceASCII(region->mapped_file, TRIM_ALL, &region->mapped_file);
+
+ return res;
+}
+
+uint64_t ReadCounterBytes(char* counter_line) {
+ uint64_t counter_value = 0;
+ int res = sscanf(counter_line, "%*s %" SCNu64 " kB", &counter_value);
+ DCHECK_EQ(1, res);
+ return counter_value * 1024;
+}
+
+uint32_t ParseSmapsCounter(char* counter_line,
+ ProcessMemoryMaps::VMRegion* region) {
+ // A smaps counter lines looks as follows: "RSS: 0 Kb\n"
+ uint32_t res = 1;
+ char counter_name[20];
+ int did_read = sscanf(counter_line, "%19[^\n ]", counter_name);
+ DCHECK_EQ(1, did_read);
+
+ if (strcmp(counter_name, "Pss:") == 0) {
+ region->byte_stats_proportional_resident = ReadCounterBytes(counter_line);
+ } else if (strcmp(counter_name, "Private_Dirty:") == 0) {
+ region->byte_stats_private_dirty_resident = ReadCounterBytes(counter_line);
+ } else if (strcmp(counter_name, "Private_Clean:") == 0) {
+ region->byte_stats_private_clean_resident = ReadCounterBytes(counter_line);
+ } else if (strcmp(counter_name, "Shared_Dirty:") == 0) {
+ region->byte_stats_shared_dirty_resident = ReadCounterBytes(counter_line);
+ } else if (strcmp(counter_name, "Shared_Clean:") == 0) {
+ region->byte_stats_shared_clean_resident = ReadCounterBytes(counter_line);
+ } else if (strcmp(counter_name, "Swap:") == 0) {
+ region->byte_stats_swapped = ReadCounterBytes(counter_line);
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+uint32_t ReadLinuxProcSmapsFile(FILE* smaps_file, ProcessMemoryMaps* pmm) {
+ if (!smaps_file)
+ return 0;
+
+ fseek(smaps_file, 0, SEEK_SET);
+
+ char line[kMaxLineSize];
+ const uint32_t kNumExpectedCountersPerRegion = 6;
+ uint32_t counters_parsed_for_current_region = 0;
+ uint32_t num_valid_regions = 0;
+ ProcessMemoryMaps::VMRegion region;
+ bool should_add_current_region = false;
+ for (;;) {
+ line[0] = '\0';
+ if (fgets(line, kMaxLineSize, smaps_file) == nullptr)
+ break;
+ DCHECK_GT(strlen(line), 0u);
+ if (isxdigit(line[0]) && !isupper(line[0])) {
+ region = ProcessMemoryMaps::VMRegion();
+ counters_parsed_for_current_region = 0;
+ should_add_current_region = ParseSmapsHeader(line, &region);
+ } else {
+ counters_parsed_for_current_region += ParseSmapsCounter(line, &region);
+ DCHECK_LE(counters_parsed_for_current_region,
+ kNumExpectedCountersPerRegion);
+ if (counters_parsed_for_current_region == kNumExpectedCountersPerRegion) {
+ if (should_add_current_region) {
+ pmm->AddVMRegion(region);
+ ++num_valid_regions;
+ should_add_current_region = false;
+ }
+ }
+ }
+ }
+ return num_valid_regions;
+}
+
+} // namespace
+
+// static
+ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() {
+ return Singleton<ProcessMemoryMapsDumpProvider,
+ LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get();
+}
+
+ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() {
+}
+
+ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot of the memory maps for
+// the current process.
+bool ProcessMemoryMapsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
+ ProcessMemoryDump* pmd) {
+ // Snapshot of memory maps is not taken for light dump requests.
+ if (args.level_of_detail == MemoryDumpLevelOfDetail::LIGHT)
+ return true;
+
+ uint32_t res = 0;
+ if (UNLIKELY(proc_smaps_for_testing)) {
+ res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps());
+ } else {
+ ScopedFILE smaps_file(fopen("/proc/self/smaps", "r"));
+ res = ReadLinuxProcSmapsFile(smaps_file.get(), pmd->process_mmaps());
+ }
+
+ if (res > 0) {
+ pmd->set_has_process_mmaps();
+ return true;
+ }
+ return false;
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/process_memory_maps_dump_provider.h b/base/trace_event/process_memory_maps_dump_provider.h
new file mode 100644
index 0000000..84badfe
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider.h
@@ -0,0 +1,43 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+#include "build/build_config.h"
+
+namespace base {
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryMapsDumpProvider : public MemoryDumpProvider {
+ public:
+ static ProcessMemoryMapsDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const MemoryDumpArgs& args,
+ ProcessMemoryDump* pmd) override;
+
+ private:
+ friend struct DefaultSingletonTraits<ProcessMemoryMapsDumpProvider>;
+ FRIEND_TEST_ALL_PREFIXES(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps);
+
+#if defined(OS_LINUX) || defined(OS_ANDROID) || defined(OS_NACL)
+ static FILE* proc_smaps_for_testing;
+#endif
+
+ ProcessMemoryMapsDumpProvider();
+ ~ProcessMemoryMapsDumpProvider() override;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryMapsDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_MAPS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_maps_dump_provider_unittest.cc b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
new file mode 100644
index 0000000..624f96f
--- /dev/null
+++ b/base/trace_event/process_memory_maps_dump_provider_unittest.cc
@@ -0,0 +1,190 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_maps_dump_provider.h"
+
+#include <stdint.h>
+
+#include "base/files/file_util.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_maps.h"
+#include "base/trace_event/trace_event_argument.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+namespace {
+const char kTestSmaps1[] =
+ "00400000-004be000 r-xp 00000000 fc:01 1234 /file/1\n"
+ "Size: 760 kB\n"
+ "Rss: 296 kB\n"
+ "Pss: 162 kB\n"
+ "Shared_Clean: 228 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 68 kB\n"
+ "Referenced: 296 kB\n"
+ "Anonymous: 68 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 4 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ "ff000000-ff800000 -w-p 00001080 fc:01 0 /file/name with space\n"
+ "Size: 0 kB\n"
+ "Rss: 192 kB\n"
+ "Pss: 128 kB\n"
+ "Shared_Clean: 120 kB\n"
+ "Shared_Dirty: 4 kB\n"
+ "Private_Clean: 60 kB\n"
+ "Private_Dirty: 8 kB\n"
+ "Referenced: 296 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd";
+
+const char kTestSmaps2[] =
+ // An invalid region, with zero size and overlapping with the last one
+ // (See crbug.com/461237).
+ "7fe7ce79c000-7fe7ce79c000 ---p 00000000 00:00 0 \n"
+ "Size: 4 kB\n"
+ "Rss: 0 kB\n"
+ "Pss: 0 kB\n"
+ "Shared_Clean: 0 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 0 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ // A invalid region with its range going backwards.
+ "00400000-00200000 ---p 00000000 00:00 0 \n"
+ "Size: 4 kB\n"
+ "Rss: 0 kB\n"
+ "Pss: 0 kB\n"
+ "Shared_Clean: 0 kB\n"
+ "Shared_Dirty: 0 kB\n"
+ "Private_Clean: 0 kB\n"
+ "Private_Dirty: 0 kB\n"
+ "Referenced: 0 kB\n"
+ "Anonymous: 0 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd ex mr mw me dw sd\n"
+ // A good anonymous region at the end.
+ "7fe7ce79c000-7fe7ce7a8000 ---p 00000000 00:00 0 \n"
+ "Size: 48 kB\n"
+ "Rss: 40 kB\n"
+ "Pss: 32 kB\n"
+ "Shared_Clean: 16 kB\n"
+ "Shared_Dirty: 12 kB\n"
+ "Private_Clean: 8 kB\n"
+ "Private_Dirty: 4 kB\n"
+ "Referenced: 40 kB\n"
+ "Anonymous: 16 kB\n"
+ "AnonHugePages: 0 kB\n"
+ "Swap: 0 kB\n"
+ "KernelPageSize: 4 kB\n"
+ "MMUPageSize: 4 kB\n"
+ "Locked: 0 kB\n"
+ "VmFlags: rd wr mr mw me ac sd\n";
+
+void CreateAndSetSmapsFileForTesting(const char* smaps_string,
+ ScopedFILE& file) {
+ FilePath temp_path;
+ FILE* temp_file = CreateAndOpenTemporaryFile(&temp_path);
+ file.reset(temp_file);
+ ASSERT_TRUE(temp_file);
+
+ ASSERT_TRUE(base::WriteFileDescriptor(fileno(temp_file), smaps_string,
+ strlen(smaps_string)));
+}
+
+} // namespace
+
+TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) {
+ const uint32_t kProtR = ProcessMemoryMaps::VMRegion::kProtectionFlagsRead;
+ const uint32_t kProtW = ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite;
+ const uint32_t kProtX = ProcessMemoryMaps::VMRegion::kProtectionFlagsExec;
+ const MemoryDumpArgs dump_args = {MemoryDumpLevelOfDetail::DETAILED};
+
+ auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance();
+
+ // Emulate an empty /proc/self/smaps.
+ ProcessMemoryDump pmd_invalid(nullptr /* session_state */);
+ ScopedFILE empty_file(OpenFile(FilePath("/dev/null"), "r"));
+ ASSERT_TRUE(empty_file.get());
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = empty_file.get();
+ pmmdp->OnMemoryDump(dump_args, &pmd_invalid);
+ ASSERT_FALSE(pmd_invalid.has_process_mmaps());
+
+ // Parse the 1st smaps file.
+ ProcessMemoryDump pmd_1(nullptr /* session_state */);
+ ScopedFILE temp_file1;
+ CreateAndSetSmapsFileForTesting(kTestSmaps1, temp_file1);
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = temp_file1.get();
+ pmmdp->OnMemoryDump(dump_args, &pmd_1);
+ ASSERT_TRUE(pmd_1.has_process_mmaps());
+ const auto& regions_1 = pmd_1.process_mmaps()->vm_regions();
+ ASSERT_EQ(2UL, regions_1.size());
+
+ EXPECT_EQ(0x00400000UL, regions_1[0].start_address);
+ EXPECT_EQ(0x004be000UL - 0x00400000UL, regions_1[0].size_in_bytes);
+ EXPECT_EQ(kProtR | kProtX, regions_1[0].protection_flags);
+ EXPECT_EQ("/file/1", regions_1[0].mapped_file);
+ EXPECT_EQ(162 * 1024UL, regions_1[0].byte_stats_proportional_resident);
+ EXPECT_EQ(228 * 1024UL, regions_1[0].byte_stats_shared_clean_resident);
+ EXPECT_EQ(0UL, regions_1[0].byte_stats_shared_dirty_resident);
+ EXPECT_EQ(0UL, regions_1[0].byte_stats_private_clean_resident);
+ EXPECT_EQ(68 * 1024UL, regions_1[0].byte_stats_private_dirty_resident);
+ EXPECT_EQ(4 * 1024UL, regions_1[0].byte_stats_swapped);
+
+ EXPECT_EQ(0xff000000UL, regions_1[1].start_address);
+ EXPECT_EQ(0xff800000UL - 0xff000000UL, regions_1[1].size_in_bytes);
+ EXPECT_EQ(kProtW, regions_1[1].protection_flags);
+ EXPECT_EQ("/file/name with space", regions_1[1].mapped_file);
+ EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_proportional_resident);
+ EXPECT_EQ(120 * 1024UL, regions_1[1].byte_stats_shared_clean_resident);
+ EXPECT_EQ(4 * 1024UL, regions_1[1].byte_stats_shared_dirty_resident);
+ EXPECT_EQ(60 * 1024UL, regions_1[1].byte_stats_private_clean_resident);
+ EXPECT_EQ(8 * 1024UL, regions_1[1].byte_stats_private_dirty_resident);
+ EXPECT_EQ(0 * 1024UL, regions_1[1].byte_stats_swapped);
+
+ // Parse the 2nd smaps file.
+ ProcessMemoryDump pmd_2(nullptr /* session_state */);
+ ScopedFILE temp_file2;
+ CreateAndSetSmapsFileForTesting(kTestSmaps2, temp_file2);
+ ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = temp_file2.get();
+ pmmdp->OnMemoryDump(dump_args, &pmd_2);
+ ASSERT_TRUE(pmd_2.has_process_mmaps());
+ const auto& regions_2 = pmd_2.process_mmaps()->vm_regions();
+ ASSERT_EQ(1UL, regions_2.size());
+ EXPECT_EQ(0x7fe7ce79c000UL, regions_2[0].start_address);
+ EXPECT_EQ(0x7fe7ce7a8000UL - 0x7fe7ce79c000UL, regions_2[0].size_in_bytes);
+ EXPECT_EQ(0U, regions_2[0].protection_flags);
+ EXPECT_EQ("", regions_2[0].mapped_file);
+ EXPECT_EQ(32 * 1024UL, regions_2[0].byte_stats_proportional_resident);
+ EXPECT_EQ(16 * 1024UL, regions_2[0].byte_stats_shared_clean_resident);
+ EXPECT_EQ(12 * 1024UL, regions_2[0].byte_stats_shared_dirty_resident);
+ EXPECT_EQ(8 * 1024UL, regions_2[0].byte_stats_private_clean_resident);
+ EXPECT_EQ(4 * 1024UL, regions_2[0].byte_stats_private_dirty_resident);
+ EXPECT_EQ(0 * 1024UL, regions_2[0].byte_stats_swapped);
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/process_memory_totals_dump_provider.cc b/base/trace_event/process_memory_totals_dump_provider.cc
new file mode 100644
index 0000000..1713ebf
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.cc
@@ -0,0 +1,92 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include <stddef.h>
+
+#include "base/process/process_metrics.h"
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "build/build_config.h"
+
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+#include <fcntl.h>
+
+#include "base/files/file_util.h"
+
+namespace {
+bool kernel_supports_rss_peak_reset = true;
+const char kClearPeakRssCommand[] = "5";
+}
+#endif
+
+namespace base {
+namespace trace_event {
+
+// static
+uint64_t ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+// static
+ProcessMemoryTotalsDumpProvider*
+ProcessMemoryTotalsDumpProvider::GetInstance() {
+ return Singleton<
+ ProcessMemoryTotalsDumpProvider,
+ LeakySingletonTraits<ProcessMemoryTotalsDumpProvider>>::get();
+}
+
+ProcessMemoryTotalsDumpProvider::ProcessMemoryTotalsDumpProvider()
+ : process_metrics_(ProcessMetrics::CreateCurrentProcessMetrics()) {}
+
+ProcessMemoryTotalsDumpProvider::~ProcessMemoryTotalsDumpProvider() {
+}
+
+// Called at trace dump point time. Creates a snapshot the memory counters for
+// the current process.
+bool ProcessMemoryTotalsDumpProvider::OnMemoryDump(const MemoryDumpArgs& args,
+ ProcessMemoryDump* pmd) {
+ const uint64_t rss_bytes = rss_bytes_for_testing
+ ? rss_bytes_for_testing
+ : process_metrics_->GetWorkingSetSize();
+
+ uint64_t peak_rss_bytes = 0;
+
+#if !defined(OS_IOS)
+ peak_rss_bytes = process_metrics_->GetPeakWorkingSetSize();
+#if defined(OS_LINUX) || defined(OS_ANDROID)
+ if (kernel_supports_rss_peak_reset) {
+ // TODO(ssid): Fix crbug.com/461788 to write to the file from sandboxed
+ // processes.
+ int clear_refs_fd = open("/proc/self/clear_refs", O_WRONLY);
+ if (clear_refs_fd > 0 &&
+ WriteFileDescriptor(clear_refs_fd, kClearPeakRssCommand,
+ sizeof(kClearPeakRssCommand))) {
+ pmd->process_totals()->set_is_peak_rss_resetable(true);
+ } else {
+ kernel_supports_rss_peak_reset = false;
+ }
+ close(clear_refs_fd);
+ }
+#elif defined(OS_MACOSX)
+ size_t private_bytes;
+ bool res = process_metrics_->GetMemoryBytes(&private_bytes,
+ nullptr /* shared_bytes */);
+ if (res) {
+ pmd->process_totals()->SetExtraFieldInBytes("private_bytes", private_bytes);
+ }
+#endif // defined(OS_LINUX) || defined(OS_ANDROID)
+#endif // !defined(OS_IOS)
+
+ if (rss_bytes > 0) {
+ pmd->process_totals()->set_resident_set_bytes(rss_bytes);
+ pmd->process_totals()->set_peak_resident_set_bytes(peak_rss_bytes);
+ pmd->set_has_process_totals();
+ return true;
+ }
+
+ return false;
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/process_memory_totals_dump_provider.h b/base/trace_event/process_memory_totals_dump_provider.h
new file mode 100644
index 0000000..d9573d3
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider.h
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+#define BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
+
+#include <stdint.h>
+
+#include "base/gtest_prod_util.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/singleton.h"
+#include "base/trace_event/memory_dump_provider.h"
+
+namespace base {
+
+class ProcessMetrics;
+
+namespace trace_event {
+
+// Dump provider which collects process-wide memory stats.
+class BASE_EXPORT ProcessMemoryTotalsDumpProvider : public MemoryDumpProvider {
+ public:
+ static ProcessMemoryTotalsDumpProvider* GetInstance();
+
+ // MemoryDumpProvider implementation.
+ bool OnMemoryDump(const MemoryDumpArgs& args,
+ ProcessMemoryDump* pmd) override;
+
+ private:
+ friend struct DefaultSingletonTraits<ProcessMemoryTotalsDumpProvider>;
+ FRIEND_TEST_ALL_PREFIXES(ProcessMemoryTotalsDumpProviderTest, DumpRSS);
+
+ static uint64_t rss_bytes_for_testing;
+
+ ProcessMemoryTotalsDumpProvider();
+ ~ProcessMemoryTotalsDumpProvider() override;
+
+ scoped_ptr<ProcessMetrics> process_metrics_;
+
+ DISALLOW_COPY_AND_ASSIGN(ProcessMemoryTotalsDumpProvider);
+};
+
+} // namespace trace_event
+} // namespace base
+
+#endif // BASE_TRACE_EVENT_PROCESS_MEMORY_TOTALS_DUMP_PROVIDER_H_
diff --git a/base/trace_event/process_memory_totals_dump_provider_unittest.cc b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
new file mode 100644
index 0000000..d3f517e
--- /dev/null
+++ b/base/trace_event/process_memory_totals_dump_provider_unittest.cc
@@ -0,0 +1,48 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/trace_event/process_memory_totals_dump_provider.h"
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "base/trace_event/process_memory_dump.h"
+#include "base/trace_event/process_memory_totals.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+namespace trace_event {
+
+TEST(ProcessMemoryTotalsDumpProviderTest, DumpRSS) {
+ const MemoryDumpArgs high_detail_args = {MemoryDumpLevelOfDetail::DETAILED};
+ auto pmtdp = ProcessMemoryTotalsDumpProvider::GetInstance();
+ scoped_ptr<ProcessMemoryDump> pmd_before(new ProcessMemoryDump(nullptr));
+ scoped_ptr<ProcessMemoryDump> pmd_after(new ProcessMemoryDump(nullptr));
+
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 1024;
+ pmtdp->OnMemoryDump(high_detail_args, pmd_before.get());
+
+ // Pretend that the RSS of the process increased of +1M.
+ const size_t kAllocSize = 1048576;
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing += kAllocSize;
+
+ pmtdp->OnMemoryDump(high_detail_args, pmd_after.get());
+
+ ProcessMemoryTotalsDumpProvider::rss_bytes_for_testing = 0;
+
+ ASSERT_TRUE(pmd_before->has_process_totals());
+ ASSERT_TRUE(pmd_after->has_process_totals());
+
+ const uint64_t rss_before =
+ pmd_before->process_totals()->resident_set_bytes();
+ const uint64_t rss_after = pmd_after->process_totals()->resident_set_bytes();
+
+ EXPECT_NE(0U, rss_before);
+ EXPECT_NE(0U, rss_after);
+
+ EXPECT_EQ(rss_after - rss_before, kAllocSize);
+}
+
+} // namespace trace_event
+} // namespace base
diff --git a/base/trace_event/trace_event.gypi b/base/trace_event/trace_event.gypi
index 090f7da..6948d7c 100644
--- a/base/trace_event/trace_event.gypi
+++ b/base/trace_event/trace_event.gypi
@@ -36,8 +36,11 @@
'trace_event/process_memory_dump.h',
'trace_event/process_memory_maps.cc',
'trace_event/process_memory_maps.h',
+ 'trace_event/process_memory_maps_dump_provider.h',
'trace_event/process_memory_totals.cc',
'trace_event/process_memory_totals.h',
+ 'trace_event/process_memory_totals_dump_provider.cc',
+ 'trace_event/process_memory_totals_dump_provider.h',
'trace_event/trace_buffer.cc',
'trace_event/trace_buffer.h',
'trace_event/trace_config.cc',
@@ -76,6 +79,7 @@
'trace_event/memory_allocator_dump_unittest.cc',
'trace_event/memory_dump_manager_unittest.cc',
'trace_event/process_memory_dump_unittest.cc',
+ 'trace_event/process_memory_totals_dump_provider_unittest.cc',
'trace_event/trace_config_memory_test_util.h',
'trace_event/trace_config_unittest.cc',
'trace_event/trace_event_argument_unittest.cc',
@@ -91,6 +95,14 @@
'trace_event/malloc_dump_provider.h',
],
}],
+ ['OS == "linux" or OS == "android"', {
+ 'trace_event_sources': [
+ 'trace_event/process_memory_maps_dump_provider.cc',
+ ],
+ 'trace_event_test_sources' : [
+ 'trace_event/process_memory_maps_dump_provider_unittest.cc',
+ ],
+ }],
['OS == "android"', {
'trace_event_test_sources' : [
'trace_event/trace_event_android_unittest.cc',