Index: base/trace_event/process_memory_maps_dump_provider.cc |
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 0000000000000000000000000000000000000000..e1cefc3a63b485b5719b7fa06baa24195d278d32 |
--- /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 <cctype> |
+#include <fstream> |
+ |
+#include "base/logging.h" |
+#include "base/process/process_metrics.h" |
+#include "base/trace_event/process_memory_dump.h" |
+#include "base/trace_event/process_memory_maps.h" |
+ |
+namespace base { |
+namespace trace_event { |
+ |
+#if defined(OS_LINUX) || defined(OS_ANDROID) |
+// static |
+std::istream* ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = nullptr; |
+ |
+namespace { |
+ |
+const uint32 kMaxLineSize = 4096; |
+ |
+bool ParseSmapsHeader(std::istream* smaps, |
+ 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 end_addr; |
+ std::string protection_flags; |
+ std::string ignored; |
+ *smaps >> std::hex >> region->start_address; |
+ smaps->ignore(1); |
+ *smaps >> std::hex >> end_addr; |
+ 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; |
+ *smaps >> protection_flags; |
+ CHECK(4UL == protection_flags.size()); |
+ 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; |
+ } |
+ *smaps >> std::hex >> region->mapped_file_offset; |
+ *smaps >> ignored; // Ignore device maj-min (fc:01 in the example above). |
+ *smaps >> ignored; // Ignore inode number (1234 in the example above). |
+ |
+ while (smaps->peek() == ' ') |
+ smaps->ignore(1); |
+ char mapped_file[kMaxLineSize]; |
+ smaps->getline(mapped_file, sizeof(mapped_file)); |
+ region->mapped_file = mapped_file; |
+ |
+ return res; |
+} |
+ |
+uint32 ParseSmapsCounter(std::istream* smaps, |
+ ProcessMemoryMaps::VMRegion* region) { |
+ // e.g., "RSS: 0 Kb\n" |
+ uint32 res = 0; |
+ std::string counter_name; |
+ *smaps >> counter_name; |
+ |
+ // TODO(primiano): "Swap" should also be accounted as resident. Check |
+ // whether Rss isn't already counting swapped and fix below if that is |
+ // the case. |
+ if (counter_name == "Rss:") { |
+ *smaps >> std::dec >> region->byte_stats_resident; |
+ region->byte_stats_resident *= 1024; |
+ res = 1; |
+ } else if (counter_name == "Anonymous:") { |
+ *smaps >> std::dec >> region->byte_stats_anonymous; |
+ region->byte_stats_anonymous *= 1024; |
+ res = 1; |
+ } |
+ |
+#ifndef NDEBUG |
+ // Paranoid check against changes of the Kernel /proc interface. |
+ if (res) { |
+ std::string unit; |
+ *smaps >> unit; |
+ DCHECK_EQ("kB", unit); |
+ } |
+#endif |
+ |
+ smaps->ignore(kMaxLineSize, '\n'); |
+ |
+ return res; |
+} |
+ |
+uint32 ReadLinuxProcSmapsFile(std::istream* smaps, ProcessMemoryMaps* pmm) { |
+ if (!smaps->good()) { |
+ LOG(ERROR) << "Could not read smaps file."; |
+ return 0; |
+ } |
+ const uint32 kNumExpectedCountersPerRegion = 2; |
+ uint32 counters_parsed_for_current_region = 0; |
+ uint32 num_valid_regions = 0; |
+ ProcessMemoryMaps::VMRegion region; |
+ bool should_add_current_region = false; |
+ for (;;) { |
+ int next = smaps->peek(); |
+ if (next == std::ifstream::traits_type::eof() || next == '\n') |
+ break; |
+ if (isxdigit(next) && !isupper(next)) { |
+ region = {0}; |
+ counters_parsed_for_current_region = 0; |
+ should_add_current_region = ParseSmapsHeader(smaps, ®ion); |
+ } else { |
+ counters_parsed_for_current_region += ParseSmapsCounter(smaps, ®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 |
+#endif // defined(OS_LINUX) || defined(OS_ANDROID) |
+ |
+// static |
+ProcessMemoryMapsDumpProvider* ProcessMemoryMapsDumpProvider::GetInstance() { |
+ return Singleton<ProcessMemoryMapsDumpProvider, |
+ LeakySingletonTraits<ProcessMemoryMapsDumpProvider>>::get(); |
+} |
+ |
+ProcessMemoryMapsDumpProvider::ProcessMemoryMapsDumpProvider() { |
+} |
+ |
+ProcessMemoryMapsDumpProvider::~ProcessMemoryMapsDumpProvider() { |
+} |
+ |
+// Called at trace dump point time. Creates a snapshot the memory maps for the |
+// current process. |
+void ProcessMemoryMapsDumpProvider::DumpInto(ProcessMemoryDump* pmd) { |
+ uint32 res = 0; |
+ |
+#if defined(OS_LINUX) || defined(OS_ANDROID) |
+ if (UNLIKELY(proc_smaps_for_testing)) { |
+ res = ReadLinuxProcSmapsFile(proc_smaps_for_testing, pmd->process_mmaps()); |
+ } else { |
+ std::ifstream proc_self_smaps("/proc/self/smaps"); |
+ res = ReadLinuxProcSmapsFile(&proc_self_smaps, pmd->process_mmaps()); |
+ } |
+#else |
+ LOG(ERROR) << "ProcessMemoryMaps dump provider is supported only on Linux"; |
+#endif |
+ |
+ if (res > 0) |
+ pmd->set_has_process_mmaps(); |
+} |
+ |
+} // namespace trace_event |
+} // namespace base |