diff options
author | oshima <oshima@chromium.org> | 2016-01-21 15:52:21 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-01-21 23:53:56 +0000 |
commit | 94bef8d8bf199e23102783a55a15c9f10bc88631 (patch) | |
tree | a819b7e93665c1d196cc27ab507ef875786a11b5 /base | |
parent | 96eeb2b4f996606caa94f5e4baee43cc6d22b1c7 (diff) | |
download | chromium_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.gn | 11 | ||||
-rw-r--r-- | base/process/process_metrics_linux.cc | 3 | ||||
-rw-r--r-- | base/trace_event/memory_dump_manager.cc | 18 | ||||
-rw-r--r-- | base/trace_event/process_memory_maps_dump_provider.cc | 176 | ||||
-rw-r--r-- | base/trace_event/process_memory_maps_dump_provider.h | 43 | ||||
-rw-r--r-- | base/trace_event/process_memory_maps_dump_provider_unittest.cc | 190 | ||||
-rw-r--r-- | base/trace_event/process_memory_totals_dump_provider.cc | 92 | ||||
-rw-r--r-- | base/trace_event/process_memory_totals_dump_provider.h | 48 | ||||
-rw-r--r-- | base/trace_event/process_memory_totals_dump_provider_unittest.cc | 48 | ||||
-rw-r--r-- | base/trace_event/trace_event.gypi | 12 |
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", + ®ion->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, ®ion->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, ®ion); + } else { + counters_parsed_for_current_region += ParseSmapsCounter(line, ®ion); + 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', |