diff options
author | primiano <primiano@chromium.org> | 2015-02-25 03:03:28 -0800 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-02-25 11:04:02 +0000 |
commit | 452b8c04929b90fc632f08997be9c01ac0a4cdce (patch) | |
tree | 121b6763972c32fc788f73f6cbf8846d6fcd9e0f /base/trace_event | |
parent | a7fe515e3ca07e260b58ebf460f842601266ed87 (diff) | |
download | chromium_src-452b8c04929b90fc632f08997be9c01ac0a4cdce.zip chromium_src-452b8c04929b90fc632f08997be9c01ac0a4cdce.tar.gz chromium_src-452b8c04929b90fc632f08997be9c01ac0a4cdce.tar.bz2 |
[tracing] Add memory maps dumper impl for Linux/Android
This is a follow-up to crrev.com/928723004. It introduces
the code which is able to read and parse the /proc/.../smaps
file into an actual TraceValue and the corresponding unittest.
BUG=460884
Review URL: https://codereview.chromium.org/951463002
Cr-Commit-Position: refs/heads/master@{#318023}
Diffstat (limited to 'base/trace_event')
-rw-r--r-- | base/trace_event/process_memory_maps_dump_provider.cc | 115 | ||||
-rw-r--r-- | base/trace_event/process_memory_maps_dump_provider_unittest.cc | 136 |
2 files changed, 246 insertions, 5 deletions
diff --git a/base/trace_event/process_memory_maps_dump_provider.cc b/base/trace_event/process_memory_maps_dump_provider.cc index b6fba80..e1cefc3 100644 --- a/base/trace_event/process_memory_maps_dump_provider.cc +++ b/base/trace_event/process_memory_maps_dump_provider.cc @@ -4,6 +4,7 @@ #include "base/trace_event/process_memory_maps_dump_provider.h" +#include <cctype> #include <fstream> #include "base/logging.h" @@ -19,15 +20,123 @@ namespace trace_event { 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; } - uint32 num_regions_processed = 0; - // TODO(primiano): in next CLs add the actual code to process the smaps file. - return num_regions_processed; + 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) 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 33c1bbb..02fd136 100644 --- a/base/trace_event/process_memory_maps_dump_provider_unittest.cc +++ b/base/trace_event/process_memory_maps_dump_provider_unittest.cc @@ -16,7 +16,101 @@ namespace base { namespace trace_event { #if defined(OS_LINUX) || defined(OS_ANDROID) +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: 0 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: 128 kB\n" + "Pss: 128 kB\n" + "Shared_Clean: 124 kB\n" + "Shared_Dirty: 0 kB\n" + "Private_Clean: 68 kB\n" + "Private_Dirty: 0 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: 0 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"; +} // namespace + TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) { + const uint32 kProtR = ProcessMemoryMaps::VMRegion::kProtectionFlagsRead; + const uint32 kProtW = ProcessMemoryMaps::VMRegion::kProtectionFlagsWrite; + const uint32 kProtX = ProcessMemoryMaps::VMRegion::kProtectionFlagsExec; + auto pmmdp = ProcessMemoryMapsDumpProvider::GetInstance(); // Emulate a non-existent /proc/self/smaps. @@ -34,8 +128,46 @@ TEST(ProcessMemoryMapsDumpProviderTest, ParseProcSmaps) { pmmdp->DumpInto(&pmd_invalid); ASSERT_FALSE(pmd_invalid.has_process_mmaps()); - // TODO(primiano): in the next CLs add the code to parse some actual smaps - // files and check that the parsing logic of the dump provider holds. + // Parse the 1st smaps file. + ProcessMemoryDump pmd_1; + std::istringstream test_smaps_1(kTestSmaps1); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_1; + pmmdp->DumpInto(&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(0UL, regions_1[0].mapped_file_offset); + EXPECT_EQ(296 * 1024UL, regions_1[0].byte_stats_resident); + EXPECT_EQ(68 * 1024UL, regions_1[0].byte_stats_anonymous); + + 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(0x00001080UL, regions_1[1].mapped_file_offset); + EXPECT_EQ(128 * 1024UL, regions_1[1].byte_stats_resident); + EXPECT_EQ(0UL, regions_1[1].byte_stats_anonymous); + + // Parse the 2nd smaps file. + ProcessMemoryDump pmd_2; + std::istringstream test_smaps_2(kTestSmaps2); + ProcessMemoryMapsDumpProvider::proc_smaps_for_testing = &test_smaps_2; + pmmdp->DumpInto(&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(0UL, regions_2[0].mapped_file_offset); + EXPECT_EQ(40 * 1024UL, regions_2[0].byte_stats_resident); + EXPECT_EQ(16 * 1024UL, regions_2[0].byte_stats_anonymous); } #endif // defined(OS_LINUX) || defined(OS_ANDROID) |